@baishuyun/ai-design 2.0.12 → 2.0.14

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,23 @@
1
1
  # @baishuyun/ai-design
2
2
 
3
+ ## 2.0.14
4
+
5
+ ### Patch Changes
6
+
7
+ - fix ui error
8
+ - Updated dependencies
9
+ - @baishuyun/ai-feat-utils@2.0.14
10
+ - @baishuyun/types@3.0.14
11
+
12
+ ## 2.0.13
13
+
14
+ ### Patch Changes
15
+
16
+ - date time format
17
+ - Updated dependencies
18
+ - @baishuyun/types@3.0.13
19
+ - @baishuyun/ai-feat-utils@2.0.13
20
+
3
21
  ## 2.0.12
4
22
 
5
23
  ### Patch Changes
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@baishuyun/ai-design",
3
- "version": "2.0.12",
3
+ "version": "2.0.14",
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.12",
64
- "@baishuyun/types": "3.0.12"
63
+ "@baishuyun/ai-feat-utils": "2.0.14",
64
+ "@baishuyun/types": "3.0.14"
65
65
  },
66
66
  "peerDependencies": {
67
67
  "react": "^19.0.0",
@@ -83,9 +83,9 @@
83
83
  "typescript": "^5.5.0",
84
84
  "vite": "^5.1.4",
85
85
  "vite-plugin-dts": "^4.5.4",
86
- "@baishuyun/typescript-config": "2.0.9",
87
86
  "@baishuyun/tailwind-config": "3.0.9",
88
- "@baishuyun/types": "3.0.12"
87
+ "@baishuyun/types": "3.0.14",
88
+ "@baishuyun/typescript-config": "2.0.9"
89
89
  },
90
90
  "keywords": [],
91
91
  "author": "",
@@ -148,7 +148,7 @@ export function RangeNumberInput({ value, onChange, disabled }: FieldValueInputP
148
148
 
149
149
  // ── RangeDateTimeInput ───────────────────────────────────────────────
150
150
 
151
- export function RangeDateTimeInput({ value, onChange, disabled }: FieldValueInputProps) {
151
+ export function RangeDateTimeInput({ value, onChange, disabled, field }: FieldValueInputProps) {
152
152
  const rangeValue = (value && typeof value === 'object' && !Array.isArray(value)
153
153
  ? value
154
154
  : { min: undefined, max: undefined }) as { min?: string; max?: string };
@@ -156,11 +156,13 @@ export function RangeDateTimeInput({ value, onChange, disabled }: FieldValueInpu
156
156
  const minDate = rangeValue.min ? new Date(rangeValue.min) : undefined;
157
157
  const maxDate = rangeValue.max ? new Date(rangeValue.max) : undefined;
158
158
 
159
+ const format = field?.widget?.format;
160
+
159
161
  return (
160
162
  <div className="flex items-center gap-[8px]">
161
163
  <DateInput
162
164
  value={minDate}
163
- onChange={(date) => onChange?.({ ...rangeValue, min: date ? formatDateByFormat(date, 'yyyy-MM-dd HH:mm:ss') /* date.toISOString() */ : undefined })}
165
+ onChange={(date) => onChange?.({ ...rangeValue, min: date ? formatDateByFormat(date, format ?? 'yyyy-MM-dd HH:mm:ss') /* date.toISOString() */ : undefined })}
164
166
  format="yyyy-MM-dd"
165
167
  placeholder="开始日期"
166
168
  disabled={disabled}
@@ -169,7 +171,7 @@ export function RangeDateTimeInput({ value, onChange, disabled }: FieldValueInpu
169
171
  <span className="text-[#999] text-[14px] shrink-0">~</span>
170
172
  <DateInput
171
173
  value={maxDate}
172
- onChange={(date) => onChange?.({ ...rangeValue, max: date ? date.toISOString() : undefined })}
174
+ onChange={(date) => onChange?.({ ...rangeValue, max: date ? formatDateByFormat(date, format ?? 'yyyy-MM-dd HH:mm:ss') /* date.toISOString() */ : undefined })}
173
175
  format="yyyy-MM-dd"
174
176
  placeholder="结束日期"
175
177
  disabled={disabled}
@@ -11,6 +11,7 @@ import { createPortal } from 'react-dom';
11
11
  import { cn } from '../../../lib/utils';
12
12
  import { transCls } from '../../../const/ui';
13
13
  import { SolidArrowDownIcon, SearchIcon } from '../../bs-icons';
14
+ import { useSelectPopoverPosition } from '../../../hooks/use-select-popover-position';
14
15
 
15
16
  const FolderIcon = () => (
16
17
  <svg xmlns="http://www.w3.org/2000/svg" width="18" height="18" viewBox="0 0 18 18" fill="none">
@@ -332,9 +333,11 @@ export const FormSelect = forwardRef<HTMLDivElement, FormSelectProps>(
332
333
  );
333
334
  const triggerRef = useRef<HTMLDivElement>(null);
334
335
  const popoverRef = useRef<HTMLDivElement>(null);
335
- const [popoverStyle, setPopoverStyle] = useState<React.CSSProperties>({});
336
336
  const [portalContainer, setPortalContainer] = useState<HTMLElement | null>(null);
337
337
 
338
+ const { popoverStyle, updatePopoverPosition } =
339
+ useSelectPopoverPosition(triggerRef);
340
+
338
341
  const selectedFormId = controlledValue ?? internalValue;
339
342
 
340
343
  // Build the form tree
@@ -350,21 +353,6 @@ export const FormSelect = forwardRef<HTMLDivElement, FormSelectProps>(
350
353
 
351
354
  const hasSearch = searchQuery.trim().length > 0;
352
355
 
353
- // ── Position helpers ──────────────────────────────────────────
354
-
355
- const updatePopoverPosition = useCallback(() => {
356
- if (!triggerRef.current) return;
357
- const rect = triggerRef.current.getBoundingClientRect();
358
- setPopoverStyle({
359
- position: 'fixed',
360
- top: rect.bottom + 4,
361
- left: rect.left,
362
- minWidth: Math.max(rect.width, 240),
363
- zIndex: 9999,
364
- pointerEvents: 'auto',
365
- });
366
- }, []);
367
-
368
356
  const setTriggerRef = useCallback((node: HTMLDivElement | null) => {
369
357
  (triggerRef as React.MutableRefObject<HTMLDivElement | null>).current = node;
370
358
  if (!node) {
@@ -14,6 +14,7 @@ import { EntryPopover } from './entry-popover';
14
14
  import { buildEntryTreeFromAliasMap } from './build-tree';
15
15
  import type { BsEntrySelectProps, BsEntrySelectChangeResult } from './types';
16
16
  import { MenuLinesIcon } from '../bs-icons';
17
+ import { useSelectPopoverPosition } from '../../hooks/use-select-popover-position';
17
18
 
18
19
  export type { BsEntrySelectProps, BsEntrySelectChangeResult };
19
20
 
@@ -41,10 +42,12 @@ export const BsEntrySelect = forwardRef<HTMLDivElement, BsEntrySelectProps>(
41
42
  );
42
43
  const triggerRef = useRef<HTMLDivElement>(null);
43
44
  const popoverRef = useRef<HTMLDivElement>(null);
44
- const [popoverStyle, setPopoverStyle] = useState<React.CSSProperties>({});
45
45
  const [portalContainer, setPortalContainer] = useState<HTMLElement | null>(null);
46
46
  const [sliderHasOverflow, setSliderHasOverflow] = useState(false);
47
47
 
48
+ const { popoverStyle, updatePopoverPosition } =
49
+ useSelectPopoverPosition(triggerRef);
50
+
48
51
  const handleOverflowChange = useCallback((hasOverflow: boolean) => {
49
52
  setSliderHasOverflow(hasOverflow);
50
53
  }, []);
@@ -55,18 +58,6 @@ export const BsEntrySelect = forwardRef<HTMLDivElement, BsEntrySelectProps>(
55
58
  return Object.keys(appDetails).filter((id) => id !== currentAppId);
56
59
  }, [appDetails, currentAppId]);
57
60
 
58
- const updatePopoverPosition = useCallback(() => {
59
- if (!triggerRef.current) return;
60
- const rect = triggerRef.current.getBoundingClientRect();
61
- setPopoverStyle({
62
- position: 'fixed',
63
- top: rect.bottom + 4,
64
- left: rect.left,
65
- minWidth: Math.max(rect.width, 240),
66
- zIndex: 9999,
67
- });
68
- }, []);
69
-
70
61
  const setTriggerRef = useCallback((node: HTMLDivElement | null) => {
71
62
  (triggerRef as React.MutableRefObject<HTMLDivElement | null>).current = node;
72
63
  if (!node) {
@@ -24,6 +24,7 @@ import type {
24
24
  BsEntrySelectSliderVariant,
25
25
  } from './types';
26
26
  import classNames from 'classnames';
27
+ import { useSelectPopoverPosition } from '../../hooks/use-select-popover-position';
27
28
 
28
29
  const ChevronRightIcon = ({ size }: { size?: number }) => (
29
30
  <SolidArrowDownIcon className="-rotate-90" size={size} />
@@ -59,10 +60,18 @@ export function ScrollSlider({
59
60
  // Group popover state
60
61
  const [activeGroup, setActiveGroup] = useState<FlatNode | null>(null);
61
62
  const [groupSearchQuery, setGroupSearchQuery] = useState('');
62
- const [popoverStyle, setPopoverStyle] = useState<React.CSSProperties>({});
63
63
  const popoverRef = useRef<HTMLDivElement>(null);
64
64
  const groupPillRefs = useRef<Map<string, HTMLButtonElement>>(new Map());
65
65
  const [groupPortalContainer, setGroupPortalContainer] = useState<HTMLElement | null>(null);
66
+ const activeGroupIdRef = useRef<string | null>(null);
67
+
68
+ const { popoverStyle, updatePopoverPosition } = useSelectPopoverPosition(
69
+ () => {
70
+ if (!activeGroupIdRef.current) return null;
71
+ return groupPillRefs.current.get(activeGroupIdRef.current) ?? null;
72
+ },
73
+ { estimatedHeight: 340 },
74
+ );
66
75
 
67
76
  const isTabs = variant === 'tabs';
68
77
 
@@ -182,21 +191,8 @@ export function ScrollSlider({
182
191
  });
183
192
  }, []);
184
193
 
185
- const updatePopoverPosition = useCallback(() => {
186
- if (!activeGroup) return;
187
- const triggerEl = groupPillRefs.current.get(activeGroup.node.id);
188
- if (!triggerEl) return;
189
- const rect = triggerEl.getBoundingClientRect();
190
- setPopoverStyle({
191
- position: 'fixed',
192
- top: rect.bottom + 4,
193
- left: rect.left,
194
- minWidth: Math.max(rect.width, 240),
195
- zIndex: 9999,
196
- });
197
- }, [activeGroup]);
198
-
199
194
  const closeGroupPopover = useCallback(() => {
195
+ activeGroupIdRef.current = null;
200
196
  setActiveGroup(null);
201
197
  setGroupSearchQuery('');
202
198
  }, []);
@@ -204,11 +200,13 @@ export function ScrollSlider({
204
200
  const openGroupPopover = useCallback((flatNode: FlatNode, event: React.MouseEvent) => {
205
201
  event.stopPropagation();
206
202
  if (activeGroup?.node.id === flatNode.node.id) {
203
+ activeGroupIdRef.current = null;
207
204
  setActiveGroup(null);
208
205
  setGroupSearchQuery('');
209
206
  return;
210
207
  }
211
208
  setGroupSearchQuery('');
209
+ activeGroupIdRef.current = flatNode.node.id;
212
210
  setActiveGroup(flatNode);
213
211
 
214
212
  // Check Shadow DOM portal
@@ -233,21 +231,10 @@ export function ScrollSlider({
233
231
  }
234
232
 
235
233
  // Position the popover after the next frame
236
- const groupId = flatNode.node.id;
237
234
  requestAnimationFrame(() => {
238
- const el = groupPillRefs.current.get(groupId);
239
- if (el) {
240
- const rect = el.getBoundingClientRect();
241
- setPopoverStyle({
242
- position: 'fixed',
243
- top: rect.bottom + 4,
244
- left: rect.left,
245
- minWidth: Math.max(rect.width, 240),
246
- zIndex: 9999,
247
- });
248
- }
235
+ updatePopoverPosition();
249
236
  });
250
- }, [activeGroup]);
237
+ }, [activeGroup, updatePopoverPosition]);
251
238
 
252
239
  const handleGroupPopoverSelect = useCallback(
253
240
  (result: BsEntrySelectChangeResult) => {
@@ -0,0 +1,74 @@
1
+ import { useState, useCallback } from 'react';
2
+
3
+ interface UseSelectPopoverPositionOptions {
4
+ estimatedHeight?: number;
5
+ gap?: number;
6
+ minWidth?: number;
7
+ zIndex?: number;
8
+ }
9
+
10
+ export function useSelectPopoverPosition(
11
+ triggerRef: React.RefObject<HTMLElement | null>,
12
+ options?: UseSelectPopoverPositionOptions,
13
+ ): {
14
+ popoverStyle: React.CSSProperties;
15
+ updatePopoverPosition: () => void;
16
+ };
17
+
18
+ export function useSelectPopoverPosition(
19
+ getTrigger: () => HTMLElement | null,
20
+ options?: UseSelectPopoverPositionOptions,
21
+ ): {
22
+ popoverStyle: React.CSSProperties;
23
+ updatePopoverPosition: () => void;
24
+ };
25
+
26
+ export function useSelectPopoverPosition(
27
+ triggerRefOrGetter:
28
+ | React.RefObject<HTMLElement | null>
29
+ | (() => HTMLElement | null),
30
+ options: UseSelectPopoverPositionOptions = {},
31
+ ) {
32
+ const {
33
+ estimatedHeight = 450,
34
+ gap = 4,
35
+ minWidth = 240,
36
+ zIndex = 9999,
37
+ } = options;
38
+
39
+ const [popoverStyle, setPopoverStyle] = useState<React.CSSProperties>({});
40
+
41
+ const getTrigger: () => HTMLElement | null =
42
+ typeof triggerRefOrGetter === 'function'
43
+ ? triggerRefOrGetter
44
+ : () => triggerRefOrGetter.current;
45
+
46
+ const updatePopoverPosition = useCallback(() => {
47
+ const trigger = getTrigger();
48
+ if (!trigger) return;
49
+
50
+ const rect = trigger.getBoundingClientRect();
51
+ const spaceBelow = window.innerHeight - rect.bottom - gap;
52
+ const spaceAbove = rect.top - gap;
53
+ const positionBelow =
54
+ spaceBelow >= estimatedHeight || spaceBelow >= spaceAbove;
55
+
56
+ setPopoverStyle({
57
+ position: 'fixed',
58
+ top: positionBelow ? rect.bottom + gap : undefined,
59
+ bottom: positionBelow
60
+ ? undefined
61
+ : window.innerHeight - rect.top + gap,
62
+ left: rect.left,
63
+ minWidth: Math.max(rect.width, minWidth),
64
+ maxHeight: Math.min(
65
+ estimatedHeight,
66
+ positionBelow ? spaceBelow : spaceAbove,
67
+ ),
68
+ zIndex,
69
+ pointerEvents: 'auto',
70
+ });
71
+ }, [getTrigger, estimatedHeight, gap, minWidth, zIndex]);
72
+
73
+ return { popoverStyle, updatePopoverPosition } as const;
74
+ }