react-resource-ui 0.1.3 → 0.1.4
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/README.md +221 -20
- package/dist/index.cjs +9 -6
- package/dist/index.js +9 -6
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# React Resource UI
|
|
2
2
|
|
|
3
|
-
A data orchestration layer for React focused on pagination and
|
|
3
|
+
A high-level data orchestration layer for React focused on pagination, lists, and UI-driven data patterns.
|
|
4
4
|
|
|
5
5
|
npm: https://www.npmjs.com/package/react-resource-ui
|
|
6
6
|
|
|
@@ -10,27 +10,75 @@ npm: https://www.npmjs.com/package/react-resource-ui
|
|
|
10
10
|
|
|
11
11
|
React Resource UI provides a unified abstraction for handling data fetching, pagination, and virtualization in React applications.
|
|
12
12
|
|
|
13
|
-
|
|
13
|
+
Instead of manually implementing pagination logic and rewriting UI behavior for different use cases, you define your data once and control behavior through configuration.
|
|
14
|
+
|
|
15
|
+
---
|
|
16
|
+
|
|
17
|
+
## Why use this?
|
|
18
|
+
|
|
19
|
+
In real applications, data fetching alone is not enough. You also need to manage:
|
|
20
|
+
|
|
21
|
+
- pagination logic
|
|
22
|
+
- switching between page, load more, and infinite scroll
|
|
23
|
+
- UI state consistency
|
|
24
|
+
- preventing stale updates and race conditions
|
|
25
|
+
|
|
26
|
+
Most tools provide low-level primitives, but you still write this logic yourself.
|
|
27
|
+
|
|
28
|
+
React Resource UI focuses on these higher-level patterns and removes the need to rewrite logic when requirements change.
|
|
14
29
|
|
|
15
30
|
---
|
|
16
31
|
|
|
17
32
|
## Installation
|
|
18
33
|
|
|
34
|
+
```bash
|
|
19
35
|
npm install react-resource-ui
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
---
|
|
39
|
+
|
|
40
|
+
## Core Concepts
|
|
41
|
+
|
|
42
|
+
### 1. Source
|
|
43
|
+
|
|
44
|
+
Defines where your data comes from. It can be:
|
|
45
|
+
|
|
46
|
+
- an async function
|
|
47
|
+
- a URL string
|
|
48
|
+
- a static array
|
|
49
|
+
|
|
50
|
+
---
|
|
51
|
+
|
|
52
|
+
### 2. Pagination
|
|
53
|
+
|
|
54
|
+
Controls how data is fetched and appended:
|
|
55
|
+
|
|
56
|
+
- page → traditional pagination
|
|
57
|
+
- loadmore → append on button click
|
|
58
|
+
- infinite → auto-fetch on scroll
|
|
59
|
+
|
|
60
|
+
---
|
|
61
|
+
|
|
62
|
+
### 3. Virtualization
|
|
63
|
+
|
|
64
|
+
Optimizes rendering for large datasets by only rendering visible rows.
|
|
20
65
|
|
|
21
66
|
---
|
|
22
67
|
|
|
23
68
|
## Basic Usage
|
|
24
69
|
|
|
70
|
+
```tsx
|
|
25
71
|
import { useResource } from "react-resource-ui";
|
|
26
72
|
|
|
27
73
|
function App() {
|
|
28
74
|
const { data, loading, error, page, setPage } = useResource({
|
|
29
75
|
source: async ({ page = 1, pageSize = 10 }) => {
|
|
30
76
|
const skip = (page - 1) * pageSize;
|
|
77
|
+
|
|
31
78
|
const res = await fetch(
|
|
32
79
|
`https://dummyjson.com/todos?limit=${pageSize}&skip=${skip}`
|
|
33
80
|
);
|
|
81
|
+
|
|
34
82
|
const json = await res.json();
|
|
35
83
|
return json.todos;
|
|
36
84
|
},
|
|
@@ -39,39 +87,192 @@ function App() {
|
|
|
39
87
|
type: "page",
|
|
40
88
|
pageSize: 20,
|
|
41
89
|
},
|
|
90
|
+
});
|
|
91
|
+
|
|
92
|
+
return null;
|
|
93
|
+
}
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
---
|
|
97
|
+
|
|
98
|
+
## Using with Total Count (Recommended)
|
|
99
|
+
|
|
100
|
+
For accurate pagination behavior, return `{ data, total }`:
|
|
101
|
+
|
|
102
|
+
```tsx
|
|
103
|
+
const { data } = useResource({
|
|
104
|
+
source: async ({ page = 1, pageSize = 10 }) => {
|
|
105
|
+
const skip = (page - 1) * pageSize;
|
|
106
|
+
|
|
107
|
+
const res = await fetch(
|
|
108
|
+
`https://dummyjson.com/todos?limit=${pageSize}&skip=${skip}`
|
|
109
|
+
);
|
|
110
|
+
|
|
111
|
+
const json = await res.json();
|
|
112
|
+
|
|
113
|
+
return {
|
|
114
|
+
data: json.todos,
|
|
115
|
+
total: json.total,
|
|
116
|
+
};
|
|
117
|
+
},
|
|
118
|
+
|
|
119
|
+
pagination: {
|
|
120
|
+
type: "page",
|
|
121
|
+
pageSize: 20,
|
|
122
|
+
},
|
|
123
|
+
});
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
---
|
|
127
|
+
|
|
128
|
+
## Pagination Modes
|
|
129
|
+
|
|
130
|
+
### Page-based
|
|
131
|
+
|
|
132
|
+
```ts
|
|
133
|
+
pagination: {
|
|
134
|
+
type: "page",
|
|
135
|
+
pageSize: 20,
|
|
136
|
+
}
|
|
137
|
+
```
|
|
138
|
+
|
|
139
|
+
You control navigation using `page` and `setPage`.
|
|
140
|
+
|
|
141
|
+
---
|
|
142
|
+
|
|
143
|
+
### Load More
|
|
144
|
+
|
|
145
|
+
```ts
|
|
146
|
+
pagination: {
|
|
147
|
+
type: "loadmore",
|
|
148
|
+
pageSize: 20,
|
|
149
|
+
}
|
|
150
|
+
```
|
|
151
|
+
|
|
152
|
+
Append data on button click.
|
|
153
|
+
|
|
154
|
+
---
|
|
155
|
+
|
|
156
|
+
### Infinite Scroll
|
|
157
|
+
|
|
158
|
+
```ts
|
|
159
|
+
pagination: {
|
|
160
|
+
type: "infinite",
|
|
161
|
+
pageSize: 20,
|
|
162
|
+
}
|
|
163
|
+
```
|
|
164
|
+
|
|
165
|
+
Automatically fetch next page when scrolling near the bottom.
|
|
166
|
+
|
|
167
|
+
---
|
|
168
|
+
|
|
169
|
+
## Using with DataTable
|
|
170
|
+
|
|
171
|
+
```tsx
|
|
172
|
+
import { useResource, DataTable } from "react-resource-ui";
|
|
173
|
+
|
|
174
|
+
function App() {
|
|
175
|
+
const {
|
|
176
|
+
data,
|
|
177
|
+
loading,
|
|
178
|
+
error,
|
|
179
|
+
page,
|
|
180
|
+
setPage,
|
|
181
|
+
setScrollTop,
|
|
182
|
+
offsetY,
|
|
183
|
+
totalHeight,
|
|
184
|
+
totalItems,
|
|
185
|
+
scrollRef,
|
|
186
|
+
hasNext,
|
|
187
|
+
} = useResource({
|
|
188
|
+
source: async ({ page = 1, pageSize = 20 }) => {
|
|
189
|
+
const skip = (page - 1) * pageSize;
|
|
190
|
+
|
|
191
|
+
const res = await fetch(
|
|
192
|
+
`https://dummyjson.com/todos?limit=${pageSize}&skip=${skip}`
|
|
193
|
+
);
|
|
194
|
+
|
|
195
|
+
const json = await res.json();
|
|
196
|
+
|
|
197
|
+
return {
|
|
198
|
+
data: json.todos,
|
|
199
|
+
total: json.total,
|
|
200
|
+
};
|
|
201
|
+
},
|
|
202
|
+
|
|
203
|
+
pagination: {
|
|
204
|
+
type: "page",
|
|
205
|
+
pageSize: 20,
|
|
206
|
+
},
|
|
42
207
|
|
|
43
208
|
virtualization: {
|
|
44
209
|
enabled: true,
|
|
45
|
-
itemHeight:
|
|
46
|
-
containerHeight:
|
|
210
|
+
itemHeight: 50,
|
|
211
|
+
containerHeight: 300,
|
|
47
212
|
},
|
|
48
213
|
});
|
|
49
214
|
|
|
50
|
-
return
|
|
215
|
+
return (
|
|
216
|
+
<DataTable
|
|
217
|
+
data={data}
|
|
218
|
+
loading={loading}
|
|
219
|
+
error={error}
|
|
220
|
+
page={page}
|
|
221
|
+
setPage={setPage}
|
|
222
|
+
setScrollTop={setScrollTop}
|
|
223
|
+
type="page"
|
|
224
|
+
virtualization={true}
|
|
225
|
+
offsetY={offsetY}
|
|
226
|
+
totalHeight={totalHeight}
|
|
227
|
+
totalItems={totalItems}
|
|
228
|
+
scrollRef={scrollRef}
|
|
229
|
+
hasNext={hasNext}
|
|
230
|
+
/>
|
|
231
|
+
);
|
|
51
232
|
}
|
|
233
|
+
```
|
|
52
234
|
|
|
53
235
|
---
|
|
54
236
|
|
|
55
|
-
##
|
|
237
|
+
## Virtualization
|
|
238
|
+
|
|
239
|
+
```ts
|
|
240
|
+
virtualization: {
|
|
241
|
+
enabled: true,
|
|
242
|
+
itemHeight: 50,
|
|
243
|
+
containerHeight: 300,
|
|
244
|
+
}
|
|
245
|
+
```
|
|
56
246
|
|
|
57
|
-
|
|
247
|
+
- `itemHeight` → height of each row (must match UI)
|
|
248
|
+
- `containerHeight` → visible scroll container height
|
|
249
|
+
|
|
250
|
+
---
|
|
58
251
|
|
|
59
|
-
|
|
60
|
-
pagination: { type: "page" }
|
|
252
|
+
## Returned Values
|
|
61
253
|
|
|
62
|
-
|
|
63
|
-
pagination: { type: "loadmore" }
|
|
254
|
+
`useResource` returns:
|
|
64
255
|
|
|
65
|
-
|
|
66
|
-
|
|
256
|
+
- `data` → current visible data
|
|
257
|
+
- `loading` → request state
|
|
258
|
+
- `error` → error state
|
|
259
|
+
- `page` → current page
|
|
260
|
+
- `setPage` → update page
|
|
261
|
+
- `setScrollTop` → update scroll position
|
|
262
|
+
- `offsetY` → virtualization offset
|
|
263
|
+
- `totalHeight` → total virtual height
|
|
264
|
+
- `totalItems` → total items loaded
|
|
265
|
+
- `scrollRef` → scroll container ref
|
|
266
|
+
- `hasNext` → whether next page exists
|
|
67
267
|
|
|
68
268
|
---
|
|
69
269
|
|
|
70
270
|
## Features
|
|
71
271
|
|
|
72
|
-
- Unified API for
|
|
73
|
-
-
|
|
74
|
-
-
|
|
272
|
+
- Unified API for pagination strategies
|
|
273
|
+
- Page-based, load more, and infinite scroll support
|
|
274
|
+
- Built-in virtualization
|
|
275
|
+
- Works with async functions, URLs, or static data
|
|
75
276
|
- Request deduplication using request tracking
|
|
76
277
|
- Lightweight page-based caching
|
|
77
278
|
- Designed for table and list UIs
|
|
@@ -82,22 +283,22 @@ pagination: { type: "infinite" }
|
|
|
82
283
|
|
|
83
284
|
- Reduce UI-specific data handling complexity
|
|
84
285
|
- Avoid rewriting logic when changing pagination behavior
|
|
85
|
-
- Provide a higher-level abstraction over common
|
|
286
|
+
- Provide a higher-level abstraction over common frontend patterns
|
|
86
287
|
- Keep configuration simple and predictable
|
|
87
288
|
|
|
88
289
|
---
|
|
89
290
|
|
|
90
291
|
## Status
|
|
91
292
|
|
|
92
|
-
Version: 0.1.
|
|
293
|
+
Version: 0.1.x
|
|
93
294
|
|
|
94
|
-
This is an early release focused on core functionality.
|
|
95
|
-
Additional testing and edge case handling are in progress.
|
|
295
|
+
This is an early release focused on core pagination and virtualization functionality. Improvements and edge case handling are in progress.
|
|
96
296
|
|
|
97
297
|
---
|
|
98
298
|
|
|
99
299
|
## Roadmap
|
|
100
300
|
|
|
301
|
+
- Grid-based virtualized rendering
|
|
101
302
|
- Sorting and filtering support
|
|
102
303
|
- Form integration
|
|
103
304
|
- Improved caching strategies
|
package/dist/index.cjs
CHANGED
|
@@ -212,16 +212,16 @@ function DataTable(props) {
|
|
|
212
212
|
const offsetY = props.offsetY ?? 0;
|
|
213
213
|
const totalHeight = props.totalHeight ?? 0;
|
|
214
214
|
const itemHeight = 50;
|
|
215
|
-
const
|
|
215
|
+
const columns = data[0] ? Object.keys(data[0]) : [];
|
|
216
216
|
if (loading) return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { children: "Loading..." });
|
|
217
217
|
if (error) return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { children: error.message });
|
|
218
218
|
if ((props.totalItems ?? data.length) === 0)
|
|
219
219
|
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { children: "No Data to create Table" });
|
|
220
220
|
const content = /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("table", { children: [
|
|
221
|
-
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("thead", { children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("tr", { children:
|
|
221
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("thead", { children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("tr", { children: columns.map((val) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)("th", { children: val }, val)) }) }),
|
|
222
222
|
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("tbody", { children: [
|
|
223
|
-
virtualization && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("tr", { style: { height: offsetY }, children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("td", {
|
|
224
|
-
data.map((val, index) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)("tr", { children:
|
|
223
|
+
virtualization && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("tr", { style: { height: offsetY }, children: columns.map((key) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)("td", {}, key)) }),
|
|
224
|
+
data.map((val, index) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)("tr", { children: columns.map((key) => {
|
|
225
225
|
const value = val[key];
|
|
226
226
|
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("td", { children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "cell", children: value != null ? value.toString() : "-" }) }, key);
|
|
227
227
|
}) }, index)),
|
|
@@ -229,9 +229,12 @@ function DataTable(props) {
|
|
|
229
229
|
"tr",
|
|
230
230
|
{
|
|
231
231
|
style: {
|
|
232
|
-
height:
|
|
232
|
+
height: Math.max(
|
|
233
|
+
0,
|
|
234
|
+
totalHeight - offsetY - data.length * itemHeight
|
|
235
|
+
)
|
|
233
236
|
},
|
|
234
|
-
children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("td", {
|
|
237
|
+
children: columns.map((key) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)("td", {}, key))
|
|
235
238
|
}
|
|
236
239
|
)
|
|
237
240
|
] })
|
package/dist/index.js
CHANGED
|
@@ -185,16 +185,16 @@ function DataTable(props) {
|
|
|
185
185
|
const offsetY = props.offsetY ?? 0;
|
|
186
186
|
const totalHeight = props.totalHeight ?? 0;
|
|
187
187
|
const itemHeight = 50;
|
|
188
|
-
const
|
|
188
|
+
const columns = data[0] ? Object.keys(data[0]) : [];
|
|
189
189
|
if (loading) return /* @__PURE__ */ jsx("div", { children: "Loading..." });
|
|
190
190
|
if (error) return /* @__PURE__ */ jsx("div", { children: error.message });
|
|
191
191
|
if ((props.totalItems ?? data.length) === 0)
|
|
192
192
|
return /* @__PURE__ */ jsx("p", { children: "No Data to create Table" });
|
|
193
193
|
const content = /* @__PURE__ */ jsxs("table", { children: [
|
|
194
|
-
/* @__PURE__ */ jsx("thead", { children: /* @__PURE__ */ jsx("tr", { children:
|
|
194
|
+
/* @__PURE__ */ jsx("thead", { children: /* @__PURE__ */ jsx("tr", { children: columns.map((val) => /* @__PURE__ */ jsx("th", { children: val }, val)) }) }),
|
|
195
195
|
/* @__PURE__ */ jsxs("tbody", { children: [
|
|
196
|
-
virtualization && /* @__PURE__ */ jsx("tr", { style: { height: offsetY }, children: /* @__PURE__ */ jsx("td", {
|
|
197
|
-
data.map((val, index) => /* @__PURE__ */ jsx("tr", { children:
|
|
196
|
+
virtualization && /* @__PURE__ */ jsx("tr", { style: { height: offsetY }, children: columns.map((key) => /* @__PURE__ */ jsx("td", {}, key)) }),
|
|
197
|
+
data.map((val, index) => /* @__PURE__ */ jsx("tr", { children: columns.map((key) => {
|
|
198
198
|
const value = val[key];
|
|
199
199
|
return /* @__PURE__ */ jsx("td", { children: /* @__PURE__ */ jsx("div", { className: "cell", children: value != null ? value.toString() : "-" }) }, key);
|
|
200
200
|
}) }, index)),
|
|
@@ -202,9 +202,12 @@ function DataTable(props) {
|
|
|
202
202
|
"tr",
|
|
203
203
|
{
|
|
204
204
|
style: {
|
|
205
|
-
height:
|
|
205
|
+
height: Math.max(
|
|
206
|
+
0,
|
|
207
|
+
totalHeight - offsetY - data.length * itemHeight
|
|
208
|
+
)
|
|
206
209
|
},
|
|
207
|
-
children: /* @__PURE__ */ jsx("td", {
|
|
210
|
+
children: columns.map((key) => /* @__PURE__ */ jsx("td", {}, key))
|
|
208
211
|
}
|
|
209
212
|
)
|
|
210
213
|
] })
|