@blocklet/list 0.8.21 → 0.8.24
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 +40 -37
- package/lib/components/aside.js +53 -0
- package/lib/components/{filter-author.js → filter/custom-chip.js} +21 -28
- package/lib/components/{filter-mobile/content.js → filter/group.js} +10 -8
- package/lib/components/{filter-mobile/index.js → filter/icon.js} +37 -21
- package/lib/components/filter/index.js +31 -0
- package/lib/components/list/list.js +1 -1
- package/lib/components/search.js +1 -1
- package/lib/contexts/filter.js +39 -38
- package/lib/libs/utils.js +3 -3
- package/package.json +3 -3
- package/src/base.js +61 -61
- package/src/components/aside.js +36 -0
- package/src/components/filter/custom-chip.js +33 -0
- package/src/components/{filter-mobile/content.js → filter/group.js} +22 -11
- package/src/components/{filter-mobile/index.js → filter/icon.js} +33 -16
- package/src/components/filter/index.js +5 -0
- package/src/components/list/list.js +5 -0
- package/src/components/search.js +3 -11
- package/src/contexts/filter.js +34 -40
- package/src/libs/utils.js +2 -2
- package/lib/components/aside/aside.js +0 -64
- package/lib/components/aside/category-link-list.js +0 -78
- package/lib/components/aside/index.js +0 -13
- package/src/components/aside/aside.js +0 -90
- package/src/components/aside/category-link-list.js +0 -53
- package/src/components/aside/index.js +0 -3
- package/src/components/filter-author.js +0 -38
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@blocklet/list",
|
|
3
|
-
"version": "0.8.
|
|
3
|
+
"version": "0.8.24",
|
|
4
4
|
"description": "Common ux components of blocklet",
|
|
5
5
|
"publishConfig": {
|
|
6
6
|
"access": "public"
|
|
@@ -37,7 +37,7 @@
|
|
|
37
37
|
"react": ">=18.1.0"
|
|
38
38
|
},
|
|
39
39
|
"dependencies": {
|
|
40
|
-
"@arcblock/ux": "^2.1.
|
|
40
|
+
"@arcblock/ux": "^2.1.7",
|
|
41
41
|
"@emotion/react": "^11.9.0",
|
|
42
42
|
"@emotion/styled": "^11.8.1",
|
|
43
43
|
"@mui/icons-material": "^5.6.2",
|
|
@@ -58,5 +58,5 @@
|
|
|
58
58
|
"babel-plugin-inline-react-svg": "^2.0.1",
|
|
59
59
|
"babel-plugin-styled-components": "^1.10.7"
|
|
60
60
|
},
|
|
61
|
-
"gitHead": "
|
|
61
|
+
"gitHead": "0127bb9206c38b378711109469684738205e07a6"
|
|
62
62
|
}
|
package/src/base.js
CHANGED
|
@@ -2,50 +2,53 @@ import React from 'react';
|
|
|
2
2
|
import styled from 'styled-components';
|
|
3
3
|
import SortIcon from '@mui/icons-material/Sort';
|
|
4
4
|
import { Box, Hidden } from '@mui/material';
|
|
5
|
+
import FaceIcon from '@mui/icons-material/Face';
|
|
5
6
|
|
|
6
7
|
import { useFilterContext } from './contexts/filter';
|
|
7
8
|
import CustomSelect from './components/custom-select';
|
|
8
|
-
import
|
|
9
|
+
import { CustomChip, FilterIcon } from './components/filter';
|
|
9
10
|
import { getSortOptions } from './libs/utils';
|
|
10
11
|
import BlockletList from './components/list';
|
|
11
12
|
import Aside from './components/aside';
|
|
12
13
|
import Search from './components/search';
|
|
13
|
-
import FilterOnMobile from './components/filter-mobile';
|
|
14
14
|
|
|
15
15
|
const ListBase = () => {
|
|
16
|
-
const {
|
|
16
|
+
const {
|
|
17
|
+
handleDeveloper,
|
|
18
|
+
blockletList,
|
|
19
|
+
filters,
|
|
20
|
+
developerName,
|
|
21
|
+
handleSort,
|
|
22
|
+
handleCategory,
|
|
23
|
+
handlePrice,
|
|
24
|
+
t,
|
|
25
|
+
getCategoryLocale,
|
|
26
|
+
priceOptions,
|
|
27
|
+
} = useFilterContext();
|
|
28
|
+
|
|
29
|
+
const sortOptions = getSortOptions(t);
|
|
30
|
+
const sortLocale = sortOptions.find((f) => f.value === filters.sortBy)?.name || t('sort.sort');
|
|
31
|
+
const categoryLocale = getCategoryLocale(filters.category);
|
|
32
|
+
const priceLocale = priceOptions.find((price) => price.value === filters.price)?.name;
|
|
33
|
+
|
|
17
34
|
return (
|
|
18
35
|
<Box display="flex" alignItems="flex-start" height="100%">
|
|
19
36
|
<Hidden mdDown>
|
|
20
37
|
<Aside />
|
|
21
38
|
</Hidden>
|
|
22
39
|
<StyledMin>
|
|
23
|
-
<Box className="
|
|
24
|
-
<
|
|
25
|
-
|
|
26
|
-
<FilterAuthor
|
|
27
|
-
user={developerName}
|
|
28
|
-
deleteUserTag={() => {
|
|
29
|
-
handleDeveloper(null);
|
|
30
|
-
}}
|
|
31
|
-
/>
|
|
32
|
-
)}
|
|
33
|
-
</Hidden>
|
|
34
|
-
<Box mt={0} className="searchContainer">
|
|
35
|
-
<Hidden mdUp>
|
|
36
|
-
<StyledSearch className="search" placeholder={t('common.searchStore')} />
|
|
37
|
-
</Hidden>
|
|
38
|
-
</Box>
|
|
39
|
-
<Box mt={0} ml="10px" className="filterContainer">
|
|
40
|
+
<Box className="filter-bar" display="flex" alignItems="center">
|
|
41
|
+
<StyledSearch className="search-container" placeholder={t('common.searchStore')} />
|
|
42
|
+
<Box mt={0} ml="16px" className="filter-container">
|
|
40
43
|
<Hidden mdUp>
|
|
41
44
|
{/* 小屏幕下类别 */}
|
|
42
|
-
<
|
|
45
|
+
<FilterIcon />
|
|
43
46
|
</Hidden>
|
|
44
47
|
{/* 排序选择器 */}
|
|
45
48
|
<CustomSelect
|
|
46
49
|
value={filters.sortBy}
|
|
47
|
-
options={
|
|
48
|
-
title={
|
|
50
|
+
options={sortOptions}
|
|
51
|
+
title={sortLocale}
|
|
49
52
|
icon={<SortIcon />}
|
|
50
53
|
onChange={(v) => {
|
|
51
54
|
handleSort(v);
|
|
@@ -53,19 +56,27 @@ const ListBase = () => {
|
|
|
53
56
|
/>
|
|
54
57
|
</Box>
|
|
55
58
|
</Box>
|
|
56
|
-
<
|
|
57
|
-
<
|
|
58
|
-
{
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
)
|
|
67
|
-
|
|
68
|
-
|
|
59
|
+
<Box display="flex" flexWrap="wrap" alignItems="center" mb="16px">
|
|
60
|
+
<CustomChip
|
|
61
|
+
label={developerName}
|
|
62
|
+
icon={<FaceIcon />}
|
|
63
|
+
onDelete={() => {
|
|
64
|
+
handleDeveloper(null);
|
|
65
|
+
}}
|
|
66
|
+
/>
|
|
67
|
+
<CustomChip
|
|
68
|
+
label={categoryLocale}
|
|
69
|
+
onDelete={() => {
|
|
70
|
+
handleCategory(null);
|
|
71
|
+
}}
|
|
72
|
+
/>
|
|
73
|
+
<CustomChip
|
|
74
|
+
label={priceLocale}
|
|
75
|
+
onDelete={() => {
|
|
76
|
+
handlePrice(null);
|
|
77
|
+
}}
|
|
78
|
+
/>
|
|
79
|
+
</Box>
|
|
69
80
|
<BlockletList blocklets={blockletList} />
|
|
70
81
|
</StyledMin>
|
|
71
82
|
</Box>
|
|
@@ -76,45 +87,34 @@ const StyledMin = styled.main`
|
|
|
76
87
|
flex: 1;
|
|
77
88
|
width: 100%;
|
|
78
89
|
min-width: 0;
|
|
79
|
-
.
|
|
90
|
+
.filter-bar {
|
|
80
91
|
justify-content: space-between;
|
|
81
|
-
margin-bottom:
|
|
92
|
+
margin-bottom: ${(props) => props.theme.spacing(2)};
|
|
82
93
|
}
|
|
83
94
|
.sort-button {
|
|
84
95
|
white-space: nowrap;
|
|
85
96
|
}
|
|
86
|
-
.search {
|
|
97
|
+
.search-container {
|
|
98
|
+
flex: 2;
|
|
87
99
|
margin-left: 0px;
|
|
88
100
|
}
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
.search {
|
|
95
|
-
width: 100%;
|
|
96
|
-
}
|
|
97
|
-
.filterContainer {
|
|
98
|
-
flex: 1;
|
|
99
|
-
display: flex;
|
|
100
|
-
justify-content: flex-end;
|
|
101
|
-
}
|
|
101
|
+
|
|
102
|
+
.filter-container {
|
|
103
|
+
flex: 1;
|
|
104
|
+
display: flex;
|
|
105
|
+
justify-content: flex-end;
|
|
102
106
|
}
|
|
103
|
-
@media (max-width:
|
|
104
|
-
.
|
|
107
|
+
@media (max-width: ${(props) => props.theme.breakpoints.values.md}px) {
|
|
108
|
+
.search-container {
|
|
105
109
|
width: 100%;
|
|
110
|
+
margin-bottom: ${(props) => props.theme.spacing(2)};
|
|
106
111
|
}
|
|
107
|
-
.
|
|
108
|
-
width: 100%;
|
|
112
|
+
.filter-container {
|
|
109
113
|
margin-left: 0;
|
|
110
114
|
display: flex;
|
|
111
115
|
justify-content: flex-start;
|
|
112
116
|
}
|
|
113
|
-
.
|
|
114
|
-
margin-bottom: 20px;
|
|
115
|
-
width: 100%;
|
|
116
|
-
}
|
|
117
|
-
.marketplace-header {
|
|
117
|
+
.filter-bar {
|
|
118
118
|
display: flex;
|
|
119
119
|
flex-direction: column;
|
|
120
120
|
align-items: flex-start;
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import styled from 'styled-components';
|
|
3
|
+
|
|
4
|
+
import { useFilterContext } from '../contexts/filter';
|
|
5
|
+
import { FilterGroup } from './filter';
|
|
6
|
+
|
|
7
|
+
const Aside = () => {
|
|
8
|
+
const { selectedCategory, handleCategory, t, handlePrice, filters, categoryOptions, priceOptions } =
|
|
9
|
+
useFilterContext();
|
|
10
|
+
|
|
11
|
+
return (
|
|
12
|
+
<StyledAside>
|
|
13
|
+
<div>
|
|
14
|
+
<FilterGroup title={t('common.price')} options={priceOptions} value={filters.price} onChange={handlePrice} />
|
|
15
|
+
</div>
|
|
16
|
+
<div style={{ marginTop: '16px' }}>
|
|
17
|
+
<FilterGroup
|
|
18
|
+
title={t('common.category')}
|
|
19
|
+
options={categoryOptions}
|
|
20
|
+
value={selectedCategory}
|
|
21
|
+
onChange={handleCategory}
|
|
22
|
+
/>
|
|
23
|
+
</div>
|
|
24
|
+
</StyledAside>
|
|
25
|
+
);
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
const StyledAside = styled.aside`
|
|
29
|
+
width: 220px;
|
|
30
|
+
margin-right: ${(props) => props.theme.spacing(2)};
|
|
31
|
+
height: 100%;
|
|
32
|
+
`;
|
|
33
|
+
|
|
34
|
+
Aside.propTypes = {};
|
|
35
|
+
Aside.defaultProps = {};
|
|
36
|
+
export default Aside;
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import PropTypes from 'prop-types';
|
|
3
|
+
import styled from 'styled-components';
|
|
4
|
+
import { Chip } from '@mui/material';
|
|
5
|
+
|
|
6
|
+
const StyleDiv = styled.div`
|
|
7
|
+
.MuiChip-root {
|
|
8
|
+
border-radius: 4px;
|
|
9
|
+
height: initial;
|
|
10
|
+
text-transform: capitalize;
|
|
11
|
+
margin-right: ${(props) => props.theme.spacing(2)};
|
|
12
|
+
padding: 4px 0;
|
|
13
|
+
}
|
|
14
|
+
`;
|
|
15
|
+
const FilterChip = ({ label, icon, onDelete, ...containerProps }) => {
|
|
16
|
+
if (!label) return null;
|
|
17
|
+
return (
|
|
18
|
+
<StyleDiv {...containerProps}>
|
|
19
|
+
<Chip icon={icon} label={label} onDelete={onDelete} />
|
|
20
|
+
</StyleDiv>
|
|
21
|
+
);
|
|
22
|
+
};
|
|
23
|
+
FilterChip.propTypes = {
|
|
24
|
+
label: PropTypes.string,
|
|
25
|
+
onDelete: PropTypes.func,
|
|
26
|
+
icon: PropTypes.element,
|
|
27
|
+
};
|
|
28
|
+
FilterChip.defaultProps = {
|
|
29
|
+
onDelete: () => {},
|
|
30
|
+
icon: null,
|
|
31
|
+
label: null,
|
|
32
|
+
};
|
|
33
|
+
export default FilterChip;
|
|
@@ -2,19 +2,21 @@ import React from 'react';
|
|
|
2
2
|
import PropTypes from 'prop-types';
|
|
3
3
|
import styled from 'styled-components';
|
|
4
4
|
|
|
5
|
-
function
|
|
5
|
+
function FilterGroup({ options, onChange, title, value }) {
|
|
6
6
|
return (
|
|
7
7
|
<StyledDiv>
|
|
8
8
|
<div className="title">{title}</div>
|
|
9
9
|
<div className="list">
|
|
10
10
|
{options.map((item) => {
|
|
11
11
|
return (
|
|
12
|
-
<
|
|
12
|
+
<div
|
|
13
|
+
title={item.name}
|
|
13
14
|
key={item.value}
|
|
15
|
+
data-cy="filter"
|
|
14
16
|
className={value === item.value ? 'select item' : 'item'}
|
|
15
17
|
onClick={() => onChange(item.value)}>
|
|
16
18
|
{item.name}
|
|
17
|
-
</
|
|
19
|
+
</div>
|
|
18
20
|
);
|
|
19
21
|
})}
|
|
20
22
|
</div>
|
|
@@ -26,29 +28,38 @@ const StyledDiv = styled.div`
|
|
|
26
28
|
.title {
|
|
27
29
|
font-size: 18px;
|
|
28
30
|
font-weight: bold;
|
|
31
|
+
margin-bottom: ${(props) => props.theme.spacing(1)};
|
|
29
32
|
}
|
|
30
33
|
.list {
|
|
31
|
-
display: flex;
|
|
32
|
-
flex-direction: column;
|
|
33
|
-
align-items: flex-start;
|
|
34
|
-
margin-left: 8px;
|
|
35
34
|
}
|
|
36
35
|
.item {
|
|
37
|
-
|
|
36
|
+
font-size: 16px;
|
|
37
|
+
padding: ${(props) => props.theme.spacing(1)};
|
|
38
|
+
color: #9397a1;
|
|
39
|
+
overflow: hidden;
|
|
40
|
+
text-overflow: ellipsis;
|
|
41
|
+
white-space: nowrap;
|
|
42
|
+
text-transform: capitalize;
|
|
38
43
|
cursor: pointer;
|
|
44
|
+
&:hover {
|
|
45
|
+
background-color: ${(props) => props.theme.palette.grey[50]};
|
|
46
|
+
color: initial;
|
|
47
|
+
font-weight: bold;
|
|
48
|
+
}
|
|
39
49
|
}
|
|
40
50
|
.select {
|
|
41
51
|
color: ${(props) => props.theme.palette.primary.main};
|
|
52
|
+
font-weight: bold;
|
|
42
53
|
}
|
|
43
54
|
`;
|
|
44
55
|
|
|
45
|
-
|
|
56
|
+
FilterGroup.propTypes = {
|
|
46
57
|
title: PropTypes.string.isRequired,
|
|
47
58
|
options: PropTypes.array.isRequired,
|
|
48
59
|
onChange: PropTypes.func.isRequired,
|
|
49
60
|
value: PropTypes.oneOfType([PropTypes.string, PropTypes.number]),
|
|
50
61
|
};
|
|
51
|
-
|
|
62
|
+
FilterGroup.defaultProps = {
|
|
52
63
|
value: null,
|
|
53
64
|
};
|
|
54
|
-
export default
|
|
65
|
+
export default FilterGroup;
|
|
@@ -1,20 +1,26 @@
|
|
|
1
1
|
import React, { useState } from 'react';
|
|
2
|
-
// import PropTypes from 'prop-types';
|
|
3
2
|
import styled from 'styled-components';
|
|
4
3
|
import FilterAltOutlinedIcon from '@mui/icons-material/FilterAltOutlined';
|
|
5
4
|
import Dialog from '@arcblock/ux/lib/Dialog';
|
|
6
5
|
import Button from '@mui/material/Button';
|
|
7
6
|
|
|
8
7
|
import { useFilterContext } from '../../contexts/filter';
|
|
9
|
-
import
|
|
10
|
-
import { getCategoriesOption, getPrices } from '../../libs/utils';
|
|
8
|
+
import FilterGroup from './group';
|
|
11
9
|
|
|
12
|
-
function
|
|
13
|
-
const { selectedCategory, handleCategory, t, handlePrice, filters,
|
|
10
|
+
function FilterIcon() {
|
|
11
|
+
const { selectedCategory, handleCategory, t, handlePrice, filters, categoryOptions, priceOptions } =
|
|
12
|
+
useFilterContext();
|
|
14
13
|
const [open, setOpen] = useState(false);
|
|
15
14
|
|
|
16
|
-
const
|
|
17
|
-
|
|
15
|
+
const handelChange = (type, value) => {
|
|
16
|
+
if (type === 'category') {
|
|
17
|
+
handleCategory(value);
|
|
18
|
+
}
|
|
19
|
+
if (type === 'price') {
|
|
20
|
+
handlePrice(value);
|
|
21
|
+
}
|
|
22
|
+
setOpen(false);
|
|
23
|
+
};
|
|
18
24
|
|
|
19
25
|
return (
|
|
20
26
|
<StyledDiv>
|
|
@@ -22,13 +28,24 @@ function MobileFilter() {
|
|
|
22
28
|
<FilterAltOutlinedIcon className="filter-icon" fontSize="small" />
|
|
23
29
|
</Button>
|
|
24
30
|
<Dialog fullWidth title="" open={open} onClose={() => setOpen(false)}>
|
|
25
|
-
<
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
+
<FilterGroup
|
|
32
|
+
title={t('common.price')}
|
|
33
|
+
options={priceOptions}
|
|
34
|
+
value={filters.price}
|
|
35
|
+
onChange={(v) => {
|
|
36
|
+
handelChange('price', v);
|
|
37
|
+
}}
|
|
31
38
|
/>
|
|
39
|
+
<div style={{ marginTop: '16px' }}>
|
|
40
|
+
<FilterGroup
|
|
41
|
+
title={t('common.category')}
|
|
42
|
+
options={categoryOptions}
|
|
43
|
+
value={selectedCategory}
|
|
44
|
+
onChange={(v) => {
|
|
45
|
+
handelChange('category', v);
|
|
46
|
+
}}
|
|
47
|
+
/>
|
|
48
|
+
</div>
|
|
32
49
|
</Dialog>
|
|
33
50
|
</StyledDiv>
|
|
34
51
|
);
|
|
@@ -47,6 +64,6 @@ const StyledDiv = styled.div`
|
|
|
47
64
|
}
|
|
48
65
|
`;
|
|
49
66
|
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
export default
|
|
67
|
+
FilterIcon.propTypes = {};
|
|
68
|
+
FilterIcon.defaultProps = {};
|
|
69
|
+
export default FilterIcon;
|
|
@@ -105,6 +105,11 @@ const StyledGrid = styled(Grid)`
|
|
|
105
105
|
padding-right: ${(props) => props.theme.spacing(0.5)};
|
|
106
106
|
}
|
|
107
107
|
}
|
|
108
|
+
@media (min-width: ${(props) => props.theme.breakpoints.values.sm}px) {
|
|
109
|
+
&.MuiGrid-item {
|
|
110
|
+
margin-bottom: ${(props) => props.theme.spacing(2)};
|
|
111
|
+
}
|
|
112
|
+
}
|
|
108
113
|
`;
|
|
109
114
|
const CustomEmpty = styled(Empty)`
|
|
110
115
|
text-align: center;
|
package/src/components/search.js
CHANGED
|
@@ -60,11 +60,10 @@ Search.defaultProps = {
|
|
|
60
60
|
};
|
|
61
61
|
const StyledSearch = styled(OutlinedInput)`
|
|
62
62
|
background-color: #fff;
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
margin: 0 10px;
|
|
63
|
+
font-size: 14px;
|
|
64
|
+
border-radius: 6px;
|
|
66
65
|
.MuiInputBase-input {
|
|
67
|
-
padding:
|
|
66
|
+
padding: 8px 0 8px 10px;
|
|
68
67
|
}
|
|
69
68
|
.MuiOutlinedInput-notchedOutline {
|
|
70
69
|
border: none;
|
|
@@ -75,13 +74,6 @@ const StyledSearch = styled(OutlinedInput)`
|
|
|
75
74
|
color: transparent;
|
|
76
75
|
}
|
|
77
76
|
}
|
|
78
|
-
@media (max-width: ${(props) => props.theme.breakpoints.values.md}px) {
|
|
79
|
-
font-size: 14px;
|
|
80
|
-
border-radius: 6px;
|
|
81
|
-
.MuiInputBase-input {
|
|
82
|
-
padding: 8px 0 8px 10px;
|
|
83
|
-
}
|
|
84
|
-
}
|
|
85
77
|
`;
|
|
86
78
|
|
|
87
79
|
const StyledSearchIcon = styled(SearchIcon)`
|
package/src/contexts/filter.js
CHANGED
|
@@ -5,28 +5,17 @@ import orderBy from 'lodash/orderBy';
|
|
|
5
5
|
import axios from 'axios';
|
|
6
6
|
// import joinUrl from 'url-join';
|
|
7
7
|
|
|
8
|
-
import { getCategories, filterBlockletByPrice, replaceTranslate } from '../libs/utils';
|
|
8
|
+
import { getCategories, filterBlockletByPrice, replaceTranslate, getPrices, getCategoryOptions } from '../libs/utils';
|
|
9
9
|
import translations from '../assets/locale';
|
|
10
10
|
import { propTypes, defaultProps } from '../libs/prop-types';
|
|
11
11
|
|
|
12
12
|
const Filter = createContext({});
|
|
13
13
|
const { Provider, Consumer } = Filter;
|
|
14
14
|
|
|
15
|
-
function FilterProvider({
|
|
16
|
-
onSelectBlocklet,
|
|
17
|
-
selectedBlocklets,
|
|
18
|
-
filters,
|
|
19
|
-
children,
|
|
20
|
-
baseUrl,
|
|
21
|
-
endpoint,
|
|
22
|
-
locale,
|
|
23
|
-
blockletRender,
|
|
24
|
-
onFilterChange,
|
|
25
|
-
}) {
|
|
15
|
+
function FilterProvider({ filters, children, baseUrl, endpoint, locale, blockletRender, onFilterChange }) {
|
|
26
16
|
const storeApi = axios.create({
|
|
27
17
|
baseURL: endpoint,
|
|
28
18
|
});
|
|
29
|
-
|
|
30
19
|
const {
|
|
31
20
|
data: allBlocklets,
|
|
32
21
|
error: fetchBlockletsError,
|
|
@@ -53,11 +42,11 @@ function FilterProvider({
|
|
|
53
42
|
{ initialData: [], manual: true }
|
|
54
43
|
);
|
|
55
44
|
|
|
56
|
-
const
|
|
57
|
-
|
|
58
|
-
const hasDeveloperFilter = !!
|
|
45
|
+
const finalFilters = { sortBy: 'popularity', sortDirection: 'desc', ...filters };
|
|
46
|
+
const selectedCategory = finalFilters.category;
|
|
47
|
+
const hasDeveloperFilter = !!finalFilters.developer;
|
|
59
48
|
const categoryState = useMemo(() => {
|
|
60
|
-
return !hasDeveloperFilter ? { data: allCategories } : getCategories(allBlocklets,
|
|
49
|
+
return !hasDeveloperFilter ? { data: allCategories } : getCategories(allBlocklets, finalFilters.developer);
|
|
61
50
|
}, [hasDeveloperFilter, allCategories]);
|
|
62
51
|
|
|
63
52
|
const blockletList = useMemo(() => {
|
|
@@ -71,16 +60,18 @@ function FilterProvider({
|
|
|
71
60
|
publishAt: sortByPublish,
|
|
72
61
|
};
|
|
73
62
|
|
|
74
|
-
let
|
|
63
|
+
let blocklets = allBlocklets || [];
|
|
75
64
|
// 按照付费/免费筛选
|
|
76
|
-
|
|
65
|
+
blocklets = filterBlockletByPrice(blocklets, finalFilters.price);
|
|
77
66
|
// 按照分类筛选
|
|
78
|
-
|
|
67
|
+
blocklets = blocklets.filter((item) => (selectedCategory ? item?.category?.name === selectedCategory : true));
|
|
79
68
|
// 按照作者筛选
|
|
80
|
-
|
|
81
|
-
|
|
69
|
+
blocklets = blocklets.filter((item) =>
|
|
70
|
+
finalFilters?.developer ? item.owner.did === finalFilters.developer : true
|
|
71
|
+
);
|
|
72
|
+
const lowerSearch = finalFilters?.keyword?.toLocaleLowerCase() || '';
|
|
82
73
|
// 按照搜索筛选
|
|
83
|
-
|
|
74
|
+
blocklets = blocklets.filter((item) => {
|
|
84
75
|
return (
|
|
85
76
|
(item?.title || item?.name)?.toLocaleLowerCase().includes(lowerSearch) ||
|
|
86
77
|
item.description?.toLocaleLowerCase().includes(lowerSearch) ||
|
|
@@ -88,8 +79,8 @@ function FilterProvider({
|
|
|
88
79
|
);
|
|
89
80
|
});
|
|
90
81
|
// 排序
|
|
91
|
-
return orderBy(
|
|
92
|
-
}, [allBlocklets,
|
|
82
|
+
return orderBy(blocklets, [sortMap[finalFilters.sortBy]], [finalFilters.sortDirection]);
|
|
83
|
+
}, [allBlocklets, finalFilters]);
|
|
93
84
|
|
|
94
85
|
const categoryList = useMemo(() => {
|
|
95
86
|
const list = categoryState.data || [];
|
|
@@ -105,50 +96,53 @@ function FilterProvider({
|
|
|
105
96
|
|
|
106
97
|
return replaceTranslate(translations[locale][key], data);
|
|
107
98
|
};
|
|
99
|
+
|
|
100
|
+
const categoryOptions = useMemo(() => getCategoryOptions(categoryList, locale), [categoryList, locale]);
|
|
101
|
+
const priceOptions = getPrices(translate);
|
|
102
|
+
|
|
108
103
|
const filterStore = {
|
|
109
104
|
errors: { fetchBlockletsError, fetchCategoriesError },
|
|
110
105
|
loadings: { fetchBlockletsLoading, fetchCategoriesLoading },
|
|
111
106
|
endpoint,
|
|
112
107
|
blockletList,
|
|
113
108
|
t: translate,
|
|
114
|
-
filters:
|
|
109
|
+
filters: finalFilters,
|
|
115
110
|
selectedCategory,
|
|
116
111
|
categoryList,
|
|
117
112
|
baseUrl,
|
|
118
113
|
blockletRender,
|
|
119
114
|
locale,
|
|
120
|
-
|
|
121
|
-
|
|
115
|
+
categoryOptions,
|
|
116
|
+
priceOptions,
|
|
122
117
|
handleSort: (sort) => {
|
|
123
118
|
const changeData = {
|
|
124
|
-
...
|
|
119
|
+
...finalFilters,
|
|
125
120
|
sortBy: sort,
|
|
126
121
|
sortDirection: sort === 'nameAsc' ? 'asc' : 'desc',
|
|
127
122
|
};
|
|
128
123
|
onFilterChange(changeData);
|
|
129
124
|
},
|
|
130
125
|
handleKeyword: (keyWord) => {
|
|
131
|
-
const changeData = { ...
|
|
126
|
+
const changeData = { ...finalFilters, keyword: keyWord || undefined };
|
|
132
127
|
onFilterChange(changeData);
|
|
133
128
|
},
|
|
134
129
|
handlePrice: (price) => {
|
|
135
130
|
const changeData = {
|
|
136
|
-
...
|
|
137
|
-
price: price ===
|
|
131
|
+
...finalFilters,
|
|
132
|
+
price: price === finalFilters.price ? undefined : price,
|
|
138
133
|
};
|
|
139
134
|
onFilterChange(changeData);
|
|
140
135
|
},
|
|
141
136
|
handleCategory: (category) => {
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
} else {
|
|
146
|
-
const changeData = { ...filters, category: category === filters.category ? undefined : category };
|
|
147
|
-
onFilterChange(changeData);
|
|
137
|
+
let finalCategory = category;
|
|
138
|
+
if (category === 'all' || category === finalFilters.category) {
|
|
139
|
+
finalCategory = undefined;
|
|
148
140
|
}
|
|
141
|
+
const changeData = { ...finalFilters, category: finalCategory };
|
|
142
|
+
onFilterChange(changeData);
|
|
149
143
|
},
|
|
150
144
|
handleDeveloper: (developer) => {
|
|
151
|
-
const changeData = { ...
|
|
145
|
+
const changeData = { ...finalFilters, developer: developer || undefined };
|
|
152
146
|
onFilterChange(changeData);
|
|
153
147
|
},
|
|
154
148
|
getCategoryLocale: (category) => {
|
|
@@ -161,7 +155,7 @@ function FilterProvider({
|
|
|
161
155
|
return result;
|
|
162
156
|
},
|
|
163
157
|
get developerName() {
|
|
164
|
-
return allBlocklets.find((
|
|
158
|
+
return allBlocklets.find((blocklet) => blocklet.owner.did === finalFilters.developer)?.owner?.name || '';
|
|
165
159
|
},
|
|
166
160
|
};
|
|
167
161
|
|
package/src/libs/utils.js
CHANGED
|
@@ -35,7 +35,7 @@ const getPrices = (t) => {
|
|
|
35
35
|
{ name: t('blocklet.payment'), value: 'payment' },
|
|
36
36
|
];
|
|
37
37
|
};
|
|
38
|
-
const
|
|
38
|
+
const getCategoryOptions = (list = [], locale = 'en') => {
|
|
39
39
|
return list.map((item) => ({ name: item.locales[locale], value: item.name }));
|
|
40
40
|
};
|
|
41
41
|
/**
|
|
@@ -117,5 +117,5 @@ export {
|
|
|
117
117
|
formatError,
|
|
118
118
|
removeUndefined,
|
|
119
119
|
urlStringify,
|
|
120
|
-
|
|
120
|
+
getCategoryOptions,
|
|
121
121
|
};
|