@lark-apaas/coding-template-nestjs-react-fullstack 0.1.36-alpha.20260903095202 → 0.1.36-alpha.20260903185731

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 (112) hide show
  1. package/package.json +1 -1
  2. package/template/client/src/components/ui/markdown.tsx +43 -0
  3. package/template/client/src/index.css +0 -1
  4. package/template/package-lock.json +5783 -13022
  5. package/template/package.json +5 -68
  6. package/template/client/src/components/business-ui/README.md +0 -519
  7. package/template/client/src/components/business-ui/api/chats/queries.ts +0 -39
  8. package/template/client/src/components/business-ui/api/chats/service.ts +0 -29
  9. package/template/client/src/components/business-ui/api/departments/queries.ts +0 -42
  10. package/template/client/src/components/business-ui/api/departments/service.ts +0 -18
  11. package/template/client/src/components/business-ui/api/files/service.ts +0 -28
  12. package/template/client/src/components/business-ui/api/user-profiles/queries.ts +0 -42
  13. package/template/client/src/components/business-ui/api/user-profiles/service.ts +0 -23
  14. package/template/client/src/components/business-ui/api/users/queries.ts +0 -34
  15. package/template/client/src/components/business-ui/api/users/service.ts +0 -40
  16. package/template/client/src/components/business-ui/chat-select/README.md +0 -128
  17. package/template/client/src/components/business-ui/chat-select/chat-item.tsx +0 -85
  18. package/template/client/src/components/business-ui/chat-select/chat-select-tag.tsx +0 -83
  19. package/template/client/src/components/business-ui/chat-select/chat-select.tsx +0 -197
  20. package/template/client/src/components/business-ui/chat-select/index.tsx +0 -12
  21. package/template/client/src/components/business-ui/chat-select/types.ts +0 -102
  22. package/template/client/src/components/business-ui/chat-select/use-chat-value.ts +0 -180
  23. package/template/client/src/components/business-ui/chat-select/utils.ts +0 -81
  24. package/template/client/src/components/business-ui/department-select/department-item.tsx +0 -55
  25. package/template/client/src/components/business-ui/department-select/department-select-field.tsx +0 -86
  26. package/template/client/src/components/business-ui/department-select/department-select-tag.tsx +0 -56
  27. package/template/client/src/components/business-ui/department-select/department-select.tsx +0 -168
  28. package/template/client/src/components/business-ui/department-select/icon-department.tsx +0 -22
  29. package/template/client/src/components/business-ui/department-select/index.tsx +0 -7
  30. package/template/client/src/components/business-ui/department-select/types.ts +0 -15
  31. package/template/client/src/components/business-ui/department-select/utils.ts +0 -17
  32. package/template/client/src/components/business-ui/entity-combobox/base-combobox-content.tsx +0 -35
  33. package/template/client/src/components/business-ui/entity-combobox/base-combobox-empty.tsx +0 -28
  34. package/template/client/src/components/business-ui/entity-combobox/base-combobox-error.tsx +0 -35
  35. package/template/client/src/components/business-ui/entity-combobox/base-combobox-item.tsx +0 -55
  36. package/template/client/src/components/business-ui/entity-combobox/base-combobox-list.tsx +0 -174
  37. package/template/client/src/components/business-ui/entity-combobox/base-combobox-loading.tsx +0 -19
  38. package/template/client/src/components/business-ui/entity-combobox/base-combobox-search.tsx +0 -31
  39. package/template/client/src/components/business-ui/entity-combobox/base-combobox-trigger.tsx +0 -281
  40. package/template/client/src/components/business-ui/entity-combobox/base-combobox.tsx +0 -132
  41. package/template/client/src/components/business-ui/entity-combobox/context.tsx +0 -19
  42. package/template/client/src/components/business-ui/entity-combobox/entity-combobox.tsx +0 -145
  43. package/template/client/src/components/business-ui/entity-combobox/highlight-text.tsx +0 -52
  44. package/template/client/src/components/business-ui/entity-combobox/hooks.tsx +0 -21
  45. package/template/client/src/components/business-ui/entity-combobox/index.tsx +0 -31
  46. package/template/client/src/components/business-ui/entity-combobox/item-pill.tsx +0 -179
  47. package/template/client/src/components/business-ui/entity-combobox/popover-wrapper.tsx +0 -149
  48. package/template/client/src/components/business-ui/entity-combobox/search-trigger.tsx +0 -262
  49. package/template/client/src/components/business-ui/entity-combobox/shared-types.ts +0 -296
  50. package/template/client/src/components/business-ui/entity-combobox/size-variants.tsx +0 -322
  51. package/template/client/src/components/business-ui/entity-combobox/types.ts +0 -246
  52. package/template/client/src/components/business-ui/entity-combobox/use-fetch-data.tsx +0 -81
  53. package/template/client/src/components/business-ui/entity-combobox/use-infinite-scroll.tsx +0 -99
  54. package/template/client/src/components/business-ui/entity-combobox/use-popover-outside-click.tsx +0 -32
  55. package/template/client/src/components/business-ui/form/checkbox-field.tsx +0 -83
  56. package/template/client/src/components/business-ui/form/context.tsx +0 -38
  57. package/template/client/src/components/business-ui/form/field-layout.tsx +0 -91
  58. package/template/client/src/components/business-ui/form/form.tsx +0 -54
  59. package/template/client/src/components/business-ui/form/hooks/form-context.tsx +0 -4
  60. package/template/client/src/components/business-ui/form/hooks/form-utils.ts +0 -23
  61. package/template/client/src/components/business-ui/form/hooks/form.tsx +0 -40
  62. package/template/client/src/components/business-ui/form/index.tsx +0 -4
  63. package/template/client/src/components/business-ui/form/input-field.tsx +0 -74
  64. package/template/client/src/components/business-ui/form/radio-group-field.tsx +0 -138
  65. package/template/client/src/components/business-ui/form/select-field.tsx +0 -155
  66. package/template/client/src/components/business-ui/form/switch-field.tsx +0 -66
  67. package/template/client/src/components/business-ui/form/textarea-field.tsx +0 -72
  68. package/template/client/src/components/business-ui/form/types.ts +0 -32
  69. package/template/client/src/components/business-ui/tiptap-editor/README.md +0 -324
  70. package/template/client/src/components/business-ui/tiptap-editor/components/attachment-toolbar-button.tsx +0 -74
  71. package/template/client/src/components/business-ui/tiptap-editor/components/blockquote-toolbar-button.tsx +0 -41
  72. package/template/client/src/components/business-ui/tiptap-editor/components/code-block-toolbar-button.tsx +0 -41
  73. package/template/client/src/components/business-ui/tiptap-editor/components/color-highlight-toolbar-button.tsx +0 -141
  74. package/template/client/src/components/business-ui/tiptap-editor/components/heading-toolbar-button.tsx +0 -99
  75. package/template/client/src/components/business-ui/tiptap-editor/components/horizontal-rule-toolbar-button.tsx +0 -39
  76. package/template/client/src/components/business-ui/tiptap-editor/components/image-upload-toolbar-button.tsx +0 -65
  77. package/template/client/src/components/business-ui/tiptap-editor/components/link-edit-form.tsx +0 -127
  78. package/template/client/src/components/business-ui/tiptap-editor/components/link-hover-toolbar.tsx +0 -380
  79. package/template/client/src/components/business-ui/tiptap-editor/components/link-toolbar-button.tsx +0 -96
  80. package/template/client/src/components/business-ui/tiptap-editor/components/list-toolbar-button.tsx +0 -75
  81. package/template/client/src/components/business-ui/tiptap-editor/components/mark-toolbar-button.tsx +0 -118
  82. package/template/client/src/components/business-ui/tiptap-editor/components/text-align-toolbar-button.tsx +0 -84
  83. package/template/client/src/components/business-ui/tiptap-editor/components/undo-redo-toolbar-button.tsx +0 -54
  84. package/template/client/src/components/business-ui/tiptap-editor/extensions/attachment.tsx +0 -539
  85. package/template/client/src/components/business-ui/tiptap-editor/extensions/code-block-shiki.tsx +0 -236
  86. package/template/client/src/components/business-ui/tiptap-editor/extensions/complete-kit.ts +0 -260
  87. package/template/client/src/components/business-ui/tiptap-editor/extensions/image.tsx +0 -456
  88. package/template/client/src/components/business-ui/tiptap-editor/hooks/use-tiptap-editor.ts +0 -47
  89. package/template/client/src/components/business-ui/tiptap-editor/index.ts +0 -38
  90. package/template/client/src/components/business-ui/tiptap-editor/tiptap-editor-complete.tsx +0 -112
  91. package/template/client/src/components/business-ui/tiptap-editor/tiptap-editor.tsx +0 -169
  92. package/template/client/src/components/business-ui/types/user.ts +0 -74
  93. package/template/client/src/components/business-ui/user-display/index.tsx +0 -2
  94. package/template/client/src/components/business-ui/user-display/overflow-tooltip-text.tsx +0 -90
  95. package/template/client/src/components/business-ui/user-display/type.ts +0 -27
  96. package/template/client/src/components/business-ui/user-display/user-display.tsx +0 -99
  97. package/template/client/src/components/business-ui/user-display/user-with-avatar.tsx +0 -158
  98. package/template/client/src/components/business-ui/user-display/utils.ts +0 -37
  99. package/template/client/src/components/business-ui/user-profile/error-image.tsx +0 -302
  100. package/template/client/src/components/business-ui/user-profile/user-external-script.ts +0 -73
  101. package/template/client/src/components/business-ui/user-profile/user-profile.tsx +0 -370
  102. package/template/client/src/components/business-ui/user-select/index.tsx +0 -28
  103. package/template/client/src/components/business-ui/user-select/types.ts +0 -109
  104. package/template/client/src/components/business-ui/user-select/use-user-value.ts +0 -160
  105. package/template/client/src/components/business-ui/user-select/user-item.tsx +0 -98
  106. package/template/client/src/components/business-ui/user-select/user-pill.tsx +0 -140
  107. package/template/client/src/components/business-ui/user-select/user-select-tag.tsx +0 -82
  108. package/template/client/src/components/business-ui/user-select/user-select.tsx +0 -359
  109. package/template/client/src/components/business-ui/user-select/utils.tsx +0 -188
  110. package/template/client/src/components/business-ui/utils/user.ts +0 -72
  111. package/template/client/src/components/ui/streamdown.tsx +0 -186
  112. package/template/client/src/lib/shiki.ts +0 -92
@@ -1,281 +0,0 @@
1
- 'use client';
2
-
3
- import { Plus } from 'lucide-react';
4
-
5
- import { useEntityComboboxContext } from '@client/src/components/business-ui/entity-combobox/context';
6
- import { SearchTrigger } from '@client/src/components/business-ui/entity-combobox/search-trigger';
7
- import {
8
- addButtonIconVariants,
9
- addButtonVariants,
10
- type ComboboxSize,
11
- } from '@client/src/components/business-ui/entity-combobox/size-variants';
12
- import type {
13
- ClassNamesConfig,
14
- ItemValue,
15
- TriggerRenderProps,
16
- TriggerType,
17
- } from '@client/src/components/business-ui/entity-combobox/types';
18
- import { cn } from '@/lib/utils';
19
- import { Badge } from '@client/src/components/ui/badge';
20
- import { Button } from '@client/src/components/ui/button';
21
- import {
22
- Tooltip,
23
- TooltipContent,
24
- TooltipTrigger,
25
- } from '@client/src/components/ui/tooltip';
26
-
27
- export type BaseComboboxTriggerProps = {
28
- triggerType?: TriggerType;
29
- size?: ComboboxSize;
30
- placeholder?: string;
31
- renderTrigger?: (props: TriggerRenderProps) => React.ReactNode;
32
- maxTagCount?: number | 'responsive';
33
- maxTagTextLength?: number;
34
- tagClosable?: boolean;
35
- classNames?: ClassNamesConfig;
36
- onFocus?: (e: React.FocusEvent<HTMLElement>) => void;
37
- onBlur?: (e: React.FocusEvent<HTMLElement>) => void;
38
- renderTag?: (
39
- value: ItemValue,
40
- onClose: (value: ItemValue, e: React.MouseEvent) => void,
41
- disabled?: boolean,
42
- ) => React.ReactNode;
43
- /**
44
- * 判断选项是否禁用
45
- */
46
- getOptionDisabled?: (value: ItemValue) => boolean;
47
- /**
48
- * button 模式下,自定义渲染加号按钮的包裹器
49
- * 用于将按钮包裹在 PopoverTrigger 中,使浮层跟随加号按钮而非整个容器
50
- */
51
- renderAddButton?: (button: React.ReactNode) => React.ReactNode;
52
- ref?: React.Ref<HTMLButtonElement>;
53
- } & Omit<React.ComponentPropsWithoutRef<'button'>, 'children'>;
54
-
55
- export const BaseComboboxTrigger = ({
56
- triggerType = 'button',
57
- size = 'medium',
58
- placeholder = '请选择',
59
- renderTrigger,
60
- maxTagCount,
61
- maxTagTextLength,
62
- tagClosable = true,
63
- classNames,
64
- onFocus,
65
- onBlur,
66
- renderTag,
67
- getOptionDisabled,
68
- renderAddButton,
69
- ref,
70
- ...props
71
- }: BaseComboboxTriggerProps) => {
72
- const {
73
- selectedValue,
74
- multiple,
75
- open,
76
- disabled,
77
- handleClear,
78
- handleDeselect,
79
- } = useEntityComboboxContext();
80
-
81
- // 自定义渲染 (custom 类型)
82
- if (triggerType === 'custom' && renderTrigger) {
83
- return <>{renderTrigger({ selectedValue, multiple, open, disabled })}</>;
84
- }
85
-
86
- // search 类型
87
- if (triggerType === 'search') {
88
- return (
89
- <SearchTrigger
90
- size={size}
91
- placeholder={placeholder}
92
- maxTagCount={maxTagCount}
93
- maxTagTextLength={maxTagTextLength}
94
- tagClosable={tagClosable}
95
- classNames={classNames}
96
- onFocus={onFocus as (e: React.FocusEvent<HTMLDivElement>) => void}
97
- onBlur={onBlur as (e: React.FocusEvent<HTMLDivElement>) => void}
98
- renderTag={renderTag}
99
- getOptionDisabled={getOptionDisabled}
100
- />
101
- );
102
- }
103
-
104
- // button 类型(默认)
105
- const hasValue = !multiple
106
- ? !!selectedValue
107
- : Array.isArray(selectedValue) && selectedValue.length > 0;
108
-
109
- // 计算最大显示标签数
110
- const maxDisplayCount =
111
- typeof maxTagCount === 'number' ? maxTagCount : Infinity;
112
-
113
- // 计算隐藏的标签
114
- const hiddenCount =
115
- multiple && Array.isArray(selectedValue) && typeof maxTagCount === 'number'
116
- ? Math.max(0, selectedValue.length - maxTagCount)
117
- : 0;
118
-
119
- const hiddenTags =
120
- multiple && Array.isArray(selectedValue) && typeof maxTagCount === 'number'
121
- ? selectedValue.slice(maxTagCount)
122
- : [];
123
-
124
- // 标签关闭回调
125
- const handleTagClose = (value: ItemValue, e: React.MouseEvent) => {
126
- e.stopPropagation(); // 阻止触发按钮点击
127
- // 禁用的选项不允许关闭
128
- if (getOptionDisabled?.(value)) return;
129
- if (!multiple) {
130
- handleClear(); // 单选模式清空
131
- } else {
132
- handleDeselect(value); // 多选模式删除指定项
133
- }
134
- };
135
-
136
- return (
137
- <div
138
- className={cn(
139
- 'flex w-full flex-wrap items-center gap-2',
140
- classNames?.trigger,
141
- )}
142
- >
143
- {/* 已选择的标签显示区域 */}
144
- {hasValue && (
145
- <>
146
- {!multiple && selectedValue ? (
147
- renderTag ? (
148
- renderTag(
149
- selectedValue as ItemValue,
150
- handleTagClose,
151
- getOptionDisabled?.(selectedValue as ItemValue),
152
- )
153
- ) : (
154
- <Badge
155
- variant="secondary"
156
- className={cn('h-7 text-xs', classNames?.tag)}
157
- >
158
- {(selectedValue as ItemValue).name}
159
- </Badge>
160
- )
161
- ) : multiple &&
162
- Array.isArray(selectedValue) &&
163
- selectedValue.length > 0 ? (
164
- <>
165
- {renderTag ? (
166
- <>
167
- {selectedValue
168
- .slice(0, maxDisplayCount)
169
- .map((item) =>
170
- renderTag(
171
- item,
172
- handleTagClose,
173
- getOptionDisabled?.(item),
174
- ),
175
- )}
176
- {hiddenCount > 0 && (
177
- <Tooltip delayDuration={200}>
178
- <TooltipTrigger asChild>
179
- <Badge
180
- variant="secondary"
181
- className={cn(
182
- 'h-7 cursor-pointer text-xs',
183
- classNames?.tag,
184
- )}
185
- >
186
- +{hiddenCount}
187
- </Badge>
188
- </TooltipTrigger>
189
- <TooltipContent
190
- className="w-auto max-w-80 p-2"
191
- align="start"
192
- >
193
- <div className="flex flex-wrap gap-1">
194
- {hiddenTags.map((item) =>
195
- renderTag(
196
- item,
197
- handleTagClose,
198
- getOptionDisabled?.(item),
199
- ),
200
- )}
201
- </div>
202
- </TooltipContent>
203
- </Tooltip>
204
- )}
205
- </>
206
- ) : (
207
- <>
208
- {selectedValue.slice(0, maxDisplayCount).map((item) => (
209
- <Badge
210
- key={item.id}
211
- variant="secondary"
212
- className={cn('h-7 text-xs', classNames?.tag)}
213
- >
214
- {item.name}
215
- </Badge>
216
- ))}
217
- {hiddenCount > 0 && (
218
- <Tooltip delayDuration={200}>
219
- <TooltipTrigger asChild>
220
- <Badge
221
- variant="secondary"
222
- className={cn(
223
- 'h-7 cursor-pointer text-xs',
224
- classNames?.tag,
225
- )}
226
- >
227
- +{hiddenCount}
228
- </Badge>
229
- </TooltipTrigger>
230
- <TooltipContent
231
- className="w-auto max-w-80 p-2"
232
- align="start"
233
- >
234
- <div className="flex flex-wrap gap-1">
235
- {hiddenTags.map((item) => (
236
- <Badge
237
- key={item.id}
238
- variant="secondary"
239
- className={cn('h-7 text-xs', classNames?.tag)}
240
- >
241
- {item.name}
242
- </Badge>
243
- ))}
244
- </div>
245
- </TooltipContent>
246
- </Tooltip>
247
- )}
248
- </>
249
- )}
250
- </>
251
- ) : null}
252
- </>
253
- )}
254
-
255
- {/* 加号按钮 - 紧跟标签 */}
256
- {(() => {
257
- const addButton = (
258
- <Button
259
- ref={ref}
260
- variant="secondary"
261
- size="icon"
262
- role="combobox"
263
- aria-expanded={open}
264
- data-state={open ? 'open' : 'closed'}
265
- aria-controls="combobox-content"
266
- disabled={disabled}
267
- className={addButtonVariants({ size })}
268
- onFocus={
269
- onFocus as (e: React.FocusEvent<HTMLButtonElement>) => void
270
- }
271
- onBlur={onBlur as (e: React.FocusEvent<HTMLButtonElement>) => void}
272
- {...props}
273
- >
274
- <Plus className={addButtonIconVariants({ size })} />
275
- </Button>
276
- );
277
- return renderAddButton ? renderAddButton(addButton) : addButton;
278
- })()}
279
- </div>
280
- );
281
- };
@@ -1,132 +0,0 @@
1
- 'use client';
2
-
3
- import { BaseComboboxContent } from '@client/src/components/business-ui/entity-combobox/base-combobox-content';
4
- import { BaseComboboxEmpty } from '@client/src/components/business-ui/entity-combobox/base-combobox-empty';
5
- import { BaseComboboxItem } from '@client/src/components/business-ui/entity-combobox/base-combobox-item';
6
- import { BaseComboboxList } from '@client/src/components/business-ui/entity-combobox/base-combobox-list';
7
- import { BaseComboboxLoading } from '@client/src/components/business-ui/entity-combobox/base-combobox-loading';
8
- import { BaseComboboxSearch } from '@client/src/components/business-ui/entity-combobox/base-combobox-search';
9
- import { EntityCombobox } from '@client/src/components/business-ui/entity-combobox/entity-combobox';
10
- import { PopoverWrapper } from '@client/src/components/business-ui/entity-combobox/popover-wrapper';
11
- import type {
12
- BaseComboboxProps,
13
- ItemValue,
14
- } from '@client/src/components/business-ui/entity-combobox/types';
15
-
16
- export function BaseCombobox<
17
- T = unknown,
18
- TRaw = unknown,
19
- TValue extends ItemValue<TRaw> = ItemValue<TRaw>,
20
- >(props: BaseComboboxProps<T, TRaw, TValue>) {
21
- const {
22
- fetchFn,
23
- size = 'medium',
24
- triggerType = 'button',
25
- renderTrigger,
26
- multiple = false,
27
- value,
28
- defaultValue,
29
- onChange,
30
- open,
31
- defaultOpen,
32
- onOpenChange,
33
- debounce = 300,
34
- disabled = false,
35
- showSearch = true,
36
- renderItem,
37
- renderTag,
38
- placeholder = '请选择',
39
- searchPlaceholder = '',
40
- emptyText = '没有匹配结果,换个关键词试试吧',
41
- loadingText = '加载中...',
42
- className,
43
- classNames = {},
44
- tagClosable = true,
45
- maxTagCount,
46
- maxTagTextLength,
47
- placement = 'bottom',
48
- align = 'start',
49
- sideOffset = 4,
50
- onSelect,
51
- onDeselect,
52
- onSearch,
53
- onClear,
54
- onFocus,
55
- onBlur,
56
- getItemValue = (item: unknown) =>
57
- ({
58
- id:
59
- (item as { id?: string; value?: string }).id ||
60
- (item as { id?: string; value?: string }).value ||
61
- '',
62
- name:
63
- (item as { name?: string; label?: string }).name ||
64
- (item as { name?: string; label?: string }).label ||
65
- '',
66
- avatar: (item as { avatar?: string }).avatar,
67
- raw: item,
68
- }) as TValue,
69
- getOptionDisabled,
70
- slotProps,
71
- children,
72
- } = props;
73
-
74
- // 浮层包装组件的通用 props
75
- const popoverProps = {
76
- size,
77
- triggerType,
78
- renderTrigger,
79
- placeholder,
80
- maxTagCount,
81
- maxTagTextLength,
82
- tagClosable,
83
- className,
84
- classNames,
85
- placement,
86
- align,
87
- sideOffset,
88
- showSearch,
89
- searchPlaceholder,
90
- renderItem: renderItem!,
91
- renderTag,
92
- getItemValue,
93
- getOptionDisabled,
94
- emptyText,
95
- loadingText,
96
- onFocus,
97
- onBlur,
98
- slotProps,
99
- children,
100
- };
101
-
102
- return (
103
- <EntityCombobox
104
- fetchFn={fetchFn}
105
- size={size}
106
- multiple={multiple}
107
- value={value}
108
- defaultValue={defaultValue}
109
- onChange={onChange}
110
- open={open}
111
- defaultOpen={defaultOpen}
112
- onOpenChange={onOpenChange}
113
- debounce={debounce}
114
- disabled={disabled}
115
- onSelect={onSelect}
116
- onDeselect={onDeselect}
117
- onSearch={onSearch}
118
- onClear={onClear}
119
- getItemValue={getItemValue}
120
- >
121
- <PopoverWrapper {...popoverProps} />
122
- </EntityCombobox>
123
- );
124
- }
125
-
126
- // 导出子组件以支持自定义使用
127
- BaseCombobox.Content = BaseComboboxContent;
128
- BaseCombobox.Search = BaseComboboxSearch;
129
- BaseCombobox.List = BaseComboboxList;
130
- BaseCombobox.Item = BaseComboboxItem;
131
- BaseCombobox.Empty = BaseComboboxEmpty;
132
- BaseCombobox.Loading = BaseComboboxLoading;
@@ -1,19 +0,0 @@
1
- 'use client';
2
-
3
- import { createContext, useContext } from 'react';
4
-
5
- import type { EntityComboboxContextValue } from '@client/src/components/business-ui/entity-combobox/types';
6
-
7
- const EntityComboboxContext = createContext<any | undefined>(undefined);
8
-
9
- export const EntityComboboxProvider = EntityComboboxContext.Provider;
10
-
11
- export function useEntityComboboxContext(): EntityComboboxContextValue {
12
- const context = useContext(EntityComboboxContext);
13
- if (!context) {
14
- throw new Error(
15
- 'useEntityComboboxContext must be used within EntityCombobox',
16
- );
17
- }
18
- return context;
19
- }
@@ -1,145 +0,0 @@
1
- 'use client';
2
-
3
- import { useCallback, useState } from 'react';
4
- import { useControllableState } from 'radix-ui/internal';
5
-
6
- import { EntityComboboxProvider } from '@client/src/components/business-ui/entity-combobox/context';
7
- import { useDebounce } from '@client/src/components/business-ui/entity-combobox/hooks';
8
- import type {
9
- EntityComboboxProps,
10
- ItemValue,
11
- } from '@client/src/components/business-ui/entity-combobox/types';
12
- import { useFetchData } from '@client/src/components/business-ui/entity-combobox/use-fetch-data';
13
-
14
- export function EntityCombobox<
15
- T = unknown,
16
- TRaw = unknown,
17
- TValue extends ItemValue<TRaw> = ItemValue<TRaw>,
18
- >(props: EntityComboboxProps<T, TRaw, TValue>) {
19
- const {
20
- fetchFn,
21
- size = 'medium',
22
- multiple = false,
23
- value: valueProp,
24
- defaultValue,
25
- onChange,
26
- open: openProp,
27
- defaultOpen = false,
28
- onOpenChange,
29
- debounce = 300,
30
- disabled = false,
31
- onSelect,
32
- onDeselect,
33
- onSearch,
34
- onClear,
35
- children,
36
- getItemValue,
37
- } = props;
38
-
39
- // 受控状态
40
- const [selectedValue, setSelectedValue] = useControllableState({
41
- prop: valueProp,
42
- defaultProp: defaultValue ?? (!multiple ? null : []),
43
- onChange,
44
- });
45
-
46
- const [searchValue, setSearchValue] = useState('');
47
- const debouncedSearch = useDebounce(searchValue, debounce);
48
-
49
- // 代理 onOpenChange,关闭时清空搜索
50
- const handleOpenChange = useCallback(
51
- (newOpen: boolean) => {
52
- if (!newOpen) {
53
- setSearchValue('');
54
- }
55
- onOpenChange?.(newOpen);
56
- },
57
- [onOpenChange],
58
- );
59
-
60
- const [open, setOpen] = useControllableState({
61
- prop: openProp,
62
- defaultProp: defaultOpen,
63
- onChange: handleOpenChange,
64
- });
65
-
66
- // 使用自定义 hook 处理数据请求
67
- const { data, isFetching, isError, isSuccess, fetchStatus, refetch } =
68
- useFetchData<T>({
69
- fetchFn,
70
- enabled: open,
71
- search: debouncedSearch,
72
- onSearch,
73
- });
74
-
75
- // 选择逻辑
76
- const handleSelect = useCallback(
77
- (itemValue: TValue) => {
78
- if (!multiple) {
79
- setSelectedValue(itemValue);
80
- setOpen(false);
81
- onSelect?.(itemValue);
82
- } else {
83
- const current = Array.isArray(selectedValue) ? selectedValue : [];
84
- const isSelected = current.some((v) => v.id === itemValue.id);
85
-
86
- if (isSelected) {
87
- const newValue = current.filter((v) => v.id !== itemValue.id);
88
- setSelectedValue(newValue);
89
- onDeselect?.(itemValue);
90
- } else {
91
- const newValue = [...current, itemValue];
92
- setSelectedValue(newValue);
93
- onSelect?.(itemValue);
94
- }
95
- }
96
- },
97
- [multiple, selectedValue, setSelectedValue, setOpen, onSelect, onDeselect],
98
- );
99
-
100
- const handleDeselect = useCallback(
101
- (itemValue: TValue) => {
102
- if (multiple && Array.isArray(selectedValue)) {
103
- const newValue = selectedValue.filter((v) => v.id !== itemValue.id);
104
- setSelectedValue(newValue);
105
- onDeselect?.(itemValue);
106
- }
107
- },
108
- [multiple, selectedValue, setSelectedValue, onDeselect],
109
- );
110
-
111
- const handleClear = useCallback(() => {
112
- setSelectedValue(!multiple ? null : []);
113
- onClear?.();
114
- }, [multiple, setSelectedValue, onClear]);
115
-
116
- // Context 值
117
- const contextValue = {
118
- open,
119
- setOpen,
120
- searchValue,
121
- setSearchValue,
122
- selectedValue: selectedValue ?? (!multiple ? null : []),
123
- debouncedSearch,
124
- handleSelect,
125
- handleDeselect,
126
- handleClear,
127
- data,
128
- isFetching,
129
- isError,
130
- refetch,
131
- isSuccess,
132
- fetchStatus,
133
- isPlaceholderData: false,
134
- multiple,
135
- disabled,
136
- size,
137
- getItemValue: getItemValue as (item: unknown) => ItemValue,
138
- };
139
-
140
- return (
141
- <EntityComboboxProvider value={contextValue}>
142
- {children}
143
- </EntityComboboxProvider>
144
- );
145
- }
@@ -1,52 +0,0 @@
1
- 'use client';
2
-
3
- import { useMemo } from 'react';
4
-
5
- export type HighlightTextProps = {
6
- text: string;
7
- highlight?: string;
8
- className?: string;
9
- highlightClassName?: string;
10
- };
11
-
12
- /**
13
- * 高亮文本组件
14
- * 将文本中匹配的部分用高亮样式标记
15
- */
16
- export const HighlightText = ({
17
- text,
18
- highlight,
19
- className,
20
- highlightClassName = 'text-primary',
21
- }: HighlightTextProps) => {
22
- const parts = useMemo(() => {
23
- if (!highlight?.trim()) {
24
- return [{ text, isMatch: false }];
25
- }
26
-
27
- const escapedHighlight = highlight.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
28
- const regex = new RegExp(`(${escapedHighlight})`, 'gi');
29
- const splitParts = text.split(regex);
30
-
31
- return splitParts
32
- .filter((part) => part !== '')
33
- .map((part) => ({
34
- text: part,
35
- isMatch: part.toLowerCase() === highlight.toLowerCase(),
36
- }));
37
- }, [text, highlight]);
38
-
39
- return (
40
- <span className={className}>
41
- {parts.map((part, index) =>
42
- part.isMatch ? (
43
- <span key={index} className={highlightClassName}>
44
- {part.text}
45
- </span>
46
- ) : (
47
- <span key={index}>{part.text}</span>
48
- ),
49
- )}
50
- </span>
51
- );
52
- };
@@ -1,21 +0,0 @@
1
- 'use client';
2
-
3
- import { useEffect, useState } from 'react';
4
-
5
- export { useEntityComboboxContext } from '@client/src/components/business-ui/entity-combobox/context';
6
-
7
- export function useDebounce<T>(value: T, delay: number): T {
8
- const [debouncedValue, setDebouncedValue] = useState(value);
9
-
10
- useEffect(() => {
11
- const handler = setTimeout(() => {
12
- setDebouncedValue(value);
13
- }, delay);
14
-
15
- return () => {
16
- clearTimeout(handler);
17
- };
18
- }, [value, delay]);
19
-
20
- return debouncedValue;
21
- }
@@ -1,31 +0,0 @@
1
- // Entity Combobox
2
- export { EntityCombobox } from '@client/src/components/business-ui/entity-combobox/entity-combobox';
3
- export { useEntityComboboxContext } from '@client/src/components/business-ui/entity-combobox/context';
4
- export type {
5
- EntityComboboxProps,
6
- EntityComboboxContextValue,
7
- } from '@client/src/components/business-ui/entity-combobox/types';
8
-
9
- // Base Combobox
10
- export { BaseCombobox } from '@client/src/components/business-ui/entity-combobox/base-combobox';
11
- export { BaseComboboxItem } from '@client/src/components/business-ui/entity-combobox/base-combobox-item';
12
- export { BaseComboboxList } from '@client/src/components/business-ui/entity-combobox/base-combobox-list';
13
- export { BaseComboboxSearch } from '@client/src/components/business-ui/entity-combobox/base-combobox-search';
14
- export { BaseComboboxTrigger } from '@client/src/components/business-ui/entity-combobox/base-combobox-trigger';
15
- export { BaseComboboxContent } from '@client/src/components/business-ui/entity-combobox/base-combobox-content';
16
- export { BaseComboboxEmpty } from '@client/src/components/business-ui/entity-combobox/base-combobox-empty';
17
- export { BaseComboboxLoading } from '@client/src/components/business-ui/entity-combobox/base-combobox-loading';
18
- export { BaseComboboxError } from '@client/src/components/business-ui/entity-combobox/base-combobox-error';
19
-
20
- // Components
21
- export { ItemPill } from '@client/src/components/business-ui/entity-combobox/item-pill';
22
-
23
- // Types
24
- export type {
25
- ItemValue,
26
- TriggerType,
27
- TriggerRenderProps,
28
- BaseEntitySelectProps,
29
- ClassNamesConfig,
30
- PopoverSlotProps,
31
- } from '@client/src/components/business-ui/entity-combobox/shared-types';