@baishuyun/ai-design 2.0.36 → 2.0.38

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.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,22 @@
1
1
  # @baishuyun/ai-design
2
2
 
3
+ ## 2.0.38
4
+
5
+ ### Patch Changes
6
+
7
+ - bugfix
8
+ - Updated dependencies
9
+ - @baishuyun/ai-feat-utils@2.0.34
10
+ - @baishuyun/types@3.0.31
11
+
12
+ ## 2.0.37
13
+
14
+ ### Patch Changes
15
+
16
+ - 移动端优化
17
+ - Updated dependencies
18
+ - @baishuyun/ai-feat-utils@2.0.33
19
+
3
20
  ## 2.0.36
4
21
 
5
22
  ### Patch Changes
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@baishuyun/ai-design",
3
- "version": "2.0.36",
3
+ "version": "2.0.38",
4
4
  "description": "AI 功能 UI 组件库 - 所有 AI 功能的 UI 表现唯一真相来源",
5
5
  "type": "module",
6
6
  "main": "src/index.ts",
@@ -60,8 +60,8 @@
60
60
  "tw-animate-css": "^1.4.0",
61
61
  "vaul": "^1.1.2",
62
62
  "zod": "^4.1.13",
63
- "@baishuyun/ai-feat-utils": "2.0.32",
64
- "@baishuyun/types": "3.0.30"
63
+ "@baishuyun/ai-feat-utils": "2.0.34",
64
+ "@baishuyun/types": "3.0.31"
65
65
  },
66
66
  "peerDependencies": {
67
67
  "react": "^19.0.0",
@@ -84,7 +84,7 @@
84
84
  "vite": "^5.1.4",
85
85
  "vite-plugin-dts": "^4.5.4",
86
86
  "@baishuyun/tailwind-config": "3.0.9",
87
- "@baishuyun/types": "3.0.30",
87
+ "@baishuyun/types": "3.0.31",
88
88
  "@baishuyun/typescript-config": "2.0.9"
89
89
  },
90
90
  "keywords": [],
@@ -28,8 +28,10 @@ function createFieldTagDom(label: string): HTMLSpanElement {
28
28
 
29
29
  function resolveFieldWidgetInfo(
30
30
  fields: IField[],
31
- key: string,
31
+ fullKey: string,
32
32
  ): { widgetName: string; label: string } | undefined {
33
+ const key = fullKey.replace('virtual.', '');
34
+
33
35
  const dotIndex = key.indexOf('.');
34
36
  if (dotIndex > 0) {
35
37
  const parentName = key.substring(0, dotIndex);
@@ -41,13 +43,13 @@ function resolveFieldWidgetInfo(
41
43
  'widget' in item && item.widget?.widgetName === childName,
42
44
  );
43
45
  if (child) {
44
- return { widgetName: key, label: child.label };
46
+ return { widgetName: fullKey, label: child.label };
45
47
  }
46
48
  }
47
49
  } else {
48
50
  const field = fields.find((f) => f.widget?.widgetName === key);
49
51
  if (field) {
50
- return { widgetName: key, label: field.label };
52
+ return { widgetName: fullKey, label: field.label };
51
53
  }
52
54
  }
53
55
  return undefined;
@@ -63,7 +65,11 @@ function addFieldToCM(
63
65
  cm.replaceSelection('${' + widgetName + '}');
64
66
  const to = cm.getCursor();
65
67
 
66
- const tagNode = createFieldTagDom(displayLabel);
68
+ const isVirtual = widgetName?.includes("virtual");
69
+
70
+ const label = isVirtual ? `触发行.${displayLabel}` : displayLabel;
71
+
72
+ const tagNode = createFieldTagDom(label);
67
73
  tagNode.setAttribute('data-field-key', widgetName);
68
74
 
69
75
  cm.markText(from, to, {
@@ -1,5 +1,3 @@
1
- import { transCls } from '../const/ui';
2
- import { cn } from '../lib/utils';
3
1
  // import { FontIcon } from './font-icon';
4
2
 
5
3
  export const DockIcon = () => (
@@ -1,4 +1,4 @@
1
- import { FC, useCallback, useEffect, useRef } from 'react';
1
+ import { FC, useCallback, useEffect, useLayoutEffect, useRef } from 'react';
2
2
  import { useResizableWidth } from '../hooks/use-resizable-width';
3
3
  import { useColumnAppear } from '../hooks/use-column-appear';
4
4
  import classNames from 'classnames';
@@ -57,6 +57,11 @@ export const OfficeContainer: FC<IOfficeContainerProps> = ({
57
57
  const transitioningRef = useRef(false);
58
58
  const mountRef = useRef(true);
59
59
  const containerRef = useRef<HTMLDivElement>(null);
60
+ const onResizeRef = useRef(onResize);
61
+
62
+ useEffect(() => {
63
+ onResizeRef.current = onResize;
64
+ }, [onResize]);
60
65
 
61
66
  const { width, handleMouseDown, isDragging } = useResizableWidth({
62
67
  range: activeRange,
@@ -107,24 +112,43 @@ export const OfficeContainer: FC<IOfficeContainerProps> = ({
107
112
  return `calc(100% - ${width}px)`;
108
113
  }, [hideLeftCol, isLeftFullscreen, isRightFullscreen, isRightResizable, width]);
109
114
 
110
- useEffect(() => {
115
+ const getTargetColWidths = useCallback((): [number, number] => {
116
+ const containerWidth = containerRef.current?.offsetWidth ?? 0;
117
+
118
+ if (isLeftFullscreen) return [containerWidth, 0];
119
+ if (isRightFullscreen) return [0, containerWidth];
120
+
121
+ if (hideLeftCol) {
122
+ if (isRightResizable) return [0, width];
123
+ return [0, Math.max(containerWidth - width, 0)];
124
+ }
125
+
126
+ if (isRightResizable) {
127
+ return [Math.max(containerWidth - width, 0), width];
128
+ }
129
+
130
+ return [width, Math.max(containerWidth - width, 0)];
131
+ }, [hideLeftCol, isLeftFullscreen, isRightFullscreen, isRightResizable, width]);
132
+
133
+ useLayoutEffect(() => {
111
134
  if (!onResize || transitioningRef.current) return;
112
- const raf = requestAnimationFrame(() => {
113
- const leftW = leftColRef.current?.offsetWidth ?? 0;
114
- const rightW = rightColRef.current?.offsetWidth ?? 0;
115
- onResize(leftW, rightW);
116
- });
117
- return () => cancelAnimationFrame(raf);
135
+ const leftW = leftColRef.current?.offsetWidth ?? 0;
136
+ const rightW = rightColRef.current?.offsetWidth ?? 0;
137
+ onResize(leftW, rightW);
118
138
  }, [width, fullscreenPart, hideLeftCol, isRightResizable, onResize]);
119
139
 
120
140
  // Set guard when fullscreen/hide triggers CSS transitions. Skip mount.
121
- useEffect(() => {
141
+ // Immediately report the final target widths so the host portal can size
142
+ // itself before paint, instead of waiting for the width transition to end.
143
+ useLayoutEffect(() => {
122
144
  if (mountRef.current) {
123
145
  mountRef.current = false;
124
146
  return;
125
147
  }
126
148
  transitioningRef.current = true;
127
- }, [fullscreenPart, hideLeftCol]);
149
+ const [leftW, rightW] = getTargetColWidths();
150
+ onResizeRef.current?.(leftW, rightW);
151
+ }, [fullscreenPart, hideLeftCol, getTargetColWidths]);
128
152
 
129
153
  // Fire onResize after CSS width transitions complete
130
154
  useEffect(() => {
@@ -20,7 +20,7 @@ const PopoverContent = React.forwardRef<
20
20
  PopoverContentProps
21
21
  >(
22
22
  (
23
- { className, align = 'start', sideOffset = 4, portalContainer, style, onWheel, ...props },
23
+ { className, align = 'start', sideOffset = 4, portalContainer, style, onWheel, onOpenAutoFocus, ...props },
24
24
  ref
25
25
  ) => {
26
26
  const handleWheel = portalContainer
@@ -45,6 +45,12 @@ const PopoverContent = React.forwardRef<
45
45
  }
46
46
  : onWheel;
47
47
 
48
+ // 默认阻止 Radix 打开浮层时自动聚焦内部第一个可聚焦元素(通常是搜索框),
49
+ // 移动端会因此唤起软键盘,不是期望的交互;调用方仍可显式传 onOpenAutoFocus 覆盖
50
+ const handleOpenAutoFocus = onOpenAutoFocus ?? ((event: Event) => {
51
+ event.preventDefault();
52
+ });
53
+
48
54
  return (
49
55
  <PopoverPrimitive.Portal container={portalContainer ?? undefined}>
50
56
  <PopoverPrimitive.Content
@@ -52,6 +58,7 @@ const PopoverContent = React.forwardRef<
52
58
  align={align}
53
59
  sideOffset={sideOffset}
54
60
  onWheel={handleWheel}
61
+ onOpenAutoFocus={handleOpenAutoFocus}
55
62
  className={cn(
56
63
  'z-50 w-60 rounded-[8px] bg-white p-1 text-[#121111] shadow-[0px_6px_16px_rgba(0,0,0,0.06)] outline-none',
57
64
  'data-[state=open]:animate-in data-[state=closed]:animate-out',