@ahoo-wang/fetcher-viewer 2.15.0 → 2.15.2

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.
@@ -0,0 +1,412 @@
1
+ # @ahoo-wang/fetcher-viewer
2
+
3
+ [![npm version](https://img.shields.io/npm/v/@ahoo-wang/fetcher-viewer.svg)](https://www.npmjs.com/package/@ahoo-wang/fetcher-viewer)
4
+ [![Build Status](https://github.com/Ahoo-Wang/fetcher/actions/workflows/ci.yml/badge.svg)](https://github.com/Ahoo-Wang/fetcher/actions)
5
+ [![codecov](https://codecov.io/gh/Ahoo-Wang/fetcher/graph/badge.svg?token=JGiWZ52CvJ)](https://codecov.io/gh/Ahoo-Wang/fetcher)
6
+ [![License](https://img.shields.io/npm/l/@ahoo-wang/fetcher-viewer.svg)](https://github.com/Ahoo-Wang/fetcher/blob/main/LICENSE)
7
+ [![npm downloads](https://img.shields.io/npm/dm/@ahoo-wang/fetcher-viewer.svg)](https://www.npmjs.com/package/@ahoo-wang/fetcher-viewer)
8
+ [![npm bundle size](https://img.shields.io/bundlephobia/minzip/%40ahoo-wang%2Ffetcher-viewer)](https://www.npmjs.com/package/@ahoo-wang/fetcher-viewer)
9
+ [![Ask DeepWiki](https://deepwiki.com/badge.svg)](https://deepwiki.com/Ahoo-Wang/fetcher)
10
+
11
+ 一个全面的 React 组件库,用于数据可视化和过滤,基于 Ant Design 和 Fetcher 生态系统。提供可重用的 UI 组件,用于构建具有高级过滤功能的数据驱动应用程序。
12
+
13
+ ## ✨ 特性
14
+
15
+ - **🔍 高级过滤系统**: 完整的过滤面板,支持动态过滤器类型、操作符和状态管理
16
+ - **📊 数据组件**: 远程搜索选择器、标签输入、数字范围输入
17
+ - **🎨 Ant Design 集成**: 与 Ant Design 组件无缝集成
18
+ - **🔧 TypeScript 优先**: 完整的 TypeScript 支持和全面的类型定义
19
+ - **⚡ 性能优化**: 防抖搜索、高效渲染和优化的状态管理
20
+ - **🧪 完善的测试**: 使用 Vitest 和 React Testing Library 的全面测试覆盖
21
+
22
+ ## 📦 安装
23
+
24
+ ```bash
25
+ # 使用 npm
26
+ npm install @ahoo-wang/fetcher-viewer
27
+
28
+ # 使用 yarn
29
+ yarn add @ahoo-wang/fetcher-viewer
30
+
31
+ # 使用 pnpm
32
+ pnpm add @ahoo-wang/fetcher-viewer
33
+ ```
34
+
35
+ ## 🚀 快速开始
36
+
37
+ ```tsx
38
+ import {
39
+ RemoteSelect,
40
+ TagInput,
41
+ NumberRange,
42
+ FilterPanel,
43
+ } from '@ahoo-wang/fetcher-viewer';
44
+ import { useFilterState } from '@ahoo-wang/fetcher-viewer';
45
+
46
+ // 基本用法
47
+ function App() {
48
+ const { filters, addFilter, removeFilter, updateFilter } = useFilterState();
49
+
50
+ return (
51
+ <div>
52
+ {/* 远程搜索选择器 */}
53
+ <RemoteSelect
54
+ search={async query => {
55
+ const response = await fetch(`/api/search?q=${query}`);
56
+ return response.json();
57
+ }}
58
+ placeholder="搜索项目..."
59
+ />
60
+
61
+ {/* 标签输入 */}
62
+ <TagInput value={['tag1', 'tag2']} onChange={tags => console.log(tags)} />
63
+
64
+ {/* 数字范围 */}
65
+ <NumberRange value={[100, 500]} onChange={range => console.log(range)} />
66
+
67
+ {/* 高级过滤面板 */}
68
+ <FilterPanel
69
+ filters={filters}
70
+ onAddFilter={addFilter}
71
+ onRemoveFilter={removeFilter}
72
+ onUpdateFilter={updateFilter}
73
+ />
74
+ </div>
75
+ );
76
+ }
77
+ ```
78
+
79
+ ## 📚 API 参考
80
+
81
+ ### 组件
82
+
83
+ #### RemoteSelect
84
+
85
+ 一个防抖搜索选择组件,从远程 API 获取选项。
86
+
87
+ ```tsx
88
+ import { RemoteSelect } from '@ahoo-wang/fetcher-viewer';
89
+
90
+ <RemoteSelect
91
+ search={async (query: string) => {
92
+ // 返回选项数组
93
+ return [
94
+ { label: '选项 1', value: '1' },
95
+ { label: '选项 2', value: '2' },
96
+ ];
97
+ }}
98
+ debounce={{ delay: 300 }}
99
+ placeholder="搜索..."
100
+ onChange={value => console.log(value)}
101
+ />;
102
+ ```
103
+
104
+ **属性:**
105
+
106
+ - `search: (query: string) => Promise<RemoteSelectOption[]>` - 搜索函数
107
+ - `debounce?: UseDebouncedCallbackOptions` - 防抖配置
108
+ - `...SelectProps` - 所有 Ant Design Select 属性
109
+
110
+ #### TagInput
111
+
112
+ 支持不同值类型序列化的标签输入组件。
113
+
114
+ ```tsx
115
+ import { TagInput, StringTagValueItemSerializer, NumberTagValueItemSerializer } from '@ahoo-wang/fetcher-viewer';
116
+
117
+ // 字符串标签
118
+ <TagInput
119
+ value={['tag1', 'tag2']}
120
+ onChange={(tags) => console.log(tags)}
121
+ />
122
+
123
+ // 数字标签
124
+ <TagInput<number>
125
+ value={[1, 2, 3]}
126
+ serializer={NumberTagValueItemSerializer}
127
+ onChange={(tags) => console.log(tags)}
128
+ />
129
+ ```
130
+
131
+ **属性:**
132
+
133
+ - `value?: ValueItemType[]` - 当前标签值
134
+ - `serializer?: TagValueItemSerializer` - 值序列化器
135
+ - `onChange?: (value: ValueItemType[]) => void` - 变更处理器
136
+ - `...SelectProps` - 其他 Ant Design Select 属性
137
+
138
+ #### NumberRange
139
+
140
+ 带有最小/最大值验证的数字范围输入组件。
141
+
142
+ ```tsx
143
+ import { NumberRange } from '@ahoo-wang/fetcher-viewer';
144
+
145
+ <NumberRange
146
+ value={[100, 500]}
147
+ min={0}
148
+ max={1000}
149
+ precision={2}
150
+ placeholder={['最小值', '最大值']}
151
+ onChange={range => console.log(range)}
152
+ />;
153
+ ```
154
+
155
+ **属性:**
156
+
157
+ - `value?: number | NumberRangeValue` - 当前范围值
158
+ - `min?: number` - 允许的最小值
159
+ - `max?: number` - 允许的最大值
160
+ - `precision?: number` - 小数精度
161
+ - `placeholder?: string[]` - 输入占位符
162
+ - `onChange?: (value: NumberRangeValue) => void` - 变更处理器
163
+
164
+ ### 过滤系统
165
+
166
+ #### FilterPanel
167
+
168
+ 具有动态过滤器管理的综合过滤面板。
169
+
170
+ ```tsx
171
+ import { FilterPanel, useFilterState } from '@ahoo-wang/fetcher-viewer';
172
+
173
+ function MyFilterComponent() {
174
+ const { filters, addFilter, removeFilter, updateFilter } = useFilterState();
175
+
176
+ return (
177
+ <FilterPanel
178
+ filters={filters}
179
+ availableFilters={[
180
+ { name: 'name', label: '名称', type: 'text' },
181
+ { name: 'age', label: '年龄', type: 'number' },
182
+ { name: 'status', label: '状态', type: 'select' },
183
+ ]}
184
+ onAddFilter={addFilter}
185
+ onRemoveFilter={removeFilter}
186
+ onUpdateFilter={updateFilter}
187
+ />
188
+ );
189
+ }
190
+ ```
191
+
192
+ #### useFilterState Hook
193
+
194
+ 过滤器操作的状态管理 hook。
195
+
196
+ ```tsx
197
+ import { useFilterState } from '@ahoo-wang/fetcher-viewer';
198
+
199
+ const {
200
+ filters, // 当前过滤器数组
201
+ addFilter, // 添加新过滤器
202
+ removeFilter, // 移除过滤器
203
+ updateFilter, // 更新过滤器
204
+ clearFilters, // 清除所有过滤器
205
+ getFilterValue, // 获取过滤器值
206
+ setFilterValue, // 设置过滤器值
207
+ resetFilters, // 重置到初始状态
208
+ } = useFilterState(initialFilters);
209
+ ```
210
+
211
+ #### 过滤器类型
212
+
213
+ 库提供了几种内置过滤器类型:
214
+
215
+ - **TextFilter**: 文本输入,支持各种操作符(=、!=、包含等)
216
+ - **NumberFilter**: 数字输入,支持比较操作符
217
+ - **SelectFilter**: 下拉选择过滤器
218
+ - **IdFilter**: 基于 ID 的过滤器
219
+ - **AssemblyFilter**: 组合多个条件的复合过滤器
220
+
221
+ #### 自定义过滤器
222
+
223
+ 通过实现 `FilterProps` 接口创建自定义过滤器组件:
224
+
225
+ ```tsx
226
+ import { FilterProps, FilterValue } from '@ahoo-wang/fetcher-viewer';
227
+
228
+ function CustomFilter({ field, onChange, value }: FilterProps) {
229
+ return (
230
+ <div>
231
+ <label>{field.label}</label>
232
+ <input
233
+ value={value?.value || ''}
234
+ onChange={e =>
235
+ onChange?.({
236
+ field: field.name,
237
+ operator: 'eq',
238
+ value: e.target.value,
239
+ })
240
+ }
241
+ />
242
+ </div>
243
+ );
244
+ }
245
+ ```
246
+
247
+ ## 🎨 主题和样式
248
+
249
+ 组件继承 Ant Design 的主题系统。您可以使用 Ant Design 的主题配置自定义外观:
250
+
251
+ ```tsx
252
+ import { ConfigProvider } from 'antd';
253
+
254
+ <ConfigProvider
255
+ theme={
256
+ {
257
+ /* 您的主题配置 */
258
+ }
259
+ }
260
+ >
261
+ <RemoteSelect search={searchFunction} />
262
+ </ConfigProvider>;
263
+ ```
264
+
265
+ ## 🌐 国际化
266
+
267
+ 过滤系统支持多种语言。目前支持的语言环境:
268
+
269
+ - **英语**(默认)
270
+ - **中文**(`zh_CN`)
271
+
272
+ ```tsx
273
+ import { FilterPanel } from '@ahoo-wang/fetcher-viewer';
274
+ import { zh_CN } from '@ahoo-wang/fetcher-viewer/locale';
275
+
276
+ <FilterPanel
277
+ locale={zh_CN}
278
+ // ... 其他属性
279
+ />;
280
+ ```
281
+
282
+ ## 🧪 测试
283
+
284
+ 库包含全面的测试。使用以下命令运行测试:
285
+
286
+ ```bash
287
+ # 运行所有测试
288
+ npm test
289
+
290
+ # 运行带覆盖率的测试
291
+ npm run test:coverage
292
+
293
+ # 在 UI 模式下运行测试
294
+ npm run test:ui
295
+ ```
296
+
297
+ ## 📖 示例
298
+
299
+ ### 带过滤器的基本数据表格
300
+
301
+ ```tsx
302
+ import React, { useState, useEffect } from 'react';
303
+ import {
304
+ FilterPanel,
305
+ useFilterState,
306
+ RemoteSelect,
307
+ } from '@ahoo-wang/fetcher-viewer';
308
+ import { Table } from 'antd';
309
+
310
+ function DataTable() {
311
+ const [data, setData] = useState([]);
312
+ const [loading, setLoading] = useState(false);
313
+ const { filters, addFilter, removeFilter, updateFilter } = useFilterState();
314
+
315
+ useEffect(() => {
316
+ fetchData();
317
+ }, [filters]);
318
+
319
+ const fetchData = async () => {
320
+ setLoading(true);
321
+ try {
322
+ const query = buildQueryFromFilters(filters);
323
+ const response = await fetch(`/api/data?${query}`);
324
+ const result = await response.json();
325
+ setData(result);
326
+ } finally {
327
+ setLoading(false);
328
+ }
329
+ };
330
+
331
+ return (
332
+ <div>
333
+ <FilterPanel
334
+ filters={filters}
335
+ availableFilters={FILTER_CONFIG}
336
+ onAddFilter={addFilter}
337
+ onRemoveFilter={removeFilter}
338
+ onUpdateFilter={updateFilter}
339
+ />
340
+
341
+ <Table dataSource={data} loading={loading} columns={COLUMNS} />
342
+ </div>
343
+ );
344
+ }
345
+ ```
346
+
347
+ ### 高级搜索组件
348
+
349
+ ```tsx
350
+ import { RemoteSelect, TagInput } from '@ahoo-wang/fetcher-viewer';
351
+
352
+ function AdvancedSearch() {
353
+ const [selectedTags, setSelectedTags] = useState([]);
354
+ const [selectedItems, setSelectedItems] = useState([]);
355
+
356
+ return (
357
+ <div className="search-container">
358
+ <RemoteSelect
359
+ search={async query => {
360
+ const response = await api.search(query);
361
+ return response.map(item => ({
362
+ label: item.name,
363
+ value: item.id,
364
+ }));
365
+ }}
366
+ mode="multiple"
367
+ placeholder="搜索项目..."
368
+ value={selectedItems}
369
+ onChange={setSelectedItems}
370
+ />
371
+
372
+ <TagInput
373
+ placeholder="添加标签..."
374
+ value={selectedTags}
375
+ onChange={setSelectedTags}
376
+ />
377
+ </div>
378
+ );
379
+ }
380
+ ```
381
+
382
+ ## 🤝 贡献
383
+
384
+ 我们欢迎贡献!请查看我们的[贡献指南](../../CONTRIBUTING.md)了解详情。
385
+
386
+ 1. Fork 此仓库
387
+ 2. 创建您的功能分支(`git checkout -b feature/amazing-feature`)
388
+ 3. 提交您的更改(`git commit -m 'Add some amazing feature'`)
389
+ 4. 推送到分支(`git push origin feature/amazing-feature`)
390
+ 5. 开启一个 Pull Request
391
+
392
+ ## 📄 许可证
393
+
394
+ 本项目采用 Apache License 2.0 许可证 - 查看 [LICENSE](../../LICENSE) 文件了解详情。
395
+
396
+ ## 🙏 致谢
397
+
398
+ - [Ant Design](https://ant.design/) - UI 组件库
399
+ - [Fetcher](https://github.com/Ahoo-Wang/fetcher) - HTTP 客户端生态系统
400
+ - [React](https://reactjs.org/) - UI 框架
401
+ - [TypeScript](https://www.typescriptlang.org/) - 类型安全
402
+
403
+ ## 📞 支持
404
+
405
+ - 📖 [文档](https://github.com/Ahoo-Wang/fetcher/tree/master/packages/fetcher-viewer)
406
+ - 🐛 [问题](https://github.com/Ahoo-Wang/fetcher/issues)
407
+ - 💬 [讨论](https://github.com/Ahoo-Wang/fetcher/discussions)
408
+
409
+ ---
410
+
411
+ 由 Fetcher 团队用 ❤️ 制作</content>
412
+ </xai:function_call">Now let me run the tests to make sure everything is working correctly.
@@ -0,0 +1,22 @@
1
+ import { SelectProps } from 'antd';
2
+ import { UseDebouncedCallbackOptions } from '@ahoo-wang/fetcher-react';
3
+ import { StyleCapable } from '../types';
4
+ export type DefaultRemoteSelectValueType = string | number;
5
+ export interface RemoteSelectOption<ValueType = DefaultRemoteSelectValueType> {
6
+ label: string;
7
+ value: ValueType;
8
+ disabled?: boolean;
9
+ }
10
+ export interface RemoteSelectProps<ValueType = DefaultRemoteSelectValueType> extends Omit<SelectProps<ValueType, RemoteSelectOption<ValueType>>, 'options' | 'loading' | 'onSearch'>, StyleCapable {
11
+ debounce?: UseDebouncedCallbackOptions;
12
+ search: (search: string) => Promise<RemoteSelectOption<ValueType>[]>;
13
+ }
14
+ /**
15
+ * A Select component that loads options from a remote API.
16
+ * Supports automatic fetching, loading states, and error handling.
17
+ */
18
+ export declare function RemoteSelect<ValueType = DefaultRemoteSelectValueType>(props: RemoteSelectProps<ValueType>): import("react/jsx-runtime").JSX.Element;
19
+ export declare namespace RemoteSelect {
20
+ var displayName: string;
21
+ }
22
+ //# sourceMappingURL=RemoteSelect.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"RemoteSelect.d.ts","sourceRoot":"","sources":["../../src/components/RemoteSelect.tsx"],"names":[],"mappings":"AAaA,OAAO,EAAU,WAAW,EAAE,MAAM,MAAM,CAAC;AAC3C,OAAO,EACL,2BAA2B,EAE5B,MAAM,0BAA0B,CAAC;AAClC,OAAO,EAAE,YAAY,EAAE,MAAM,UAAU,CAAC;AAExC,MAAM,MAAM,4BAA4B,GAAG,MAAM,GAAG,MAAM,CAAC;AAE3D,MAAM,WAAW,kBAAkB,CAAC,SAAS,GAAG,4BAA4B;IAC1E,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,SAAS,CAAC;IACjB,QAAQ,CAAC,EAAE,OAAO,CAAC;CACpB;AAED,MAAM,WAAW,iBAAiB,CAAC,SAAS,GAAG,4BAA4B,CACzE,SAAQ,IAAI,CAAC,WAAW,CAAC,SAAS,EAAE,kBAAkB,CAAC,SAAS,CAAC,CAAC,EAAE,SAAS,GAAG,SAAS,GAAG,UAAU,CAAC,EACrG,YAAY;IACd,QAAQ,CAAC,EAAE,2BAA2B,CAAC;IACvC,MAAM,EAAE,CAAC,MAAM,EAAE,MAAM,KAAK,OAAO,CAAC,kBAAkB,CAAC,SAAS,CAAC,EAAE,CAAC,CAAC;CACtE;AAQD;;;GAGG;AACH,wBAAgB,YAAY,CAAC,SAAS,GAAG,4BAA4B,EACnE,KAAK,EAAE,iBAAiB,CAAC,SAAS,CAAC,2CAqBpC;yBAtBe,YAAY"}
@@ -1,3 +1,4 @@
1
1
  export * from './TagInput';
2
2
  export * from './NumberRange';
3
+ export * from './RemoteSelect';
3
4
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/components/index.ts"],"names":[],"mappings":"AAaA,cAAc,YAAY,CAAC;AAC3B,cAAc,eAAe,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/components/index.ts"],"names":[],"mappings":"AAaA,cAAc,YAAY,CAAC;AAC3B,cAAc,eAAe,CAAC;AAC9B,cAAc,gBAAgB,CAAC"}