@report-designer/designer 0.1.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.
Files changed (253) hide show
  1. package/dist/band-metadata.d.ts +8 -0
  2. package/dist/band-metadata.d.ts.map +1 -0
  3. package/dist/band-metadata.js +75 -0
  4. package/dist/band-metadata.js.map +1 -0
  5. package/dist/component-factory.d.ts +9 -0
  6. package/dist/component-factory.d.ts.map +1 -0
  7. package/dist/component-factory.js +141 -0
  8. package/dist/component-factory.js.map +1 -0
  9. package/dist/component-palette-model.d.ts +14 -0
  10. package/dist/component-palette-model.d.ts.map +1 -0
  11. package/dist/component-palette-model.js +21 -0
  12. package/dist/component-palette-model.js.map +1 -0
  13. package/dist/components/Canvas.d.ts +5 -0
  14. package/dist/components/Canvas.d.ts.map +1 -0
  15. package/dist/components/Canvas.js +2428 -0
  16. package/dist/components/Canvas.js.map +1 -0
  17. package/dist/components/ConditionalFormatManager.d.ts +8 -0
  18. package/dist/components/ConditionalFormatManager.d.ts.map +1 -0
  19. package/dist/components/ConditionalFormatManager.js +135 -0
  20. package/dist/components/ConditionalFormatManager.js.map +1 -0
  21. package/dist/components/Designer.d.ts +22 -0
  22. package/dist/components/Designer.d.ts.map +1 -0
  23. package/dist/components/Designer.js +115 -0
  24. package/dist/components/Designer.js.map +1 -0
  25. package/dist/components/ExpressionEditor.d.ts +10 -0
  26. package/dist/components/ExpressionEditor.d.ts.map +1 -0
  27. package/dist/components/ExpressionEditor.js +203 -0
  28. package/dist/components/ExpressionEditor.js.map +1 -0
  29. package/dist/components/LeftPanel.d.ts +11 -0
  30. package/dist/components/LeftPanel.d.ts.map +1 -0
  31. package/dist/components/LeftPanel.js +551 -0
  32. package/dist/components/LeftPanel.js.map +1 -0
  33. package/dist/components/PropertyEditor.d.ts +6 -0
  34. package/dist/components/PropertyEditor.d.ts.map +1 -0
  35. package/dist/components/PropertyEditor.js +1002 -0
  36. package/dist/components/PropertyEditor.js.map +1 -0
  37. package/dist/components/RibbonToolbar.d.ts +3 -0
  38. package/dist/components/RibbonToolbar.d.ts.map +1 -0
  39. package/dist/components/RibbonToolbar.js +179 -0
  40. package/dist/components/RibbonToolbar.js.map +1 -0
  41. package/dist/components/TextFormatEditor.d.ts +13 -0
  42. package/dist/components/TextFormatEditor.d.ts.map +1 -0
  43. package/dist/components/TextFormatEditor.js +199 -0
  44. package/dist/components/TextFormatEditor.js.map +1 -0
  45. package/dist/components/TextStyleLibraryDialog.d.ts +8 -0
  46. package/dist/components/TextStyleLibraryDialog.d.ts.map +1 -0
  47. package/dist/components/TextStyleLibraryDialog.js +367 -0
  48. package/dist/components/TextStyleLibraryDialog.js.map +1 -0
  49. package/dist/components/canvas/DesignerCanvasFrame.d.ts +10 -0
  50. package/dist/components/canvas/DesignerCanvasFrame.d.ts.map +1 -0
  51. package/dist/components/canvas/DesignerCanvasFrame.js +22 -0
  52. package/dist/components/canvas/DesignerCanvasFrame.js.map +1 -0
  53. package/dist/components/chart/ChartAxesPanel.d.ts +12 -0
  54. package/dist/components/chart/ChartAxesPanel.d.ts.map +1 -0
  55. package/dist/components/chart/ChartAxesPanel.js +71 -0
  56. package/dist/components/chart/ChartAxesPanel.js.map +1 -0
  57. package/dist/components/chart/ChartDataPanel.d.ts +21 -0
  58. package/dist/components/chart/ChartDataPanel.d.ts.map +1 -0
  59. package/dist/components/chart/ChartDataPanel.js +80 -0
  60. package/dist/components/chart/ChartDataPanel.js.map +1 -0
  61. package/dist/components/chart/ChartLabelPanel.d.ts +12 -0
  62. package/dist/components/chart/ChartLabelPanel.d.ts.map +1 -0
  63. package/dist/components/chart/ChartLabelPanel.js +34 -0
  64. package/dist/components/chart/ChartLabelPanel.js.map +1 -0
  65. package/dist/components/chart/ChartLegendPanel.d.ts +12 -0
  66. package/dist/components/chart/ChartLegendPanel.d.ts.map +1 -0
  67. package/dist/components/chart/ChartLegendPanel.js +48 -0
  68. package/dist/components/chart/ChartLegendPanel.js.map +1 -0
  69. package/dist/components/chart/ChartPropertyPanel.d.ts +26 -0
  70. package/dist/components/chart/ChartPropertyPanel.d.ts.map +1 -0
  71. package/dist/components/chart/ChartPropertyPanel.js +119 -0
  72. package/dist/components/chart/ChartPropertyPanel.js.map +1 -0
  73. package/dist/components/chart/ChartThemePanel.d.ts +9 -0
  74. package/dist/components/chart/ChartThemePanel.d.ts.map +1 -0
  75. package/dist/components/chart/ChartThemePanel.js +21 -0
  76. package/dist/components/chart/ChartThemePanel.js.map +1 -0
  77. package/dist/components/chart/ChartTitlePanel.d.ts +10 -0
  78. package/dist/components/chart/ChartTitlePanel.d.ts.map +1 -0
  79. package/dist/components/chart/ChartTitlePanel.js +45 -0
  80. package/dist/components/chart/ChartTitlePanel.js.map +1 -0
  81. package/dist/components/chart/ChartTypeStylePanel.d.ts +11 -0
  82. package/dist/components/chart/ChartTypeStylePanel.d.ts.map +1 -0
  83. package/dist/components/chart/ChartTypeStylePanel.js +37 -0
  84. package/dist/components/chart/ChartTypeStylePanel.js.map +1 -0
  85. package/dist/components/chart/ColorPaletteEditor.d.ts +10 -0
  86. package/dist/components/chart/ColorPaletteEditor.d.ts.map +1 -0
  87. package/dist/components/chart/ColorPaletteEditor.js +18 -0
  88. package/dist/components/chart/ColorPaletteEditor.js.map +1 -0
  89. package/dist/components/chart/chart-options.d.ts +119 -0
  90. package/dist/components/chart/chart-options.d.ts.map +1 -0
  91. package/dist/components/chart/chart-options.js +217 -0
  92. package/dist/components/chart/chart-options.js.map +1 -0
  93. package/dist/components/dialogs/BandWizardDialog.d.ts +8 -0
  94. package/dist/components/dialogs/BandWizardDialog.d.ts.map +1 -0
  95. package/dist/components/dialogs/BandWizardDialog.js +54 -0
  96. package/dist/components/dialogs/BandWizardDialog.js.map +1 -0
  97. package/dist/components/dialogs/GroupWizardDialog.d.ts +8 -0
  98. package/dist/components/dialogs/GroupWizardDialog.d.ts.map +1 -0
  99. package/dist/components/dialogs/GroupWizardDialog.js +70 -0
  100. package/dist/components/dialogs/GroupWizardDialog.js.map +1 -0
  101. package/dist/components/dialogs/JsonDataSourceDialog.d.ts +8 -0
  102. package/dist/components/dialogs/JsonDataSourceDialog.d.ts.map +1 -0
  103. package/dist/components/dialogs/JsonDataSourceDialog.js +67 -0
  104. package/dist/components/dialogs/JsonDataSourceDialog.js.map +1 -0
  105. package/dist/components/dialogs/PageSetupDialog.d.ts +8 -0
  106. package/dist/components/dialogs/PageSetupDialog.d.ts.map +1 -0
  107. package/dist/components/dialogs/PageSetupDialog.js +145 -0
  108. package/dist/components/dialogs/PageSetupDialog.js.map +1 -0
  109. package/dist/components/dialogs/dialog-utils.d.ts +6 -0
  110. package/dist/components/dialogs/dialog-utils.d.ts.map +1 -0
  111. package/dist/components/dialogs/dialog-utils.js +37 -0
  112. package/dist/components/dialogs/dialog-utils.js.map +1 -0
  113. package/dist/components/events/EventEditorDialog.d.ts +43 -0
  114. package/dist/components/events/EventEditorDialog.d.ts.map +1 -0
  115. package/dist/components/events/EventEditorDialog.js +271 -0
  116. package/dist/components/events/EventEditorDialog.js.map +1 -0
  117. package/dist/components/events/EventScriptEditor.d.ts +41 -0
  118. package/dist/components/events/EventScriptEditor.d.ts.map +1 -0
  119. package/dist/components/events/EventScriptEditor.js +140 -0
  120. package/dist/components/events/EventScriptEditor.js.map +1 -0
  121. package/dist/components/events/event-editor-utils.d.ts +18 -0
  122. package/dist/components/events/event-editor-utils.d.ts.map +1 -0
  123. package/dist/components/events/event-editor-utils.js +66 -0
  124. package/dist/components/events/event-editor-utils.js.map +1 -0
  125. package/dist/components/events/event-script-monaco.d.ts +74 -0
  126. package/dist/components/events/event-script-monaco.d.ts.map +1 -0
  127. package/dist/components/events/event-script-monaco.js +282 -0
  128. package/dist/components/events/event-script-monaco.js.map +1 -0
  129. package/dist/components/events/event-script-templates.d.ts +7 -0
  130. package/dist/components/events/event-script-templates.d.ts.map +1 -0
  131. package/dist/components/events/event-script-templates.js +100 -0
  132. package/dist/components/events/event-script-templates.js.map +1 -0
  133. package/dist/components/expression/ExpressionMonacoEditor.d.ts +16 -0
  134. package/dist/components/expression/ExpressionMonacoEditor.d.ts.map +1 -0
  135. package/dist/components/expression/ExpressionMonacoEditor.js +63 -0
  136. package/dist/components/expression/ExpressionMonacoEditor.js.map +1 -0
  137. package/dist/components/expression/InlineExpressionEditor.d.ts +10 -0
  138. package/dist/components/expression/InlineExpressionEditor.d.ts.map +1 -0
  139. package/dist/components/expression/InlineExpressionEditor.js +33 -0
  140. package/dist/components/expression/InlineExpressionEditor.js.map +1 -0
  141. package/dist/components/expression/expression-monaco.d.ts +34 -0
  142. package/dist/components/expression/expression-monaco.d.ts.map +1 -0
  143. package/dist/components/expression/expression-monaco.js +87 -0
  144. package/dist/components/expression/expression-monaco.js.map +1 -0
  145. package/dist/components/panels/DesignerLeftPanel.d.ts +11 -0
  146. package/dist/components/panels/DesignerLeftPanel.d.ts.map +1 -0
  147. package/dist/components/panels/DesignerLeftPanel.js +8 -0
  148. package/dist/components/panels/DesignerLeftPanel.js.map +1 -0
  149. package/dist/components/panels/DesignerPropertyPanel.d.ts +6 -0
  150. package/dist/components/panels/DesignerPropertyPanel.d.ts.map +1 -0
  151. package/dist/components/panels/DesignerPropertyPanel.js +441 -0
  152. package/dist/components/panels/DesignerPropertyPanel.js.map +1 -0
  153. package/dist/components/panels/PanelSearchBox.d.ts +9 -0
  154. package/dist/components/panels/PanelSearchBox.d.ts.map +1 -0
  155. package/dist/components/panels/PanelSearchBox.js +5 -0
  156. package/dist/components/panels/PanelSearchBox.js.map +1 -0
  157. package/dist/components/properties/BandPropertyGrid.d.ts +6 -0
  158. package/dist/components/properties/BandPropertyGrid.d.ts.map +1 -0
  159. package/dist/components/properties/BandPropertyGrid.js +504 -0
  160. package/dist/components/properties/BandPropertyGrid.js.map +1 -0
  161. package/dist/components/properties/BoxStyleEditors.d.ts +59 -0
  162. package/dist/components/properties/BoxStyleEditors.d.ts.map +1 -0
  163. package/dist/components/properties/BoxStyleEditors.js +87 -0
  164. package/dist/components/properties/BoxStyleEditors.js.map +1 -0
  165. package/dist/components/properties/FontEditor.d.ts +28 -0
  166. package/dist/components/properties/FontEditor.d.ts.map +1 -0
  167. package/dist/components/properties/FontEditor.js +22 -0
  168. package/dist/components/properties/FontEditor.js.map +1 -0
  169. package/dist/components/ribbon/DesignerRibbon.d.ts +3 -0
  170. package/dist/components/ribbon/DesignerRibbon.d.ts.map +1 -0
  171. package/dist/components/ribbon/DesignerRibbon.js +193 -0
  172. package/dist/components/ribbon/DesignerRibbon.js.map +1 -0
  173. package/dist/components/richtext/RichTextInlineEditor.d.ts +15 -0
  174. package/dist/components/richtext/RichTextInlineEditor.d.ts.map +1 -0
  175. package/dist/components/richtext/RichTextInlineEditor.js +94 -0
  176. package/dist/components/richtext/RichTextInlineEditor.js.map +1 -0
  177. package/dist/components/shell/DesignerShell.d.ts +12 -0
  178. package/dist/components/shell/DesignerShell.d.ts.map +1 -0
  179. package/dist/components/shell/DesignerShell.js +124 -0
  180. package/dist/components/shell/DesignerShell.js.map +1 -0
  181. package/dist/components/shell/DesignerStatusBar.d.ts +3 -0
  182. package/dist/components/shell/DesignerStatusBar.d.ts.map +1 -0
  183. package/dist/components/shell/DesignerStatusBar.js +23 -0
  184. package/dist/components/shell/DesignerStatusBar.js.map +1 -0
  185. package/dist/components/tree/ReportTree.d.ts +3 -0
  186. package/dist/components/tree/ReportTree.d.ts.map +1 -0
  187. package/dist/components/tree/ReportTree.js +17 -0
  188. package/dist/components/tree/ReportTree.js.map +1 -0
  189. package/dist/data-source-fields.d.ts +14 -0
  190. package/dist/data-source-fields.d.ts.map +1 -0
  191. package/dist/data-source-fields.js +49 -0
  192. package/dist/data-source-fields.js.map +1 -0
  193. package/dist/data-source-paths.d.ts +10 -0
  194. package/dist/data-source-paths.d.ts.map +1 -0
  195. package/dist/data-source-paths.js +61 -0
  196. package/dist/data-source-paths.js.map +1 -0
  197. package/dist/expression/expression-catalog.d.ts +39 -0
  198. package/dist/expression/expression-catalog.d.ts.map +1 -0
  199. package/dist/expression/expression-catalog.js +127 -0
  200. package/dist/expression/expression-catalog.js.map +1 -0
  201. package/dist/expression/expression-preview.d.ts +11 -0
  202. package/dist/expression/expression-preview.d.ts.map +1 -0
  203. package/dist/expression/expression-preview.js +58 -0
  204. package/dist/expression/expression-preview.js.map +1 -0
  205. package/dist/expression/expression-validation.d.ts +12 -0
  206. package/dist/expression/expression-validation.d.ts.map +1 -0
  207. package/dist/expression/expression-validation.js +85 -0
  208. package/dist/expression/expression-validation.js.map +1 -0
  209. package/dist/expression/function-catalog.d.ts +21 -0
  210. package/dist/expression/function-catalog.d.ts.map +1 -0
  211. package/dist/expression/function-catalog.js +96 -0
  212. package/dist/expression/function-catalog.js.map +1 -0
  213. package/dist/i18n/DesignerI18nProvider.d.ts +11 -0
  214. package/dist/i18n/DesignerI18nProvider.d.ts.map +1 -0
  215. package/dist/i18n/DesignerI18nProvider.js +32 -0
  216. package/dist/i18n/DesignerI18nProvider.js.map +1 -0
  217. package/dist/i18n/index.d.ts +3 -0
  218. package/dist/i18n/index.d.ts.map +1 -0
  219. package/dist/i18n/index.js +2 -0
  220. package/dist/i18n/index.js.map +1 -0
  221. package/dist/i18n/messages.d.ts +5 -0
  222. package/dist/i18n/messages.d.ts.map +1 -0
  223. package/dist/i18n/messages.js +1383 -0
  224. package/dist/i18n/messages.js.map +1 -0
  225. package/dist/index.d.ts +21 -0
  226. package/dist/index.d.ts.map +1 -0
  227. package/dist/index.js +18 -0
  228. package/dist/index.js.map +1 -0
  229. package/dist/page-settings.d.ts +21 -0
  230. package/dist/page-settings.d.ts.map +1 -0
  231. package/dist/page-settings.js +66 -0
  232. package/dist/page-settings.js.map +1 -0
  233. package/dist/report-structure.d.ts +20 -0
  234. package/dist/report-structure.d.ts.map +1 -0
  235. package/dist/report-structure.js +219 -0
  236. package/dist/report-structure.js.map +1 -0
  237. package/dist/store/designer-store.d.ts +161 -0
  238. package/dist/store/designer-store.d.ts.map +1 -0
  239. package/dist/store/designer-store.js +1851 -0
  240. package/dist/store/designer-store.js.map +1 -0
  241. package/dist/table/table-structure.d.ts +50 -0
  242. package/dist/table/table-structure.d.ts.map +1 -0
  243. package/dist/table/table-structure.js +251 -0
  244. package/dist/table/table-structure.js.map +1 -0
  245. package/dist/text-style-application.d.ts +11 -0
  246. package/dist/text-style-application.d.ts.map +1 -0
  247. package/dist/text-style-application.js +135 -0
  248. package/dist/text-style-application.js.map +1 -0
  249. package/dist/text-style-bindings.d.ts +16 -0
  250. package/dist/text-style-bindings.d.ts.map +1 -0
  251. package/dist/text-style-bindings.js +549 -0
  252. package/dist/text-style-bindings.js.map +1 -0
  253. package/package.json +70 -0
@@ -0,0 +1,1002 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import React, { useMemo, useState } from 'react';
3
+ import { Form, Input, InputNumber, Select, Switch, ColorPicker, Collapse, Space, Button, Divider, Typography, Tooltip } from 'antd';
4
+ import { AlignCenterOutlined, AlignLeftOutlined, AlignRightOutlined, EditOutlined, DisconnectOutlined, UploadOutlined, } from '@ant-design/icons';
5
+ import { useDesignerStore } from '../store/designer-store';
6
+ import { getReportFontOptions, supportsComponentStyle } from '@report-designer/core';
7
+ import { formatUnitValue, getUnitStep, parseUnitValue } from '../page-settings';
8
+ import { ExpressionEditor } from './ExpressionEditor';
9
+ import { normalizeTable } from '../table/table-structure';
10
+ import { isTextStylePropertyLocked } from '../text-style-application';
11
+ import { TextFormatEditor } from './TextFormatEditor';
12
+ import { useDesignerI18n } from '../i18n';
13
+ import { EventEditorDialog } from './events/EventEditorDialog';
14
+ import { buildEventEditorDataContext } from './events/event-editor-utils';
15
+ import { BorderEditor, PaddingEditor } from './properties/BoxStyleEditors';
16
+ import { FontEditor } from './properties/FontEditor';
17
+ import { BARCODE_FORMATS, QR_CODE_FORMATS } from '@report-designer/viewer';
18
+ import { isComponentNameAvailable, normalizeComponentName } from '../report-structure';
19
+ import { createArrayPathOptions } from '../data-source-paths';
20
+ import { buildChartPropertyItems } from './chart/ChartPropertyPanel';
21
+ const NO_CONDITIONAL_FORMAT = '__none__';
22
+ const EMPTY_DATA_PATHS = [];
23
+ export const PropertyEditor = ({ expressionExtensions }) => {
24
+ const { locale, t: globalT } = useDesignerI18n();
25
+ const t = React.useMemo(() => createPropertyT(locale), [locale]);
26
+ const template = useDesignerStore(s => s.template);
27
+ const currentPageId = useDesignerStore(s => s.currentPageId);
28
+ const selectedComponentIds = useDesignerStore(s => s.selectedComponentIds);
29
+ const updateComponent = useDesignerStore(s => s.updateComponent);
30
+ const updateSelectedTable = useDesignerStore(s => s.updateSelectedTable);
31
+ const applySelectedStyle = useDesignerStore(s => s.applySelectedStyle);
32
+ const unbindSelectedStyle = useDesignerStore(s => s.unbindSelectedStyle);
33
+ const applySelectedConditionalFormat = useDesignerStore(s => s.applySelectedConditionalFormat);
34
+ const replaceComponentEvents = useDesignerStore(s => s.replaceComponentEvents);
35
+ const openConditionalFormatLibrary = useDesignerStore(s => s.openConditionalFormatLibrary);
36
+ const reportUnit = useDesignerStore(s => s.reportUnit);
37
+ const pendingEventEditorTarget = useDesignerStore(s => s.pendingEventEditorTarget);
38
+ const consumeEventEditorTarget = useDesignerStore(s => s.consumeEventEditorTarget);
39
+ const runtimeDataSources = useDesignerStore(s => s.dataSources);
40
+ const [eventEditorDataTemplate, setEventEditorDataTemplate] = React.useState(null);
41
+ const { component, bandId } = useMemo(() => {
42
+ if (selectedComponentIds.length !== 1)
43
+ return { component: null, bandId: null };
44
+ const page = template.pages.find(p => p.id === currentPageId);
45
+ if (!page)
46
+ return { component: null, bandId: null };
47
+ for (const band of page.bands) {
48
+ const comp = findComponentInTree(band.components, selectedComponentIds[0]);
49
+ if (comp)
50
+ return { component: comp, bandId: band.id };
51
+ }
52
+ return { component: null, bandId: null };
53
+ }, [template, currentPageId, selectedComponentIds]);
54
+ const componentId = component?.id;
55
+ const [expressionTarget, setExpressionTarget] = useState(null);
56
+ const [eventEditorOpen, setEventEditorOpen] = useState(false);
57
+ const [eventEditorTarget, setEventEditorTarget] = useState(null);
58
+ const [componentNameError, setComponentNameError] = useState(null);
59
+ const unitStep = getUnitStep(reportUnit);
60
+ const fineUnitStep = getUnitStep(reportUnit, 'fine');
61
+ const pendingTarget = pendingEventEditorTarget?.ownerType === 'component' && pendingEventEditorTarget.ownerId === component?.id
62
+ ? pendingEventEditorTarget
63
+ : null;
64
+ React.useEffect(() => {
65
+ if (!pendingTarget)
66
+ return;
67
+ setEventEditorTarget(pendingTarget);
68
+ setEventEditorOpen(true);
69
+ consumeEventEditorTarget(pendingTarget.requestId);
70
+ }, [consumeEventEditorTarget, pendingTarget]);
71
+ React.useEffect(() => {
72
+ setComponentNameError(null);
73
+ }, [component?.id]);
74
+ React.useEffect(() => {
75
+ if (!eventEditorOpen)
76
+ return;
77
+ setEventEditorDataTemplate(template);
78
+ }, [eventEditorOpen, template]);
79
+ const dictionaryItems = React.useMemo(() => (eventEditorOpen && eventEditorDataTemplate ? buildDictionaryEventItems(eventEditorDataTemplate) : []), [eventEditorDataTemplate, eventEditorOpen]);
80
+ const componentItems = React.useMemo(() => (eventEditorOpen && eventEditorDataTemplate ? buildComponentEventItems(eventEditorDataTemplate) : []), [eventEditorDataTemplate, eventEditorOpen]);
81
+ const currentPage = React.useMemo(() => template.pages.find(p => p.id === currentPageId), [currentPageId, template.pages]);
82
+ const bandDataPathSignature = React.useMemo(() => currentPage?.bands.map(b => b.dataBand?.dataSourceId ?? b.dataSource ?? '').join('|') ?? '', [currentPage?.bands]);
83
+ const bandDataPaths = React.useMemo(() => currentPage?.bands.reduce((acc, b) => {
84
+ const dataSourceId = b.dataBand?.dataSourceId ?? b.dataSource;
85
+ if (dataSourceId && !acc.includes(dataSourceId))
86
+ acc.push(dataSourceId);
87
+ return acc;
88
+ }, []) ?? EMPTY_DATA_PATHS, [bandDataPathSignature]);
89
+ const dataSourceOptions = React.useMemo(() => createArrayPathOptions(template.dataSources, runtimeDataSources, bandDataPaths), [template.dataSources, runtimeDataSources, bandDataPaths]);
90
+ const reportFontOptions = React.useMemo(() => getReportFontOptions(template.fonts), [template.fonts]);
91
+ const handleChange = React.useCallback((field, value) => {
92
+ if (!componentId || !bandId || !currentPageId)
93
+ return;
94
+ const currentBand = useDesignerStore.getState().template.pages
95
+ .find(p => p.id === currentPageId)
96
+ ?.bands.find(b => b.id === bandId);
97
+ const currentComponent = currentBand ? findComponentInTree(currentBand.components, componentId) : null;
98
+ updateComponent(currentPageId, bandId, componentId, { [field]: value }, { [field]: currentComponent?.[field] });
99
+ }, [bandId, componentId, currentPageId, updateComponent]);
100
+ const handleChangeMany = React.useCallback((updates) => {
101
+ if (!componentId || !bandId || !currentPageId)
102
+ return;
103
+ const currentBand = useDesignerStore.getState().template.pages
104
+ .find(p => p.id === currentPageId)
105
+ ?.bands.find(b => b.id === bandId);
106
+ const currentComponent = currentBand ? findComponentInTree(currentBand.components, componentId) : null;
107
+ const previous = Object.fromEntries(Object.keys(updates).map(field => [field, currentComponent?.[field]]));
108
+ updateComponent(currentPageId, bandId, componentId, updates, previous);
109
+ }, [bandId, componentId, currentPageId, updateComponent]);
110
+ if (selectedComponentIds.length === 0) {
111
+ return (_jsx("div", { style: { padding: 16, textAlign: 'center', color: '#999', fontSize: 13 }, children: t('selectComponent') }));
112
+ }
113
+ if (selectedComponentIds.length > 1) {
114
+ return (_jsx("div", { style: { padding: 16, textAlign: 'center', color: '#999', fontSize: 13 }, children: t('selectedComponents', { count: selectedComponentIds.length }) }));
115
+ }
116
+ if (!component || !bandId)
117
+ return null;
118
+ const comp = component;
119
+ const handleNameChange = (value) => {
120
+ const name = normalizeComponentName(value);
121
+ if (!name) {
122
+ setComponentNameError(t('componentNameRequired'));
123
+ return;
124
+ }
125
+ if (name && !isComponentNameAvailable(template, name, component.id)) {
126
+ setComponentNameError(t('componentNameDuplicate'));
127
+ return;
128
+ }
129
+ setComponentNameError(null);
130
+ handleChange('name', value);
131
+ };
132
+ const openFieldExpressionEditor = (field, label) => {
133
+ setExpressionTarget({ field, label });
134
+ };
135
+ // ---- Border helpers ----
136
+ const border = normalizeOptionalBorder(comp.border);
137
+ // ---- Font helpers ----
138
+ const font = comp.font ?? { family: '', size: 12, bold: false, italic: false, underline: false, strikethrough: false, color: '#000000' };
139
+ const handleFontField = (field, value) => {
140
+ handleChange('font', { ...font, [field]: value });
141
+ };
142
+ const format = (comp.format ?? { type: 'none' });
143
+ // ---- Padding helpers ----
144
+ const padding = normalizeOptionalPadding(comp.padding);
145
+ const supportsSharedStyle = supportsComponentStyle(component);
146
+ const supportsFontProperties = ['text', 'barcode', 'checkbox', 'pagenumber', 'datetime', 'table'].includes(component.type);
147
+ const supportsBorderProperties = ['text', 'image', 'chart', 'barcode', 'qrcode', 'checkbox', 'panel', 'pagenumber', 'datetime', 'table'].includes(component.type);
148
+ const supportsAppearanceProperties = ['text', 'image', 'chart', 'barcode', 'qrcode', 'checkbox', 'richtext', 'panel', 'subreport', 'pagenumber', 'datetime', 'table'].includes(component.type);
149
+ const supportsForegroundColor = component.type === 'barcode' || component.type === 'qrcode' || component.type === 'checkbox';
150
+ const isTextStyleLocked = (pathOrPrefix) => (supportsSharedStyle ? isTextStylePropertyLocked(component, pathOrPrefix) : false);
151
+ const backgroundLocked = isTextStyleLocked('backgroundColor');
152
+ return (_jsxs("div", { style: { padding: 8 }, children: [_jsx(Collapse, { defaultActiveKey: ['general', 'position', 'behavior', 'text', 'font', 'border', 'appearance', 'events', 'table', 'line', 'shape', 'pagenumber', 'datetime', 'chartBasic', 'chartData'], size: "small", items: [
153
+ // ---- 基本信息 ----
154
+ {
155
+ key: 'general',
156
+ label: t('general'),
157
+ children: (_jsxs(Form, { layout: "horizontal", size: "small", labelCol: { span: 8 }, wrapperCol: { span: 16 }, children: [_jsx(Form.Item, { label: t('name'), validateStatus: componentNameError ? 'error' : undefined, help: componentNameError, children: _jsx(Input, { "aria-label": t('name'), value: comp.name || '', onChange: (e) => handleNameChange(e.target.value), size: "small", placeholder: t('componentName') }) }), _jsx(Form.Item, { label: t('type'), children: _jsx(Input, { "aria-label": t('type'), value: comp.type, size: "small", disabled: true }) }), supportsSharedStyle ? (_jsx(Form.Item, { label: t('textStyle'), children: _jsxs(Space.Compact, { "data-testid": "text-style-select-row", style: { width: '100%', minWidth: 0 }, children: [_jsx(Select, { "data-testid": "text-style-select", "aria-label": t('textStyle'), value: comp.style, onChange: (value) => (value ? applySelectedStyle(value) : unbindSelectedStyle()), onClear: () => unbindSelectedStyle(), size: "small", style: { flex: 1, width: '100%', minWidth: 0 }, allowClear: true, virtual: false, placeholder: t('chooseStyle'), options: template.styles.map(style => ({ value: style.id, label: style.name })) }), _jsx(Tooltip, { title: t('unbindStyle'), children: _jsx(Button, { "aria-label": t('unbindStyle'), icon: _jsx(DisconnectOutlined, {}), onClick: unbindSelectedStyle, disabled: !comp.style, size: "small", style: { width: 32, flex: '0 0 32px' } }) })] }) })) : null] })),
158
+ },
159
+ // ---- 位置尺寸 ----
160
+ {
161
+ key: 'position',
162
+ label: t('position'),
163
+ children: (_jsxs(Form, { layout: "horizontal", size: "small", labelCol: { span: 8 }, wrapperCol: { span: 16 }, children: [_jsx(Form.Item, { label: "X", children: _jsx(InputNumber, { "aria-label": "X", value: formatUnitValue(comp.x, reportUnit), onChange: (v) => handleChange('x', parseUnitValue(v, reportUnit, comp.x)), size: "small", style: { width: '100%' }, step: unitStep }) }), _jsx(Form.Item, { label: "Y", children: _jsx(InputNumber, { "aria-label": "Y", value: formatUnitValue(comp.y, reportUnit), onChange: (v) => handleChange('y', parseUnitValue(v, reportUnit, comp.y)), size: "small", style: { width: '100%' }, step: unitStep }) }), _jsx(Form.Item, { label: t('width'), children: _jsx(InputNumber, { "aria-label": t('width'), value: formatUnitValue(comp.width, reportUnit), onChange: (v) => handleChange('width', parseUnitValue(v, reportUnit, comp.width)), size: "small", style: { width: '100%' }, step: unitStep, min: formatUnitValue(1, reportUnit) }) }), _jsx(Form.Item, { label: t('height'), children: _jsx(InputNumber, { "aria-label": t('height'), value: formatUnitValue(comp.height, reportUnit), onChange: (v) => handleChange('height', parseUnitValue(v, reportUnit, comp.height)), size: "small", style: { width: '100%' }, step: unitStep, min: formatUnitValue(1, reportUnit) }) })] })),
164
+ },
165
+ {
166
+ key: 'behavior',
167
+ label: t('behavior'),
168
+ children: (_jsxs(Form, { layout: "horizontal", size: "small", labelCol: { span: 8 }, wrapperCol: { span: 16 }, children: [_jsx(Form.Item, { label: t('visibleExpression'), children: _jsxs(Space.Compact, { style: { width: '100%' }, children: [_jsx(Input, { "aria-label": t('visibleExpression'), value: comp.visible || '', onChange: (event) => handleChange('visible', event.target.value), size: "small", placeholder: t('visibleExpressionPlaceholder') }), _jsx(Button, { "aria-label": t('openExpressionEditorFor', { field: t('visibleExpression') }), title: t('openExpressionEditorFor', { field: t('visibleExpression') }), icon: _jsx(EditOutlined, {}), onClick: () => openFieldExpressionEditor('visible', t('visibleExpression')), style: { width: 32 } })] }) }), _jsx(Form.Item, { label: t('enabledExpression'), children: _jsxs(Space.Compact, { style: { width: '100%' }, children: [_jsx(Input, { "aria-label": t('enabledExpression'), value: comp.enabledExpression || '', onChange: (event) => handleChange('enabledExpression', event.target.value), size: "small", placeholder: t('enabledExpressionPlaceholder') }), _jsx(Button, { "aria-label": t('openExpressionEditorFor', { field: t('enabledExpression') }), title: t('openExpressionEditorFor', { field: t('enabledExpression') }), icon: _jsx(EditOutlined, {}), onClick: () => openFieldExpressionEditor('enabledExpression', t('enabledExpression')), style: { width: 32 } })] }) }), _jsx(Form.Item, { label: t('printableExpression'), children: _jsxs(Space.Compact, { style: { width: '100%' }, children: [_jsx(Input, { "aria-label": t('printableExpression'), value: comp.printableExpression || '', onChange: (event) => handleChange('printableExpression', event.target.value), size: "small", placeholder: t('printableExpressionPlaceholder') }), _jsx(Button, { "aria-label": t('openExpressionEditorFor', { field: t('printableExpression') }), title: t('openExpressionEditorFor', { field: t('printableExpression') }), icon: _jsx(EditOutlined, {}), onClick: () => openFieldExpressionEditor('printableExpression', t('printableExpression')), style: { width: 32 } })] }) })] })),
169
+ },
170
+ // ---- 文本内容(图表不走此组,由下方 chartItems spread)----
171
+ component.type !== 'chart' ? {
172
+ key: 'text',
173
+ label: t('text'),
174
+ children: component.type === 'text' ? (_jsxs(Form, { size: "small", labelCol: { span: 6 }, wrapperCol: { span: 18 }, children: [_jsx(Form.Item, { label: t('textContent'), children: _jsxs(Space.Compact, { style: { width: '100%' }, children: [_jsx(Input, { "aria-label": t('textContent'), value: comp.text || '', onChange: (e) => handleChange('text', e.target.value), size: "small", placeholder: t('textContentPlaceholder') }), _jsx(Button, { "aria-label": t('openExpressionEditorFor', { field: t('textContent') }), title: t('openExpressionEditorFor', { field: t('textContent') }), icon: _jsx(EditOutlined, {}), onClick: () => openFieldExpressionEditor('text', t('textContent')), style: { width: 32 } })] }) }), _jsx(Form.Item, { label: t('conditionalFormat'), children: _jsxs(Space.Compact, { "data-testid": "conditional-format-select-row", style: { width: '100%', minWidth: 0 }, children: [_jsx(Select, { "data-testid": "conditional-format-select", "aria-label": t('conditionalFormat'), value: comp.conditionalFormat ?? undefined, onChange: (value) => applySelectedConditionalFormat(value === NO_CONDITIONAL_FORMAT ? undefined : value), size: "small", style: { flex: 1, width: '100%', minWidth: 0 }, virtual: false, placeholder: t('chooseConditionalFormat'), options: [
175
+ { value: NO_CONDITIONAL_FORMAT, label: t('none') },
176
+ ...template.conditionalFormats.map(format => ({ value: format.id, label: format.name })),
177
+ ] }), _jsx(Tooltip, { title: t('manage'), children: _jsx(Button, { "aria-label": t('manage'), icon: _jsx(EditOutlined, {}), onClick: openConditionalFormatLibrary, size: "small", style: { width: 32, flex: '0 0 32px' } }) })] }) }), _jsx(Form.Item, { wrapperCol: { span: 24 }, style: { marginBottom: 8 }, children: _jsx("div", { "data-testid": "property-format-editor-full-width", style: { width: '100%', minWidth: 0 }, children: _jsx(TextFormatEditor, { value: format, onChange: (nextFormat) => handleChange('format', nextFormat), isFieldDisabled: isTextStyleLocked, labelWidth: 88, size: "small" }) }) }), _jsx(Form.Item, { label: t('horizontalAlign'), children: _jsx(HorizontalAlignButtons, { value: comp.textAlign || 'left', onChange: (value) => handleChange('textAlign', value), disabled: isTextStyleLocked('textAlign'), labels: {
178
+ left: t('alignLeft'),
179
+ center: t('alignCenter'),
180
+ right: t('alignRight'),
181
+ } }) }), _jsx(Form.Item, { label: t('verticalAlign'), children: _jsx(VerticalAlignButtons, { value: comp.verticalAlign || 'top', onChange: (value) => handleChange('verticalAlign', value), disabled: isTextStyleLocked('verticalAlign'), labels: {
182
+ top: t('verticalTop'),
183
+ middle: t('verticalMiddle'),
184
+ bottom: t('verticalBottom'),
185
+ } }) }), _jsx(Form.Item, { label: t('canGrow'), children: _jsx(Switch, { "aria-label": t('canGrow'), size: "small", checked: comp.canGrow || false, onChange: (v) => handleChange('canGrow', v), disabled: isTextStyleLocked('canGrow') }) }), _jsx(Form.Item, { label: t('canShrink'), children: _jsx(Switch, { "aria-label": t('canShrink'), size: "small", checked: comp.canShrink || false, onChange: (v) => handleChange('canShrink', v), disabled: isTextStyleLocked('canShrink') }) })] })) : (_jsx(ComponentContentProperties, { component: component, comp: comp, onChange: handleChange, onChangeMany: handleChangeMany, onOpenExpressionEditor: openFieldExpressionEditor, t: t, dataSourceOptions: dataSourceOptions, dataSourceDefinitions: template.dataSources })),
186
+ } : null,
187
+ // ---- 图表属性(扁平展开,取消原 chart 包装层)----
188
+ ...(component.type === 'chart' ? buildChartPropertyItems({
189
+ chart: comp,
190
+ dataSourceOptions,
191
+ dataSourceDefinitions: template.dataSources,
192
+ reportFontOptions,
193
+ onChange: handleChange,
194
+ onChangeMany: handleChangeMany,
195
+ t,
196
+ }) : []),
197
+ // ---- 字体 ----
198
+ supportsFontProperties ? {
199
+ key: 'font',
200
+ label: t('font'),
201
+ children: (_jsx(FontEditor, { value: font, onChange: (next) => handleChange('font', next), reportFontOptions: reportFontOptions, sizeRange: [6, 72], disabled: (field) => isTextStyleLocked(`font.${field}`), labels: {
202
+ fontFamily: t('fontFamily'),
203
+ fontSize: t('fontSize'),
204
+ textColor: t('textColor'),
205
+ bold: t('bold'),
206
+ italic: t('italic'),
207
+ underline: t('underline'),
208
+ strike: t('strike'),
209
+ } })),
210
+ } : null,
211
+ // ---- 边框 ----
212
+ supportsBorderProperties ? {
213
+ key: 'border',
214
+ label: t('border'),
215
+ children: (_jsx(Form, { size: "small", labelCol: { span: 8 }, wrapperCol: { span: 16 }, children: _jsx(BorderEditor, { value: border, labels: propertyBorderLabels(t), onChange: nextBorder => handleChange('border', nextBorder), disabled: isTextStyleLocked, formatWidth: value => formatUnitValue(value, reportUnit), parseWidth: (value, fallback) => parseUnitValue(value, reportUnit, fallback), minWidth: formatUnitValue(0.1, reportUnit), maxWidth: formatUnitValue(5, reportUnit), step: fineUnitStep }) })),
216
+ } : null,
217
+ // ---- 外观 ----
218
+ supportsAppearanceProperties ? {
219
+ key: 'appearance',
220
+ label: t('appearance'),
221
+ children: (_jsxs(Form, { layout: "horizontal", size: "small", labelCol: { span: 8 }, wrapperCol: { span: 16 }, children: [supportsForegroundColor ? (_jsx(Form.Item, { label: t('foregroundColor'), children: _jsxs(Space.Compact, { style: { width: '100%' }, children: [_jsx(ColorPicker, { "aria-label": t('foregroundColorPicker'), size: "small", value: comp.foregroundColor || '#000000', onChange: (color) => handleChange('foregroundColor', color.toHexString()) }), _jsx(Input, { "aria-label": t('foregroundColor'), value: comp.foregroundColor || '#000000', onChange: (event) => handleChange('foregroundColor', event.target.value), size: "small", style: { width: '100%' }, placeholder: "#000000" })] }) })) : null, _jsx(Form.Item, { label: t('backgroundColor'), children: _jsx(ColorPicker, { "aria-label": t('backgroundColor'), size: "small", value: comp.backgroundColor || '#ffffff', onChange: (color) => handleChange('backgroundColor', color.toHexString()), onClear: () => handleChange('backgroundColor', undefined), allowClear: true, disabled: backgroundLocked }) }), _jsx(Divider, { style: { margin: '4px 0' } }), _jsx(PaddingEditor, { value: padding, labels: propertyPaddingLabels(t), onChange: nextPadding => handleChange('padding', nextPadding), disabled: isTextStyleLocked, formatValue: value => formatUnitValue(value, reportUnit), parseValue: (value, fallback) => parseUnitValue(value, reportUnit, fallback), min: 0, step: unitStep })] })),
222
+ } : null,
223
+ // ---- 表格 ----
224
+ component.type === 'table' ? {
225
+ key: 'table',
226
+ label: t('table'),
227
+ children: (_jsx(TablePropertyPanel, { table: normalizeTable(component), onChange: updateSelectedTable, t: t })),
228
+ } : null,
229
+ // ---- 线条 ----
230
+ component.type === 'line' ? {
231
+ key: 'line',
232
+ label: t('line'),
233
+ children: (_jsxs(Form, { size: "small", labelCol: { span: 8 }, wrapperCol: { span: 16 }, children: [_jsx(Form.Item, { label: t('color'), children: _jsx(ColorPicker, { size: "small", value: comp.lineColor || '#000000', onChange: (color) => handleChange('lineColor', color.toHexString()) }) }), _jsx(Form.Item, { label: t('lineWidth'), children: _jsx(InputNumber, { value: formatUnitValue(comp.lineWidth ?? 0.2, reportUnit), onChange: (v) => handleChange('lineWidth', parseUnitValue(v, reportUnit, comp.lineWidth ?? 0.2)), size: "small", style: { width: '100%' }, min: formatUnitValue(0.1, reportUnit), max: formatUnitValue(5, reportUnit), step: fineUnitStep }) }), _jsx(Form.Item, { label: t('lineStyle'), children: _jsx(Select, { value: comp.lineStyle || 'solid', onChange: (v) => handleChange('lineStyle', v), size: "small", style: { width: '100%' }, options: [
234
+ { value: 'solid', label: t('borderSolid') },
235
+ { value: 'dashed', label: t('borderDashed') },
236
+ { value: 'dotted', label: t('borderDotted') },
237
+ ] }) }), _jsx(Divider, { style: { margin: '4px 0' } }), _jsx("div", { style: { fontSize: 12, color: '#666', marginBottom: 4 }, children: t('lineEndpoints') }), _jsx(Form.Item, { label: t('startX'), children: _jsx(InputNumber, { value: formatUnitValue(comp.startX ?? 0, reportUnit), onChange: (v) => handleChange('startX', parseUnitValue(v, reportUnit, comp.startX ?? 0)), size: "small", style: { width: '100%' }, step: unitStep }) }), _jsx(Form.Item, { label: t('startY'), children: _jsx(InputNumber, { value: formatUnitValue(comp.startY ?? 0, reportUnit), onChange: (v) => handleChange('startY', parseUnitValue(v, reportUnit, comp.startY ?? 0)), size: "small", style: { width: '100%' }, step: unitStep }) }), _jsx(Form.Item, { label: t('endX'), children: _jsx(InputNumber, { value: formatUnitValue(comp.endX ?? comp.width, reportUnit), onChange: (v) => handleChange('endX', parseUnitValue(v, reportUnit, comp.endX ?? comp.width)), size: "small", style: { width: '100%' }, step: unitStep }) }), _jsx(Form.Item, { label: t('endY'), children: _jsx(InputNumber, { value: formatUnitValue(comp.endY ?? comp.height, reportUnit), onChange: (v) => handleChange('endY', parseUnitValue(v, reportUnit, comp.endY ?? comp.height)), size: "small", style: { width: '100%' }, step: unitStep }) })] })),
238
+ } : null,
239
+ // ---- 形状 ----
240
+ component.type === 'shape' ? {
241
+ key: 'shape',
242
+ label: t('shape'),
243
+ children: (_jsxs(Form, { size: "small", labelCol: { span: 8 }, wrapperCol: { span: 16 }, children: [_jsx(Form.Item, { label: t('shapeType'), children: _jsx(Select, { value: comp.shapeType || 'rectangle', onChange: (v) => handleChange('shapeType', v), size: "small", style: { width: '100%' }, options: [
244
+ { value: 'rectangle', label: t('rectangle') },
245
+ { value: 'ellipse', label: t('ellipse') },
246
+ { value: 'roundRect', label: t('roundRect') },
247
+ { value: 'triangle', label: t('triangle') },
248
+ ] }) }), _jsx(Form.Item, { label: t('fillColor'), children: _jsx(ColorPicker, { size: "small", value: comp.fillColor || '#ffffff', onChange: (color) => handleChange('fillColor', color.toHexString()), allowClear: true }) }), _jsx(Form.Item, { label: t('shapeBorderColor'), children: _jsx(ColorPicker, { size: "small", value: comp.borderColor || '#000000', onChange: (color) => handleChange('borderColor', color.toHexString()) }) }), _jsx(Form.Item, { label: t('shapeBorderWidth'), children: _jsx(InputNumber, { value: formatUnitValue(comp.borderWidth ?? 0.2, reportUnit), onChange: (v) => handleChange('borderWidth', parseUnitValue(v, reportUnit, comp.borderWidth ?? 0.2)), size: "small", style: { width: '100%' }, min: 0, max: formatUnitValue(5, reportUnit), step: fineUnitStep }) }), _jsx(Form.Item, { label: t('shapeBorderStyle'), children: _jsx(Select, { value: comp.borderStyle || 'solid', onChange: (v) => handleChange('borderStyle', v), size: "small", style: { width: '100%' }, options: [
249
+ { value: 'solid', label: t('borderSolid') },
250
+ { value: 'dashed', label: t('borderDashed') },
251
+ { value: 'dotted', label: t('borderDotted') },
252
+ ] }) })] })),
253
+ } : null,
254
+ // ---- 页码 ----
255
+ component.type === 'pagenumber' ? {
256
+ key: 'pagenumber',
257
+ label: t('pageNumber'),
258
+ children: (_jsxs(Form, { size: "small", labelCol: { span: 8 }, wrapperCol: { span: 16 }, children: [_jsx(Form.Item, { label: t('format'), children: _jsx(Select, { value: comp.format || '1/N', onChange: (v) => handleChange('format', v), size: "small", style: { width: '100%' }, options: [
259
+ { value: '1', label: '1, 2, 3...' },
260
+ { value: '1/N', label: t('pageNumberFormatFraction') },
261
+ { value: 'Page 1 of N', label: t('pageNumberFormatPageOf') },
262
+ { value: 'Page 1', label: t('pageNumberFormatPageOnly') },
263
+ ] }) }), _jsx(Form.Item, { label: t('horizontalAlign'), children: _jsx(HorizontalAlignButtons, { value: comp.textAlign || 'center', onChange: (value) => handleChange('textAlign', value), labels: {
264
+ left: t('alignLeft'),
265
+ center: t('alignCenter'),
266
+ right: t('alignRight'),
267
+ } }) }), _jsx(Form.Item, { label: t('verticalAlign'), children: _jsx(VerticalAlignButtons, { value: comp.verticalAlign || 'middle', onChange: (value) => handleChange('verticalAlign', value), labels: {
268
+ top: t('verticalTop'),
269
+ middle: t('verticalMiddle'),
270
+ bottom: t('verticalBottom'),
271
+ } }) })] })),
272
+ } : null,
273
+ // ---- 日期时间 ----
274
+ component.type === 'datetime' ? {
275
+ key: 'datetime',
276
+ label: t('dateTime'),
277
+ children: (_jsxs(Form, { size: "small", labelCol: { span: 8 }, wrapperCol: { span: 16 }, children: [_jsx(Form.Item, { label: t('format'), children: _jsx(Input, { value: comp.format || 'yyyy-MM-dd', onChange: (e) => handleChange('format', e.target.value), size: "small", placeholder: t('dateTimeFormatPlaceholder') }) }), _jsx(Form.Item, { label: t('horizontalAlign'), children: _jsx(HorizontalAlignButtons, { value: comp.textAlign || 'left', onChange: (value) => handleChange('textAlign', value), labels: {
278
+ left: t('alignLeft'),
279
+ center: t('alignCenter'),
280
+ right: t('alignRight'),
281
+ } }) }), _jsx(Form.Item, { label: t('verticalAlign'), children: _jsx(VerticalAlignButtons, { value: comp.verticalAlign || 'middle', onChange: (value) => handleChange('verticalAlign', value), labels: {
282
+ top: t('verticalTop'),
283
+ middle: t('verticalMiddle'),
284
+ bottom: t('verticalBottom'),
285
+ } }) })] })),
286
+ } : null,
287
+ {
288
+ key: 'events',
289
+ label: globalT('events.title'),
290
+ children: (_jsxs(Space, { orientation: "vertical", style: { width: '100%' }, children: [_jsx(Button, { block: true, size: "small", icon: _jsx(EditOutlined, {}), onClick: () => {
291
+ setEventEditorTarget(null);
292
+ setEventEditorOpen(true);
293
+ }, children: globalT('events.edit') }), _jsxs(Typography.Text, { type: "secondary", style: { fontSize: 12 }, children: [Object.keys(comp.events ?? {}).length, " ", globalT('events.title')] })] })),
294
+ },
295
+ ].filter(Boolean) }), _jsx(EventEditorDialog, { open: eventEditorOpen, targetType: "component", events: component.events, initialEventName: eventEditorTarget?.eventName, initialCursor: eventEditorTarget ? { line: eventEditorTarget.line, column: eventEditorTarget.column } : undefined, dataContext: buildEventEditorDataContext(template, { targetType: 'component', componentId: component.id }), dictionaryItems: dictionaryItems, componentItems: componentItems, onCancel: () => {
296
+ setEventEditorOpen(false);
297
+ setEventEditorTarget(null);
298
+ }, onSave: (events) => {
299
+ replaceComponentEvents(currentPageId, bandId, component.id, events);
300
+ setEventEditorOpen(false);
301
+ setEventEditorTarget(null);
302
+ } }), _jsx(ExpressionEditor, { open: Boolean(expressionTarget), value: expressionTarget ? String(comp[expressionTarget.field] ?? '') : '', expressionExtensions: expressionExtensions, onChange: (v) => {
303
+ if (expressionTarget)
304
+ handleChange(expressionTarget.field, v);
305
+ }, onClose: () => setExpressionTarget(null) })] }));
306
+ };
307
+ const ComponentContentProperties = ({ component, comp, dataSourceDefinitions, dataSourceOptions, onChange, onChangeMany, onOpenExpressionEditor, t }) => {
308
+ switch (component.type) {
309
+ case 'chart':
310
+ // 图表属性由主组件通过 buildChartPropertyItems 直接展开到外层 Collapse,
311
+ // 不再走 text 包装组,避免折叠面板嵌套。
312
+ return null;
313
+ case 'image': {
314
+ const uploadInputId = `rd-image-upload-${comp.id ?? 'selected'}`;
315
+ const handleImageFileChange = async (event) => {
316
+ const file = event.target.files?.[0];
317
+ if (!file)
318
+ return;
319
+ try {
320
+ const dataUrl = await readImageAsDataUrl(file);
321
+ onChange('src', dataUrl);
322
+ }
323
+ finally {
324
+ event.target.value = '';
325
+ }
326
+ };
327
+ return (_jsxs(Form, { size: "small", labelCol: { span: 8 }, wrapperCol: { span: 16 }, children: [_jsx(Form.Item, { label: t('imageUrlOrBase64'), children: _jsxs(Space.Compact, { style: { width: '100%' }, children: [_jsx(Input.TextArea, { "aria-label": t('imageUrlOrBase64'), value: comp.src || '', onChange: (event) => onChange('src', event.target.value), size: "small", autoSize: false, rows: 3, placeholder: t('imageUrlOrBase64Placeholder') }), _jsx(ExpressionFieldButton, { label: t('imageUrlOrBase64'), t: t, onClick: () => onOpenExpressionEditor('src', t('imageUrlOrBase64')) })] }) }), _jsxs(Form.Item, { label: t('uploadImage'), children: [_jsx("input", { id: uploadInputId, "aria-label": t('uploadImage'), type: "file", accept: "image/*", style: { position: 'absolute', width: 1, height: 1, opacity: 0, pointerEvents: 'none' }, onChange: handleImageFileChange }), _jsx(Button, { size: "small", icon: _jsx(UploadOutlined, {}), onClick: () => document.getElementById(uploadInputId)?.click(), children: t('uploadImage') })] }), _jsx(Form.Item, { label: t('fitMode'), children: _jsx(Select, { "aria-label": t('fitMode'), value: comp.fitMode || 'contain', onChange: (value) => onChange('fitMode', value), size: "small", virtual: false, options: [
328
+ { value: 'contain', label: t('fitContain') },
329
+ { value: 'cover', label: t('fitCover') },
330
+ { value: 'fill', label: t('fitFill') },
331
+ { value: 'stretch', label: t('fitStretch') },
332
+ ] }) })] }));
333
+ }
334
+ case 'barcode':
335
+ return (_jsxs(Form, { size: "small", labelCol: { span: 8 }, wrapperCol: { span: 16 }, children: [_jsx(Form.Item, { label: t('barcodeContent'), children: _jsxs(Space.Compact, { style: { width: '100%' }, children: [_jsx(Input, { "aria-label": t('barcodeContent'), value: comp.value || '', onChange: (event) => onChange('value', event.target.value), size: "small", placeholder: t('expressionLikePlaceholder') }), _jsx(ExpressionFieldButton, { label: t('barcodeContent'), t: t, onClick: () => onOpenExpressionEditor('value', t('barcodeContent')) })] }) }), _jsx(Form.Item, { label: t('barcodeFormat'), children: _jsx(Select, { "aria-label": t('barcodeFormat'), value: comp.format || 'CODE128', onChange: (value) => onChange('format', value), size: "small", virtual: false, options: BARCODE_FORMATS.map(value => ({ value, label: value })) }) }), _jsx(Form.Item, { label: t('showText'), children: _jsx(Switch, { "aria-label": t('showText'), size: "small", checked: comp.showText ?? true, onChange: (checked) => onChange('showText', checked) }) })] }));
336
+ case 'qrcode':
337
+ return (_jsxs(Form, { size: "small", labelCol: { span: 8 }, wrapperCol: { span: 16 }, children: [_jsx(Form.Item, { label: t('qrcodeContent'), children: _jsxs(Space.Compact, { style: { width: '100%' }, children: [_jsx(Input, { "aria-label": t('qrcodeContent'), value: comp.value || '', onChange: (event) => onChange('value', event.target.value), size: "small", placeholder: t('expressionLikePlaceholder') }), _jsx(ExpressionFieldButton, { label: t('qrcodeContent'), t: t, onClick: () => onOpenExpressionEditor('value', t('qrcodeContent')) })] }) }), _jsx(Form.Item, { label: t('qrcodeFormat'), children: _jsx(Select, { "aria-label": t('qrcodeFormat'), value: comp.format || 'QR_CODE', onChange: (value) => onChange('format', value), size: "small", virtual: false, options: QR_CODE_FORMATS.map(value => ({ value, label: value })) }) })] }));
338
+ case 'checkbox':
339
+ return (_jsxs(Form, { size: "small", labelCol: { span: 8 }, wrapperCol: { span: 16 }, children: [_jsx(Form.Item, { label: t('checkedExpression'), children: _jsxs(Space.Compact, { style: { width: '100%' }, children: [_jsx(Input, { "aria-label": t('checkedExpression'), value: comp.checked || '', onChange: (event) => onChange('checked', event.target.value), size: "small", placeholder: t('expressionLikePlaceholder') }), _jsx(ExpressionFieldButton, { label: t('checkedExpression'), t: t, onClick: () => onOpenExpressionEditor('checked', t('checkedExpression')) })] }) }), _jsx(Form.Item, { label: t('labelText'), children: _jsxs(Space.Compact, { style: { width: '100%' }, children: [_jsx(Input, { "aria-label": t('labelText'), value: comp.label || '', onChange: (event) => onChange('label', event.target.value), size: "small" }), _jsx(ExpressionFieldButton, { label: t('labelText'), t: t, onClick: () => onOpenExpressionEditor('label', t('labelText')) })] }) })] }));
340
+ case 'richtext':
341
+ return (_jsx(Form, { size: "small", labelCol: { span: 8 }, wrapperCol: { span: 16 }, children: _jsx(Form.Item, { label: t('richTextContent'), children: _jsxs(Space.Compact, { style: { width: '100%' }, children: [_jsx(Input.TextArea, { "aria-label": t('richTextContent'), value: comp.html || '', onChange: (event) => onChange('html', event.target.value), autoSize: false, rows: 5, placeholder: "<p>{Data.Field}</p>" }), _jsx(ExpressionFieldButton, { label: t('richTextContent'), t: t, onClick: () => onOpenExpressionEditor('html', t('richTextContent')) })] }) }) }));
342
+ case 'subreport':
343
+ return (_jsxs(Form, { size: "small", labelCol: { span: 8 }, wrapperCol: { span: 16 }, children: [_jsx(Form.Item, { label: t('localTemplateKey'), children: _jsx(Input, { "aria-label": t('localTemplateKey'), value: comp.templateUrl || '', onChange: (event) => onChange('templateUrl', event.target.value), size: "small", placeholder: t('localTemplateKeyPlaceholder') }) }), _jsx(Form.Item, { label: t('parameters'), children: _jsx(Input.TextArea, { "aria-label": t('parameters'), value: JSON.stringify(comp.parameters ?? {}, null, 2), onChange: (event) => {
344
+ try {
345
+ onChange('parameters', JSON.parse(event.target.value || '{}'));
346
+ }
347
+ catch {
348
+ onChange('parameters', comp.parameters ?? {});
349
+ }
350
+ }, autoSize: false, rows: 5 }) })] }));
351
+ case 'panel':
352
+ return _jsx("div", { style: { padding: 8, color: '#999', fontSize: 12 }, children: t('panelContentHint') });
353
+ default:
354
+ return _jsx("div", { style: { padding: 8, color: '#999', fontSize: 12 }, children: t('noContentProperties') });
355
+ }
356
+ };
357
+ const ExpressionFieldButton = ({ label, onClick, t }) => {
358
+ const title = t('openExpressionEditorFor', { field: label });
359
+ return (_jsx(Button, { "aria-label": title, title: title, icon: _jsx(EditOutlined, {}), onClick: onClick, style: { width: 32 } }));
360
+ };
361
+ function readImageAsDataUrl(file) {
362
+ return new Promise((resolve, reject) => {
363
+ const reader = new FileReader();
364
+ reader.onload = () => resolve(String(reader.result ?? ''));
365
+ reader.onerror = () => reject(reader.error ?? new Error('Failed to read image file'));
366
+ reader.readAsDataURL(file);
367
+ });
368
+ }
369
+ function findComponentInTree(components, componentId) {
370
+ for (const component of components) {
371
+ if (component.id === componentId)
372
+ return component;
373
+ if ('components' in component && Array.isArray(component.components)) {
374
+ const child = findComponentInTree(component.components ?? [], componentId);
375
+ if (child)
376
+ return child;
377
+ }
378
+ }
379
+ return undefined;
380
+ }
381
+ function buildDictionaryEventItems(template) {
382
+ return template.dataSources.map(source => ({
383
+ key: source.id,
384
+ title: source.name || source.id,
385
+ children: (source.schema ?? source.fields ?? []).map(field => ({
386
+ key: `${source.id}.${field.name}`,
387
+ title: field.label || field.name,
388
+ })),
389
+ }));
390
+ }
391
+ function buildComponentEventItems(template) {
392
+ return template.pages.map(page => ({
393
+ key: page.id,
394
+ title: page.id,
395
+ insertable: false,
396
+ children: page.bands.map(band => ({
397
+ key: band.id,
398
+ title: band.name || band.id,
399
+ insertable: false,
400
+ children: buildComponentTreeItems(band.components),
401
+ })),
402
+ }));
403
+ }
404
+ function buildComponentTreeItems(components) {
405
+ return components.flatMap(component => {
406
+ const children = component.type === 'panel'
407
+ ? buildComponentTreeItems(component.components ?? [])
408
+ : undefined;
409
+ const item = {
410
+ key: component.id,
411
+ title: component.name ? `${component.name} (${component.type})` : `${component.type}: ${component.id}`,
412
+ name: component.name,
413
+ type: component.type,
414
+ insertable: Boolean(component.name),
415
+ children,
416
+ };
417
+ if (!item.name && !children?.length) {
418
+ return [];
419
+ }
420
+ return [item];
421
+ });
422
+ }
423
+ function normalizeOptionalBorder(border) {
424
+ if (!border)
425
+ return undefined;
426
+ const hasSide = Boolean(border.sides?.top || border.sides?.right || border.sides?.bottom || border.sides?.left);
427
+ if ((border.style == null || border.style === 'none') && !hasSide) {
428
+ return undefined;
429
+ }
430
+ return border;
431
+ }
432
+ function normalizeOptionalPadding(padding) {
433
+ if (!padding)
434
+ return undefined;
435
+ return padding.top || padding.right || padding.bottom || padding.left ? padding : undefined;
436
+ }
437
+ function propertyBorderLabels(t) {
438
+ return {
439
+ style: t('borderStyle'),
440
+ none: t('borderNone'),
441
+ solid: t('borderSolid'),
442
+ dashed: t('borderDashed'),
443
+ dotted: t('borderDotted'),
444
+ double: t('borderDouble'),
445
+ width: t('borderWidth'),
446
+ color: t('borderColor'),
447
+ sides: t('applySides'),
448
+ sideLabels: {
449
+ top: t('top'),
450
+ right: t('right'),
451
+ bottom: t('bottom'),
452
+ left: t('left'),
453
+ },
454
+ };
455
+ }
456
+ function propertyPaddingLabels(t) {
457
+ return {
458
+ title: t('padding'),
459
+ top: t('top'),
460
+ right: t('right'),
461
+ bottom: t('bottom'),
462
+ left: t('left'),
463
+ ariaTop: t('paddingTop'),
464
+ ariaRight: t('paddingRight'),
465
+ ariaBottom: t('paddingBottom'),
466
+ ariaLeft: t('paddingLeft'),
467
+ };
468
+ }
469
+ const propertyEditorMessages = {
470
+ 'zh-CN': {
471
+ selectComponent: '选择组件以编辑属性',
472
+ selectedComponents: '已选择 {count} 个组件',
473
+ general: '基本信息',
474
+ position: '位置与尺寸',
475
+ behavior: '行为',
476
+ text: '文本内容',
477
+ font: '字体',
478
+ border: '边框',
479
+ appearance: '外观',
480
+ table: '表格',
481
+ line: '线条属性',
482
+ shape: '形状属性',
483
+ pageNumber: '页码属性',
484
+ dateTime: '日期时间属性',
485
+ chart: '图表属性',
486
+ chartType: '图表类型',
487
+ chartVariant: '图表变种',
488
+ chartBinding: '数据绑定',
489
+ chartDataSource: '数据源',
490
+ chartArrayPath: '数组路径',
491
+ chartArrayPathTooltip: '用于主从报表场景,指定当前行中的子数组字段名(如 Items)。留空则使用数据源顶级数组。',
492
+ chartArrayPathPlaceholder: '如 Items 或 Orders.Lines',
493
+ chartCategoryField: '类目字段',
494
+ chartValueField: '数值字段',
495
+ chartXField: 'X 轴字段',
496
+ chartYField: 'Y 轴字段',
497
+ chartSeriesField: '系列字段',
498
+ chartLabelField: '标签字段',
499
+ chartAggregate: '聚合',
500
+ chartAggregateNone: '不聚合',
501
+ chartAggregateSum: '求和',
502
+ chartAggregateAvg: '平均值',
503
+ chartAggregateCount: '计数',
504
+ chartAggregateMin: '最小值',
505
+ chartAggregateMax: '最大值',
506
+ chartAppearance: '图表外观',
507
+ chartTitle: '图表标题',
508
+ chartSubtitle: '副标题',
509
+ chartShowLegend: '显示图例',
510
+ chartLegendPosition: '图例位置',
511
+ chartShowAxes: '显示坐标轴',
512
+ chartShowGrid: '显示网格',
513
+ chartShowLabels: '显示标签',
514
+ chartAxisTitleX: 'X 轴标题',
515
+ chartAxisTitleY: 'Y 轴标题',
516
+ chartPalette: '调色板',
517
+ chartPalettePlaceholder: '使用逗号分隔,例如 #2f6fed,#16a34a',
518
+ chartEmptyMessage: '空数据提示',
519
+ chartTypeColumn: '柱状图',
520
+ chartTypeColumnParallel: '分组柱状图',
521
+ chartTypeColumnPercent: '百分比柱状图',
522
+ chartTypeBar: '条形图',
523
+ chartTypeBarParallel: '分组条形图',
524
+ chartTypeBarPercent: '百分比条形图',
525
+ chartTypeLine: '折线图',
526
+ chartTypeArea: '面积图',
527
+ chartTypeAreaPercent: '百分比面积图',
528
+ chartTypePie: '饼图',
529
+ chartTypeDonut: '环形图',
530
+ chartTypeRose: '玫瑰图',
531
+ chartTypeScatter: '散点图',
532
+ chartTypeRadar: '雷达图',
533
+ chartTypeFunnel: '漏斗图',
534
+ chartTypeDualAxis: '双轴图',
535
+ chartTypeHeatmap: '热力图',
536
+ chartTypeHistogram: '直方图',
537
+ chartTypeBoxPlot: '箱线图',
538
+ chartTypeTreeMap: '矩形树图',
539
+ chartTypeSunburst: '旭日图',
540
+ chartTypeCirclePacking: '圆形填充图',
541
+ chartLabelType: '标签类型',
542
+ chartLabelTypeName: '名称',
543
+ chartLabelTypeValue: '数值',
544
+ chartLabelTypePercent: '百分比',
545
+ chartLabelTypeNameValue: '名称+数值',
546
+ chartAxisLabelRotation: '轴标签旋转',
547
+ chartTheme: '主题设置',
548
+ chartBaseTheme: '基础主题',
549
+ chartThemeLight: '浅色',
550
+ chartThemeDark: '深色',
551
+ chartMarkStyle: '样式设置',
552
+ chartBarWidth: '柱宽',
553
+ chartCornerRadius: '圆角',
554
+ chartFillOpacity: '填充透明度',
555
+ chartCurveType: '曲线类型',
556
+ chartCurveLinear: '直线',
557
+ chartCurveMonotone: '平滑',
558
+ chartCurveStep: '阶梯',
559
+ chartShowPoint: '显示节点',
560
+ chartPointSize: '节点大小',
561
+ chartInnerRadius: '内径',
562
+ chartOuterRadius: '外径',
563
+ chartRoseType: '玫瑰类型',
564
+ chartRoseRadius: '半径',
565
+ chartRoseArea: '面积',
566
+ chartShowTrendLine: '显示趋势线',
567
+ chartTrendLineType: '趋势线类型',
568
+ chartTrendPolynomial: '多项式',
569
+ chartTrendExponential: '指数',
570
+ chartRadarShape: '雷达形状',
571
+ chartRadarPolygon: '多边形',
572
+ chartRadarCircle: '圆形',
573
+ chartShowRadarArea: '填充雷达区域',
574
+ chartRadarAreaOpacity: '雷达填充透明度',
575
+ chartFunnelDirection: '漏斗方向',
576
+ chartVertical: '垂直',
577
+ chartHorizontal: '水平',
578
+ chartFunnelShape: '漏斗形状',
579
+ chartFunnelTrapezoid: '梯形',
580
+ chartFunnelTriangle: '三角形',
581
+ chartFunnelRect: '矩形',
582
+ chartShowConversionRate: '显示转化率',
583
+ name: '名称',
584
+ type: '类型',
585
+ componentName: '组件名称',
586
+ componentNameDuplicate: '组件名称不能重复',
587
+ componentNameRequired: '组件名称不能为空',
588
+ width: '宽度',
589
+ height: '高度',
590
+ visibleExpression: '可见表达式',
591
+ visibleExpressionPlaceholder: '留空表示始终可见,例如 {Orders.ShowTitle}',
592
+ enabledExpression: '启用表达式',
593
+ enabledExpressionPlaceholder: '留空表示始终启用,例如 {Orders.EnableTitle}',
594
+ printableExpression: '可打印表达式',
595
+ printableExpressionPlaceholder: '留空表示始终打印,例如 {Orders.PrintTitle}',
596
+ textContent: '文本内容',
597
+ textContentPlaceholder: '普通文本或 {DataSource.Field}',
598
+ openExpressionEditor: '打开表达式编辑器',
599
+ openExpressionEditorFor: '打开表达式编辑器:{field}',
600
+ expressionLikePlaceholder: '普通值或 {DataSource.Field}',
601
+ textStyle: '文本样式',
602
+ chooseStyle: '选择样式集',
603
+ unbindStyle: '解除绑定',
604
+ conditionalFormat: '条件格式',
605
+ chooseConditionalFormat: '选择条件格式',
606
+ none: '无',
607
+ manage: '管理',
608
+ horizontalAlign: '水平对齐',
609
+ verticalAlign: '垂直对齐',
610
+ canGrow: '自动增大',
611
+ canShrink: '自动缩小',
612
+ textOnly: '文本组件专属',
613
+ imageContent: '图片内容',
614
+ imageContentPlaceholder: '图片地址或 {DataSource.Field}',
615
+ imageUrlOrBase64: '图片地址/Base64',
616
+ imageUrlOrBase64Placeholder: 'URL、Base64 或 {DataSource.Field}',
617
+ uploadImage: '上传图片',
618
+ fitMode: '适应方式',
619
+ fitContain: '等比包含',
620
+ fitCover: '等比裁切',
621
+ fitFill: '填充',
622
+ fitStretch: '拉伸',
623
+ barcodeContent: '条码内容',
624
+ barcodeFormat: '条码类型',
625
+ qrcodeContent: '二维码内容',
626
+ qrcodeFormat: '二维码类型',
627
+ showText: '显示文本',
628
+ checkedExpression: '选中表达式',
629
+ labelText: '标签文本',
630
+ richTextContent: '富文本内容',
631
+ localTemplateKey: '本地模板键/名称',
632
+ localTemplateKeyPlaceholder: '例如:invoice-detail',
633
+ parameters: '参数',
634
+ panelContentHint: '面板可承载子组件,使用外观和边框属性设置容器样式。',
635
+ noContentProperties: '该组件暂无专属内容属性。',
636
+ fontFamily: '字体系列',
637
+ fontSize: '字号',
638
+ textColor: '字体颜色',
639
+ bold: '加粗',
640
+ italic: '斜体',
641
+ underline: '下划线',
642
+ strike: '删除线',
643
+ borderStyle: '边框样式',
644
+ borderNone: '无边框',
645
+ borderSolid: '实线',
646
+ borderDashed: '虚线',
647
+ borderDotted: '点线',
648
+ borderDouble: '双线',
649
+ borderWidth: '边框宽度',
650
+ borderColor: '边框颜色',
651
+ applySides: '应用边',
652
+ top: '上',
653
+ right: '右',
654
+ bottom: '下',
655
+ left: '左',
656
+ backgroundColor: '背景色',
657
+ foregroundColor: '前景色',
658
+ foregroundColorPicker: '前景色选择器',
659
+ padding: '内边距',
660
+ paddingTop: '内边距上',
661
+ paddingRight: '内边距右',
662
+ paddingBottom: '内边距下',
663
+ paddingLeft: '内边距左',
664
+ tableDataSource: '表格数据源',
665
+ tableBindingMode: '绑定模式',
666
+ tableBindingFixed: '固定',
667
+ tableBindingDetail: '明细',
668
+ tableBindingDataSourceId: '绑定数组属性',
669
+ tableBindingArrayPath: '数组路径',
670
+ tableBindingArrayPathPlaceholder: '例如:Items 或 Customer.Orders',
671
+ dataSource: '数据源',
672
+ chooseDataSource: '选择数据源',
673
+ columnCount: '列数',
674
+ rowCount: '行数',
675
+ rowHeight: '明细行高',
676
+ tableColumns: '列定义',
677
+ tableColumn: '第 {index} 列',
678
+ tableColumnHeader: '标题',
679
+ tableColumnField: '字段',
680
+ tableColumnWidth: '宽度',
681
+ tableColumnType: '类型',
682
+ tableColumnHeaderAria: '第 {index} 列标题',
683
+ tableColumnFieldAria: '第 {index} 列字段',
684
+ tableColumnWidthAria: '第 {index} 列宽度',
685
+ tableColumnTypeAria: '第 {index} 列类型',
686
+ tableCellTypeText: '文本',
687
+ canBreak: '允许跨页',
688
+ showBorder: '显示边框',
689
+ color: '颜色',
690
+ lineWidth: '宽度',
691
+ lineStyle: '样式',
692
+ lineEndpoints: '端点坐标',
693
+ startX: '起点 X',
694
+ startY: '起点 Y',
695
+ endX: '终点 X',
696
+ endY: '终点 Y',
697
+ shapeType: '形状类型',
698
+ rectangle: '矩形',
699
+ ellipse: '椭圆',
700
+ roundRect: '圆角矩形',
701
+ triangle: '三角形',
702
+ fillColor: '填充色',
703
+ shapeBorderColor: '边框色',
704
+ shapeBorderWidth: '边框宽',
705
+ shapeBorderStyle: '边框样式',
706
+ format: '格式',
707
+ pageNumberFormatFraction: '第 1 / N 页',
708
+ pageNumberFormatPageOf: '第 1 页,共 N 页',
709
+ pageNumberFormatPageOnly: '第 1 页',
710
+ dateTimeFormatPlaceholder: 'yyyy-MM-dd HH:mm:ss',
711
+ alignLeft: '左对齐',
712
+ alignCenter: '水平居中',
713
+ alignRight: '右对齐',
714
+ verticalTop: '顶部对齐',
715
+ verticalMiddle: '垂直居中',
716
+ verticalBottom: '底部对齐',
717
+ },
718
+ 'en-US': {
719
+ selectComponent: 'Select a component to edit properties',
720
+ selectedComponents: '{count} components selected',
721
+ general: 'General',
722
+ position: 'Position and Size',
723
+ behavior: 'Behavior',
724
+ text: 'Text Content',
725
+ font: 'Font',
726
+ border: 'Border',
727
+ appearance: 'Appearance',
728
+ table: 'Table',
729
+ line: 'Line Properties',
730
+ shape: 'Shape Properties',
731
+ pageNumber: 'Page Number Properties',
732
+ dateTime: 'Date/Time Properties',
733
+ chart: 'Chart Properties',
734
+ chartType: 'Chart type',
735
+ chartVariant: 'Variant',
736
+ chartBinding: 'Data binding',
737
+ chartDataSource: 'Data source',
738
+ chartArrayPath: 'Array path',
739
+ chartArrayPathTooltip: 'For master-detail reports, specifies the sub-array field on the current row (e.g. Items). Leave empty to use the top-level data source array.',
740
+ chartArrayPathPlaceholder: 'e.g. Items or Orders.Lines',
741
+ chartCategoryField: 'Category field',
742
+ chartValueField: 'Value field',
743
+ chartXField: 'X field',
744
+ chartYField: 'Y field',
745
+ chartSeriesField: 'Series field',
746
+ chartLabelField: 'Label field',
747
+ chartAggregate: 'Aggregate',
748
+ chartAggregateNone: 'None',
749
+ chartAggregateSum: 'Sum',
750
+ chartAggregateAvg: 'Average',
751
+ chartAggregateCount: 'Count',
752
+ chartAggregateMin: 'Min',
753
+ chartAggregateMax: 'Max',
754
+ chartAppearance: 'Chart appearance',
755
+ chartTitle: 'Chart title',
756
+ chartSubtitle: 'Subtitle',
757
+ chartShowLegend: 'Show legend',
758
+ chartLegendPosition: 'Legend position',
759
+ chartShowAxes: 'Show axes',
760
+ chartShowGrid: 'Show grid',
761
+ chartShowLabels: 'Show labels',
762
+ chartAxisTitleX: 'X axis title',
763
+ chartAxisTitleY: 'Y axis title',
764
+ chartPalette: 'Palette',
765
+ chartPalettePlaceholder: 'Comma separated, for example #2f6fed,#16a34a',
766
+ chartEmptyMessage: 'Empty message',
767
+ chartTypeColumn: 'Column',
768
+ chartTypeColumnParallel: 'Grouped Column',
769
+ chartTypeColumnPercent: 'Percent Column',
770
+ chartTypeBar: 'Bar',
771
+ chartTypeBarParallel: 'Grouped Bar',
772
+ chartTypeBarPercent: 'Percent Bar',
773
+ chartTypeLine: 'Line',
774
+ chartTypeArea: 'Area',
775
+ chartTypeAreaPercent: 'Percent Area',
776
+ chartTypePie: 'Pie',
777
+ chartTypeDonut: 'Donut',
778
+ chartTypeRose: 'Rose',
779
+ chartTypeScatter: 'Scatter',
780
+ chartTypeRadar: 'Radar',
781
+ chartTypeFunnel: 'Funnel',
782
+ chartTypeDualAxis: 'Dual Axis',
783
+ chartTypeHeatmap: 'Heatmap',
784
+ chartTypeHistogram: 'Histogram',
785
+ chartTypeBoxPlot: 'Box Plot',
786
+ chartTypeTreeMap: 'Tree Map',
787
+ chartTypeSunburst: 'Sunburst',
788
+ chartTypeCirclePacking: 'Circle Packing',
789
+ chartLabelType: 'Label type',
790
+ chartLabelTypeName: 'Name',
791
+ chartLabelTypeValue: 'Value',
792
+ chartLabelTypePercent: 'Percent',
793
+ chartLabelTypeNameValue: 'Name+Value',
794
+ chartAxisLabelRotation: 'Axis label rotation',
795
+ chartTheme: 'Theme',
796
+ chartBaseTheme: 'Base theme',
797
+ chartThemeLight: 'Light',
798
+ chartThemeDark: 'Dark',
799
+ chartMarkStyle: 'Mark Style',
800
+ chartBarWidth: 'Bar width',
801
+ chartCornerRadius: 'Corner radius',
802
+ chartFillOpacity: 'Fill opacity',
803
+ chartCurveType: 'Curve type',
804
+ chartCurveLinear: 'Linear',
805
+ chartCurveMonotone: 'Smooth',
806
+ chartCurveStep: 'Step',
807
+ chartShowPoint: 'Show points',
808
+ chartPointSize: 'Point size',
809
+ chartInnerRadius: 'Inner radius',
810
+ chartOuterRadius: 'Outer radius',
811
+ chartRoseType: 'Rose type',
812
+ chartRoseRadius: 'Radius',
813
+ chartRoseArea: 'Area',
814
+ chartShowTrendLine: 'Show trend line',
815
+ chartTrendLineType: 'Trend line type',
816
+ chartTrendPolynomial: 'Polynomial',
817
+ chartTrendExponential: 'Exponential',
818
+ chartRadarShape: 'Radar shape',
819
+ chartRadarPolygon: 'Polygon',
820
+ chartRadarCircle: 'Circle',
821
+ chartShowRadarArea: 'Fill radar area',
822
+ chartRadarAreaOpacity: 'Radar area opacity',
823
+ chartFunnelDirection: 'Funnel direction',
824
+ chartVertical: 'Vertical',
825
+ chartHorizontal: 'Horizontal',
826
+ chartFunnelShape: 'Funnel shape',
827
+ chartFunnelTrapezoid: 'Trapezoid',
828
+ chartFunnelTriangle: 'Triangle',
829
+ chartFunnelRect: 'Rectangle',
830
+ chartShowConversionRate: 'Show conversion rate',
831
+ name: 'Name',
832
+ type: 'Type',
833
+ componentName: 'Component name',
834
+ componentNameDuplicate: 'Component name must be unique',
835
+ componentNameRequired: 'Component name is required',
836
+ width: 'Width',
837
+ height: 'Height',
838
+ visibleExpression: 'Visible expression',
839
+ visibleExpressionPlaceholder: 'Leave empty to always show, for example {Orders.ShowTitle}',
840
+ enabledExpression: 'Enabled expression',
841
+ enabledExpressionPlaceholder: 'Leave empty to always enable, for example {Orders.EnableTitle}',
842
+ printableExpression: 'Printable expression',
843
+ printableExpressionPlaceholder: 'Leave empty to always print, for example {Orders.PrintTitle}',
844
+ textContent: 'Text content',
845
+ textContentPlaceholder: 'Plain text or {DataSource.Field}',
846
+ openExpressionEditor: 'Open expression editor',
847
+ openExpressionEditorFor: 'Open expression editor: {field}',
848
+ expressionLikePlaceholder: 'Plain value or {DataSource.Field}',
849
+ textStyle: 'Text style',
850
+ chooseStyle: 'Select style',
851
+ unbindStyle: 'Unbind style',
852
+ conditionalFormat: 'Conditional format',
853
+ chooseConditionalFormat: 'Select conditional format',
854
+ none: 'None',
855
+ manage: 'Manage',
856
+ horizontalAlign: 'Horizontal align',
857
+ verticalAlign: 'Vertical align',
858
+ canGrow: 'Can grow',
859
+ canShrink: 'Can shrink',
860
+ textOnly: 'Text components only',
861
+ imageContent: 'Image content',
862
+ imageContentPlaceholder: 'Image URL or {DataSource.Field}',
863
+ imageUrlOrBase64: 'Image URL/Base64',
864
+ imageUrlOrBase64Placeholder: 'URL, Base64, or {DataSource.Field}',
865
+ uploadImage: 'Upload image',
866
+ fitMode: 'Fit mode',
867
+ fitContain: 'Contain',
868
+ fitCover: 'Cover',
869
+ fitFill: 'Fill',
870
+ fitStretch: 'Stretch',
871
+ barcodeContent: 'Barcode content',
872
+ barcodeFormat: 'Barcode type',
873
+ qrcodeContent: 'QR code content',
874
+ qrcodeFormat: 'QR code type',
875
+ showText: 'Show text',
876
+ checkedExpression: 'Checked expression',
877
+ labelText: 'Label text',
878
+ richTextContent: 'Rich Text Content',
879
+ localTemplateKey: 'Local template key/name',
880
+ localTemplateKeyPlaceholder: 'For example: invoice-detail',
881
+ parameters: 'Parameters',
882
+ panelContentHint: 'Panels host child components. Use appearance and border properties for container styling.',
883
+ noContentProperties: 'This component has no content-specific properties.',
884
+ fontFamily: 'Font family',
885
+ fontSize: 'Font size',
886
+ textColor: 'Font color',
887
+ bold: 'Bold',
888
+ italic: 'Italic',
889
+ underline: 'Underline',
890
+ strike: 'Strike',
891
+ borderStyle: 'Border style',
892
+ borderNone: 'None',
893
+ borderSolid: 'Solid',
894
+ borderDashed: 'Dashed',
895
+ borderDotted: 'Dotted',
896
+ borderDouble: 'Double',
897
+ borderWidth: 'Border width',
898
+ borderColor: 'Border color',
899
+ applySides: 'Apply sides',
900
+ top: 'Top',
901
+ right: 'Right',
902
+ bottom: 'Bottom',
903
+ left: 'Left',
904
+ backgroundColor: 'Background color',
905
+ foregroundColor: 'Foreground color',
906
+ foregroundColorPicker: 'Foreground color picker',
907
+ padding: 'Padding',
908
+ paddingTop: 'Padding top',
909
+ paddingRight: 'Padding right',
910
+ paddingBottom: 'Padding bottom',
911
+ paddingLeft: 'Padding left',
912
+ tableDataSource: 'Table data source',
913
+ tableBindingMode: 'Binding mode',
914
+ tableBindingFixed: 'Fixed',
915
+ tableBindingDetail: 'Detail',
916
+ tableBindingDataSourceId: 'Bound array property',
917
+ tableBindingArrayPath: 'Array path',
918
+ tableBindingArrayPathPlaceholder: 'For example: Items or Customer.Orders',
919
+ dataSource: 'Data source',
920
+ chooseDataSource: 'Select data source',
921
+ columnCount: 'Columns',
922
+ rowCount: 'Rows',
923
+ rowHeight: 'Detail row height',
924
+ tableColumns: 'Columns',
925
+ tableColumn: 'Column {index}',
926
+ tableColumnHeader: 'Header',
927
+ tableColumnField: 'Field',
928
+ tableColumnWidth: 'Width',
929
+ tableColumnType: 'Type',
930
+ tableColumnHeaderAria: 'Column {index} header',
931
+ tableColumnFieldAria: 'Column {index} field',
932
+ tableColumnWidthAria: 'Column {index} width',
933
+ tableColumnTypeAria: 'Column {index} type',
934
+ tableCellTypeText: 'Text',
935
+ canBreak: 'Can break',
936
+ showBorder: 'Show border',
937
+ color: 'Color',
938
+ lineWidth: 'Width',
939
+ lineStyle: 'Style',
940
+ lineEndpoints: 'Endpoints',
941
+ startX: 'Start X',
942
+ startY: 'Start Y',
943
+ endX: 'End X',
944
+ endY: 'End Y',
945
+ shapeType: 'Shape type',
946
+ rectangle: 'Rectangle',
947
+ ellipse: 'Ellipse',
948
+ roundRect: 'Round rectangle',
949
+ triangle: 'Triangle',
950
+ fillColor: 'Fill',
951
+ shapeBorderColor: 'Border color',
952
+ shapeBorderWidth: 'Border width',
953
+ shapeBorderStyle: 'Border style',
954
+ format: 'Format',
955
+ pageNumberFormatFraction: '1 / N',
956
+ pageNumberFormatPageOf: 'Page 1 of N',
957
+ pageNumberFormatPageOnly: 'Page 1',
958
+ dateTimeFormatPlaceholder: 'yyyy-MM-dd HH:mm:ss',
959
+ alignLeft: 'Align left',
960
+ alignCenter: 'Center horizontally',
961
+ alignRight: 'Align right',
962
+ verticalTop: 'Align top',
963
+ verticalMiddle: 'Center vertically',
964
+ verticalBottom: 'Align bottom',
965
+ },
966
+ };
967
+ function createPropertyT(locale) {
968
+ const messages = propertyEditorMessages[locale] ?? propertyEditorMessages['zh-CN'];
969
+ const fallback = propertyEditorMessages['en-US'];
970
+ return (key, values) => {
971
+ const message = messages[key] ?? fallback[key] ?? key;
972
+ if (!values)
973
+ return message;
974
+ return message.replace(/\{(\w+)\}/g, (match, valueKey) => (Object.prototype.hasOwnProperty.call(values, valueKey) ? String(values[valueKey]) : match));
975
+ };
976
+ }
977
+ const HorizontalAlignButtons = ({ disabled = false, labels, onChange, value }) => (_jsx(IconButtonGroup, { items: [
978
+ { value: 'left', label: labels?.left ?? '左对齐', icon: _jsx(AlignLeftOutlined, {}) },
979
+ { value: 'center', label: labels?.center ?? '水平居中', icon: _jsx(AlignCenterOutlined, {}) },
980
+ { value: 'right', label: labels?.right ?? '右对齐', icon: _jsx(AlignRightOutlined, {}) },
981
+ ], value: value, onChange: onChange, disabled: disabled }));
982
+ const VerticalAlignButtons = ({ disabled = false, labels, onChange, value }) => (_jsx(IconButtonGroup, { items: [
983
+ { value: 'top', label: labels?.top ?? '顶部对齐', icon: _jsx(VerticalAlignGlyph, { position: "top" }) },
984
+ { value: 'middle', label: labels?.middle ?? '垂直居中', icon: _jsx(VerticalAlignGlyph, { position: "middle" }) },
985
+ { value: 'bottom', label: labels?.bottom ?? '底部对齐', icon: _jsx(VerticalAlignGlyph, { position: "bottom" }) },
986
+ ], value: value, onChange: onChange, disabled: disabled }));
987
+ const IconButtonGroup = ({ disabled, items, onChange, value, }) => (_jsx(Space, { size: 4, wrap: true, children: items.map(item => (_jsx(Button, { "aria-label": item.label, title: item.label, size: "small", type: value === item.value ? 'primary' : 'default', icon: item.icon, disabled: disabled, onClick: () => onChange(item.value), style: { width: 28, paddingInline: 0 } }, item.value))) }));
988
+ const VerticalAlignGlyph = ({ position }) => {
989
+ const blockTop = position === 'top' ? 2 : position === 'middle' ? 8 : 14;
990
+ return (_jsxs("span", { "aria-hidden": "true", style: {
991
+ position: 'relative',
992
+ display: 'inline-block',
993
+ width: 14,
994
+ height: 18,
995
+ }, children: [_jsx("span", { style: { position: 'absolute', left: 0, right: 0, top: 0, height: 1.5, background: 'currentColor', opacity: 0.45, borderRadius: 999 } }), _jsx("span", { style: { position: 'absolute', left: 0, right: 0, top: 8, height: 1.5, background: 'currentColor', opacity: 0.45, borderRadius: 999 } }), _jsx("span", { style: { position: 'absolute', left: 0, right: 0, top: 16, height: 1.5, background: 'currentColor', opacity: 0.45, borderRadius: 999 } }), _jsx("span", { style: { position: 'absolute', left: 2, right: 2, top: blockTop, height: 3.5, background: 'currentColor', borderRadius: 999 } })] }));
996
+ };
997
+ const TablePropertyPanel = ({ table, onChange, t }) => {
998
+ const rowCount = table.rows?.length ?? table.rowCount ?? 1;
999
+ const columnCount = table.rows?.[0]?.cells.length ?? table.columnCount ?? 1;
1000
+ return (_jsx(Space, { orientation: "vertical", size: 12, style: { width: '100%' }, children: _jsxs(Form, { layout: "horizontal", size: "small", labelCol: { span: 8 }, wrapperCol: { span: 16 }, children: [_jsx(Form.Item, { label: t('columnCount'), children: _jsx(InputNumber, { "aria-label": t('columnCount'), value: columnCount, onChange: (value) => onChange({ columnCount: value ?? 1 }), size: "small", style: { width: '100%' }, min: 1, step: 1 }) }), _jsx(Form.Item, { label: t('rowCount'), children: _jsx(InputNumber, { "aria-label": t('rowCount'), value: rowCount, onChange: (value) => onChange({ rowCount: value ?? 1 }), size: "small", style: { width: '100%' }, min: 1, step: 1 }) }), _jsx(Form.Item, { label: t('canBreak'), children: _jsx(Switch, { "aria-label": t('canBreak'), size: "small", checked: table.canBreak ?? true, onChange: (checked) => onChange({ canBreak: checked }) }) })] }) }));
1001
+ };
1002
+ //# sourceMappingURL=PropertyEditor.js.map