@blocklet/list 0.8.34 → 0.8.35

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/lib/base.js CHANGED
@@ -40,7 +40,7 @@ function ListBase() {
40
40
 
41
41
  const {
42
42
  handleDeveloper,
43
- blockletList,
43
+ finalBlockletList,
44
44
  filters,
45
45
  developerName,
46
46
  handleSort,
@@ -111,7 +111,7 @@ function ListBase() {
111
111
  }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactErrorBoundary.ErrorBoundary, {
112
112
  FallbackComponent: _ErrorBoundary.ErrorFallback,
113
113
  children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_list.default, {
114
- blocklets: blockletList
114
+ blocklets: finalBlockletList
115
115
  })
116
116
  })]
117
117
  })]
@@ -5,6 +5,8 @@ Object.defineProperty(exports, "__esModule", {
5
5
  });
6
6
  exports.default = BlockletList;
7
7
 
8
+ var _react = require("react");
9
+
8
10
  var _propTypes = _interopRequireDefault(require("prop-types"));
9
11
 
10
12
  var _styledComponents = _interopRequireDefault(require("styled-components"));
@@ -17,6 +19,8 @@ var _Grid = _interopRequireDefault(require("@mui/material/Grid"));
17
19
 
18
20
  var _CircularProgress = _interopRequireDefault(require("@mui/material/CircularProgress"));
19
21
 
22
+ var _reactInfiniteScrollHook = _interopRequireDefault(require("react-infinite-scroll-hook"));
23
+
20
24
  var _ErrorBoundary = require("@arcblock/ux/lib/ErrorBoundary");
21
25
 
22
26
  var _empty = require("./empty");
@@ -50,13 +54,21 @@ function BlockletList(_ref) {
50
54
  errors,
51
55
  loadings,
52
56
  selectedCategory,
53
- blockletList,
57
+ finalBlockletList,
54
58
  getCategoryLocale,
55
59
  filters,
56
60
  t,
61
+ hasNextPage,
62
+ loadMore,
57
63
  endpoint
58
64
  } = (0, _filter.useFilterContext)();
59
65
  const showFilterTip = !!selectedCategory || !!filters.price;
66
+ const [sentryRef] = (0, _reactInfiniteScrollHook.default)({
67
+ loading: loadings.fetchBlockletsLoading,
68
+ hasNextPage,
69
+ onLoadMore: loadMore,
70
+ rootMargin: '0px 0px 400px 0px'
71
+ });
60
72
 
61
73
  if (errors.fetchBlockletsError) {
62
74
  return /*#__PURE__*/(0, _jsxRuntime.jsx)(_ErrorBoundary.ErrorFallback, {
@@ -73,7 +85,7 @@ function BlockletList(_ref) {
73
85
  });
74
86
  }
75
87
 
76
- if (filters.keyword && showFilterTip && blockletList.length === 0) {
88
+ if (filters.keyword && showFilterTip && finalBlockletList.length === 0) {
77
89
  return /*#__PURE__*/(0, _jsxRuntime.jsxs)(CustomEmpty, {
78
90
  children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_empty.EmptyTitle, {
79
91
  primaryStart: t('blocklet.noBlockletPart1'),
@@ -86,7 +98,7 @@ function BlockletList(_ref) {
86
98
  });
87
99
  }
88
100
 
89
- if (filters.keyword && blockletList.length === 0) {
101
+ if (filters.keyword && finalBlockletList.length === 0) {
90
102
  return /*#__PURE__*/(0, _jsxRuntime.jsxs)(CustomEmpty, {
91
103
  children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_empty.EmptyTitle, {
92
104
  primaryStart: t('blocklet.noBlockletPart1'),
@@ -98,7 +110,7 @@ function BlockletList(_ref) {
98
110
  });
99
111
  }
100
112
 
101
- if (showFilterTip && blockletList.length === 0) {
113
+ if (showFilterTip && finalBlockletList.length === 0) {
102
114
  const categoryLocale = getCategoryLocale(selectedCategory);
103
115
  return /*#__PURE__*/(0, _jsxRuntime.jsxs)(CustomEmpty, {
104
116
  children: [categoryLocale ? /*#__PURE__*/(0, _jsxRuntime.jsx)(_empty.EmptyTitle, {
@@ -111,16 +123,16 @@ function BlockletList(_ref) {
111
123
  });
112
124
  }
113
125
 
114
- if (blockletList.length === 0) {
126
+ if (finalBlockletList.length === 0) {
115
127
  return /*#__PURE__*/(0, _jsxRuntime.jsx)(CustomEmpty, {
116
128
  children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_empty.NoResults, {})
117
129
  });
118
130
  }
119
131
 
120
- return /*#__PURE__*/(0, _jsxRuntime.jsx)(StyledGrid, _objectSpread(_objectSpread({
132
+ return /*#__PURE__*/(0, _jsxRuntime.jsxs)(StyledGrid, _objectSpread(_objectSpread({
121
133
  container: true
122
134
  }, rest), {}, {
123
- children: blocklets.map(blocklet => /*#__PURE__*/(0, _jsxRuntime.jsx)(StyledGridItem, {
135
+ children: [blocklets.map(blocklet => /*#__PURE__*/(0, _jsxRuntime.jsx)(StyledGridItem, {
124
136
  item: true,
125
137
  lg: 4,
126
138
  md: 6,
@@ -129,9 +141,20 @@ function BlockletList(_ref) {
129
141
  "data-blocklet-did": blocklet.did,
130
142
  children: blockletRender({
131
143
  blocklet,
132
- blocklets: blockletList
144
+ blocklets: finalBlockletList
145
+ })
146
+ }, blocklet.did)), hasNextPage && /*#__PURE__*/(0, _jsxRuntime.jsx)(StyledGridItem, {
147
+ item: true,
148
+ md: 12,
149
+ sm: 12,
150
+ xs: 12,
151
+ ref: sentryRef,
152
+ children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_Box.default, {
153
+ display: "flex",
154
+ justifyContent: "center",
155
+ children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_CircularProgress.default, {})
133
156
  })
134
- }, blocklet.did))
157
+ })]
135
158
  }));
136
159
  }
137
160
 
@@ -143,10 +166,10 @@ const StyledGrid = (0, _styledComponents.default)(_Grid.default).withConfig({
143
166
  displayName: "list__StyledGrid",
144
167
  componentId: "sc-1guvpon-0"
145
168
  })(["&.MuiGrid-root{width:auto;margin:0 -16px;}"]);
146
- const StyledGridItem = (0, _styledComponents.default)(_Grid.default).withConfig({
169
+ const StyledGridItem = /*#__PURE__*/(0, _react.memo)((0, _styledComponents.default)(_Grid.default).withConfig({
147
170
  displayName: "list__StyledGridItem",
148
171
  componentId: "sc-1guvpon-1"
149
- })(["@media (max-width:", "px){&.MuiGrid-item{padding-bottom:0px;}}@media (min-width:", "px){&.MuiGrid-item{margin-bottom:", ";}}"], props => props.theme.breakpoints.values.sm, props => props.theme.breakpoints.values.sm, props => props.theme.spacing(2));
172
+ })(["@media (max-width:", "px){&.MuiGrid-item{padding-bottom:0px;}}@media (min-width:", "px){&.MuiGrid-item{margin-bottom:", ";}}"], props => props.theme.breakpoints.values.sm, props => props.theme.breakpoints.values.sm, props => props.theme.spacing(2)));
150
173
  const CustomEmpty = (0, _styledComponents.default)(_Empty.default).withConfig({
151
174
  displayName: "list__CustomEmpty",
152
175
  componentId: "sc-1guvpon-2"
@@ -95,6 +95,11 @@ function FilterProvider(_ref) {
95
95
  initialData: [],
96
96
  manual: true
97
97
  });
98
+ const paginateState = (0, _ahooks.useReactive)({
99
+ currentPage: 1,
100
+ pageSize: (0, _utils.isMobileScreen)() ? 10 : 18,
101
+ defaultCurrentPage: 1
102
+ });
98
103
 
99
104
  const finalFilters = _objectSpread({
100
105
  sortBy: 'popularity',
@@ -153,6 +158,10 @@ function FilterProvider(_ref) {
153
158
 
154
159
  return (0, _orderBy.default)(blocklets, [sortMap[finalFilters.sortBy]], [finalFilters.sortDirection]);
155
160
  }, [allBlocklets, finalFilters]);
161
+ const finalBlockletList = (0, _react.useMemo)(() => {
162
+ // 前端分页 currentPage 当前页数 pageSize 每页条数
163
+ return blockletList.slice((paginateState.defaultCurrentPage - 1) * paginateState.pageSize, paginateState.currentPage * paginateState.pageSize);
164
+ }, [blockletList, paginateState]);
156
165
  const categoryList = (0, _react.useMemo)(() => {
157
166
  const list = categoryState.data || []; // 分类按照名称排序
158
167
 
@@ -180,7 +189,7 @@ function FilterProvider(_ref) {
180
189
  fetchCategoriesLoading
181
190
  },
182
191
  endpoint,
183
- blockletList,
192
+ finalBlockletList,
184
193
  t: translate,
185
194
  filters: finalFilters,
186
195
  selectedCategory,
@@ -189,6 +198,7 @@ function FilterProvider(_ref) {
189
198
  locale,
190
199
  categoryOptions,
191
200
  priceOptions,
201
+ hasNextPage: blockletList.length >= paginateState.pageSize * paginateState.currentPage,
192
202
  handleSort: sort => {
193
203
  const changeData = _objectSpread(_objectSpread({}, finalFilters), {}, {
194
204
  sortBy: sort,
@@ -231,6 +241,18 @@ function FilterProvider(_ref) {
231
241
 
232
242
  onFilterChange(changeData);
233
243
  },
244
+ handlePage: page => {
245
+ const changeData = _objectSpread(_objectSpread({}, finalFilters), {}, {
246
+ currentPage: page
247
+ });
248
+
249
+ onFilterChange(changeData);
250
+ },
251
+ loadMore: () => {
252
+ setTimeout(() => {
253
+ paginateState.currentPage += 1;
254
+ }, 1000);
255
+ },
234
256
  getCategoryLocale: category => {
235
257
  if (!category) return null;
236
258
  let result = null;
@@ -243,6 +265,10 @@ function FilterProvider(_ref) {
243
265
  return result;
244
266
  },
245
267
 
268
+ get allBlocklets() {
269
+ return allBlocklets || [];
270
+ },
271
+
246
272
  get developerName() {
247
273
  var _blocklets$find, _blocklets$find$owner;
248
274
 
package/lib/libs/utils.js CHANGED
@@ -3,7 +3,7 @@
3
3
  Object.defineProperty(exports, "__esModule", {
4
4
  value: true
5
5
  });
6
- exports.urlStringify = exports.replaceTranslate = exports.removeUndefined = exports.getStoreDetail = exports.getSortOptions = exports.getPrices = exports.getCategoryOptions = exports.getCategories = exports.formatLogoPath = exports.formatError = exports.filterBlockletByPrice = void 0;
6
+ exports.urlStringify = exports.replaceTranslate = exports.removeUndefined = exports.isMobileScreen = exports.getStoreDetail = exports.getSortOptions = exports.getPrices = exports.getCategoryOptions = exports.getCategories = exports.formatLogoPath = exports.formatError = exports.filterBlockletByPrice = void 0;
7
7
 
8
8
  var _urlJoin = _interopRequireDefault(require("url-join"));
9
9
 
@@ -159,4 +159,10 @@ const urlStringify = obj => {
159
159
  return new URLSearchParams(removeUndefined(obj)).toString();
160
160
  };
161
161
 
162
- exports.urlStringify = urlStringify;
162
+ exports.urlStringify = urlStringify;
163
+
164
+ const isMobileScreen = () => {
165
+ return window.innerWidth <= 600;
166
+ };
167
+
168
+ exports.isMobileScreen = isMobileScreen;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@blocklet/list",
3
- "version": "0.8.34",
3
+ "version": "0.8.35",
4
4
  "description": "Common ux components of blocklet",
5
5
  "publishConfig": {
6
6
  "access": "public"
@@ -38,7 +38,7 @@
38
38
  "react": ">=18.1.0"
39
39
  },
40
40
  "dependencies": {
41
- "@arcblock/ux": "^2.1.19",
41
+ "@arcblock/ux": "^2.1.21",
42
42
  "@emotion/react": "^11.9.0",
43
43
  "@emotion/styled": "^11.8.1",
44
44
  "@mui/icons-material": "^5.6.2",
@@ -48,6 +48,7 @@
48
48
  "lodash": "^4.17.21",
49
49
  "prop-types": "^15.7.2",
50
50
  "react-error-boundary": "^3.1.4",
51
+ "react-infinite-scroll-hook": "^4.0.3",
51
52
  "styled-components": "5.3.5",
52
53
  "url-join": "^4.0.1"
53
54
  },
@@ -63,5 +64,5 @@
63
64
  "eslint": "^8.16.0",
64
65
  "prettier": "^2.6.2"
65
66
  },
66
- "gitHead": "fed8918e15b5bd8cb3f4bac84b32434b58dda21b"
67
+ "gitHead": "f6a52e3c84e6eb4cd5625f688a55d7da1b477740"
67
68
  }
package/src/base.js CHANGED
@@ -16,7 +16,7 @@ import Search from './components/search';
16
16
  function ListBase() {
17
17
  const {
18
18
  handleDeveloper,
19
- blockletList,
19
+ finalBlockletList,
20
20
  filters,
21
21
  developerName,
22
22
  handleSort,
@@ -79,7 +79,7 @@ function ListBase() {
79
79
  />
80
80
  </Box>
81
81
  <ErrorBoundary FallbackComponent={ErrorFallback}>
82
- <BlockletList blocklets={blockletList} />
82
+ <BlockletList blocklets={finalBlockletList} />
83
83
  </ErrorBoundary>
84
84
  </StyledMin>
85
85
  </Box>
@@ -1,20 +1,40 @@
1
+ import { memo } from 'react';
1
2
  import PropTypes from 'prop-types';
2
3
  import styled from 'styled-components';
3
4
  import Empty from '@arcblock/ux/lib/Empty';
4
5
  import Box from '@mui/material/Box';
5
6
  import Grid from '@mui/material/Grid';
6
7
  import CircularProgress from '@mui/material/CircularProgress';
8
+ import useInfiniteScroll from 'react-infinite-scroll-hook';
7
9
  import { ErrorFallback } from '@arcblock/ux/lib/ErrorBoundary';
8
10
 
9
11
  import { NoResults, EmptyTitle, NoResultsTips } from './empty';
10
12
  import { useFilterContext } from '../../contexts/filter';
11
13
 
12
14
  export default function BlockletList({ blocklets, ...rest }) {
13
- const { blockletRender, errors, loadings, selectedCategory, blockletList, getCategoryLocale, filters, t, endpoint } =
14
- useFilterContext();
15
+ const {
16
+ blockletRender,
17
+ errors,
18
+ loadings,
19
+ selectedCategory,
20
+ finalBlockletList,
21
+ getCategoryLocale,
22
+ filters,
23
+ t,
24
+ hasNextPage,
25
+ loadMore,
26
+ endpoint,
27
+ } = useFilterContext();
15
28
 
16
29
  const showFilterTip = !!selectedCategory || !!filters.price;
17
30
 
31
+ const [sentryRef] = useInfiniteScroll({
32
+ loading: loadings.fetchBlockletsLoading,
33
+ hasNextPage,
34
+ onLoadMore: loadMore,
35
+ rootMargin: '0px 0px 400px 0px',
36
+ });
37
+
18
38
  if (errors.fetchBlockletsError) {
19
39
  return (
20
40
  <ErrorFallback
@@ -29,7 +49,7 @@ export default function BlockletList({ blocklets, ...rest }) {
29
49
  </Box>
30
50
  );
31
51
  }
32
- if (filters.keyword && showFilterTip && blockletList.length === 0) {
52
+ if (filters.keyword && showFilterTip && finalBlockletList.length === 0) {
33
53
  return (
34
54
  <CustomEmpty>
35
55
  <EmptyTitle
@@ -41,7 +61,7 @@ export default function BlockletList({ blocklets, ...rest }) {
41
61
  </CustomEmpty>
42
62
  );
43
63
  }
44
- if (filters.keyword && blockletList.length === 0) {
64
+ if (filters.keyword && finalBlockletList.length === 0) {
45
65
  return (
46
66
  <CustomEmpty>
47
67
  <EmptyTitle
@@ -53,7 +73,7 @@ export default function BlockletList({ blocklets, ...rest }) {
53
73
  </CustomEmpty>
54
74
  );
55
75
  }
56
- if (showFilterTip && blockletList.length === 0) {
76
+ if (showFilterTip && finalBlockletList.length === 0) {
57
77
  const categoryLocale = getCategoryLocale(selectedCategory);
58
78
  return (
59
79
  <CustomEmpty>
@@ -70,7 +90,7 @@ export default function BlockletList({ blocklets, ...rest }) {
70
90
  </CustomEmpty>
71
91
  );
72
92
  }
73
- if (blockletList.length === 0) {
93
+ if (finalBlockletList.length === 0) {
74
94
  return (
75
95
  <CustomEmpty>
76
96
  <NoResults />
@@ -82,9 +102,16 @@ export default function BlockletList({ blocklets, ...rest }) {
82
102
  <StyledGrid container {...rest}>
83
103
  {blocklets.map((blocklet) => (
84
104
  <StyledGridItem item lg={4} md={6} sm={6} xs={12} key={blocklet.did} data-blocklet-did={blocklet.did}>
85
- {blockletRender({ blocklet, blocklets: blockletList })}
105
+ {blockletRender({ blocklet, blocklets: finalBlockletList })}
86
106
  </StyledGridItem>
87
107
  ))}
108
+ {hasNextPage && (
109
+ <StyledGridItem item md={12} sm={12} xs={12} ref={sentryRef}>
110
+ <Box display="flex" justifyContent="center">
111
+ <CircularProgress />
112
+ </Box>
113
+ </StyledGridItem>
114
+ )}
88
115
  </StyledGrid>
89
116
  );
90
117
  }
@@ -102,7 +129,7 @@ const StyledGrid = styled(Grid)`
102
129
  }
103
130
  `;
104
131
 
105
- const StyledGridItem = styled(Grid)`
132
+ const StyledGridItem = memo(styled(Grid)`
106
133
  @media (max-width: ${(props) => props.theme.breakpoints.values.sm}px) {
107
134
  &.MuiGrid-item {
108
135
  padding-bottom: 0px;
@@ -113,7 +140,7 @@ const StyledGridItem = styled(Grid)`
113
140
  margin-bottom: ${(props) => props.theme.spacing(2)};
114
141
  }
115
142
  }
116
- `;
143
+ `);
117
144
  const CustomEmpty = styled(Empty)`
118
145
  text-align: center;
119
146
  .primary {
@@ -1,11 +1,17 @@
1
1
  import { useContext, createContext, useMemo, useEffect } from 'react';
2
2
  import PropTypes from 'prop-types';
3
- import { useRequest } from 'ahooks';
3
+ import { useRequest, useReactive } from 'ahooks';
4
4
  import orderBy from 'lodash/orderBy';
5
5
  import axios from 'axios';
6
6
  import isArray from 'lodash/isArray';
7
-
8
- import { getCategories, filterBlockletByPrice, replaceTranslate, getPrices, getCategoryOptions } from '../libs/utils';
7
+ import {
8
+ getCategories,
9
+ filterBlockletByPrice,
10
+ replaceTranslate,
11
+ getPrices,
12
+ getCategoryOptions,
13
+ isMobileScreen,
14
+ } from '../libs/utils';
9
15
  import translations from '../assets/locale';
10
16
  import { propTypes, defaultProps } from '../libs/prop-types';
11
17
 
@@ -48,6 +54,7 @@ function FilterProvider({ filters, children, endpoint, locale, blockletRender, o
48
54
  { initialData: [], manual: true }
49
55
  );
50
56
 
57
+ const paginateState = useReactive({ currentPage: 1, pageSize: isMobileScreen() ? 10 : 18, defaultCurrentPage: 1 });
51
58
  const finalFilters = { sortBy: 'popularity', sortDirection: 'desc', ...filters };
52
59
  const selectedCategory = finalFilters.category;
53
60
  const hasDeveloperFilter = !!finalFilters.developer;
@@ -89,6 +96,14 @@ function FilterProvider({ filters, children, endpoint, locale, blockletRender, o
89
96
  return orderBy(blocklets, [sortMap[finalFilters.sortBy]], [finalFilters.sortDirection]);
90
97
  }, [allBlocklets, finalFilters]);
91
98
 
99
+ const finalBlockletList = useMemo(() => {
100
+ // 前端分页 currentPage 当前页数 pageSize 每页条数
101
+ return blockletList.slice(
102
+ (paginateState.defaultCurrentPage - 1) * paginateState.pageSize,
103
+ paginateState.currentPage * paginateState.pageSize
104
+ );
105
+ }, [blockletList, paginateState]);
106
+
92
107
  const categoryList = useMemo(() => {
93
108
  const list = categoryState.data || [];
94
109
  // 分类按照名称排序
@@ -109,7 +124,7 @@ function FilterProvider({ filters, children, endpoint, locale, blockletRender, o
109
124
  errors: { fetchBlockletsError, fetchCategoriesError },
110
125
  loadings: { fetchBlockletsLoading, fetchCategoriesLoading },
111
126
  endpoint,
112
- blockletList,
127
+ finalBlockletList,
113
128
  t: translate,
114
129
  filters: finalFilters,
115
130
  selectedCategory,
@@ -118,6 +133,7 @@ function FilterProvider({ filters, children, endpoint, locale, blockletRender, o
118
133
  locale,
119
134
  categoryOptions,
120
135
  priceOptions,
136
+ hasNextPage: blockletList.length >= paginateState.pageSize * paginateState.currentPage,
121
137
  handleSort: (sort) => {
122
138
  const changeData = {
123
139
  ...finalFilters,
@@ -149,6 +165,15 @@ function FilterProvider({ filters, children, endpoint, locale, blockletRender, o
149
165
  const changeData = { ...finalFilters, developer: developer || undefined };
150
166
  onFilterChange(changeData);
151
167
  },
168
+ handlePage: (page) => {
169
+ const changeData = { ...finalFilters, currentPage: page };
170
+ onFilterChange(changeData);
171
+ },
172
+ loadMore: () => {
173
+ setTimeout(() => {
174
+ paginateState.currentPage += 1;
175
+ }, 1000);
176
+ },
152
177
  getCategoryLocale: (category) => {
153
178
  if (!category) return null;
154
179
  let result = null;
@@ -158,6 +183,9 @@ function FilterProvider({ filters, children, endpoint, locale, blockletRender, o
158
183
  }
159
184
  return result;
160
185
  },
186
+ get allBlocklets() {
187
+ return allBlocklets || [];
188
+ },
161
189
  get developerName() {
162
190
  const blocklets = allBlocklets || [];
163
191
  return blocklets.find((blocklet) => blocklet.owner.did === finalFilters.developer)?.owner?.name || '';
package/src/libs/utils.js CHANGED
@@ -5,10 +5,10 @@ const isFreeBlocklet = (meta) => {
5
5
  if (!meta.payment) {
6
6
  return true;
7
7
  }
8
-
9
8
  const priceList = (meta.payment.price || []).map((x) => x.value || 0);
10
9
  return priceList.every((x) => x === 0);
11
10
  };
11
+
12
12
  const getSortOptions = (t) => {
13
13
  return [
14
14
  {
@@ -106,6 +106,10 @@ const urlStringify = (obj) => {
106
106
  return new URLSearchParams(removeUndefined(obj)).toString();
107
107
  };
108
108
 
109
+ const isMobileScreen = () => {
110
+ return window.innerWidth <= 600;
111
+ };
112
+
109
113
  export {
110
114
  getSortOptions,
111
115
  getPrices,
@@ -118,4 +122,5 @@ export {
118
122
  removeUndefined,
119
123
  urlStringify,
120
124
  getCategoryOptions,
125
+ isMobileScreen,
121
126
  };