@ahoo-wang/fetcher-viewer 2.15.1 → 2.15.3

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