@blocklet/list 0.9.7 → 0.9.10

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
@@ -29,11 +29,11 @@ var _list = _interopRequireDefault(require("./components/list"));
29
29
 
30
30
  var _aside = _interopRequireDefault(require("./components/aside"));
31
31
 
32
- var _search = _interopRequireDefault(require("./components/search"));
32
+ var _autocomplete = _interopRequireDefault(require("./components/autocomplete"));
33
33
 
34
34
  var _jsxRuntime = require("react/jsx-runtime");
35
35
 
36
- var _templateObject, _templateObject2, _templateObject3;
36
+ var _templateObject, _templateObject2;
37
37
 
38
38
  function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
39
39
 
@@ -71,10 +71,7 @@ function ListBase() {
71
71
  className: "filter-bar",
72
72
  display: "flex",
73
73
  alignItems: "center",
74
- children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(StyledSearch, {
75
- className: "search-container",
76
- placeholder: t('common.searchStore')
77
- }), /*#__PURE__*/(0, _jsxRuntime.jsxs)(_material.Box, {
74
+ children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_autocomplete.default, {}), /*#__PURE__*/(0, _jsxRuntime.jsxs)(_material.Box, {
78
75
  mt: 0,
79
76
  ml: "16px",
80
77
  className: "filter-container",
@@ -131,11 +128,9 @@ function ListBase() {
131
128
  });
132
129
  }
133
130
 
134
- const StyledMin = _styled.default.main(_templateObject || (_templateObject = _taggedTemplateLiteral(["\n display: flex;\n flex-direction: column;\n flex: 1;\n width: 100%;\n height: 100%;\n .filter-bar {\n justify-content: space-between;\n margin-bottom: ", ";\n }\n .sort-button {\n white-space: nowrap;\n }\n .search-container {\n flex: 2;\n margin-left: 0px;\n }\n\n .filter-container {\n flex: 1;\n display: flex;\n justify-content: flex-end;\n }\n @media (max-width: ", "px) {\n .search-container {\n width: 100%;\n margin-bottom: ", ";\n }\n .filter-container {\n margin-left: 0;\n display: flex;\n justify-content: flex-start;\n }\n .filter-bar {\n display: flex;\n flex-direction: column;\n align-items: flex-start;\n }\n }\n @media (max-width: ", "px) {\n .sort-button {\n font-size: 12px;\n }\n }\n"])), props => props.theme.spacing(2), props => props.theme.breakpoints.values.md, props => props.theme.spacing(2), props => props.theme.breakpoints.values.md);
131
+ const StyledMin = _styled.default.main(_templateObject || (_templateObject = _taggedTemplateLiteral(["\n display: flex;\n flex-direction: column;\n flex: 1;\n width: 100%;\n height: 100%;\n .filter-bar {\n justify-content: space-between;\n margin-bottom: ", ";\n }\n .sort-button {\n white-space: nowrap;\n }\n .bl-search-container {\n flex: 2;\n margin-left: 0px;\n }\n\n .filter-container {\n flex: 1;\n display: flex;\n justify-content: flex-end;\n }\n @media (max-width: ", "px) {\n .bl-search-container {\n width: 100%;\n margin-bottom: ", ";\n }\n .filter-container {\n margin-left: 0;\n display: flex;\n justify-content: flex-start;\n }\n .filter-bar {\n display: flex;\n flex-direction: column;\n align-items: flex-start;\n }\n }\n @media (max-width: ", "px) {\n .sort-button {\n font-size: 12px;\n }\n }\n"])), props => props.theme.spacing(2), props => props.theme.breakpoints.values.md, props => props.theme.spacing(2), props => props.theme.breakpoints.values.md);
135
132
 
136
- const StyledSearch = (0, _styled.default)(_search.default)(_templateObject2 || (_templateObject2 = _taggedTemplateLiteral(["\n background-color: ", ";\n"])), props => props.theme.palette.grey[50]);
137
-
138
- const FilterContainer = _styled.default.div(_templateObject3 || (_templateObject3 = _taggedTemplateLiteral(["\n position: sticky;\n top: 0;\n z-index: 999;\n background-color: white;\n"])));
133
+ const FilterContainer = _styled.default.div(_templateObject2 || (_templateObject2 = _taggedTemplateLiteral(["\n position: sticky;\n top: 0;\n z-index: 9;\n background-color: white;\n"])));
139
134
 
140
135
  var _default = ListBase;
141
136
  exports.default = _default;
@@ -51,7 +51,7 @@ function Aside() {
51
51
  });
52
52
  }
53
53
 
54
- const StyledAside = _styled.default.aside(_templateObject || (_templateObject = _taggedTemplateLiteral(["\n width: 220px;\n margin-right: ", ";\n height: 100%;\n position: sticky;\n top: 0;\n overflow-y: auto;\n z-index: 999;\n"])), props => props.theme.spacing(2));
54
+ const StyledAside = _styled.default.aside(_templateObject || (_templateObject = _taggedTemplateLiteral(["\n width: 220px;\n margin-right: ", ";\n height: 100%;\n position: sticky;\n top: 0;\n overflow-y: auto;\n z-index: auto;\n"])), props => props.theme.spacing(2));
55
55
 
56
56
  Aside.propTypes = {};
57
57
  Aside.defaultProps = {};
@@ -0,0 +1,188 @@
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
6
+ exports.default = Autocomplete;
7
+
8
+ var _react = require("react");
9
+
10
+ var _client = require("react-dom/client");
11
+
12
+ var _autocompleteJs = require("@algolia/autocomplete-js");
13
+
14
+ var _react2 = require("@emotion/react");
15
+
16
+ var _styles = require("@mui/material/styles");
17
+
18
+ var _urlJoin = _interopRequireDefault(require("url-join"));
19
+
20
+ var _constant = _interopRequireDefault(require("../../libs/constant"));
21
+
22
+ var _utils = require("../../libs/utils");
23
+
24
+ var _filter = require("../../contexts/filter");
25
+
26
+ var _item = _interopRequireDefault(require("./item"));
27
+
28
+ var _jsxRuntime = require("react/jsx-runtime");
29
+
30
+ function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
31
+
32
+ function ownKeys(object, enumerableOnly) { var keys = Object.keys(object); if (Object.getOwnPropertySymbols) { var symbols = Object.getOwnPropertySymbols(object); enumerableOnly && (symbols = symbols.filter(function (sym) { return Object.getOwnPropertyDescriptor(object, sym).enumerable; })), keys.push.apply(keys, symbols); } return keys; }
33
+
34
+ function _objectSpread(target) { for (var i = 1; i < arguments.length; i++) { var source = null != arguments[i] ? arguments[i] : {}; i % 2 ? ownKeys(Object(source), !0).forEach(function (key) { _defineProperty(target, key, source[key]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(target, Object.getOwnPropertyDescriptors(source)) : ownKeys(Object(source)).forEach(function (key) { Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key)); }); } return target; }
35
+
36
+ function _defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; }
37
+
38
+ function Autocomplete() {
39
+ const containerRef = (0, _react.useRef)(null);
40
+ const panelRootRef = (0, _react.useRef)(null);
41
+ const rootRef = (0, _react.useRef)(null);
42
+ const {
43
+ storeApi,
44
+ filters,
45
+ endpoint,
46
+ handleKeyword,
47
+ t
48
+ } = (0, _filter.useFilterContext)();
49
+ const muiTheme = (0, _styles.useTheme)();
50
+ const getResults = (0, _react.useCallback)(async params => {
51
+ const {
52
+ data
53
+ } = await storeApi.get(_constant.default.blockletsPath, {
54
+ params
55
+ });
56
+ return data.dataList || [];
57
+ }, [storeApi]);
58
+
59
+ const onClickItem = detailUrl => {
60
+ window.location.href = detailUrl;
61
+ };
62
+
63
+ const onSubmit = (0, _react.useCallback)(e => {
64
+ handleKeyword(e.state.query);
65
+ }, [handleKeyword]);
66
+ const onReset = (0, _react.useCallback)(() => {
67
+ handleKeyword();
68
+ }, [handleKeyword]);
69
+ (0, _react.useEffect)(() => {
70
+ if (!containerRef.current) {
71
+ return undefined;
72
+ }
73
+
74
+ const search = (0, _autocompleteJs.autocomplete)({
75
+ container: containerRef.current,
76
+ renderer: {
77
+ createElement: _react.createElement,
78
+ Fragment: _react.Fragment,
79
+ render: () => {}
80
+ },
81
+
82
+ render(_ref, root) {
83
+ let {
84
+ children
85
+ } = _ref;
86
+
87
+ if (!panelRootRef.current || rootRef.current !== root) {
88
+ var _panelRootRef$current;
89
+
90
+ rootRef.current = root;
91
+ (_panelRootRef$current = panelRootRef.current) === null || _panelRootRef$current === void 0 ? void 0 : _panelRootRef$current.unmount();
92
+ panelRootRef.current = (0, _client.createRoot)(root);
93
+ }
94
+
95
+ panelRootRef.current.render(children);
96
+ },
97
+
98
+ classNames: {
99
+ panel: 'bl-autocomplete-panel',
100
+ label: 'bl-autocomplete-label',
101
+ form: 'bl-autocomplete-form',
102
+ input: 'bl-autocomplete-input',
103
+ clearButton: 'bl-autocomplete-clear'
104
+ },
105
+ placeholder: t('common.searchStore'),
106
+ initialState: {
107
+ query: filters.keyword
108
+ },
109
+ onSubmit,
110
+ onReset,
111
+
112
+ getSources(_ref2) {
113
+ let {
114
+ query
115
+ } = _ref2;
116
+
117
+ const params = _objectSpread(_objectSpread({}, filters), {}, {
118
+ sortBy: _constant.default[filters.sortBy],
119
+ page: 1,
120
+ pageSize: 10,
121
+ keyword: query
122
+ });
123
+
124
+ return (0, _utils.debounced)([{
125
+ sourceId: 'blocklets',
126
+
127
+ getItems() {
128
+ return getResults(params);
129
+ },
130
+
131
+ // 选中后填充 搜索框中值
132
+ getItemInputValue(_ref3) {
133
+ let {
134
+ item
135
+ } = _ref3;
136
+ return item.title;
137
+ },
138
+
139
+ getItemUrl(_ref4) {
140
+ let {
141
+ item
142
+ } = _ref4;
143
+ const detailUrl = (0, _urlJoin.default)(endpoint, 'blocklets', item.did);
144
+ return detailUrl;
145
+ },
146
+
147
+ templates: {
148
+ // eslint-disable-next-line react/no-unstable-nested-components
149
+ item(_ref5) {
150
+ let {
151
+ item
152
+ } = _ref5;
153
+ const logoUrl = item.logo ? (0, _urlJoin.default)(endpoint, 'assets', item.did, item.logo) : null;
154
+ const detailUrl = (0, _urlJoin.default)(endpoint, 'blocklets', item.did);
155
+ return /*#__PURE__*/(0, _jsxRuntime.jsx)(_styles.ThemeProvider, {
156
+ theme: muiTheme,
157
+ children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_react2.ThemeProvider, {
158
+ theme: muiTheme,
159
+ children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_item.default, {
160
+ item: item,
161
+ title: (item === null || item === void 0 ? void 0 : item.title) || (item === null || item === void 0 ? void 0 : item.name),
162
+ did: item.did,
163
+ description: item.description,
164
+ cover: logoUrl,
165
+ onMainClick: () => onClickItem(detailUrl)
166
+ })
167
+ })
168
+ });
169
+ },
170
+
171
+ noResults() {
172
+ return t('blocklet.noResults');
173
+ }
174
+
175
+ }
176
+ }]);
177
+ }
178
+
179
+ });
180
+ return () => {
181
+ search.destroy();
182
+ };
183
+ }, [endpoint, filters, getResults, muiTheme, onReset, onSubmit, t]);
184
+ return /*#__PURE__*/(0, _jsxRuntime.jsx)("div", {
185
+ className: "bl-search-container",
186
+ ref: containerRef
187
+ });
188
+ }
@@ -0,0 +1,156 @@
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
6
+ exports.default = Blocklet;
7
+
8
+ var _styled = _interopRequireDefault(require("@emotion/styled"));
9
+
10
+ var _propTypes = _interopRequireDefault(require("prop-types"));
11
+
12
+ var _Typography = _interopRequireDefault(require("@mui/material/Typography"));
13
+
14
+ var _Img = _interopRequireDefault(require("@arcblock/ux/lib/Img"));
15
+
16
+ var _Avatar = _interopRequireDefault(require("@arcblock/did-connect/lib/Avatar"));
17
+
18
+ var _jsxRuntime = require("react/jsx-runtime");
19
+
20
+ const _excluded = ["cover", "onMainClick", "item", "className"];
21
+
22
+ var _templateObject;
23
+
24
+ function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
25
+
26
+ function ownKeys(object, enumerableOnly) { var keys = Object.keys(object); if (Object.getOwnPropertySymbols) { var symbols = Object.getOwnPropertySymbols(object); enumerableOnly && (symbols = symbols.filter(function (sym) { return Object.getOwnPropertyDescriptor(object, sym).enumerable; })), keys.push.apply(keys, symbols); } return keys; }
27
+
28
+ function _objectSpread(target) { for (var i = 1; i < arguments.length; i++) { var source = null != arguments[i] ? arguments[i] : {}; i % 2 ? ownKeys(Object(source), !0).forEach(function (key) { _defineProperty(target, key, source[key]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(target, Object.getOwnPropertyDescriptors(source)) : ownKeys(Object(source)).forEach(function (key) { Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key)); }); } return target; }
29
+
30
+ function _defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; }
31
+
32
+ function _objectWithoutProperties(source, excluded) { if (source == null) return {}; var target = _objectWithoutPropertiesLoose(source, excluded); var key, i; if (Object.getOwnPropertySymbols) { var sourceSymbolKeys = Object.getOwnPropertySymbols(source); for (i = 0; i < sourceSymbolKeys.length; i++) { key = sourceSymbolKeys[i]; if (excluded.indexOf(key) >= 0) continue; if (!Object.prototype.propertyIsEnumerable.call(source, key)) continue; target[key] = source[key]; } } return target; }
33
+
34
+ function _objectWithoutPropertiesLoose(source, excluded) { if (source == null) return {}; var target = {}; var sourceKeys = Object.keys(source); var key, i; for (i = 0; i < sourceKeys.length; i++) { key = sourceKeys[i]; if (excluded.indexOf(key) >= 0) continue; target[key] = source[key]; } return target; }
35
+
36
+ function _taggedTemplateLiteral(strings, raw) { if (!raw) { raw = strings.slice(0); } return Object.freeze(Object.defineProperties(strings, { raw: { value: Object.freeze(raw) } })); }
37
+
38
+ const Div = _styled.default.div(_templateObject || (_templateObject = _taggedTemplateLiteral(["\n &.arcblock-blocklet {\n padding: ", " ", " 0\n ", ";\n }\n .arcblock-blocklet__content {\n cursor: pointer;\n display: flex;\n }\n .arcblock-blocklet__cover {\n width: 64px;\n height: 64px;\n margin-right: ", ";\n overflow: hidden;\n border-radius: 12px;\n transform: translateZ(0);\n }\n\n .arcblock-blocklet__info {\n flex: 1;\n overflow: hidden;\n border-bottom: 1px solid ", ";\n padding-bottom: ", ";\n }\n .arcblock-blocklet__text {\n height: 57px;\n overflow: hidden;\n }\n /* \u8BBE\u7F6E\u591A\u884C\u6587\u672C\u6EA2\u51FA\u663E\u793A\u7701\u7565\u53F7 \u517C\u5BB9fireFox\u3001safari */\n .arcblock-blocklet__title {\n margin: 0;\n font-size: 16px;\n font-weight: 500;\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n }\n .arcblock-blocklet__describe {\n margin-top: ", ";\n color: ", ";\n font-size: 14px;\n font-weight: 500;\n line-height: 17px;\n max-height: 34px;\n overflow: hidden;\n text-overflow: ellipsis;\n display: -webkit-box;\n -webkit-line-clamp: 2;\n -webkit-box-orient: vertical;\n word-break: break-word;\n }\n .ms-highlight {\n background-color: yellow;\n }\n"])), props => props.theme.spacing(2), props => props.theme.spacing(2), props => props.theme.spacing(2), props => props.theme.spacing(2), props => props.theme.palette.divider, props => props.theme.spacing(2), props => props.theme.spacing(0.5), props => props.theme.palette.grey[600]);
39
+
40
+ function Blocklet(_ref) {
41
+ let {
42
+ cover,
43
+ onMainClick,
44
+ item,
45
+ className
46
+ } = _ref,
47
+ rest = _objectWithoutProperties(_ref, _excluded);
48
+
49
+ const wrapHandler = function wrapHandler(handler) {
50
+ let stopFn = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : () => false;
51
+ return function (e) {
52
+ if (stopFn()) {
53
+ e.preventDefault();
54
+ e.stopPropagation();
55
+ } else if (handler instanceof Function) {
56
+ e.preventDefault();
57
+ e.stopPropagation();
58
+
59
+ for (var _len = arguments.length, args = new Array(_len > 1 ? _len - 1 : 0), _key = 1; _key < _len; _key++) {
60
+ args[_key - 1] = arguments[_key];
61
+ }
62
+
63
+ handler(...args);
64
+ }
65
+ };
66
+ };
67
+
68
+ const _onMainClick = wrapHandler(onMainClick);
69
+
70
+ const {
71
+ did,
72
+ description,
73
+ title,
74
+ name
75
+ } = item;
76
+ const blockletTitle = title || name;
77
+ return /*#__PURE__*/(0, _jsxRuntime.jsx)(Div, _objectSpread(_objectSpread({
78
+ className: "".concat(className, " arcblock-blocklet")
79
+ }, rest), {}, {
80
+ children: /*#__PURE__*/(0, _jsxRuntime.jsxs)("div", {
81
+ className: "arcblock-blocklet__content",
82
+ onClick: _onMainClick,
83
+ children: [cover ? /*#__PURE__*/(0, _jsxRuntime.jsx)("div", {
84
+ className: "arcblock-blocklet__cover",
85
+ children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_Img.default, {
86
+ src: cover
87
+ })
88
+ }) : did && /*#__PURE__*/(0, _jsxRuntime.jsx)("div", {
89
+ className: "arcblock-blocklet__cover",
90
+ children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_Avatar.default, {
91
+ did: did,
92
+ size: 64
93
+ })
94
+ }), /*#__PURE__*/(0, _jsxRuntime.jsx)("div", {
95
+ className: "arcblock-blocklet__info",
96
+ children: /*#__PURE__*/(0, _jsxRuntime.jsxs)("div", {
97
+ className: "arcblock-blocklet__text",
98
+ children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_Typography.default, {
99
+ component: "h3",
100
+ variant: "h3",
101
+ className: "arcblock-blocklet__title",
102
+ title: title,
103
+ children: /*#__PURE__*/(0, _jsxRuntime.jsx)(ShowAttributes, {
104
+ item: item,
105
+ attribute: "title",
106
+ value: blockletTitle
107
+ })
108
+ }), description && /*#__PURE__*/(0, _jsxRuntime.jsx)(_Typography.default, {
109
+ component: "div",
110
+ variant: "body2",
111
+ className: "arcblock-blocklet__describe",
112
+ title: description,
113
+ children: /*#__PURE__*/(0, _jsxRuntime.jsx)(ShowAttributes, {
114
+ item: item,
115
+ attribute: "description",
116
+ value: description
117
+ })
118
+ })]
119
+ })
120
+ })]
121
+ })
122
+ }));
123
+ }
124
+
125
+ Blocklet.propTypes = {
126
+ cover: _propTypes.default.string,
127
+ onMainClick: _propTypes.default.func,
128
+ className: _propTypes.default.string,
129
+ item: _propTypes.default.object.isRequired
130
+ };
131
+ Blocklet.defaultProps = {
132
+ cover: null,
133
+ onMainClick: null,
134
+ className: null
135
+ };
136
+
137
+ function ShowAttributes(_ref2) {
138
+ let {
139
+ item,
140
+ attribute,
141
+ value
142
+ } = _ref2;
143
+ // eslint-disable-next-line react/no-danger
144
+ if (item._formatted) return /*#__PURE__*/(0, _jsxRuntime.jsx)("span", {
145
+ dangerouslySetInnerHTML: {
146
+ __html: item._formatted[attribute]
147
+ }
148
+ });
149
+ return value;
150
+ }
151
+
152
+ ShowAttributes.propTypes = {
153
+ item: _propTypes.default.object.isRequired,
154
+ attribute: _propTypes.default.string.isRequired,
155
+ value: _propTypes.default.string.isRequired
156
+ };
@@ -65,8 +65,7 @@ function BlockletList(_ref) {
65
65
  loadMore,
66
66
  endpoint
67
67
  } = (0, _filter.useFilterContext)();
68
- const showFilterTip = !!selectedCategory || !!filters.price; // eslint-disable-next-line no-unused-vars
69
-
68
+ const showFilterTip = !!selectedCategory || !!filters.price;
70
69
  const [sentryRef] = (0, _reactInfiniteScrollHook.default)({
71
70
  loading: loadings.fetchBlockletsLoading || loadings.loadingMore,
72
71
  hasNextPage,
@@ -86,7 +86,7 @@ function FilterProvider(_ref) {
86
86
 
87
87
  const {
88
88
  data = {}
89
- } = await storeApi.get('/api/v2/blocklets.json', {
89
+ } = await storeApi.get(_constant.default.blockletsPath, {
90
90
  params
91
91
  });
92
92
  return data;
@@ -97,7 +97,6 @@ function FilterProvider(_ref) {
97
97
  },
98
98
  loadMore: true,
99
99
  isNoMore: d => d ? d.list.length >= d.total : false,
100
- // eslint-disable-next-line no-console
101
100
  formatResult: response => {
102
101
  return {
103
102
  list: response === null || response === void 0 ? void 0 : response.dataList,
@@ -113,7 +112,7 @@ function FilterProvider(_ref) {
113
112
  } = (0, _ahooks.useRequest)(async () => {
114
113
  const {
115
114
  data
116
- } = await storeApi.get('/api/blocklets/categories');
115
+ } = await storeApi.get(_constant.default.categoriesPath);
117
116
  return data;
118
117
  }, {
119
118
  initialData: []
@@ -137,7 +136,7 @@ function FilterProvider(_ref) {
137
136
  let blocklets = blockletsState.list || []; // 用户传入的过滤函数
138
137
 
139
138
  blocklets = extraFilter(blocklets);
140
- return blocklets; // eslint-disable-next-line react-hooks/exhaustive-deps
139
+ return blocklets;
141
140
  }, [blockletsState, extraFilter]);
142
141
  const categoryOptions = (0, _react.useMemo)(() => (0, _utils.getCategoryOptions)(categoryList, locale), [categoryList, locale]);
143
142
  const priceOptions = (0, _utils.getPrices)(translate);
@@ -161,6 +160,7 @@ function FilterProvider(_ref) {
161
160
  locale,
162
161
  categoryOptions,
163
162
  priceOptions,
163
+ storeApi,
164
164
  hasNextPage: blockletsState.list.length < blockletsState.total,
165
165
  handleSort: sort => {
166
166
  const changeData = _objectSpread(_objectSpread({}, finalFilters), {}, {
package/lib/index.js CHANGED
@@ -5,14 +5,24 @@ Object.defineProperty(exports, "__esModule", {
5
5
  });
6
6
  exports.default = BlockletList;
7
7
 
8
+ var _react = require("@emotion/react");
9
+
10
+ var _styles = require("@mui/material/styles");
11
+
8
12
  var _base = _interopRequireDefault(require("./base"));
9
13
 
10
14
  var _filter = require("./contexts/filter");
11
15
 
12
16
  var _propTypes = require("./libs/prop-types");
13
17
 
18
+ var _utils = require("./libs/utils");
19
+
20
+ require("@algolia/autocomplete-theme-classic/dist/theme.css");
21
+
14
22
  var _jsxRuntime = require("react/jsx-runtime");
15
23
 
24
+ var _templateObject;
25
+
16
26
  function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
17
27
 
18
28
  function ownKeys(object, enumerableOnly) { var keys = Object.keys(object); if (Object.getOwnPropertySymbols) { var symbols = Object.getOwnPropertySymbols(object); enumerableOnly && (symbols = symbols.filter(function (sym) { return Object.getOwnPropertyDescriptor(object, sym).enumerable; })), keys.push.apply(keys, symbols); } return keys; }
@@ -21,9 +31,18 @@ function _objectSpread(target) { for (var i = 1; i < arguments.length; i++) { va
21
31
 
22
32
  function _defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; }
23
33
 
34
+ function _taggedTemplateLiteral(strings, raw) { if (!raw) { raw = strings.slice(0); } return Object.freeze(Object.defineProperties(strings, { raw: { value: Object.freeze(raw) } })); }
35
+
24
36
  function BlockletList(props) {
25
- return /*#__PURE__*/(0, _jsxRuntime.jsx)(_filter.FilterProvider, _objectSpread(_objectSpread({}, props), {}, {
26
- children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_base.default, {})
37
+ const muiTheme = (0, _styles.useTheme)();
38
+ const primaryColor = (0, _utils.toColorRgb)(muiTheme.palette.primary.main);
39
+ const globalStyles = {
40
+ autocomplete: theme => (0, _react.css)(_templateObject || (_templateObject = _taggedTemplateLiteral(["\n :root {\n --aa-detached-media-query: (max-width: ", "px) !important;\n --aa-detached-modal-media-query: (min-width: ", "px) !important;\n --aa-detached-modal-max-width: ", "px !important;\n --aa-selected-color-rgb: ", ", ", ", ", " !important;\n --aa-primary-color-rgb: ", ", ", ", ", " !important;\n --aa-input-background-color-rgb: 250, 250, 250 !important;\n }\n .bl-autocomplete-panel {\n z-index: ", " !important;\n }\n .bl-autocomplete-label {\n .aa-SubmitIcon {\n color: ", " !important;\n }\n }\n .bl-autocomplete-form {\n border: none !important;\n &:focus-within {\n box-shadow: none !important;\n }\n }\n .aa-DetachedSearchButton {\n border: none !important;\n &:focus-within {\n box-shadow: none !important;\n }\n .aa-SubmitIcon {\n color: ", " !important;\n }\n }\n "])), theme.breakpoints.values.md, theme.breakpoints.values.md, theme.breakpoints.values.md, primaryColor.r, primaryColor.g, primaryColor.b, primaryColor.r, primaryColor.g, primaryColor.b, theme.zIndex.modal + 1, theme.palette.grey[500], theme.palette.grey[500])
41
+ };
42
+ return /*#__PURE__*/(0, _jsxRuntime.jsxs)(_filter.FilterProvider, _objectSpread(_objectSpread({}, props), {}, {
43
+ children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_react.Global, {
44
+ styles: globalStyles.autocomplete
45
+ }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_base.default, {})]
27
46
  }));
28
47
  }
29
48
 
@@ -11,7 +11,10 @@ const constant = {
11
11
  publishAt: 'lastPublishedAt',
12
12
  mobilePageSize: 10,
13
13
  pageSize: 18,
14
- defaultCurrentPage: 1
14
+ defaultCurrentPage: 1,
15
+ blockletsPath: '/api/v2/blocklets.json',
16
+ categoriesPath: '/api/blocklets/categories',
17
+ cacheSize: 100
15
18
  };
16
19
  var _default = constant;
17
20
  exports.default = _default;
package/lib/libs/utils.js CHANGED
@@ -3,12 +3,14 @@
3
3
  Object.defineProperty(exports, "__esModule", {
4
4
  value: true
5
5
  });
6
- exports.urlStringify = exports.replaceTranslate = exports.removeUndefined = exports.isMobileScreen = exports.getStoreDetail = exports.getSortOptions = exports.getPrices = exports.getCurrentPage = exports.getCategoryOptions = exports.formatLogoPath = exports.formatError = exports.filterBlockletByPrice = void 0;
6
+ exports.urlStringify = exports.toColorRgb = exports.replaceTranslate = exports.removeUndefined = exports.isMobileScreen = exports.getStoreDetail = exports.getSortOptions = exports.getPrices = exports.getCurrentPage = exports.getCategoryOptions = exports.formatLogoPath = exports.formatError = exports.filterBlockletByPrice = exports.debounced = void 0;
7
7
 
8
8
  var _urlJoin = _interopRequireDefault(require("url-join"));
9
9
 
10
10
  var _cloneDeep = _interopRequireDefault(require("lodash/cloneDeep"));
11
11
 
12
+ var _color = _interopRequireDefault(require("color"));
13
+
12
14
  function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
13
15
 
14
16
  const isFreeBlocklet = blocklet => {
@@ -152,4 +154,31 @@ const getCurrentPage = (length, pageSize) => {
152
154
  return 1;
153
155
  };
154
156
 
155
- exports.getCurrentPage = getCurrentPage;
157
+ exports.getCurrentPage = getCurrentPage;
158
+
159
+ const toColorRgb = colorStr => {
160
+ const color = (0, _color.default)(colorStr);
161
+ return color.rgb().object();
162
+ };
163
+
164
+ exports.toColorRgb = toColorRgb;
165
+
166
+ function debouncePromise(fn, time) {
167
+ let timerId;
168
+ return function debounced() {
169
+ for (var _len = arguments.length, args = new Array(_len), _key = 0; _key < _len; _key++) {
170
+ args[_key] = arguments[_key];
171
+ }
172
+
173
+ if (timerId) {
174
+ clearTimeout(timerId);
175
+ }
176
+
177
+ return new Promise(resolve => {
178
+ timerId = setTimeout(() => resolve(fn(...args)), time);
179
+ });
180
+ };
181
+ }
182
+
183
+ const debounced = debouncePromise(items => Promise.resolve(items), 300);
184
+ exports.debounced = debounced;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@blocklet/list",
3
- "version": "0.9.7",
3
+ "version": "0.9.10",
4
4
  "description": "Common ux components of blocklet",
5
5
  "publishConfig": {
6
6
  "access": "public"
@@ -33,16 +33,23 @@
33
33
  "url": "https://github.com/blocklet/blocklet-store/issues"
34
34
  },
35
35
  "peerDependencies": {
36
+ "@arcblock/did-connect": ">=2.2.0",
36
37
  "@mui/material": ">=5.6.4",
37
- "react": ">=18.1.0"
38
+ "@mui/styles": ">=5.6.2",
39
+ "react": ">=18.1.0",
40
+ "react-dom": ">=18.2.0"
38
41
  },
39
42
  "dependencies": {
43
+ "@algolia/autocomplete-js": "^1.7.1",
44
+ "@algolia/autocomplete-theme-classic": "^1.7.1",
40
45
  "@arcblock/ux": "^2.4.0",
41
46
  "@emotion/react": "^11.10.0",
42
47
  "@emotion/styled": "^11.10.0",
43
48
  "@mui/icons-material": "^5.8.4",
44
49
  "ahooks": "^2.10.14",
50
+ "algoliasearch": "^4.14.2",
45
51
  "axios": "^0.27.2",
52
+ "color": "^4.2.3",
46
53
  "flat": "^5.0.2",
47
54
  "lodash": "^4.17.21",
48
55
  "prop-types": "^15.8.1",
@@ -61,5 +68,5 @@
61
68
  "eslint": "^8.21.0",
62
69
  "prettier": "^2.7.1"
63
70
  },
64
- "gitHead": "9ad2112390be19ef648ec199cb07a5c6a4a5e10b"
71
+ "gitHead": "3dd78770094c338b18c0958af3db442b9f4784c8"
65
72
  }
package/src/base.js CHANGED
@@ -4,14 +4,13 @@ import { Box, Hidden } from '@mui/material';
4
4
  import FaceIcon from '@mui/icons-material/Face';
5
5
  import { ErrorBoundary } from 'react-error-boundary';
6
6
  import { ErrorFallback } from '@arcblock/ux/lib/ErrorBoundary';
7
-
8
7
  import { useFilterContext } from './contexts/filter';
9
8
  import CustomSelect from './components/custom-select';
10
9
  import { CustomChip, FilterIcon } from './components/filter';
11
10
  import { getSortOptions } from './libs/utils';
12
11
  import BlockletList from './components/list';
13
12
  import Aside from './components/aside';
14
- import Search from './components/search';
13
+ import Autocomplete from './components/autocomplete';
15
14
 
16
15
  function ListBase() {
17
16
  const {
@@ -39,7 +38,7 @@ function ListBase() {
39
38
  <StyledMin>
40
39
  <FilterContainer>
41
40
  <Box className="filter-bar" display="flex" alignItems="center">
42
- <StyledSearch className="search-container" placeholder={t('common.searchStore')} />
41
+ <Autocomplete />
43
42
  <Box mt={0} ml="16px" className="filter-container">
44
43
  <Hidden mdUp>
45
44
  {/* 小屏幕下类别 */}
@@ -102,7 +101,7 @@ const StyledMin = styled.main`
102
101
  .sort-button {
103
102
  white-space: nowrap;
104
103
  }
105
- .search-container {
104
+ .bl-search-container {
106
105
  flex: 2;
107
106
  margin-left: 0px;
108
107
  }
@@ -113,7 +112,7 @@ const StyledMin = styled.main`
113
112
  justify-content: flex-end;
114
113
  }
115
114
  @media (max-width: ${(props) => props.theme.breakpoints.values.md}px) {
116
- .search-container {
115
+ .bl-search-container {
117
116
  width: 100%;
118
117
  margin-bottom: ${(props) => props.theme.spacing(2)};
119
118
  }
@@ -135,14 +134,10 @@ const StyledMin = styled.main`
135
134
  }
136
135
  `;
137
136
 
138
- const StyledSearch = styled(Search)`
139
- background-color: ${(props) => props.theme.palette.grey[50]};
140
- `;
141
-
142
137
  const FilterContainer = styled.div`
143
138
  position: sticky;
144
139
  top: 0;
145
- z-index: 999;
140
+ z-index: 9;
146
141
  background-color: white;
147
142
  `;
148
143
 
@@ -33,7 +33,7 @@ const StyledAside = styled.aside`
33
33
  position: sticky;
34
34
  top: 0;
35
35
  overflow-y: auto;
36
- z-index: 999;
36
+ z-index: auto;
37
37
  `;
38
38
 
39
39
  Aside.propTypes = {};
@@ -0,0 +1,131 @@
1
+ import { createElement, Fragment, useEffect, useRef, useCallback } from 'react';
2
+ import { createRoot } from 'react-dom/client';
3
+ import { autocomplete } from '@algolia/autocomplete-js';
4
+
5
+ import { ThemeProvider as EmotionThemeProvider } from '@emotion/react';
6
+ import { ThemeProvider as MuiThemeProvider, useTheme } from '@mui/material/styles';
7
+ import joinUrl from 'url-join';
8
+
9
+ import constant from '../../libs/constant';
10
+ import { debounced } from '../../libs/utils';
11
+ import { useFilterContext } from '../../contexts/filter';
12
+ import Blocklet from './item';
13
+
14
+ export default function Autocomplete() {
15
+ const containerRef = useRef(null);
16
+ const panelRootRef = useRef(null);
17
+ const rootRef = useRef(null);
18
+ const { storeApi, filters, endpoint, handleKeyword, t } = useFilterContext();
19
+ const muiTheme = useTheme();
20
+
21
+ const getResults = useCallback(
22
+ async (params) => {
23
+ const { data } = await storeApi.get(constant.blockletsPath, { params });
24
+
25
+ return data.dataList || [];
26
+ },
27
+ [storeApi]
28
+ );
29
+
30
+ const onClickItem = (detailUrl) => {
31
+ window.location.href = detailUrl;
32
+ };
33
+ const onSubmit = useCallback(
34
+ (e) => {
35
+ handleKeyword(e.state.query);
36
+ },
37
+ [handleKeyword]
38
+ );
39
+ const onReset = useCallback(() => {
40
+ handleKeyword();
41
+ }, [handleKeyword]);
42
+
43
+ useEffect(() => {
44
+ if (!containerRef.current) {
45
+ return undefined;
46
+ }
47
+
48
+ const search = autocomplete({
49
+ container: containerRef.current,
50
+ renderer: { createElement, Fragment, render: () => {} },
51
+ render({ children }, root) {
52
+ if (!panelRootRef.current || rootRef.current !== root) {
53
+ rootRef.current = root;
54
+
55
+ panelRootRef.current?.unmount();
56
+ panelRootRef.current = createRoot(root);
57
+ }
58
+
59
+ panelRootRef.current.render(children);
60
+ },
61
+ classNames: {
62
+ panel: 'bl-autocomplete-panel',
63
+ label: 'bl-autocomplete-label',
64
+ form: 'bl-autocomplete-form',
65
+ input: 'bl-autocomplete-input',
66
+ clearButton: 'bl-autocomplete-clear',
67
+ },
68
+ placeholder: t('common.searchStore'),
69
+ initialState: {
70
+ query: filters.keyword,
71
+ },
72
+ onSubmit,
73
+ onReset,
74
+ getSources({ query }) {
75
+ const params = {
76
+ ...filters,
77
+ sortBy: constant[filters.sortBy],
78
+ page: 1,
79
+ pageSize: 10,
80
+ keyword: query,
81
+ };
82
+ return debounced([
83
+ {
84
+ sourceId: 'blocklets',
85
+ getItems() {
86
+ return getResults(params);
87
+ },
88
+ // 选中后填充 搜索框中值
89
+ getItemInputValue({ item }) {
90
+ return item.title;
91
+ },
92
+ getItemUrl({ item }) {
93
+ const detailUrl = joinUrl(endpoint, 'blocklets', item.did);
94
+ return detailUrl;
95
+ },
96
+ templates: {
97
+ // eslint-disable-next-line react/no-unstable-nested-components
98
+ item({ item }) {
99
+ const logoUrl = item.logo ? joinUrl(endpoint, 'assets', item.did, item.logo) : null;
100
+ const detailUrl = joinUrl(endpoint, 'blocklets', item.did);
101
+ return (
102
+ <MuiThemeProvider theme={muiTheme}>
103
+ <EmotionThemeProvider theme={muiTheme}>
104
+ <Blocklet
105
+ item={item}
106
+ title={item?.title || item?.name}
107
+ did={item.did}
108
+ description={item.description}
109
+ cover={logoUrl}
110
+ onMainClick={() => onClickItem(detailUrl)}
111
+ />
112
+ </EmotionThemeProvider>
113
+ </MuiThemeProvider>
114
+ );
115
+ },
116
+ noResults() {
117
+ return t('blocklet.noResults');
118
+ },
119
+ },
120
+ },
121
+ ]);
122
+ },
123
+ });
124
+
125
+ return () => {
126
+ search.destroy();
127
+ };
128
+ }, [endpoint, filters, getResults, muiTheme, onReset, onSubmit, t]);
129
+
130
+ return <div className="bl-search-container" ref={containerRef} />;
131
+ }
@@ -0,0 +1,133 @@
1
+ import styled from '@emotion/styled';
2
+ import PropTypes from 'prop-types';
3
+ import Typography from '@mui/material/Typography';
4
+ import Img from '@arcblock/ux/lib/Img';
5
+ import Avatar from '@arcblock/did-connect/lib/Avatar';
6
+
7
+ const Div = styled.div`
8
+ &.arcblock-blocklet {
9
+ padding: ${(props) => props.theme.spacing(2)} ${(props) => props.theme.spacing(2)} 0
10
+ ${(props) => props.theme.spacing(2)};
11
+ }
12
+ .arcblock-blocklet__content {
13
+ cursor: pointer;
14
+ display: flex;
15
+ }
16
+ .arcblock-blocklet__cover {
17
+ width: 64px;
18
+ height: 64px;
19
+ margin-right: ${(props) => props.theme.spacing(2)};
20
+ overflow: hidden;
21
+ border-radius: 12px;
22
+ transform: translateZ(0);
23
+ }
24
+
25
+ .arcblock-blocklet__info {
26
+ flex: 1;
27
+ overflow: hidden;
28
+ border-bottom: 1px solid ${(props) => props.theme.palette.divider};
29
+ padding-bottom: ${(props) => props.theme.spacing(2)};
30
+ }
31
+ .arcblock-blocklet__text {
32
+ height: 57px;
33
+ overflow: hidden;
34
+ }
35
+ /* 设置多行文本溢出显示省略号 兼容fireFox、safari */
36
+ .arcblock-blocklet__title {
37
+ margin: 0;
38
+ font-size: 16px;
39
+ font-weight: 500;
40
+ overflow: hidden;
41
+ text-overflow: ellipsis;
42
+ white-space: nowrap;
43
+ }
44
+ .arcblock-blocklet__describe {
45
+ margin-top: ${(props) => props.theme.spacing(0.5)};
46
+ color: ${(props) => props.theme.palette.grey[600]};
47
+ font-size: 14px;
48
+ font-weight: 500;
49
+ line-height: 17px;
50
+ max-height: 34px;
51
+ overflow: hidden;
52
+ text-overflow: ellipsis;
53
+ display: -webkit-box;
54
+ -webkit-line-clamp: 2;
55
+ -webkit-box-orient: vertical;
56
+ word-break: break-word;
57
+ }
58
+ .ms-highlight {
59
+ background-color: yellow;
60
+ }
61
+ `;
62
+
63
+ export default function Blocklet({ cover, onMainClick, item, className, ...rest }) {
64
+ const wrapHandler =
65
+ (handler, stopFn = () => false) =>
66
+ (e, ...args) => {
67
+ if (stopFn()) {
68
+ e.preventDefault();
69
+ e.stopPropagation();
70
+ } else if (handler instanceof Function) {
71
+ e.preventDefault();
72
+ e.stopPropagation();
73
+ handler(...args);
74
+ }
75
+ };
76
+ const _onMainClick = wrapHandler(onMainClick);
77
+ const { did, description, title, name } = item;
78
+ const blockletTitle = title || name;
79
+
80
+ return (
81
+ <Div className={`${className} arcblock-blocklet`} {...rest}>
82
+ <div className="arcblock-blocklet__content" onClick={_onMainClick}>
83
+ {cover ? (
84
+ <div className="arcblock-blocklet__cover">
85
+ <Img src={cover} />
86
+ </div>
87
+ ) : (
88
+ did && (
89
+ <div className="arcblock-blocklet__cover">
90
+ <Avatar did={did} size={64} />
91
+ </div>
92
+ )
93
+ )}
94
+ <div className="arcblock-blocklet__info">
95
+ <div className="arcblock-blocklet__text">
96
+ <Typography component="h3" variant="h3" className="arcblock-blocklet__title" title={title}>
97
+ <ShowAttributes item={item} attribute="title" value={blockletTitle} />
98
+ </Typography>
99
+ {description && (
100
+ <Typography component="div" variant="body2" className="arcblock-blocklet__describe" title={description}>
101
+ <ShowAttributes item={item} attribute="description" value={description} />
102
+ </Typography>
103
+ )}
104
+ </div>
105
+ </div>
106
+ </div>
107
+ </Div>
108
+ );
109
+ }
110
+
111
+ Blocklet.propTypes = {
112
+ cover: PropTypes.string,
113
+ onMainClick: PropTypes.func,
114
+ className: PropTypes.string,
115
+ item: PropTypes.object.isRequired,
116
+ };
117
+
118
+ Blocklet.defaultProps = {
119
+ cover: null,
120
+ onMainClick: null,
121
+ className: null,
122
+ };
123
+
124
+ function ShowAttributes({ item, attribute, value }) {
125
+ // eslint-disable-next-line react/no-danger
126
+ if (item._formatted) return <span dangerouslySetInnerHTML={{ __html: item._formatted[attribute] }} />;
127
+ return value;
128
+ }
129
+ ShowAttributes.propTypes = {
130
+ item: PropTypes.object.isRequired,
131
+ attribute: PropTypes.string.isRequired,
132
+ value: PropTypes.string.isRequired,
133
+ };
@@ -27,7 +27,6 @@ export default function BlockletList({ blocklets, ...rest }) {
27
27
 
28
28
  const showFilterTip = !!selectedCategory || !!filters.price;
29
29
 
30
- // eslint-disable-next-line no-unused-vars
31
30
  const [sentryRef] = useInfiniteScroll({
32
31
  loading: loadings.fetchBlockletsLoading || loadings.loadingMore,
33
32
  hasNextPage,
@@ -42,14 +42,13 @@ function FilterProvider({ filters, children, endpoint, locale, blockletRender, o
42
42
  page: getCurrentPage(d?.list?.length || 0, paginateState.pageSize),
43
43
  pageSize: paginateState.pageSize,
44
44
  };
45
- const { data = {} } = await storeApi.get('/api/v2/blocklets.json', { params });
45
+ const { data = {} } = await storeApi.get(constant.blockletsPath, { params });
46
46
  return data;
47
47
  },
48
48
  {
49
49
  initialData: { list: [], total: 0 },
50
50
  loadMore: true,
51
51
  isNoMore: (d) => (d ? d.list.length >= d.total : false),
52
- // eslint-disable-next-line no-console
53
52
  formatResult: (response) => {
54
53
  return { list: response?.dataList, total: response.total };
55
54
  },
@@ -63,7 +62,7 @@ function FilterProvider({ filters, children, endpoint, locale, blockletRender, o
63
62
  loading: fetchCategoriesLoading,
64
63
  } = useRequest(
65
64
  async () => {
66
- const { data } = await storeApi.get('/api/blocklets/categories');
65
+ const { data } = await storeApi.get(constant.categoriesPath);
67
66
  return data;
68
67
  },
69
68
  { initialData: [] }
@@ -88,7 +87,6 @@ function FilterProvider({ filters, children, endpoint, locale, blockletRender, o
88
87
  // 用户传入的过滤函数
89
88
  blocklets = extraFilter(blocklets);
90
89
  return blocklets;
91
- // eslint-disable-next-line react-hooks/exhaustive-deps
92
90
  }, [blockletsState, extraFilter]);
93
91
 
94
92
  const categoryOptions = useMemo(() => getCategoryOptions(categoryList, locale), [categoryList, locale]);
@@ -106,6 +104,7 @@ function FilterProvider({ filters, children, endpoint, locale, blockletRender, o
106
104
  locale,
107
105
  categoryOptions,
108
106
  priceOptions,
107
+ storeApi,
109
108
  hasNextPage: blockletsState.list.length < blockletsState.total,
110
109
  handleSort: (sort) => {
111
110
  const changeData = {
package/src/index.js CHANGED
@@ -1,10 +1,54 @@
1
+ import { Global, css } from '@emotion/react';
2
+ import { useTheme } from '@mui/material/styles';
1
3
  import SelectBase from './base';
2
4
  import { FilterProvider } from './contexts/filter';
3
5
  import { propTypes, defaultProps } from './libs/prop-types';
6
+ import { toColorRgb } from './libs/utils';
7
+ // 不知道为什么引入 theme.min.css 话,blocklet-store 打包就会报错
8
+ import '@algolia/autocomplete-theme-classic/dist/theme.css';
4
9
 
5
10
  export default function BlockletList(props) {
11
+ const muiTheme = useTheme();
12
+ const primaryColor = toColorRgb(muiTheme.palette.primary.main);
13
+
14
+ const globalStyles = {
15
+ autocomplete: (theme) => css`
16
+ :root {
17
+ --aa-detached-media-query: (max-width: ${theme.breakpoints.values.md}px) !important;
18
+ --aa-detached-modal-media-query: (min-width: ${theme.breakpoints.values.md}px) !important;
19
+ --aa-detached-modal-max-width: ${theme.breakpoints.values.md}px !important;
20
+ --aa-selected-color-rgb: ${primaryColor.r}, ${primaryColor.g}, ${primaryColor.b} !important;
21
+ --aa-primary-color-rgb: ${primaryColor.r}, ${primaryColor.g}, ${primaryColor.b} !important;
22
+ --aa-input-background-color-rgb: 250, 250, 250 !important;
23
+ }
24
+ .bl-autocomplete-panel {
25
+ z-index: ${theme.zIndex.modal + 1} !important;
26
+ }
27
+ .bl-autocomplete-label {
28
+ .aa-SubmitIcon {
29
+ color: ${theme.palette.grey[500]} !important;
30
+ }
31
+ }
32
+ .bl-autocomplete-form {
33
+ border: none !important;
34
+ &:focus-within {
35
+ box-shadow: none !important;
36
+ }
37
+ }
38
+ .aa-DetachedSearchButton {
39
+ border: none !important;
40
+ &:focus-within {
41
+ box-shadow: none !important;
42
+ }
43
+ .aa-SubmitIcon {
44
+ color: ${theme.palette.grey[500]} !important;
45
+ }
46
+ }
47
+ `,
48
+ };
6
49
  return (
7
50
  <FilterProvider {...props}>
51
+ <Global styles={globalStyles.autocomplete} />
8
52
  <SelectBase />
9
53
  </FilterProvider>
10
54
  );
@@ -6,6 +6,9 @@ const constant = {
6
6
  mobilePageSize: 10,
7
7
  pageSize: 18,
8
8
  defaultCurrentPage: 1,
9
+ blockletsPath: '/api/v2/blocklets.json',
10
+ categoriesPath: '/api/blocklets/categories',
11
+ cacheSize: 100,
9
12
  };
10
13
 
11
14
  export default constant;
package/src/libs/utils.js CHANGED
@@ -1,5 +1,6 @@
1
1
  import joinURL from 'url-join';
2
2
  import cloneDeep from 'lodash/cloneDeep';
3
+ import Color from 'color';
3
4
 
4
5
  const isFreeBlocklet = (blocklet) => {
5
6
  if (!blocklet.payment) {
@@ -104,6 +105,26 @@ const getCurrentPage = (length, pageSize) => {
104
105
  return 1;
105
106
  };
106
107
 
108
+ const toColorRgb = (colorStr) => {
109
+ const color = Color(colorStr);
110
+ return color.rgb().object();
111
+ };
112
+
113
+ function debouncePromise(fn, time) {
114
+ let timerId;
115
+
116
+ return function debounced(...args) {
117
+ if (timerId) {
118
+ clearTimeout(timerId);
119
+ }
120
+
121
+ return new Promise((resolve) => {
122
+ timerId = setTimeout(() => resolve(fn(...args)), time);
123
+ });
124
+ };
125
+ }
126
+ const debounced = debouncePromise((items) => Promise.resolve(items), 300);
127
+
107
128
  export {
108
129
  getSortOptions,
109
130
  getPrices,
@@ -117,4 +138,6 @@ export {
117
138
  getCategoryOptions,
118
139
  isMobileScreen,
119
140
  getCurrentPage,
141
+ toColorRgb,
142
+ debounced,
120
143
  };
@@ -1,108 +0,0 @@
1
- "use strict";
2
-
3
- Object.defineProperty(exports, "__esModule", {
4
- value: true
5
- });
6
- exports.default = void 0;
7
-
8
- var _react = require("react");
9
-
10
- var _propTypes = _interopRequireDefault(require("prop-types"));
11
-
12
- var _Search = _interopRequireDefault(require("@mui/icons-material/Search"));
13
-
14
- var _Close = _interopRequireDefault(require("@mui/icons-material/Close"));
15
-
16
- var _material = require("@mui/material");
17
-
18
- var _ahooks = require("ahooks");
19
-
20
- var _styled = _interopRequireDefault(require("@emotion/styled"));
21
-
22
- var _filter = require("../contexts/filter");
23
-
24
- var _jsxRuntime = require("react/jsx-runtime");
25
-
26
- var _templateObject, _templateObject2, _templateObject3;
27
-
28
- const _excluded = ["placeholder"];
29
-
30
- function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
31
-
32
- function _taggedTemplateLiteral(strings, raw) { if (!raw) { raw = strings.slice(0); } return Object.freeze(Object.defineProperties(strings, { raw: { value: Object.freeze(raw) } })); }
33
-
34
- function ownKeys(object, enumerableOnly) { var keys = Object.keys(object); if (Object.getOwnPropertySymbols) { var symbols = Object.getOwnPropertySymbols(object); enumerableOnly && (symbols = symbols.filter(function (sym) { return Object.getOwnPropertyDescriptor(object, sym).enumerable; })), keys.push.apply(keys, symbols); } return keys; }
35
-
36
- function _objectSpread(target) { for (var i = 1; i < arguments.length; i++) { var source = null != arguments[i] ? arguments[i] : {}; i % 2 ? ownKeys(Object(source), !0).forEach(function (key) { _defineProperty(target, key, source[key]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(target, Object.getOwnPropertyDescriptors(source)) : ownKeys(Object(source)).forEach(function (key) { Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key)); }); } return target; }
37
-
38
- function _defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; }
39
-
40
- function _objectWithoutProperties(source, excluded) { if (source == null) return {}; var target = _objectWithoutPropertiesLoose(source, excluded); var key, i; if (Object.getOwnPropertySymbols) { var sourceSymbolKeys = Object.getOwnPropertySymbols(source); for (i = 0; i < sourceSymbolKeys.length; i++) { key = sourceSymbolKeys[i]; if (excluded.indexOf(key) >= 0) continue; if (!Object.prototype.propertyIsEnumerable.call(source, key)) continue; target[key] = source[key]; } } return target; }
41
-
42
- function _objectWithoutPropertiesLoose(source, excluded) { if (source == null) return {}; var target = {}; var sourceKeys = Object.keys(source); var key, i; for (i = 0; i < sourceKeys.length; i++) { key = sourceKeys[i]; if (excluded.indexOf(key) >= 0) continue; target[key] = source[key]; } return target; }
43
-
44
- function Search(_ref) {
45
- let {
46
- placeholder
47
- } = _ref,
48
- rest = _objectWithoutProperties(_ref, _excluded);
49
-
50
- const {
51
- filters,
52
- handleKeyword
53
- } = (0, _filter.useFilterContext)();
54
- const [searchStr, setSearchStr] = (0, _react.useState)(filters.keyword || '');
55
- const debouncedSearch = (0, _ahooks.useDebounceFn)(handleKeyword, {
56
- wait: 300
57
- });
58
-
59
- const handleChange = event => {
60
- const {
61
- value
62
- } = event.target;
63
- setSearchStr(value);
64
- debouncedSearch.run(value);
65
- };
66
-
67
- const handleClose = () => {
68
- setSearchStr('');
69
- handleKeyword();
70
- };
71
-
72
- (0, _react.useEffect)(() => {
73
- setSearchStr(filters.keyword || '');
74
- }, [filters.keyword]);
75
- return /*#__PURE__*/(0, _jsxRuntime.jsx)(StyledSearch, _objectSpread({
76
- inputProps: {
77
- 'data-cy': 'search-blocklet'
78
- },
79
- startAdornment: /*#__PURE__*/(0, _jsxRuntime.jsx)(_material.InputAdornment, {
80
- position: "start",
81
- children: /*#__PURE__*/(0, _jsxRuntime.jsx)(StyledSearchIcon, {})
82
- }),
83
- onChange: handleChange,
84
- placeholder: placeholder,
85
- value: searchStr,
86
- title: placeholder,
87
- "data-cy": "search",
88
- endAdornment: searchStr && /*#__PURE__*/(0, _jsxRuntime.jsx)(_material.InputAdornment, {
89
- position: "end",
90
- children: /*#__PURE__*/(0, _jsxRuntime.jsx)(StyledCloseIcon, {
91
- "data-cy": "search-delete",
92
- onClick: handleClose
93
- })
94
- })
95
- }, rest));
96
- }
97
-
98
- Search.propTypes = {
99
- placeholder: _propTypes.default.string
100
- };
101
- Search.defaultProps = {
102
- placeholder: 'Type to search...'
103
- };
104
- const StyledSearch = (0, _styled.default)(_material.OutlinedInput)(_templateObject || (_templateObject = _taggedTemplateLiteral(["\n background-color: #fff;\n font-size: 14px;\n border-radius: 6px;\n .MuiInputBase-input {\n padding: 8px 0 8px 10px;\n }\n .MuiOutlinedInput-notchedOutline {\n border: none;\n }\n .Mui-focused {\n background-color: #f6f6f6;\n .MuiInputBase-input::placeholder {\n color: transparent;\n }\n }\n"])));
105
- const StyledSearchIcon = (0, _styled.default)(_Search.default)(_templateObject2 || (_templateObject2 = _taggedTemplateLiteral(["\n color: ", ";\n font-size: 28px;\n @media (max-width: ", "px) {\n font-size: 24px;\n }\n"])), props => props.theme.palette.grey[500], props => props.theme.breakpoints.values.md);
106
- const StyledCloseIcon = (0, _styled.default)(_Close.default)(_templateObject3 || (_templateObject3 = _taggedTemplateLiteral(["\n color: ", ";\n font-size: 16px;\n cursor: pointer;\n"])), props => props.theme.palette.grey[500]);
107
- var _default = Search;
108
- exports.default = _default;
@@ -1,93 +0,0 @@
1
- import { useEffect, useState } from 'react';
2
- import PropTypes from 'prop-types';
3
- import SearchIcon from '@mui/icons-material/Search';
4
- import CloseIcon from '@mui/icons-material/Close';
5
- import { OutlinedInput, InputAdornment } from '@mui/material';
6
- import { useDebounceFn } from 'ahooks';
7
- import styled from '@emotion/styled';
8
-
9
- import { useFilterContext } from '../contexts/filter';
10
-
11
- function Search({ placeholder, ...rest }) {
12
- const { filters, handleKeyword } = useFilterContext();
13
- const [searchStr, setSearchStr] = useState(filters.keyword || '');
14
-
15
- const debouncedSearch = useDebounceFn(handleKeyword, { wait: 300 });
16
- const handleChange = (event) => {
17
- const { value } = event.target;
18
- setSearchStr(value);
19
- debouncedSearch.run(value);
20
- };
21
- const handleClose = () => {
22
- setSearchStr('');
23
- handleKeyword();
24
- };
25
- useEffect(() => {
26
- setSearchStr(filters.keyword || '');
27
- }, [filters.keyword]);
28
-
29
- return (
30
- <StyledSearch
31
- inputProps={{
32
- 'data-cy': 'search-blocklet',
33
- }}
34
- startAdornment={
35
- <InputAdornment position="start">
36
- <StyledSearchIcon />
37
- </InputAdornment>
38
- }
39
- onChange={handleChange}
40
- placeholder={placeholder}
41
- value={searchStr}
42
- title={placeholder}
43
- data-cy="search"
44
- endAdornment={
45
- searchStr && (
46
- <InputAdornment position="end">
47
- <StyledCloseIcon data-cy="search-delete" onClick={handleClose} />
48
- </InputAdornment>
49
- )
50
- }
51
- {...rest}
52
- />
53
- );
54
- }
55
- Search.propTypes = {
56
- placeholder: PropTypes.string,
57
- };
58
- Search.defaultProps = {
59
- placeholder: 'Type to search...',
60
- };
61
- const StyledSearch = styled(OutlinedInput)`
62
- background-color: #fff;
63
- font-size: 14px;
64
- border-radius: 6px;
65
- .MuiInputBase-input {
66
- padding: 8px 0 8px 10px;
67
- }
68
- .MuiOutlinedInput-notchedOutline {
69
- border: none;
70
- }
71
- .Mui-focused {
72
- background-color: #f6f6f6;
73
- .MuiInputBase-input::placeholder {
74
- color: transparent;
75
- }
76
- }
77
- `;
78
-
79
- const StyledSearchIcon = styled(SearchIcon)`
80
- color: ${(props) => props.theme.palette.grey[500]};
81
- font-size: 28px;
82
- @media (max-width: ${(props) => props.theme.breakpoints.values.md}px) {
83
- font-size: 24px;
84
- }
85
- `;
86
-
87
- const StyledCloseIcon = styled(CloseIcon)`
88
- color: ${(props) => props.theme.palette.grey[500]};
89
- font-size: 16px;
90
- cursor: pointer;
91
- `;
92
-
93
- export default Search;