@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 +18 -0
- package/package.json +5 -5
- package/src/components/agent-helper/field-value-input/inputs.tsx +5 -3
- package/src/components/agent-helper/form-select/index.tsx +4 -16
- package/src/components/bs-entry-select/bs-entry-select.tsx +4 -13
- package/src/components/bs-entry-select/scroll-slider.tsx +15 -28
- package/src/hooks/use-select-popover-position.ts +74 -0
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.
|
|
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.
|
|
64
|
-
"@baishuyun/types": "3.0.
|
|
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.
|
|
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
|
-
|
|
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
|
+
}
|