@pactor-app/ui 1.42.0 → 1.44.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.
@@ -36,7 +36,6 @@ __export(components_exports, {
36
36
  Alert: () => Alert2,
37
37
  AlertDialog: () => AlertDialog2,
38
38
  Anchor: () => Anchor,
39
- AreaChart: () => AreaChart,
40
39
  ArtifactPanel: () => ArtifactPanel,
41
40
  AspectRatio: () => AspectRatio2,
42
41
  Attachment: () => Attachment,
@@ -44,10 +43,8 @@ __export(components_exports, {
44
43
  Avatar: () => Avatar2,
45
44
  BackTop: () => BackTop,
46
45
  Badge: () => Badge2,
47
- BarChart: () => BarChart2,
48
46
  Breadcrumb: () => Breadcrumb2,
49
47
  Bubble: () => Bubble2,
50
- BubbleChart: () => BubbleChart,
51
48
  Button: () => Button2,
52
49
  ButtonGroup: () => ButtonGroup2,
53
50
  CONTROL_SIZE_AREA: () => CONTROL_SIZE_AREA,
@@ -57,7 +54,6 @@ __export(components_exports, {
57
54
  CONTROL_SIZE_TRIGGER_TEXT: () => CONTROL_SIZE_TRIGGER_TEXT,
58
55
  Calendar: () => Calendar3,
59
56
  Card: () => Card2,
60
- CardList: () => CardList,
61
57
  Carousel: () => Carousel2,
62
58
  Cascader: () => Cascader,
63
59
  ChatInput: () => ChatInput,
@@ -67,14 +63,13 @@ __export(components_exports, {
67
63
  ChipFilter: () => ChipFilter,
68
64
  ChipNode: () => ChipNode,
69
65
  Citation: () => Citation,
66
+ ClearHoverButton: () => ClearHoverButton,
70
67
  CodeBlock: () => CodeBlock,
71
68
  Col: () => Col,
72
69
  Collapsible: () => Collapsible2,
73
70
  ColorPicker: () => ColorPicker,
74
- ColumnChart: () => ColumnChart,
75
71
  Combobox: () => Combobox2,
76
72
  Command: () => Command2,
77
- ComposedChart: () => ComposedChart,
78
73
  Composer: () => Composer,
79
74
  Container: () => Container,
80
75
  Content: () => Content,
@@ -86,26 +81,18 @@ __export(components_exports, {
86
81
  Dialog: () => Dialog2,
87
82
  DiffViewer: () => DiffViewer,
88
83
  DocumentPreview: () => DocumentPreview,
89
- DonutChart: () => DonutChart,
90
84
  Drawer: () => Drawer2,
91
85
  DropdownMenu: () => DropdownMenu2,
92
- EChartBase: () => EChartBase,
93
86
  Empty: () => Empty2,
94
- EntityCard: () => EntityCard,
95
- ExpressionEditor: () => ExpressionEditor,
96
87
  Field: () => Field2,
97
- FieldCondition: () => FieldCondition,
98
88
  FilePreview: () => FilePreview,
99
89
  Flex: () => Flex,
100
90
  Footer: () => Footer,
101
91
  For: () => For,
102
92
  Form: () => Form,
103
- FunnelChart: () => FunnelChart2,
104
93
  Gallery: () => Gallery,
105
- GaugeChart: () => GaugeChart2,
106
94
  Grid: () => Grid,
107
95
  Header: () => Header,
108
- Heatmap: () => Heatmap,
109
96
  HoverCard: () => HoverCard2,
110
97
  ICONS: () => ICONS,
111
98
  Icon: () => Icon,
@@ -120,31 +107,25 @@ __export(components_exports, {
120
107
  Kbd: () => Kbd2,
121
108
  Label: () => Label2,
122
109
  Layout: () => Layout,
123
- LineChart: () => LineChart2,
124
110
  Link: () => Link2,
125
111
  List: () => List2,
126
112
  LoadingOverlay: () => LoadingOverlay,
127
- Login: () => Login,
128
- MapChart: () => MapChart2,
129
113
  Markdown: () => import_markdown2.MarkdownDslComponent,
130
114
  Mentions: () => Mentions,
131
115
  Menu: () => Menu2,
132
116
  Menubar: () => Menubar2,
133
117
  Message: () => Message,
134
118
  MessageList: () => MessageList,
135
- MetricCard: () => MetricCard,
136
119
  NavigationMenu: () => NavigationMenu2,
137
120
  Notification: () => Notification,
138
121
  NumberInput: () => NumberInput,
139
122
  Page: () => Page,
140
123
  Pagination: () => Pagination2,
141
- PieChart: () => PieChart2,
142
124
  Popconfirm: () => Popconfirm,
143
125
  Popover: () => Popover2,
144
126
  Progress: () => Progress2,
145
127
  Prompt: () => Prompt,
146
128
  QRCode: () => QRCode,
147
- RadarChart: () => RadarChart2,
148
129
  RadioGroup: () => RadioGroup2,
149
130
  RailTooltip: () => RailTooltip,
150
131
  Rate: () => Rate,
@@ -152,13 +133,7 @@ __export(components_exports, {
152
133
  Result: () => Result,
153
134
  RichText: () => RichText,
154
135
  Row: () => Row,
155
- RuleConfig: () => RuleConfig,
156
- RuleExpression: () => RuleExpression,
157
- SankeyChart: () => SankeyChart2,
158
- ScatterChart: () => ScatterChart2,
159
- ScriptEditor: () => ScriptEditor,
160
136
  ScrollArea: () => ScrollArea2,
161
- SearchableSelect: () => SearchableSelect,
162
137
  Segmented: () => Segmented,
163
138
  Select: () => Select2,
164
139
  Sender: () => Sender,
@@ -166,7 +141,6 @@ __export(components_exports, {
166
141
  Sheet: () => Sheet2,
167
142
  Sidebar: () => Sidebar,
168
143
  Sider: () => Sider,
169
- SiteFooter: () => SiteFooter,
170
144
  Skeleton: () => Skeleton2,
171
145
  Slider: () => Slider2,
172
146
  Sonner: () => Sonner,
@@ -194,7 +168,6 @@ __export(components_exports, {
194
168
  Tour: () => Tour,
195
169
  Transfer: () => Transfer,
196
170
  Tree: () => Tree,
197
- TreeChart: () => TreeChart2,
198
171
  TreeSelect: () => TreeSelect,
199
172
  Typography: () => Typography2,
200
173
  Upload: () => Upload2,
@@ -531,6 +504,9 @@ var ICONS = {
531
504
  "refresh-ccw": import_lucide_react2.RefreshCcw,
532
505
  "chart-column": import_lucide_react2.ChartColumn,
533
506
  "layout-grid": import_lucide_react2.LayoutGrid,
507
+ "layout-template": import_lucide_react2.LayoutTemplate,
508
+ palette: import_lucide_react2.Palette,
509
+ paintbrush: import_lucide_react2.Paintbrush,
534
510
  funnel: import_lucide_react2.Funnel,
535
511
  "loader-circle": import_lucide_react2.LoaderCircle,
536
512
  ellipsis: import_lucide_react2.Ellipsis,
@@ -540,7 +516,12 @@ var ICONS = {
540
516
  "zoom-in": import_lucide_react2.ZoomIn,
541
517
  "zoom-out": import_lucide_react2.ZoomOut,
542
518
  video: import_lucide_react2.Video,
543
- music: import_lucide_react2.Music
519
+ music: import_lucide_react2.Music,
520
+ // 录音 / 音频场景(spec: chat-page 录音模板):波形、麦克风、播放控制
521
+ "audio-lines": import_lucide_react2.AudioLines,
522
+ mic: import_lucide_react2.Mic,
523
+ play: import_lucide_react2.Play,
524
+ "rotate-cw": import_lucide_react2.RotateCw
544
525
  };
545
526
  function normalizeIconName(name) {
546
527
  return name.replace(/([a-z0-9])([A-Z])/g, "$1-$2").replace(/[_\s]+/g, "-").toLowerCase();
@@ -718,7 +699,7 @@ function AlertDialogOverlay({
718
699
  {
719
700
  "data-slot": "alert-dialog-overlay",
720
701
  className: cn(
721
- "fixed inset-0 z-50 bg-(--overlay-background) data-open:animate-in data-open:fade-in-0 data-closed:animate-out data-closed:fill-mode-forwards data-closed:fade-out-0",
702
+ "fixed inset-0 z-50 bg-(--overlay-background) duration-200 data-open:animate-in data-open:fade-in-0 data-closed:animate-out data-closed:fill-mode-forwards data-closed:fade-out-0",
722
703
  className
723
704
  ),
724
705
  ...props
@@ -4152,7 +4133,7 @@ function DialogOverlay({
4152
4133
  {
4153
4134
  "data-slot": "dialog-overlay",
4154
4135
  className: cn(
4155
- "fixed inset-0 z-50 bg-(--overlay-background) data-open:animate-in data-open:fade-in-0 data-closed:animate-out data-closed:fade-out-0 data-closed:fill-mode-forwards",
4136
+ "fixed inset-0 z-50 bg-(--overlay-background) duration-200 data-open:animate-in data-open:fade-in-0 data-closed:animate-out data-closed:fade-out-0 data-closed:fill-mode-forwards",
4156
4137
  className
4157
4138
  ),
4158
4139
  ...props
@@ -7053,7 +7034,7 @@ function SheetOverlay({
7053
7034
  {
7054
7035
  "data-slot": "sheet-overlay",
7055
7036
  className: cn(
7056
- "fixed inset-0 z-50 bg-(--overlay-background) data-open:animate-in data-open:fade-in-0 data-closed:animate-out data-closed:fill-mode-forwards data-closed:fade-out-0",
7037
+ "fixed inset-0 z-50 bg-(--overlay-background) duration-200 data-open:animate-in data-open:fade-in-0 data-closed:animate-out data-closed:fill-mode-forwards data-closed:fade-out-0",
7057
7038
  className
7058
7039
  ),
7059
7040
  ...props
@@ -14145,7 +14126,7 @@ var MentionInput = (0, import_react66.forwardRef)(
14145
14126
  }
14146
14127
  onChipsChange?.(collectChips(fieldRef.current));
14147
14128
  };
14148
- const serialize2 = () => fieldRef.current ? serializeNode(fieldRef.current).replace(/\n$/, "") : "";
14129
+ const serialize = () => fieldRef.current ? serializeNode(fieldRef.current).replace(/\n$/, "") : "";
14149
14130
  const buildChipElement = (item, source) => {
14150
14131
  const chip = document.createElement("span");
14151
14132
  chip.setAttribute(CHIP_ATTR, "");
@@ -14197,7 +14178,7 @@ var MentionInput = (0, import_react66.forwardRef)(
14197
14178
  selection.addRange(range);
14198
14179
  }
14199
14180
  setEmpty(false);
14200
- onChange?.(serialize2());
14181
+ onChange?.(serialize());
14201
14182
  emitChips();
14202
14183
  };
14203
14184
  (0, import_react66.useImperativeHandle)(ref, () => ({
@@ -14286,7 +14267,7 @@ var MentionInput = (0, import_react66.forwardRef)(
14286
14267
  field.innerHTML = "";
14287
14268
  }
14288
14269
  setEmpty(serializeNode(field).trim() === "");
14289
- onChange?.(serialize2());
14270
+ onChange?.(serialize());
14290
14271
  refreshMenu();
14291
14272
  };
14292
14273
  const handleKeyDown = (event) => {
@@ -14321,7 +14302,7 @@ var MentionInput = (0, import_react66.forwardRef)(
14321
14302
  }
14322
14303
  if (event.key === "Enter" && !event.shiftKey && !composingRef.current && !event.nativeEvent.isComposing) {
14323
14304
  event.preventDefault();
14324
- const text = serialize2();
14305
+ const text = serialize();
14325
14306
  if (text.trim() !== "" && !disabled) {
14326
14307
  onSubmit?.(text);
14327
14308
  }
@@ -14348,7 +14329,7 @@ var MentionInput = (0, import_react66.forwardRef)(
14348
14329
  if (chip) {
14349
14330
  event.preventDefault();
14350
14331
  chip.remove();
14351
- onChange?.(serialize2());
14332
+ onChange?.(serialize());
14352
14333
  emitChips();
14353
14334
  setEmpty(serializeNode(field).trim() === "");
14354
14335
  }
@@ -14920,6 +14901,7 @@ function ChatPage({
14920
14901
  artifactOpen,
14921
14902
  defaultArtifactOpen = false,
14922
14903
  onArtifactOpenChange,
14904
+ content,
14923
14905
  className,
14924
14906
  style
14925
14907
  }) {
@@ -15173,7 +15155,7 @@ function ChatPage({
15173
15155
  "div",
15174
15156
  {
15175
15157
  "data-slot": "chat-page-menu",
15176
- className: "flex h-full w-[264px] shrink-0 flex-col gap-2 overflow-hidden border-r border-border px-2 py-3",
15158
+ className: "flex h-full w-[250px] shrink-0 flex-col gap-2 overflow-hidden border-r border-border px-2 py-3",
15177
15159
  children: [
15178
15160
  brand ? /* @__PURE__ */ (0, import_jsx_runtime191.jsxs)("div", { className: "flex shrink-0 items-center justify-between px-2", children: [
15179
15161
  /* @__PURE__ */ (0, import_jsx_runtime191.jsxs)("div", { className: "flex flex-col gap-0.5", children: [
@@ -15256,7 +15238,7 @@ function ChatPage({
15256
15238
  ]
15257
15239
  }
15258
15240
  ),
15259
- /* @__PURE__ */ (0, import_jsx_runtime191.jsx)("div", { className: "@container h-full min-w-0 flex-1", children: open || activePanelItem ? /* @__PURE__ */ (0, import_jsx_runtime191.jsxs)(PanelGroup, { orientation: "horizontal", style: { width: "100%", height: "100%" }, children: [
15241
+ /* @__PURE__ */ (0, import_jsx_runtime191.jsx)("div", { className: "@container h-full min-w-0 flex-1", children: content !== void 0 ? renderSlot(content) : open || activePanelItem ? /* @__PURE__ */ (0, import_jsx_runtime191.jsxs)(PanelGroup, { orientation: "horizontal", style: { width: "100%", height: "100%" }, children: [
15260
15242
  /* @__PURE__ */ (0, import_jsx_runtime191.jsx)(Panel, { defaultSize: "62%", children: chatArea }),
15261
15243
  /* @__PURE__ */ (0, import_jsx_runtime191.jsx)(ResizableHandle, { withHandle: true }),
15262
15244
  /* @__PURE__ */ (0, import_jsx_runtime191.jsx)(Panel, { defaultSize: "38%", minSize: "380px", maxSize: "760px", children: activePanelItem ? /* @__PURE__ */ (0, import_jsx_runtime191.jsx)(
@@ -15411,4064 +15393,6 @@ function Suggestion({ items = [], justify = "start", onSelect, className, style
15411
15393
  );
15412
15394
  }
15413
15395
 
15414
- // src/components/chart/EChartBase/index.tsx
15415
- var import_react71 = require("react");
15416
-
15417
- // src/components/chart/echarts-modules.ts
15418
- var echarts = __toESM(require("echarts/core"), 1);
15419
- var import_charts = require("echarts/charts");
15420
- var import_components = require("echarts/components");
15421
- var import_renderers = require("echarts/renderers");
15422
- echarts.use([
15423
- import_charts.LineChart,
15424
- import_charts.BarChart,
15425
- import_charts.PieChart,
15426
- import_charts.ScatterChart,
15427
- import_charts.RadarChart,
15428
- import_charts.FunnelChart,
15429
- import_charts.GaugeChart,
15430
- import_charts.HeatmapChart,
15431
- import_charts.TreeChart,
15432
- import_charts.SankeyChart,
15433
- import_charts.MapChart,
15434
- import_components.GridComponent,
15435
- import_components.TooltipComponent,
15436
- import_components.LegendComponent,
15437
- import_components.TitleComponent,
15438
- import_components.VisualMapComponent,
15439
- import_components.DataZoomComponent,
15440
- import_components.GeoComponent,
15441
- import_components.RadarComponent,
15442
- import_renderers.CanvasRenderer
15443
- ]);
15444
-
15445
- // src/components/chart/EChartBase/index.tsx
15446
- var import_jsx_runtime195 = require("react/jsx-runtime");
15447
- function EChartBase({
15448
- option,
15449
- height = 256,
15450
- onSelect,
15451
- className,
15452
- style,
15453
- slot
15454
- }) {
15455
- const hostRef = (0, import_react71.useRef)(null);
15456
- const chartRef = (0, import_react71.useRef)(null);
15457
- const onSelectRef = (0, import_react71.useRef)(onSelect);
15458
- onSelectRef.current = onSelect;
15459
- const [failed, setFailed] = (0, import_react71.useState)(false);
15460
- (0, import_react71.useEffect)(() => {
15461
- const host = hostRef.current;
15462
- if (!host) return;
15463
- let chart = null;
15464
- try {
15465
- chart = echarts.init(host);
15466
- } catch {
15467
- setFailed(true);
15468
- return;
15469
- }
15470
- chartRef.current = chart;
15471
- chart.on("click", (params) => {
15472
- onSelectRef.current?.({
15473
- name: params.name,
15474
- value: params.value,
15475
- seriesName: params.seriesName,
15476
- dataIndex: params.dataIndex
15477
- });
15478
- });
15479
- const observer2 = new ResizeObserver(() => chart?.resize());
15480
- observer2.observe(host);
15481
- return () => {
15482
- observer2.disconnect();
15483
- chart?.dispose();
15484
- chartRef.current = null;
15485
- };
15486
- }, []);
15487
- (0, import_react71.useEffect)(() => {
15488
- try {
15489
- chartRef.current?.setOption(option, { notMerge: true });
15490
- } catch {
15491
- }
15492
- }, [option]);
15493
- if (failed) {
15494
- return /* @__PURE__ */ (0, import_jsx_runtime195.jsx)(
15495
- "div",
15496
- {
15497
- "data-slot": "chart-fallback",
15498
- className: cn(
15499
- "flex h-32 w-full items-center justify-center rounded-md border border-dashed text-sm text-muted-foreground",
15500
- className
15501
- ),
15502
- style,
15503
- children: "\u5F53\u524D\u73AF\u5883\u4E0D\u652F\u6301\u56FE\u8868\u6E32\u67D3"
15504
- }
15505
- );
15506
- }
15507
- return /* @__PURE__ */ (0, import_jsx_runtime195.jsx)(
15508
- "div",
15509
- {
15510
- ref: hostRef,
15511
- "data-slot": slot,
15512
- className: cn("w-full", className),
15513
- style: { height: typeof height === "number" ? `${height}px` : height, ...style }
15514
- }
15515
- );
15516
- }
15517
-
15518
- // src/components/chart/lib/option-builders.ts
15519
- function pickColor(theme, colors, index) {
15520
- if (colors && colors.length > 0) return colors[index % colors.length];
15521
- return theme.palette[index % theme.palette.length];
15522
- }
15523
- function toNumber(value) {
15524
- const n = Number(value);
15525
- return Number.isFinite(n) ? n : 0;
15526
- }
15527
- function axisLabel(theme) {
15528
- return { color: theme.muted };
15529
- }
15530
- function grid() {
15531
- return { left: 8, right: 16, top: 40, bottom: 4, containLabel: true };
15532
- }
15533
- function axisTooltip(theme) {
15534
- return {
15535
- trigger: "axis",
15536
- backgroundColor: theme.card,
15537
- borderColor: theme.border,
15538
- textStyle: { color: theme.foreground }
15539
- };
15540
- }
15541
- function itemTooltip(theme) {
15542
- return {
15543
- trigger: "item",
15544
- backgroundColor: theme.card,
15545
- borderColor: theme.border,
15546
- textStyle: { color: theme.foreground }
15547
- };
15548
- }
15549
- function titleOption(title) {
15550
- return title ? { text: title, left: 8, top: 4 } : void 0;
15551
- }
15552
- function legendOption(theme, show) {
15553
- return show ? { top: 4, textStyle: { color: theme.muted } } : void 0;
15554
- }
15555
- function categorySeries(input, theme, kind, area) {
15556
- const rows = input.data;
15557
- const keys = input.yKeys && input.yKeys.length > 0 ? input.yKeys : ["value"];
15558
- const series = keys.map((key, index) => ({
15559
- type: kind,
15560
- name: key,
15561
- data: rows.map((row) => toNumber(row[key])),
15562
- ...kind === "line" ? { smooth: input.smooth ?? false, symbolSize: 6 } : {},
15563
- ...area ? { areaStyle: { opacity: input.fillOpacity ?? 0.25 } } : {},
15564
- ...input.stacked ? { stack: "total" } : {},
15565
- ...kind === "line" ? { emphasis: { focus: "series" } } : {},
15566
- itemStyle: { color: pickColor(theme, input.colors, index) },
15567
- ...kind === "line" ? { lineStyle: { color: pickColor(theme, input.colors, index) } } : {},
15568
- ...area ? { lineStyle: { color: pickColor(theme, input.colors, index) } } : {}
15569
- }));
15570
- const legend = legendOption(theme, keys.length > 1);
15571
- return {
15572
- color: input.colors && input.colors.length > 0 ? input.colors : theme.palette,
15573
- title: titleOption(input.title),
15574
- grid: grid(),
15575
- tooltip: axisTooltip(theme),
15576
- ...legend ? { legend } : {},
15577
- xAxis: { type: "category", data: rows.map((row) => String(input.xKey ? row[input.xKey] : "")), axisLabel: axisLabel(theme), axisLine: { lineStyle: { color: theme.border } } },
15578
- yAxis: { type: "value", axisLabel: axisLabel(theme), splitLine: { lineStyle: { color: theme.border } } },
15579
- series
15580
- };
15581
- }
15582
- function buildLineOption(input, theme) {
15583
- return categorySeries(input, theme, "line", input.showArea ?? false);
15584
- }
15585
- function buildColumnOption(input, theme) {
15586
- return categorySeries(input, theme, "bar", false);
15587
- }
15588
- function buildBarOption(input, theme) {
15589
- const rows = input.data;
15590
- const keys = input.yKeys && input.yKeys.length > 0 ? input.yKeys : ["value"];
15591
- const series = keys.map((key, index) => ({
15592
- type: "bar",
15593
- name: key,
15594
- data: rows.map((row) => toNumber(row[key])),
15595
- ...input.stacked ? { stack: "total" } : {},
15596
- itemStyle: { color: pickColor(theme, input.colors, index) }
15597
- }));
15598
- const legend = legendOption(theme, keys.length > 1);
15599
- return {
15600
- color: input.colors && input.colors.length > 0 ? input.colors : theme.palette,
15601
- title: titleOption(input.title),
15602
- grid: grid(),
15603
- tooltip: axisTooltip(theme),
15604
- ...legend ? { legend } : {},
15605
- xAxis: { type: "value", axisLabel: axisLabel(theme), splitLine: { lineStyle: { color: theme.border } } },
15606
- yAxis: { type: "category", data: rows.map((row) => String(input.yKey ? row[input.yKey] : "")), axisLabel: axisLabel(theme), axisLine: { lineStyle: { color: theme.border } } },
15607
- series
15608
- };
15609
- }
15610
- function buildAreaOption(input, theme) {
15611
- const withArea = { ...input, showArea: true };
15612
- return categorySeries(withArea, theme, "line", true);
15613
- }
15614
- function pieOption(input, theme, radius, extra) {
15615
- const nameKey = input.nameKey ?? "name";
15616
- const valueKey = input.valueKey ?? "value";
15617
- return {
15618
- color: input.colors && input.colors.length > 0 ? input.colors : theme.palette,
15619
- title: titleOption(input.title),
15620
- tooltip: itemTooltip(theme),
15621
- legend: { bottom: 0, textStyle: { color: theme.muted } },
15622
- series: [
15623
- {
15624
- type: "pie",
15625
- radius,
15626
- center: ["50%", "52%"],
15627
- data: input.data.map((row) => ({ name: String(row[nameKey] ?? ""), value: toNumber(row[valueKey]) })),
15628
- label: { show: input.showLabel ?? false, color: theme.muted },
15629
- emphasis: { scaleSize: 6 }
15630
- }
15631
- ],
15632
- ...extra
15633
- };
15634
- }
15635
- function buildPieOption(input, theme) {
15636
- return pieOption(input, theme, [0, "72%"], {});
15637
- }
15638
- function buildDonutOption(input, theme) {
15639
- const inner = input.innerRadius ?? "48%";
15640
- const outer = input.outerRadius ?? "72%";
15641
- const extra = {};
15642
- if (input.centerValue !== void 0 || input.centerLabel !== void 0) {
15643
- extra.title = {
15644
- text: input.centerValue !== void 0 ? String(input.centerValue) : "",
15645
- subtext: input.centerLabel ?? "",
15646
- left: "center",
15647
- top: "40%",
15648
- textStyle: { color: theme.foreground, fontSize: 22 },
15649
- subtextStyle: { color: theme.muted }
15650
- };
15651
- }
15652
- return pieOption(input, theme, [inner, outer], extra);
15653
- }
15654
- function scatterSeries(input, theme, extra) {
15655
- const xKey = input.xKey ?? "x";
15656
- const yKey = input.yKey ?? "y";
15657
- const groups = /* @__PURE__ */ new Map();
15658
- if (input.seriesKey) {
15659
- for (const row of input.data) {
15660
- const g = String(row[input.seriesKey] ?? "");
15661
- if (!groups.has(g)) groups.set(g, []);
15662
- groups.get(g).push(row);
15663
- }
15664
- } else {
15665
- groups.set("", input.data);
15666
- }
15667
- const names = [...groups.keys()];
15668
- const series = names.map((name, index) => ({
15669
- type: "scatter",
15670
- name: name || void 0,
15671
- data: groups.get(name).map(
15672
- (row) => extra?.datum ? extra.datum(row) : [toNumber(row[xKey]), toNumber(row[yKey])]
15673
- ),
15674
- symbolSize: extra?.symbolSize ?? 10,
15675
- itemStyle: { color: pickColor(theme, input.colors, index) }
15676
- }));
15677
- const legend = legendOption(theme, input.seriesKey !== void 0 && names.length > 1);
15678
- return {
15679
- color: input.colors && input.colors.length > 0 ? input.colors : theme.palette,
15680
- title: titleOption(input.title),
15681
- grid: grid(),
15682
- tooltip: axisTooltip(theme),
15683
- ...legend ? { legend } : {},
15684
- xAxis: { type: "value", axisLabel: axisLabel(theme), splitLine: { lineStyle: { color: theme.border } } },
15685
- yAxis: { type: "value", axisLabel: axisLabel(theme), splitLine: { lineStyle: { color: theme.border } } },
15686
- series
15687
- };
15688
- }
15689
- function buildScatterOption(input, theme) {
15690
- return scatterSeries(input, theme);
15691
- }
15692
- function buildBubbleOption(input, theme) {
15693
- const sizeKey = input.sizeKey ?? "size";
15694
- const sizes = input.data.map((row) => toNumber(row[sizeKey]));
15695
- const min = Math.min(...sizes, 0);
15696
- const max = Math.max(...sizes, 1);
15697
- const span = max - min || 1;
15698
- return scatterSeries(input, theme, {
15699
- datum: (row) => [toNumber(row[input.xKey ?? "x"]), toNumber(row[input.yKey ?? "y"]), toNumber(row[sizeKey])],
15700
- symbolSize: (datum) => {
15701
- const [x, y, size] = datum;
15702
- void x;
15703
- void y;
15704
- return 12 + (toNumber(size) - min) / span * 36;
15705
- }
15706
- });
15707
- }
15708
- function buildRadarOption(input, theme) {
15709
- const indicatorKey = input.indicatorKey ?? "indicator";
15710
- const keys = input.yKeys && input.yKeys.length > 0 ? input.yKeys : ["value"];
15711
- return {
15712
- color: input.colors && input.colors.length > 0 ? input.colors : theme.palette,
15713
- title: titleOption(input.title),
15714
- tooltip: itemTooltip(theme),
15715
- ...keys.length > 1 ? { legend: legendOption(theme, true) } : {},
15716
- radar: {
15717
- indicator: input.data.map((row) => ({ name: String(row[indicatorKey] ?? ""), max: 100 })),
15718
- axisName: { color: theme.muted },
15719
- splitLine: { lineStyle: { color: theme.border } },
15720
- splitArea: { areaStyle: { color: [] } }
15721
- },
15722
- series: [
15723
- {
15724
- type: "radar",
15725
- data: keys.map((key) => ({
15726
- name: key,
15727
- value: input.data.map((row) => toNumber(row[key]))
15728
- }))
15729
- }
15730
- ]
15731
- };
15732
- }
15733
- function buildFunnelOption(input, theme) {
15734
- const nameKey = input.nameKey ?? "name";
15735
- const valueKey = input.valueKey ?? "value";
15736
- return {
15737
- color: input.colors && input.colors.length > 0 ? input.colors : theme.palette,
15738
- title: titleOption(input.title),
15739
- tooltip: itemTooltip(theme),
15740
- series: [
15741
- {
15742
- type: "funnel",
15743
- left: "12%",
15744
- width: "76%",
15745
- sort: input.sort ?? "descending",
15746
- gap: 2,
15747
- label: { show: true, position: "inside", color: theme.card },
15748
- data: input.data.map((row) => ({ name: String(row[nameKey] ?? ""), value: toNumber(row[valueKey]) }))
15749
- }
15750
- ]
15751
- };
15752
- }
15753
- function buildGaugeOption(input, theme) {
15754
- const min = input.min ?? 0;
15755
- const max = input.max ?? 100;
15756
- const span = max - min || 1;
15757
- const defaultThresholds = [{ value: max, color: theme.palette[0] }];
15758
- const stops = (input.thresholds && input.thresholds.length > 0 ? input.thresholds : defaultThresholds).slice().sort((a, b) => a.value - b.value).map((t, index, arr) => {
15759
- const upper = index < arr.length - 1 ? t.value : max;
15760
- return [(upper - min) / span, t.color ?? theme.palette[0]];
15761
- });
15762
- return {
15763
- title: titleOption(input.title),
15764
- series: [
15765
- {
15766
- type: "gauge",
15767
- min,
15768
- max,
15769
- startAngle: 210,
15770
- endAngle: -30,
15771
- progress: { show: true, width: 10 },
15772
- axisLine: { lineStyle: { width: 10, color: stops } },
15773
- axisTick: { show: false },
15774
- splitLine: { show: false },
15775
- axisLabel: { show: false },
15776
- pointer: { show: false },
15777
- anchor: { show: false },
15778
- title: { show: false },
15779
- detail: {
15780
- valueAnimation: true,
15781
- offsetCenter: [0, 0],
15782
- fontSize: 26,
15783
- color: theme.foreground,
15784
- formatter: (value) => `${value}${input.unit ?? ""}`
15785
- },
15786
- data: [{ value: toNumber(input.value) }]
15787
- }
15788
- ]
15789
- };
15790
- }
15791
- function valueRange(values) {
15792
- if (values.length === 0) return { min: 0, max: 1 };
15793
- const min = Math.min(...values);
15794
- const max = Math.max(...values);
15795
- return max === min ? { min, max: min + 1 } : { min, max };
15796
- }
15797
- function buildHeatmapOption(input, theme) {
15798
- const xKey = input.xKey ?? "x";
15799
- const yKey = input.yKey ?? "y";
15800
- const valueKey = input.valueKey ?? "value";
15801
- const xs = [...new Set(input.data.map((row) => String(row[xKey])))];
15802
- const ys = [...new Set(input.data.map((row) => String(row[yKey])))];
15803
- const range = valueRange(input.data.map((row) => toNumber(row[valueKey])));
15804
- return {
15805
- title: titleOption(input.title),
15806
- grid: grid(),
15807
- tooltip: itemTooltip(theme),
15808
- xAxis: { type: "category", data: xs, axisLabel: axisLabel(theme), splitArea: { show: true } },
15809
- yAxis: { type: "category", data: ys, axisLabel: axisLabel(theme), splitArea: { show: true } },
15810
- visualMap: {
15811
- min: range.min,
15812
- max: range.max,
15813
- calculable: true,
15814
- orient: "horizontal",
15815
- left: "center",
15816
- bottom: 0,
15817
- textStyle: { color: theme.muted },
15818
- inRange: { color: [theme.palette[2] ?? theme.palette[0], theme.palette[0]] }
15819
- },
15820
- series: [
15821
- {
15822
- type: "heatmap",
15823
- data: input.data.map((row) => [String(row[xKey]), String(row[yKey]), toNumber(row[valueKey])]),
15824
- label: { show: false }
15825
- }
15826
- ]
15827
- };
15828
- }
15829
- function buildTreeOption(input, theme) {
15830
- const horizontal = input.orient === "horizontal";
15831
- return {
15832
- title: titleOption(input.title),
15833
- tooltip: itemTooltip(theme),
15834
- series: [
15835
- {
15836
- type: "tree",
15837
- data: input.data,
15838
- layout: "orthogonal",
15839
- orient: horizontal ? "LR" : "TB",
15840
- edgeShape: input.edgeShape ?? "curve",
15841
- roam: false,
15842
- expandAndCollapse: true,
15843
- initialTreeDepth: input.initialTreeDepth ?? 2,
15844
- label: {
15845
- show: true,
15846
- position: horizontal ? "right" : "bottom",
15847
- color: theme.foreground,
15848
- fontSize: 12
15849
- },
15850
- itemStyle: { color: theme.palette[0], borderColor: theme.palette[0] },
15851
- lineStyle: { color: theme.border, width: 1.5, curveness: 0.5 },
15852
- leaves: {
15853
- itemStyle: { color: theme.palette[1], borderColor: theme.palette[1] }
15854
- },
15855
- emphasis: { focus: "relative" }
15856
- }
15857
- ]
15858
- };
15859
- }
15860
- function buildSankeyOption(input, theme) {
15861
- return {
15862
- color: theme.palette,
15863
- title: titleOption(input.title),
15864
- tooltip: itemTooltip(theme),
15865
- series: [
15866
- {
15867
- type: "sankey",
15868
- left: 24,
15869
- right: 64,
15870
- top: 40,
15871
- bottom: 24,
15872
- nodeWidth: 16,
15873
- nodeGap: 12,
15874
- draggable: false,
15875
- label: { color: theme.muted },
15876
- lineStyle: { color: "gradient", curveness: 0.5 },
15877
- emphasis: { focus: "adjacency" },
15878
- data: input.nodes,
15879
- links: input.links
15880
- }
15881
- ]
15882
- };
15883
- }
15884
- function buildMapOption(input, theme) {
15885
- const nameKey = input.nameKey ?? "name";
15886
- const valueKey = input.valueKey ?? "value";
15887
- const range = valueRange(input.data.map((row) => toNumber(row[valueKey])));
15888
- return {
15889
- title: titleOption(input.title),
15890
- tooltip: itemTooltip(theme),
15891
- visualMap: {
15892
- min: range.min,
15893
- max: range.max,
15894
- left: 8,
15895
- bottom: 8,
15896
- textStyle: { color: theme.muted },
15897
- inRange: { color: [theme.palette[2] ?? theme.palette[0], theme.palette[0]] }
15898
- },
15899
- series: [
15900
- {
15901
- type: "map",
15902
- map: input.mapName,
15903
- roam: false,
15904
- label: { show: false },
15905
- emphasis: { label: { show: true, color: theme.foreground } },
15906
- itemStyle: { borderColor: theme.card, borderWidth: 1 },
15907
- data: input.data.map((row) => ({ name: String(row[nameKey] ?? ""), value: toNumber(row[valueKey]) }))
15908
- }
15909
- ]
15910
- };
15911
- }
15912
- function buildComposedOption(input, theme) {
15913
- const rows = input.data;
15914
- const hasSecondAxis = input.series.some((s) => s.yAxisIndex === 1);
15915
- const series = input.series.map((s, index) => {
15916
- const color = pickColor(theme, input.colors, index);
15917
- return {
15918
- type: s.type === "area" ? "line" : s.type,
15919
- name: s.name ?? s.dataKey,
15920
- data: rows.map((row) => toNumber(row[s.dataKey])),
15921
- ...s.type === "line" ? { smooth: false, symbolSize: 6 } : {},
15922
- ...s.type === "area" ? { areaStyle: { opacity: 0.25 } } : {},
15923
- yAxisIndex: s.yAxisIndex ?? 0,
15924
- itemStyle: { color },
15925
- ...s.type !== "bar" ? { lineStyle: { color } } : {}
15926
- };
15927
- });
15928
- return {
15929
- color: input.colors && input.colors.length > 0 ? input.colors : theme.palette,
15930
- title: titleOption(input.title),
15931
- grid: grid(),
15932
- tooltip: axisTooltip(theme),
15933
- legend: legendOption(theme, true),
15934
- xAxis: { type: "category", data: rows.map((row) => String(input.xKey ? row[input.xKey] : "")), axisLabel: axisLabel(theme), axisLine: { lineStyle: { color: theme.border } } },
15935
- yAxis: hasSecondAxis ? [
15936
- { type: "value", axisLabel: axisLabel(theme), splitLine: { lineStyle: { color: theme.border } } },
15937
- { type: "value", axisLabel: axisLabel(theme), splitLine: { show: false } }
15938
- ] : { type: "value", axisLabel: axisLabel(theme), splitLine: { lineStyle: { color: theme.border } } },
15939
- series
15940
- };
15941
- }
15942
-
15943
- // src/components/chart/lib/use-chart-theme.ts
15944
- var import_react72 = require("react");
15945
-
15946
- // src/components/chart/lib/color.ts
15947
- var LPAREN = String.fromCharCode(40);
15948
- var RPAREN = String.fromCharCode(41);
15949
- var OKLCH_RE = new RegExp(
15950
- "^oklch" + String.fromCharCode(92) + LPAREN + "\\s*([\\d.]+%?)\\s+([-\\d.eE]+)\\s+([-\\d.eE]+)(?:\\s*\\/\\s*[\\d.]+%?)?\\s*" + String.fromCharCode(92) + RPAREN + "$",
15951
- "i"
15952
- );
15953
- var OKLAB_RE = new RegExp(
15954
- "^oklab" + String.fromCharCode(92) + LPAREN + "\\s*([\\d.]+%?)\\s+([-\\d.eE]+)\\s+([-\\d.eE]+)(?:\\s*\\/\\s*[\\d.]+%?)?\\s*" + String.fromCharCode(92) + RPAREN + "$",
15955
- "i"
15956
- );
15957
- function parseLightness(raw) {
15958
- const value = parseFloat(raw);
15959
- return raw.endsWith("%") ? value / 100 : value;
15960
- }
15961
- function oklabToLinearSrgbColor(L, a, b) {
15962
- const l_ = L + 0.3963377774 * a + 0.2158037573 * b;
15963
- const m_ = L - 0.1055613458 * a - 0.0638541728 * b;
15964
- const s_ = L - 0.0894841775 * a - 1.291485548 * b;
15965
- const l = l_ * l_ * l_;
15966
- const m = m_ * m_ * m_;
15967
- const s = s_ * s_ * s_;
15968
- return [
15969
- 4.0767416621 * l - 3.3077115913 * m + 0.2309699292 * s,
15970
- -1.2684380046 * l + 2.6097574011 * m - 0.3413193965 * s,
15971
- -0.0041960863 * l - 0.7034186147 * m + 1.707614701 * s
15972
- ];
15973
- }
15974
- function linearToSrgbChannel(c) {
15975
- const clamped = Math.min(1, Math.max(0, c));
15976
- const encoded = clamped <= 31308e-7 ? 12.92 * clamped : 1.055 * Math.pow(clamped, 1 / 2.4) - 0.055;
15977
- return Math.round(encoded * 255);
15978
- }
15979
- function formatSrgbString(r, g, b) {
15980
- return "rgb" + LPAREN + `${linearToSrgbChannel(r)}, ${linearToSrgbChannel(g)}, ${linearToSrgbChannel(b)}` + RPAREN;
15981
- }
15982
- function normalizeColor(value) {
15983
- const trimmed = value.trim();
15984
- const oklch = OKLCH_RE.exec(trimmed);
15985
- if (oklch) {
15986
- const L = parseLightness(oklch[1]);
15987
- const C = parseFloat(oklch[2]);
15988
- const h = parseFloat(oklch[3]) * Math.PI / 180;
15989
- const [r, g, b] = oklabToLinearSrgbColor(L, C * Math.cos(h), C * Math.sin(h));
15990
- return formatSrgbString(r, g, b);
15991
- }
15992
- const oklab = OKLAB_RE.exec(trimmed);
15993
- if (oklab) {
15994
- const L = parseLightness(oklab[1]);
15995
- const a = parseFloat(oklab[2]);
15996
- const b = parseFloat(oklab[3]);
15997
- const [r, g, bl] = oklabToLinearSrgbColor(L, a, b);
15998
- return formatSrgbString(r, g, bl);
15999
- }
16000
- return trimmed;
16001
- }
16002
-
16003
- // src/components/chart/lib/theme.ts
16004
- var FALLBACK_THEME = {
16005
- palette: ["steelblue", "seagreen", "darkorange", "slateblue", "tomato"],
16006
- foreground: "black",
16007
- muted: "gray",
16008
- border: "gainsboro",
16009
- card: "white",
16010
- success: "seagreen",
16011
- warning: "darkorange",
16012
- destructive: "tomato",
16013
- info: "steelblue"
16014
- };
16015
- var cached = null;
16016
- function getChartTheme() {
16017
- if (cached) return cached;
16018
- if (typeof document === "undefined") return FALLBACK_THEME;
16019
- try {
16020
- const style = getComputedStyle(document.documentElement);
16021
- const read = (name) => normalizeColor(style.getPropertyValue(name));
16022
- const readColor = (name, fallback) => read(name) || fallback;
16023
- const palette = [1, 2, 3, 4, 5].map(
16024
- (i, index) => readColor(`--chart-${i}`, FALLBACK_THEME.palette[index])
16025
- );
16026
- cached = {
16027
- palette,
16028
- foreground: readColor("--foreground", FALLBACK_THEME.foreground),
16029
- muted: readColor("--muted-foreground", FALLBACK_THEME.muted),
16030
- border: readColor("--border", FALLBACK_THEME.border),
16031
- card: readColor("--card", FALLBACK_THEME.card),
16032
- success: readColor("--success", FALLBACK_THEME.success),
16033
- warning: readColor("--warning", FALLBACK_THEME.warning),
16034
- destructive: readColor("--destructive", FALLBACK_THEME.destructive),
16035
- info: readColor("--info", FALLBACK_THEME.info)
16036
- };
16037
- return cached;
16038
- } catch {
16039
- return FALLBACK_THEME;
16040
- }
16041
- }
16042
- function resetChartTheme() {
16043
- cached = null;
16044
- }
16045
- var CHART_SKIN_CHANGE_EVENT = "pactor:skin-change";
16046
- var listeners = /* @__PURE__ */ new Set();
16047
- var observer = null;
16048
- var scheduled = false;
16049
- function scheduleNotify() {
16050
- if (scheduled) return;
16051
- scheduled = true;
16052
- const enqueue = typeof requestAnimationFrame === "function" ? requestAnimationFrame : (cb) => setTimeout(cb, 0);
16053
- enqueue(() => {
16054
- scheduled = false;
16055
- resetChartTheme();
16056
- for (const listener of [...listeners]) {
16057
- listener();
16058
- }
16059
- });
16060
- }
16061
- function handleResourceLoad(event) {
16062
- const target = event.target;
16063
- if (target instanceof HTMLLinkElement || target instanceof HTMLStyleElement) {
16064
- scheduleNotify();
16065
- }
16066
- }
16067
- function subscribeChartTheme(listener) {
16068
- if (typeof document === "undefined" || typeof MutationObserver === "undefined") {
16069
- return () => {
16070
- };
16071
- }
16072
- listeners.add(listener);
16073
- if (!observer) {
16074
- observer = new MutationObserver(scheduleNotify);
16075
- observer.observe(document.documentElement, { attributes: true });
16076
- observer.observe(document.head, {
16077
- attributes: true,
16078
- childList: true,
16079
- characterData: true,
16080
- subtree: true
16081
- });
16082
- document.addEventListener("load", handleResourceLoad, true);
16083
- window.addEventListener(CHART_SKIN_CHANGE_EVENT, scheduleNotify);
16084
- }
16085
- return () => {
16086
- listeners.delete(listener);
16087
- if (listeners.size === 0 && observer) {
16088
- observer.disconnect();
16089
- observer = null;
16090
- document.removeEventListener("load", handleResourceLoad, true);
16091
- window.removeEventListener(CHART_SKIN_CHANGE_EVENT, scheduleNotify);
16092
- }
16093
- };
16094
- }
16095
-
16096
- // src/components/chart/lib/use-chart-theme.ts
16097
- function useChartTheme() {
16098
- return (0, import_react72.useSyncExternalStore)(subscribeChartTheme, getChartTheme, getChartTheme);
16099
- }
16100
-
16101
- // src/components/chart/AreaChart/index.tsx
16102
- var import_jsx_runtime196 = require("react/jsx-runtime");
16103
- function AreaChart({
16104
- data = [],
16105
- xKey,
16106
- yKeys,
16107
- stacked = false,
16108
- fillOpacity,
16109
- colors,
16110
- title,
16111
- height,
16112
- onSelect,
16113
- className,
16114
- style
16115
- }) {
16116
- const option = buildAreaOption(
16117
- { data, xKey, yKeys, stacked, fillOpacity, colors, title },
16118
- useChartTheme()
16119
- );
16120
- return /* @__PURE__ */ (0, import_jsx_runtime196.jsx)(
16121
- EChartBase,
16122
- {
16123
- slot: "area-chart",
16124
- option,
16125
- height,
16126
- onSelect,
16127
- className,
16128
- style
16129
- }
16130
- );
16131
- }
16132
-
16133
- // src/components/chart/BarChart/index.tsx
16134
- var import_jsx_runtime197 = require("react/jsx-runtime");
16135
- function BarChart2({
16136
- data = [],
16137
- yKey,
16138
- xKeys,
16139
- stacked = false,
16140
- colors,
16141
- title,
16142
- height,
16143
- onSelect,
16144
- className,
16145
- style
16146
- }) {
16147
- const option = buildBarOption(
16148
- { data, yKey, yKeys: xKeys, stacked, colors, title },
16149
- useChartTheme()
16150
- );
16151
- return /* @__PURE__ */ (0, import_jsx_runtime197.jsx)(
16152
- EChartBase,
16153
- {
16154
- slot: "bar-chart",
16155
- option,
16156
- height,
16157
- onSelect,
16158
- className,
16159
- style
16160
- }
16161
- );
16162
- }
16163
-
16164
- // src/components/chart/BubbleChart/index.tsx
16165
- var import_jsx_runtime198 = require("react/jsx-runtime");
16166
- function BubbleChart({
16167
- data = [],
16168
- xKey,
16169
- yKey,
16170
- sizeKey,
16171
- seriesKey,
16172
- colors,
16173
- title,
16174
- height,
16175
- onSelect,
16176
- className,
16177
- style
16178
- }) {
16179
- const option = buildBubbleOption(
16180
- { data, xKey, yKey, sizeKey, seriesKey, colors, title },
16181
- useChartTheme()
16182
- );
16183
- return /* @__PURE__ */ (0, import_jsx_runtime198.jsx)(
16184
- EChartBase,
16185
- {
16186
- slot: "bubble-chart",
16187
- option,
16188
- height,
16189
- onSelect,
16190
- className,
16191
- style
16192
- }
16193
- );
16194
- }
16195
-
16196
- // src/components/chart/ColumnChart/index.tsx
16197
- var import_jsx_runtime199 = require("react/jsx-runtime");
16198
- function ColumnChart({
16199
- data = [],
16200
- xKey,
16201
- yKeys,
16202
- stacked = false,
16203
- colors,
16204
- title,
16205
- height,
16206
- onSelect,
16207
- className,
16208
- style
16209
- }) {
16210
- const option = buildColumnOption(
16211
- { data, xKey, yKeys, stacked, colors, title },
16212
- useChartTheme()
16213
- );
16214
- return /* @__PURE__ */ (0, import_jsx_runtime199.jsx)(
16215
- EChartBase,
16216
- {
16217
- slot: "column-chart",
16218
- option,
16219
- height,
16220
- onSelect,
16221
- className,
16222
- style
16223
- }
16224
- );
16225
- }
16226
-
16227
- // src/components/chart/ComposedChart/index.tsx
16228
- var import_jsx_runtime200 = require("react/jsx-runtime");
16229
- function ComposedChart({
16230
- data = [],
16231
- xKey,
16232
- series = [],
16233
- colors,
16234
- title,
16235
- height,
16236
- onSelect,
16237
- className,
16238
- style
16239
- }) {
16240
- const option = buildComposedOption(
16241
- { data, xKey, series, colors, title },
16242
- useChartTheme()
16243
- );
16244
- return /* @__PURE__ */ (0, import_jsx_runtime200.jsx)(
16245
- EChartBase,
16246
- {
16247
- slot: "composed-chart",
16248
- option,
16249
- height,
16250
- onSelect,
16251
- className,
16252
- style
16253
- }
16254
- );
16255
- }
16256
-
16257
- // src/components/chart/DonutChart/index.tsx
16258
- var import_jsx_runtime201 = require("react/jsx-runtime");
16259
- function DonutChart({
16260
- data = [],
16261
- nameKey,
16262
- valueKey,
16263
- innerRadius,
16264
- outerRadius,
16265
- centerLabel,
16266
- centerValue,
16267
- showLabel,
16268
- colors,
16269
- title,
16270
- height,
16271
- onSelect,
16272
- className,
16273
- style
16274
- }) {
16275
- const option = buildDonutOption(
16276
- {
16277
- data,
16278
- nameKey,
16279
- valueKey,
16280
- innerRadius,
16281
- outerRadius,
16282
- centerLabel,
16283
- centerValue,
16284
- showLabel,
16285
- colors,
16286
- title
16287
- },
16288
- useChartTheme()
16289
- );
16290
- return /* @__PURE__ */ (0, import_jsx_runtime201.jsx)(
16291
- EChartBase,
16292
- {
16293
- slot: "donut-chart",
16294
- option,
16295
- height,
16296
- onSelect,
16297
- className,
16298
- style
16299
- }
16300
- );
16301
- }
16302
-
16303
- // src/components/chart/FunnelChart/index.tsx
16304
- var import_jsx_runtime202 = require("react/jsx-runtime");
16305
- function FunnelChart2({
16306
- data = [],
16307
- nameKey,
16308
- valueKey,
16309
- sort,
16310
- colors,
16311
- title,
16312
- height,
16313
- onSelect,
16314
- className,
16315
- style
16316
- }) {
16317
- const option = buildFunnelOption(
16318
- { data, nameKey, valueKey, sort, colors, title },
16319
- useChartTheme()
16320
- );
16321
- return /* @__PURE__ */ (0, import_jsx_runtime202.jsx)(
16322
- EChartBase,
16323
- {
16324
- slot: "funnel-chart",
16325
- option,
16326
- height,
16327
- onSelect,
16328
- className,
16329
- style
16330
- }
16331
- );
16332
- }
16333
-
16334
- // src/components/chart/GaugeChart/index.tsx
16335
- var import_jsx_runtime203 = require("react/jsx-runtime");
16336
- function GaugeChart2({
16337
- value = 0,
16338
- min,
16339
- max,
16340
- thresholds,
16341
- unit,
16342
- title,
16343
- height,
16344
- onSelect,
16345
- className,
16346
- style
16347
- }) {
16348
- const option = buildGaugeOption(
16349
- { value, min, max, thresholds, unit, title },
16350
- useChartTheme()
16351
- );
16352
- return /* @__PURE__ */ (0, import_jsx_runtime203.jsx)(
16353
- EChartBase,
16354
- {
16355
- slot: "gauge-chart",
16356
- option,
16357
- height,
16358
- onSelect,
16359
- className,
16360
- style
16361
- }
16362
- );
16363
- }
16364
-
16365
- // src/components/chart/Heatmap/index.tsx
16366
- var import_jsx_runtime204 = require("react/jsx-runtime");
16367
- function Heatmap({
16368
- data = [],
16369
- xKey,
16370
- yKey,
16371
- valueKey,
16372
- title,
16373
- height,
16374
- onSelect,
16375
- className,
16376
- style
16377
- }) {
16378
- const option = buildHeatmapOption(
16379
- { data, xKey, yKey, valueKey, title },
16380
- useChartTheme()
16381
- );
16382
- return /* @__PURE__ */ (0, import_jsx_runtime204.jsx)(
16383
- EChartBase,
16384
- {
16385
- slot: "heatmap",
16386
- option,
16387
- height,
16388
- onSelect,
16389
- className,
16390
- style
16391
- }
16392
- );
16393
- }
16394
-
16395
- // src/components/chart/LineChart/index.tsx
16396
- var import_jsx_runtime205 = require("react/jsx-runtime");
16397
- function LineChart2({
16398
- data = [],
16399
- xKey,
16400
- yKeys,
16401
- smooth = false,
16402
- showArea = false,
16403
- stacked = false,
16404
- colors,
16405
- title,
16406
- height,
16407
- onSelect,
16408
- className,
16409
- style
16410
- }) {
16411
- const option = buildLineOption(
16412
- { data, xKey, yKeys, smooth, showArea, stacked, colors, title },
16413
- useChartTheme()
16414
- );
16415
- return /* @__PURE__ */ (0, import_jsx_runtime205.jsx)(
16416
- EChartBase,
16417
- {
16418
- slot: "line-chart",
16419
- option,
16420
- height,
16421
- onSelect,
16422
- className,
16423
- style
16424
- }
16425
- );
16426
- }
16427
-
16428
- // src/components/chart/MapChart/index.tsx
16429
- var import_react73 = require("react");
16430
- var import_jsx_runtime206 = require("react/jsx-runtime");
16431
- var fetchedMaps = /* @__PURE__ */ new Set();
16432
- function hasRegisteredMap(mapName) {
16433
- try {
16434
- return typeof echarts.getMap === "function" && echarts.getMap(mapName) != null;
16435
- } catch {
16436
- return false;
16437
- }
16438
- }
16439
- function MapChart2({
16440
- mapName = "map",
16441
- data = [],
16442
- nameKey,
16443
- valueKey,
16444
- geoJson,
16445
- geoJsonUrl,
16446
- title,
16447
- height,
16448
- onSelect,
16449
- className,
16450
- style
16451
- }) {
16452
- const theme = useChartTheme();
16453
- const [loadState, setLoadState] = (0, import_react73.useState)(
16454
- () => geoJson || !geoJsonUrl || hasRegisteredMap(mapName) ? "idle" : "loading"
16455
- );
16456
- if (geoJson) {
16457
- try {
16458
- echarts.registerMap(mapName, geoJson);
16459
- } catch {
16460
- }
16461
- }
16462
- (0, import_react73.useEffect)(() => {
16463
- if (geoJson || !geoJsonUrl || hasRegisteredMap(mapName) || fetchedMaps.has(mapName)) {
16464
- setLoadState("idle");
16465
- return;
16466
- }
16467
- let cancelled = false;
16468
- setLoadState("loading");
16469
- fetch(geoJsonUrl).then((res) => {
16470
- if (!res.ok) throw new Error(`HTTP ${res.status}`);
16471
- return res.json();
16472
- }).then((json) => {
16473
- if (cancelled) return;
16474
- echarts.registerMap(mapName, json);
16475
- fetchedMaps.add(mapName);
16476
- setLoadState("ready");
16477
- }).catch(() => {
16478
- if (cancelled) return;
16479
- console.warn(`[MapChart] \u5730\u56FE\u6570\u636E\u52A0\u8F7D\u5931\u8D25\uFF1A${geoJsonUrl}`);
16480
- setLoadState("error");
16481
- });
16482
- return () => {
16483
- cancelled = true;
16484
- };
16485
- }, [geoJson, geoJsonUrl, mapName]);
16486
- if (geoJsonUrl && !geoJson && loadState === "loading") {
16487
- return /* @__PURE__ */ (0, import_jsx_runtime206.jsx)(
16488
- "div",
16489
- {
16490
- "data-slot": "map-chart-loading",
16491
- className,
16492
- style: { height: typeof height === "number" ? `${height}px` : height ?? 256, ...style }
16493
- }
16494
- );
16495
- }
16496
- if (!geoJson && loadState === "error") {
16497
- return /* @__PURE__ */ (0, import_jsx_runtime206.jsx)(
16498
- Empty2,
16499
- {
16500
- title: "\u5730\u56FE\u52A0\u8F7D\u5931\u8D25",
16501
- description: "\u8BF7\u68C0\u67E5 geoJsonUrl \u7F51\u7EDC\u53EF\u8FBE\u6027\uFF0C\u6216\u6539\u7528 geoJson / \u9884\u6CE8\u518C\u5730\u56FE",
16502
- className,
16503
- style
16504
- }
16505
- );
16506
- }
16507
- if (!geoJson && !hasRegisteredMap(mapName)) {
16508
- console.warn(`[MapChart] \u5730\u56FE "${mapName}" \u672A\u6CE8\u518C\uFF1A\u8BF7\u4F20\u5165 geoJson / geoJsonUrl\uFF0C\u6216\u5728\u5E94\u7528\u542F\u52A8\u65F6 echarts.registerMap`);
16509
- return /* @__PURE__ */ (0, import_jsx_runtime206.jsx)(
16510
- Empty2,
16511
- {
16512
- title: "\u5730\u56FE\u672A\u6CE8\u518C",
16513
- description: "\u8BF7\u4F20\u5165 geoJson / geoJsonUrl\uFF0C\u6216\u5728\u5E94\u7528\u542F\u52A8\u65F6\u6CE8\u518C\u5730\u56FE\u6570\u636E",
16514
- className,
16515
- style
16516
- }
16517
- );
16518
- }
16519
- const option = buildMapOption({ mapName, data, nameKey, valueKey, title }, theme);
16520
- return /* @__PURE__ */ (0, import_jsx_runtime206.jsx)(
16521
- EChartBase,
16522
- {
16523
- slot: "map-chart",
16524
- option,
16525
- height,
16526
- onSelect,
16527
- className,
16528
- style
16529
- }
16530
- );
16531
- }
16532
-
16533
- // src/components/chart/MetricCard/index.tsx
16534
- var import_lucide_react40 = require("lucide-react");
16535
- var import_jsx_runtime207 = require("react/jsx-runtime");
16536
- function MetricCard({
16537
- title,
16538
- value,
16539
- prefix,
16540
- suffix,
16541
- trend,
16542
- trendLabel,
16543
- className,
16544
- style
16545
- }) {
16546
- const showTrend = typeof trend === "number" && trend !== 0;
16547
- const rising = (trend ?? 0) > 0;
16548
- return /* @__PURE__ */ (0, import_jsx_runtime207.jsxs)(
16549
- "div",
16550
- {
16551
- "data-slot": "metric-card",
16552
- className: cn("rounded-lg border bg-card p-4 text-card-foreground shadow-sm", className),
16553
- style,
16554
- children: [
16555
- title ? /* @__PURE__ */ (0, import_jsx_runtime207.jsx)("div", { "data-slot": "metric-card-title", className: "text-sm text-muted-foreground", children: title }) : null,
16556
- /* @__PURE__ */ (0, import_jsx_runtime207.jsxs)(
16557
- "div",
16558
- {
16559
- "data-slot": "metric-card-value",
16560
- className: "mt-1 text-2xl font-semibold tracking-tight tabular-nums",
16561
- children: [
16562
- prefix,
16563
- value,
16564
- suffix
16565
- ]
16566
- }
16567
- ),
16568
- showTrend ? /* @__PURE__ */ (0, import_jsx_runtime207.jsxs)(
16569
- "div",
16570
- {
16571
- "data-slot": "metric-card-trend",
16572
- "data-trend": rising ? "up" : "down",
16573
- className: cn(
16574
- "mt-1 inline-flex items-center gap-0.5 text-sm",
16575
- rising ? "text-success" : "text-destructive"
16576
- ),
16577
- children: [
16578
- rising ? /* @__PURE__ */ (0, import_jsx_runtime207.jsx)(import_lucide_react40.ArrowUpRight, { className: "size-4" }) : /* @__PURE__ */ (0, import_jsx_runtime207.jsx)(import_lucide_react40.ArrowDownRight, { className: "size-4" }),
16579
- Math.abs(trend ?? 0),
16580
- "%",
16581
- trendLabel ? /* @__PURE__ */ (0, import_jsx_runtime207.jsx)("span", { className: "ml-1 text-muted-foreground", children: trendLabel }) : null
16582
- ]
16583
- }
16584
- ) : null
16585
- ]
16586
- }
16587
- );
16588
- }
16589
-
16590
- // src/components/chart/PieChart/index.tsx
16591
- var import_jsx_runtime208 = require("react/jsx-runtime");
16592
- function PieChart2({
16593
- data = [],
16594
- nameKey,
16595
- valueKey,
16596
- showLabel,
16597
- colors,
16598
- title,
16599
- height,
16600
- onSelect,
16601
- className,
16602
- style
16603
- }) {
16604
- const option = buildPieOption(
16605
- { data, nameKey, valueKey, showLabel, colors, title },
16606
- useChartTheme()
16607
- );
16608
- return /* @__PURE__ */ (0, import_jsx_runtime208.jsx)(
16609
- EChartBase,
16610
- {
16611
- slot: "pie-chart",
16612
- option,
16613
- height,
16614
- onSelect,
16615
- className,
16616
- style
16617
- }
16618
- );
16619
- }
16620
-
16621
- // src/components/chart/RadarChart/index.tsx
16622
- var import_jsx_runtime209 = require("react/jsx-runtime");
16623
- function RadarChart2({
16624
- data = [],
16625
- indicatorKey,
16626
- yKeys,
16627
- colors,
16628
- title,
16629
- height,
16630
- onSelect,
16631
- className,
16632
- style
16633
- }) {
16634
- const option = buildRadarOption(
16635
- { data, indicatorKey, yKeys, colors, title },
16636
- useChartTheme()
16637
- );
16638
- return /* @__PURE__ */ (0, import_jsx_runtime209.jsx)(
16639
- EChartBase,
16640
- {
16641
- slot: "radar-chart",
16642
- option,
16643
- height,
16644
- onSelect,
16645
- className,
16646
- style
16647
- }
16648
- );
16649
- }
16650
-
16651
- // src/components/chart/SankeyChart/index.tsx
16652
- var import_jsx_runtime210 = require("react/jsx-runtime");
16653
- function SankeyChart2({
16654
- nodes = [],
16655
- links = [],
16656
- title,
16657
- height,
16658
- onSelect,
16659
- className,
16660
- style
16661
- }) {
16662
- const option = buildSankeyOption({ nodes, links, title }, useChartTheme());
16663
- return /* @__PURE__ */ (0, import_jsx_runtime210.jsx)(
16664
- EChartBase,
16665
- {
16666
- slot: "sankey-chart",
16667
- option,
16668
- height,
16669
- onSelect,
16670
- className,
16671
- style
16672
- }
16673
- );
16674
- }
16675
-
16676
- // src/components/chart/ScatterChart/index.tsx
16677
- var import_jsx_runtime211 = require("react/jsx-runtime");
16678
- function ScatterChart2({
16679
- data = [],
16680
- xKey,
16681
- yKey,
16682
- seriesKey,
16683
- colors,
16684
- title,
16685
- height,
16686
- onSelect,
16687
- className,
16688
- style
16689
- }) {
16690
- const option = buildScatterOption(
16691
- { data, xKey, yKey, seriesKey, colors, title },
16692
- useChartTheme()
16693
- );
16694
- return /* @__PURE__ */ (0, import_jsx_runtime211.jsx)(
16695
- EChartBase,
16696
- {
16697
- slot: "scatter-chart",
16698
- option,
16699
- height,
16700
- onSelect,
16701
- className,
16702
- style
16703
- }
16704
- );
16705
- }
16706
-
16707
- // src/components/chart/TreeChart/index.tsx
16708
- var import_jsx_runtime212 = require("react/jsx-runtime");
16709
- function TreeChart2({
16710
- data = [],
16711
- title,
16712
- orient,
16713
- edgeShape,
16714
- initialTreeDepth,
16715
- height,
16716
- onSelect,
16717
- className,
16718
- style
16719
- }) {
16720
- const option = buildTreeOption(
16721
- { data, title, orient, edgeShape, initialTreeDepth },
16722
- useChartTheme()
16723
- );
16724
- return /* @__PURE__ */ (0, import_jsx_runtime212.jsx)(
16725
- EChartBase,
16726
- {
16727
- slot: "tree-chart",
16728
- option,
16729
- height,
16730
- onSelect,
16731
- className,
16732
- style
16733
- }
16734
- );
16735
- }
16736
-
16737
- // src/components/business/CardList/index.tsx
16738
- var import_runtime52 = require("@pactor-app/runtime");
16739
-
16740
- // src/components/business/EntityCard/index.tsx
16741
- var import_jsx_runtime213 = require("react/jsx-runtime");
16742
- function EntityCard({
16743
- icon,
16744
- logo,
16745
- title,
16746
- subtitle,
16747
- tags,
16748
- description,
16749
- meta,
16750
- actions,
16751
- footerBackground = true,
16752
- hoverable,
16753
- className,
16754
- contentClassName,
16755
- style,
16756
- onClick,
16757
- onAction
16758
- }) {
16759
- return /* @__PURE__ */ (0, import_jsx_runtime213.jsx)(
16760
- Card2,
16761
- {
16762
- hoverable,
16763
- className,
16764
- contentClassName,
16765
- style,
16766
- onClick,
16767
- children: /* @__PURE__ */ (0, import_jsx_runtime213.jsxs)("div", { "data-slot": "entity-card", className: "flex flex-col gap-2", children: [
16768
- /* @__PURE__ */ (0, import_jsx_runtime213.jsxs)("div", { "data-slot": "entity-card-header", className: "flex items-center justify-between gap-2", children: [
16769
- /* @__PURE__ */ (0, import_jsx_runtime213.jsxs)("div", { className: "flex min-w-0 items-center gap-2", children: [
16770
- logo ? /* @__PURE__ */ (0, import_jsx_runtime213.jsx)("img", { src: logo, alt: title ?? "", className: "size-9 shrink-0 rounded-md" }) : icon ? /* @__PURE__ */ (0, import_jsx_runtime213.jsx)("span", { className: "flex size-9 shrink-0 items-center justify-center rounded-md bg-muted", children: /* @__PURE__ */ (0, import_jsx_runtime213.jsx)(Icon, { name: icon }) }) : null,
16771
- title || subtitle ? /* @__PURE__ */ (0, import_jsx_runtime213.jsxs)("span", { className: "flex min-w-0 flex-col gap-0.5", children: [
16772
- title ? /* @__PURE__ */ (0, import_jsx_runtime213.jsx)(Text, { text: title, strong: true, className: "truncate" }) : null,
16773
- subtitle ? /* @__PURE__ */ (0, import_jsx_runtime213.jsx)(Text, { text: subtitle, type: "secondary", size: "small" }) : null
16774
- ] }) : null
16775
- ] }),
16776
- tags?.length ? /* @__PURE__ */ (0, import_jsx_runtime213.jsx)("span", { className: "flex shrink-0 items-center gap-1", children: tags.map((tag, i) => /* @__PURE__ */ (0, import_jsx_runtime213.jsx)(Tag2, { text: tag.text, color: tag.color }, `${tag.text}-${i}`)) }) : null
16777
- ] }),
16778
- description || meta ? /* @__PURE__ */ (0, import_jsx_runtime213.jsxs)("div", { "data-slot": "entity-card-body", className: "flex items-center justify-between gap-4", children: [
16779
- description ? /* @__PURE__ */ (0, import_jsx_runtime213.jsx)(
16780
- Text,
16781
- {
16782
- text: description,
16783
- type: "secondary",
16784
- size: "small",
16785
- className: "line-clamp-2",
16786
- style: { minHeight: 32 }
16787
- }
16788
- ) : null,
16789
- meta ? /* @__PURE__ */ (0, import_jsx_runtime213.jsx)(Text, { text: meta, type: "secondary", size: "small", className: "shrink-0" }) : null
16790
- ] }) : null,
16791
- actions?.length ? /* @__PURE__ */ (0, import_jsx_runtime213.jsx)(
16792
- "div",
16793
- {
16794
- "data-slot": "entity-card-footer",
16795
- className: cn(
16796
- "mt-1 flex items-center justify-end gap-1",
16797
- footerBackground && "-mx-4 -mb-4 rounded-b-lg bg-muted px-4 py-2"
16798
- ),
16799
- onClick: (event) => event.stopPropagation(),
16800
- children: actions.map((action, index) => /* @__PURE__ */ (0, import_jsx_runtime213.jsx)(
16801
- Button2,
16802
- {
16803
- text: action.text,
16804
- variant: action.variant,
16805
- danger: action.danger,
16806
- onClick: () => onAction?.({ key: action.key ?? action.text, text: action.text, index })
16807
- },
16808
- `${action.key ?? action.text}-${index}`
16809
- ))
16810
- }
16811
- ) : null
16812
- ] })
16813
- }
16814
- );
16815
- }
16816
-
16817
- // src/components/business/CardList/index.tsx
16818
- var import_jsx_runtime214 = require("react/jsx-runtime");
16819
- function CardList({
16820
- items,
16821
- columns = 3,
16822
- gap = 16,
16823
- createText,
16824
- createIcon = "plus",
16825
- hoverable,
16826
- showCount,
16827
- emptyText,
16828
- className,
16829
- style,
16830
- onItemClick,
16831
- onItemAction,
16832
- onCreateClick
16833
- }) {
16834
- const { t } = (0, import_runtime52.useI18n)();
16835
- const list = items ?? [];
16836
- return /* @__PURE__ */ (0, import_jsx_runtime214.jsxs)("div", { "data-slot": "card-list", className: cn("flex flex-col gap-3", className), style, children: [
16837
- list.length === 0 && !createText ? /* @__PURE__ */ (0, import_jsx_runtime214.jsx)(Empty2, { title: emptyText ?? t("pactor.table.empty") }) : /* @__PURE__ */ (0, import_jsx_runtime214.jsxs)(
16838
- "div",
16839
- {
16840
- "data-slot": "card-list-grid",
16841
- className: "grid",
16842
- style: { gridTemplateColumns: `repeat(${columns}, minmax(0, 1fr))`, gap },
16843
- children: [
16844
- createText ? /* @__PURE__ */ (0, import_jsx_runtime214.jsxs)(
16845
- Card2,
16846
- {
16847
- hoverable: true,
16848
- className: "h-full border-dashed",
16849
- contentClassName: "flex flex-1 flex-col items-center justify-center gap-2",
16850
- onClick: onCreateClick,
16851
- children: [
16852
- /* @__PURE__ */ (0, import_jsx_runtime214.jsx)(Icon, { name: createIcon, size: "lg", className: "text-muted-foreground" }),
16853
- /* @__PURE__ */ (0, import_jsx_runtime214.jsx)(Text, { text: createText, type: "secondary" })
16854
- ]
16855
- }
16856
- ) : null,
16857
- list.map((item, index) => /* @__PURE__ */ (0, import_jsx_runtime214.jsx)(
16858
- EntityCard,
16859
- {
16860
- icon: item.icon,
16861
- logo: item.logo,
16862
- title: item.title,
16863
- subtitle: item.subtitle,
16864
- tags: item.tags,
16865
- description: item.description,
16866
- meta: item.meta,
16867
- actions: item.actions,
16868
- hoverable,
16869
- onClick: onItemClick ? () => onItemClick({ item, index }) : void 0,
16870
- onAction: onItemAction ? ({ key, text }) => onItemAction({ key, text, item, index }) : void 0
16871
- },
16872
- index
16873
- ))
16874
- ]
16875
- }
16876
- ),
16877
- showCount ? /* @__PURE__ */ (0, import_jsx_runtime214.jsx)(
16878
- Text,
16879
- {
16880
- text: t("pactor.cardList.total", { count: list.length }),
16881
- type: "secondary",
16882
- size: "small"
16883
- }
16884
- ) : null
16885
- ] });
16886
- }
16887
-
16888
- // src/components/business/ExpressionEditor/index.tsx
16889
- var import_react75 = require("react");
16890
-
16891
- // src/components/business/FieldCondition/index.tsx
16892
- var import_lucide_react42 = require("lucide-react");
16893
- var import_react74 = require("react");
16894
-
16895
- // src/components/business/SearchableSelect/index.tsx
16896
- var import_lucide_react41 = require("lucide-react");
16897
- var React8 = __toESM(require("react"), 1);
16898
- var import_jsx_runtime215 = require("react/jsx-runtime");
16899
- var DEFAULT_PLACEHOLDER = "\u8BF7\u9009\u62E9";
16900
- var DEFAULT_SEARCH_PLACEHOLDER = "\u641C\u7D22\u2026";
16901
- var DEFAULT_EMPTY_TEXT = "\u65E0\u5339\u914D\u7ED3\u679C";
16902
- var NO_OPTIONS_TEXT = "\u65E0\u9009\u9879";
16903
- function SearchableSelect({
16904
- value,
16905
- onChange,
16906
- options,
16907
- placeholder,
16908
- searchPlaceholder,
16909
- emptyText,
16910
- disabled = false,
16911
- allowClear = true,
16912
- size = "middle",
16913
- className,
16914
- id,
16915
- showSearch,
16916
- multiple = false,
16917
- fullOptionText = false,
16918
- listClassName
16919
- }) {
16920
- const resolvedPlaceholder = placeholder ?? DEFAULT_PLACEHOLDER;
16921
- const resolvedSearchPlaceholder = searchPlaceholder ?? DEFAULT_SEARCH_PLACEHOLDER;
16922
- const resolvedEmptyText = emptyText ?? DEFAULT_EMPTY_TEXT;
16923
- const [open, setOpen] = React8.useState(false);
16924
- const shouldShowSearch = showSearch ?? options.length > 8;
16925
- const selectedValues = multiple ? Array.isArray(value) ? value : value ? [value] : [] : [];
16926
- const selectedLabel = React8.useMemo(
16927
- () => !multiple ? options.find((opt) => opt.value === value)?.label : void 0,
16928
- [options, value, multiple]
16929
- );
16930
- const handleSelect = (optValue) => {
16931
- if (!multiple) {
16932
- onChange?.(optValue);
16933
- setOpen(false);
16934
- return;
16935
- }
16936
- const next = selectedValues.includes(optValue) ? selectedValues.filter((v) => v !== optValue) : [...selectedValues, optValue];
16937
- onChange?.(next);
16938
- };
16939
- const handleRemove = (e, optValue) => {
16940
- e.stopPropagation();
16941
- onChange?.(selectedValues.filter((v) => v !== optValue));
16942
- };
16943
- return /* @__PURE__ */ (0, import_jsx_runtime215.jsxs)(Popover, { open, onOpenChange: setOpen, children: [
16944
- /* @__PURE__ */ (0, import_jsx_runtime215.jsxs)(
16945
- PopoverTrigger,
16946
- {
16947
- id,
16948
- type: "button",
16949
- role: "combobox",
16950
- "aria-expanded": open,
16951
- disabled,
16952
- className: cn(
16953
- "group border-input bg-background ring-offset-background flex min-h-9 w-full items-center justify-between rounded-md border px-3 py-2 text-sm",
16954
- "placeholder:text-muted-foreground min-w-32",
16955
- "focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2",
16956
- "disabled:cursor-not-allowed disabled:opacity-50",
16957
- CONTROL_SIZE_BOX_GROW[size],
16958
- multiple && "flex-wrap gap-1",
16959
- className
16960
- ),
16961
- children: [
16962
- multiple ? selectedValues.length > 0 ? /* @__PURE__ */ (0, import_jsx_runtime215.jsx)("div", { className: "flex flex-wrap gap-1 flex-1", children: selectedValues.map((v) => {
16963
- const label = options.find((o) => o.value === v)?.label ?? v;
16964
- return /* @__PURE__ */ (0, import_jsx_runtime215.jsxs)(
16965
- "span",
16966
- {
16967
- className: "inline-flex items-center gap-1 rounded bg-muted px-2 py-0.5 text-xs font-medium",
16968
- children: [
16969
- label,
16970
- /* @__PURE__ */ (0, import_jsx_runtime215.jsx)(
16971
- import_lucide_react41.X,
16972
- {
16973
- className: "h-3 w-3 cursor-pointer opacity-60 hover:opacity-100",
16974
- onClick: (e) => handleRemove(e, v)
16975
- }
16976
- )
16977
- ]
16978
- },
16979
- v
16980
- );
16981
- }) }) : /* @__PURE__ */ (0, import_jsx_runtime215.jsx)("span", { className: "text-muted-foreground flex-1 text-left", children: resolvedPlaceholder }) : /* @__PURE__ */ (0, import_jsx_runtime215.jsx)("span", { className: cn("truncate", !selectedLabel && "text-muted-foreground"), children: selectedLabel || resolvedPlaceholder }),
16982
- allowClear && !multiple && !disabled && value !== void 0 && value !== "" ? /* @__PURE__ */ (0, import_jsx_runtime215.jsx)(
16983
- ClearHoverButton,
16984
- {
16985
- label: "clear-selection",
16986
- className: "ml-2 rounded-sm opacity-50 hover:opacity-100",
16987
- onClear: () => onChange?.("")
16988
- }
16989
- ) : null,
16990
- /* @__PURE__ */ (0, import_jsx_runtime215.jsx)(
16991
- import_lucide_react41.ChevronsUpDown,
16992
- {
16993
- className: cn(
16994
- "ml-2 h-4 w-4 shrink-0 opacity-50",
16995
- allowClear && !multiple && !disabled && value !== void 0 && value !== "" && "group-hover:hidden"
16996
- )
16997
- }
16998
- )
16999
- ]
17000
- }
17001
- ),
17002
- /* @__PURE__ */ (0, import_jsx_runtime215.jsx)(
17003
- PopoverContent,
17004
- {
17005
- className: cn(
17006
- "p-0",
17007
- // Base UI Positioner 在定位元素上暴露 --anchor-width(原 Radix 的
17008
- // --radix-popover-trigger-width 在 Base UI 下不存在,会退化为 auto 宽度)
17009
- fullOptionText ? "w-auto min-w-(--anchor-width)" : "w-(--anchor-width)"
17010
- ),
17011
- align: "start",
17012
- onWheel: (e) => e.stopPropagation(),
17013
- onTouchMove: (e) => e.stopPropagation(),
17014
- children: /* @__PURE__ */ (0, import_jsx_runtime215.jsxs)(Command, { value: !multiple ? selectedLabel : void 0, children: [
17015
- shouldShowSearch && /* @__PURE__ */ (0, import_jsx_runtime215.jsx)(CommandInput, { placeholder: resolvedSearchPlaceholder }),
17016
- /* @__PURE__ */ (0, import_jsx_runtime215.jsxs)(CommandList, { className: listClassName, children: [
17017
- /* @__PURE__ */ (0, import_jsx_runtime215.jsx)(CommandEmpty, { children: options.length === 0 ? NO_OPTIONS_TEXT : resolvedEmptyText }),
17018
- /* @__PURE__ */ (0, import_jsx_runtime215.jsx)(CommandGroup, { children: options.map((opt) => {
17019
- const isSelected = multiple ? selectedValues.includes(opt.value) : value === opt.value;
17020
- return /* @__PURE__ */ (0, import_jsx_runtime215.jsxs)(
17021
- CommandItem,
17022
- {
17023
- value: opt.label,
17024
- onSelect: () => handleSelect(opt.value),
17025
- className: "flex items-center justify-between",
17026
- children: [
17027
- /* @__PURE__ */ (0, import_jsx_runtime215.jsx)("span", { className: cn(fullOptionText ? "whitespace-normal" : "truncate"), children: opt.label }),
17028
- /* @__PURE__ */ (0, import_jsx_runtime215.jsx)(
17029
- import_lucide_react41.Check,
17030
- {
17031
- className: cn(
17032
- "ml-2 h-4 w-4 shrink-0",
17033
- isSelected ? "opacity-100" : "opacity-0"
17034
- )
17035
- }
17036
- )
17037
- ]
17038
- },
17039
- opt.value
17040
- );
17041
- }) })
17042
- ] })
17043
- ] })
17044
- }
17045
- )
17046
- ] });
17047
- }
17048
-
17049
- // src/components/business/FieldCondition/index.tsx
17050
- var import_jsx_runtime216 = require("react/jsx-runtime");
17051
- var OPERATORS = [
17052
- { value: "EQUAL", valueTypes: ["NUMBER", "STRING", "DATE"] },
17053
- { value: "GREATER", valueTypes: ["NUMBER", "DATE"] },
17054
- { value: "GREATER_EQUAL", valueTypes: ["NUMBER", "DATE"] },
17055
- { value: "LESS", valueTypes: ["NUMBER", "DATE"] },
17056
- { value: "LESS_EQUAL", valueTypes: ["NUMBER", "DATE"] },
17057
- { value: "UNEQUAL", valueTypes: ["NUMBER", "STRING", "DATE"] },
17058
- { value: "BETWEEN", valueTypes: ["NUMBER", "DATE"] },
17059
- { value: "HASVAL", valueTypes: "ALL" },
17060
- { value: "NOHASVAL", valueTypes: "ALL" },
17061
- { value: "TRUE", valueTypes: ["BOOLEAN"] },
17062
- { value: "FALSE", valueTypes: ["BOOLEAN"] },
17063
- { value: "IN", valueTypes: ["NUMBER", "STRING"] },
17064
- { value: "NOT_IN", valueTypes: ["NUMBER", "STRING"] }
17065
- ];
17066
- var NO_VALUE_OPERATORS = ["HASVAL", "NOHASVAL", "TRUE", "FALSE"];
17067
- var OPERATOR_LABELS = {
17068
- EQUAL: "\u7B49\u4E8E",
17069
- GREATER: "\u5927\u4E8E",
17070
- GREATER_EQUAL: "\u5927\u4E8E\u7B49\u4E8E",
17071
- LESS: "\u5C0F\u4E8E",
17072
- LESS_EQUAL: "\u5C0F\u4E8E\u7B49\u4E8E",
17073
- UNEQUAL: "\u4E0D\u7B49\u4E8E",
17074
- BETWEEN: "\u8303\u56F4",
17075
- HASVAL: "\u6709\u503C",
17076
- NOHASVAL: "\u65E0\u503C",
17077
- TRUE: "\u4E3A\u771F",
17078
- FALSE: "\u4E3A\u5047",
17079
- IN: "\u5305\u542B",
17080
- NOT_IN: "\u4E0D\u5305\u542B"
17081
- };
17082
- function operatorLabel(operator) {
17083
- return OPERATOR_LABELS[operator] ?? operator;
17084
- }
17085
- function getOperatorsByFieldType(fieldType) {
17086
- return OPERATORS.filter(
17087
- (op) => !fieldType || op.valueTypes === "ALL" || op.valueTypes.includes(fieldType)
17088
- );
17089
- }
17090
- function isNoValueOperator(operator) {
17091
- return !!operator && NO_VALUE_OPERATORS.includes(operator);
17092
- }
17093
- var OPERATOR_STATEMENTS = {
17094
- EQUAL: "${field} == ${value}",
17095
- GREATER: "${field} > ${value}",
17096
- GREATER_EQUAL: "${field} >= ${value}",
17097
- LESS: "${field} < ${value}",
17098
- LESS_EQUAL: "${field} <= ${value}",
17099
- UNEQUAL: "${field} != ${value}",
17100
- BETWEEN: "${field} BETWEEN ${v1} AND ${v2}",
17101
- HASVAL: "${field} HASVAL",
17102
- NOHASVAL: "${field} NOHASVAL",
17103
- TRUE: "${field} == TRUE",
17104
- FALSE: "${field} == FALSE",
17105
- IN: "${field} IN (${value})",
17106
- NOT_IN: "${field} NOT IN (${value})"
17107
- };
17108
- function formatConditionValue(item) {
17109
- const isString = item.field.dataType === "STRING";
17110
- const quote = (v) => isString ? `'${v}'` : String(v);
17111
- if (item.term.value === "IN" || item.term.value === "NOT_IN") {
17112
- return String(item.value ?? "").split(",").map((s) => s.trim()).filter(Boolean).map(quote).join(", ");
17113
- }
17114
- return quote(item.value ?? "");
17115
- }
17116
- function itemToStatement(item) {
17117
- const fieldCode = item.field?.value;
17118
- const termValue = item.term?.value;
17119
- const statement = termValue ? OPERATOR_STATEMENTS[termValue] : void 0;
17120
- if (!fieldCode || !statement) return null;
17121
- if (termValue === "BETWEEN") {
17122
- const arr = Array.isArray(item.value) ? item.value : [];
17123
- const isString = item.field.dataType === "STRING";
17124
- const quote = (v) => isString ? `'${v ?? ""}'` : String(v ?? "");
17125
- return statement.replace("${field}", fieldCode).replace("${v1}", quote(arr[0])).replace("${v2}", quote(arr[1]));
17126
- }
17127
- let result = statement.replace("${field}", fieldCode);
17128
- if (!isNoValueOperator(termValue)) {
17129
- result = result.replace("${value}", formatConditionValue(item));
17130
- }
17131
- return result;
17132
- }
17133
- function conditionToStatement(value) {
17134
- if (!value?.children?.length) return "";
17135
- const groupParts = value.children.map((group) => {
17136
- const joiner = group.relation === "AND" ? " && " : " || ";
17137
- const parts = group.children.map(itemToStatement).filter((s) => !!s);
17138
- if (!parts.length) return "";
17139
- const joined = parts.join(joiner);
17140
- return parts.length > 1 ? `(${joined})` : joined;
17141
- }).filter(Boolean);
17142
- return groupParts.join(value.relation === "AND" ? " && " : " || ");
17143
- }
17144
- function splitByJoiner(expr, joiner) {
17145
- const parts = [];
17146
- const token = ` ${joiner} `;
17147
- let depth = 0;
17148
- let quote = null;
17149
- let current = "";
17150
- for (let i = 0; i < expr.length; i++) {
17151
- const ch = expr[i];
17152
- if (quote) {
17153
- current += ch;
17154
- if (ch === quote) quote = null;
17155
- continue;
17156
- }
17157
- if (ch === "'" || ch === '"') {
17158
- quote = ch;
17159
- current += ch;
17160
- continue;
17161
- }
17162
- if (ch === "(") depth++;
17163
- if (ch === ")") depth--;
17164
- if (depth === 0 && expr.startsWith(token, i)) {
17165
- parts.push(current);
17166
- current = "";
17167
- i += token.length - 1;
17168
- continue;
17169
- }
17170
- current += ch;
17171
- }
17172
- parts.push(current);
17173
- return parts.map((s) => s.trim()).filter(Boolean);
17174
- }
17175
- function isWrappedByOuterParens(s) {
17176
- if (!s.startsWith("(") || !s.endsWith(")")) return false;
17177
- let depth = 0;
17178
- let quote = null;
17179
- for (let i = 0; i < s.length; i++) {
17180
- const ch = s[i];
17181
- if (quote) {
17182
- if (ch === quote) quote = null;
17183
- continue;
17184
- }
17185
- if (ch === "'" || ch === '"') {
17186
- quote = ch;
17187
- continue;
17188
- }
17189
- if (ch === "(") depth++;
17190
- if (ch === ")") {
17191
- depth--;
17192
- if (depth === 0 && i < s.length - 1) return false;
17193
- }
17194
- }
17195
- return depth === 0;
17196
- }
17197
- function unquote(v) {
17198
- const t = v.trim();
17199
- if (t.length >= 2 && (t.startsWith("'") && t.endsWith("'") || t.startsWith('"') && t.endsWith('"'))) {
17200
- return t.slice(1, -1);
17201
- }
17202
- return t;
17203
- }
17204
- var COMPARE_OPERATOR_MAP = {
17205
- "==": "EQUAL",
17206
- "!=": "UNEQUAL",
17207
- ">": "GREATER",
17208
- ">=": "GREATER_EQUAL",
17209
- "<": "LESS",
17210
- "<=": "LESS_EQUAL"
17211
- };
17212
- function parseAtom(atom, fields) {
17213
- const s = atom.trim();
17214
- if (!s) return null;
17215
- const fieldInfo = (code) => {
17216
- const f = fields?.find((x) => x.featureCode === code);
17217
- return { value: code, label: f?.featureName, dataType: f?.fieldType };
17218
- };
17219
- let m = s.match(/^([\w.]+)\s+BETWEEN\s+(.+?)\s+AND\s+(.+)$/i);
17220
- if (m) {
17221
- return {
17222
- field: fieldInfo(m[1]),
17223
- term: { value: "BETWEEN" },
17224
- value: [unquote(m[2]), unquote(m[3])]
17225
- };
17226
- }
17227
- m = s.match(/^([\w.]+)\s+(NOT\s+IN|IN)\s*\((.*)\)\s*$/i);
17228
- if (m) {
17229
- const vals = m[3].split(",").map((v) => unquote(v)).filter((v) => v !== "");
17230
- return {
17231
- field: fieldInfo(m[1]),
17232
- term: { value: m[2].toUpperCase().replace(/\s+/g, "_") },
17233
- value: vals.join(", ")
17234
- };
17235
- }
17236
- m = s.match(/^([\w.]+)\s*(==|!=|>=|<=|>|<)\s*(.+)$/);
17237
- if (m) {
17238
- const raw = m[3].trim();
17239
- const val = unquote(raw);
17240
- if (m[2] === "==" && raw === val && (val === "TRUE" || val === "FALSE")) {
17241
- return { field: fieldInfo(m[1]), term: { value: val }, value: void 0 };
17242
- }
17243
- return {
17244
- field: fieldInfo(m[1]),
17245
- term: { value: COMPARE_OPERATOR_MAP[m[2]] },
17246
- value: val
17247
- };
17248
- }
17249
- m = s.match(/^([\w.]+)\s+(HASVAL|NOHASVAL)\s*$/i);
17250
- if (m) {
17251
- return {
17252
- field: fieldInfo(m[1]),
17253
- term: { value: m[2].toUpperCase() },
17254
- value: void 0
17255
- };
17256
- }
17257
- return null;
17258
- }
17259
- function statementToCondition(statement, fields) {
17260
- const trimmed = statement?.trim();
17261
- if (!trimmed) return null;
17262
- const stripOuterParens = (s) => {
17263
- const t = s.trim();
17264
- return isWrappedByOuterParens(t) ? t.slice(1, -1).trim() : t;
17265
- };
17266
- const toGroup = (relation, parts) => {
17267
- if (parts.some(
17268
- (s) => splitByJoiner(s, "&&").length > 1 || splitByJoiner(s, "||").length > 1
17269
- )) {
17270
- return null;
17271
- }
17272
- const items = parts.map((s) => parseAtom(s, fields));
17273
- if (items.length === 0 || items.some((it) => !it)) return null;
17274
- return {
17275
- relation,
17276
- children: [{ relation, children: items }]
17277
- };
17278
- };
17279
- const orParts = splitByJoiner(trimmed, "||");
17280
- if (orParts.length > 1) {
17281
- return toGroup("OR", orParts.map(stripOuterParens));
17282
- }
17283
- const inner = stripOuterParens(trimmed);
17284
- const innerOr = splitByJoiner(inner, "||");
17285
- if (innerOr.length > 1) return toGroup("OR", innerOr);
17286
- return toGroup("AND", splitByJoiner(inner, "&&"));
17287
- }
17288
- var defaultItem = () => ({ field: {}, term: {} });
17289
- var defaultGroup = () => ({
17290
- relation: "AND",
17291
- children: [defaultItem()]
17292
- });
17293
- function defaultFieldConditionValue() {
17294
- return { relation: "OR", children: [defaultGroup()] };
17295
- }
17296
- var toggleRelation = (r) => r === "AND" ? "OR" : "AND";
17297
- function FieldCondition({
17298
- value,
17299
- onChange,
17300
- fields = [],
17301
- disabled,
17302
- size = "middle"
17303
- }) {
17304
- const data = (0, import_react74.useMemo)(
17305
- () => value?.children?.length ? value : defaultFieldConditionValue(),
17306
- [value]
17307
- );
17308
- const group = data.children[0];
17309
- const updateGroup = (0, import_react74.useCallback)(
17310
- (nextGroup) => onChange?.({ ...data, children: [nextGroup] }),
17311
- [data, onChange]
17312
- );
17313
- const updateItem = (itemIndex, patch) => {
17314
- updateGroup({
17315
- ...group,
17316
- children: group.children.map(
17317
- (item, ii) => ii === itemIndex ? { ...item, ...patch } : item
17318
- )
17319
- });
17320
- };
17321
- const fieldOptions = fields.map((f) => ({
17322
- value: f.featureCode,
17323
- label: f.featureName
17324
- }));
17325
- return /* @__PURE__ */ (0, import_jsx_runtime216.jsxs)("div", { className: "w-full", children: [
17326
- /* @__PURE__ */ (0, import_jsx_runtime216.jsxs)("div", { className: "flex gap-2", children: [
17327
- /* @__PURE__ */ (0, import_jsx_runtime216.jsx)(
17328
- RelationButton,
17329
- {
17330
- relation: group.relation,
17331
- visible: group.children.length > 1,
17332
- disabled,
17333
- onToggle: () => updateGroup({ ...group, relation: toggleRelation(group.relation) })
17334
- }
17335
- ),
17336
- /* @__PURE__ */ (0, import_jsx_runtime216.jsx)("div", { className: "flex-1 space-y-2", children: group.children.map((item, itemIndex) => /* @__PURE__ */ (0, import_jsx_runtime216.jsx)(
17337
- ConditionRow,
17338
- {
17339
- item,
17340
- fields: fieldOptions,
17341
- rawFields: fields,
17342
- disabled,
17343
- size,
17344
- fieldPlaceholder: "\u9009\u62E9\u5B57\u6BB5",
17345
- operatorLabel,
17346
- inputPlaceholder: "\u8BF7\u8F93\u5165\u503C",
17347
- showDelete: group.children.length > 1,
17348
- onChange: (patch) => updateItem(itemIndex, patch),
17349
- onDelete: () => updateGroup({
17350
- ...group,
17351
- children: group.children.filter((_, ii) => ii !== itemIndex)
17352
- })
17353
- },
17354
- itemIndex
17355
- )) })
17356
- ] }),
17357
- !disabled && /* @__PURE__ */ (0, import_jsx_runtime216.jsxs)(
17358
- Button,
17359
- {
17360
- type: "button",
17361
- variant: "link",
17362
- size: "sm",
17363
- className: "h-auto p-0 ml-10 mt-2 text-sm",
17364
- onClick: () => updateGroup({ ...group, children: [...group.children, defaultItem()] }),
17365
- children: [
17366
- /* @__PURE__ */ (0, import_jsx_runtime216.jsx)(import_lucide_react42.Plus, { className: "h-3.5 w-3.5" }),
17367
- "\u6DFB\u52A0\u6761\u4EF6"
17368
- ]
17369
- }
17370
- )
17371
- ] });
17372
- }
17373
- function RelationButton({
17374
- relation,
17375
- visible = true,
17376
- disabled,
17377
- onToggle
17378
- }) {
17379
- if (!visible) return /* @__PURE__ */ (0, import_jsx_runtime216.jsx)("div", { className: "w-8 shrink-0" });
17380
- return /* @__PURE__ */ (0, import_jsx_runtime216.jsx)(
17381
- "button",
17382
- {
17383
- type: "button",
17384
- disabled,
17385
- onClick: onToggle,
17386
- className: cn(
17387
- "w-8 shrink-0 self-center rounded px-1 py-0.5 text-xs font-medium",
17388
- disabled ? "cursor-not-allowed" : "cursor-pointer",
17389
- // 语义色走皮肤令牌(AND=信息蓝 / OR=警示橙),随宿主主题明暗自适应
17390
- relation === "AND" ? "bg-info/10 text-info" : "bg-warning/10 text-warning"
17391
- ),
17392
- children: relation === "AND" ? "\u4E14" : "\u6216"
17393
- }
17394
- );
17395
- }
17396
- function ConditionRow({
17397
- item,
17398
- fields,
17399
- rawFields,
17400
- disabled,
17401
- size,
17402
- fieldPlaceholder,
17403
- operatorLabel: operatorLabel3,
17404
- inputPlaceholder,
17405
- showDelete,
17406
- onChange,
17407
- onDelete
17408
- }) {
17409
- const dataType = item.field.dataType;
17410
- const termValue = item.term.value;
17411
- const operators = getOperatorsByFieldType(dataType);
17412
- return /* @__PURE__ */ (0, import_jsx_runtime216.jsxs)("div", { className: "flex items-center gap-2", children: [
17413
- /* @__PURE__ */ (0, import_jsx_runtime216.jsx)(
17414
- SearchableSelect,
17415
- {
17416
- value: item.field.value || void 0,
17417
- onChange: (v) => {
17418
- const raw = rawFields.find((f) => f.featureCode === v);
17419
- onChange({
17420
- field: raw ? { value: raw.featureCode, label: raw.featureName, dataType: raw.fieldType } : { value: v },
17421
- term: {},
17422
- value: void 0
17423
- });
17424
- },
17425
- options: fields,
17426
- placeholder: fieldPlaceholder,
17427
- disabled,
17428
- size,
17429
- fullOptionText: true,
17430
- className: "w-36"
17431
- }
17432
- ),
17433
- /* @__PURE__ */ (0, import_jsx_runtime216.jsxs)(
17434
- Select,
17435
- {
17436
- items: operators.map((op) => ({
17437
- value: op.value,
17438
- label: operatorLabel3(op.value)
17439
- })),
17440
- value: termValue ?? null,
17441
- disabled: disabled || !dataType,
17442
- onValueChange: (v) => {
17443
- if (v == null) return;
17444
- const resetValue = v === "BETWEEN" || isNoValueOperator(v) || isNoValueOperator(termValue);
17445
- onChange({
17446
- term: { value: v, label: operatorLabel3(v) },
17447
- value: resetValue ? void 0 : item.value
17448
- });
17449
- },
17450
- children: [
17451
- /* @__PURE__ */ (0, import_jsx_runtime216.jsx)(
17452
- SelectTrigger,
17453
- {
17454
- size: CONTROL_SIZE_SELECT_ATTR[size],
17455
- className: cn("w-28 shrink-0", CONTROL_SIZE_TRIGGER_TEXT[size]),
17456
- children: /* @__PURE__ */ (0, import_jsx_runtime216.jsx)(SelectValue, { placeholder: "\u8BF7\u9009\u62E9" })
17457
- }
17458
- ),
17459
- /* @__PURE__ */ (0, import_jsx_runtime216.jsx)(SelectContent, { children: operators.map((op) => /* @__PURE__ */ (0, import_jsx_runtime216.jsx)(SelectItem, { value: op.value, children: operatorLabel3(op.value) }, op.value)) })
17460
- ]
17461
- }
17462
- ),
17463
- dataType && termValue && !isNoValueOperator(termValue) && /* @__PURE__ */ (0, import_jsx_runtime216.jsx)(
17464
- ConditionValueInput,
17465
- {
17466
- dataType,
17467
- termValue,
17468
- value: item.value,
17469
- disabled,
17470
- size,
17471
- placeholder: inputPlaceholder,
17472
- onChange: (v) => onChange({ value: v })
17473
- }
17474
- ),
17475
- showDelete && !disabled && /* @__PURE__ */ (0, import_jsx_runtime216.jsx)(
17476
- "button",
17477
- {
17478
- type: "button",
17479
- "aria-label": "delete-condition",
17480
- className: "text-muted-foreground hover:text-destructive",
17481
- onClick: onDelete,
17482
- children: /* @__PURE__ */ (0, import_jsx_runtime216.jsx)(import_lucide_react42.X, { className: "h-4 w-4" })
17483
- }
17484
- )
17485
- ] });
17486
- }
17487
- function ConditionValueInput({
17488
- dataType,
17489
- termValue,
17490
- value,
17491
- disabled,
17492
- size,
17493
- placeholder,
17494
- onChange
17495
- }) {
17496
- const inputType = dataType === "NUMBER" ? "number" : "text";
17497
- if (termValue === "BETWEEN") {
17498
- const arr = Array.isArray(value) ? value : [];
17499
- if (dataType === "DATE") {
17500
- return /* @__PURE__ */ (0, import_jsx_runtime216.jsxs)("div", { className: "flex items-center gap-1", children: [
17501
- /* @__PURE__ */ (0, import_jsx_runtime216.jsx)(
17502
- DatePicker,
17503
- {
17504
- value: typeof arr[0] === "string" && arr[0] ? arr[0] : void 0,
17505
- disabled,
17506
- size,
17507
- placeholder,
17508
- className: "w-32",
17509
- onChange: (v) => onChange([v, arr[1] ?? ""])
17510
- }
17511
- ),
17512
- /* @__PURE__ */ (0, import_jsx_runtime216.jsx)("span", { className: "text-xs text-muted-foreground", children: "~" }),
17513
- /* @__PURE__ */ (0, import_jsx_runtime216.jsx)(
17514
- DatePicker,
17515
- {
17516
- value: typeof arr[1] === "string" && arr[1] ? arr[1] : void 0,
17517
- disabled,
17518
- size,
17519
- placeholder,
17520
- className: "w-32",
17521
- onChange: (v) => onChange([arr[0] ?? "", v])
17522
- }
17523
- )
17524
- ] });
17525
- }
17526
- return /* @__PURE__ */ (0, import_jsx_runtime216.jsxs)("div", { className: "flex items-center gap-1", children: [
17527
- /* @__PURE__ */ (0, import_jsx_runtime216.jsx)(
17528
- Input,
17529
- {
17530
- type: inputType,
17531
- className: cn("w-28", CONTROL_SIZE_BOX[size]),
17532
- value: arr[0] ?? "",
17533
- disabled,
17534
- placeholder,
17535
- "aria-label": "between-min",
17536
- onChange: (e) => onChange([e.target.value, arr[1] ?? ""])
17537
- }
17538
- ),
17539
- /* @__PURE__ */ (0, import_jsx_runtime216.jsx)("span", { className: "text-xs text-muted-foreground", children: "~" }),
17540
- /* @__PURE__ */ (0, import_jsx_runtime216.jsx)(
17541
- Input,
17542
- {
17543
- type: inputType,
17544
- className: cn("w-28", CONTROL_SIZE_BOX[size]),
17545
- value: arr[1] ?? "",
17546
- disabled,
17547
- placeholder,
17548
- "aria-label": "between-max",
17549
- onChange: (e) => onChange([arr[0] ?? "", e.target.value])
17550
- }
17551
- )
17552
- ] });
17553
- }
17554
- if (dataType === "DATE") {
17555
- return /* @__PURE__ */ (0, import_jsx_runtime216.jsx)(
17556
- DatePicker,
17557
- {
17558
- value: typeof value === "string" && value ? value : void 0,
17559
- disabled,
17560
- size,
17561
- placeholder,
17562
- className: "w-36",
17563
- onChange: (v) => onChange(v ?? "")
17564
- }
17565
- );
17566
- }
17567
- return /* @__PURE__ */ (0, import_jsx_runtime216.jsx)(
17568
- Input,
17569
- {
17570
- type: inputType,
17571
- className: cn("w-36", CONTROL_SIZE_BOX[size]),
17572
- value: value ?? "",
17573
- disabled,
17574
- placeholder,
17575
- "aria-label": "condition-value",
17576
- onChange: (e) => onChange(e.target.value)
17577
- }
17578
- );
17579
- }
17580
-
17581
- // src/components/business/ExpressionEditor/index.tsx
17582
- var import_jsx_runtime217 = require("react/jsx-runtime");
17583
- var TEXT = {
17584
- /** 头部进入编辑态的按钮文案 */
17585
- edit: "\u7F16\u8F91",
17586
- cancel: "\u53D6\u6D88",
17587
- confirm: "\u786E\u5B9A",
17588
- /** validation 未配置 message 时的兜底提示 */
17589
- invalidFormat: "\u8F93\u5165\u683C\u5F0F\u4E0D\u6B63\u786E"
17590
- };
17591
- function resolveI18nText(value, locale) {
17592
- if (value === null || value === void 0) return "";
17593
- if (typeof value === "string") return value;
17594
- if (typeof value === "number" || typeof value === "boolean") {
17595
- return String(value);
17596
- }
17597
- if (typeof value === "object") {
17598
- const lang = (locale || "zh").toLowerCase();
17599
- const candidates = lang.startsWith("zh") ? ["zh-CN", "zh_CN", "zh", "en-US", "en_US", "en"] : ["en-US", "en_US", "en", "zh-CN", "zh_CN", "zh"];
17600
- for (const key of candidates) {
17601
- if (value[key]) return String(value[key]);
17602
- }
17603
- const first = Object.values(value).find((v) => v);
17604
- return first ? String(first) : "";
17605
- }
17606
- return String(value);
17607
- }
17608
- var PLACEHOLDER_RE = /\$\{(\w+)\}|\{\{(\w+)\}\}/g;
17609
- function parseTemplateSegments(template, mapping) {
17610
- const segments = [];
17611
- const re = new RegExp(PLACEHOLDER_RE.source, "g");
17612
- let lastIndex = 0;
17613
- let match;
17614
- while ((match = re.exec(template)) !== null) {
17615
- const key = match[1] ?? match[2];
17616
- const config = mapping?.[key];
17617
- if (!config) continue;
17618
- if (match.index > lastIndex) {
17619
- segments.push({ type: "text", text: template.slice(lastIndex, match.index) });
17620
- }
17621
- segments.push({ type: "field", key, config });
17622
- lastIndex = match.index + match[0].length;
17623
- }
17624
- if (lastIndex < template.length) {
17625
- segments.push({ type: "text", text: template.slice(lastIndex) });
17626
- }
17627
- return segments;
17628
- }
17629
- function coerceMappingValue(raw, type) {
17630
- if (type !== "number") return raw ?? "";
17631
- if (raw === "" || raw === void 0 || raw === null) return "";
17632
- const num = Number(raw);
17633
- return Number.isFinite(num) ? num : raw;
17634
- }
17635
- function composeExpression(template, values, mapping) {
17636
- return template.replace(
17637
- new RegExp(PLACEHOLDER_RE.source, "g"),
17638
- (raw, dollarKey, braceKey) => {
17639
- const key = dollarKey ?? braceKey;
17640
- return mapping?.[key] ? String(values[key] ?? "") : raw;
17641
- }
17642
- );
17643
- }
17644
- function buildDefaultValues(mapping) {
17645
- const values = {};
17646
- for (const [key, config] of Object.entries(mapping ?? {})) {
17647
- values[key] = coerceMappingValue(config.default ?? "", config.type);
17648
- }
17649
- return values;
17650
- }
17651
- function validateMappingValue(value, config, fallbackMessage) {
17652
- const isEmpty2 = value === "" || value === void 0 || value === null;
17653
- if (config.required && isEmpty2) {
17654
- return config.validation?.message ?? fallbackMessage;
17655
- }
17656
- const pattern = config.validation?.pattern;
17657
- if (!pattern) return null;
17658
- if (isEmpty2) return null;
17659
- if (typeof value === "number" && Number.isNaN(value)) return null;
17660
- return new RegExp(pattern).test(String(value)) ? null : config.validation?.message ?? fallbackMessage;
17661
- }
17662
- function escapeRegExp2(text) {
17663
- return text.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
17664
- }
17665
- function parseExpressionValues(template, value, mapping) {
17666
- if (!value) return null;
17667
- const segments = parseTemplateSegments(template, mapping);
17668
- const keys = [];
17669
- const pattern = segments.map((seg) => {
17670
- if (seg.type === "text") return escapeRegExp2(seg.text);
17671
- keys.push(seg.key);
17672
- return "(.*?)";
17673
- }).join("");
17674
- const match = value.match(new RegExp(`^${pattern}$`));
17675
- if (!match) return null;
17676
- const values = {};
17677
- keys.forEach((key, i) => {
17678
- values[key] = coerceMappingValue(match[i + 1], mapping?.[key]?.type);
17679
- });
17680
- return values;
17681
- }
17682
- function resolveMappingOptions(config, optionSources, locale) {
17683
- const raw = config.options?.length ? config.options : config.optionSource ? optionSources?.[config.optionSource]?.options : null;
17684
- if (!raw?.length) return null;
17685
- return raw.map((opt) => ({
17686
- ...opt,
17687
- label: resolveI18nText(opt?.label, locale),
17688
- value: opt?.value
17689
- }));
17690
- }
17691
- function MappingSelect({
17692
- config,
17693
- optionSources,
17694
- value,
17695
- onChange,
17696
- disabled,
17697
- size,
17698
- fallbackLabel,
17699
- className,
17700
- viewOnly
17701
- }) {
17702
- const options = resolveMappingOptions(config, optionSources) ?? [];
17703
- const displayOptions = (0, import_react75.useMemo)(() => {
17704
- if (!viewOnly || value === void 0 || value === null || value === "") {
17705
- return options;
17706
- }
17707
- return options.some((opt) => String(opt.value) === String(value)) ? options : [...options, { label: String(value), value: String(value) }];
17708
- }, [options, value, viewOnly]);
17709
- return /* @__PURE__ */ (0, import_jsx_runtime217.jsx)(
17710
- SearchableSelect,
17711
- {
17712
- value: value === "" ? void 0 : value,
17713
- onChange: (v) => onChange(v),
17714
- options: displayOptions,
17715
- placeholder: fallbackLabel,
17716
- disabled,
17717
- size,
17718
- showSearch: options.length > 10,
17719
- fullOptionText: true,
17720
- listClassName: "max-h-60 overflow-y-scroll",
17721
- className
17722
- }
17723
- );
17724
- }
17725
- function useMappingFieldOptions(config, optionSources) {
17726
- const entry = config.optionSource ? optionSources?.[config.optionSource] : null;
17727
- return (0, import_react75.useMemo)(() => {
17728
- const opts = config.options?.length ? config.options : entry?.options ?? [];
17729
- return {
17730
- loading: false,
17731
- fields: opts.map((o) => ({
17732
- featureCode: o.value,
17733
- featureName: o.label,
17734
- fieldType: o.fieldType
17735
- }))
17736
- };
17737
- }, [entry, config.options]);
17738
- }
17739
- function MappingFieldCondition({
17740
- config,
17741
- optionSources,
17742
- value,
17743
- onChange,
17744
- disabled,
17745
- size
17746
- }) {
17747
- const { fields } = useMappingFieldOptions(config, optionSources);
17748
- const [condition, setCondition] = (0, import_react75.useState)(
17749
- () => (value ? statementToCondition(value, fields) : null) ?? defaultFieldConditionValue()
17750
- );
17751
- const touchedRef = (0, import_react75.useRef)(false);
17752
- (0, import_react75.useEffect)(() => {
17753
- if (touchedRef.current || !value) return;
17754
- const parsed = statementToCondition(value, fields);
17755
- if (parsed) setCondition(parsed);
17756
- }, [fields, value]);
17757
- return /* @__PURE__ */ (0, import_jsx_runtime217.jsx)(
17758
- FieldCondition,
17759
- {
17760
- value: condition,
17761
- fields,
17762
- disabled,
17763
- size,
17764
- onChange: (next) => {
17765
- touchedRef.current = true;
17766
- setCondition(next);
17767
- onChange(conditionToStatement(next));
17768
- }
17769
- }
17770
- );
17771
- }
17772
- function ExpressionEditor({
17773
- id,
17774
- value,
17775
- onChange,
17776
- valueTemplate,
17777
- valueMapping,
17778
- optionSources,
17779
- label,
17780
- required,
17781
- readonly,
17782
- viewOnly,
17783
- size = "middle",
17784
- className,
17785
- onValidityChange
17786
- }) {
17787
- const labelOf = (seg) => resolveI18nText(seg.config.label) || seg.key;
17788
- const hintOf = (seg) => resolveI18nText(seg.config.placeholder) || labelOf(seg);
17789
- const segments = (0, import_react75.useMemo)(
17790
- () => parseTemplateSegments(valueTemplate, valueMapping),
17791
- [valueTemplate, valueMapping]
17792
- );
17793
- const fieldSegments = (0, import_react75.useMemo)(() => {
17794
- const seen = /* @__PURE__ */ new Set();
17795
- return segments.filter((seg) => {
17796
- if (seg.type !== "field" || seen.has(seg.key)) return false;
17797
- seen.add(seg.key);
17798
- return true;
17799
- });
17800
- }, [segments]);
17801
- const [values, setValues] = (0, import_react75.useState)(
17802
- () => (
17803
- // 外部已有值(编辑回显/AI 填充)时优先反向解析回控件
17804
- (value ? parseExpressionValues(valueTemplate, value, valueMapping) : null) ?? buildDefaultValues(valueMapping)
17805
- )
17806
- );
17807
- const composed = composeExpression(valueTemplate, values, valueMapping);
17808
- const [editing, setEditing] = (0, import_react75.useState)(false);
17809
- const [draft, setDraft] = (0, import_react75.useState)(values);
17810
- const [draftErrors, setDraftErrors] = (0, import_react75.useState)({});
17811
- const [text, setText] = (0, import_react75.useState)(composed);
17812
- const formValues = viewOnly ? values : draft;
17813
- const lastEmittedRef = (0, import_react75.useRef)(null);
17814
- const onChangeRef = (0, import_react75.useRef)(onChange);
17815
- onChangeRef.current = onChange;
17816
- const valuesRef = (0, import_react75.useRef)(values);
17817
- valuesRef.current = values;
17818
- const onValidityChangeRef = (0, import_react75.useRef)(onValidityChange);
17819
- onValidityChangeRef.current = onValidityChange;
17820
- (0, import_react75.useEffect)(() => {
17821
- onValidityChangeRef.current?.(editing && Object.keys(draftErrors).length > 0);
17822
- }, [editing, draftErrors]);
17823
- (0, import_react75.useEffect)(() => {
17824
- if (viewOnly) return;
17825
- const initial = composeExpression(valueTemplate, values, valueMapping);
17826
- if (initial !== (value ?? "")) {
17827
- lastEmittedRef.current = initial;
17828
- onChangeRef.current?.(initial);
17829
- }
17830
- }, []);
17831
- (0, import_react75.useEffect)(() => {
17832
- if (!value) return;
17833
- const composed2 = composeExpression(valueTemplate, valuesRef.current, valueMapping);
17834
- if (value === composed2) return;
17835
- const parsed = parseExpressionValues(valueTemplate, value, valueMapping);
17836
- if (parsed) setValues(parsed);
17837
- }, [value, valueTemplate, valueMapping]);
17838
- (0, import_react75.useEffect)(() => {
17839
- if (value !== void 0 && value !== "") return;
17840
- const defaults = buildDefaultValues(valueMapping);
17841
- setValues((prev) => {
17842
- const same = Object.keys(defaults).every((k) => prev[k] === defaults[k]);
17843
- return same ? prev : defaults;
17844
- });
17845
- }, [value, valueMapping]);
17846
- (0, import_react75.useEffect)(() => {
17847
- if (value === void 0 || value === lastEmittedRef.current) return;
17848
- setText(value === "" ? composed : value);
17849
- }, [value, composed]);
17850
- const enterEdit = () => {
17851
- setDraft(valuesRef.current);
17852
- setDraftErrors({});
17853
- setEditing(true);
17854
- };
17855
- const handleConfirm = () => {
17856
- const nextErrors = {};
17857
- for (const seg of fieldSegments) {
17858
- const message = validateMappingValue(draft[seg.key], seg.config, TEXT.invalidFormat);
17859
- if (message) nextErrors[seg.key] = message;
17860
- }
17861
- setDraftErrors(nextErrors);
17862
- if (Object.keys(nextErrors).length > 0) return;
17863
- const next = composeExpression(valueTemplate, draft, valueMapping);
17864
- setValues(draft);
17865
- lastEmittedRef.current = next;
17866
- setText(next);
17867
- onChangeRef.current?.(next);
17868
- setEditing(false);
17869
- };
17870
- const handleCancel = () => {
17871
- setDraftErrors({});
17872
- setEditing(false);
17873
- };
17874
- const handleTextChange = (raw) => {
17875
- setText(raw);
17876
- lastEmittedRef.current = raw;
17877
- const parsed = parseExpressionValues(valueTemplate, raw, valueMapping);
17878
- if (parsed) setValues(parsed);
17879
- onChangeRef.current?.(raw);
17880
- };
17881
- const handleDraftChange = (key, raw) => {
17882
- const config = valueMapping?.[key];
17883
- const next = coerceMappingValue(raw, config?.type);
17884
- setDraft((prev) => ({
17885
- ...prev,
17886
- [key]: next
17887
- }));
17888
- if (config?.validation?.pattern) {
17889
- const message = validateMappingValue(next, config, TEXT.invalidFormat);
17890
- setDraftErrors((prev) => {
17891
- if (!message && !prev[key]) return prev;
17892
- const updated = { ...prev };
17893
- if (message) updated[key] = message;
17894
- else delete updated[key];
17895
- return updated;
17896
- });
17897
- }
17898
- };
17899
- if (!valueTemplate) return null;
17900
- return /* @__PURE__ */ (0, import_jsx_runtime217.jsxs)("div", { id, className: cn("w-full space-y-2", className), children: [
17901
- !viewOnly && /* @__PURE__ */ (0, import_jsx_runtime217.jsxs)("div", { className: "flex items-center justify-between", children: [
17902
- label ? /* @__PURE__ */ (0, import_jsx_runtime217.jsxs)(Label, { children: [
17903
- label,
17904
- required && /* @__PURE__ */ (0, import_jsx_runtime217.jsx)("span", { className: "text-destructive ml-1", children: "*" })
17905
- ] }) : /* @__PURE__ */ (0, import_jsx_runtime217.jsx)("span", {}),
17906
- !readonly && !editing && /* @__PURE__ */ (0, import_jsx_runtime217.jsx)(
17907
- Button,
17908
- {
17909
- type: "button",
17910
- variant: "ghost",
17911
- size: "sm",
17912
- className: "h-6 px-2 text-xs",
17913
- onClick: enterEdit,
17914
- children: TEXT.edit
17915
- }
17916
- )
17917
- ] }),
17918
- editing || viewOnly ? /* @__PURE__ */ (0, import_jsx_runtime217.jsxs)("div", { className: "flex flex-col gap-3 rounded-md border bg-background px-3 py-3", children: [
17919
- fieldSegments.map((seg) => seg.config.type === "fieldCondition" ? (
17920
- // fieldCondition:label 在上,组件在下(块级布局)
17921
- /* @__PURE__ */ (0, import_jsx_runtime217.jsxs)("div", { className: "flex flex-col gap-1.5", children: [
17922
- /* @__PURE__ */ (0, import_jsx_runtime217.jsx)("span", { className: "text-sm text-muted-foreground", children: labelOf(seg) }),
17923
- /* @__PURE__ */ (0, import_jsx_runtime217.jsx)(
17924
- MappingFieldCondition,
17925
- {
17926
- config: seg.config,
17927
- optionSources,
17928
- disabled: viewOnly,
17929
- size,
17930
- value: formValues[seg.key] === "" || formValues[seg.key] === void 0 ? void 0 : String(formValues[seg.key]),
17931
- onChange: (v) => handleDraftChange(seg.key, v)
17932
- }
17933
- ),
17934
- draftErrors[seg.key] && /* @__PURE__ */ (0, import_jsx_runtime217.jsx)("p", { className: "text-xs text-destructive", children: draftErrors[seg.key] })
17935
- ] }, seg.key)
17936
- ) : /* @__PURE__ */ (0, import_jsx_runtime217.jsxs)("div", { className: "flex items-center gap-3", children: [
17937
- /* @__PURE__ */ (0, import_jsx_runtime217.jsx)("span", { className: "w-24 shrink-0 text-sm text-muted-foreground truncate", children: labelOf(seg) }),
17938
- /* @__PURE__ */ (0, import_jsx_runtime217.jsxs)("div", { className: "flex-1", children: [
17939
- seg.config.type === "select" ? /* @__PURE__ */ (0, import_jsx_runtime217.jsx)(
17940
- MappingSelect,
17941
- {
17942
- config: seg.config,
17943
- optionSources,
17944
- disabled: viewOnly,
17945
- viewOnly,
17946
- size,
17947
- value: formValues[seg.key] === "" ? void 0 : String(formValues[seg.key]),
17948
- onChange: (v) => handleDraftChange(seg.key, v),
17949
- fallbackLabel: hintOf(seg)
17950
- }
17951
- ) : /* @__PURE__ */ (0, import_jsx_runtime217.jsx)(
17952
- Input,
17953
- {
17954
- type: seg.config.type === "number" ? "number" : "text",
17955
- value: formValues[seg.key] ?? "",
17956
- onChange: (e) => handleDraftChange(seg.key, e.target.value),
17957
- disabled: viewOnly,
17958
- placeholder: hintOf(seg),
17959
- "aria-label": labelOf(seg),
17960
- className: cn(CONTROL_SIZE_BOX[size], "w-full font-mono")
17961
- }
17962
- ),
17963
- draftErrors[seg.key] && /* @__PURE__ */ (0, import_jsx_runtime217.jsx)("p", { className: "mt-1 text-xs text-destructive", children: draftErrors[seg.key] })
17964
- ] })
17965
- ] }, seg.key)),
17966
- !viewOnly && /* @__PURE__ */ (0, import_jsx_runtime217.jsxs)("div", { className: "flex justify-end gap-2", children: [
17967
- /* @__PURE__ */ (0, import_jsx_runtime217.jsx)(
17968
- Button,
17969
- {
17970
- type: "button",
17971
- variant: "outline",
17972
- size: "sm",
17973
- onClick: handleCancel,
17974
- children: TEXT.cancel
17975
- }
17976
- ),
17977
- /* @__PURE__ */ (0, import_jsx_runtime217.jsx)(Button, { type: "button", size: "sm", onClick: handleConfirm, children: TEXT.confirm })
17978
- ] })
17979
- ] }) : (
17980
- // display:可直接编辑的表达式文本框;内容实时 emit,
17981
- // 能按模板解析时会映射回「编辑」表单的控件
17982
- /* @__PURE__ */ (0, import_jsx_runtime217.jsx)(
17983
- Textarea,
17984
- {
17985
- value: text,
17986
- onChange: (e) => handleTextChange(e.target.value),
17987
- readOnly: readonly,
17988
- rows: 1,
17989
- className: cn(
17990
- CONTROL_SIZE_BOX_GROW[size],
17991
- "w-full resize-none break-all bg-muted/30 font-mono [field-sizing:content]"
17992
- )
17993
- }
17994
- )
17995
- )
17996
- ] });
17997
- }
17998
-
17999
- // src/components/business/Login/index.tsx
18000
- var import_runtime53 = require("@pactor-app/runtime");
18001
- var import_jsx_runtime218 = require("react/jsx-runtime");
18002
- function DefaultIllustration({ className }) {
18003
- return /* @__PURE__ */ (0, import_jsx_runtime218.jsxs)(
18004
- "svg",
18005
- {
18006
- "data-slot": "login-default-illustration",
18007
- viewBox: "0 0 320 180",
18008
- role: "img",
18009
- "aria-hidden": "true",
18010
- className: cn("text-primary", className),
18011
- children: [
18012
- /* @__PURE__ */ (0, import_jsx_runtime218.jsx)(
18013
- "rect",
18014
- {
18015
- x: "24",
18016
- y: "96",
18017
- width: "88",
18018
- height: "60",
18019
- rx: "10",
18020
- fill: "currentColor",
18021
- opacity: "0.16"
18022
- }
18023
- ),
18024
- /* @__PURE__ */ (0, import_jsx_runtime218.jsx)(
18025
- "rect",
18026
- {
18027
- x: "120",
18028
- y: "60",
18029
- width: "88",
18030
- height: "96",
18031
- rx: "10",
18032
- fill: "currentColor",
18033
- opacity: "0.28"
18034
- }
18035
- ),
18036
- /* @__PURE__ */ (0, import_jsx_runtime218.jsx)(
18037
- "rect",
18038
- {
18039
- x: "216",
18040
- y: "24",
18041
- width: "80",
18042
- height: "132",
18043
- rx: "10",
18044
- fill: "currentColor",
18045
- opacity: "0.4"
18046
- }
18047
- ),
18048
- /* @__PURE__ */ (0, import_jsx_runtime218.jsx)("circle", { cx: "68", cy: "52", r: "26", fill: "currentColor", opacity: "0.2" }),
18049
- /* @__PURE__ */ (0, import_jsx_runtime218.jsx)("circle", { cx: "248", cy: "140", r: "14", fill: "currentColor", opacity: "0.55" }),
18050
- /* @__PURE__ */ (0, import_jsx_runtime218.jsx)(
18051
- "path",
18052
- {
18053
- d: "M40 156 C 90 116, 130 150, 180 118 S 260 96, 300 128",
18054
- fill: "none",
18055
- stroke: "currentColor",
18056
- strokeWidth: "3",
18057
- strokeLinecap: "round",
18058
- opacity: "0.5"
18059
- }
18060
- )
18061
- ]
18062
- }
18063
- );
18064
- }
18065
- var BRAND_PANEL_BG = "bg-muted bg-linear-to-br from-primary/15 via-muted to-primary/5 dark:from-primary/25 dark:to-primary/10";
18066
- function BgDecor({ className }) {
18067
- return /* @__PURE__ */ (0, import_jsx_runtime218.jsxs)(
18068
- "div",
18069
- {
18070
- "data-slot": "login-bg-decor",
18071
- "aria-hidden": "true",
18072
- className: cn(
18073
- "pointer-events-none absolute inset-0 overflow-hidden",
18074
- className
18075
- ),
18076
- children: [
18077
- /* @__PURE__ */ (0, import_jsx_runtime218.jsx)("div", { className: "absolute -right-24 -top-24 size-96 rounded-full bg-primary/8" }),
18078
- /* @__PURE__ */ (0, import_jsx_runtime218.jsx)("div", { className: "absolute -left-32 bottom-0 size-80 rounded-full bg-primary/5" }),
18079
- /* @__PURE__ */ (0, import_jsx_runtime218.jsx)("div", { className: "absolute right-10 top-1/3 size-40 rounded-full border-[12px] border-primary/10" }),
18080
- /* @__PURE__ */ (0, import_jsx_runtime218.jsx)("div", { className: "absolute left-1/4 top-16 h-40 w-64 opacity-35 [background-image:radial-gradient(var(--primary)_1.2px,transparent_1.2px)] [background-size:18px_18px]" }),
18081
- /* @__PURE__ */ (0, import_jsx_runtime218.jsxs)(
18082
- "svg",
18083
- {
18084
- className: "absolute bottom-0 left-0 w-full text-primary",
18085
- viewBox: "0 0 1440 320",
18086
- preserveAspectRatio: "none",
18087
- children: [
18088
- /* @__PURE__ */ (0, import_jsx_runtime218.jsx)(
18089
- "path",
18090
- {
18091
- d: "M0,224 C240,160 480,288 720,240 C960,192 1200,96 1440,160",
18092
- fill: "none",
18093
- stroke: "currentColor",
18094
- strokeOpacity: "0.12",
18095
- strokeWidth: "2"
18096
- }
18097
- ),
18098
- /* @__PURE__ */ (0, import_jsx_runtime218.jsx)(
18099
- "path",
18100
- {
18101
- d: "M0,256 C240,192 480,320 720,272 C960,224 1200,128 1440,192 L1440,320 L0,320 Z",
18102
- fill: "currentColor",
18103
- fillOpacity: "0.05"
18104
- }
18105
- )
18106
- ]
18107
- }
18108
- )
18109
- ]
18110
- }
18111
- );
18112
- }
18113
- function Login({
18114
- layout = "split",
18115
- brandSide = "left",
18116
- brandRatio = 0.5,
18117
- authVariant = "card",
18118
- brandPanelBackground = true,
18119
- decor = true,
18120
- brand,
18121
- title,
18122
- subtitle,
18123
- usernameLabel,
18124
- usernamePlaceholder,
18125
- passwordLabel,
18126
- passwordPlaceholder,
18127
- submitText,
18128
- registerLink,
18129
- forgotLink,
18130
- footer,
18131
- children,
18132
- className,
18133
- style,
18134
- onSubmit,
18135
- onRegisterClick,
18136
- onForgotClick
18137
- }) {
18138
- const { t } = (0, import_runtime53.useI18n)();
18139
- const brandIdentity = brand && (brand.logo || brand.name) ? /* @__PURE__ */ (0, import_jsx_runtime218.jsxs)("div", { "data-slot": "login-brand-identity", className: "flex items-center gap-2", children: [
18140
- brand.logo ? /* @__PURE__ */ (0, import_jsx_runtime218.jsx)(
18141
- "img",
18142
- {
18143
- src: brand.logo,
18144
- alt: brand.name ?? "",
18145
- className: "size-7 rounded-md"
18146
- }
18147
- ) : null,
18148
- brand.name ? /* @__PURE__ */ (0, import_jsx_runtime218.jsx)("span", { className: "text-base font-semibold", children: brand.name }) : null
18149
- ] }) : null;
18150
- const brandVisual = (alignRight = false) => /* @__PURE__ */ (0, import_jsx_runtime218.jsxs)(
18151
- "div",
18152
- {
18153
- className: cn("flex w-full flex-col gap-4", alignRight && "text-right"),
18154
- children: [
18155
- brand?.title ? /* @__PURE__ */ (0, import_jsx_runtime218.jsx)("h2", { className: "text-3xl font-bold leading-tight tracking-tight", children: brand.title }) : null,
18156
- brand?.description ? /* @__PURE__ */ (0, import_jsx_runtime218.jsx)(
18157
- "p",
18158
- {
18159
- className: cn(
18160
- "max-w-md text-sm text-muted-foreground",
18161
- alignRight && "ml-auto"
18162
- ),
18163
- children: brand.description
18164
- }
18165
- ) : null,
18166
- brand?.illustration ? /* @__PURE__ */ (0, import_jsx_runtime218.jsx)(
18167
- "img",
18168
- {
18169
- src: brand.illustration,
18170
- alt: "",
18171
- className: cn(
18172
- "mt-4 w-full max-w-lg rounded-lg object-cover",
18173
- alignRight && "self-end"
18174
- )
18175
- }
18176
- ) : brand?.illustration === "" ? null : /* @__PURE__ */ (0, import_jsx_runtime218.jsx)(
18177
- DefaultIllustration,
18178
- {
18179
- className: cn("mt-4 w-full max-w-md", alignRight && "self-end")
18180
- }
18181
- )
18182
- ]
18183
- }
18184
- );
18185
- const renderAuxLink = (link, onClick) => {
18186
- if (!link) return null;
18187
- if (link.href) {
18188
- return /* @__PURE__ */ (0, import_jsx_runtime218.jsx)(Link2, { href: link.href, text: link.text, className: "text-sm" });
18189
- }
18190
- return /* @__PURE__ */ (0, import_jsx_runtime218.jsx)(
18191
- "button",
18192
- {
18193
- type: "button",
18194
- className: "text-sm text-primary underline-offset-4 hover:underline",
18195
- onClick,
18196
- children: link.text
18197
- }
18198
- );
18199
- };
18200
- const authContent = /* @__PURE__ */ (0, import_jsx_runtime218.jsxs)(import_jsx_runtime218.Fragment, { children: [
18201
- /* @__PURE__ */ (0, import_jsx_runtime218.jsxs)("div", { className: "mb-6 flex flex-col gap-1.5", children: [
18202
- /* @__PURE__ */ (0, import_jsx_runtime218.jsx)("h1", { className: "text-2xl font-semibold tracking-tight", children: title ?? t("pactor.login.title") }),
18203
- subtitle ? /* @__PURE__ */ (0, import_jsx_runtime218.jsx)("p", { className: "text-sm text-muted-foreground", children: subtitle }) : null
18204
- ] }),
18205
- children ?? /* @__PURE__ */ (0, import_jsx_runtime218.jsxs)(Form, { name: "login", className: "flex flex-col gap-2", onSubmit, children: [
18206
- /* @__PURE__ */ (0, import_jsx_runtime218.jsx)(Field2, { label: usernameLabel ?? t("pactor.login.username"), required: true, children: /* @__PURE__ */ (0, import_jsx_runtime218.jsx)(
18207
- Input2,
18208
- {
18209
- name: "username",
18210
- size: "large",
18211
- placeholder: usernamePlaceholder ?? t("pactor.login.usernamePlaceholder"),
18212
- rules: [
18213
- { required: true, message: t("pactor.login.usernameRequired") }
18214
- ]
18215
- }
18216
- ) }),
18217
- /* @__PURE__ */ (0, import_jsx_runtime218.jsx)(Field2, { label: passwordLabel ?? t("pactor.login.password"), required: true, children: /* @__PURE__ */ (0, import_jsx_runtime218.jsx)(
18218
- Input2,
18219
- {
18220
- name: "password",
18221
- type: "password",
18222
- size: "large",
18223
- placeholder: passwordPlaceholder ?? t("pactor.login.passwordPlaceholder"),
18224
- rules: [
18225
- { required: true, message: t("pactor.login.passwordRequired") }
18226
- ]
18227
- }
18228
- ) }),
18229
- /* @__PURE__ */ (0, import_jsx_runtime218.jsx)(Field2, { children: /* @__PURE__ */ (0, import_jsx_runtime218.jsx)(
18230
- Button2,
18231
- {
18232
- submit: true,
18233
- variant: "primary",
18234
- size: "large",
18235
- text: submitText ?? t("pactor.login.submit"),
18236
- className: "mt-2 w-full"
18237
- }
18238
- ) })
18239
- ] }),
18240
- registerLink || forgotLink ? /* @__PURE__ */ (0, import_jsx_runtime218.jsxs)("div", { className: "mt-4 flex items-center justify-between gap-4", children: [
18241
- renderAuxLink(registerLink, onRegisterClick),
18242
- renderAuxLink(forgotLink, onForgotClick)
18243
- ] }) : null
18244
- ] });
18245
- const authBox = (variant) => {
18246
- if (variant === "plain") {
18247
- return /* @__PURE__ */ (0, import_jsx_runtime218.jsx)("div", { className: "w-full max-w-[360px]", children: authContent });
18248
- }
18249
- if (variant === "float") {
18250
- return /* @__PURE__ */ (0, import_jsx_runtime218.jsx)(Card2, { className: "w-full max-w-[440px] p-4 shadow-xl md:p-6", children: authContent });
18251
- }
18252
- return /* @__PURE__ */ (0, import_jsx_runtime218.jsx)(Card2, { className: "w-full max-w-[400px] p-2 md:p-4", children: authContent });
18253
- };
18254
- const footerBar = footer ? /* @__PURE__ */ (0, import_jsx_runtime218.jsx)("footer", { className: "p-4 text-center text-xs text-muted-foreground", children: footer }) : null;
18255
- const brandPanel = (extraClass, withIdentity = true) => /* @__PURE__ */ (0, import_jsx_runtime218.jsxs)(
18256
- "div",
18257
- {
18258
- className: cn(
18259
- "relative hidden flex-col overflow-hidden p-10 md:flex",
18260
- brandPanelBackground && BRAND_PANEL_BG,
18261
- extraClass
18262
- ),
18263
- children: [
18264
- decor ? /* @__PURE__ */ (0, import_jsx_runtime218.jsx)(BgDecor, {}) : null,
18265
- /* @__PURE__ */ (0, import_jsx_runtime218.jsxs)("div", { className: "relative flex h-full flex-col justify-between", children: [
18266
- withIdentity ? brandIdentity : null,
18267
- brandVisual(),
18268
- /* @__PURE__ */ (0, import_jsx_runtime218.jsx)("div", {})
18269
- ] })
18270
- ]
18271
- }
18272
- );
18273
- const authColumn = (variant, identityAlways = false) => /* @__PURE__ */ (0, import_jsx_runtime218.jsxs)("div", { className: "flex min-h-svh flex-col bg-linear-to-b from-primary/5 via-background to-background dark:from-primary/10", children: [
18274
- brandIdentity ? /* @__PURE__ */ (0, import_jsx_runtime218.jsx)("div", { className: cn("p-6", !identityAlways && "md:hidden"), children: brandIdentity }) : null,
18275
- /* @__PURE__ */ (0, import_jsx_runtime218.jsx)("div", { className: "flex flex-1 items-center justify-center p-6", children: authBox(variant) }),
18276
- footerBar
18277
- ] });
18278
- const brandCard = (widthClass, alignRight = false) => /* @__PURE__ */ (0, import_jsx_runtime218.jsx)(Card2, { className: cn("w-full", widthClass, BRAND_PANEL_BG), children: /* @__PURE__ */ (0, import_jsx_runtime218.jsxs)(
18279
- "div",
18280
- {
18281
- "data-slot": "login-brand-card-content",
18282
- className: cn("flex flex-col gap-6", alignRight && "text-right"),
18283
- children: [
18284
- brandIdentity ? /* @__PURE__ */ (0, import_jsx_runtime218.jsx)("div", { className: cn(alignRight && "flex justify-end"), children: brandIdentity }) : null,
18285
- brandVisual(alignRight)
18286
- ]
18287
- }
18288
- ) });
18289
- const overlapAuthCard = /* @__PURE__ */ (0, import_jsx_runtime218.jsx)(
18290
- "div",
18291
- {
18292
- className: cn(
18293
- "z-10 w-full max-w-[400px]",
18294
- brandSide === "left" ? "md:-ml-24" : "md:-mr-24"
18295
- ),
18296
- children: /* @__PURE__ */ (0, import_jsx_runtime218.jsx)(Card2, { className: "p-2 shadow-xl md:p-4", children: authContent })
18297
- }
18298
- );
18299
- const backdropBrandColumn = /* @__PURE__ */ (0, import_jsx_runtime218.jsxs)("div", { className: "relative hidden flex-col justify-between p-10 md:flex", children: [
18300
- brandIdentity,
18301
- brandVisual(),
18302
- /* @__PURE__ */ (0, import_jsx_runtime218.jsx)("div", {})
18303
- ] });
18304
- const backdropAuthColumn = /* @__PURE__ */ (0, import_jsx_runtime218.jsxs)("div", { className: "relative flex min-h-svh flex-col", children: [
18305
- brandIdentity ? /* @__PURE__ */ (0, import_jsx_runtime218.jsx)("div", { className: "p-6 md:hidden", children: brandIdentity }) : null,
18306
- /* @__PURE__ */ (0, import_jsx_runtime218.jsx)("div", { className: "flex flex-1 items-center justify-center p-6", children: /* @__PURE__ */ (0, import_jsx_runtime218.jsx)(Card2, { className: "w-full max-w-[400px] p-2 shadow-xl md:p-4", children: authContent }) }),
18307
- footerBar
18308
- ] });
18309
- if (layout === "centered") {
18310
- return /* @__PURE__ */ (0, import_jsx_runtime218.jsxs)(
18311
- "div",
18312
- {
18313
- "data-slot": "login",
18314
- "data-layout": layout,
18315
- className: cn(
18316
- "relative flex min-h-svh w-full flex-col overflow-hidden bg-background bg-linear-to-b from-primary/10 via-background to-background dark:from-primary/15",
18317
- className
18318
- ),
18319
- style,
18320
- children: [
18321
- decor ? /* @__PURE__ */ (0, import_jsx_runtime218.jsx)(BgDecor, {}) : null,
18322
- /* @__PURE__ */ (0, import_jsx_runtime218.jsxs)("div", { className: "relative flex flex-1 flex-col items-center justify-center gap-6 p-6", children: [
18323
- brandIdentity,
18324
- authBox(authVariant)
18325
- ] }),
18326
- footerBar
18327
- ]
18328
- }
18329
- );
18330
- }
18331
- if (layout === "brand-card") {
18332
- return /* @__PURE__ */ (0, import_jsx_runtime218.jsxs)(
18333
- "div",
18334
- {
18335
- "data-slot": "login",
18336
- "data-layout": layout,
18337
- "data-brand-side": brandSide,
18338
- className: cn(
18339
- "relative flex min-h-svh w-full flex-col overflow-hidden bg-background p-6 md:p-10",
18340
- className
18341
- ),
18342
- style,
18343
- children: [
18344
- decor ? /* @__PURE__ */ (0, import_jsx_runtime218.jsx)(BgDecor, {}) : null,
18345
- /* @__PURE__ */ (0, import_jsx_runtime218.jsx)("div", { className: "relative flex flex-1 flex-col items-center gap-6 md:flex-row md:gap-10", children: brandSide === "left" ? /* @__PURE__ */ (0, import_jsx_runtime218.jsxs)(import_jsx_runtime218.Fragment, { children: [
18346
- brandCard("md:w-[45%]"),
18347
- /* @__PURE__ */ (0, import_jsx_runtime218.jsx)("div", { className: "flex w-full flex-1 justify-center", children: authBox("plain") })
18348
- ] }) : /* @__PURE__ */ (0, import_jsx_runtime218.jsxs)(import_jsx_runtime218.Fragment, { children: [
18349
- /* @__PURE__ */ (0, import_jsx_runtime218.jsx)("div", { className: "flex w-full flex-1 justify-center", children: authBox("plain") }),
18350
- brandCard("md:w-[45%]")
18351
- ] }) }),
18352
- footerBar
18353
- ]
18354
- }
18355
- );
18356
- }
18357
- if (layout === "overlap") {
18358
- return /* @__PURE__ */ (0, import_jsx_runtime218.jsxs)(
18359
- "div",
18360
- {
18361
- "data-slot": "login",
18362
- "data-layout": layout,
18363
- "data-brand-side": brandSide,
18364
- className: cn(
18365
- "relative flex min-h-svh w-full flex-col overflow-hidden bg-background p-6 md:p-10",
18366
- className
18367
- ),
18368
- style,
18369
- children: [
18370
- decor ? /* @__PURE__ */ (0, import_jsx_runtime218.jsx)(BgDecor, {}) : null,
18371
- /* @__PURE__ */ (0, import_jsx_runtime218.jsx)("div", { className: "relative flex flex-1 flex-col items-center justify-center gap-6 md:flex-row md:items-center md:gap-0", children: brandSide === "left" ? /* @__PURE__ */ (0, import_jsx_runtime218.jsxs)(import_jsx_runtime218.Fragment, { children: [
18372
- brandCard("md:w-[52%]"),
18373
- overlapAuthCard
18374
- ] }) : /* @__PURE__ */ (0, import_jsx_runtime218.jsxs)(import_jsx_runtime218.Fragment, { children: [
18375
- overlapAuthCard,
18376
- brandCard("md:w-[52%]", true)
18377
- ] }) }),
18378
- footerBar
18379
- ]
18380
- }
18381
- );
18382
- }
18383
- if (layout === "backdrop") {
18384
- return /* @__PURE__ */ (0, import_jsx_runtime218.jsxs)(
18385
- "div",
18386
- {
18387
- "data-slot": "login",
18388
- "data-layout": layout,
18389
- "data-brand-side": brandSide,
18390
- className: cn(
18391
- "relative grid min-h-svh w-full overflow-hidden bg-linear-to-br from-primary/20 via-accent to-primary/10 md:grid-cols-2 dark:from-primary/25 dark:to-primary/15",
18392
- className
18393
- ),
18394
- style,
18395
- children: [
18396
- decor ? /* @__PURE__ */ (0, import_jsx_runtime218.jsx)(BgDecor, {}) : null,
18397
- brandSide === "left" ? /* @__PURE__ */ (0, import_jsx_runtime218.jsxs)(import_jsx_runtime218.Fragment, { children: [
18398
- backdropBrandColumn,
18399
- backdropAuthColumn
18400
- ] }) : /* @__PURE__ */ (0, import_jsx_runtime218.jsxs)(import_jsx_runtime218.Fragment, { children: [
18401
- backdropAuthColumn,
18402
- backdropBrandColumn
18403
- ] })
18404
- ]
18405
- }
18406
- );
18407
- }
18408
- const ratio = Math.min(Math.max(brandRatio, 0.2), 0.8);
18409
- const rootStyle = {
18410
- "--login-grid": `minmax(0, ${ratio}fr) minmax(0, ${1 - ratio}fr)`,
18411
- ...style
18412
- };
18413
- return /* @__PURE__ */ (0, import_jsx_runtime218.jsx)(
18414
- "div",
18415
- {
18416
- "data-slot": "login",
18417
- "data-layout": layout,
18418
- "data-brand-side": brandSide,
18419
- className: cn(
18420
- "grid min-h-svh w-full bg-background md:[grid-template-columns:var(--login-grid)]",
18421
- className
18422
- ),
18423
- style: rootStyle,
18424
- children: brandSide === "left" ? /* @__PURE__ */ (0, import_jsx_runtime218.jsxs)(import_jsx_runtime218.Fragment, { children: [
18425
- brandPanel(),
18426
- authColumn(authVariant)
18427
- ] }) : /* @__PURE__ */ (0, import_jsx_runtime218.jsxs)(import_jsx_runtime218.Fragment, { children: [
18428
- authColumn(authVariant, true),
18429
- brandPanel("order-last", false)
18430
- ] })
18431
- }
18432
- );
18433
- }
18434
-
18435
- // src/components/business/RuleConfig/index.tsx
18436
- var import_lucide_react43 = require("lucide-react");
18437
- var import_react76 = require("react");
18438
- var import_jsx_runtime219 = require("react/jsx-runtime");
18439
- var isBlank = (v) => v == null || String(v).trim() === "";
18440
- function isValueMissing(condition) {
18441
- const { opt, value } = condition;
18442
- if (NO_VALUE_OPT.includes(opt)) return false;
18443
- if (opt === "BETWEEN") {
18444
- const [a, b] = normalizeBetweenValue(value);
18445
- return isBlank(a) || isBlank(b);
18446
- }
18447
- if (Array.isArray(value)) return value.length === 0 || value.every(isBlank);
18448
- return isBlank(value);
18449
- }
18450
- function getConditionIssue(condition) {
18451
- const missingValue = isValueMissing(condition);
18452
- const missingScore = condition.score == null || Number.isNaN(condition.score);
18453
- if (!missingValue && !missingScore) return null;
18454
- return { missingValue, missingScore };
18455
- }
18456
- var OPERATOR_VALUES = [
18457
- "GREATER",
18458
- "GREATER_EQUAL",
18459
- "LESS_EQUAL",
18460
- "LESS",
18461
- "EQUAL",
18462
- "IN",
18463
- "NOT_IN",
18464
- "UNEQUAL",
18465
- "LIKE",
18466
- "NULL",
18467
- "HASVAL",
18468
- "NOTNULL",
18469
- "BETWEEN"
18470
- ];
18471
- var NUMERIC_OPT = [
18472
- "GREATER",
18473
- "LESS",
18474
- "LESS_EQUAL",
18475
- "GREATER_EQUAL",
18476
- "UNEQUAL"
18477
- ];
18478
- var NO_VALUE_OPT = ["NULL", "HASVAL", "NOTNULL"];
18479
- var MULTI_VALUE_OPT = ["IN", "NOT_IN"];
18480
- var OPERATOR_LABELS2 = {
18481
- GREATER: "\u5927\u4E8E",
18482
- GREATER_EQUAL: "\u5927\u4E8E\u7B49\u4E8E",
18483
- LESS_EQUAL: "\u5C0F\u4E8E\u7B49\u4E8E",
18484
- LESS: "\u5C0F\u4E8E",
18485
- EQUAL: "\u7B49\u4E8E",
18486
- IN: "\u5305\u542B",
18487
- NOT_IN: "\u4E0D\u5305\u542B",
18488
- UNEQUAL: "\u4E0D\u7B49\u4E8E",
18489
- LIKE: "\u7C7B\u4F3C",
18490
- NULL: "\u4E3A\u7A7A",
18491
- HASVAL: "\u6709\u503C",
18492
- NOTNULL: "\u4E0D\u4E3A\u7A7A",
18493
- BETWEEN: "\u8303\u56F4"
18494
- };
18495
- function operatorLabel2(operator) {
18496
- return OPERATOR_LABELS2[operator] ?? operator;
18497
- }
18498
- var _key = 0;
18499
- var nextKey = () => ++_key;
18500
- var defaultCondition = () => ({ key: nextKey(), opt: "EQUAL" });
18501
- function normalizeBetweenValue(value) {
18502
- if (Array.isArray(value)) return [value[0], value[1]];
18503
- if (typeof value === "string" && value.includes(",")) {
18504
- const [a, b] = value.split(",");
18505
- return [a?.trim(), b?.trim()];
18506
- }
18507
- if (value == null || value === "") return [void 0, void 0];
18508
- return [value, void 0];
18509
- }
18510
- function formatConditionValue2(condition) {
18511
- const { opt, value } = condition;
18512
- if (value == null) return "-";
18513
- if (opt === "BETWEEN") {
18514
- const [a, b] = normalizeBetweenValue(value);
18515
- return `${a ?? "-"} ~ ${b ?? "-"}`;
18516
- }
18517
- if (Array.isArray(value)) return value.join(", ");
18518
- return String(value);
18519
- }
18520
- function RuleConfig({ value, onChange, disabled, size = "middle" }) {
18521
- const rawConditions = value?.ruleConditions;
18522
- const conditions = (0, import_react76.useMemo)(() => {
18523
- if (!rawConditions?.length) return [defaultCondition()];
18524
- const seen = /* @__PURE__ */ new Set();
18525
- return rawConditions.map((c) => {
18526
- if (c.key != null && !seen.has(c.key)) {
18527
- seen.add(c.key);
18528
- return c;
18529
- }
18530
- const key = nextKey();
18531
- seen.add(key);
18532
- return { ...c, key };
18533
- });
18534
- }, [rawConditions]);
18535
- const initScore = value?.score ?? 1;
18536
- const [scoreText, setScoreText] = (0, import_react76.useState)(String(initScore));
18537
- const lastScoreRef = (0, import_react76.useRef)(value?.score);
18538
- (0, import_react76.useEffect)(() => {
18539
- if (value?.score === lastScoreRef.current) return;
18540
- lastScoreRef.current = value?.score;
18541
- setScoreText(value?.score == null ? "" : String(value.score));
18542
- }, [value?.score]);
18543
- const commitScore = (0, import_react76.useCallback)(
18544
- (score) => {
18545
- lastScoreRef.current = score;
18546
- onChange?.({ score, ruleConditions: conditions });
18547
- },
18548
- [onChange, conditions]
18549
- );
18550
- const normalizeScore = (n) => Math.min(1e3, Math.max(1, Math.round(n)));
18551
- const handleScoreTextChange = (text) => {
18552
- setScoreText(text);
18553
- if (text === "") return;
18554
- const n = Number(text);
18555
- if (Number.isFinite(n) && n >= 1 && n <= 1e3 && Number.isInteger(n)) {
18556
- commitScore(n);
18557
- }
18558
- };
18559
- const handleScoreBlur = () => {
18560
- const n = scoreText.trim() === "" ? NaN : Number(scoreText);
18561
- const next = Number.isFinite(n) ? normalizeScore(n) : 1;
18562
- setScoreText(String(next));
18563
- if (next !== lastScoreRef.current) commitScore(next);
18564
- };
18565
- const update = (0, import_react76.useCallback)(
18566
- (next, nextScore) => onChange?.({
18567
- score: nextScore ?? initScore,
18568
- ruleConditions: next
18569
- }),
18570
- [onChange, initScore]
18571
- );
18572
- const handleOptChange = (key, opt) => {
18573
- update(
18574
- conditions.map(
18575
- (c) => c.key === key ? { ...c, opt, value: opt === "BETWEEN" ? [] : void 0 } : c
18576
- )
18577
- );
18578
- };
18579
- const handleValueChange = (key, val) => {
18580
- update(conditions.map((c) => c.key === key ? { ...c, value: val } : c));
18581
- };
18582
- const handleScoreChange = (key, score) => {
18583
- update(
18584
- conditions.map((c) => c.key === key ? { ...c, score } : c)
18585
- );
18586
- };
18587
- const handleAdd = () => update([...conditions, defaultCondition()]);
18588
- const handleDelete = (key) => update(conditions.filter((c) => c.key !== key));
18589
- if (disabled) {
18590
- return /* @__PURE__ */ (0, import_jsx_runtime219.jsxs)("div", { className: "space-y-2 text-sm", children: [
18591
- /* @__PURE__ */ (0, import_jsx_runtime219.jsxs)("div", { className: "flex items-center gap-2 pb-2 border-b", children: [
18592
- /* @__PURE__ */ (0, import_jsx_runtime219.jsx)("span", { className: "text-muted-foreground", children: "\u6743\u91CD" }),
18593
- /* @__PURE__ */ (0, import_jsx_runtime219.jsx)("span", { className: "font-medium", children: initScore })
18594
- ] }),
18595
- conditions.map((condition) => /* @__PURE__ */ (0, import_jsx_runtime219.jsxs)("div", { className: "flex items-center gap-2", children: [
18596
- /* @__PURE__ */ (0, import_jsx_runtime219.jsx)("span", { children: operatorLabel2(condition.opt) }),
18597
- !NO_VALUE_OPT.includes(condition.opt) && /* @__PURE__ */ (0, import_jsx_runtime219.jsx)("span", { className: "font-medium", children: formatConditionValue2(condition) }),
18598
- /* @__PURE__ */ (0, import_jsx_runtime219.jsxs)("span", { className: "text-muted-foreground", children: [
18599
- "\u8BC4\u5206: ",
18600
- condition.score ?? "-"
18601
- ] })
18602
- ] }, condition.key))
18603
- ] });
18604
- }
18605
- return /* @__PURE__ */ (0, import_jsx_runtime219.jsxs)("div", { className: "space-y-2", children: [
18606
- /* @__PURE__ */ (0, import_jsx_runtime219.jsxs)("div", { className: "flex items-center gap-1 pb-2 border-b gap-2", children: [
18607
- /* @__PURE__ */ (0, import_jsx_runtime219.jsx)("span", { className: "text-sm shrink-0", children: "\u6743\u91CD" }),
18608
- /* @__PURE__ */ (0, import_jsx_runtime219.jsx)(
18609
- Input,
18610
- {
18611
- type: "number",
18612
- className: cn("w-48", CONTROL_SIZE_BOX[size]),
18613
- min: 1,
18614
- max: 1e3,
18615
- step: 1,
18616
- value: scoreText,
18617
- disabled,
18618
- placeholder: "\u8BF7\u8F93\u5165\u6743\u91CD",
18619
- onChange: (e) => handleScoreTextChange(e.target.value),
18620
- onBlur: handleScoreBlur
18621
- }
18622
- )
18623
- ] }),
18624
- conditions.map((condition) => /* @__PURE__ */ (0, import_jsx_runtime219.jsx)(
18625
- ConditionRow2,
18626
- {
18627
- condition,
18628
- issue: getConditionIssue(condition),
18629
- showDelete: conditions.length > 1,
18630
- disabled,
18631
- size,
18632
- onOptChange: (opt) => handleOptChange(condition.key, opt),
18633
- onValueChange: (val) => handleValueChange(condition.key, val),
18634
- onScoreChange: (score) => handleScoreChange(condition.key, score),
18635
- onDelete: () => handleDelete(condition.key)
18636
- },
18637
- condition.key
18638
- )),
18639
- !disabled && /* @__PURE__ */ (0, import_jsx_runtime219.jsxs)(
18640
- Button,
18641
- {
18642
- type: "button",
18643
- variant: "link",
18644
- size: "sm",
18645
- className: "h-auto p-0 text-sm mt-1",
18646
- onClick: handleAdd,
18647
- children: [
18648
- /* @__PURE__ */ (0, import_jsx_runtime219.jsx)(import_lucide_react43.Plus, { className: "h-3.5 w-3.5" }),
18649
- "\u6DFB\u52A0\u6761\u4EF6"
18650
- ]
18651
- }
18652
- )
18653
- ] });
18654
- }
18655
- function ConditionRow2({
18656
- condition,
18657
- issue,
18658
- showDelete,
18659
- disabled,
18660
- size,
18661
- onOptChange,
18662
- onValueChange,
18663
- onScoreChange,
18664
- onDelete
18665
- }) {
18666
- const { opt, value, score } = condition;
18667
- const missingValue = !disabled && !!issue?.missingValue;
18668
- const missingScore = !disabled && !!issue?.missingScore;
18669
- return /* @__PURE__ */ (0, import_jsx_runtime219.jsxs)("div", { className: "space-y-1", children: [
18670
- /* @__PURE__ */ (0, import_jsx_runtime219.jsxs)("div", { className: "flex items-center gap-2", children: [
18671
- /* @__PURE__ */ (0, import_jsx_runtime219.jsxs)(
18672
- Select,
18673
- {
18674
- items: OPERATOR_VALUES.map((op) => ({
18675
- value: op,
18676
- label: operatorLabel2(op)
18677
- })),
18678
- value: opt,
18679
- onValueChange: (v) => onOptChange(v),
18680
- disabled,
18681
- children: [
18682
- /* @__PURE__ */ (0, import_jsx_runtime219.jsx)(
18683
- SelectTrigger,
18684
- {
18685
- size: CONTROL_SIZE_SELECT_ATTR[size],
18686
- className: cn("w-36 shrink-0", CONTROL_SIZE_TRIGGER_TEXT[size]),
18687
- children: /* @__PURE__ */ (0, import_jsx_runtime219.jsx)(SelectValue, {})
18688
- }
18689
- ),
18690
- /* @__PURE__ */ (0, import_jsx_runtime219.jsx)(SelectContent, { children: OPERATOR_VALUES.map((op) => /* @__PURE__ */ (0, import_jsx_runtime219.jsx)(SelectItem, { value: op, children: operatorLabel2(op) }, op)) })
18691
- ]
18692
- }
18693
- ),
18694
- !NO_VALUE_OPT.includes(opt) && /* @__PURE__ */ (0, import_jsx_runtime219.jsx)(
18695
- "div",
18696
- {
18697
- className: "w-[30%]" + (missingValue ? " [&_input]:border-destructive" : ""),
18698
- children: /* @__PURE__ */ (0, import_jsx_runtime219.jsx)(
18699
- ValueInput,
18700
- {
18701
- opt,
18702
- value,
18703
- disabled,
18704
- size,
18705
- onChange: onValueChange
18706
- }
18707
- )
18708
- }
18709
- ),
18710
- /* @__PURE__ */ (0, import_jsx_runtime219.jsx)(
18711
- Input,
18712
- {
18713
- type: "number",
18714
- className: cn(
18715
- "w-24 shrink-0",
18716
- CONTROL_SIZE_BOX[size],
18717
- missingScore && "border-destructive"
18718
- ),
18719
- placeholder: "\u8BC4\u5206",
18720
- value: score ?? "",
18721
- disabled,
18722
- "aria-invalid": missingScore || void 0,
18723
- onChange: (e) => onScoreChange(
18724
- e.target.value === "" ? void 0 : e.target.valueAsNumber
18725
- )
18726
- }
18727
- ),
18728
- showDelete && !disabled && /* @__PURE__ */ (0, import_jsx_runtime219.jsx)(
18729
- Button,
18730
- {
18731
- type: "button",
18732
- variant: "ghost",
18733
- size: "icon",
18734
- className: "h-8 w-8 shrink-0 text-muted-foreground hover:text-destructive",
18735
- onClick: onDelete,
18736
- children: /* @__PURE__ */ (0, import_jsx_runtime219.jsx)(import_lucide_react43.Trash2, { className: "h-4 w-4" })
18737
- }
18738
- )
18739
- ] }),
18740
- !disabled && issue && /* @__PURE__ */ (0, import_jsx_runtime219.jsx)("p", { className: "text-xs text-destructive", children: [
18741
- missingValue ? "\u8BF7\u586B\u5199\u53D6\u503C" : null,
18742
- missingScore ? "\u8BF7\u586B\u5199\u8BC4\u5206" : null
18743
- ].filter(Boolean).join("\uFF1B") })
18744
- ] });
18745
- }
18746
- function ValueInput({ opt, value, disabled, size, onChange }) {
18747
- if (NO_VALUE_OPT.includes(opt)) {
18748
- return null;
18749
- }
18750
- if (opt === "BETWEEN") {
18751
- const [v1, v2] = normalizeBetweenValue(value);
18752
- return /* @__PURE__ */ (0, import_jsx_runtime219.jsxs)("div", { className: "flex items-center gap-2", children: [
18753
- /* @__PURE__ */ (0, import_jsx_runtime219.jsx)(
18754
- Input,
18755
- {
18756
- type: "number",
18757
- className: cn("flex-1", CONTROL_SIZE_BOX[size]),
18758
- value: v1 ?? "",
18759
- disabled,
18760
- onChange: (e) => onChange([e.target.valueAsNumber, v2])
18761
- }
18762
- ),
18763
- /* @__PURE__ */ (0, import_jsx_runtime219.jsx)("span", { className: "text-muted-foreground text-sm shrink-0", children: "~" }),
18764
- /* @__PURE__ */ (0, import_jsx_runtime219.jsx)(
18765
- Input,
18766
- {
18767
- type: "number",
18768
- className: cn("flex-1", CONTROL_SIZE_BOX[size]),
18769
- value: v2 ?? "",
18770
- disabled,
18771
- onChange: (e) => onChange([v1, e.target.valueAsNumber])
18772
- }
18773
- )
18774
- ] });
18775
- }
18776
- if (MULTI_VALUE_OPT.includes(opt)) {
18777
- return /* @__PURE__ */ (0, import_jsx_runtime219.jsx)(
18778
- TagInput,
18779
- {
18780
- value: value ?? [],
18781
- disabled,
18782
- size,
18783
- onChange: (tags) => onChange(tags)
18784
- }
18785
- );
18786
- }
18787
- if (NUMERIC_OPT.includes(opt)) {
18788
- return /* @__PURE__ */ (0, import_jsx_runtime219.jsx)(
18789
- Input,
18790
- {
18791
- type: "number",
18792
- className: CONTROL_SIZE_BOX[size],
18793
- value: value ?? "",
18794
- disabled,
18795
- onChange: (e) => onChange(e.target.valueAsNumber)
18796
- }
18797
- );
18798
- }
18799
- return /* @__PURE__ */ (0, import_jsx_runtime219.jsx)(
18800
- Input,
18801
- {
18802
- type: "text",
18803
- className: CONTROL_SIZE_BOX[size],
18804
- value: value ?? "",
18805
- disabled,
18806
- onChange: (e) => onChange(e.target.value)
18807
- }
18808
- );
18809
- }
18810
- function TagInput({
18811
- value,
18812
- disabled,
18813
- size,
18814
- onChange
18815
- }) {
18816
- const [input, setInput] = (0, import_react76.useState)("");
18817
- const addTag = () => {
18818
- const tag = input.trim();
18819
- if (tag && !value.includes(tag)) {
18820
- onChange([...value, tag]);
18821
- }
18822
- setInput("");
18823
- };
18824
- const removeTag = (tag) => onChange(value.filter((t) => t !== tag));
18825
- return /* @__PURE__ */ (0, import_jsx_runtime219.jsxs)(
18826
- "div",
18827
- {
18828
- className: cn(
18829
- "flex flex-wrap items-center gap-1 min-h-9 rounded-md border bg-transparent px-3 py-1 text-sm",
18830
- CONTROL_SIZE_BOX_GROW[size]
18831
- ),
18832
- children: [
18833
- " ",
18834
- value.map((tag) => /* @__PURE__ */ (0, import_jsx_runtime219.jsxs)(Badge, { variant: "secondary", className: "gap-1 pr-1", children: [
18835
- tag,
18836
- !disabled && /* @__PURE__ */ (0, import_jsx_runtime219.jsx)(
18837
- "button",
18838
- {
18839
- type: "button",
18840
- className: "text-muted-foreground hover:text-foreground",
18841
- onClick: () => removeTag(tag),
18842
- children: "\xD7"
18843
- }
18844
- )
18845
- ] }, tag)),
18846
- !disabled && /* @__PURE__ */ (0, import_jsx_runtime219.jsx)(
18847
- "input",
18848
- {
18849
- className: "flex-1 min-w-16 bg-transparent outline-none placeholder:text-muted-foreground",
18850
- value: input,
18851
- placeholder: "\u8F93\u5165\u540E\u6309 Enter",
18852
- onChange: (e) => setInput(e.target.value),
18853
- onKeyDown: (e) => {
18854
- if (e.key === "Enter") {
18855
- e.preventDefault();
18856
- addTag();
18857
- }
18858
- }
18859
- }
18860
- )
18861
- ]
18862
- }
18863
- );
18864
- }
18865
-
18866
- // src/components/business/RuleExpression/index.tsx
18867
- var import_react77 = require("react");
18868
- var import_jsx_runtime220 = require("react/jsx-runtime");
18869
- function trailingWord(text) {
18870
- const m = text.match(/[\w一-龥]+$/u);
18871
- return m ? m[0] : "";
18872
- }
18873
- function labelSuffixLength(input, label) {
18874
- for (let len = Math.min(input.length, label.length); len > 0; len--) {
18875
- const suffix = input.slice(input.length - len);
18876
- if (label.includes(suffix)) return len;
18877
- }
18878
- return 0;
18879
- }
18880
- function serialize(tokens, fieldKey = "value") {
18881
- let out = "";
18882
- tokens.forEach((t, i) => {
18883
- if (t.type === "field") {
18884
- if (out && !out.endsWith(" ")) out += " ";
18885
- out += t[fieldKey];
18886
- } else {
18887
- if (i === tokens.length - 1) {
18888
- out += t.text;
18889
- return;
18890
- }
18891
- const text = t.text.trim();
18892
- if (!text) return;
18893
- if (out && !out.endsWith(" ")) out += " ";
18894
- out += `${text} `;
18895
- }
18896
- });
18897
- return out;
18898
- }
18899
- function seedTokens(value, options) {
18900
- if (!value) return [];
18901
- if (typeof value === "string") {
18902
- return value ? [{ type: "text", text: value }] : [];
18903
- }
18904
- const fields = (value.sourceField || []).map((v) => ({
18905
- type: "field",
18906
- value: String(v),
18907
- label: options.find((o) => o.value === String(v))?.label ?? String(v)
18908
- }));
18909
- const tokens = [];
18910
- let rest = value.expression ?? "";
18911
- for (const field of fields) {
18912
- const idx = rest.indexOf(field.value);
18913
- if (idx === -1) {
18914
- tokens.push(field);
18915
- continue;
18916
- }
18917
- const before = rest.slice(0, idx);
18918
- if (before) tokens.push({ type: "text", text: before });
18919
- tokens.push(field);
18920
- rest = rest.slice(idx + field.value.length);
18921
- }
18922
- if (rest) tokens.push({ type: "text", text: rest });
18923
- return tokens;
18924
- }
18925
- var X_SVG = '<svg xmlns="http://www.w3.org/2000/svg" width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M18 6 6 18"/><path d="m6 6 12 12"/></svg>';
18926
- function buildChip(field, disabled) {
18927
- const chip = document.createElement("span");
18928
- chip.contentEditable = "false";
18929
- chip.dataset.value = field.value;
18930
- chip.dataset.label = field.label;
18931
- chip.className = "mx-0.5 inline-flex items-center gap-1 rounded bg-muted px-2 py-0.5 align-middle text-xs font-medium";
18932
- const label = document.createElement("span");
18933
- label.textContent = field.label;
18934
- chip.appendChild(label);
18935
- if (!disabled) {
18936
- const btn = document.createElement("button");
18937
- btn.type = "button";
18938
- btn.dataset.remove = "true";
18939
- btn.setAttribute("aria-label", `remove-${field.value}`);
18940
- btn.className = "cursor-pointer opacity-60 hover:opacity-100";
18941
- btn.innerHTML = X_SVG;
18942
- chip.appendChild(btn);
18943
- }
18944
- return chip;
18945
- }
18946
- function parseEditor(root) {
18947
- const tokens = [];
18948
- const pushText = (text) => {
18949
- if (!text) return;
18950
- const last = tokens[tokens.length - 1];
18951
- if (last?.type === "text") last.text += text;
18952
- else tokens.push({ type: "text", text });
18953
- };
18954
- const walk = (node) => {
18955
- if (node.nodeType === Node.TEXT_NODE) {
18956
- pushText(node.textContent ?? "");
18957
- return;
18958
- }
18959
- if (!(node instanceof HTMLElement)) return;
18960
- if (node.dataset.value != null) {
18961
- tokens.push({
18962
- type: "field",
18963
- value: node.dataset.value,
18964
- label: node.dataset.label ?? node.dataset.value
18965
- });
18966
- return;
18967
- }
18968
- if (node.tagName === "BR") {
18969
- pushText("\n");
18970
- return;
18971
- }
18972
- node.childNodes.forEach(walk);
18973
- if (node.tagName === "DIV" || node.tagName === "P") pushText("\n");
18974
- };
18975
- root.childNodes.forEach(walk);
18976
- return tokens;
18977
- }
18978
- function setCaret(node, offset) {
18979
- const sel = window.getSelection();
18980
- if (!sel) return;
18981
- const range = document.createRange();
18982
- range.setStart(node, offset);
18983
- range.collapse(true);
18984
- sel.removeAllRanges();
18985
- sel.addRange(range);
18986
- }
18987
- function setCaretToEnd(el) {
18988
- const sel = window.getSelection();
18989
- if (!sel) return;
18990
- const range = document.createRange();
18991
- range.selectNodeContents(el);
18992
- range.collapse(false);
18993
- sel.removeAllRanges();
18994
- sel.addRange(range);
18995
- }
18996
- function RuleExpression({
18997
- options = [],
18998
- value,
18999
- onChange,
19000
- placeholder,
19001
- disabled,
19002
- size = "middle",
19003
- id,
19004
- className
19005
- }) {
19006
- const editorRef = (0, import_react77.useRef)(null);
19007
- const [focused, setFocused] = (0, import_react77.useState)(false);
19008
- const [activeIndex, setActiveIndex] = (0, import_react77.useState)(0);
19009
- const [matchText, setMatchText] = (0, import_react77.useState)("");
19010
- const lastEmittedRef = (0, import_react77.useRef)(null);
19011
- const matched = matchText ? options.filter((o) => o.label.toLowerCase().includes(matchText.toLowerCase())) : [];
19012
- const showSuggestions = focused && !disabled && matched.length > 0;
19013
- const emitTokens = (tokens) => {
19014
- const out = {
19015
- expression: serialize(tokens),
19016
- expressionLabel: serialize(tokens, "label"),
19017
- sourceField: tokens.filter((t) => t.type === "field").map((t) => t.value)
19018
- };
19019
- lastEmittedRef.current = JSON.stringify(out);
19020
- onChange?.(out);
19021
- };
19022
- const caretBeforeText = () => {
19023
- const ed = editorRef.current;
19024
- const sel = window.getSelection();
19025
- if (!ed || !sel || !sel.rangeCount) return "";
19026
- const node = sel.anchorNode;
19027
- if (!node || !ed.contains(node)) return "";
19028
- if (node === ed) {
19029
- const prev = ed.childNodes[sel.anchorOffset - 1];
19030
- return prev?.nodeType === Node.TEXT_NODE ? prev.textContent ?? "" : "";
19031
- }
19032
- if (node.nodeType === Node.TEXT_NODE) {
19033
- return (node.textContent ?? "").slice(0, sel.anchorOffset);
19034
- }
19035
- return "";
19036
- };
19037
- const refreshSuggest = () => {
19038
- setMatchText(trailingWord(caretBeforeText()));
19039
- };
19040
- const syncFromDOM = () => {
19041
- const ed = editorRef.current;
19042
- if (!ed) return;
19043
- emitTokens(parseEditor(ed));
19044
- refreshSuggest();
19045
- };
19046
- const rebuild = (tokens) => {
19047
- const ed = editorRef.current;
19048
- if (!ed) return;
19049
- ed.textContent = "";
19050
- for (const t of tokens) {
19051
- if (t.type === "field") ed.appendChild(buildChip(t, disabled));
19052
- else ed.appendChild(document.createTextNode(t.text));
19053
- }
19054
- };
19055
- (0, import_react77.useLayoutEffect)(() => {
19056
- const sig = JSON.stringify(value ?? null);
19057
- if (sig !== lastEmittedRef.current) {
19058
- rebuild(seedTokens(value, options));
19059
- lastEmittedRef.current = sig;
19060
- setMatchText("");
19061
- }
19062
- }, [value, options, disabled]);
19063
- const selectOption = (opt) => {
19064
- const ed = editorRef.current;
19065
- const sel = window.getSelection();
19066
- if (!ed || !sel || !sel.rangeCount) return;
19067
- let node = sel.anchorNode;
19068
- let offset = sel.anchorOffset;
19069
- if (!node || !ed.contains(node)) return;
19070
- if (node === ed) {
19071
- const tn = document.createTextNode("");
19072
- ed.insertBefore(tn, ed.childNodes[offset] ?? null);
19073
- node = tn;
19074
- offset = 0;
19075
- }
19076
- if (node.nodeType !== Node.TEXT_NODE) return;
19077
- const textNode = node;
19078
- const text = textNode.textContent ?? "";
19079
- const before = text.slice(0, offset);
19080
- const after = text.slice(offset);
19081
- const replaceLen = Math.max(
19082
- labelSuffixLength(before, opt.label),
19083
- trailingWord(before).length
19084
- );
19085
- textNode.textContent = before.slice(0, before.length - replaceLen);
19086
- const afterNode = document.createTextNode(after);
19087
- const chip = buildChip({ type: "field", label: opt.label, value: opt.value }, disabled);
19088
- ed.insertBefore(chip, textNode.nextSibling);
19089
- ed.insertBefore(afterNode, chip.nextSibling);
19090
- if (!textNode.textContent) ed.removeChild(textNode);
19091
- ed.focus();
19092
- setCaret(afterNode, 0);
19093
- setActiveIndex(0);
19094
- syncFromDOM();
19095
- };
19096
- const removeChip = (chip) => {
19097
- const ed = editorRef.current;
19098
- if (!ed) return;
19099
- const prev = chip.previousSibling;
19100
- const caretNode = prev && prev.nodeType === Node.TEXT_NODE ? prev : document.createTextNode("");
19101
- if (!caretNode.isConnected) ed.insertBefore(caretNode, chip);
19102
- const caretPos = caretNode.textContent?.length ?? 0;
19103
- ed.removeChild(chip);
19104
- ed.normalize();
19105
- ed.focus();
19106
- if (caretNode.isConnected) setCaret(caretNode, caretPos);
19107
- else setCaretToEnd(ed);
19108
- syncFromDOM();
19109
- };
19110
- const tryRemoveAdjacentChip = (dir) => {
19111
- const ed = editorRef.current;
19112
- const sel = window.getSelection();
19113
- if (!ed || !sel || !sel.rangeCount || !sel.isCollapsed) return false;
19114
- const node = sel.anchorNode;
19115
- if (!node || !ed.contains(node)) return false;
19116
- let target = null;
19117
- if (node === ed) {
19118
- target = dir === "before" ? ed.childNodes[sel.anchorOffset - 1] : ed.childNodes[sel.anchorOffset];
19119
- } else if (node.nodeType === Node.TEXT_NODE) {
19120
- const len = node.textContent?.length ?? 0;
19121
- const atEdge = dir === "before" ? sel.anchorOffset === 0 : sel.anchorOffset === len;
19122
- if (atEdge) {
19123
- target = dir === "before" ? node.previousSibling : node.nextSibling;
19124
- }
19125
- }
19126
- if (target instanceof HTMLElement && target.dataset.value != null) {
19127
- removeChip(target);
19128
- return true;
19129
- }
19130
- return false;
19131
- };
19132
- const handleKeyDown = (e) => {
19133
- if (e.key === "ArrowDown" && showSuggestions) {
19134
- e.preventDefault();
19135
- setActiveIndex((x) => (x + 1) % matched.length);
19136
- return;
19137
- }
19138
- if (e.key === "ArrowUp" && showSuggestions) {
19139
- e.preventDefault();
19140
- setActiveIndex((x) => (x - 1 + matched.length) % matched.length);
19141
- return;
19142
- }
19143
- if (e.key === "Enter") {
19144
- if (showSuggestions) {
19145
- e.preventDefault();
19146
- selectOption(matched[Math.min(activeIndex, matched.length - 1)]);
19147
- }
19148
- return;
19149
- }
19150
- if (e.key === "Backspace" && tryRemoveAdjacentChip("before")) {
19151
- e.preventDefault();
19152
- return;
19153
- }
19154
- if (e.key === "Delete" && tryRemoveAdjacentChip("after")) {
19155
- e.preventDefault();
19156
- }
19157
- };
19158
- return /* @__PURE__ */ (0, import_jsx_runtime220.jsxs)(
19159
- "div",
19160
- {
19161
- className: cn("relative", className),
19162
- onBlur: (e) => {
19163
- if (!e.currentTarget.contains(e.relatedTarget)) {
19164
- setFocused(false);
19165
- }
19166
- },
19167
- children: [
19168
- /* @__PURE__ */ (0, import_jsx_runtime220.jsx)(
19169
- "div",
19170
- {
19171
- ref: editorRef,
19172
- id,
19173
- contentEditable: !disabled,
19174
- suppressContentEditableWarning: true,
19175
- role: "textbox",
19176
- "aria-multiline": "true",
19177
- "data-placeholder": placeholder,
19178
- className: cn(
19179
- "min-h-9 w-full whitespace-pre-wrap break-words rounded-md border border-input bg-background px-3 py-2 text-sm",
19180
- CONTROL_SIZE_BOX_GROW[size],
19181
- "focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2",
19182
- "empty:before:pointer-events-none empty:before:text-muted-foreground empty:before:content-[attr(data-placeholder)]",
19183
- disabled && "cursor-not-allowed opacity-50"
19184
- ),
19185
- onFocus: () => {
19186
- setFocused(true);
19187
- refreshSuggest();
19188
- },
19189
- onInput: syncFromDOM,
19190
- onKeyDown: handleKeyDown,
19191
- onKeyUp: refreshSuggest,
19192
- onClick: (e) => {
19193
- const btn = e.target.closest?.("[data-remove]");
19194
- if (btn) {
19195
- e.preventDefault();
19196
- const chip = btn.closest("[data-value]");
19197
- if (chip instanceof HTMLElement) removeChip(chip);
19198
- return;
19199
- }
19200
- refreshSuggest();
19201
- }
19202
- }
19203
- ),
19204
- showSuggestions && /* @__PURE__ */ (0, import_jsx_runtime220.jsx)(
19205
- "ul",
19206
- {
19207
- role: "list",
19208
- className: "absolute left-0 top-full z-50 mt-1 max-h-60 w-full overflow-auto rounded-md border bg-popover p-1 shadow-md",
19209
- onMouseDown: (e) => e.preventDefault(),
19210
- children: matched.map((opt, index) => /* @__PURE__ */ (0, import_jsx_runtime220.jsx)("li", { children: /* @__PURE__ */ (0, import_jsx_runtime220.jsx)(
19211
- "button",
19212
- {
19213
- type: "button",
19214
- className: cn(
19215
- "w-full cursor-pointer rounded-sm px-2 py-1.5 text-left text-sm",
19216
- index === activeIndex && "bg-accent"
19217
- ),
19218
- onMouseEnter: () => setActiveIndex(index),
19219
- onClick: () => selectOption(opt),
19220
- children: opt.label
19221
- }
19222
- ) }, opt.value))
19223
- }
19224
- )
19225
- ]
19226
- }
19227
- );
19228
- }
19229
-
19230
- // src/components/business/ScriptEditor/index.tsx
19231
- var import_react78 = require("react");
19232
- var import_jsx_runtime221 = require("react/jsx-runtime");
19233
- function escapeHtml(text) {
19234
- return text.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;");
19235
- }
19236
- function buildFunctionSnippet(fn) {
19237
- const params = fn.parameters ?? [];
19238
- if (params.length === 0) return `${fn.name}()`;
19239
- const inner = params.map((p, i) => `\${${i + 1}:${p.name}}`).join(", ");
19240
- return `${fn.name}(${inner})`;
19241
- }
19242
- function buildFunctionDoc(fn) {
19243
- const parts = [];
19244
- if (fn.description) parts.push(`<p>${escapeHtml(fn.description)}</p>`);
19245
- const params = fn.parameters ?? [];
19246
- if (params.length > 0) {
19247
- const rows = params.map(
19248
- (p) => `<li><b>${escapeHtml(p.name)}</b>` + (p.type ? ` <i>${escapeHtml(p.type)}</i>` : "") + (p.description ? ` \u2014 ${escapeHtml(p.description)}` : "") + `</li>`
19249
- ).join("");
19250
- parts.push(`<ul>${rows}</ul>`);
19251
- }
19252
- return parts.join("");
19253
- }
19254
- function buildVariableDoc(variable) {
19255
- const parts = [];
19256
- if (variable.type) parts.push(`<p><i>${escapeHtml(variable.type)}</i></p>`);
19257
- if (variable.description) parts.push(`<p>${escapeHtml(variable.description)}</p>`);
19258
- return parts.join("");
19259
- }
19260
- function buildScriptCompletions(functions, variables) {
19261
- return {
19262
- getCompletions(_editor, _session, _position, _prefix, callback) {
19263
- const completions = [
19264
- ...(functions ?? []).map((fn) => ({
19265
- caption: fn.name,
19266
- snippet: buildFunctionSnippet(fn),
19267
- meta: "function",
19268
- docHTML: buildFunctionDoc(fn)
19269
- })),
19270
- ...(variables ?? []).map((v) => ({
19271
- caption: v.name,
19272
- value: v.name,
19273
- meta: "variable",
19274
- docHTML: buildVariableDoc(v)
19275
- }))
19276
- ];
19277
- callback(null, completions);
19278
- }
19279
- };
19280
- }
19281
- var MODE_LOADERS = {
19282
- python: () => Promise.all([
19283
- import("ace-builds/src-noconflict/mode-python"),
19284
- import("ace-builds/src-noconflict/snippets/python")
19285
- ])
19286
- };
19287
- function ScriptEditor({
19288
- id,
19289
- value,
19290
- onChange,
19291
- placeholder,
19292
- language = "python",
19293
- functions,
19294
- variables,
19295
- readonly,
19296
- height = 160,
19297
- className
19298
- }) {
19299
- const containerRef = (0, import_react78.useRef)(null);
19300
- const editorRef = (0, import_react78.useRef)(null);
19301
- const onChangeRef = (0, import_react78.useRef)(onChange);
19302
- onChangeRef.current = onChange;
19303
- const [isEmpty2, setIsEmpty] = (0, import_react78.useState)(!value);
19304
- (0, import_react78.useEffect)(() => {
19305
- let disposed = false;
19306
- (async () => {
19307
- const ace = await import("ace-builds");
19308
- await Promise.all([
19309
- import("ace-builds/src-noconflict/theme-textmate"),
19310
- import("ace-builds/src-noconflict/ext-language_tools"),
19311
- MODE_LOADERS[language]?.() ?? Promise.resolve()
19312
- ]);
19313
- if (disposed || !containerRef.current) return;
19314
- const editor = ace.edit(containerRef.current, {
19315
- value: value ?? "",
19316
- theme: "ace/theme/textmate",
19317
- mode: MODE_LOADERS[language] ? `ace/mode/${language}` : "ace/mode/text",
19318
- showPrintMargin: false,
19319
- highlightActiveLine: true,
19320
- tabSize: 4,
19321
- useSoftTabs: true,
19322
- readOnly: readonly ?? false,
19323
- enableBasicAutocompletion: [buildScriptCompletions(functions, variables)],
19324
- enableLiveAutocompletion: true,
19325
- enableSnippets: true
19326
- });
19327
- editor.session.setUseWorker(false);
19328
- editor.on("change", () => {
19329
- const current = editor.getValue();
19330
- setIsEmpty(current === "");
19331
- onChangeRef.current?.(current);
19332
- });
19333
- editorRef.current = editor;
19334
- })().catch(() => {
19335
- });
19336
- return () => {
19337
- disposed = true;
19338
- editorRef.current?.destroy();
19339
- editorRef.current = null;
19340
- };
19341
- }, []);
19342
- (0, import_react78.useEffect)(() => {
19343
- const editor = editorRef.current;
19344
- if (!editor) return;
19345
- const next = value ?? "";
19346
- if (editor.getValue() !== next) {
19347
- editor.setValue(next, -1);
19348
- setIsEmpty(next === "");
19349
- }
19350
- }, [value]);
19351
- (0, import_react78.useEffect)(() => {
19352
- editorRef.current?.setOptions({
19353
- enableBasicAutocompletion: [buildScriptCompletions(functions, variables)]
19354
- });
19355
- }, [functions, variables]);
19356
- (0, import_react78.useEffect)(() => {
19357
- editorRef.current?.setReadOnly(readonly ?? false);
19358
- }, [readonly]);
19359
- return /* @__PURE__ */ (0, import_jsx_runtime221.jsxs)(
19360
- "div",
19361
- {
19362
- className: cn(
19363
- "relative w-full rounded-md border bg-background overflow-hidden",
19364
- readonly && "bg-muted",
19365
- className
19366
- ),
19367
- children: [
19368
- /* @__PURE__ */ (0, import_jsx_runtime221.jsx)("div", { ref: containerRef, id, style: { height }, className: "w-full" }),
19369
- isEmpty2 && placeholder && /* @__PURE__ */ (0, import_jsx_runtime221.jsx)("div", { className: "absolute top-0 left-0 px-3 py-2 text-sm text-muted-foreground pointer-events-none z-10", children: placeholder })
19370
- ]
19371
- }
19372
- );
19373
- }
19374
-
19375
- // src/components/business/SiteFooter/index.tsx
19376
- var import_jsx_runtime222 = require("react/jsx-runtime");
19377
- function SiteFooter({
19378
- brand,
19379
- groups,
19380
- copyright,
19381
- bottomLinks,
19382
- onLinkClick,
19383
- className,
19384
- style
19385
- }) {
19386
- const renderLink = (link) => {
19387
- if (link.href) {
19388
- return /* @__PURE__ */ (0, import_jsx_runtime222.jsx)(
19389
- Link2,
19390
- {
19391
- href: link.href,
19392
- text: link.text,
19393
- className: "text-sm text-muted-foreground transition-colors hover:text-foreground"
19394
- }
19395
- );
19396
- }
19397
- return /* @__PURE__ */ (0, import_jsx_runtime222.jsx)(
19398
- "button",
19399
- {
19400
- type: "button",
19401
- "data-slot": "site-footer-link",
19402
- className: "text-left text-sm text-muted-foreground transition-colors hover:text-foreground",
19403
- onClick: () => onLinkClick?.({ key: link.text, text: link.text }),
19404
- children: link.text
19405
- }
19406
- );
19407
- };
19408
- const hasBrand = Boolean(brand && (brand.logo || brand.name || brand.description));
19409
- const hasGroups = Boolean(groups && groups.length > 0);
19410
- const hasBottom = Boolean(copyright || bottomLinks && bottomLinks.length > 0);
19411
- if (!hasBrand && !hasGroups && !hasBottom) {
19412
- return null;
19413
- }
19414
- return /* @__PURE__ */ (0, import_jsx_runtime222.jsxs)(
19415
- "footer",
19416
- {
19417
- "data-slot": "site-footer",
19418
- className: cn("border-t bg-muted/40", className),
19419
- style,
19420
- children: [
19421
- hasBrand || hasGroups ? /* @__PURE__ */ (0, import_jsx_runtime222.jsxs)(
19422
- "div",
19423
- {
19424
- "data-slot": "site-footer-main",
19425
- className: "mx-auto flex w-full max-w-6xl flex-col gap-8 px-6 py-10 md:flex-row md:justify-between",
19426
- children: [
19427
- hasBrand ? /* @__PURE__ */ (0, import_jsx_runtime222.jsxs)("div", { "data-slot": "site-footer-brand", className: "flex max-w-xs flex-col gap-3", children: [
19428
- brand.logo || brand.name ? /* @__PURE__ */ (0, import_jsx_runtime222.jsxs)("div", { className: "flex items-center gap-2", children: [
19429
- brand.logo ? /* @__PURE__ */ (0, import_jsx_runtime222.jsx)(
19430
- "img",
19431
- {
19432
- src: brand.logo,
19433
- alt: brand.name ?? "",
19434
- className: "size-8 rounded-md object-contain"
19435
- }
19436
- ) : null,
19437
- brand.name ? /* @__PURE__ */ (0, import_jsx_runtime222.jsx)("span", { className: "text-base font-semibold", children: brand.name }) : null
19438
- ] }) : null,
19439
- brand.description ? /* @__PURE__ */ (0, import_jsx_runtime222.jsx)("p", { className: "text-sm leading-relaxed text-muted-foreground", children: brand.description }) : null
19440
- ] }) : null,
19441
- hasGroups ? /* @__PURE__ */ (0, import_jsx_runtime222.jsx)(
19442
- "div",
19443
- {
19444
- "data-slot": "site-footer-groups",
19445
- className: "grid flex-1 grid-cols-2 gap-8 sm:grid-cols-3 md:justify-items-end",
19446
- children: groups.map((group) => /* @__PURE__ */ (0, import_jsx_runtime222.jsxs)(
19447
- "div",
19448
- {
19449
- "data-slot": "site-footer-group",
19450
- className: "flex min-w-24 flex-col gap-3",
19451
- children: [
19452
- /* @__PURE__ */ (0, import_jsx_runtime222.jsx)("span", { className: "text-sm font-medium", children: group.title }),
19453
- /* @__PURE__ */ (0, import_jsx_runtime222.jsx)("div", { className: "flex flex-col gap-2", children: group.links.map((link) => /* @__PURE__ */ (0, import_jsx_runtime222.jsx)("div", { children: renderLink(link) }, link.text)) })
19454
- ]
19455
- },
19456
- group.title
19457
- ))
19458
- }
19459
- ) : null
19460
- ]
19461
- }
19462
- ) : null,
19463
- hasBottom ? /* @__PURE__ */ (0, import_jsx_runtime222.jsx)("div", { "data-slot": "site-footer-bottom", className: "border-t", children: /* @__PURE__ */ (0, import_jsx_runtime222.jsxs)("div", { className: "mx-auto flex w-full max-w-6xl flex-col items-center gap-2 px-6 py-4 text-xs text-muted-foreground sm:flex-row sm:justify-between", children: [
19464
- copyright ? /* @__PURE__ */ (0, import_jsx_runtime222.jsx)("span", { children: copyright }) : /* @__PURE__ */ (0, import_jsx_runtime222.jsx)("span", {}),
19465
- bottomLinks && bottomLinks.length > 0 ? /* @__PURE__ */ (0, import_jsx_runtime222.jsx)("div", { "data-slot": "site-footer-bottom-links", className: "flex items-center gap-4", children: bottomLinks.map((link) => /* @__PURE__ */ (0, import_jsx_runtime222.jsx)("div", { children: renderLink(link) }, link.text)) }) : null
19466
- ] }) }) : null
19467
- ]
19468
- }
19469
- );
19470
- }
19471
-
19472
15396
  // src/components/register.ts
19473
15397
  var builtinComponents = {
19474
15398
  Page,
@@ -19618,38 +15542,7 @@ var builtinComponents = {
19618
15542
  MessageList,
19619
15543
  Composer,
19620
15544
  ConversationList,
19621
- StepsPanel,
19622
- // 图表组件集(src/components/chart 目录,echarts 渲染;
19623
- // 交互事件经 echarts click → onSelect,DSL 用 events.onSelect + ${event})
19624
- MetricCard,
19625
- LineChart: LineChart2,
19626
- ColumnChart,
19627
- BarChart: BarChart2,
19628
- AreaChart,
19629
- PieChart: PieChart2,
19630
- DonutChart,
19631
- ScatterChart: ScatterChart2,
19632
- BubbleChart,
19633
- RadarChart: RadarChart2,
19634
- FunnelChart: FunnelChart2,
19635
- GaugeChart: GaugeChart2,
19636
- Heatmap,
19637
- TreeChart: TreeChart2,
19638
- SankeyChart: SankeyChart2,
19639
- MapChart: MapChart2,
19640
- ComposedChart,
19641
- // 业务组件(business 子目录)
19642
- Login,
19643
- EntityCard,
19644
- CardList,
19645
- SiteFooter,
19646
- // 业务表单类组件(value/onChange 受控;DSL 用 props.value + events.onChange 读写 ${event})
19647
- SearchableSelect,
19648
- FieldCondition,
19649
- ExpressionEditor,
19650
- RuleConfig,
19651
- RuleExpression,
19652
- ScriptEditor
15545
+ StepsPanel
19653
15546
  };
19654
15547
  function registerBuiltinComponents(registry) {
19655
15548
  for (const [type, component] of Object.entries(builtinComponents)) {
@@ -19669,7 +15562,6 @@ function createComponentRegistry() {
19669
15562
  Alert,
19670
15563
  AlertDialog,
19671
15564
  Anchor,
19672
- AreaChart,
19673
15565
  ArtifactPanel,
19674
15566
  AspectRatio,
19675
15567
  Attachment,
@@ -19677,10 +15569,8 @@ function createComponentRegistry() {
19677
15569
  Avatar,
19678
15570
  BackTop,
19679
15571
  Badge,
19680
- BarChart,
19681
15572
  Breadcrumb,
19682
15573
  Bubble,
19683
- BubbleChart,
19684
15574
  Button,
19685
15575
  ButtonGroup,
19686
15576
  CONTROL_SIZE_AREA,
@@ -19690,7 +15580,6 @@ function createComponentRegistry() {
19690
15580
  CONTROL_SIZE_TRIGGER_TEXT,
19691
15581
  Calendar,
19692
15582
  Card,
19693
- CardList,
19694
15583
  Carousel,
19695
15584
  Cascader,
19696
15585
  ChatInput,
@@ -19700,14 +15589,13 @@ function createComponentRegistry() {
19700
15589
  ChipFilter,
19701
15590
  ChipNode,
19702
15591
  Citation,
15592
+ ClearHoverButton,
19703
15593
  CodeBlock,
19704
15594
  Col,
19705
15595
  Collapsible,
19706
15596
  ColorPicker,
19707
- ColumnChart,
19708
15597
  Combobox,
19709
15598
  Command,
19710
- ComposedChart,
19711
15599
  Composer,
19712
15600
  Container,
19713
15601
  Content,
@@ -19719,26 +15607,18 @@ function createComponentRegistry() {
19719
15607
  Dialog,
19720
15608
  DiffViewer,
19721
15609
  DocumentPreview,
19722
- DonutChart,
19723
15610
  Drawer,
19724
15611
  DropdownMenu,
19725
- EChartBase,
19726
15612
  Empty,
19727
- EntityCard,
19728
- ExpressionEditor,
19729
15613
  Field,
19730
- FieldCondition,
19731
15614
  FilePreview,
19732
15615
  Flex,
19733
15616
  Footer,
19734
15617
  For,
19735
15618
  Form,
19736
- FunnelChart,
19737
15619
  Gallery,
19738
- GaugeChart,
19739
15620
  Grid,
19740
15621
  Header,
19741
- Heatmap,
19742
15622
  HoverCard,
19743
15623
  ICONS,
19744
15624
  Icon,
@@ -19753,31 +15633,25 @@ function createComponentRegistry() {
19753
15633
  Kbd,
19754
15634
  Label,
19755
15635
  Layout,
19756
- LineChart,
19757
15636
  Link,
19758
15637
  List,
19759
15638
  LoadingOverlay,
19760
- Login,
19761
- MapChart,
19762
15639
  Markdown,
19763
15640
  Mentions,
19764
15641
  Menu,
19765
15642
  Menubar,
19766
15643
  Message,
19767
15644
  MessageList,
19768
- MetricCard,
19769
15645
  NavigationMenu,
19770
15646
  Notification,
19771
15647
  NumberInput,
19772
15648
  Page,
19773
15649
  Pagination,
19774
- PieChart,
19775
15650
  Popconfirm,
19776
15651
  Popover,
19777
15652
  Progress,
19778
15653
  Prompt,
19779
15654
  QRCode,
19780
- RadarChart,
19781
15655
  RadioGroup,
19782
15656
  RailTooltip,
19783
15657
  Rate,
@@ -19785,13 +15659,7 @@ function createComponentRegistry() {
19785
15659
  Result,
19786
15660
  RichText,
19787
15661
  Row,
19788
- RuleConfig,
19789
- RuleExpression,
19790
- SankeyChart,
19791
- ScatterChart,
19792
- ScriptEditor,
19793
15662
  ScrollArea,
19794
- SearchableSelect,
19795
15663
  Segmented,
19796
15664
  Select,
19797
15665
  Sender,
@@ -19799,7 +15667,6 @@ function createComponentRegistry() {
19799
15667
  Sheet,
19800
15668
  Sidebar,
19801
15669
  Sider,
19802
- SiteFooter,
19803
15670
  Skeleton,
19804
15671
  Slider,
19805
15672
  Sonner,
@@ -19827,7 +15694,6 @@ function createComponentRegistry() {
19827
15694
  Tour,
19828
15695
  Transfer,
19829
15696
  Tree,
19830
- TreeChart,
19831
15697
  TreeSelect,
19832
15698
  Typography,
19833
15699
  Upload,