@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.
@@ -2,7 +2,7 @@ import {
2
2
  Icon,
3
3
  Menu,
4
4
  Sidebar
5
- } from "./chunk-PWXXEJ4J.js";
5
+ } from "./chunk-SD2XSQIP.js";
6
6
  import {
7
7
  Alert,
8
8
  AlertDescription,
@@ -140,20 +140,7 @@ import {
140
140
  Typography,
141
141
  navigationMenuTriggerStyle,
142
142
  toast
143
- } from "./chunk-XMWIT53K.js";
144
- import {
145
- Dialog,
146
- DialogContent,
147
- DialogDescription,
148
- DialogHeader,
149
- DialogTitle,
150
- DialogTrigger,
151
- Drawer,
152
- DrawerContent,
153
- DrawerFooter,
154
- DrawerHeader,
155
- DrawerTitle
156
- } from "./chunk-HAOAXDTW.js";
143
+ } from "./chunk-T3TGKYAB.js";
157
144
  import {
158
145
  Accordion,
159
146
  AccordionContent,
@@ -176,7 +163,20 @@ import {
176
163
  Tooltip,
177
164
  TooltipContent,
178
165
  TooltipTrigger
179
- } from "./chunk-IBMIQXOM.js";
166
+ } from "./chunk-7OBUSV5S.js";
167
+ import {
168
+ Dialog,
169
+ DialogContent,
170
+ DialogDescription,
171
+ DialogHeader,
172
+ DialogTitle,
173
+ DialogTrigger,
174
+ Drawer,
175
+ DrawerContent,
176
+ DrawerFooter,
177
+ DrawerHeader,
178
+ DrawerTitle
179
+ } from "./chunk-YVHPJHW3.js";
180
180
  import {
181
181
  Button,
182
182
  buttonVariants,
@@ -9848,7 +9848,7 @@ var MentionInput = forwardRef(
9848
9848
  }
9849
9849
  onChipsChange?.(collectChips(fieldRef.current));
9850
9850
  };
9851
- const serialize2 = () => fieldRef.current ? serializeNode(fieldRef.current).replace(/\n$/, "") : "";
9851
+ const serialize = () => fieldRef.current ? serializeNode(fieldRef.current).replace(/\n$/, "") : "";
9852
9852
  const buildChipElement = (item, source) => {
9853
9853
  const chip = document.createElement("span");
9854
9854
  chip.setAttribute(CHIP_ATTR, "");
@@ -9900,7 +9900,7 @@ var MentionInput = forwardRef(
9900
9900
  selection.addRange(range);
9901
9901
  }
9902
9902
  setEmpty(false);
9903
- onChange?.(serialize2());
9903
+ onChange?.(serialize());
9904
9904
  emitChips();
9905
9905
  };
9906
9906
  useImperativeHandle(ref, () => ({
@@ -9989,7 +9989,7 @@ var MentionInput = forwardRef(
9989
9989
  field.innerHTML = "";
9990
9990
  }
9991
9991
  setEmpty(serializeNode(field).trim() === "");
9992
- onChange?.(serialize2());
9992
+ onChange?.(serialize());
9993
9993
  refreshMenu();
9994
9994
  };
9995
9995
  const handleKeyDown = (event) => {
@@ -10024,7 +10024,7 @@ var MentionInput = forwardRef(
10024
10024
  }
10025
10025
  if (event.key === "Enter" && !event.shiftKey && !composingRef.current && !event.nativeEvent.isComposing) {
10026
10026
  event.preventDefault();
10027
- const text = serialize2();
10027
+ const text = serialize();
10028
10028
  if (text.trim() !== "" && !disabled) {
10029
10029
  onSubmit?.(text);
10030
10030
  }
@@ -10051,7 +10051,7 @@ var MentionInput = forwardRef(
10051
10051
  if (chip) {
10052
10052
  event.preventDefault();
10053
10053
  chip.remove();
10054
- onChange?.(serialize2());
10054
+ onChange?.(serialize());
10055
10055
  emitChips();
10056
10056
  setEmpty(serializeNode(field).trim() === "");
10057
10057
  }
@@ -10623,6 +10623,7 @@ function ChatPage({
10623
10623
  artifactOpen,
10624
10624
  defaultArtifactOpen = false,
10625
10625
  onArtifactOpenChange,
10626
+ content,
10626
10627
  className,
10627
10628
  style
10628
10629
  }) {
@@ -10876,7 +10877,7 @@ function ChatPage({
10876
10877
  "div",
10877
10878
  {
10878
10879
  "data-slot": "chat-page-menu",
10879
- className: "flex h-full w-[264px] shrink-0 flex-col gap-2 overflow-hidden border-r border-border px-2 py-3",
10880
+ className: "flex h-full w-[250px] shrink-0 flex-col gap-2 overflow-hidden border-r border-border px-2 py-3",
10880
10881
  children: [
10881
10882
  brand ? /* @__PURE__ */ jsxs92("div", { className: "flex shrink-0 items-center justify-between px-2", children: [
10882
10883
  /* @__PURE__ */ jsxs92("div", { className: "flex flex-col gap-0.5", children: [
@@ -10959,7 +10960,7 @@ function ChatPage({
10959
10960
  ]
10960
10961
  }
10961
10962
  ),
10962
- /* @__PURE__ */ jsx133("div", { className: "@container h-full min-w-0 flex-1", children: open || activePanelItem ? /* @__PURE__ */ jsxs92(PanelGroup, { orientation: "horizontal", style: { width: "100%", height: "100%" }, children: [
10963
+ /* @__PURE__ */ jsx133("div", { className: "@container h-full min-w-0 flex-1", children: content !== void 0 ? renderSlot(content) : open || activePanelItem ? /* @__PURE__ */ jsxs92(PanelGroup, { orientation: "horizontal", style: { width: "100%", height: "100%" }, children: [
10963
10964
  /* @__PURE__ */ jsx133(Panel, { defaultSize: "62%", children: chatArea }),
10964
10965
  /* @__PURE__ */ jsx133(ResizableHandle, { withHandle: true }),
10965
10966
  /* @__PURE__ */ jsx133(Panel, { defaultSize: "38%", minSize: "380px", maxSize: "760px", children: activePanelItem ? /* @__PURE__ */ jsx133(
@@ -11114,4085 +11115,6 @@ function Suggestion({ items = [], justify = "start", onSelect, className, style
11114
11115
  );
11115
11116
  }
11116
11117
 
11117
- // src/components/chart/EChartBase/index.tsx
11118
- import { useEffect as useEffect40, useRef as useRef14, useState as useState47 } from "react";
11119
-
11120
- // src/components/chart/echarts-modules.ts
11121
- import * as echarts from "echarts/core";
11122
- import {
11123
- BarChart,
11124
- FunnelChart,
11125
- GaugeChart,
11126
- HeatmapChart,
11127
- LineChart,
11128
- MapChart,
11129
- PieChart,
11130
- RadarChart,
11131
- SankeyChart,
11132
- ScatterChart,
11133
- TreeChart
11134
- } from "echarts/charts";
11135
- import {
11136
- DataZoomComponent,
11137
- GeoComponent,
11138
- GridComponent,
11139
- LegendComponent,
11140
- RadarComponent,
11141
- TitleComponent,
11142
- TooltipComponent,
11143
- VisualMapComponent
11144
- } from "echarts/components";
11145
- import { CanvasRenderer } from "echarts/renderers";
11146
- echarts.use([
11147
- LineChart,
11148
- BarChart,
11149
- PieChart,
11150
- ScatterChart,
11151
- RadarChart,
11152
- FunnelChart,
11153
- GaugeChart,
11154
- HeatmapChart,
11155
- TreeChart,
11156
- SankeyChart,
11157
- MapChart,
11158
- GridComponent,
11159
- TooltipComponent,
11160
- LegendComponent,
11161
- TitleComponent,
11162
- VisualMapComponent,
11163
- DataZoomComponent,
11164
- GeoComponent,
11165
- RadarComponent,
11166
- CanvasRenderer
11167
- ]);
11168
-
11169
- // src/components/chart/EChartBase/index.tsx
11170
- import { jsx as jsx137 } from "react/jsx-runtime";
11171
- function EChartBase({
11172
- option,
11173
- height = 256,
11174
- onSelect,
11175
- className,
11176
- style,
11177
- slot
11178
- }) {
11179
- const hostRef = useRef14(null);
11180
- const chartRef = useRef14(null);
11181
- const onSelectRef = useRef14(onSelect);
11182
- onSelectRef.current = onSelect;
11183
- const [failed, setFailed] = useState47(false);
11184
- useEffect40(() => {
11185
- const host = hostRef.current;
11186
- if (!host) return;
11187
- let chart = null;
11188
- try {
11189
- chart = echarts.init(host);
11190
- } catch {
11191
- setFailed(true);
11192
- return;
11193
- }
11194
- chartRef.current = chart;
11195
- chart.on("click", (params) => {
11196
- onSelectRef.current?.({
11197
- name: params.name,
11198
- value: params.value,
11199
- seriesName: params.seriesName,
11200
- dataIndex: params.dataIndex
11201
- });
11202
- });
11203
- const observer2 = new ResizeObserver(() => chart?.resize());
11204
- observer2.observe(host);
11205
- return () => {
11206
- observer2.disconnect();
11207
- chart?.dispose();
11208
- chartRef.current = null;
11209
- };
11210
- }, []);
11211
- useEffect40(() => {
11212
- try {
11213
- chartRef.current?.setOption(option, { notMerge: true });
11214
- } catch {
11215
- }
11216
- }, [option]);
11217
- if (failed) {
11218
- return /* @__PURE__ */ jsx137(
11219
- "div",
11220
- {
11221
- "data-slot": "chart-fallback",
11222
- className: cn(
11223
- "flex h-32 w-full items-center justify-center rounded-md border border-dashed text-sm text-muted-foreground",
11224
- className
11225
- ),
11226
- style,
11227
- children: "\u5F53\u524D\u73AF\u5883\u4E0D\u652F\u6301\u56FE\u8868\u6E32\u67D3"
11228
- }
11229
- );
11230
- }
11231
- return /* @__PURE__ */ jsx137(
11232
- "div",
11233
- {
11234
- ref: hostRef,
11235
- "data-slot": slot,
11236
- className: cn("w-full", className),
11237
- style: { height: typeof height === "number" ? `${height}px` : height, ...style }
11238
- }
11239
- );
11240
- }
11241
-
11242
- // src/components/chart/lib/option-builders.ts
11243
- function pickColor(theme, colors, index) {
11244
- if (colors && colors.length > 0) return colors[index % colors.length];
11245
- return theme.palette[index % theme.palette.length];
11246
- }
11247
- function toNumber(value) {
11248
- const n = Number(value);
11249
- return Number.isFinite(n) ? n : 0;
11250
- }
11251
- function axisLabel(theme) {
11252
- return { color: theme.muted };
11253
- }
11254
- function grid() {
11255
- return { left: 8, right: 16, top: 40, bottom: 4, containLabel: true };
11256
- }
11257
- function axisTooltip(theme) {
11258
- return {
11259
- trigger: "axis",
11260
- backgroundColor: theme.card,
11261
- borderColor: theme.border,
11262
- textStyle: { color: theme.foreground }
11263
- };
11264
- }
11265
- function itemTooltip(theme) {
11266
- return {
11267
- trigger: "item",
11268
- backgroundColor: theme.card,
11269
- borderColor: theme.border,
11270
- textStyle: { color: theme.foreground }
11271
- };
11272
- }
11273
- function titleOption(title) {
11274
- return title ? { text: title, left: 8, top: 4 } : void 0;
11275
- }
11276
- function legendOption(theme, show) {
11277
- return show ? { top: 4, textStyle: { color: theme.muted } } : void 0;
11278
- }
11279
- function categorySeries(input, theme, kind, area) {
11280
- const rows = input.data;
11281
- const keys = input.yKeys && input.yKeys.length > 0 ? input.yKeys : ["value"];
11282
- const series = keys.map((key, index) => ({
11283
- type: kind,
11284
- name: key,
11285
- data: rows.map((row) => toNumber(row[key])),
11286
- ...kind === "line" ? { smooth: input.smooth ?? false, symbolSize: 6 } : {},
11287
- ...area ? { areaStyle: { opacity: input.fillOpacity ?? 0.25 } } : {},
11288
- ...input.stacked ? { stack: "total" } : {},
11289
- ...kind === "line" ? { emphasis: { focus: "series" } } : {},
11290
- itemStyle: { color: pickColor(theme, input.colors, index) },
11291
- ...kind === "line" ? { lineStyle: { color: pickColor(theme, input.colors, index) } } : {},
11292
- ...area ? { lineStyle: { color: pickColor(theme, input.colors, index) } } : {}
11293
- }));
11294
- const legend = legendOption(theme, keys.length > 1);
11295
- return {
11296
- color: input.colors && input.colors.length > 0 ? input.colors : theme.palette,
11297
- title: titleOption(input.title),
11298
- grid: grid(),
11299
- tooltip: axisTooltip(theme),
11300
- ...legend ? { legend } : {},
11301
- xAxis: { type: "category", data: rows.map((row) => String(input.xKey ? row[input.xKey] : "")), axisLabel: axisLabel(theme), axisLine: { lineStyle: { color: theme.border } } },
11302
- yAxis: { type: "value", axisLabel: axisLabel(theme), splitLine: { lineStyle: { color: theme.border } } },
11303
- series
11304
- };
11305
- }
11306
- function buildLineOption(input, theme) {
11307
- return categorySeries(input, theme, "line", input.showArea ?? false);
11308
- }
11309
- function buildColumnOption(input, theme) {
11310
- return categorySeries(input, theme, "bar", false);
11311
- }
11312
- function buildBarOption(input, theme) {
11313
- const rows = input.data;
11314
- const keys = input.yKeys && input.yKeys.length > 0 ? input.yKeys : ["value"];
11315
- const series = keys.map((key, index) => ({
11316
- type: "bar",
11317
- name: key,
11318
- data: rows.map((row) => toNumber(row[key])),
11319
- ...input.stacked ? { stack: "total" } : {},
11320
- itemStyle: { color: pickColor(theme, input.colors, index) }
11321
- }));
11322
- const legend = legendOption(theme, keys.length > 1);
11323
- return {
11324
- color: input.colors && input.colors.length > 0 ? input.colors : theme.palette,
11325
- title: titleOption(input.title),
11326
- grid: grid(),
11327
- tooltip: axisTooltip(theme),
11328
- ...legend ? { legend } : {},
11329
- xAxis: { type: "value", axisLabel: axisLabel(theme), splitLine: { lineStyle: { color: theme.border } } },
11330
- yAxis: { type: "category", data: rows.map((row) => String(input.yKey ? row[input.yKey] : "")), axisLabel: axisLabel(theme), axisLine: { lineStyle: { color: theme.border } } },
11331
- series
11332
- };
11333
- }
11334
- function buildAreaOption(input, theme) {
11335
- const withArea = { ...input, showArea: true };
11336
- return categorySeries(withArea, theme, "line", true);
11337
- }
11338
- function pieOption(input, theme, radius, extra) {
11339
- const nameKey = input.nameKey ?? "name";
11340
- const valueKey = input.valueKey ?? "value";
11341
- return {
11342
- color: input.colors && input.colors.length > 0 ? input.colors : theme.palette,
11343
- title: titleOption(input.title),
11344
- tooltip: itemTooltip(theme),
11345
- legend: { bottom: 0, textStyle: { color: theme.muted } },
11346
- series: [
11347
- {
11348
- type: "pie",
11349
- radius,
11350
- center: ["50%", "52%"],
11351
- data: input.data.map((row) => ({ name: String(row[nameKey] ?? ""), value: toNumber(row[valueKey]) })),
11352
- label: { show: input.showLabel ?? false, color: theme.muted },
11353
- emphasis: { scaleSize: 6 }
11354
- }
11355
- ],
11356
- ...extra
11357
- };
11358
- }
11359
- function buildPieOption(input, theme) {
11360
- return pieOption(input, theme, [0, "72%"], {});
11361
- }
11362
- function buildDonutOption(input, theme) {
11363
- const inner = input.innerRadius ?? "48%";
11364
- const outer = input.outerRadius ?? "72%";
11365
- const extra = {};
11366
- if (input.centerValue !== void 0 || input.centerLabel !== void 0) {
11367
- extra.title = {
11368
- text: input.centerValue !== void 0 ? String(input.centerValue) : "",
11369
- subtext: input.centerLabel ?? "",
11370
- left: "center",
11371
- top: "40%",
11372
- textStyle: { color: theme.foreground, fontSize: 22 },
11373
- subtextStyle: { color: theme.muted }
11374
- };
11375
- }
11376
- return pieOption(input, theme, [inner, outer], extra);
11377
- }
11378
- function scatterSeries(input, theme, extra) {
11379
- const xKey = input.xKey ?? "x";
11380
- const yKey = input.yKey ?? "y";
11381
- const groups = /* @__PURE__ */ new Map();
11382
- if (input.seriesKey) {
11383
- for (const row of input.data) {
11384
- const g = String(row[input.seriesKey] ?? "");
11385
- if (!groups.has(g)) groups.set(g, []);
11386
- groups.get(g).push(row);
11387
- }
11388
- } else {
11389
- groups.set("", input.data);
11390
- }
11391
- const names = [...groups.keys()];
11392
- const series = names.map((name, index) => ({
11393
- type: "scatter",
11394
- name: name || void 0,
11395
- data: groups.get(name).map(
11396
- (row) => extra?.datum ? extra.datum(row) : [toNumber(row[xKey]), toNumber(row[yKey])]
11397
- ),
11398
- symbolSize: extra?.symbolSize ?? 10,
11399
- itemStyle: { color: pickColor(theme, input.colors, index) }
11400
- }));
11401
- const legend = legendOption(theme, input.seriesKey !== void 0 && names.length > 1);
11402
- return {
11403
- color: input.colors && input.colors.length > 0 ? input.colors : theme.palette,
11404
- title: titleOption(input.title),
11405
- grid: grid(),
11406
- tooltip: axisTooltip(theme),
11407
- ...legend ? { legend } : {},
11408
- xAxis: { type: "value", axisLabel: axisLabel(theme), splitLine: { lineStyle: { color: theme.border } } },
11409
- yAxis: { type: "value", axisLabel: axisLabel(theme), splitLine: { lineStyle: { color: theme.border } } },
11410
- series
11411
- };
11412
- }
11413
- function buildScatterOption(input, theme) {
11414
- return scatterSeries(input, theme);
11415
- }
11416
- function buildBubbleOption(input, theme) {
11417
- const sizeKey = input.sizeKey ?? "size";
11418
- const sizes = input.data.map((row) => toNumber(row[sizeKey]));
11419
- const min = Math.min(...sizes, 0);
11420
- const max = Math.max(...sizes, 1);
11421
- const span = max - min || 1;
11422
- return scatterSeries(input, theme, {
11423
- datum: (row) => [toNumber(row[input.xKey ?? "x"]), toNumber(row[input.yKey ?? "y"]), toNumber(row[sizeKey])],
11424
- symbolSize: (datum) => {
11425
- const [x, y, size] = datum;
11426
- void x;
11427
- void y;
11428
- return 12 + (toNumber(size) - min) / span * 36;
11429
- }
11430
- });
11431
- }
11432
- function buildRadarOption(input, theme) {
11433
- const indicatorKey = input.indicatorKey ?? "indicator";
11434
- const keys = input.yKeys && input.yKeys.length > 0 ? input.yKeys : ["value"];
11435
- return {
11436
- color: input.colors && input.colors.length > 0 ? input.colors : theme.palette,
11437
- title: titleOption(input.title),
11438
- tooltip: itemTooltip(theme),
11439
- ...keys.length > 1 ? { legend: legendOption(theme, true) } : {},
11440
- radar: {
11441
- indicator: input.data.map((row) => ({ name: String(row[indicatorKey] ?? ""), max: 100 })),
11442
- axisName: { color: theme.muted },
11443
- splitLine: { lineStyle: { color: theme.border } },
11444
- splitArea: { areaStyle: { color: [] } }
11445
- },
11446
- series: [
11447
- {
11448
- type: "radar",
11449
- data: keys.map((key) => ({
11450
- name: key,
11451
- value: input.data.map((row) => toNumber(row[key]))
11452
- }))
11453
- }
11454
- ]
11455
- };
11456
- }
11457
- function buildFunnelOption(input, theme) {
11458
- const nameKey = input.nameKey ?? "name";
11459
- const valueKey = input.valueKey ?? "value";
11460
- return {
11461
- color: input.colors && input.colors.length > 0 ? input.colors : theme.palette,
11462
- title: titleOption(input.title),
11463
- tooltip: itemTooltip(theme),
11464
- series: [
11465
- {
11466
- type: "funnel",
11467
- left: "12%",
11468
- width: "76%",
11469
- sort: input.sort ?? "descending",
11470
- gap: 2,
11471
- label: { show: true, position: "inside", color: theme.card },
11472
- data: input.data.map((row) => ({ name: String(row[nameKey] ?? ""), value: toNumber(row[valueKey]) }))
11473
- }
11474
- ]
11475
- };
11476
- }
11477
- function buildGaugeOption(input, theme) {
11478
- const min = input.min ?? 0;
11479
- const max = input.max ?? 100;
11480
- const span = max - min || 1;
11481
- const defaultThresholds = [{ value: max, color: theme.palette[0] }];
11482
- const stops = (input.thresholds && input.thresholds.length > 0 ? input.thresholds : defaultThresholds).slice().sort((a, b) => a.value - b.value).map((t, index, arr) => {
11483
- const upper = index < arr.length - 1 ? t.value : max;
11484
- return [(upper - min) / span, t.color ?? theme.palette[0]];
11485
- });
11486
- return {
11487
- title: titleOption(input.title),
11488
- series: [
11489
- {
11490
- type: "gauge",
11491
- min,
11492
- max,
11493
- startAngle: 210,
11494
- endAngle: -30,
11495
- progress: { show: true, width: 10 },
11496
- axisLine: { lineStyle: { width: 10, color: stops } },
11497
- axisTick: { show: false },
11498
- splitLine: { show: false },
11499
- axisLabel: { show: false },
11500
- pointer: { show: false },
11501
- anchor: { show: false },
11502
- title: { show: false },
11503
- detail: {
11504
- valueAnimation: true,
11505
- offsetCenter: [0, 0],
11506
- fontSize: 26,
11507
- color: theme.foreground,
11508
- formatter: (value) => `${value}${input.unit ?? ""}`
11509
- },
11510
- data: [{ value: toNumber(input.value) }]
11511
- }
11512
- ]
11513
- };
11514
- }
11515
- function valueRange(values) {
11516
- if (values.length === 0) return { min: 0, max: 1 };
11517
- const min = Math.min(...values);
11518
- const max = Math.max(...values);
11519
- return max === min ? { min, max: min + 1 } : { min, max };
11520
- }
11521
- function buildHeatmapOption(input, theme) {
11522
- const xKey = input.xKey ?? "x";
11523
- const yKey = input.yKey ?? "y";
11524
- const valueKey = input.valueKey ?? "value";
11525
- const xs = [...new Set(input.data.map((row) => String(row[xKey])))];
11526
- const ys = [...new Set(input.data.map((row) => String(row[yKey])))];
11527
- const range = valueRange(input.data.map((row) => toNumber(row[valueKey])));
11528
- return {
11529
- title: titleOption(input.title),
11530
- grid: grid(),
11531
- tooltip: itemTooltip(theme),
11532
- xAxis: { type: "category", data: xs, axisLabel: axisLabel(theme), splitArea: { show: true } },
11533
- yAxis: { type: "category", data: ys, axisLabel: axisLabel(theme), splitArea: { show: true } },
11534
- visualMap: {
11535
- min: range.min,
11536
- max: range.max,
11537
- calculable: true,
11538
- orient: "horizontal",
11539
- left: "center",
11540
- bottom: 0,
11541
- textStyle: { color: theme.muted },
11542
- inRange: { color: [theme.palette[2] ?? theme.palette[0], theme.palette[0]] }
11543
- },
11544
- series: [
11545
- {
11546
- type: "heatmap",
11547
- data: input.data.map((row) => [String(row[xKey]), String(row[yKey]), toNumber(row[valueKey])]),
11548
- label: { show: false }
11549
- }
11550
- ]
11551
- };
11552
- }
11553
- function buildTreeOption(input, theme) {
11554
- const horizontal = input.orient === "horizontal";
11555
- return {
11556
- title: titleOption(input.title),
11557
- tooltip: itemTooltip(theme),
11558
- series: [
11559
- {
11560
- type: "tree",
11561
- data: input.data,
11562
- layout: "orthogonal",
11563
- orient: horizontal ? "LR" : "TB",
11564
- edgeShape: input.edgeShape ?? "curve",
11565
- roam: false,
11566
- expandAndCollapse: true,
11567
- initialTreeDepth: input.initialTreeDepth ?? 2,
11568
- label: {
11569
- show: true,
11570
- position: horizontal ? "right" : "bottom",
11571
- color: theme.foreground,
11572
- fontSize: 12
11573
- },
11574
- itemStyle: { color: theme.palette[0], borderColor: theme.palette[0] },
11575
- lineStyle: { color: theme.border, width: 1.5, curveness: 0.5 },
11576
- leaves: {
11577
- itemStyle: { color: theme.palette[1], borderColor: theme.palette[1] }
11578
- },
11579
- emphasis: { focus: "relative" }
11580
- }
11581
- ]
11582
- };
11583
- }
11584
- function buildSankeyOption(input, theme) {
11585
- return {
11586
- color: theme.palette,
11587
- title: titleOption(input.title),
11588
- tooltip: itemTooltip(theme),
11589
- series: [
11590
- {
11591
- type: "sankey",
11592
- left: 24,
11593
- right: 64,
11594
- top: 40,
11595
- bottom: 24,
11596
- nodeWidth: 16,
11597
- nodeGap: 12,
11598
- draggable: false,
11599
- label: { color: theme.muted },
11600
- lineStyle: { color: "gradient", curveness: 0.5 },
11601
- emphasis: { focus: "adjacency" },
11602
- data: input.nodes,
11603
- links: input.links
11604
- }
11605
- ]
11606
- };
11607
- }
11608
- function buildMapOption(input, theme) {
11609
- const nameKey = input.nameKey ?? "name";
11610
- const valueKey = input.valueKey ?? "value";
11611
- const range = valueRange(input.data.map((row) => toNumber(row[valueKey])));
11612
- return {
11613
- title: titleOption(input.title),
11614
- tooltip: itemTooltip(theme),
11615
- visualMap: {
11616
- min: range.min,
11617
- max: range.max,
11618
- left: 8,
11619
- bottom: 8,
11620
- textStyle: { color: theme.muted },
11621
- inRange: { color: [theme.palette[2] ?? theme.palette[0], theme.palette[0]] }
11622
- },
11623
- series: [
11624
- {
11625
- type: "map",
11626
- map: input.mapName,
11627
- roam: false,
11628
- label: { show: false },
11629
- emphasis: { label: { show: true, color: theme.foreground } },
11630
- itemStyle: { borderColor: theme.card, borderWidth: 1 },
11631
- data: input.data.map((row) => ({ name: String(row[nameKey] ?? ""), value: toNumber(row[valueKey]) }))
11632
- }
11633
- ]
11634
- };
11635
- }
11636
- function buildComposedOption(input, theme) {
11637
- const rows = input.data;
11638
- const hasSecondAxis = input.series.some((s) => s.yAxisIndex === 1);
11639
- const series = input.series.map((s, index) => {
11640
- const color = pickColor(theme, input.colors, index);
11641
- return {
11642
- type: s.type === "area" ? "line" : s.type,
11643
- name: s.name ?? s.dataKey,
11644
- data: rows.map((row) => toNumber(row[s.dataKey])),
11645
- ...s.type === "line" ? { smooth: false, symbolSize: 6 } : {},
11646
- ...s.type === "area" ? { areaStyle: { opacity: 0.25 } } : {},
11647
- yAxisIndex: s.yAxisIndex ?? 0,
11648
- itemStyle: { color },
11649
- ...s.type !== "bar" ? { lineStyle: { color } } : {}
11650
- };
11651
- });
11652
- return {
11653
- color: input.colors && input.colors.length > 0 ? input.colors : theme.palette,
11654
- title: titleOption(input.title),
11655
- grid: grid(),
11656
- tooltip: axisTooltip(theme),
11657
- legend: legendOption(theme, true),
11658
- xAxis: { type: "category", data: rows.map((row) => String(input.xKey ? row[input.xKey] : "")), axisLabel: axisLabel(theme), axisLine: { lineStyle: { color: theme.border } } },
11659
- yAxis: hasSecondAxis ? [
11660
- { type: "value", axisLabel: axisLabel(theme), splitLine: { lineStyle: { color: theme.border } } },
11661
- { type: "value", axisLabel: axisLabel(theme), splitLine: { show: false } }
11662
- ] : { type: "value", axisLabel: axisLabel(theme), splitLine: { lineStyle: { color: theme.border } } },
11663
- series
11664
- };
11665
- }
11666
-
11667
- // src/components/chart/lib/use-chart-theme.ts
11668
- import { useSyncExternalStore } from "react";
11669
-
11670
- // src/components/chart/lib/color.ts
11671
- var LPAREN = String.fromCharCode(40);
11672
- var RPAREN = String.fromCharCode(41);
11673
- var OKLCH_RE = new RegExp(
11674
- "^oklch" + String.fromCharCode(92) + LPAREN + "\\s*([\\d.]+%?)\\s+([-\\d.eE]+)\\s+([-\\d.eE]+)(?:\\s*\\/\\s*[\\d.]+%?)?\\s*" + String.fromCharCode(92) + RPAREN + "$",
11675
- "i"
11676
- );
11677
- var OKLAB_RE = new RegExp(
11678
- "^oklab" + String.fromCharCode(92) + LPAREN + "\\s*([\\d.]+%?)\\s+([-\\d.eE]+)\\s+([-\\d.eE]+)(?:\\s*\\/\\s*[\\d.]+%?)?\\s*" + String.fromCharCode(92) + RPAREN + "$",
11679
- "i"
11680
- );
11681
- function parseLightness(raw) {
11682
- const value = parseFloat(raw);
11683
- return raw.endsWith("%") ? value / 100 : value;
11684
- }
11685
- function oklabToLinearSrgbColor(L, a, b) {
11686
- const l_ = L + 0.3963377774 * a + 0.2158037573 * b;
11687
- const m_ = L - 0.1055613458 * a - 0.0638541728 * b;
11688
- const s_ = L - 0.0894841775 * a - 1.291485548 * b;
11689
- const l = l_ * l_ * l_;
11690
- const m = m_ * m_ * m_;
11691
- const s = s_ * s_ * s_;
11692
- return [
11693
- 4.0767416621 * l - 3.3077115913 * m + 0.2309699292 * s,
11694
- -1.2684380046 * l + 2.6097574011 * m - 0.3413193965 * s,
11695
- -0.0041960863 * l - 0.7034186147 * m + 1.707614701 * s
11696
- ];
11697
- }
11698
- function linearToSrgbChannel(c) {
11699
- const clamped = Math.min(1, Math.max(0, c));
11700
- const encoded = clamped <= 31308e-7 ? 12.92 * clamped : 1.055 * Math.pow(clamped, 1 / 2.4) - 0.055;
11701
- return Math.round(encoded * 255);
11702
- }
11703
- function formatSrgbString(r, g, b) {
11704
- return "rgb" + LPAREN + `${linearToSrgbChannel(r)}, ${linearToSrgbChannel(g)}, ${linearToSrgbChannel(b)}` + RPAREN;
11705
- }
11706
- function normalizeColor(value) {
11707
- const trimmed = value.trim();
11708
- const oklch = OKLCH_RE.exec(trimmed);
11709
- if (oklch) {
11710
- const L = parseLightness(oklch[1]);
11711
- const C = parseFloat(oklch[2]);
11712
- const h = parseFloat(oklch[3]) * Math.PI / 180;
11713
- const [r, g, b] = oklabToLinearSrgbColor(L, C * Math.cos(h), C * Math.sin(h));
11714
- return formatSrgbString(r, g, b);
11715
- }
11716
- const oklab = OKLAB_RE.exec(trimmed);
11717
- if (oklab) {
11718
- const L = parseLightness(oklab[1]);
11719
- const a = parseFloat(oklab[2]);
11720
- const b = parseFloat(oklab[3]);
11721
- const [r, g, bl] = oklabToLinearSrgbColor(L, a, b);
11722
- return formatSrgbString(r, g, bl);
11723
- }
11724
- return trimmed;
11725
- }
11726
-
11727
- // src/components/chart/lib/theme.ts
11728
- var FALLBACK_THEME = {
11729
- palette: ["steelblue", "seagreen", "darkorange", "slateblue", "tomato"],
11730
- foreground: "black",
11731
- muted: "gray",
11732
- border: "gainsboro",
11733
- card: "white",
11734
- success: "seagreen",
11735
- warning: "darkorange",
11736
- destructive: "tomato",
11737
- info: "steelblue"
11738
- };
11739
- var cached = null;
11740
- function getChartTheme() {
11741
- if (cached) return cached;
11742
- if (typeof document === "undefined") return FALLBACK_THEME;
11743
- try {
11744
- const style = getComputedStyle(document.documentElement);
11745
- const read = (name) => normalizeColor(style.getPropertyValue(name));
11746
- const readColor = (name, fallback) => read(name) || fallback;
11747
- const palette = [1, 2, 3, 4, 5].map(
11748
- (i, index) => readColor(`--chart-${i}`, FALLBACK_THEME.palette[index])
11749
- );
11750
- cached = {
11751
- palette,
11752
- foreground: readColor("--foreground", FALLBACK_THEME.foreground),
11753
- muted: readColor("--muted-foreground", FALLBACK_THEME.muted),
11754
- border: readColor("--border", FALLBACK_THEME.border),
11755
- card: readColor("--card", FALLBACK_THEME.card),
11756
- success: readColor("--success", FALLBACK_THEME.success),
11757
- warning: readColor("--warning", FALLBACK_THEME.warning),
11758
- destructive: readColor("--destructive", FALLBACK_THEME.destructive),
11759
- info: readColor("--info", FALLBACK_THEME.info)
11760
- };
11761
- return cached;
11762
- } catch {
11763
- return FALLBACK_THEME;
11764
- }
11765
- }
11766
- function resetChartTheme() {
11767
- cached = null;
11768
- }
11769
- var CHART_SKIN_CHANGE_EVENT = "pactor:skin-change";
11770
- var listeners = /* @__PURE__ */ new Set();
11771
- var observer = null;
11772
- var scheduled = false;
11773
- function scheduleNotify() {
11774
- if (scheduled) return;
11775
- scheduled = true;
11776
- const enqueue = typeof requestAnimationFrame === "function" ? requestAnimationFrame : (cb) => setTimeout(cb, 0);
11777
- enqueue(() => {
11778
- scheduled = false;
11779
- resetChartTheme();
11780
- for (const listener of [...listeners]) {
11781
- listener();
11782
- }
11783
- });
11784
- }
11785
- function handleResourceLoad(event) {
11786
- const target = event.target;
11787
- if (target instanceof HTMLLinkElement || target instanceof HTMLStyleElement) {
11788
- scheduleNotify();
11789
- }
11790
- }
11791
- function subscribeChartTheme(listener) {
11792
- if (typeof document === "undefined" || typeof MutationObserver === "undefined") {
11793
- return () => {
11794
- };
11795
- }
11796
- listeners.add(listener);
11797
- if (!observer) {
11798
- observer = new MutationObserver(scheduleNotify);
11799
- observer.observe(document.documentElement, { attributes: true });
11800
- observer.observe(document.head, {
11801
- attributes: true,
11802
- childList: true,
11803
- characterData: true,
11804
- subtree: true
11805
- });
11806
- document.addEventListener("load", handleResourceLoad, true);
11807
- window.addEventListener(CHART_SKIN_CHANGE_EVENT, scheduleNotify);
11808
- }
11809
- return () => {
11810
- listeners.delete(listener);
11811
- if (listeners.size === 0 && observer) {
11812
- observer.disconnect();
11813
- observer = null;
11814
- document.removeEventListener("load", handleResourceLoad, true);
11815
- window.removeEventListener(CHART_SKIN_CHANGE_EVENT, scheduleNotify);
11816
- }
11817
- };
11818
- }
11819
-
11820
- // src/components/chart/lib/use-chart-theme.ts
11821
- function useChartTheme() {
11822
- return useSyncExternalStore(subscribeChartTheme, getChartTheme, getChartTheme);
11823
- }
11824
-
11825
- // src/components/chart/AreaChart/index.tsx
11826
- import { jsx as jsx138 } from "react/jsx-runtime";
11827
- function AreaChart({
11828
- data = [],
11829
- xKey,
11830
- yKeys,
11831
- stacked = false,
11832
- fillOpacity,
11833
- colors,
11834
- title,
11835
- height,
11836
- onSelect,
11837
- className,
11838
- style
11839
- }) {
11840
- const option = buildAreaOption(
11841
- { data, xKey, yKeys, stacked, fillOpacity, colors, title },
11842
- useChartTheme()
11843
- );
11844
- return /* @__PURE__ */ jsx138(
11845
- EChartBase,
11846
- {
11847
- slot: "area-chart",
11848
- option,
11849
- height,
11850
- onSelect,
11851
- className,
11852
- style
11853
- }
11854
- );
11855
- }
11856
-
11857
- // src/components/chart/BarChart/index.tsx
11858
- import { jsx as jsx139 } from "react/jsx-runtime";
11859
- function BarChart2({
11860
- data = [],
11861
- yKey,
11862
- xKeys,
11863
- stacked = false,
11864
- colors,
11865
- title,
11866
- height,
11867
- onSelect,
11868
- className,
11869
- style
11870
- }) {
11871
- const option = buildBarOption(
11872
- { data, yKey, yKeys: xKeys, stacked, colors, title },
11873
- useChartTheme()
11874
- );
11875
- return /* @__PURE__ */ jsx139(
11876
- EChartBase,
11877
- {
11878
- slot: "bar-chart",
11879
- option,
11880
- height,
11881
- onSelect,
11882
- className,
11883
- style
11884
- }
11885
- );
11886
- }
11887
-
11888
- // src/components/chart/BubbleChart/index.tsx
11889
- import { jsx as jsx140 } from "react/jsx-runtime";
11890
- function BubbleChart({
11891
- data = [],
11892
- xKey,
11893
- yKey,
11894
- sizeKey,
11895
- seriesKey,
11896
- colors,
11897
- title,
11898
- height,
11899
- onSelect,
11900
- className,
11901
- style
11902
- }) {
11903
- const option = buildBubbleOption(
11904
- { data, xKey, yKey, sizeKey, seriesKey, colors, title },
11905
- useChartTheme()
11906
- );
11907
- return /* @__PURE__ */ jsx140(
11908
- EChartBase,
11909
- {
11910
- slot: "bubble-chart",
11911
- option,
11912
- height,
11913
- onSelect,
11914
- className,
11915
- style
11916
- }
11917
- );
11918
- }
11919
-
11920
- // src/components/chart/ColumnChart/index.tsx
11921
- import { jsx as jsx141 } from "react/jsx-runtime";
11922
- function ColumnChart({
11923
- data = [],
11924
- xKey,
11925
- yKeys,
11926
- stacked = false,
11927
- colors,
11928
- title,
11929
- height,
11930
- onSelect,
11931
- className,
11932
- style
11933
- }) {
11934
- const option = buildColumnOption(
11935
- { data, xKey, yKeys, stacked, colors, title },
11936
- useChartTheme()
11937
- );
11938
- return /* @__PURE__ */ jsx141(
11939
- EChartBase,
11940
- {
11941
- slot: "column-chart",
11942
- option,
11943
- height,
11944
- onSelect,
11945
- className,
11946
- style
11947
- }
11948
- );
11949
- }
11950
-
11951
- // src/components/chart/ComposedChart/index.tsx
11952
- import { jsx as jsx142 } from "react/jsx-runtime";
11953
- function ComposedChart({
11954
- data = [],
11955
- xKey,
11956
- series = [],
11957
- colors,
11958
- title,
11959
- height,
11960
- onSelect,
11961
- className,
11962
- style
11963
- }) {
11964
- const option = buildComposedOption(
11965
- { data, xKey, series, colors, title },
11966
- useChartTheme()
11967
- );
11968
- return /* @__PURE__ */ jsx142(
11969
- EChartBase,
11970
- {
11971
- slot: "composed-chart",
11972
- option,
11973
- height,
11974
- onSelect,
11975
- className,
11976
- style
11977
- }
11978
- );
11979
- }
11980
-
11981
- // src/components/chart/DonutChart/index.tsx
11982
- import { jsx as jsx143 } from "react/jsx-runtime";
11983
- function DonutChart({
11984
- data = [],
11985
- nameKey,
11986
- valueKey,
11987
- innerRadius,
11988
- outerRadius,
11989
- centerLabel,
11990
- centerValue,
11991
- showLabel,
11992
- colors,
11993
- title,
11994
- height,
11995
- onSelect,
11996
- className,
11997
- style
11998
- }) {
11999
- const option = buildDonutOption(
12000
- {
12001
- data,
12002
- nameKey,
12003
- valueKey,
12004
- innerRadius,
12005
- outerRadius,
12006
- centerLabel,
12007
- centerValue,
12008
- showLabel,
12009
- colors,
12010
- title
12011
- },
12012
- useChartTheme()
12013
- );
12014
- return /* @__PURE__ */ jsx143(
12015
- EChartBase,
12016
- {
12017
- slot: "donut-chart",
12018
- option,
12019
- height,
12020
- onSelect,
12021
- className,
12022
- style
12023
- }
12024
- );
12025
- }
12026
-
12027
- // src/components/chart/FunnelChart/index.tsx
12028
- import { jsx as jsx144 } from "react/jsx-runtime";
12029
- function FunnelChart2({
12030
- data = [],
12031
- nameKey,
12032
- valueKey,
12033
- sort,
12034
- colors,
12035
- title,
12036
- height,
12037
- onSelect,
12038
- className,
12039
- style
12040
- }) {
12041
- const option = buildFunnelOption(
12042
- { data, nameKey, valueKey, sort, colors, title },
12043
- useChartTheme()
12044
- );
12045
- return /* @__PURE__ */ jsx144(
12046
- EChartBase,
12047
- {
12048
- slot: "funnel-chart",
12049
- option,
12050
- height,
12051
- onSelect,
12052
- className,
12053
- style
12054
- }
12055
- );
12056
- }
12057
-
12058
- // src/components/chart/GaugeChart/index.tsx
12059
- import { jsx as jsx145 } from "react/jsx-runtime";
12060
- function GaugeChart2({
12061
- value = 0,
12062
- min,
12063
- max,
12064
- thresholds,
12065
- unit,
12066
- title,
12067
- height,
12068
- onSelect,
12069
- className,
12070
- style
12071
- }) {
12072
- const option = buildGaugeOption(
12073
- { value, min, max, thresholds, unit, title },
12074
- useChartTheme()
12075
- );
12076
- return /* @__PURE__ */ jsx145(
12077
- EChartBase,
12078
- {
12079
- slot: "gauge-chart",
12080
- option,
12081
- height,
12082
- onSelect,
12083
- className,
12084
- style
12085
- }
12086
- );
12087
- }
12088
-
12089
- // src/components/chart/Heatmap/index.tsx
12090
- import { jsx as jsx146 } from "react/jsx-runtime";
12091
- function Heatmap({
12092
- data = [],
12093
- xKey,
12094
- yKey,
12095
- valueKey,
12096
- title,
12097
- height,
12098
- onSelect,
12099
- className,
12100
- style
12101
- }) {
12102
- const option = buildHeatmapOption(
12103
- { data, xKey, yKey, valueKey, title },
12104
- useChartTheme()
12105
- );
12106
- return /* @__PURE__ */ jsx146(
12107
- EChartBase,
12108
- {
12109
- slot: "heatmap",
12110
- option,
12111
- height,
12112
- onSelect,
12113
- className,
12114
- style
12115
- }
12116
- );
12117
- }
12118
-
12119
- // src/components/chart/LineChart/index.tsx
12120
- import { jsx as jsx147 } from "react/jsx-runtime";
12121
- function LineChart2({
12122
- data = [],
12123
- xKey,
12124
- yKeys,
12125
- smooth = false,
12126
- showArea = false,
12127
- stacked = false,
12128
- colors,
12129
- title,
12130
- height,
12131
- onSelect,
12132
- className,
12133
- style
12134
- }) {
12135
- const option = buildLineOption(
12136
- { data, xKey, yKeys, smooth, showArea, stacked, colors, title },
12137
- useChartTheme()
12138
- );
12139
- return /* @__PURE__ */ jsx147(
12140
- EChartBase,
12141
- {
12142
- slot: "line-chart",
12143
- option,
12144
- height,
12145
- onSelect,
12146
- className,
12147
- style
12148
- }
12149
- );
12150
- }
12151
-
12152
- // src/components/chart/MapChart/index.tsx
12153
- import { useEffect as useEffect41, useState as useState48 } from "react";
12154
- import { jsx as jsx148 } from "react/jsx-runtime";
12155
- var fetchedMaps = /* @__PURE__ */ new Set();
12156
- function hasRegisteredMap(mapName) {
12157
- try {
12158
- return typeof echarts.getMap === "function" && echarts.getMap(mapName) != null;
12159
- } catch {
12160
- return false;
12161
- }
12162
- }
12163
- function MapChart2({
12164
- mapName = "map",
12165
- data = [],
12166
- nameKey,
12167
- valueKey,
12168
- geoJson,
12169
- geoJsonUrl,
12170
- title,
12171
- height,
12172
- onSelect,
12173
- className,
12174
- style
12175
- }) {
12176
- const theme = useChartTheme();
12177
- const [loadState, setLoadState] = useState48(
12178
- () => geoJson || !geoJsonUrl || hasRegisteredMap(mapName) ? "idle" : "loading"
12179
- );
12180
- if (geoJson) {
12181
- try {
12182
- echarts.registerMap(mapName, geoJson);
12183
- } catch {
12184
- }
12185
- }
12186
- useEffect41(() => {
12187
- if (geoJson || !geoJsonUrl || hasRegisteredMap(mapName) || fetchedMaps.has(mapName)) {
12188
- setLoadState("idle");
12189
- return;
12190
- }
12191
- let cancelled = false;
12192
- setLoadState("loading");
12193
- fetch(geoJsonUrl).then((res) => {
12194
- if (!res.ok) throw new Error(`HTTP ${res.status}`);
12195
- return res.json();
12196
- }).then((json) => {
12197
- if (cancelled) return;
12198
- echarts.registerMap(mapName, json);
12199
- fetchedMaps.add(mapName);
12200
- setLoadState("ready");
12201
- }).catch(() => {
12202
- if (cancelled) return;
12203
- console.warn(`[MapChart] \u5730\u56FE\u6570\u636E\u52A0\u8F7D\u5931\u8D25\uFF1A${geoJsonUrl}`);
12204
- setLoadState("error");
12205
- });
12206
- return () => {
12207
- cancelled = true;
12208
- };
12209
- }, [geoJson, geoJsonUrl, mapName]);
12210
- if (geoJsonUrl && !geoJson && loadState === "loading") {
12211
- return /* @__PURE__ */ jsx148(
12212
- "div",
12213
- {
12214
- "data-slot": "map-chart-loading",
12215
- className,
12216
- style: { height: typeof height === "number" ? `${height}px` : height ?? 256, ...style }
12217
- }
12218
- );
12219
- }
12220
- if (!geoJson && loadState === "error") {
12221
- return /* @__PURE__ */ jsx148(
12222
- Empty2,
12223
- {
12224
- title: "\u5730\u56FE\u52A0\u8F7D\u5931\u8D25",
12225
- description: "\u8BF7\u68C0\u67E5 geoJsonUrl \u7F51\u7EDC\u53EF\u8FBE\u6027\uFF0C\u6216\u6539\u7528 geoJson / \u9884\u6CE8\u518C\u5730\u56FE",
12226
- className,
12227
- style
12228
- }
12229
- );
12230
- }
12231
- if (!geoJson && !hasRegisteredMap(mapName)) {
12232
- 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`);
12233
- return /* @__PURE__ */ jsx148(
12234
- Empty2,
12235
- {
12236
- title: "\u5730\u56FE\u672A\u6CE8\u518C",
12237
- description: "\u8BF7\u4F20\u5165 geoJson / geoJsonUrl\uFF0C\u6216\u5728\u5E94\u7528\u542F\u52A8\u65F6\u6CE8\u518C\u5730\u56FE\u6570\u636E",
12238
- className,
12239
- style
12240
- }
12241
- );
12242
- }
12243
- const option = buildMapOption({ mapName, data, nameKey, valueKey, title }, theme);
12244
- return /* @__PURE__ */ jsx148(
12245
- EChartBase,
12246
- {
12247
- slot: "map-chart",
12248
- option,
12249
- height,
12250
- onSelect,
12251
- className,
12252
- style
12253
- }
12254
- );
12255
- }
12256
-
12257
- // src/components/chart/MetricCard/index.tsx
12258
- import { ArrowDownRight, ArrowUpRight } from "lucide-react";
12259
- import { jsx as jsx149, jsxs as jsxs96 } from "react/jsx-runtime";
12260
- function MetricCard({
12261
- title,
12262
- value,
12263
- prefix,
12264
- suffix,
12265
- trend,
12266
- trendLabel,
12267
- className,
12268
- style
12269
- }) {
12270
- const showTrend = typeof trend === "number" && trend !== 0;
12271
- const rising = (trend ?? 0) > 0;
12272
- return /* @__PURE__ */ jsxs96(
12273
- "div",
12274
- {
12275
- "data-slot": "metric-card",
12276
- className: cn("rounded-lg border bg-card p-4 text-card-foreground shadow-sm", className),
12277
- style,
12278
- children: [
12279
- title ? /* @__PURE__ */ jsx149("div", { "data-slot": "metric-card-title", className: "text-sm text-muted-foreground", children: title }) : null,
12280
- /* @__PURE__ */ jsxs96(
12281
- "div",
12282
- {
12283
- "data-slot": "metric-card-value",
12284
- className: "mt-1 text-2xl font-semibold tracking-tight tabular-nums",
12285
- children: [
12286
- prefix,
12287
- value,
12288
- suffix
12289
- ]
12290
- }
12291
- ),
12292
- showTrend ? /* @__PURE__ */ jsxs96(
12293
- "div",
12294
- {
12295
- "data-slot": "metric-card-trend",
12296
- "data-trend": rising ? "up" : "down",
12297
- className: cn(
12298
- "mt-1 inline-flex items-center gap-0.5 text-sm",
12299
- rising ? "text-success" : "text-destructive"
12300
- ),
12301
- children: [
12302
- rising ? /* @__PURE__ */ jsx149(ArrowUpRight, { className: "size-4" }) : /* @__PURE__ */ jsx149(ArrowDownRight, { className: "size-4" }),
12303
- Math.abs(trend ?? 0),
12304
- "%",
12305
- trendLabel ? /* @__PURE__ */ jsx149("span", { className: "ml-1 text-muted-foreground", children: trendLabel }) : null
12306
- ]
12307
- }
12308
- ) : null
12309
- ]
12310
- }
12311
- );
12312
- }
12313
-
12314
- // src/components/chart/PieChart/index.tsx
12315
- import { jsx as jsx150 } from "react/jsx-runtime";
12316
- function PieChart2({
12317
- data = [],
12318
- nameKey,
12319
- valueKey,
12320
- showLabel,
12321
- colors,
12322
- title,
12323
- height,
12324
- onSelect,
12325
- className,
12326
- style
12327
- }) {
12328
- const option = buildPieOption(
12329
- { data, nameKey, valueKey, showLabel, colors, title },
12330
- useChartTheme()
12331
- );
12332
- return /* @__PURE__ */ jsx150(
12333
- EChartBase,
12334
- {
12335
- slot: "pie-chart",
12336
- option,
12337
- height,
12338
- onSelect,
12339
- className,
12340
- style
12341
- }
12342
- );
12343
- }
12344
-
12345
- // src/components/chart/RadarChart/index.tsx
12346
- import { jsx as jsx151 } from "react/jsx-runtime";
12347
- function RadarChart2({
12348
- data = [],
12349
- indicatorKey,
12350
- yKeys,
12351
- colors,
12352
- title,
12353
- height,
12354
- onSelect,
12355
- className,
12356
- style
12357
- }) {
12358
- const option = buildRadarOption(
12359
- { data, indicatorKey, yKeys, colors, title },
12360
- useChartTheme()
12361
- );
12362
- return /* @__PURE__ */ jsx151(
12363
- EChartBase,
12364
- {
12365
- slot: "radar-chart",
12366
- option,
12367
- height,
12368
- onSelect,
12369
- className,
12370
- style
12371
- }
12372
- );
12373
- }
12374
-
12375
- // src/components/chart/SankeyChart/index.tsx
12376
- import { jsx as jsx152 } from "react/jsx-runtime";
12377
- function SankeyChart2({
12378
- nodes = [],
12379
- links = [],
12380
- title,
12381
- height,
12382
- onSelect,
12383
- className,
12384
- style
12385
- }) {
12386
- const option = buildSankeyOption({ nodes, links, title }, useChartTheme());
12387
- return /* @__PURE__ */ jsx152(
12388
- EChartBase,
12389
- {
12390
- slot: "sankey-chart",
12391
- option,
12392
- height,
12393
- onSelect,
12394
- className,
12395
- style
12396
- }
12397
- );
12398
- }
12399
-
12400
- // src/components/chart/ScatterChart/index.tsx
12401
- import { jsx as jsx153 } from "react/jsx-runtime";
12402
- function ScatterChart2({
12403
- data = [],
12404
- xKey,
12405
- yKey,
12406
- seriesKey,
12407
- colors,
12408
- title,
12409
- height,
12410
- onSelect,
12411
- className,
12412
- style
12413
- }) {
12414
- const option = buildScatterOption(
12415
- { data, xKey, yKey, seriesKey, colors, title },
12416
- useChartTheme()
12417
- );
12418
- return /* @__PURE__ */ jsx153(
12419
- EChartBase,
12420
- {
12421
- slot: "scatter-chart",
12422
- option,
12423
- height,
12424
- onSelect,
12425
- className,
12426
- style
12427
- }
12428
- );
12429
- }
12430
-
12431
- // src/components/chart/TreeChart/index.tsx
12432
- import { jsx as jsx154 } from "react/jsx-runtime";
12433
- function TreeChart2({
12434
- data = [],
12435
- title,
12436
- orient,
12437
- edgeShape,
12438
- initialTreeDepth,
12439
- height,
12440
- onSelect,
12441
- className,
12442
- style
12443
- }) {
12444
- const option = buildTreeOption(
12445
- { data, title, orient, edgeShape, initialTreeDepth },
12446
- useChartTheme()
12447
- );
12448
- return /* @__PURE__ */ jsx154(
12449
- EChartBase,
12450
- {
12451
- slot: "tree-chart",
12452
- option,
12453
- height,
12454
- onSelect,
12455
- className,
12456
- style
12457
- }
12458
- );
12459
- }
12460
-
12461
- // src/components/business/CardList/index.tsx
12462
- import { useI18n as useI18n34 } from "@pactor-app/runtime";
12463
-
12464
- // src/components/business/EntityCard/index.tsx
12465
- import { jsx as jsx155, jsxs as jsxs97 } from "react/jsx-runtime";
12466
- function EntityCard({
12467
- icon,
12468
- logo,
12469
- title,
12470
- subtitle,
12471
- tags,
12472
- description,
12473
- meta,
12474
- actions,
12475
- footerBackground = true,
12476
- hoverable,
12477
- className,
12478
- contentClassName,
12479
- style,
12480
- onClick,
12481
- onAction
12482
- }) {
12483
- return /* @__PURE__ */ jsx155(
12484
- Card2,
12485
- {
12486
- hoverable,
12487
- className,
12488
- contentClassName,
12489
- style,
12490
- onClick,
12491
- children: /* @__PURE__ */ jsxs97("div", { "data-slot": "entity-card", className: "flex flex-col gap-2", children: [
12492
- /* @__PURE__ */ jsxs97("div", { "data-slot": "entity-card-header", className: "flex items-center justify-between gap-2", children: [
12493
- /* @__PURE__ */ jsxs97("div", { className: "flex min-w-0 items-center gap-2", children: [
12494
- logo ? /* @__PURE__ */ jsx155("img", { src: logo, alt: title ?? "", className: "size-9 shrink-0 rounded-md" }) : icon ? /* @__PURE__ */ jsx155("span", { className: "flex size-9 shrink-0 items-center justify-center rounded-md bg-muted", children: /* @__PURE__ */ jsx155(Icon, { name: icon }) }) : null,
12495
- title || subtitle ? /* @__PURE__ */ jsxs97("span", { className: "flex min-w-0 flex-col gap-0.5", children: [
12496
- title ? /* @__PURE__ */ jsx155(Text, { text: title, strong: true, className: "truncate" }) : null,
12497
- subtitle ? /* @__PURE__ */ jsx155(Text, { text: subtitle, type: "secondary", size: "small" }) : null
12498
- ] }) : null
12499
- ] }),
12500
- tags?.length ? /* @__PURE__ */ jsx155("span", { className: "flex shrink-0 items-center gap-1", children: tags.map((tag, i) => /* @__PURE__ */ jsx155(Tag, { text: tag.text, color: tag.color }, `${tag.text}-${i}`)) }) : null
12501
- ] }),
12502
- description || meta ? /* @__PURE__ */ jsxs97("div", { "data-slot": "entity-card-body", className: "flex items-center justify-between gap-4", children: [
12503
- description ? /* @__PURE__ */ jsx155(
12504
- Text,
12505
- {
12506
- text: description,
12507
- type: "secondary",
12508
- size: "small",
12509
- className: "line-clamp-2",
12510
- style: { minHeight: 32 }
12511
- }
12512
- ) : null,
12513
- meta ? /* @__PURE__ */ jsx155(Text, { text: meta, type: "secondary", size: "small", className: "shrink-0" }) : null
12514
- ] }) : null,
12515
- actions?.length ? /* @__PURE__ */ jsx155(
12516
- "div",
12517
- {
12518
- "data-slot": "entity-card-footer",
12519
- className: cn(
12520
- "mt-1 flex items-center justify-end gap-1",
12521
- footerBackground && "-mx-4 -mb-4 rounded-b-lg bg-muted px-4 py-2"
12522
- ),
12523
- onClick: (event) => event.stopPropagation(),
12524
- children: actions.map((action, index) => /* @__PURE__ */ jsx155(
12525
- Button2,
12526
- {
12527
- text: action.text,
12528
- variant: action.variant,
12529
- danger: action.danger,
12530
- onClick: () => onAction?.({ key: action.key ?? action.text, text: action.text, index })
12531
- },
12532
- `${action.key ?? action.text}-${index}`
12533
- ))
12534
- }
12535
- ) : null
12536
- ] })
12537
- }
12538
- );
12539
- }
12540
-
12541
- // src/components/business/CardList/index.tsx
12542
- import { jsx as jsx156, jsxs as jsxs98 } from "react/jsx-runtime";
12543
- function CardList({
12544
- items,
12545
- columns = 3,
12546
- gap = 16,
12547
- createText,
12548
- createIcon = "plus",
12549
- hoverable,
12550
- showCount,
12551
- emptyText,
12552
- className,
12553
- style,
12554
- onItemClick,
12555
- onItemAction,
12556
- onCreateClick
12557
- }) {
12558
- const { t } = useI18n34();
12559
- const list = items ?? [];
12560
- return /* @__PURE__ */ jsxs98("div", { "data-slot": "card-list", className: cn("flex flex-col gap-3", className), style, children: [
12561
- list.length === 0 && !createText ? /* @__PURE__ */ jsx156(Empty2, { title: emptyText ?? t("pactor.table.empty") }) : /* @__PURE__ */ jsxs98(
12562
- "div",
12563
- {
12564
- "data-slot": "card-list-grid",
12565
- className: "grid",
12566
- style: { gridTemplateColumns: `repeat(${columns}, minmax(0, 1fr))`, gap },
12567
- children: [
12568
- createText ? /* @__PURE__ */ jsxs98(
12569
- Card2,
12570
- {
12571
- hoverable: true,
12572
- className: "h-full border-dashed",
12573
- contentClassName: "flex flex-1 flex-col items-center justify-center gap-2",
12574
- onClick: onCreateClick,
12575
- children: [
12576
- /* @__PURE__ */ jsx156(Icon, { name: createIcon, size: "lg", className: "text-muted-foreground" }),
12577
- /* @__PURE__ */ jsx156(Text, { text: createText, type: "secondary" })
12578
- ]
12579
- }
12580
- ) : null,
12581
- list.map((item, index) => /* @__PURE__ */ jsx156(
12582
- EntityCard,
12583
- {
12584
- icon: item.icon,
12585
- logo: item.logo,
12586
- title: item.title,
12587
- subtitle: item.subtitle,
12588
- tags: item.tags,
12589
- description: item.description,
12590
- meta: item.meta,
12591
- actions: item.actions,
12592
- hoverable,
12593
- onClick: onItemClick ? () => onItemClick({ item, index }) : void 0,
12594
- onAction: onItemAction ? ({ key, text }) => onItemAction({ key, text, item, index }) : void 0
12595
- },
12596
- index
12597
- ))
12598
- ]
12599
- }
12600
- ),
12601
- showCount ? /* @__PURE__ */ jsx156(
12602
- Text,
12603
- {
12604
- text: t("pactor.cardList.total", { count: list.length }),
12605
- type: "secondary",
12606
- size: "small"
12607
- }
12608
- ) : null
12609
- ] });
12610
- }
12611
-
12612
- // src/components/business/ExpressionEditor/index.tsx
12613
- import { useEffect as useEffect42, useMemo as useMemo10, useRef as useRef15, useState as useState50 } from "react";
12614
-
12615
- // src/components/business/FieldCondition/index.tsx
12616
- import { Plus, X as X5 } from "lucide-react";
12617
- import { useCallback as useCallback4, useMemo as useMemo9 } from "react";
12618
-
12619
- // src/components/business/SearchableSelect/index.tsx
12620
- import { Check, ChevronsUpDown, X as X4 } from "lucide-react";
12621
- import * as React from "react";
12622
- import { jsx as jsx157, jsxs as jsxs99 } from "react/jsx-runtime";
12623
- var DEFAULT_PLACEHOLDER = "\u8BF7\u9009\u62E9";
12624
- var DEFAULT_SEARCH_PLACEHOLDER = "\u641C\u7D22\u2026";
12625
- var DEFAULT_EMPTY_TEXT = "\u65E0\u5339\u914D\u7ED3\u679C";
12626
- var NO_OPTIONS_TEXT = "\u65E0\u9009\u9879";
12627
- function SearchableSelect({
12628
- value,
12629
- onChange,
12630
- options,
12631
- placeholder,
12632
- searchPlaceholder,
12633
- emptyText,
12634
- disabled = false,
12635
- allowClear = true,
12636
- size = "middle",
12637
- className,
12638
- id,
12639
- showSearch,
12640
- multiple = false,
12641
- fullOptionText = false,
12642
- listClassName
12643
- }) {
12644
- const resolvedPlaceholder = placeholder ?? DEFAULT_PLACEHOLDER;
12645
- const resolvedSearchPlaceholder = searchPlaceholder ?? DEFAULT_SEARCH_PLACEHOLDER;
12646
- const resolvedEmptyText = emptyText ?? DEFAULT_EMPTY_TEXT;
12647
- const [open, setOpen] = React.useState(false);
12648
- const shouldShowSearch = showSearch ?? options.length > 8;
12649
- const selectedValues = multiple ? Array.isArray(value) ? value : value ? [value] : [] : [];
12650
- const selectedLabel = React.useMemo(
12651
- () => !multiple ? options.find((opt) => opt.value === value)?.label : void 0,
12652
- [options, value, multiple]
12653
- );
12654
- const handleSelect = (optValue) => {
12655
- if (!multiple) {
12656
- onChange?.(optValue);
12657
- setOpen(false);
12658
- return;
12659
- }
12660
- const next = selectedValues.includes(optValue) ? selectedValues.filter((v) => v !== optValue) : [...selectedValues, optValue];
12661
- onChange?.(next);
12662
- };
12663
- const handleRemove = (e, optValue) => {
12664
- e.stopPropagation();
12665
- onChange?.(selectedValues.filter((v) => v !== optValue));
12666
- };
12667
- return /* @__PURE__ */ jsxs99(Popover, { open, onOpenChange: setOpen, children: [
12668
- /* @__PURE__ */ jsxs99(
12669
- PopoverTrigger,
12670
- {
12671
- id,
12672
- type: "button",
12673
- role: "combobox",
12674
- "aria-expanded": open,
12675
- disabled,
12676
- className: cn(
12677
- "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",
12678
- "placeholder:text-muted-foreground min-w-32",
12679
- "focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2",
12680
- "disabled:cursor-not-allowed disabled:opacity-50",
12681
- CONTROL_SIZE_BOX_GROW[size],
12682
- multiple && "flex-wrap gap-1",
12683
- className
12684
- ),
12685
- children: [
12686
- multiple ? selectedValues.length > 0 ? /* @__PURE__ */ jsx157("div", { className: "flex flex-wrap gap-1 flex-1", children: selectedValues.map((v) => {
12687
- const label = options.find((o) => o.value === v)?.label ?? v;
12688
- return /* @__PURE__ */ jsxs99(
12689
- "span",
12690
- {
12691
- className: "inline-flex items-center gap-1 rounded bg-muted px-2 py-0.5 text-xs font-medium",
12692
- children: [
12693
- label,
12694
- /* @__PURE__ */ jsx157(
12695
- X4,
12696
- {
12697
- className: "h-3 w-3 cursor-pointer opacity-60 hover:opacity-100",
12698
- onClick: (e) => handleRemove(e, v)
12699
- }
12700
- )
12701
- ]
12702
- },
12703
- v
12704
- );
12705
- }) }) : /* @__PURE__ */ jsx157("span", { className: "text-muted-foreground flex-1 text-left", children: resolvedPlaceholder }) : /* @__PURE__ */ jsx157("span", { className: cn("truncate", !selectedLabel && "text-muted-foreground"), children: selectedLabel || resolvedPlaceholder }),
12706
- allowClear && !multiple && !disabled && value !== void 0 && value !== "" ? /* @__PURE__ */ jsx157(
12707
- ClearHoverButton,
12708
- {
12709
- label: "clear-selection",
12710
- className: "ml-2 rounded-sm opacity-50 hover:opacity-100",
12711
- onClear: () => onChange?.("")
12712
- }
12713
- ) : null,
12714
- /* @__PURE__ */ jsx157(
12715
- ChevronsUpDown,
12716
- {
12717
- className: cn(
12718
- "ml-2 h-4 w-4 shrink-0 opacity-50",
12719
- allowClear && !multiple && !disabled && value !== void 0 && value !== "" && "group-hover:hidden"
12720
- )
12721
- }
12722
- )
12723
- ]
12724
- }
12725
- ),
12726
- /* @__PURE__ */ jsx157(
12727
- PopoverContent,
12728
- {
12729
- className: cn(
12730
- "p-0",
12731
- // Base UI Positioner 在定位元素上暴露 --anchor-width(原 Radix 的
12732
- // --radix-popover-trigger-width 在 Base UI 下不存在,会退化为 auto 宽度)
12733
- fullOptionText ? "w-auto min-w-(--anchor-width)" : "w-(--anchor-width)"
12734
- ),
12735
- align: "start",
12736
- onWheel: (e) => e.stopPropagation(),
12737
- onTouchMove: (e) => e.stopPropagation(),
12738
- children: /* @__PURE__ */ jsxs99(Command, { value: !multiple ? selectedLabel : void 0, children: [
12739
- shouldShowSearch && /* @__PURE__ */ jsx157(CommandInput, { placeholder: resolvedSearchPlaceholder }),
12740
- /* @__PURE__ */ jsxs99(CommandList, { className: listClassName, children: [
12741
- /* @__PURE__ */ jsx157(CommandEmpty, { children: options.length === 0 ? NO_OPTIONS_TEXT : resolvedEmptyText }),
12742
- /* @__PURE__ */ jsx157(CommandGroup, { children: options.map((opt) => {
12743
- const isSelected = multiple ? selectedValues.includes(opt.value) : value === opt.value;
12744
- return /* @__PURE__ */ jsxs99(
12745
- CommandItem,
12746
- {
12747
- value: opt.label,
12748
- onSelect: () => handleSelect(opt.value),
12749
- className: "flex items-center justify-between",
12750
- children: [
12751
- /* @__PURE__ */ jsx157("span", { className: cn(fullOptionText ? "whitespace-normal" : "truncate"), children: opt.label }),
12752
- /* @__PURE__ */ jsx157(
12753
- Check,
12754
- {
12755
- className: cn(
12756
- "ml-2 h-4 w-4 shrink-0",
12757
- isSelected ? "opacity-100" : "opacity-0"
12758
- )
12759
- }
12760
- )
12761
- ]
12762
- },
12763
- opt.value
12764
- );
12765
- }) })
12766
- ] })
12767
- ] })
12768
- }
12769
- )
12770
- ] });
12771
- }
12772
-
12773
- // src/components/business/FieldCondition/index.tsx
12774
- import { jsx as jsx158, jsxs as jsxs100 } from "react/jsx-runtime";
12775
- var OPERATORS = [
12776
- { value: "EQUAL", valueTypes: ["NUMBER", "STRING", "DATE"] },
12777
- { value: "GREATER", valueTypes: ["NUMBER", "DATE"] },
12778
- { value: "GREATER_EQUAL", valueTypes: ["NUMBER", "DATE"] },
12779
- { value: "LESS", valueTypes: ["NUMBER", "DATE"] },
12780
- { value: "LESS_EQUAL", valueTypes: ["NUMBER", "DATE"] },
12781
- { value: "UNEQUAL", valueTypes: ["NUMBER", "STRING", "DATE"] },
12782
- { value: "BETWEEN", valueTypes: ["NUMBER", "DATE"] },
12783
- { value: "HASVAL", valueTypes: "ALL" },
12784
- { value: "NOHASVAL", valueTypes: "ALL" },
12785
- { value: "TRUE", valueTypes: ["BOOLEAN"] },
12786
- { value: "FALSE", valueTypes: ["BOOLEAN"] },
12787
- { value: "IN", valueTypes: ["NUMBER", "STRING"] },
12788
- { value: "NOT_IN", valueTypes: ["NUMBER", "STRING"] }
12789
- ];
12790
- var NO_VALUE_OPERATORS = ["HASVAL", "NOHASVAL", "TRUE", "FALSE"];
12791
- var OPERATOR_LABELS = {
12792
- EQUAL: "\u7B49\u4E8E",
12793
- GREATER: "\u5927\u4E8E",
12794
- GREATER_EQUAL: "\u5927\u4E8E\u7B49\u4E8E",
12795
- LESS: "\u5C0F\u4E8E",
12796
- LESS_EQUAL: "\u5C0F\u4E8E\u7B49\u4E8E",
12797
- UNEQUAL: "\u4E0D\u7B49\u4E8E",
12798
- BETWEEN: "\u8303\u56F4",
12799
- HASVAL: "\u6709\u503C",
12800
- NOHASVAL: "\u65E0\u503C",
12801
- TRUE: "\u4E3A\u771F",
12802
- FALSE: "\u4E3A\u5047",
12803
- IN: "\u5305\u542B",
12804
- NOT_IN: "\u4E0D\u5305\u542B"
12805
- };
12806
- function operatorLabel(operator) {
12807
- return OPERATOR_LABELS[operator] ?? operator;
12808
- }
12809
- function getOperatorsByFieldType(fieldType) {
12810
- return OPERATORS.filter(
12811
- (op) => !fieldType || op.valueTypes === "ALL" || op.valueTypes.includes(fieldType)
12812
- );
12813
- }
12814
- function isNoValueOperator(operator) {
12815
- return !!operator && NO_VALUE_OPERATORS.includes(operator);
12816
- }
12817
- var OPERATOR_STATEMENTS = {
12818
- EQUAL: "${field} == ${value}",
12819
- GREATER: "${field} > ${value}",
12820
- GREATER_EQUAL: "${field} >= ${value}",
12821
- LESS: "${field} < ${value}",
12822
- LESS_EQUAL: "${field} <= ${value}",
12823
- UNEQUAL: "${field} != ${value}",
12824
- BETWEEN: "${field} BETWEEN ${v1} AND ${v2}",
12825
- HASVAL: "${field} HASVAL",
12826
- NOHASVAL: "${field} NOHASVAL",
12827
- TRUE: "${field} == TRUE",
12828
- FALSE: "${field} == FALSE",
12829
- IN: "${field} IN (${value})",
12830
- NOT_IN: "${field} NOT IN (${value})"
12831
- };
12832
- function formatConditionValue(item) {
12833
- const isString = item.field.dataType === "STRING";
12834
- const quote = (v) => isString ? `'${v}'` : String(v);
12835
- if (item.term.value === "IN" || item.term.value === "NOT_IN") {
12836
- return String(item.value ?? "").split(",").map((s) => s.trim()).filter(Boolean).map(quote).join(", ");
12837
- }
12838
- return quote(item.value ?? "");
12839
- }
12840
- function itemToStatement(item) {
12841
- const fieldCode = item.field?.value;
12842
- const termValue = item.term?.value;
12843
- const statement = termValue ? OPERATOR_STATEMENTS[termValue] : void 0;
12844
- if (!fieldCode || !statement) return null;
12845
- if (termValue === "BETWEEN") {
12846
- const arr = Array.isArray(item.value) ? item.value : [];
12847
- const isString = item.field.dataType === "STRING";
12848
- const quote = (v) => isString ? `'${v ?? ""}'` : String(v ?? "");
12849
- return statement.replace("${field}", fieldCode).replace("${v1}", quote(arr[0])).replace("${v2}", quote(arr[1]));
12850
- }
12851
- let result = statement.replace("${field}", fieldCode);
12852
- if (!isNoValueOperator(termValue)) {
12853
- result = result.replace("${value}", formatConditionValue(item));
12854
- }
12855
- return result;
12856
- }
12857
- function conditionToStatement(value) {
12858
- if (!value?.children?.length) return "";
12859
- const groupParts = value.children.map((group) => {
12860
- const joiner = group.relation === "AND" ? " && " : " || ";
12861
- const parts = group.children.map(itemToStatement).filter((s) => !!s);
12862
- if (!parts.length) return "";
12863
- const joined = parts.join(joiner);
12864
- return parts.length > 1 ? `(${joined})` : joined;
12865
- }).filter(Boolean);
12866
- return groupParts.join(value.relation === "AND" ? " && " : " || ");
12867
- }
12868
- function splitByJoiner(expr, joiner) {
12869
- const parts = [];
12870
- const token = ` ${joiner} `;
12871
- let depth = 0;
12872
- let quote = null;
12873
- let current = "";
12874
- for (let i = 0; i < expr.length; i++) {
12875
- const ch = expr[i];
12876
- if (quote) {
12877
- current += ch;
12878
- if (ch === quote) quote = null;
12879
- continue;
12880
- }
12881
- if (ch === "'" || ch === '"') {
12882
- quote = ch;
12883
- current += ch;
12884
- continue;
12885
- }
12886
- if (ch === "(") depth++;
12887
- if (ch === ")") depth--;
12888
- if (depth === 0 && expr.startsWith(token, i)) {
12889
- parts.push(current);
12890
- current = "";
12891
- i += token.length - 1;
12892
- continue;
12893
- }
12894
- current += ch;
12895
- }
12896
- parts.push(current);
12897
- return parts.map((s) => s.trim()).filter(Boolean);
12898
- }
12899
- function isWrappedByOuterParens(s) {
12900
- if (!s.startsWith("(") || !s.endsWith(")")) return false;
12901
- let depth = 0;
12902
- let quote = null;
12903
- for (let i = 0; i < s.length; i++) {
12904
- const ch = s[i];
12905
- if (quote) {
12906
- if (ch === quote) quote = null;
12907
- continue;
12908
- }
12909
- if (ch === "'" || ch === '"') {
12910
- quote = ch;
12911
- continue;
12912
- }
12913
- if (ch === "(") depth++;
12914
- if (ch === ")") {
12915
- depth--;
12916
- if (depth === 0 && i < s.length - 1) return false;
12917
- }
12918
- }
12919
- return depth === 0;
12920
- }
12921
- function unquote(v) {
12922
- const t = v.trim();
12923
- if (t.length >= 2 && (t.startsWith("'") && t.endsWith("'") || t.startsWith('"') && t.endsWith('"'))) {
12924
- return t.slice(1, -1);
12925
- }
12926
- return t;
12927
- }
12928
- var COMPARE_OPERATOR_MAP = {
12929
- "==": "EQUAL",
12930
- "!=": "UNEQUAL",
12931
- ">": "GREATER",
12932
- ">=": "GREATER_EQUAL",
12933
- "<": "LESS",
12934
- "<=": "LESS_EQUAL"
12935
- };
12936
- function parseAtom(atom, fields) {
12937
- const s = atom.trim();
12938
- if (!s) return null;
12939
- const fieldInfo = (code) => {
12940
- const f = fields?.find((x) => x.featureCode === code);
12941
- return { value: code, label: f?.featureName, dataType: f?.fieldType };
12942
- };
12943
- let m = s.match(/^([\w.]+)\s+BETWEEN\s+(.+?)\s+AND\s+(.+)$/i);
12944
- if (m) {
12945
- return {
12946
- field: fieldInfo(m[1]),
12947
- term: { value: "BETWEEN" },
12948
- value: [unquote(m[2]), unquote(m[3])]
12949
- };
12950
- }
12951
- m = s.match(/^([\w.]+)\s+(NOT\s+IN|IN)\s*\((.*)\)\s*$/i);
12952
- if (m) {
12953
- const vals = m[3].split(",").map((v) => unquote(v)).filter((v) => v !== "");
12954
- return {
12955
- field: fieldInfo(m[1]),
12956
- term: { value: m[2].toUpperCase().replace(/\s+/g, "_") },
12957
- value: vals.join(", ")
12958
- };
12959
- }
12960
- m = s.match(/^([\w.]+)\s*(==|!=|>=|<=|>|<)\s*(.+)$/);
12961
- if (m) {
12962
- const raw = m[3].trim();
12963
- const val = unquote(raw);
12964
- if (m[2] === "==" && raw === val && (val === "TRUE" || val === "FALSE")) {
12965
- return { field: fieldInfo(m[1]), term: { value: val }, value: void 0 };
12966
- }
12967
- return {
12968
- field: fieldInfo(m[1]),
12969
- term: { value: COMPARE_OPERATOR_MAP[m[2]] },
12970
- value: val
12971
- };
12972
- }
12973
- m = s.match(/^([\w.]+)\s+(HASVAL|NOHASVAL)\s*$/i);
12974
- if (m) {
12975
- return {
12976
- field: fieldInfo(m[1]),
12977
- term: { value: m[2].toUpperCase() },
12978
- value: void 0
12979
- };
12980
- }
12981
- return null;
12982
- }
12983
- function statementToCondition(statement, fields) {
12984
- const trimmed = statement?.trim();
12985
- if (!trimmed) return null;
12986
- const stripOuterParens = (s) => {
12987
- const t = s.trim();
12988
- return isWrappedByOuterParens(t) ? t.slice(1, -1).trim() : t;
12989
- };
12990
- const toGroup = (relation, parts) => {
12991
- if (parts.some(
12992
- (s) => splitByJoiner(s, "&&").length > 1 || splitByJoiner(s, "||").length > 1
12993
- )) {
12994
- return null;
12995
- }
12996
- const items = parts.map((s) => parseAtom(s, fields));
12997
- if (items.length === 0 || items.some((it) => !it)) return null;
12998
- return {
12999
- relation,
13000
- children: [{ relation, children: items }]
13001
- };
13002
- };
13003
- const orParts = splitByJoiner(trimmed, "||");
13004
- if (orParts.length > 1) {
13005
- return toGroup("OR", orParts.map(stripOuterParens));
13006
- }
13007
- const inner = stripOuterParens(trimmed);
13008
- const innerOr = splitByJoiner(inner, "||");
13009
- if (innerOr.length > 1) return toGroup("OR", innerOr);
13010
- return toGroup("AND", splitByJoiner(inner, "&&"));
13011
- }
13012
- var defaultItem = () => ({ field: {}, term: {} });
13013
- var defaultGroup = () => ({
13014
- relation: "AND",
13015
- children: [defaultItem()]
13016
- });
13017
- function defaultFieldConditionValue() {
13018
- return { relation: "OR", children: [defaultGroup()] };
13019
- }
13020
- var toggleRelation = (r) => r === "AND" ? "OR" : "AND";
13021
- function FieldCondition({
13022
- value,
13023
- onChange,
13024
- fields = [],
13025
- disabled,
13026
- size = "middle"
13027
- }) {
13028
- const data = useMemo9(
13029
- () => value?.children?.length ? value : defaultFieldConditionValue(),
13030
- [value]
13031
- );
13032
- const group = data.children[0];
13033
- const updateGroup = useCallback4(
13034
- (nextGroup) => onChange?.({ ...data, children: [nextGroup] }),
13035
- [data, onChange]
13036
- );
13037
- const updateItem = (itemIndex, patch) => {
13038
- updateGroup({
13039
- ...group,
13040
- children: group.children.map(
13041
- (item, ii) => ii === itemIndex ? { ...item, ...patch } : item
13042
- )
13043
- });
13044
- };
13045
- const fieldOptions = fields.map((f) => ({
13046
- value: f.featureCode,
13047
- label: f.featureName
13048
- }));
13049
- return /* @__PURE__ */ jsxs100("div", { className: "w-full", children: [
13050
- /* @__PURE__ */ jsxs100("div", { className: "flex gap-2", children: [
13051
- /* @__PURE__ */ jsx158(
13052
- RelationButton,
13053
- {
13054
- relation: group.relation,
13055
- visible: group.children.length > 1,
13056
- disabled,
13057
- onToggle: () => updateGroup({ ...group, relation: toggleRelation(group.relation) })
13058
- }
13059
- ),
13060
- /* @__PURE__ */ jsx158("div", { className: "flex-1 space-y-2", children: group.children.map((item, itemIndex) => /* @__PURE__ */ jsx158(
13061
- ConditionRow,
13062
- {
13063
- item,
13064
- fields: fieldOptions,
13065
- rawFields: fields,
13066
- disabled,
13067
- size,
13068
- fieldPlaceholder: "\u9009\u62E9\u5B57\u6BB5",
13069
- operatorLabel,
13070
- inputPlaceholder: "\u8BF7\u8F93\u5165\u503C",
13071
- showDelete: group.children.length > 1,
13072
- onChange: (patch) => updateItem(itemIndex, patch),
13073
- onDelete: () => updateGroup({
13074
- ...group,
13075
- children: group.children.filter((_, ii) => ii !== itemIndex)
13076
- })
13077
- },
13078
- itemIndex
13079
- )) })
13080
- ] }),
13081
- !disabled && /* @__PURE__ */ jsxs100(
13082
- Button,
13083
- {
13084
- type: "button",
13085
- variant: "link",
13086
- size: "sm",
13087
- className: "h-auto p-0 ml-10 mt-2 text-sm",
13088
- onClick: () => updateGroup({ ...group, children: [...group.children, defaultItem()] }),
13089
- children: [
13090
- /* @__PURE__ */ jsx158(Plus, { className: "h-3.5 w-3.5" }),
13091
- "\u6DFB\u52A0\u6761\u4EF6"
13092
- ]
13093
- }
13094
- )
13095
- ] });
13096
- }
13097
- function RelationButton({
13098
- relation,
13099
- visible = true,
13100
- disabled,
13101
- onToggle
13102
- }) {
13103
- if (!visible) return /* @__PURE__ */ jsx158("div", { className: "w-8 shrink-0" });
13104
- return /* @__PURE__ */ jsx158(
13105
- "button",
13106
- {
13107
- type: "button",
13108
- disabled,
13109
- onClick: onToggle,
13110
- className: cn(
13111
- "w-8 shrink-0 self-center rounded px-1 py-0.5 text-xs font-medium",
13112
- disabled ? "cursor-not-allowed" : "cursor-pointer",
13113
- // 语义色走皮肤令牌(AND=信息蓝 / OR=警示橙),随宿主主题明暗自适应
13114
- relation === "AND" ? "bg-info/10 text-info" : "bg-warning/10 text-warning"
13115
- ),
13116
- children: relation === "AND" ? "\u4E14" : "\u6216"
13117
- }
13118
- );
13119
- }
13120
- function ConditionRow({
13121
- item,
13122
- fields,
13123
- rawFields,
13124
- disabled,
13125
- size,
13126
- fieldPlaceholder,
13127
- operatorLabel: operatorLabel3,
13128
- inputPlaceholder,
13129
- showDelete,
13130
- onChange,
13131
- onDelete
13132
- }) {
13133
- const dataType = item.field.dataType;
13134
- const termValue = item.term.value;
13135
- const operators = getOperatorsByFieldType(dataType);
13136
- return /* @__PURE__ */ jsxs100("div", { className: "flex items-center gap-2", children: [
13137
- /* @__PURE__ */ jsx158(
13138
- SearchableSelect,
13139
- {
13140
- value: item.field.value || void 0,
13141
- onChange: (v) => {
13142
- const raw = rawFields.find((f) => f.featureCode === v);
13143
- onChange({
13144
- field: raw ? { value: raw.featureCode, label: raw.featureName, dataType: raw.fieldType } : { value: v },
13145
- term: {},
13146
- value: void 0
13147
- });
13148
- },
13149
- options: fields,
13150
- placeholder: fieldPlaceholder,
13151
- disabled,
13152
- size,
13153
- fullOptionText: true,
13154
- className: "w-36"
13155
- }
13156
- ),
13157
- /* @__PURE__ */ jsxs100(
13158
- Select,
13159
- {
13160
- items: operators.map((op) => ({
13161
- value: op.value,
13162
- label: operatorLabel3(op.value)
13163
- })),
13164
- value: termValue ?? null,
13165
- disabled: disabled || !dataType,
13166
- onValueChange: (v) => {
13167
- if (v == null) return;
13168
- const resetValue = v === "BETWEEN" || isNoValueOperator(v) || isNoValueOperator(termValue);
13169
- onChange({
13170
- term: { value: v, label: operatorLabel3(v) },
13171
- value: resetValue ? void 0 : item.value
13172
- });
13173
- },
13174
- children: [
13175
- /* @__PURE__ */ jsx158(
13176
- SelectTrigger,
13177
- {
13178
- size: CONTROL_SIZE_SELECT_ATTR[size],
13179
- className: cn("w-28 shrink-0", CONTROL_SIZE_TRIGGER_TEXT[size]),
13180
- children: /* @__PURE__ */ jsx158(SelectValue, { placeholder: "\u8BF7\u9009\u62E9" })
13181
- }
13182
- ),
13183
- /* @__PURE__ */ jsx158(SelectContent, { children: operators.map((op) => /* @__PURE__ */ jsx158(SelectItem, { value: op.value, children: operatorLabel3(op.value) }, op.value)) })
13184
- ]
13185
- }
13186
- ),
13187
- dataType && termValue && !isNoValueOperator(termValue) && /* @__PURE__ */ jsx158(
13188
- ConditionValueInput,
13189
- {
13190
- dataType,
13191
- termValue,
13192
- value: item.value,
13193
- disabled,
13194
- size,
13195
- placeholder: inputPlaceholder,
13196
- onChange: (v) => onChange({ value: v })
13197
- }
13198
- ),
13199
- showDelete && !disabled && /* @__PURE__ */ jsx158(
13200
- "button",
13201
- {
13202
- type: "button",
13203
- "aria-label": "delete-condition",
13204
- className: "text-muted-foreground hover:text-destructive",
13205
- onClick: onDelete,
13206
- children: /* @__PURE__ */ jsx158(X5, { className: "h-4 w-4" })
13207
- }
13208
- )
13209
- ] });
13210
- }
13211
- function ConditionValueInput({
13212
- dataType,
13213
- termValue,
13214
- value,
13215
- disabled,
13216
- size,
13217
- placeholder,
13218
- onChange
13219
- }) {
13220
- const inputType = dataType === "NUMBER" ? "number" : "text";
13221
- if (termValue === "BETWEEN") {
13222
- const arr = Array.isArray(value) ? value : [];
13223
- if (dataType === "DATE") {
13224
- return /* @__PURE__ */ jsxs100("div", { className: "flex items-center gap-1", children: [
13225
- /* @__PURE__ */ jsx158(
13226
- DatePicker,
13227
- {
13228
- value: typeof arr[0] === "string" && arr[0] ? arr[0] : void 0,
13229
- disabled,
13230
- size,
13231
- placeholder,
13232
- className: "w-32",
13233
- onChange: (v) => onChange([v, arr[1] ?? ""])
13234
- }
13235
- ),
13236
- /* @__PURE__ */ jsx158("span", { className: "text-xs text-muted-foreground", children: "~" }),
13237
- /* @__PURE__ */ jsx158(
13238
- DatePicker,
13239
- {
13240
- value: typeof arr[1] === "string" && arr[1] ? arr[1] : void 0,
13241
- disabled,
13242
- size,
13243
- placeholder,
13244
- className: "w-32",
13245
- onChange: (v) => onChange([arr[0] ?? "", v])
13246
- }
13247
- )
13248
- ] });
13249
- }
13250
- return /* @__PURE__ */ jsxs100("div", { className: "flex items-center gap-1", children: [
13251
- /* @__PURE__ */ jsx158(
13252
- Input,
13253
- {
13254
- type: inputType,
13255
- className: cn("w-28", CONTROL_SIZE_BOX[size]),
13256
- value: arr[0] ?? "",
13257
- disabled,
13258
- placeholder,
13259
- "aria-label": "between-min",
13260
- onChange: (e) => onChange([e.target.value, arr[1] ?? ""])
13261
- }
13262
- ),
13263
- /* @__PURE__ */ jsx158("span", { className: "text-xs text-muted-foreground", children: "~" }),
13264
- /* @__PURE__ */ jsx158(
13265
- Input,
13266
- {
13267
- type: inputType,
13268
- className: cn("w-28", CONTROL_SIZE_BOX[size]),
13269
- value: arr[1] ?? "",
13270
- disabled,
13271
- placeholder,
13272
- "aria-label": "between-max",
13273
- onChange: (e) => onChange([arr[0] ?? "", e.target.value])
13274
- }
13275
- )
13276
- ] });
13277
- }
13278
- if (dataType === "DATE") {
13279
- return /* @__PURE__ */ jsx158(
13280
- DatePicker,
13281
- {
13282
- value: typeof value === "string" && value ? value : void 0,
13283
- disabled,
13284
- size,
13285
- placeholder,
13286
- className: "w-36",
13287
- onChange: (v) => onChange(v ?? "")
13288
- }
13289
- );
13290
- }
13291
- return /* @__PURE__ */ jsx158(
13292
- Input,
13293
- {
13294
- type: inputType,
13295
- className: cn("w-36", CONTROL_SIZE_BOX[size]),
13296
- value: value ?? "",
13297
- disabled,
13298
- placeholder,
13299
- "aria-label": "condition-value",
13300
- onChange: (e) => onChange(e.target.value)
13301
- }
13302
- );
13303
- }
13304
-
13305
- // src/components/business/ExpressionEditor/index.tsx
13306
- import { jsx as jsx159, jsxs as jsxs101 } from "react/jsx-runtime";
13307
- var TEXT = {
13308
- /** 头部进入编辑态的按钮文案 */
13309
- edit: "\u7F16\u8F91",
13310
- cancel: "\u53D6\u6D88",
13311
- confirm: "\u786E\u5B9A",
13312
- /** validation 未配置 message 时的兜底提示 */
13313
- invalidFormat: "\u8F93\u5165\u683C\u5F0F\u4E0D\u6B63\u786E"
13314
- };
13315
- function resolveI18nText(value, locale) {
13316
- if (value === null || value === void 0) return "";
13317
- if (typeof value === "string") return value;
13318
- if (typeof value === "number" || typeof value === "boolean") {
13319
- return String(value);
13320
- }
13321
- if (typeof value === "object") {
13322
- const lang = (locale || "zh").toLowerCase();
13323
- 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"];
13324
- for (const key of candidates) {
13325
- if (value[key]) return String(value[key]);
13326
- }
13327
- const first = Object.values(value).find((v) => v);
13328
- return first ? String(first) : "";
13329
- }
13330
- return String(value);
13331
- }
13332
- var PLACEHOLDER_RE = /\$\{(\w+)\}|\{\{(\w+)\}\}/g;
13333
- function parseTemplateSegments(template, mapping) {
13334
- const segments = [];
13335
- const re = new RegExp(PLACEHOLDER_RE.source, "g");
13336
- let lastIndex = 0;
13337
- let match;
13338
- while ((match = re.exec(template)) !== null) {
13339
- const key = match[1] ?? match[2];
13340
- const config = mapping?.[key];
13341
- if (!config) continue;
13342
- if (match.index > lastIndex) {
13343
- segments.push({ type: "text", text: template.slice(lastIndex, match.index) });
13344
- }
13345
- segments.push({ type: "field", key, config });
13346
- lastIndex = match.index + match[0].length;
13347
- }
13348
- if (lastIndex < template.length) {
13349
- segments.push({ type: "text", text: template.slice(lastIndex) });
13350
- }
13351
- return segments;
13352
- }
13353
- function coerceMappingValue(raw, type) {
13354
- if (type !== "number") return raw ?? "";
13355
- if (raw === "" || raw === void 0 || raw === null) return "";
13356
- const num = Number(raw);
13357
- return Number.isFinite(num) ? num : raw;
13358
- }
13359
- function composeExpression(template, values, mapping) {
13360
- return template.replace(
13361
- new RegExp(PLACEHOLDER_RE.source, "g"),
13362
- (raw, dollarKey, braceKey) => {
13363
- const key = dollarKey ?? braceKey;
13364
- return mapping?.[key] ? String(values[key] ?? "") : raw;
13365
- }
13366
- );
13367
- }
13368
- function buildDefaultValues(mapping) {
13369
- const values = {};
13370
- for (const [key, config] of Object.entries(mapping ?? {})) {
13371
- values[key] = coerceMappingValue(config.default ?? "", config.type);
13372
- }
13373
- return values;
13374
- }
13375
- function validateMappingValue(value, config, fallbackMessage) {
13376
- const isEmpty2 = value === "" || value === void 0 || value === null;
13377
- if (config.required && isEmpty2) {
13378
- return config.validation?.message ?? fallbackMessage;
13379
- }
13380
- const pattern = config.validation?.pattern;
13381
- if (!pattern) return null;
13382
- if (isEmpty2) return null;
13383
- if (typeof value === "number" && Number.isNaN(value)) return null;
13384
- return new RegExp(pattern).test(String(value)) ? null : config.validation?.message ?? fallbackMessage;
13385
- }
13386
- function escapeRegExp2(text) {
13387
- return text.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
13388
- }
13389
- function parseExpressionValues(template, value, mapping) {
13390
- if (!value) return null;
13391
- const segments = parseTemplateSegments(template, mapping);
13392
- const keys = [];
13393
- const pattern = segments.map((seg) => {
13394
- if (seg.type === "text") return escapeRegExp2(seg.text);
13395
- keys.push(seg.key);
13396
- return "(.*?)";
13397
- }).join("");
13398
- const match = value.match(new RegExp(`^${pattern}$`));
13399
- if (!match) return null;
13400
- const values = {};
13401
- keys.forEach((key, i) => {
13402
- values[key] = coerceMappingValue(match[i + 1], mapping?.[key]?.type);
13403
- });
13404
- return values;
13405
- }
13406
- function resolveMappingOptions(config, optionSources, locale) {
13407
- const raw = config.options?.length ? config.options : config.optionSource ? optionSources?.[config.optionSource]?.options : null;
13408
- if (!raw?.length) return null;
13409
- return raw.map((opt) => ({
13410
- ...opt,
13411
- label: resolveI18nText(opt?.label, locale),
13412
- value: opt?.value
13413
- }));
13414
- }
13415
- function MappingSelect({
13416
- config,
13417
- optionSources,
13418
- value,
13419
- onChange,
13420
- disabled,
13421
- size,
13422
- fallbackLabel,
13423
- className,
13424
- viewOnly
13425
- }) {
13426
- const options = resolveMappingOptions(config, optionSources) ?? [];
13427
- const displayOptions = useMemo10(() => {
13428
- if (!viewOnly || value === void 0 || value === null || value === "") {
13429
- return options;
13430
- }
13431
- return options.some((opt) => String(opt.value) === String(value)) ? options : [...options, { label: String(value), value: String(value) }];
13432
- }, [options, value, viewOnly]);
13433
- return /* @__PURE__ */ jsx159(
13434
- SearchableSelect,
13435
- {
13436
- value: value === "" ? void 0 : value,
13437
- onChange: (v) => onChange(v),
13438
- options: displayOptions,
13439
- placeholder: fallbackLabel,
13440
- disabled,
13441
- size,
13442
- showSearch: options.length > 10,
13443
- fullOptionText: true,
13444
- listClassName: "max-h-60 overflow-y-scroll",
13445
- className
13446
- }
13447
- );
13448
- }
13449
- function useMappingFieldOptions(config, optionSources) {
13450
- const entry = config.optionSource ? optionSources?.[config.optionSource] : null;
13451
- return useMemo10(() => {
13452
- const opts = config.options?.length ? config.options : entry?.options ?? [];
13453
- return {
13454
- loading: false,
13455
- fields: opts.map((o) => ({
13456
- featureCode: o.value,
13457
- featureName: o.label,
13458
- fieldType: o.fieldType
13459
- }))
13460
- };
13461
- }, [entry, config.options]);
13462
- }
13463
- function MappingFieldCondition({
13464
- config,
13465
- optionSources,
13466
- value,
13467
- onChange,
13468
- disabled,
13469
- size
13470
- }) {
13471
- const { fields } = useMappingFieldOptions(config, optionSources);
13472
- const [condition, setCondition] = useState50(
13473
- () => (value ? statementToCondition(value, fields) : null) ?? defaultFieldConditionValue()
13474
- );
13475
- const touchedRef = useRef15(false);
13476
- useEffect42(() => {
13477
- if (touchedRef.current || !value) return;
13478
- const parsed = statementToCondition(value, fields);
13479
- if (parsed) setCondition(parsed);
13480
- }, [fields, value]);
13481
- return /* @__PURE__ */ jsx159(
13482
- FieldCondition,
13483
- {
13484
- value: condition,
13485
- fields,
13486
- disabled,
13487
- size,
13488
- onChange: (next) => {
13489
- touchedRef.current = true;
13490
- setCondition(next);
13491
- onChange(conditionToStatement(next));
13492
- }
13493
- }
13494
- );
13495
- }
13496
- function ExpressionEditor({
13497
- id,
13498
- value,
13499
- onChange,
13500
- valueTemplate,
13501
- valueMapping,
13502
- optionSources,
13503
- label,
13504
- required,
13505
- readonly,
13506
- viewOnly,
13507
- size = "middle",
13508
- className,
13509
- onValidityChange
13510
- }) {
13511
- const labelOf = (seg) => resolveI18nText(seg.config.label) || seg.key;
13512
- const hintOf = (seg) => resolveI18nText(seg.config.placeholder) || labelOf(seg);
13513
- const segments = useMemo10(
13514
- () => parseTemplateSegments(valueTemplate, valueMapping),
13515
- [valueTemplate, valueMapping]
13516
- );
13517
- const fieldSegments = useMemo10(() => {
13518
- const seen = /* @__PURE__ */ new Set();
13519
- return segments.filter((seg) => {
13520
- if (seg.type !== "field" || seen.has(seg.key)) return false;
13521
- seen.add(seg.key);
13522
- return true;
13523
- });
13524
- }, [segments]);
13525
- const [values, setValues] = useState50(
13526
- () => (
13527
- // 外部已有值(编辑回显/AI 填充)时优先反向解析回控件
13528
- (value ? parseExpressionValues(valueTemplate, value, valueMapping) : null) ?? buildDefaultValues(valueMapping)
13529
- )
13530
- );
13531
- const composed = composeExpression(valueTemplate, values, valueMapping);
13532
- const [editing, setEditing] = useState50(false);
13533
- const [draft, setDraft] = useState50(values);
13534
- const [draftErrors, setDraftErrors] = useState50({});
13535
- const [text, setText] = useState50(composed);
13536
- const formValues = viewOnly ? values : draft;
13537
- const lastEmittedRef = useRef15(null);
13538
- const onChangeRef = useRef15(onChange);
13539
- onChangeRef.current = onChange;
13540
- const valuesRef = useRef15(values);
13541
- valuesRef.current = values;
13542
- const onValidityChangeRef = useRef15(onValidityChange);
13543
- onValidityChangeRef.current = onValidityChange;
13544
- useEffect42(() => {
13545
- onValidityChangeRef.current?.(editing && Object.keys(draftErrors).length > 0);
13546
- }, [editing, draftErrors]);
13547
- useEffect42(() => {
13548
- if (viewOnly) return;
13549
- const initial = composeExpression(valueTemplate, values, valueMapping);
13550
- if (initial !== (value ?? "")) {
13551
- lastEmittedRef.current = initial;
13552
- onChangeRef.current?.(initial);
13553
- }
13554
- }, []);
13555
- useEffect42(() => {
13556
- if (!value) return;
13557
- const composed2 = composeExpression(valueTemplate, valuesRef.current, valueMapping);
13558
- if (value === composed2) return;
13559
- const parsed = parseExpressionValues(valueTemplate, value, valueMapping);
13560
- if (parsed) setValues(parsed);
13561
- }, [value, valueTemplate, valueMapping]);
13562
- useEffect42(() => {
13563
- if (value !== void 0 && value !== "") return;
13564
- const defaults = buildDefaultValues(valueMapping);
13565
- setValues((prev) => {
13566
- const same = Object.keys(defaults).every((k) => prev[k] === defaults[k]);
13567
- return same ? prev : defaults;
13568
- });
13569
- }, [value, valueMapping]);
13570
- useEffect42(() => {
13571
- if (value === void 0 || value === lastEmittedRef.current) return;
13572
- setText(value === "" ? composed : value);
13573
- }, [value, composed]);
13574
- const enterEdit = () => {
13575
- setDraft(valuesRef.current);
13576
- setDraftErrors({});
13577
- setEditing(true);
13578
- };
13579
- const handleConfirm = () => {
13580
- const nextErrors = {};
13581
- for (const seg of fieldSegments) {
13582
- const message = validateMappingValue(draft[seg.key], seg.config, TEXT.invalidFormat);
13583
- if (message) nextErrors[seg.key] = message;
13584
- }
13585
- setDraftErrors(nextErrors);
13586
- if (Object.keys(nextErrors).length > 0) return;
13587
- const next = composeExpression(valueTemplate, draft, valueMapping);
13588
- setValues(draft);
13589
- lastEmittedRef.current = next;
13590
- setText(next);
13591
- onChangeRef.current?.(next);
13592
- setEditing(false);
13593
- };
13594
- const handleCancel = () => {
13595
- setDraftErrors({});
13596
- setEditing(false);
13597
- };
13598
- const handleTextChange = (raw) => {
13599
- setText(raw);
13600
- lastEmittedRef.current = raw;
13601
- const parsed = parseExpressionValues(valueTemplate, raw, valueMapping);
13602
- if (parsed) setValues(parsed);
13603
- onChangeRef.current?.(raw);
13604
- };
13605
- const handleDraftChange = (key, raw) => {
13606
- const config = valueMapping?.[key];
13607
- const next = coerceMappingValue(raw, config?.type);
13608
- setDraft((prev) => ({
13609
- ...prev,
13610
- [key]: next
13611
- }));
13612
- if (config?.validation?.pattern) {
13613
- const message = validateMappingValue(next, config, TEXT.invalidFormat);
13614
- setDraftErrors((prev) => {
13615
- if (!message && !prev[key]) return prev;
13616
- const updated = { ...prev };
13617
- if (message) updated[key] = message;
13618
- else delete updated[key];
13619
- return updated;
13620
- });
13621
- }
13622
- };
13623
- if (!valueTemplate) return null;
13624
- return /* @__PURE__ */ jsxs101("div", { id, className: cn("w-full space-y-2", className), children: [
13625
- !viewOnly && /* @__PURE__ */ jsxs101("div", { className: "flex items-center justify-between", children: [
13626
- label ? /* @__PURE__ */ jsxs101(Label, { children: [
13627
- label,
13628
- required && /* @__PURE__ */ jsx159("span", { className: "text-destructive ml-1", children: "*" })
13629
- ] }) : /* @__PURE__ */ jsx159("span", {}),
13630
- !readonly && !editing && /* @__PURE__ */ jsx159(
13631
- Button,
13632
- {
13633
- type: "button",
13634
- variant: "ghost",
13635
- size: "sm",
13636
- className: "h-6 px-2 text-xs",
13637
- onClick: enterEdit,
13638
- children: TEXT.edit
13639
- }
13640
- )
13641
- ] }),
13642
- editing || viewOnly ? /* @__PURE__ */ jsxs101("div", { className: "flex flex-col gap-3 rounded-md border bg-background px-3 py-3", children: [
13643
- fieldSegments.map((seg) => seg.config.type === "fieldCondition" ? (
13644
- // fieldCondition:label 在上,组件在下(块级布局)
13645
- /* @__PURE__ */ jsxs101("div", { className: "flex flex-col gap-1.5", children: [
13646
- /* @__PURE__ */ jsx159("span", { className: "text-sm text-muted-foreground", children: labelOf(seg) }),
13647
- /* @__PURE__ */ jsx159(
13648
- MappingFieldCondition,
13649
- {
13650
- config: seg.config,
13651
- optionSources,
13652
- disabled: viewOnly,
13653
- size,
13654
- value: formValues[seg.key] === "" || formValues[seg.key] === void 0 ? void 0 : String(formValues[seg.key]),
13655
- onChange: (v) => handleDraftChange(seg.key, v)
13656
- }
13657
- ),
13658
- draftErrors[seg.key] && /* @__PURE__ */ jsx159("p", { className: "text-xs text-destructive", children: draftErrors[seg.key] })
13659
- ] }, seg.key)
13660
- ) : /* @__PURE__ */ jsxs101("div", { className: "flex items-center gap-3", children: [
13661
- /* @__PURE__ */ jsx159("span", { className: "w-24 shrink-0 text-sm text-muted-foreground truncate", children: labelOf(seg) }),
13662
- /* @__PURE__ */ jsxs101("div", { className: "flex-1", children: [
13663
- seg.config.type === "select" ? /* @__PURE__ */ jsx159(
13664
- MappingSelect,
13665
- {
13666
- config: seg.config,
13667
- optionSources,
13668
- disabled: viewOnly,
13669
- viewOnly,
13670
- size,
13671
- value: formValues[seg.key] === "" ? void 0 : String(formValues[seg.key]),
13672
- onChange: (v) => handleDraftChange(seg.key, v),
13673
- fallbackLabel: hintOf(seg)
13674
- }
13675
- ) : /* @__PURE__ */ jsx159(
13676
- Input,
13677
- {
13678
- type: seg.config.type === "number" ? "number" : "text",
13679
- value: formValues[seg.key] ?? "",
13680
- onChange: (e) => handleDraftChange(seg.key, e.target.value),
13681
- disabled: viewOnly,
13682
- placeholder: hintOf(seg),
13683
- "aria-label": labelOf(seg),
13684
- className: cn(CONTROL_SIZE_BOX[size], "w-full font-mono")
13685
- }
13686
- ),
13687
- draftErrors[seg.key] && /* @__PURE__ */ jsx159("p", { className: "mt-1 text-xs text-destructive", children: draftErrors[seg.key] })
13688
- ] })
13689
- ] }, seg.key)),
13690
- !viewOnly && /* @__PURE__ */ jsxs101("div", { className: "flex justify-end gap-2", children: [
13691
- /* @__PURE__ */ jsx159(
13692
- Button,
13693
- {
13694
- type: "button",
13695
- variant: "outline",
13696
- size: "sm",
13697
- onClick: handleCancel,
13698
- children: TEXT.cancel
13699
- }
13700
- ),
13701
- /* @__PURE__ */ jsx159(Button, { type: "button", size: "sm", onClick: handleConfirm, children: TEXT.confirm })
13702
- ] })
13703
- ] }) : (
13704
- // display:可直接编辑的表达式文本框;内容实时 emit,
13705
- // 能按模板解析时会映射回「编辑」表单的控件
13706
- /* @__PURE__ */ jsx159(
13707
- Textarea,
13708
- {
13709
- value: text,
13710
- onChange: (e) => handleTextChange(e.target.value),
13711
- readOnly: readonly,
13712
- rows: 1,
13713
- className: cn(
13714
- CONTROL_SIZE_BOX_GROW[size],
13715
- "w-full resize-none break-all bg-muted/30 font-mono [field-sizing:content]"
13716
- )
13717
- }
13718
- )
13719
- )
13720
- ] });
13721
- }
13722
-
13723
- // src/components/business/Login/index.tsx
13724
- import { useI18n as useI18n35 } from "@pactor-app/runtime";
13725
- import { Fragment as Fragment18, jsx as jsx160, jsxs as jsxs102 } from "react/jsx-runtime";
13726
- function DefaultIllustration({ className }) {
13727
- return /* @__PURE__ */ jsxs102(
13728
- "svg",
13729
- {
13730
- "data-slot": "login-default-illustration",
13731
- viewBox: "0 0 320 180",
13732
- role: "img",
13733
- "aria-hidden": "true",
13734
- className: cn("text-primary", className),
13735
- children: [
13736
- /* @__PURE__ */ jsx160(
13737
- "rect",
13738
- {
13739
- x: "24",
13740
- y: "96",
13741
- width: "88",
13742
- height: "60",
13743
- rx: "10",
13744
- fill: "currentColor",
13745
- opacity: "0.16"
13746
- }
13747
- ),
13748
- /* @__PURE__ */ jsx160(
13749
- "rect",
13750
- {
13751
- x: "120",
13752
- y: "60",
13753
- width: "88",
13754
- height: "96",
13755
- rx: "10",
13756
- fill: "currentColor",
13757
- opacity: "0.28"
13758
- }
13759
- ),
13760
- /* @__PURE__ */ jsx160(
13761
- "rect",
13762
- {
13763
- x: "216",
13764
- y: "24",
13765
- width: "80",
13766
- height: "132",
13767
- rx: "10",
13768
- fill: "currentColor",
13769
- opacity: "0.4"
13770
- }
13771
- ),
13772
- /* @__PURE__ */ jsx160("circle", { cx: "68", cy: "52", r: "26", fill: "currentColor", opacity: "0.2" }),
13773
- /* @__PURE__ */ jsx160("circle", { cx: "248", cy: "140", r: "14", fill: "currentColor", opacity: "0.55" }),
13774
- /* @__PURE__ */ jsx160(
13775
- "path",
13776
- {
13777
- d: "M40 156 C 90 116, 130 150, 180 118 S 260 96, 300 128",
13778
- fill: "none",
13779
- stroke: "currentColor",
13780
- strokeWidth: "3",
13781
- strokeLinecap: "round",
13782
- opacity: "0.5"
13783
- }
13784
- )
13785
- ]
13786
- }
13787
- );
13788
- }
13789
- 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";
13790
- function BgDecor({ className }) {
13791
- return /* @__PURE__ */ jsxs102(
13792
- "div",
13793
- {
13794
- "data-slot": "login-bg-decor",
13795
- "aria-hidden": "true",
13796
- className: cn(
13797
- "pointer-events-none absolute inset-0 overflow-hidden",
13798
- className
13799
- ),
13800
- children: [
13801
- /* @__PURE__ */ jsx160("div", { className: "absolute -right-24 -top-24 size-96 rounded-full bg-primary/8" }),
13802
- /* @__PURE__ */ jsx160("div", { className: "absolute -left-32 bottom-0 size-80 rounded-full bg-primary/5" }),
13803
- /* @__PURE__ */ jsx160("div", { className: "absolute right-10 top-1/3 size-40 rounded-full border-[12px] border-primary/10" }),
13804
- /* @__PURE__ */ jsx160("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]" }),
13805
- /* @__PURE__ */ jsxs102(
13806
- "svg",
13807
- {
13808
- className: "absolute bottom-0 left-0 w-full text-primary",
13809
- viewBox: "0 0 1440 320",
13810
- preserveAspectRatio: "none",
13811
- children: [
13812
- /* @__PURE__ */ jsx160(
13813
- "path",
13814
- {
13815
- d: "M0,224 C240,160 480,288 720,240 C960,192 1200,96 1440,160",
13816
- fill: "none",
13817
- stroke: "currentColor",
13818
- strokeOpacity: "0.12",
13819
- strokeWidth: "2"
13820
- }
13821
- ),
13822
- /* @__PURE__ */ jsx160(
13823
- "path",
13824
- {
13825
- d: "M0,256 C240,192 480,320 720,272 C960,224 1200,128 1440,192 L1440,320 L0,320 Z",
13826
- fill: "currentColor",
13827
- fillOpacity: "0.05"
13828
- }
13829
- )
13830
- ]
13831
- }
13832
- )
13833
- ]
13834
- }
13835
- );
13836
- }
13837
- function Login({
13838
- layout = "split",
13839
- brandSide = "left",
13840
- brandRatio = 0.5,
13841
- authVariant = "card",
13842
- brandPanelBackground = true,
13843
- decor = true,
13844
- brand,
13845
- title,
13846
- subtitle,
13847
- usernameLabel,
13848
- usernamePlaceholder,
13849
- passwordLabel,
13850
- passwordPlaceholder,
13851
- submitText,
13852
- registerLink,
13853
- forgotLink,
13854
- footer,
13855
- children,
13856
- className,
13857
- style,
13858
- onSubmit,
13859
- onRegisterClick,
13860
- onForgotClick
13861
- }) {
13862
- const { t } = useI18n35();
13863
- const brandIdentity = brand && (brand.logo || brand.name) ? /* @__PURE__ */ jsxs102("div", { "data-slot": "login-brand-identity", className: "flex items-center gap-2", children: [
13864
- brand.logo ? /* @__PURE__ */ jsx160(
13865
- "img",
13866
- {
13867
- src: brand.logo,
13868
- alt: brand.name ?? "",
13869
- className: "size-7 rounded-md"
13870
- }
13871
- ) : null,
13872
- brand.name ? /* @__PURE__ */ jsx160("span", { className: "text-base font-semibold", children: brand.name }) : null
13873
- ] }) : null;
13874
- const brandVisual = (alignRight = false) => /* @__PURE__ */ jsxs102(
13875
- "div",
13876
- {
13877
- className: cn("flex w-full flex-col gap-4", alignRight && "text-right"),
13878
- children: [
13879
- brand?.title ? /* @__PURE__ */ jsx160("h2", { className: "text-3xl font-bold leading-tight tracking-tight", children: brand.title }) : null,
13880
- brand?.description ? /* @__PURE__ */ jsx160(
13881
- "p",
13882
- {
13883
- className: cn(
13884
- "max-w-md text-sm text-muted-foreground",
13885
- alignRight && "ml-auto"
13886
- ),
13887
- children: brand.description
13888
- }
13889
- ) : null,
13890
- brand?.illustration ? /* @__PURE__ */ jsx160(
13891
- "img",
13892
- {
13893
- src: brand.illustration,
13894
- alt: "",
13895
- className: cn(
13896
- "mt-4 w-full max-w-lg rounded-lg object-cover",
13897
- alignRight && "self-end"
13898
- )
13899
- }
13900
- ) : brand?.illustration === "" ? null : /* @__PURE__ */ jsx160(
13901
- DefaultIllustration,
13902
- {
13903
- className: cn("mt-4 w-full max-w-md", alignRight && "self-end")
13904
- }
13905
- )
13906
- ]
13907
- }
13908
- );
13909
- const renderAuxLink = (link, onClick) => {
13910
- if (!link) return null;
13911
- if (link.href) {
13912
- return /* @__PURE__ */ jsx160(Link, { href: link.href, text: link.text, className: "text-sm" });
13913
- }
13914
- return /* @__PURE__ */ jsx160(
13915
- "button",
13916
- {
13917
- type: "button",
13918
- className: "text-sm text-primary underline-offset-4 hover:underline",
13919
- onClick,
13920
- children: link.text
13921
- }
13922
- );
13923
- };
13924
- const authContent = /* @__PURE__ */ jsxs102(Fragment18, { children: [
13925
- /* @__PURE__ */ jsxs102("div", { className: "mb-6 flex flex-col gap-1.5", children: [
13926
- /* @__PURE__ */ jsx160("h1", { className: "text-2xl font-semibold tracking-tight", children: title ?? t("pactor.login.title") }),
13927
- subtitle ? /* @__PURE__ */ jsx160("p", { className: "text-sm text-muted-foreground", children: subtitle }) : null
13928
- ] }),
13929
- children ?? /* @__PURE__ */ jsxs102(Form, { name: "login", className: "flex flex-col gap-2", onSubmit, children: [
13930
- /* @__PURE__ */ jsx160(Field2, { label: usernameLabel ?? t("pactor.login.username"), required: true, children: /* @__PURE__ */ jsx160(
13931
- Input2,
13932
- {
13933
- name: "username",
13934
- size: "large",
13935
- placeholder: usernamePlaceholder ?? t("pactor.login.usernamePlaceholder"),
13936
- rules: [
13937
- { required: true, message: t("pactor.login.usernameRequired") }
13938
- ]
13939
- }
13940
- ) }),
13941
- /* @__PURE__ */ jsx160(Field2, { label: passwordLabel ?? t("pactor.login.password"), required: true, children: /* @__PURE__ */ jsx160(
13942
- Input2,
13943
- {
13944
- name: "password",
13945
- type: "password",
13946
- size: "large",
13947
- placeholder: passwordPlaceholder ?? t("pactor.login.passwordPlaceholder"),
13948
- rules: [
13949
- { required: true, message: t("pactor.login.passwordRequired") }
13950
- ]
13951
- }
13952
- ) }),
13953
- /* @__PURE__ */ jsx160(Field2, { children: /* @__PURE__ */ jsx160(
13954
- Button2,
13955
- {
13956
- submit: true,
13957
- variant: "primary",
13958
- size: "large",
13959
- text: submitText ?? t("pactor.login.submit"),
13960
- className: "mt-2 w-full"
13961
- }
13962
- ) })
13963
- ] }),
13964
- registerLink || forgotLink ? /* @__PURE__ */ jsxs102("div", { className: "mt-4 flex items-center justify-between gap-4", children: [
13965
- renderAuxLink(registerLink, onRegisterClick),
13966
- renderAuxLink(forgotLink, onForgotClick)
13967
- ] }) : null
13968
- ] });
13969
- const authBox = (variant) => {
13970
- if (variant === "plain") {
13971
- return /* @__PURE__ */ jsx160("div", { className: "w-full max-w-[360px]", children: authContent });
13972
- }
13973
- if (variant === "float") {
13974
- return /* @__PURE__ */ jsx160(Card2, { className: "w-full max-w-[440px] p-4 shadow-xl md:p-6", children: authContent });
13975
- }
13976
- return /* @__PURE__ */ jsx160(Card2, { className: "w-full max-w-[400px] p-2 md:p-4", children: authContent });
13977
- };
13978
- const footerBar = footer ? /* @__PURE__ */ jsx160("footer", { className: "p-4 text-center text-xs text-muted-foreground", children: footer }) : null;
13979
- const brandPanel = (extraClass, withIdentity = true) => /* @__PURE__ */ jsxs102(
13980
- "div",
13981
- {
13982
- className: cn(
13983
- "relative hidden flex-col overflow-hidden p-10 md:flex",
13984
- brandPanelBackground && BRAND_PANEL_BG,
13985
- extraClass
13986
- ),
13987
- children: [
13988
- decor ? /* @__PURE__ */ jsx160(BgDecor, {}) : null,
13989
- /* @__PURE__ */ jsxs102("div", { className: "relative flex h-full flex-col justify-between", children: [
13990
- withIdentity ? brandIdentity : null,
13991
- brandVisual(),
13992
- /* @__PURE__ */ jsx160("div", {})
13993
- ] })
13994
- ]
13995
- }
13996
- );
13997
- const authColumn = (variant, identityAlways = false) => /* @__PURE__ */ jsxs102("div", { className: "flex min-h-svh flex-col bg-linear-to-b from-primary/5 via-background to-background dark:from-primary/10", children: [
13998
- brandIdentity ? /* @__PURE__ */ jsx160("div", { className: cn("p-6", !identityAlways && "md:hidden"), children: brandIdentity }) : null,
13999
- /* @__PURE__ */ jsx160("div", { className: "flex flex-1 items-center justify-center p-6", children: authBox(variant) }),
14000
- footerBar
14001
- ] });
14002
- const brandCard = (widthClass, alignRight = false) => /* @__PURE__ */ jsx160(Card2, { className: cn("w-full", widthClass, BRAND_PANEL_BG), children: /* @__PURE__ */ jsxs102(
14003
- "div",
14004
- {
14005
- "data-slot": "login-brand-card-content",
14006
- className: cn("flex flex-col gap-6", alignRight && "text-right"),
14007
- children: [
14008
- brandIdentity ? /* @__PURE__ */ jsx160("div", { className: cn(alignRight && "flex justify-end"), children: brandIdentity }) : null,
14009
- brandVisual(alignRight)
14010
- ]
14011
- }
14012
- ) });
14013
- const overlapAuthCard = /* @__PURE__ */ jsx160(
14014
- "div",
14015
- {
14016
- className: cn(
14017
- "z-10 w-full max-w-[400px]",
14018
- brandSide === "left" ? "md:-ml-24" : "md:-mr-24"
14019
- ),
14020
- children: /* @__PURE__ */ jsx160(Card2, { className: "p-2 shadow-xl md:p-4", children: authContent })
14021
- }
14022
- );
14023
- const backdropBrandColumn = /* @__PURE__ */ jsxs102("div", { className: "relative hidden flex-col justify-between p-10 md:flex", children: [
14024
- brandIdentity,
14025
- brandVisual(),
14026
- /* @__PURE__ */ jsx160("div", {})
14027
- ] });
14028
- const backdropAuthColumn = /* @__PURE__ */ jsxs102("div", { className: "relative flex min-h-svh flex-col", children: [
14029
- brandIdentity ? /* @__PURE__ */ jsx160("div", { className: "p-6 md:hidden", children: brandIdentity }) : null,
14030
- /* @__PURE__ */ jsx160("div", { className: "flex flex-1 items-center justify-center p-6", children: /* @__PURE__ */ jsx160(Card2, { className: "w-full max-w-[400px] p-2 shadow-xl md:p-4", children: authContent }) }),
14031
- footerBar
14032
- ] });
14033
- if (layout === "centered") {
14034
- return /* @__PURE__ */ jsxs102(
14035
- "div",
14036
- {
14037
- "data-slot": "login",
14038
- "data-layout": layout,
14039
- className: cn(
14040
- "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",
14041
- className
14042
- ),
14043
- style,
14044
- children: [
14045
- decor ? /* @__PURE__ */ jsx160(BgDecor, {}) : null,
14046
- /* @__PURE__ */ jsxs102("div", { className: "relative flex flex-1 flex-col items-center justify-center gap-6 p-6", children: [
14047
- brandIdentity,
14048
- authBox(authVariant)
14049
- ] }),
14050
- footerBar
14051
- ]
14052
- }
14053
- );
14054
- }
14055
- if (layout === "brand-card") {
14056
- return /* @__PURE__ */ jsxs102(
14057
- "div",
14058
- {
14059
- "data-slot": "login",
14060
- "data-layout": layout,
14061
- "data-brand-side": brandSide,
14062
- className: cn(
14063
- "relative flex min-h-svh w-full flex-col overflow-hidden bg-background p-6 md:p-10",
14064
- className
14065
- ),
14066
- style,
14067
- children: [
14068
- decor ? /* @__PURE__ */ jsx160(BgDecor, {}) : null,
14069
- /* @__PURE__ */ jsx160("div", { className: "relative flex flex-1 flex-col items-center gap-6 md:flex-row md:gap-10", children: brandSide === "left" ? /* @__PURE__ */ jsxs102(Fragment18, { children: [
14070
- brandCard("md:w-[45%]"),
14071
- /* @__PURE__ */ jsx160("div", { className: "flex w-full flex-1 justify-center", children: authBox("plain") })
14072
- ] }) : /* @__PURE__ */ jsxs102(Fragment18, { children: [
14073
- /* @__PURE__ */ jsx160("div", { className: "flex w-full flex-1 justify-center", children: authBox("plain") }),
14074
- brandCard("md:w-[45%]")
14075
- ] }) }),
14076
- footerBar
14077
- ]
14078
- }
14079
- );
14080
- }
14081
- if (layout === "overlap") {
14082
- return /* @__PURE__ */ jsxs102(
14083
- "div",
14084
- {
14085
- "data-slot": "login",
14086
- "data-layout": layout,
14087
- "data-brand-side": brandSide,
14088
- className: cn(
14089
- "relative flex min-h-svh w-full flex-col overflow-hidden bg-background p-6 md:p-10",
14090
- className
14091
- ),
14092
- style,
14093
- children: [
14094
- decor ? /* @__PURE__ */ jsx160(BgDecor, {}) : null,
14095
- /* @__PURE__ */ jsx160("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__ */ jsxs102(Fragment18, { children: [
14096
- brandCard("md:w-[52%]"),
14097
- overlapAuthCard
14098
- ] }) : /* @__PURE__ */ jsxs102(Fragment18, { children: [
14099
- overlapAuthCard,
14100
- brandCard("md:w-[52%]", true)
14101
- ] }) }),
14102
- footerBar
14103
- ]
14104
- }
14105
- );
14106
- }
14107
- if (layout === "backdrop") {
14108
- return /* @__PURE__ */ jsxs102(
14109
- "div",
14110
- {
14111
- "data-slot": "login",
14112
- "data-layout": layout,
14113
- "data-brand-side": brandSide,
14114
- className: cn(
14115
- "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",
14116
- className
14117
- ),
14118
- style,
14119
- children: [
14120
- decor ? /* @__PURE__ */ jsx160(BgDecor, {}) : null,
14121
- brandSide === "left" ? /* @__PURE__ */ jsxs102(Fragment18, { children: [
14122
- backdropBrandColumn,
14123
- backdropAuthColumn
14124
- ] }) : /* @__PURE__ */ jsxs102(Fragment18, { children: [
14125
- backdropAuthColumn,
14126
- backdropBrandColumn
14127
- ] })
14128
- ]
14129
- }
14130
- );
14131
- }
14132
- const ratio = Math.min(Math.max(brandRatio, 0.2), 0.8);
14133
- const rootStyle = {
14134
- "--login-grid": `minmax(0, ${ratio}fr) minmax(0, ${1 - ratio}fr)`,
14135
- ...style
14136
- };
14137
- return /* @__PURE__ */ jsx160(
14138
- "div",
14139
- {
14140
- "data-slot": "login",
14141
- "data-layout": layout,
14142
- "data-brand-side": brandSide,
14143
- className: cn(
14144
- "grid min-h-svh w-full bg-background md:[grid-template-columns:var(--login-grid)]",
14145
- className
14146
- ),
14147
- style: rootStyle,
14148
- children: brandSide === "left" ? /* @__PURE__ */ jsxs102(Fragment18, { children: [
14149
- brandPanel(),
14150
- authColumn(authVariant)
14151
- ] }) : /* @__PURE__ */ jsxs102(Fragment18, { children: [
14152
- authColumn(authVariant, true),
14153
- brandPanel("order-last", false)
14154
- ] })
14155
- }
14156
- );
14157
- }
14158
-
14159
- // src/components/business/RuleConfig/index.tsx
14160
- import { Plus as Plus2, Trash2 } from "lucide-react";
14161
- import { useCallback as useCallback5, useEffect as useEffect43, useMemo as useMemo11, useRef as useRef16, useState as useState51 } from "react";
14162
- import { jsx as jsx161, jsxs as jsxs103 } from "react/jsx-runtime";
14163
- var isBlank = (v) => v == null || String(v).trim() === "";
14164
- function isValueMissing(condition) {
14165
- const { opt, value } = condition;
14166
- if (NO_VALUE_OPT.includes(opt)) return false;
14167
- if (opt === "BETWEEN") {
14168
- const [a, b] = normalizeBetweenValue(value);
14169
- return isBlank(a) || isBlank(b);
14170
- }
14171
- if (Array.isArray(value)) return value.length === 0 || value.every(isBlank);
14172
- return isBlank(value);
14173
- }
14174
- function getConditionIssue(condition) {
14175
- const missingValue = isValueMissing(condition);
14176
- const missingScore = condition.score == null || Number.isNaN(condition.score);
14177
- if (!missingValue && !missingScore) return null;
14178
- return { missingValue, missingScore };
14179
- }
14180
- var OPERATOR_VALUES = [
14181
- "GREATER",
14182
- "GREATER_EQUAL",
14183
- "LESS_EQUAL",
14184
- "LESS",
14185
- "EQUAL",
14186
- "IN",
14187
- "NOT_IN",
14188
- "UNEQUAL",
14189
- "LIKE",
14190
- "NULL",
14191
- "HASVAL",
14192
- "NOTNULL",
14193
- "BETWEEN"
14194
- ];
14195
- var NUMERIC_OPT = [
14196
- "GREATER",
14197
- "LESS",
14198
- "LESS_EQUAL",
14199
- "GREATER_EQUAL",
14200
- "UNEQUAL"
14201
- ];
14202
- var NO_VALUE_OPT = ["NULL", "HASVAL", "NOTNULL"];
14203
- var MULTI_VALUE_OPT = ["IN", "NOT_IN"];
14204
- var OPERATOR_LABELS2 = {
14205
- GREATER: "\u5927\u4E8E",
14206
- GREATER_EQUAL: "\u5927\u4E8E\u7B49\u4E8E",
14207
- LESS_EQUAL: "\u5C0F\u4E8E\u7B49\u4E8E",
14208
- LESS: "\u5C0F\u4E8E",
14209
- EQUAL: "\u7B49\u4E8E",
14210
- IN: "\u5305\u542B",
14211
- NOT_IN: "\u4E0D\u5305\u542B",
14212
- UNEQUAL: "\u4E0D\u7B49\u4E8E",
14213
- LIKE: "\u7C7B\u4F3C",
14214
- NULL: "\u4E3A\u7A7A",
14215
- HASVAL: "\u6709\u503C",
14216
- NOTNULL: "\u4E0D\u4E3A\u7A7A",
14217
- BETWEEN: "\u8303\u56F4"
14218
- };
14219
- function operatorLabel2(operator) {
14220
- return OPERATOR_LABELS2[operator] ?? operator;
14221
- }
14222
- var _key = 0;
14223
- var nextKey = () => ++_key;
14224
- var defaultCondition = () => ({ key: nextKey(), opt: "EQUAL" });
14225
- function normalizeBetweenValue(value) {
14226
- if (Array.isArray(value)) return [value[0], value[1]];
14227
- if (typeof value === "string" && value.includes(",")) {
14228
- const [a, b] = value.split(",");
14229
- return [a?.trim(), b?.trim()];
14230
- }
14231
- if (value == null || value === "") return [void 0, void 0];
14232
- return [value, void 0];
14233
- }
14234
- function formatConditionValue2(condition) {
14235
- const { opt, value } = condition;
14236
- if (value == null) return "-";
14237
- if (opt === "BETWEEN") {
14238
- const [a, b] = normalizeBetweenValue(value);
14239
- return `${a ?? "-"} ~ ${b ?? "-"}`;
14240
- }
14241
- if (Array.isArray(value)) return value.join(", ");
14242
- return String(value);
14243
- }
14244
- function RuleConfig({ value, onChange, disabled, size = "middle" }) {
14245
- const rawConditions = value?.ruleConditions;
14246
- const conditions = useMemo11(() => {
14247
- if (!rawConditions?.length) return [defaultCondition()];
14248
- const seen = /* @__PURE__ */ new Set();
14249
- return rawConditions.map((c) => {
14250
- if (c.key != null && !seen.has(c.key)) {
14251
- seen.add(c.key);
14252
- return c;
14253
- }
14254
- const key = nextKey();
14255
- seen.add(key);
14256
- return { ...c, key };
14257
- });
14258
- }, [rawConditions]);
14259
- const initScore = value?.score ?? 1;
14260
- const [scoreText, setScoreText] = useState51(String(initScore));
14261
- const lastScoreRef = useRef16(value?.score);
14262
- useEffect43(() => {
14263
- if (value?.score === lastScoreRef.current) return;
14264
- lastScoreRef.current = value?.score;
14265
- setScoreText(value?.score == null ? "" : String(value.score));
14266
- }, [value?.score]);
14267
- const commitScore = useCallback5(
14268
- (score) => {
14269
- lastScoreRef.current = score;
14270
- onChange?.({ score, ruleConditions: conditions });
14271
- },
14272
- [onChange, conditions]
14273
- );
14274
- const normalizeScore = (n) => Math.min(1e3, Math.max(1, Math.round(n)));
14275
- const handleScoreTextChange = (text) => {
14276
- setScoreText(text);
14277
- if (text === "") return;
14278
- const n = Number(text);
14279
- if (Number.isFinite(n) && n >= 1 && n <= 1e3 && Number.isInteger(n)) {
14280
- commitScore(n);
14281
- }
14282
- };
14283
- const handleScoreBlur = () => {
14284
- const n = scoreText.trim() === "" ? NaN : Number(scoreText);
14285
- const next = Number.isFinite(n) ? normalizeScore(n) : 1;
14286
- setScoreText(String(next));
14287
- if (next !== lastScoreRef.current) commitScore(next);
14288
- };
14289
- const update = useCallback5(
14290
- (next, nextScore) => onChange?.({
14291
- score: nextScore ?? initScore,
14292
- ruleConditions: next
14293
- }),
14294
- [onChange, initScore]
14295
- );
14296
- const handleOptChange = (key, opt) => {
14297
- update(
14298
- conditions.map(
14299
- (c) => c.key === key ? { ...c, opt, value: opt === "BETWEEN" ? [] : void 0 } : c
14300
- )
14301
- );
14302
- };
14303
- const handleValueChange = (key, val) => {
14304
- update(conditions.map((c) => c.key === key ? { ...c, value: val } : c));
14305
- };
14306
- const handleScoreChange = (key, score) => {
14307
- update(
14308
- conditions.map((c) => c.key === key ? { ...c, score } : c)
14309
- );
14310
- };
14311
- const handleAdd = () => update([...conditions, defaultCondition()]);
14312
- const handleDelete = (key) => update(conditions.filter((c) => c.key !== key));
14313
- if (disabled) {
14314
- return /* @__PURE__ */ jsxs103("div", { className: "space-y-2 text-sm", children: [
14315
- /* @__PURE__ */ jsxs103("div", { className: "flex items-center gap-2 pb-2 border-b", children: [
14316
- /* @__PURE__ */ jsx161("span", { className: "text-muted-foreground", children: "\u6743\u91CD" }),
14317
- /* @__PURE__ */ jsx161("span", { className: "font-medium", children: initScore })
14318
- ] }),
14319
- conditions.map((condition) => /* @__PURE__ */ jsxs103("div", { className: "flex items-center gap-2", children: [
14320
- /* @__PURE__ */ jsx161("span", { children: operatorLabel2(condition.opt) }),
14321
- !NO_VALUE_OPT.includes(condition.opt) && /* @__PURE__ */ jsx161("span", { className: "font-medium", children: formatConditionValue2(condition) }),
14322
- /* @__PURE__ */ jsxs103("span", { className: "text-muted-foreground", children: [
14323
- "\u8BC4\u5206: ",
14324
- condition.score ?? "-"
14325
- ] })
14326
- ] }, condition.key))
14327
- ] });
14328
- }
14329
- return /* @__PURE__ */ jsxs103("div", { className: "space-y-2", children: [
14330
- /* @__PURE__ */ jsxs103("div", { className: "flex items-center gap-1 pb-2 border-b gap-2", children: [
14331
- /* @__PURE__ */ jsx161("span", { className: "text-sm shrink-0", children: "\u6743\u91CD" }),
14332
- /* @__PURE__ */ jsx161(
14333
- Input,
14334
- {
14335
- type: "number",
14336
- className: cn("w-48", CONTROL_SIZE_BOX[size]),
14337
- min: 1,
14338
- max: 1e3,
14339
- step: 1,
14340
- value: scoreText,
14341
- disabled,
14342
- placeholder: "\u8BF7\u8F93\u5165\u6743\u91CD",
14343
- onChange: (e) => handleScoreTextChange(e.target.value),
14344
- onBlur: handleScoreBlur
14345
- }
14346
- )
14347
- ] }),
14348
- conditions.map((condition) => /* @__PURE__ */ jsx161(
14349
- ConditionRow2,
14350
- {
14351
- condition,
14352
- issue: getConditionIssue(condition),
14353
- showDelete: conditions.length > 1,
14354
- disabled,
14355
- size,
14356
- onOptChange: (opt) => handleOptChange(condition.key, opt),
14357
- onValueChange: (val) => handleValueChange(condition.key, val),
14358
- onScoreChange: (score) => handleScoreChange(condition.key, score),
14359
- onDelete: () => handleDelete(condition.key)
14360
- },
14361
- condition.key
14362
- )),
14363
- !disabled && /* @__PURE__ */ jsxs103(
14364
- Button,
14365
- {
14366
- type: "button",
14367
- variant: "link",
14368
- size: "sm",
14369
- className: "h-auto p-0 text-sm mt-1",
14370
- onClick: handleAdd,
14371
- children: [
14372
- /* @__PURE__ */ jsx161(Plus2, { className: "h-3.5 w-3.5" }),
14373
- "\u6DFB\u52A0\u6761\u4EF6"
14374
- ]
14375
- }
14376
- )
14377
- ] });
14378
- }
14379
- function ConditionRow2({
14380
- condition,
14381
- issue,
14382
- showDelete,
14383
- disabled,
14384
- size,
14385
- onOptChange,
14386
- onValueChange,
14387
- onScoreChange,
14388
- onDelete
14389
- }) {
14390
- const { opt, value, score } = condition;
14391
- const missingValue = !disabled && !!issue?.missingValue;
14392
- const missingScore = !disabled && !!issue?.missingScore;
14393
- return /* @__PURE__ */ jsxs103("div", { className: "space-y-1", children: [
14394
- /* @__PURE__ */ jsxs103("div", { className: "flex items-center gap-2", children: [
14395
- /* @__PURE__ */ jsxs103(
14396
- Select,
14397
- {
14398
- items: OPERATOR_VALUES.map((op) => ({
14399
- value: op,
14400
- label: operatorLabel2(op)
14401
- })),
14402
- value: opt,
14403
- onValueChange: (v) => onOptChange(v),
14404
- disabled,
14405
- children: [
14406
- /* @__PURE__ */ jsx161(
14407
- SelectTrigger,
14408
- {
14409
- size: CONTROL_SIZE_SELECT_ATTR[size],
14410
- className: cn("w-36 shrink-0", CONTROL_SIZE_TRIGGER_TEXT[size]),
14411
- children: /* @__PURE__ */ jsx161(SelectValue, {})
14412
- }
14413
- ),
14414
- /* @__PURE__ */ jsx161(SelectContent, { children: OPERATOR_VALUES.map((op) => /* @__PURE__ */ jsx161(SelectItem, { value: op, children: operatorLabel2(op) }, op)) })
14415
- ]
14416
- }
14417
- ),
14418
- !NO_VALUE_OPT.includes(opt) && /* @__PURE__ */ jsx161(
14419
- "div",
14420
- {
14421
- className: "w-[30%]" + (missingValue ? " [&_input]:border-destructive" : ""),
14422
- children: /* @__PURE__ */ jsx161(
14423
- ValueInput,
14424
- {
14425
- opt,
14426
- value,
14427
- disabled,
14428
- size,
14429
- onChange: onValueChange
14430
- }
14431
- )
14432
- }
14433
- ),
14434
- /* @__PURE__ */ jsx161(
14435
- Input,
14436
- {
14437
- type: "number",
14438
- className: cn(
14439
- "w-24 shrink-0",
14440
- CONTROL_SIZE_BOX[size],
14441
- missingScore && "border-destructive"
14442
- ),
14443
- placeholder: "\u8BC4\u5206",
14444
- value: score ?? "",
14445
- disabled,
14446
- "aria-invalid": missingScore || void 0,
14447
- onChange: (e) => onScoreChange(
14448
- e.target.value === "" ? void 0 : e.target.valueAsNumber
14449
- )
14450
- }
14451
- ),
14452
- showDelete && !disabled && /* @__PURE__ */ jsx161(
14453
- Button,
14454
- {
14455
- type: "button",
14456
- variant: "ghost",
14457
- size: "icon",
14458
- className: "h-8 w-8 shrink-0 text-muted-foreground hover:text-destructive",
14459
- onClick: onDelete,
14460
- children: /* @__PURE__ */ jsx161(Trash2, { className: "h-4 w-4" })
14461
- }
14462
- )
14463
- ] }),
14464
- !disabled && issue && /* @__PURE__ */ jsx161("p", { className: "text-xs text-destructive", children: [
14465
- missingValue ? "\u8BF7\u586B\u5199\u53D6\u503C" : null,
14466
- missingScore ? "\u8BF7\u586B\u5199\u8BC4\u5206" : null
14467
- ].filter(Boolean).join("\uFF1B") })
14468
- ] });
14469
- }
14470
- function ValueInput({ opt, value, disabled, size, onChange }) {
14471
- if (NO_VALUE_OPT.includes(opt)) {
14472
- return null;
14473
- }
14474
- if (opt === "BETWEEN") {
14475
- const [v1, v2] = normalizeBetweenValue(value);
14476
- return /* @__PURE__ */ jsxs103("div", { className: "flex items-center gap-2", children: [
14477
- /* @__PURE__ */ jsx161(
14478
- Input,
14479
- {
14480
- type: "number",
14481
- className: cn("flex-1", CONTROL_SIZE_BOX[size]),
14482
- value: v1 ?? "",
14483
- disabled,
14484
- onChange: (e) => onChange([e.target.valueAsNumber, v2])
14485
- }
14486
- ),
14487
- /* @__PURE__ */ jsx161("span", { className: "text-muted-foreground text-sm shrink-0", children: "~" }),
14488
- /* @__PURE__ */ jsx161(
14489
- Input,
14490
- {
14491
- type: "number",
14492
- className: cn("flex-1", CONTROL_SIZE_BOX[size]),
14493
- value: v2 ?? "",
14494
- disabled,
14495
- onChange: (e) => onChange([v1, e.target.valueAsNumber])
14496
- }
14497
- )
14498
- ] });
14499
- }
14500
- if (MULTI_VALUE_OPT.includes(opt)) {
14501
- return /* @__PURE__ */ jsx161(
14502
- TagInput,
14503
- {
14504
- value: value ?? [],
14505
- disabled,
14506
- size,
14507
- onChange: (tags) => onChange(tags)
14508
- }
14509
- );
14510
- }
14511
- if (NUMERIC_OPT.includes(opt)) {
14512
- return /* @__PURE__ */ jsx161(
14513
- Input,
14514
- {
14515
- type: "number",
14516
- className: CONTROL_SIZE_BOX[size],
14517
- value: value ?? "",
14518
- disabled,
14519
- onChange: (e) => onChange(e.target.valueAsNumber)
14520
- }
14521
- );
14522
- }
14523
- return /* @__PURE__ */ jsx161(
14524
- Input,
14525
- {
14526
- type: "text",
14527
- className: CONTROL_SIZE_BOX[size],
14528
- value: value ?? "",
14529
- disabled,
14530
- onChange: (e) => onChange(e.target.value)
14531
- }
14532
- );
14533
- }
14534
- function TagInput({
14535
- value,
14536
- disabled,
14537
- size,
14538
- onChange
14539
- }) {
14540
- const [input, setInput] = useState51("");
14541
- const addTag = () => {
14542
- const tag = input.trim();
14543
- if (tag && !value.includes(tag)) {
14544
- onChange([...value, tag]);
14545
- }
14546
- setInput("");
14547
- };
14548
- const removeTag = (tag) => onChange(value.filter((t) => t !== tag));
14549
- return /* @__PURE__ */ jsxs103(
14550
- "div",
14551
- {
14552
- className: cn(
14553
- "flex flex-wrap items-center gap-1 min-h-9 rounded-md border bg-transparent px-3 py-1 text-sm",
14554
- CONTROL_SIZE_BOX_GROW[size]
14555
- ),
14556
- children: [
14557
- " ",
14558
- value.map((tag) => /* @__PURE__ */ jsxs103(Badge, { variant: "secondary", className: "gap-1 pr-1", children: [
14559
- tag,
14560
- !disabled && /* @__PURE__ */ jsx161(
14561
- "button",
14562
- {
14563
- type: "button",
14564
- className: "text-muted-foreground hover:text-foreground",
14565
- onClick: () => removeTag(tag),
14566
- children: "\xD7"
14567
- }
14568
- )
14569
- ] }, tag)),
14570
- !disabled && /* @__PURE__ */ jsx161(
14571
- "input",
14572
- {
14573
- className: "flex-1 min-w-16 bg-transparent outline-none placeholder:text-muted-foreground",
14574
- value: input,
14575
- placeholder: "\u8F93\u5165\u540E\u6309 Enter",
14576
- onChange: (e) => setInput(e.target.value),
14577
- onKeyDown: (e) => {
14578
- if (e.key === "Enter") {
14579
- e.preventDefault();
14580
- addTag();
14581
- }
14582
- }
14583
- }
14584
- )
14585
- ]
14586
- }
14587
- );
14588
- }
14589
-
14590
- // src/components/business/RuleExpression/index.tsx
14591
- import { useLayoutEffect as useLayoutEffect3, useRef as useRef17, useState as useState52 } from "react";
14592
- import { jsx as jsx162, jsxs as jsxs104 } from "react/jsx-runtime";
14593
- function trailingWord(text) {
14594
- const m = text.match(/[\w一-龥]+$/u);
14595
- return m ? m[0] : "";
14596
- }
14597
- function labelSuffixLength(input, label) {
14598
- for (let len = Math.min(input.length, label.length); len > 0; len--) {
14599
- const suffix = input.slice(input.length - len);
14600
- if (label.includes(suffix)) return len;
14601
- }
14602
- return 0;
14603
- }
14604
- function serialize(tokens, fieldKey = "value") {
14605
- let out = "";
14606
- tokens.forEach((t, i) => {
14607
- if (t.type === "field") {
14608
- if (out && !out.endsWith(" ")) out += " ";
14609
- out += t[fieldKey];
14610
- } else {
14611
- if (i === tokens.length - 1) {
14612
- out += t.text;
14613
- return;
14614
- }
14615
- const text = t.text.trim();
14616
- if (!text) return;
14617
- if (out && !out.endsWith(" ")) out += " ";
14618
- out += `${text} `;
14619
- }
14620
- });
14621
- return out;
14622
- }
14623
- function seedTokens(value, options) {
14624
- if (!value) return [];
14625
- if (typeof value === "string") {
14626
- return value ? [{ type: "text", text: value }] : [];
14627
- }
14628
- const fields = (value.sourceField || []).map((v) => ({
14629
- type: "field",
14630
- value: String(v),
14631
- label: options.find((o) => o.value === String(v))?.label ?? String(v)
14632
- }));
14633
- const tokens = [];
14634
- let rest = value.expression ?? "";
14635
- for (const field of fields) {
14636
- const idx = rest.indexOf(field.value);
14637
- if (idx === -1) {
14638
- tokens.push(field);
14639
- continue;
14640
- }
14641
- const before = rest.slice(0, idx);
14642
- if (before) tokens.push({ type: "text", text: before });
14643
- tokens.push(field);
14644
- rest = rest.slice(idx + field.value.length);
14645
- }
14646
- if (rest) tokens.push({ type: "text", text: rest });
14647
- return tokens;
14648
- }
14649
- 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>';
14650
- function buildChip(field, disabled) {
14651
- const chip = document.createElement("span");
14652
- chip.contentEditable = "false";
14653
- chip.dataset.value = field.value;
14654
- chip.dataset.label = field.label;
14655
- 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";
14656
- const label = document.createElement("span");
14657
- label.textContent = field.label;
14658
- chip.appendChild(label);
14659
- if (!disabled) {
14660
- const btn = document.createElement("button");
14661
- btn.type = "button";
14662
- btn.dataset.remove = "true";
14663
- btn.setAttribute("aria-label", `remove-${field.value}`);
14664
- btn.className = "cursor-pointer opacity-60 hover:opacity-100";
14665
- btn.innerHTML = X_SVG;
14666
- chip.appendChild(btn);
14667
- }
14668
- return chip;
14669
- }
14670
- function parseEditor(root) {
14671
- const tokens = [];
14672
- const pushText = (text) => {
14673
- if (!text) return;
14674
- const last = tokens[tokens.length - 1];
14675
- if (last?.type === "text") last.text += text;
14676
- else tokens.push({ type: "text", text });
14677
- };
14678
- const walk = (node) => {
14679
- if (node.nodeType === Node.TEXT_NODE) {
14680
- pushText(node.textContent ?? "");
14681
- return;
14682
- }
14683
- if (!(node instanceof HTMLElement)) return;
14684
- if (node.dataset.value != null) {
14685
- tokens.push({
14686
- type: "field",
14687
- value: node.dataset.value,
14688
- label: node.dataset.label ?? node.dataset.value
14689
- });
14690
- return;
14691
- }
14692
- if (node.tagName === "BR") {
14693
- pushText("\n");
14694
- return;
14695
- }
14696
- node.childNodes.forEach(walk);
14697
- if (node.tagName === "DIV" || node.tagName === "P") pushText("\n");
14698
- };
14699
- root.childNodes.forEach(walk);
14700
- return tokens;
14701
- }
14702
- function setCaret(node, offset) {
14703
- const sel = window.getSelection();
14704
- if (!sel) return;
14705
- const range = document.createRange();
14706
- range.setStart(node, offset);
14707
- range.collapse(true);
14708
- sel.removeAllRanges();
14709
- sel.addRange(range);
14710
- }
14711
- function setCaretToEnd(el) {
14712
- const sel = window.getSelection();
14713
- if (!sel) return;
14714
- const range = document.createRange();
14715
- range.selectNodeContents(el);
14716
- range.collapse(false);
14717
- sel.removeAllRanges();
14718
- sel.addRange(range);
14719
- }
14720
- function RuleExpression({
14721
- options = [],
14722
- value,
14723
- onChange,
14724
- placeholder,
14725
- disabled,
14726
- size = "middle",
14727
- id,
14728
- className
14729
- }) {
14730
- const editorRef = useRef17(null);
14731
- const [focused, setFocused] = useState52(false);
14732
- const [activeIndex, setActiveIndex] = useState52(0);
14733
- const [matchText, setMatchText] = useState52("");
14734
- const lastEmittedRef = useRef17(null);
14735
- const matched = matchText ? options.filter((o) => o.label.toLowerCase().includes(matchText.toLowerCase())) : [];
14736
- const showSuggestions = focused && !disabled && matched.length > 0;
14737
- const emitTokens = (tokens) => {
14738
- const out = {
14739
- expression: serialize(tokens),
14740
- expressionLabel: serialize(tokens, "label"),
14741
- sourceField: tokens.filter((t) => t.type === "field").map((t) => t.value)
14742
- };
14743
- lastEmittedRef.current = JSON.stringify(out);
14744
- onChange?.(out);
14745
- };
14746
- const caretBeforeText = () => {
14747
- const ed = editorRef.current;
14748
- const sel = window.getSelection();
14749
- if (!ed || !sel || !sel.rangeCount) return "";
14750
- const node = sel.anchorNode;
14751
- if (!node || !ed.contains(node)) return "";
14752
- if (node === ed) {
14753
- const prev = ed.childNodes[sel.anchorOffset - 1];
14754
- return prev?.nodeType === Node.TEXT_NODE ? prev.textContent ?? "" : "";
14755
- }
14756
- if (node.nodeType === Node.TEXT_NODE) {
14757
- return (node.textContent ?? "").slice(0, sel.anchorOffset);
14758
- }
14759
- return "";
14760
- };
14761
- const refreshSuggest = () => {
14762
- setMatchText(trailingWord(caretBeforeText()));
14763
- };
14764
- const syncFromDOM = () => {
14765
- const ed = editorRef.current;
14766
- if (!ed) return;
14767
- emitTokens(parseEditor(ed));
14768
- refreshSuggest();
14769
- };
14770
- const rebuild = (tokens) => {
14771
- const ed = editorRef.current;
14772
- if (!ed) return;
14773
- ed.textContent = "";
14774
- for (const t of tokens) {
14775
- if (t.type === "field") ed.appendChild(buildChip(t, disabled));
14776
- else ed.appendChild(document.createTextNode(t.text));
14777
- }
14778
- };
14779
- useLayoutEffect3(() => {
14780
- const sig = JSON.stringify(value ?? null);
14781
- if (sig !== lastEmittedRef.current) {
14782
- rebuild(seedTokens(value, options));
14783
- lastEmittedRef.current = sig;
14784
- setMatchText("");
14785
- }
14786
- }, [value, options, disabled]);
14787
- const selectOption = (opt) => {
14788
- const ed = editorRef.current;
14789
- const sel = window.getSelection();
14790
- if (!ed || !sel || !sel.rangeCount) return;
14791
- let node = sel.anchorNode;
14792
- let offset = sel.anchorOffset;
14793
- if (!node || !ed.contains(node)) return;
14794
- if (node === ed) {
14795
- const tn = document.createTextNode("");
14796
- ed.insertBefore(tn, ed.childNodes[offset] ?? null);
14797
- node = tn;
14798
- offset = 0;
14799
- }
14800
- if (node.nodeType !== Node.TEXT_NODE) return;
14801
- const textNode = node;
14802
- const text = textNode.textContent ?? "";
14803
- const before = text.slice(0, offset);
14804
- const after = text.slice(offset);
14805
- const replaceLen = Math.max(
14806
- labelSuffixLength(before, opt.label),
14807
- trailingWord(before).length
14808
- );
14809
- textNode.textContent = before.slice(0, before.length - replaceLen);
14810
- const afterNode = document.createTextNode(after);
14811
- const chip = buildChip({ type: "field", label: opt.label, value: opt.value }, disabled);
14812
- ed.insertBefore(chip, textNode.nextSibling);
14813
- ed.insertBefore(afterNode, chip.nextSibling);
14814
- if (!textNode.textContent) ed.removeChild(textNode);
14815
- ed.focus();
14816
- setCaret(afterNode, 0);
14817
- setActiveIndex(0);
14818
- syncFromDOM();
14819
- };
14820
- const removeChip = (chip) => {
14821
- const ed = editorRef.current;
14822
- if (!ed) return;
14823
- const prev = chip.previousSibling;
14824
- const caretNode = prev && prev.nodeType === Node.TEXT_NODE ? prev : document.createTextNode("");
14825
- if (!caretNode.isConnected) ed.insertBefore(caretNode, chip);
14826
- const caretPos = caretNode.textContent?.length ?? 0;
14827
- ed.removeChild(chip);
14828
- ed.normalize();
14829
- ed.focus();
14830
- if (caretNode.isConnected) setCaret(caretNode, caretPos);
14831
- else setCaretToEnd(ed);
14832
- syncFromDOM();
14833
- };
14834
- const tryRemoveAdjacentChip = (dir) => {
14835
- const ed = editorRef.current;
14836
- const sel = window.getSelection();
14837
- if (!ed || !sel || !sel.rangeCount || !sel.isCollapsed) return false;
14838
- const node = sel.anchorNode;
14839
- if (!node || !ed.contains(node)) return false;
14840
- let target = null;
14841
- if (node === ed) {
14842
- target = dir === "before" ? ed.childNodes[sel.anchorOffset - 1] : ed.childNodes[sel.anchorOffset];
14843
- } else if (node.nodeType === Node.TEXT_NODE) {
14844
- const len = node.textContent?.length ?? 0;
14845
- const atEdge = dir === "before" ? sel.anchorOffset === 0 : sel.anchorOffset === len;
14846
- if (atEdge) {
14847
- target = dir === "before" ? node.previousSibling : node.nextSibling;
14848
- }
14849
- }
14850
- if (target instanceof HTMLElement && target.dataset.value != null) {
14851
- removeChip(target);
14852
- return true;
14853
- }
14854
- return false;
14855
- };
14856
- const handleKeyDown = (e) => {
14857
- if (e.key === "ArrowDown" && showSuggestions) {
14858
- e.preventDefault();
14859
- setActiveIndex((x) => (x + 1) % matched.length);
14860
- return;
14861
- }
14862
- if (e.key === "ArrowUp" && showSuggestions) {
14863
- e.preventDefault();
14864
- setActiveIndex((x) => (x - 1 + matched.length) % matched.length);
14865
- return;
14866
- }
14867
- if (e.key === "Enter") {
14868
- if (showSuggestions) {
14869
- e.preventDefault();
14870
- selectOption(matched[Math.min(activeIndex, matched.length - 1)]);
14871
- }
14872
- return;
14873
- }
14874
- if (e.key === "Backspace" && tryRemoveAdjacentChip("before")) {
14875
- e.preventDefault();
14876
- return;
14877
- }
14878
- if (e.key === "Delete" && tryRemoveAdjacentChip("after")) {
14879
- e.preventDefault();
14880
- }
14881
- };
14882
- return /* @__PURE__ */ jsxs104(
14883
- "div",
14884
- {
14885
- className: cn("relative", className),
14886
- onBlur: (e) => {
14887
- if (!e.currentTarget.contains(e.relatedTarget)) {
14888
- setFocused(false);
14889
- }
14890
- },
14891
- children: [
14892
- /* @__PURE__ */ jsx162(
14893
- "div",
14894
- {
14895
- ref: editorRef,
14896
- id,
14897
- contentEditable: !disabled,
14898
- suppressContentEditableWarning: true,
14899
- role: "textbox",
14900
- "aria-multiline": "true",
14901
- "data-placeholder": placeholder,
14902
- className: cn(
14903
- "min-h-9 w-full whitespace-pre-wrap break-words rounded-md border border-input bg-background px-3 py-2 text-sm",
14904
- CONTROL_SIZE_BOX_GROW[size],
14905
- "focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2",
14906
- "empty:before:pointer-events-none empty:before:text-muted-foreground empty:before:content-[attr(data-placeholder)]",
14907
- disabled && "cursor-not-allowed opacity-50"
14908
- ),
14909
- onFocus: () => {
14910
- setFocused(true);
14911
- refreshSuggest();
14912
- },
14913
- onInput: syncFromDOM,
14914
- onKeyDown: handleKeyDown,
14915
- onKeyUp: refreshSuggest,
14916
- onClick: (e) => {
14917
- const btn = e.target.closest?.("[data-remove]");
14918
- if (btn) {
14919
- e.preventDefault();
14920
- const chip = btn.closest("[data-value]");
14921
- if (chip instanceof HTMLElement) removeChip(chip);
14922
- return;
14923
- }
14924
- refreshSuggest();
14925
- }
14926
- }
14927
- ),
14928
- showSuggestions && /* @__PURE__ */ jsx162(
14929
- "ul",
14930
- {
14931
- role: "list",
14932
- 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",
14933
- onMouseDown: (e) => e.preventDefault(),
14934
- children: matched.map((opt, index) => /* @__PURE__ */ jsx162("li", { children: /* @__PURE__ */ jsx162(
14935
- "button",
14936
- {
14937
- type: "button",
14938
- className: cn(
14939
- "w-full cursor-pointer rounded-sm px-2 py-1.5 text-left text-sm",
14940
- index === activeIndex && "bg-accent"
14941
- ),
14942
- onMouseEnter: () => setActiveIndex(index),
14943
- onClick: () => selectOption(opt),
14944
- children: opt.label
14945
- }
14946
- ) }, opt.value))
14947
- }
14948
- )
14949
- ]
14950
- }
14951
- );
14952
- }
14953
-
14954
- // src/components/business/ScriptEditor/index.tsx
14955
- import { useEffect as useEffect44, useRef as useRef18, useState as useState53 } from "react";
14956
- import { jsx as jsx163, jsxs as jsxs105 } from "react/jsx-runtime";
14957
- function escapeHtml(text) {
14958
- return text.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;");
14959
- }
14960
- function buildFunctionSnippet(fn) {
14961
- const params = fn.parameters ?? [];
14962
- if (params.length === 0) return `${fn.name}()`;
14963
- const inner = params.map((p, i) => `\${${i + 1}:${p.name}}`).join(", ");
14964
- return `${fn.name}(${inner})`;
14965
- }
14966
- function buildFunctionDoc(fn) {
14967
- const parts = [];
14968
- if (fn.description) parts.push(`<p>${escapeHtml(fn.description)}</p>`);
14969
- const params = fn.parameters ?? [];
14970
- if (params.length > 0) {
14971
- const rows = params.map(
14972
- (p) => `<li><b>${escapeHtml(p.name)}</b>` + (p.type ? ` <i>${escapeHtml(p.type)}</i>` : "") + (p.description ? ` \u2014 ${escapeHtml(p.description)}` : "") + `</li>`
14973
- ).join("");
14974
- parts.push(`<ul>${rows}</ul>`);
14975
- }
14976
- return parts.join("");
14977
- }
14978
- function buildVariableDoc(variable) {
14979
- const parts = [];
14980
- if (variable.type) parts.push(`<p><i>${escapeHtml(variable.type)}</i></p>`);
14981
- if (variable.description) parts.push(`<p>${escapeHtml(variable.description)}</p>`);
14982
- return parts.join("");
14983
- }
14984
- function buildScriptCompletions(functions, variables) {
14985
- return {
14986
- getCompletions(_editor, _session, _position, _prefix, callback) {
14987
- const completions = [
14988
- ...(functions ?? []).map((fn) => ({
14989
- caption: fn.name,
14990
- snippet: buildFunctionSnippet(fn),
14991
- meta: "function",
14992
- docHTML: buildFunctionDoc(fn)
14993
- })),
14994
- ...(variables ?? []).map((v) => ({
14995
- caption: v.name,
14996
- value: v.name,
14997
- meta: "variable",
14998
- docHTML: buildVariableDoc(v)
14999
- }))
15000
- ];
15001
- callback(null, completions);
15002
- }
15003
- };
15004
- }
15005
- var MODE_LOADERS = {
15006
- python: () => Promise.all([
15007
- import("ace-builds/src-noconflict/mode-python"),
15008
- import("ace-builds/src-noconflict/snippets/python")
15009
- ])
15010
- };
15011
- function ScriptEditor({
15012
- id,
15013
- value,
15014
- onChange,
15015
- placeholder,
15016
- language = "python",
15017
- functions,
15018
- variables,
15019
- readonly,
15020
- height = 160,
15021
- className
15022
- }) {
15023
- const containerRef = useRef18(null);
15024
- const editorRef = useRef18(null);
15025
- const onChangeRef = useRef18(onChange);
15026
- onChangeRef.current = onChange;
15027
- const [isEmpty2, setIsEmpty] = useState53(!value);
15028
- useEffect44(() => {
15029
- let disposed = false;
15030
- (async () => {
15031
- const ace = await import("ace-builds");
15032
- await Promise.all([
15033
- import("ace-builds/src-noconflict/theme-textmate"),
15034
- import("ace-builds/src-noconflict/ext-language_tools"),
15035
- MODE_LOADERS[language]?.() ?? Promise.resolve()
15036
- ]);
15037
- if (disposed || !containerRef.current) return;
15038
- const editor = ace.edit(containerRef.current, {
15039
- value: value ?? "",
15040
- theme: "ace/theme/textmate",
15041
- mode: MODE_LOADERS[language] ? `ace/mode/${language}` : "ace/mode/text",
15042
- showPrintMargin: false,
15043
- highlightActiveLine: true,
15044
- tabSize: 4,
15045
- useSoftTabs: true,
15046
- readOnly: readonly ?? false,
15047
- enableBasicAutocompletion: [buildScriptCompletions(functions, variables)],
15048
- enableLiveAutocompletion: true,
15049
- enableSnippets: true
15050
- });
15051
- editor.session.setUseWorker(false);
15052
- editor.on("change", () => {
15053
- const current = editor.getValue();
15054
- setIsEmpty(current === "");
15055
- onChangeRef.current?.(current);
15056
- });
15057
- editorRef.current = editor;
15058
- })().catch(() => {
15059
- });
15060
- return () => {
15061
- disposed = true;
15062
- editorRef.current?.destroy();
15063
- editorRef.current = null;
15064
- };
15065
- }, []);
15066
- useEffect44(() => {
15067
- const editor = editorRef.current;
15068
- if (!editor) return;
15069
- const next = value ?? "";
15070
- if (editor.getValue() !== next) {
15071
- editor.setValue(next, -1);
15072
- setIsEmpty(next === "");
15073
- }
15074
- }, [value]);
15075
- useEffect44(() => {
15076
- editorRef.current?.setOptions({
15077
- enableBasicAutocompletion: [buildScriptCompletions(functions, variables)]
15078
- });
15079
- }, [functions, variables]);
15080
- useEffect44(() => {
15081
- editorRef.current?.setReadOnly(readonly ?? false);
15082
- }, [readonly]);
15083
- return /* @__PURE__ */ jsxs105(
15084
- "div",
15085
- {
15086
- className: cn(
15087
- "relative w-full rounded-md border bg-background overflow-hidden",
15088
- readonly && "bg-muted",
15089
- className
15090
- ),
15091
- children: [
15092
- /* @__PURE__ */ jsx163("div", { ref: containerRef, id, style: { height }, className: "w-full" }),
15093
- isEmpty2 && placeholder && /* @__PURE__ */ jsx163("div", { className: "absolute top-0 left-0 px-3 py-2 text-sm text-muted-foreground pointer-events-none z-10", children: placeholder })
15094
- ]
15095
- }
15096
- );
15097
- }
15098
-
15099
- // src/components/business/SiteFooter/index.tsx
15100
- import { jsx as jsx164, jsxs as jsxs106 } from "react/jsx-runtime";
15101
- function SiteFooter({
15102
- brand,
15103
- groups,
15104
- copyright,
15105
- bottomLinks,
15106
- onLinkClick,
15107
- className,
15108
- style
15109
- }) {
15110
- const renderLink = (link) => {
15111
- if (link.href) {
15112
- return /* @__PURE__ */ jsx164(
15113
- Link,
15114
- {
15115
- href: link.href,
15116
- text: link.text,
15117
- className: "text-sm text-muted-foreground transition-colors hover:text-foreground"
15118
- }
15119
- );
15120
- }
15121
- return /* @__PURE__ */ jsx164(
15122
- "button",
15123
- {
15124
- type: "button",
15125
- "data-slot": "site-footer-link",
15126
- className: "text-left text-sm text-muted-foreground transition-colors hover:text-foreground",
15127
- onClick: () => onLinkClick?.({ key: link.text, text: link.text }),
15128
- children: link.text
15129
- }
15130
- );
15131
- };
15132
- const hasBrand = Boolean(brand && (brand.logo || brand.name || brand.description));
15133
- const hasGroups = Boolean(groups && groups.length > 0);
15134
- const hasBottom = Boolean(copyright || bottomLinks && bottomLinks.length > 0);
15135
- if (!hasBrand && !hasGroups && !hasBottom) {
15136
- return null;
15137
- }
15138
- return /* @__PURE__ */ jsxs106(
15139
- "footer",
15140
- {
15141
- "data-slot": "site-footer",
15142
- className: cn("border-t bg-muted/40", className),
15143
- style,
15144
- children: [
15145
- hasBrand || hasGroups ? /* @__PURE__ */ jsxs106(
15146
- "div",
15147
- {
15148
- "data-slot": "site-footer-main",
15149
- className: "mx-auto flex w-full max-w-6xl flex-col gap-8 px-6 py-10 md:flex-row md:justify-between",
15150
- children: [
15151
- hasBrand ? /* @__PURE__ */ jsxs106("div", { "data-slot": "site-footer-brand", className: "flex max-w-xs flex-col gap-3", children: [
15152
- brand.logo || brand.name ? /* @__PURE__ */ jsxs106("div", { className: "flex items-center gap-2", children: [
15153
- brand.logo ? /* @__PURE__ */ jsx164(
15154
- "img",
15155
- {
15156
- src: brand.logo,
15157
- alt: brand.name ?? "",
15158
- className: "size-8 rounded-md object-contain"
15159
- }
15160
- ) : null,
15161
- brand.name ? /* @__PURE__ */ jsx164("span", { className: "text-base font-semibold", children: brand.name }) : null
15162
- ] }) : null,
15163
- brand.description ? /* @__PURE__ */ jsx164("p", { className: "text-sm leading-relaxed text-muted-foreground", children: brand.description }) : null
15164
- ] }) : null,
15165
- hasGroups ? /* @__PURE__ */ jsx164(
15166
- "div",
15167
- {
15168
- "data-slot": "site-footer-groups",
15169
- className: "grid flex-1 grid-cols-2 gap-8 sm:grid-cols-3 md:justify-items-end",
15170
- children: groups.map((group) => /* @__PURE__ */ jsxs106(
15171
- "div",
15172
- {
15173
- "data-slot": "site-footer-group",
15174
- className: "flex min-w-24 flex-col gap-3",
15175
- children: [
15176
- /* @__PURE__ */ jsx164("span", { className: "text-sm font-medium", children: group.title }),
15177
- /* @__PURE__ */ jsx164("div", { className: "flex flex-col gap-2", children: group.links.map((link) => /* @__PURE__ */ jsx164("div", { children: renderLink(link) }, link.text)) })
15178
- ]
15179
- },
15180
- group.title
15181
- ))
15182
- }
15183
- ) : null
15184
- ]
15185
- }
15186
- ) : null,
15187
- hasBottom ? /* @__PURE__ */ jsx164("div", { "data-slot": "site-footer-bottom", className: "border-t", children: /* @__PURE__ */ jsxs106("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: [
15188
- copyright ? /* @__PURE__ */ jsx164("span", { children: copyright }) : /* @__PURE__ */ jsx164("span", {}),
15189
- bottomLinks && bottomLinks.length > 0 ? /* @__PURE__ */ jsx164("div", { "data-slot": "site-footer-bottom-links", className: "flex items-center gap-4", children: bottomLinks.map((link) => /* @__PURE__ */ jsx164("div", { children: renderLink(link) }, link.text)) }) : null
15190
- ] }) }) : null
15191
- ]
15192
- }
15193
- );
15194
- }
15195
-
15196
11118
  // src/components/register.ts
15197
11119
  var builtinComponents = {
15198
11120
  Page,
@@ -15342,38 +11264,7 @@ var builtinComponents = {
15342
11264
  MessageList,
15343
11265
  Composer,
15344
11266
  ConversationList,
15345
- StepsPanel,
15346
- // 图表组件集(src/components/chart 目录,echarts 渲染;
15347
- // 交互事件经 echarts click → onSelect,DSL 用 events.onSelect + ${event})
15348
- MetricCard,
15349
- LineChart: LineChart2,
15350
- ColumnChart,
15351
- BarChart: BarChart2,
15352
- AreaChart,
15353
- PieChart: PieChart2,
15354
- DonutChart,
15355
- ScatterChart: ScatterChart2,
15356
- BubbleChart,
15357
- RadarChart: RadarChart2,
15358
- FunnelChart: FunnelChart2,
15359
- GaugeChart: GaugeChart2,
15360
- Heatmap,
15361
- TreeChart: TreeChart2,
15362
- SankeyChart: SankeyChart2,
15363
- MapChart: MapChart2,
15364
- ComposedChart,
15365
- // 业务组件(business 子目录)
15366
- Login,
15367
- EntityCard,
15368
- CardList,
15369
- SiteFooter,
15370
- // 业务表单类组件(value/onChange 受控;DSL 用 props.value + events.onChange 读写 ${event})
15371
- SearchableSelect,
15372
- FieldCondition,
15373
- ExpressionEditor,
15374
- RuleConfig,
15375
- RuleExpression,
15376
- ScriptEditor
11267
+ StepsPanel
15377
11268
  };
15378
11269
  function registerBuiltinComponents(registry) {
15379
11270
  for (const [type, component] of Object.entries(builtinComponents)) {
@@ -15404,6 +11295,7 @@ export {
15404
11295
  Calendar2 as Calendar,
15405
11296
  Card2 as Card,
15406
11297
  Carousel2 as Carousel,
11298
+ ClearHoverButton,
15407
11299
  CONTROL_SIZE_BOX,
15408
11300
  CONTROL_SIZE_BOX_GROW,
15409
11301
  CONTROL_SIZE_AREA,
@@ -15529,34 +11421,6 @@ export {
15529
11421
  Citation,
15530
11422
  StepsPanel,
15531
11423
  Suggestion,
15532
- EChartBase,
15533
- AreaChart,
15534
- BarChart2 as BarChart,
15535
- BubbleChart,
15536
- ColumnChart,
15537
- ComposedChart,
15538
- DonutChart,
15539
- FunnelChart2 as FunnelChart,
15540
- GaugeChart2 as GaugeChart,
15541
- Heatmap,
15542
- LineChart2 as LineChart,
15543
- MapChart2 as MapChart,
15544
- MetricCard,
15545
- PieChart2 as PieChart,
15546
- RadarChart2 as RadarChart,
15547
- SankeyChart2 as SankeyChart,
15548
- ScatterChart2 as ScatterChart,
15549
- TreeChart2 as TreeChart,
15550
- EntityCard,
15551
- CardList,
15552
- SearchableSelect,
15553
- FieldCondition,
15554
- ExpressionEditor,
15555
- Login,
15556
- RuleConfig,
15557
- RuleExpression,
15558
- ScriptEditor,
15559
- SiteFooter,
15560
11424
  builtinComponents,
15561
11425
  registerBuiltinComponents,
15562
11426
  createComponentRegistry