@baishuyun/ai-design 2.0.47 → 2.0.49

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.
Files changed (36) hide show
  1. package/CHANGELOG.md +18 -0
  2. package/package.json +4 -4
  3. package/src/components/agent-drawer.tsx +40 -6
  4. package/src/components/agent-helper/agent-helper-card/index.tsx +3 -3
  5. package/src/components/agent-helper/app-select/index.tsx +7 -7
  6. package/src/components/agent-helper/condition-item/index.tsx +1 -0
  7. package/src/components/agent-helper/field-value-input/dept-select.tsx +447 -0
  8. package/src/components/agent-helper/field-value-input/index.tsx +3 -2
  9. package/src/components/agent-helper/field-value-input/picker-inputs.tsx +1 -1
  10. package/src/components/agent-helper/form-select/index.tsx +8 -5
  11. package/src/components/agent-helper/relational-operator-select/index.tsx +37 -23
  12. package/src/components/agent-helper/table/index.tsx +6 -2
  13. package/src/components/agent-select.tsx +115 -35
  14. package/src/components/card.tsx +19 -6
  15. package/src/components/fields-portal.tsx +2 -2
  16. package/src/components/fields-previewer.tsx +1 -0
  17. package/src/components/legacy-slot.tsx +11 -16
  18. package/src/components/output-config-editor.tsx +32 -19
  19. package/src/components/shadow-content.tsx +19 -0
  20. package/src/components/shadow-portal.tsx +23 -0
  21. package/src/components/ui/dialog.tsx +4 -1
  22. package/src/components/ui/drawer.tsx +3 -1
  23. package/src/components/ui/popover.tsx +3 -2
  24. package/src/components/ui/select.tsx +3 -2
  25. package/src/components/ui/tooltip.tsx +3 -2
  26. package/src/const/shadow.ts +12 -0
  27. package/src/hooks/use-portal-container.ts +6 -3
  28. package/src/hooks/use-shadow-boundary.ts +64 -0
  29. package/src/hooks/use-shadow.ts +24 -7
  30. package/src/index.ts +2 -0
  31. package/src/stories/AppSelect.stories.tsx +44 -0
  32. package/src/stories/FieldValueInput.stories.tsx +6 -3
  33. package/src/stories/FormSelect.stories.tsx +40 -0
  34. package/src/stories/RelationalOperatorSelect.stories.tsx +27 -0
  35. package/src/style.css +28 -1
  36. package/style.css +153 -126
@@ -0,0 +1,23 @@
1
+ import { useLayoutEffect, type ElementType, type ReactNode } from 'react';
2
+ import { usePortalContainer } from '../hooks/use-portal-container';
3
+
4
+ export const ShadowPortal = ({ component: Portal, container, ...props }: {
5
+ component: ElementType;
6
+ container?: Element | DocumentFragment | null;
7
+ children?: ReactNode;
8
+ forceMount?: true;
9
+ 'data-slot'?: string;
10
+ }) => {
11
+ const { portalContainer, setTriggerRef, portalReady } = usePortalContainer('data-bs-portal-container');
12
+ useLayoutEffect(() => {
13
+ if (container instanceof Element && container.parentNode instanceof ShadowRoot) {
14
+ container.setAttribute('data-bs-portal-container', '');
15
+ }
16
+ }, [container]);
17
+ return (
18
+ <>
19
+ <span ref={setTriggerRef} style={{ display: 'none' }} aria-hidden />
20
+ {portalReady && <Portal {...props} container={container === undefined ? portalContainer ?? undefined : container} />}
21
+ </>
22
+ );
23
+ };
@@ -2,12 +2,15 @@ import * as React from 'react';
2
2
  import * as DialogPrimitive from '@radix-ui/react-dialog';
3
3
 
4
4
  import { cn } from '../../lib/utils';
5
+ import { ShadowPortal } from '../shadow-portal';
5
6
 
6
7
  const Dialog = DialogPrimitive.Root;
7
8
 
8
9
  const DialogTrigger = DialogPrimitive.Trigger;
9
10
 
10
- const DialogPortal = DialogPrimitive.Portal;
11
+ const DialogPortal = (props: React.ComponentProps<typeof DialogPrimitive.Portal>) => (
12
+ <ShadowPortal component={DialogPrimitive.Portal} {...props} />
13
+ );
11
14
 
12
15
  const DialogClose = DialogPrimitive.Close;
13
16
 
@@ -4,6 +4,7 @@ import { Drawer as DrawerPrimitive } from "vaul"
4
4
  import { cn } from "../../lib/utils"
5
5
  import { IconBtn } from "../icon-btn"
6
6
  import { CloseIcon } from "../bs-icons"
7
+ import { ShadowPortal } from '../shadow-portal';
7
8
 
8
9
  function Drawer({
9
10
  ...props
@@ -24,7 +25,8 @@ function DrawerPortal({
24
25
  container?: HTMLElement | null;
25
26
  }) {
26
27
  return (
27
- <DrawerPrimitive.Portal
28
+ <ShadowPortal
29
+ component={DrawerPrimitive.Portal}
28
30
  data-slot="drawer-portal"
29
31
  container={container ?? undefined}
30
32
  {...props}
@@ -2,6 +2,7 @@ import * as React from 'react';
2
2
  import * as PopoverPrimitive from '@radix-ui/react-popover';
3
3
 
4
4
  import { cn } from '../../lib/utils';
5
+ import { ShadowPortal } from '../shadow-portal';
5
6
 
6
7
  const Popover = PopoverPrimitive.Root;
7
8
 
@@ -52,7 +53,7 @@ const PopoverContent = React.forwardRef<
52
53
  });
53
54
 
54
55
  return (
55
- <PopoverPrimitive.Portal container={portalContainer ?? undefined}>
56
+ <ShadowPortal component={PopoverPrimitive.Portal} container={portalContainer ?? undefined}>
56
57
  <PopoverPrimitive.Content
57
58
  ref={ref}
58
59
  align={align}
@@ -76,7 +77,7 @@ const PopoverContent = React.forwardRef<
76
77
  }}
77
78
  {...props}
78
79
  />
79
- </PopoverPrimitive.Portal>
80
+ </ShadowPortal>
80
81
  );
81
82
  }
82
83
  );
@@ -3,6 +3,7 @@ import * as SelectPrimitive from '@radix-ui/react-select';
3
3
  import { ChevronDown, ChevronUp } from 'lucide-react';
4
4
 
5
5
  import { cn } from '../../lib/utils';
6
+ import { ShadowPortal } from '../shadow-portal';
6
7
 
7
8
  const Select = SelectPrimitive.Root;
8
9
 
@@ -112,7 +113,7 @@ const SelectContent = React.forwardRef<
112
113
  {containerProp === undefined && (
113
114
  <span ref={setDetectorRef} style={{ display: 'none' }} aria-hidden />
114
115
  )}
115
- <SelectPrimitive.Portal container={effectiveContainer ?? undefined}>
116
+ <ShadowPortal component={SelectPrimitive.Portal} container={effectiveContainer ?? undefined}>
116
117
  <SelectPrimitive.Content
117
118
  ref={ref}
118
119
  className={cn(
@@ -161,7 +162,7 @@ const SelectContent = React.forwardRef<
161
162
  </SelectPrimitive.Viewport>
162
163
  <SelectScrollDownButton />
163
164
  </SelectPrimitive.Content>
164
- </SelectPrimitive.Portal>
165
+ </ShadowPortal>
165
166
  </>
166
167
  );
167
168
  }
@@ -2,6 +2,7 @@ import * as React from 'react';
2
2
  import * as TooltipPrimitive from '@radix-ui/react-tooltip';
3
3
 
4
4
  import { cn } from '../../lib/utils';
5
+ import { ShadowPortal } from '../shadow-portal';
5
6
 
6
7
  const TooltipZIndexContext = React.createContext<number | undefined>(undefined);
7
8
 
@@ -30,7 +31,7 @@ const TooltipContent = React.forwardRef<
30
31
  const zIndexFromProvider = React.useContext(TooltipZIndexContext);
31
32
 
32
33
  return (
33
- <TooltipPrimitive.Portal container={portalContainer ?? undefined}>
34
+ <ShadowPortal component={TooltipPrimitive.Portal} container={portalContainer ?? undefined}>
34
35
  <TooltipPrimitive.Content
35
36
  ref={ref}
36
37
  sideOffset={sideOffset}
@@ -44,7 +45,7 @@ const TooltipContent = React.forwardRef<
44
45
  {props.children}
45
46
  <TooltipPrimitive.Arrow className="fill-[#525867] stroke-[#525867] stroke-[2px] stroke-linejoin-round [paint-order:stroke_fill]" />
46
47
  </TooltipPrimitive.Content>
47
- </TooltipPrimitive.Portal>
48
+ </ShadowPortal>
48
49
  );
49
50
  });
50
51
 
@@ -0,0 +1,12 @@
1
+ export const scopeSdkStyles = (css: string) => css.replace(/:host(?![-(])/g, ':is(:host, :host > div, [data-bs-shadow-content], [data-bs-portal-container])');
2
+
3
+ export const SDK_SHADOW_BASELINE = `
4
+ :host, :host > div {
5
+ font: 14px/1.5 ui-sans-serif, system-ui, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji";
6
+ color: #121111;
7
+ letter-spacing: normal;
8
+ text-align: start;
9
+ text-transform: none;
10
+ color-scheme: light;
11
+ }
12
+ `;
@@ -2,7 +2,9 @@ import { useCallback, useState } from 'react';
2
2
 
3
3
  export const usePortalContainer = (portalName: string) => {
4
4
  const [portalContainer, setPortalContainer] = useState<HTMLElement | null>(null);
5
+ const [portalReady, setPortalReady] = useState(false);
5
6
  const setTriggerRef = useCallback((node: HTMLElement | null) => {
7
+ setPortalReady(!!node);
6
8
  if (!node) {
7
9
  setPortalContainer(null);
8
10
  return;
@@ -14,14 +16,15 @@ export const usePortalContainer = (portalName: string) => {
14
16
  setPortalContainer(existing);
15
17
  return;
16
18
  }
17
- const container = document.createElement('div');
19
+ const container = node.ownerDocument.createElement('div');
18
20
  container.setAttribute(portalName, 'true');
21
+ container.setAttribute('data-bs-portal-container', '');
19
22
  rootNode.appendChild(container);
20
23
  setPortalContainer(container);
21
24
  } else {
22
25
  setPortalContainer(null);
23
26
  }
24
- }, []);
27
+ }, [portalName]);
25
28
 
26
- return { portalContainer, setTriggerRef };
29
+ return { portalContainer, setTriggerRef, portalReady };
27
30
  };
@@ -0,0 +1,64 @@
1
+ import { useEffect, useLayoutEffect, useState, type RefObject } from 'react';
2
+ import { syncHostStylesIntoShadow } from '@baishuyun/ai-feat-utils';
3
+ import { SDK_SHADOW_BASELINE, scopeSdkStyles } from '../const/shadow';
4
+
5
+ export type ShadowStyleMode = 'sdk' | 'host';
6
+ export type ShadowStyle = string | (() => string);
7
+
8
+ export const useShadowBoundary = (mode: ShadowStyleMode, styles?: ShadowStyle) => {
9
+ const [host, setHostRef] = useState<HTMLDivElement | null>(null);
10
+ const [boundary, setBoundary] = useState<{
11
+ root: ShadowRoot;
12
+ container: HTMLDivElement;
13
+ style: HTMLStyleElement;
14
+ } | null>(null);
15
+
16
+ useLayoutEffect(() => {
17
+ if (!host) return;
18
+ const root = host.shadowRoot || host.attachShadow({ mode: 'open' });
19
+ const style = host.ownerDocument.createElement('style');
20
+ const container = host.ownerDocument.createElement('div');
21
+ container.setAttribute('data-bs-shadow-content', '');
22
+ container.style.display = 'contents';
23
+ root.append(style, container);
24
+ setBoundary({ root, container, style });
25
+ return () => {
26
+ style.remove();
27
+ container.remove();
28
+ setBoundary(null);
29
+ };
30
+ }, [host]);
31
+
32
+ useLayoutEffect(() => {
33
+ if (!boundary || !host) return;
34
+ host.setAttribute('data-bs-style-mode', mode);
35
+ if (mode === 'sdk') boundary.container.className = '';
36
+ return mode === 'host' ? syncHostStylesIntoShadow(boundary.root, host) : undefined;
37
+ }, [boundary, host, mode]);
38
+
39
+ useLayoutEffect(() => {
40
+ if (!boundary) return;
41
+ boundary.style.toggleAttribute('data-bs-sdk-styles', mode === 'sdk');
42
+ boundary.style.toggleAttribute('data-bs-custom-styles', mode === 'host');
43
+ const css = typeof styles === 'function' ? styles() : styles || '';
44
+ boundary.style.textContent = mode === 'sdk' ? SDK_SHADOW_BASELINE + scopeSdkStyles(css) : css;
45
+ }, [boundary, mode, styles]);
46
+
47
+ return { setHostRef, container: boundary?.container ?? null };
48
+ };
49
+
50
+ export const useInjectedShadowStyle = (host: RefObject<HTMLElement | null>, css: string | undefined, attribute: string) => {
51
+ useEffect(() => {
52
+ const root = host.current?.shadowRoot;
53
+ if (!root) return;
54
+ let style = root.querySelector<HTMLStyleElement>(`style[${attribute}]`);
55
+ if (!css) { style?.remove(); return; }
56
+ if (!style) {
57
+ style = root.ownerDocument.createElement('style');
58
+ style.setAttribute(attribute, '');
59
+ root.appendChild(style);
60
+ }
61
+ style.textContent = css;
62
+ return () => style?.remove();
63
+ }, [host, css, attribute]);
64
+ };
@@ -1,4 +1,6 @@
1
1
  import { useState, useEffect, useRef } from 'react';
2
+ import { SDK_SHADOW_BASELINE, scopeSdkStyles } from '../const/shadow';
3
+ import sdkStyles from '../style.css?inline';
2
4
 
3
5
  interface UseShadowOptions {
4
6
  /** Shadow DOM挂载的根元素(默认挂到body) */
@@ -15,6 +17,7 @@ interface UseShadowOptions {
15
17
  extraHtml?: string;
16
18
 
17
19
  id: string;
20
+ enabled?: boolean;
18
21
  }
19
22
 
20
23
  /**
@@ -24,6 +27,7 @@ interface UseShadowOptions {
24
27
  */
25
28
  export const useShadow = ({
26
29
  id,
30
+ enabled = true,
27
31
  style = '',
28
32
  rootEl,
29
33
  containerClasses = [],
@@ -57,10 +61,13 @@ export const useShadow = ({
57
61
  wrapperRef.current = null;
58
62
  }
59
63
 
64
+ if (!enabled) return;
65
+
60
66
  try {
61
67
  // 1. 创建外层包裹元素(挂载Shadow Root的载体)
62
68
  const wrapperEl = document.createElement('div');
63
- wrapperEl.classList.add(...wrapperClasses, 'wrapper');
69
+ wrapperEl.classList.add(...wrapperClasses, 'bs-sdk-shadow-wrapper');
70
+ wrapperEl.setAttribute('data-bs-style-mode', 'sdk');
64
71
  // wrapperEl.style.height = '100%';
65
72
  wrapperRef.current = wrapperEl;
66
73
 
@@ -81,15 +88,15 @@ export const useShadow = ({
81
88
  }
82
89
 
83
90
  // 3. 注入样式到Shadow DOM
84
- if (style) {
85
- const styleEl = document.createElement('style');
86
- styleEl.textContent = style;
87
- shadowRoot.appendChild(styleEl);
88
- }
91
+ const styleEl = document.createElement('style');
92
+ styleEl.setAttribute('data-bs-sdk-styles', '');
93
+ styleEl.textContent = SDK_SHADOW_BASELINE + scopeSdkStyles(sdkStyles + style);
94
+ shadowRoot.appendChild(styleEl);
89
95
 
90
96
  // 4. 创建Shadow内部的容器(最终挂载React Portal的元素)
91
97
  const containerEl = document.createElement('div');
92
98
  containerEl.classList.add(...containerClasses, 'shadow-container');
99
+ containerEl.setAttribute('data-bs-shadow-content', '');
93
100
  shadowRoot.appendChild(containerEl);
94
101
 
95
102
  // 5. 将外层元素挂载到指定根节点
@@ -109,7 +116,17 @@ export const useShadow = ({
109
116
  // wrapperRef.current = null;
110
117
  // }
111
118
  // };
112
- }, [rootEl]); // Recreate when rootEl changes
119
+ return () => {
120
+ wrapperRef.current?.remove();
121
+ wrapperRef.current = null;
122
+ setShadowContainer(null);
123
+ };
124
+ }, [rootEl, enabled]); // Recreate when rootEl changes
125
+
126
+ useEffect(() => {
127
+ const styleEl = wrapperRef.current?.shadowRoot?.querySelector('style[data-bs-sdk-styles]');
128
+ if (styleEl) styleEl.textContent = SDK_SHADOW_BASELINE + scopeSdkStyles(sdkStyles + style);
129
+ }, [style, shadowContainer]);
113
130
 
114
131
  if (shadowContainer) {
115
132
  shadowContainer.id = id;
package/src/index.ts CHANGED
@@ -3,6 +3,8 @@ export { cn, copyTextToClipboard, toHumanReadable, isSubformFieldEqual } from '.
3
3
 
4
4
  // Hooks
5
5
  export { useShadow } from './hooks/use-shadow';
6
+ export { ShadowContent } from './components/shadow-content';
7
+ export type { ShadowContentProps } from './components/shadow-content';
6
8
 
7
9
  // Constants
8
10
  export {
@@ -1,5 +1,6 @@
1
1
  import { useState } from 'react';
2
2
  import type { Meta, StoryObj } from '@storybook/react-vite';
3
+ import { expect, userEvent, waitFor, within } from 'storybook/test';
3
4
  import { AppSelect, groupsToAppTreeNodes } from '../components/agent-helper/app-select';
4
5
  import type { AppTreeNode } from '../components/agent-helper/app-select';
5
6
  import type { IAppGroup } from '@baishuyun/types';
@@ -125,6 +126,49 @@ type Story = StoryObj<typeof meta>;
125
126
 
126
127
  export const Default: Story = {};
127
128
 
129
+ export const ConstrainedHeight: Story = {
130
+ args: { allowEmpty: true, showAll: true },
131
+ play: async ({ canvasElement }) => {
132
+ await userEvent.click(within(canvasElement).getByRole('button'));
133
+ const popover = await within(canvasElement.ownerDocument.body).findByRole('dialog');
134
+ await userEvent.clear(within(popover).getByRole('textbox'));
135
+ const content = popover.firstElementChild as HTMLElement;
136
+ const tree = content.lastElementChild as HTMLElement;
137
+ await waitFor(() => {
138
+ expect(popover.getBoundingClientRect().height).toBeLessThanOrEqual(parseFloat(getComputedStyle(popover).maxHeight));
139
+ expect(content.getBoundingClientRect().bottom).toBeLessThanOrEqual(popover.getBoundingClientRect().bottom);
140
+ expect(popover.scrollHeight).toBeLessThanOrEqual(popover.clientHeight);
141
+ expect(tree.scrollHeight).toBeGreaterThan(tree.clientHeight);
142
+ expect(content.firstElementChild!.getBoundingClientRect().height).toBe(30);
143
+ expect(within(popover).getByRole('button', { name: '无' }).getBoundingClientRect().height).toBe(34);
144
+ expect(within(popover).getByRole('button', { name: '全部应用' }).getBoundingClientRect().height).toBe(34);
145
+ });
146
+ tree.scrollTop = tree.scrollHeight;
147
+ expect(tree.scrollTop).toBeGreaterThan(0);
148
+ await userEvent.type(within(popover).getByRole('textbox'), 'API接口管理');
149
+ await userEvent.click(within(popover).getByRole('button', { name: 'API接口管理' }));
150
+ expect(within(canvasElement).getByRole('button')).toHaveTextContent('API接口管理');
151
+ },
152
+ };
153
+
154
+ export const EmbeddedConstrainedHeight: Story = {
155
+ args: { popoverContentOnly: true, disableMaxHeight: true, showAll: true, contentCls: 'h-full' },
156
+ render: (args) => <div style={{ height: 200 }}><AppSelect {...args} /></div>,
157
+ play: async ({ canvasElement }) => {
158
+ const content = within(canvasElement).getByRole('textbox').parentElement!.parentElement!;
159
+ const tree = content.lastElementChild as HTMLElement;
160
+ await waitFor(() => {
161
+ expect(content.getBoundingClientRect().height).toBe(200);
162
+ expect(tree.getBoundingClientRect().bottom).toBeLessThanOrEqual(content.getBoundingClientRect().bottom);
163
+ expect(tree.scrollHeight).toBeGreaterThan(tree.clientHeight);
164
+ expect(within(content).getByRole('button', { name: '全部应用' }).getBoundingClientRect().height).toBe(34);
165
+ });
166
+ expect(content.firstElementChild!.getBoundingClientRect().height).toBe(30);
167
+ tree.scrollTop = tree.scrollHeight;
168
+ expect(tree.scrollTop).toBeGreaterThan(0);
169
+ },
170
+ };
171
+
128
172
  export const SingleSelected: Story = {
129
173
  args: { defaultValue: 'app2' },
130
174
  };
@@ -21,9 +21,12 @@ const mockService: IFieldValueService = {
21
21
  searchDepts: async (keyword) => {
22
22
  await new Promise((r) => setTimeout(r, 500));
23
23
  const depts = [
24
- { id: 'dept1', name: '技术部' },
25
- { id: 'dept2', name: '产品部' },
26
- { id: 'dept3', name: '设计部' },
24
+ { id: 'dept-root', name: '百数科技' },
25
+ { id: 'dept1', name: '技术部', parentId: 'dept-root' },
26
+ { id: 'dept1-1', name: '前端组', parentId: 'dept1' },
27
+ { id: 'dept1-2', name: '后端组', parentId: 'dept1' },
28
+ { id: 'dept2', name: '产品部', parentId: 'dept-root' },
29
+ { id: 'dept3', name: '设计部', parentId: 'dept-root' },
27
30
  ];
28
31
  return keyword
29
32
  ? depts.filter((d) => d.name.includes(keyword))
@@ -1,5 +1,6 @@
1
1
  import { useState } from 'react';
2
2
  import type { Meta, StoryObj } from '@storybook/react-vite';
3
+ import { expect, userEvent, waitFor, within } from 'storybook/test';
3
4
  import { FormSelect } from '../components/agent-helper/form-select';
4
5
  import type { FormSelectItem, FormSelectChangeResult } from '../components/agent-helper/form-select';
5
6
 
@@ -91,6 +92,45 @@ export const ManyItems: Story = {
91
92
  },
92
93
  };
93
94
 
95
+ export const ConstrainedHeight: Story = {
96
+ args: { items: manyFormItems, allowEmpty: true },
97
+ play: async ({ canvasElement, args }) => {
98
+ if (!args.popoverOnly) {
99
+ await userEvent.click(within(canvasElement).getByText(/^(无|测试表单 30)$/));
100
+ }
101
+ const search = await within(canvasElement.ownerDocument.body).findByRole('textbox');
102
+ await userEvent.clear(search);
103
+ const content = search.parentElement!.parentElement!;
104
+ const list = content.lastElementChild as HTMLElement;
105
+ await waitFor(() => {
106
+ expect(content.getBoundingClientRect().height).toBeLessThanOrEqual(300);
107
+ expect(content.scrollHeight).toBeLessThanOrEqual(content.clientHeight);
108
+ expect(search.parentElement!.getBoundingClientRect().height).toBe(30);
109
+ expect(within(content).getByText('无').parentElement!.getBoundingClientRect().height).toBe(34);
110
+ expect(list.scrollHeight).toBeGreaterThan(list.clientHeight);
111
+ if (!args.popoverOnly) {
112
+ const portal = content.parentElement!;
113
+ expect(parseFloat(getComputedStyle(portal).maxHeight)).toBeLessThanOrEqual(300);
114
+ expect(content.getBoundingClientRect().bottom).toBeLessThanOrEqual(portal.getBoundingClientRect().bottom);
115
+ }
116
+ });
117
+ const searchTop = search.getBoundingClientRect().top;
118
+ list.scrollTop = list.scrollHeight;
119
+ expect(list.scrollTop).toBeGreaterThan(0);
120
+ expect(search.getBoundingClientRect().top).toBe(searchTop);
121
+ await userEvent.type(search, '测试表单 30');
122
+ await userEvent.click(within(content).getByText('测试表单 30'));
123
+ if (!args.popoverOnly) {
124
+ expect(within(canvasElement).getByText('测试表单 30')).toBeVisible();
125
+ }
126
+ },
127
+ };
128
+
129
+ export const PopoverOnlyConstrainedHeight: Story = {
130
+ ...ConstrainedHeight,
131
+ args: { ...ConstrainedHeight.args, popoverOnly: true },
132
+ };
133
+
94
134
  export const Empty: Story = {
95
135
  args: {
96
136
  items: [],
@@ -45,6 +45,33 @@ export const Disabled: Story = {
45
45
  },
46
46
  };
47
47
 
48
+ export const DeptGroupLabels: Story = {
49
+ args: {
50
+ defaultValue: 'in',
51
+ operators: ['in', 'all', 'eq', 'empty', 'not_empty'],
52
+ fieldType: 'deptgroup',
53
+ },
54
+ name: '按字段类型显示描述(部门多选)',
55
+ };
56
+
57
+ export const AddressLabels: Story = {
58
+ args: {
59
+ defaultValue: 'in',
60
+ operators: ['in', 'nin', 'empty', 'not_empty'],
61
+ fieldType: 'address',
62
+ },
63
+ name: '按字段类型显示描述(地址)',
64
+ };
65
+
66
+ export const CustomOperatorLabels: Story = {
67
+ args: {
68
+ defaultValue: 'eq',
69
+ operators: ['eq', 'neq'],
70
+ operatorLabels: { eq: '是', neq: '不是' },
71
+ },
72
+ name: '自定义文案(operatorLabels)',
73
+ };
74
+
48
75
  export const Controlled: Story = {
49
76
  render: () => {
50
77
  const [value, setValue] = useState<relationalOperator>('eq');
package/src/style.css CHANGED
@@ -82,7 +82,34 @@
82
82
  --color-sidebar-border: var(--sidebar-border);
83
83
  --color-sidebar-ring: var(--sidebar-ring);
84
84
 
85
- --radius: 0.625rem;
85
+ --spacing: 3.5px;
86
+ --radius: 8.75px;
87
+ --text-xs: 10.5px;
88
+ --text-xs--line-height: 14px;
89
+ --text-sm: 12.25px;
90
+ --text-sm--line-height: 17.5px;
91
+ --text-base: 14px;
92
+ --text-base--line-height: 21px;
93
+ --text-lg: 15.75px;
94
+ --text-lg--line-height: 24.5px;
95
+ --text-xl: 17.5px;
96
+ --text-xl--line-height: 24.5px;
97
+ --text-2xl: 21px;
98
+ --text-2xl--line-height: 28px;
99
+ --text-3xl: 26.25px;
100
+ --text-3xl--line-height: 31.5px;
101
+ --text-4xl: 31.5px;
102
+ --text-4xl--line-height: 35px;
103
+ --text-5xl: 42px;
104
+ --text-5xl--line-height: 1;
105
+ --text-6xl: 52.5px;
106
+ --text-6xl--line-height: 1;
107
+ --text-7xl: 63px;
108
+ --text-7xl--line-height: 1;
109
+ --text-8xl: 84px;
110
+ --text-8xl--line-height: 1;
111
+ --text-9xl: 112px;
112
+ --text-9xl--line-height: 1;
86
113
  --background: oklch(1 0 0);
87
114
  --foreground: oklch(0.129 0.042 264.695);
88
115
  --card: oklch(1 0 0);