diginet-core-ui 1.3.94-beta.4 → 1.3.94-beta.5

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.
@@ -106,7 +106,7 @@ export { default as ModalFooter } from "./modal/footer";
106
106
 
107
107
  // PAGING
108
108
  export { default as PagingInfo } from "./paging/page-info";
109
- export { default as PagingSelector } from "./paging/page-selector2";
109
+ export { default as PagingSelector } from "./paging/page-selector";
110
110
 
111
111
  // PAPER
112
112
  export { default as Paper } from "./paper";
@@ -1,254 +1,382 @@
1
1
  /** @jsxRuntime classic */
2
2
  /** @jsx jsx */
3
- import React, { memo, useRef, useState, useEffect, forwardRef } from 'react';
3
+ import { css, jsx } from '@emotion/core';
4
+ import { ButtonIcon } from "./..";
5
+ import OptionWrapper from "../others/option-wrapper";
6
+ import { getGlobal } from "../../global";
4
7
  import PropTypes from 'prop-types';
5
- import { jsx, css } from '@emotion/core';
6
- import theme from "../../theme/settings";
7
- import { typography } from "../../styles/typography";
8
+ import { forwardRef, memo, useEffect, useImperativeHandle, useMemo, useRef, useState } from 'react';
9
+ import { bgColor, borderTop, boxBorder, displayFlex, flexRow, itemsCenter, parseWidthHeight, pd } from "../../styles/general";
8
10
  import { useTheme } from "../../theme";
11
+ import { refType as ref, useElementSize } from "../../utils";
9
12
  const {
10
- zIndex
13
+ typography: {
14
+ paragraph2
15
+ }
11
16
  } = useTheme();
17
+ const getLastPage = (totalItems, itemsPerPage) => {
18
+ let _lastPage = 0;
19
+ if (totalItems && totalItems >= 0 && itemsPerPage && itemsPerPage > 0) {
20
+ _lastPage = Math.ceil(totalItems / itemsPerPage);
21
+ return _lastPage;
22
+ }
23
+ return _lastPage + 1;
24
+ };
25
+ const delayOnInput = getGlobal('delayOnInput');
12
26
  const PagingSelector = /*#__PURE__*/memo( /*#__PURE__*/forwardRef(({
13
- size,
14
- style,
15
- pageItemStyle,
16
- currentPage: currentPageProps = 1,
17
- totalItems = 0,
18
- itemsPerPage = 10,
27
+ action = {},
28
+ className,
29
+ currentPage,
30
+ hideEllipsis,
31
+ hideWithPage,
32
+ id,
33
+ itemsPerPage,
19
34
  onChangePage,
20
- ...props
21
- }, ref) => {
22
- if (!ref) {
23
- ref = useRef(null);
24
- }
25
- const [currentPage, setCurrentPage] = useState(currentPageProps);
26
- const [paging, setPaging] = useState([1]);
27
- const PageItemNum = css`
28
- color: #fff;
29
- ${typography.heading3};
30
- z-index: ${zIndex(2)};
31
- `;
32
- const morePaging = css`
33
- position: absolute;
34
- display: inline-flex;
35
- opacity: 0;
36
- transform: scaleX(0);
37
- transition: all 0.5s;
38
- z-index: ${zIndex(3)};
39
- max-width: 176px;
40
- overflow: scroll hidden;
41
- padding-bottom: 2px;
42
- outline: none;
43
- &.visible {
44
- opacity: 1;
45
- transform: scaleX(1);
46
- }
47
- &::-webkit-scrollbar {
48
- height: 4px;
49
- }
50
- &::-webkit-scrollbar-thumb {
51
- border-radius: 10px;
52
- background-color: #c1c1c1;
53
- }
54
- `;
55
- const PageItem = css`
56
- width: ${size === 'small' ? 20 : 24}px;
57
- height: ${size === 'small' ? 20 : 24}px;
58
- font-size: ${size === 'small' ? 12 : 14}px;
59
- background-color: ${theme.colors.system.rest};
60
- border-radius: 50%;
61
- text-align: center;
62
- display: flex;
63
- justify-content: center;
64
- align-items: center;
65
- margin: 0px 4px;
66
- user-select: none;
67
- cursor: pointer;
68
- position: relative;
69
- &:hover {
70
- background-color: ${theme.colors.fill.hover};
71
- &:not(.active) .css-${PageItemNum.name} {
72
- color: ${theme.colors.system.active};
73
- }
74
- }
75
- &.active {
76
- background-color: ${theme.colors.brand};
77
- }
78
- `;
79
- const setPagination = (newCurrentPage = 1) => {
80
- const newLastPage = Math.ceil(totalItems / itemsPerPage);
81
- if (newCurrentPage > newLastPage || newCurrentPage < 0) {
82
- newCurrentPage = 1;
35
+ onChangePerPage,
36
+ onChangingPage,
37
+ onChangingPerPage,
38
+ style,
39
+ totalItems
40
+ }, reference) => {
41
+ const ref = useRef(null);
42
+ const timer = useRef(null);
43
+ const currentPageRef = useRef(currentPage);
44
+ const [totalItemsState, setTotalItemsState] = useState(totalItems);
45
+ const [itemsPerPageState, setItemsPerPageState] = useState(itemsPerPage || 10);
46
+ const [currentPageState, setCurrentPageState] = useState(currentPage);
47
+ const [disablePrevState, setDisablePrevState] = useState(true);
48
+ const [disableNextState, setDisableNextState] = useState(!(totalItems > itemsPerPage));
49
+ const [refSize, {
50
+ width: componentWidth
51
+ }] = useElementSize();
52
+ const lastPage = getLastPage(totalItemsState, itemsPerPageState);
53
+ const _onChangePage = async ({
54
+ page,
55
+ e,
56
+ changeBy
57
+ }) => {
58
+ let disablePrev = false;
59
+ let disableNext = false;
60
+
61
+ // If last page = 1. Disable all
62
+ if (lastPage === 1) {
63
+ disablePrev = true;
64
+ disableNext = true;
83
65
  }
84
- setCurrentPage(newCurrentPage);
85
- if (newLastPage > 1) {
86
- const newPaging = [1];
87
- if (newLastPage > 5) {
88
- if (newCurrentPage < 4) {
89
- newPaging.push(...[2, 3]);
90
- let morePageRight = [];
91
- if (newCurrentPage === 3) {
92
- newPaging.push(4);
93
- for (let r = newCurrentPage + 2; r < newLastPage; r++) {
94
- morePageRight.push(r);
95
- }
96
- } else {
97
- for (let r = 4; r < newLastPage; r++) {
98
- morePageRight.push(r);
99
- }
100
- }
101
- if (morePageRight.length === 1) {
102
- morePageRight = morePageRight[0];
103
- }
104
- newPaging.push(...[morePageRight, newLastPage]);
105
- } else if (newCurrentPage > newLastPage - 3) {
106
- let morePageLeft = [];
107
- if (newCurrentPage - 2 === 2) {
108
- morePageLeft = 2;
109
- } else {
110
- for (let l = 2; l < newCurrentPage - 1 - (newCurrentPage - newLastPage + 1); l++) {
111
- morePageLeft.push(l);
112
- }
113
- }
114
- newPaging.push(morePageLeft);
115
- if (newCurrentPage === newLastPage - 2) {
116
- newPaging.push(newLastPage - 3);
117
- }
118
- newPaging.push(...[newLastPage - 2, newLastPage - 1, newLastPage]);
119
- } else {
120
- let morePageLeft = [],
121
- morePageRight = [];
122
- if (newCurrentPage - 2 === 2) {
123
- morePageLeft = 2;
124
- } else {
125
- for (let l = 2; l < newCurrentPage - 1; l++) {
126
- morePageLeft.push(l);
127
- }
128
- }
129
- if (newLastPage - 1 === newCurrentPage + 2) {
130
- morePageRight = newCurrentPage + 2;
131
- } else {
132
- for (let r = newCurrentPage + 2; r < newLastPage; r++) {
133
- morePageRight.push(r);
134
- }
135
- }
136
- newPaging.push(...[morePageLeft, newCurrentPage - 1, newCurrentPage, newCurrentPage + 1, morePageRight, newLastPage]);
137
- }
138
- } else {
139
- for (let i = 2; i <= newLastPage; i++) {
140
- newPaging.push(i);
141
- }
142
- }
143
- return newPaging;
144
- } else if (paging.length !== 1) {
145
- return [1];
66
+
67
+ // If current page = last page. Disable Next and Last
68
+ if (page + 1 >= lastPage) {
69
+ disableNext = true;
146
70
  }
147
- };
148
- const onChangePageHandler = (e, pageNum) => {
149
- if (!e.currentTarget.classList.contains('active')) {
150
- if (isNaN(pageNum)) {
151
- const el = e.currentTarget.querySelector(`.css-${morePaging.name}`);
152
- if (!el.classList.contains('visible')) {
153
- el.classList.add('visible');
154
- }
155
- el.setAttribute('tabIndex', -1);
156
- el.focus();
157
- } else {
158
- setCurrentPage(pageNum);
159
- setPaging(setPagination(pageNum));
160
- // paginationRef.current.querySelector('.active').classList.remove('active');
161
- if (onChangePage) onChangePage(pageNum);
71
+
72
+ // If current page = 0. Disable Previous and First
73
+ if (page === 0) {
74
+ disablePrev = true;
75
+ }
76
+ setDisablePrevState(disablePrev);
77
+ setDisableNextState(disableNext);
78
+ if (page === currentPageState) return;
79
+ if (onChangingPage) {
80
+ const event = {
81
+ ...e,
82
+ cancel: false,
83
+ prevPage: currentPageState,
84
+ newPage: page,
85
+ changeBy: changeBy
86
+ };
87
+ await onChangingPage(event);
88
+ if (event.cancel) {
89
+ if (e !== null && e !== void 0 && e.reset) e.reset();
90
+ currentPageRef.current = currentPageState;
91
+ setCurrentPageState(currentPageState);
92
+ return;
162
93
  }
163
94
  }
95
+ currentPageRef.current = page;
96
+ setCurrentPageState(page);
97
+ if (onChangePage) onChangePage(page);
164
98
  };
165
- const closeMorePaging = e => {
166
- if (e.currentTarget.classList.contains('visible')) {
167
- e.currentTarget.classList.remove('visible');
168
- e.currentTarget.blur();
99
+ const _onChangePerPage = async per => {
100
+ if (per === itemsPerPageState) return;
101
+ const lastPage = getLastPage(totalItemsState, per);
102
+ per = parseInt(per, 10);
103
+ let disableNext = false;
104
+
105
+ // If last page = 1. Disable all
106
+ if (lastPage === 1) {
107
+ disableNext = true;
108
+ }
109
+ if (onChangingPerPage) {
110
+ const event = {
111
+ cancel: false,
112
+ prevPerPage: itemsPerPageState,
113
+ newPerPage: per
114
+ };
115
+ await onChangingPerPage(event);
116
+ if (event.cancel) {
117
+ return;
118
+ }
169
119
  }
120
+ setItemsPerPageState(per);
121
+ // currentPageRef.current = 0;
122
+ // setCurrentPageState(0);
123
+ _onChangePage({
124
+ page: 0
125
+ });
126
+ setDisablePrevState(true);
127
+ setDisableNextState(disableNext);
128
+ if (onChangePerPage) onChangePerPage(per);
170
129
  };
171
-
172
- // Get totalItems, itemsPerPage to set last page
173
130
  useEffect(() => {
174
- setPaging(setPagination(currentPageProps));
175
- }, [currentPageProps]);
131
+ setTotalItemsState(totalItems);
132
+ }, [totalItems]);
176
133
  useEffect(() => {
177
- setPaging(setPagination(currentPage));
178
- }, [totalItems, itemsPerPage]);
179
- const RenderPageItem = /*#__PURE__*/memo(({
180
- pageNum,
181
- style
182
- }) => {
183
- return jsx("div", {
184
- css: PageItem,
185
- className: pageNum === currentPage ? 'active' : '',
186
- style: style,
187
- onClick: e => onChangePageHandler(e, pageNum)
188
- }, jsx("div", {
189
- css: PageItemNum
190
- }, isNaN(pageNum) ? jsx(React.Fragment, null, jsx("div", {
191
- css: morePaging,
192
- onBlur: closeMorePaging,
193
- style: {
194
- top: '-100%',
195
- ...(pageNum[0] > currentPage ? {
196
- right: 3,
197
- transformOrigin: 'right'
198
- } : {
199
- left: 1,
200
- transformOrigin: 'left'
201
- })
202
- }
203
- }, pageNum.map(page => jsx("div", {
204
- key: page,
205
- css: PageItem,
134
+ _onChangePerPage(itemsPerPage);
135
+ }, [itemsPerPage]);
136
+ useEffect(() => {
137
+ _onChangePage({
138
+ page: currentPage,
139
+ changeBy: 'currentPage'
140
+ });
141
+ }, [currentPage]);
142
+ useEffect(() => {
143
+ _onChangePage({
144
+ page: totalItemsState / itemsPerPageState < currentPageState - 1 ? 0 : currentPageState,
145
+ changeBy: 'totalItems'
146
+ });
147
+ }, [totalItemsState]);
148
+ useEffect(() => {
149
+ _onChangePage({
150
+ page: totalItemsState / itemsPerPageState < currentPageState - 1 ? 0 : currentPageState,
151
+ changeBy: 'itemsPerPage'
152
+ });
153
+ }, [itemsPerPageState]);
154
+ useImperativeHandle(reference, () => {
155
+ const currentRef = ref.current || {};
156
+ currentRef.element = ref.current;
157
+ const _instance = {
158
+ onChangePage: page => _onChangePage({
159
+ page: page,
160
+ changeBy: 'ref'
161
+ }),
162
+ onChangePerPage: per => _onChangePerPage(per),
163
+ setTotalItems: items => setTotalItemsState(items),
164
+ ...action
165
+ }; // methods
166
+ _instance.__proto__ = {}; // hidden methods
167
+ currentRef.instance = _instance;
168
+ currentRef.onChangePage = page => _onChangePage({
169
+ page: page,
170
+ changeBy: 'ref'
171
+ });
172
+ currentRef.onChangePerPage = per => _onChangePerPage(per);
173
+ currentRef.setTotalItems = items => setTotalItemsState(items);
174
+ return currentRef;
175
+ });
176
+ const renderButton = (key, page, isSelected, customDisplay) => {
177
+ if (page >= lastPage || page < 0) return;
178
+ return jsx(ButtonIcon, {
179
+ key: key,
180
+ css: paragraph2,
181
+ circular: true,
182
+ viewType: isSelected ? 'filled' : 'text',
183
+ size: 'small',
184
+ color: 'primary',
206
185
  style: {
207
- width: 18,
208
- height: 18,
209
- minWidth: 18,
210
- minHeight: 18,
211
- fontSize: 8,
212
- margin: '0px 2px'
186
+ fontWeight: 'normal'
213
187
  },
214
- onClick: e => onChangePageHandler(e, page)
215
- }, jsx("span", {
216
- css: PageItemNum
217
- }, page)))), jsx("p", null, "...")) : pageNum));
218
- });
219
- return jsx("div", {
220
- ...props,
221
- ref: ref,
222
- style: {
223
- display: 'inline-flex',
224
- ...style
188
+ onClick: e => page !== currentPageState && _onChangePage({
189
+ page: page,
190
+ e: e
191
+ })
192
+ }, customDisplay || page + 1);
193
+ };
194
+ const checkShowSelector = () => {
195
+ switch (hideWithPage) {
196
+ case 'empty':
197
+ return totalItemsState !== 0;
198
+ case 'single':
199
+ return totalItemsState / itemsPerPageState > 1;
200
+ case 'none':
201
+ default:
202
+ return true;
225
203
  }
226
- }, paging.map((pageNum, index) => jsx(RenderPageItem, {
227
- key: index,
228
- pageNum: pageNum,
229
- style: pageItemStyle
230
- })));
204
+ };
205
+ return useMemo(() => {
206
+ const count = componentWidth >= 368 ? 10 : componentWidth >= 208 ? 5 : 1;
207
+ const pageArr = [...Array(count).keys()];
208
+ const halfArrLength = Math.floor(count / 2);
209
+ const quarterArrLength = Math.floor(count / 2.5);
210
+ const showEllipsis = !hideEllipsis && count !== lastPage && count >= 5;
211
+ const showSelector = checkShowSelector();
212
+ return showSelector && jsx("div", {
213
+ css: PageSelectorCSS,
214
+ ref: node => {
215
+ ref.current = node;
216
+ refSize(node);
217
+ },
218
+ id: id,
219
+ style: style,
220
+ className: ['DGN-UI-PagingSelector', className].join(' ').trim().replace(/\s+/g, ' ')
221
+ }, jsx(ButtonIcon, {
222
+ circular: true,
223
+ viewType: 'text',
224
+ size: 'small',
225
+ name: 'ArrowLeft',
226
+ disabled: disablePrevState,
227
+ onClick: e => {
228
+ currentPageRef.current = currentPageRef.current === 0 ? 0 : currentPageRef.current - 1;
229
+ if (timer.current) {
230
+ clearTimeout(timer.current);
231
+ }
232
+ timer.current = setTimeout(() => {
233
+ _onChangePage({
234
+ page: currentPageRef.current,
235
+ e: e
236
+ });
237
+ }, delayOnInput);
238
+ }
239
+ }), pageArr.map((_, idx) => {
240
+ if (currentPageState < halfArrLength) {
241
+ // các trang đầu
242
+ if (showEllipsis) {
243
+ if (idx === count - 1 && lastPage - 1 >= 10) {
244
+ return renderButton(idx, lastPage - 1);
245
+ } else {
246
+ return renderButton(idx, idx, idx === currentPageState, idx === count - 2 && '...');
247
+ }
248
+ } else return renderButton(idx, idx, idx === currentPageState);
249
+ } else if (currentPageState >= lastPage - halfArrLength) {
250
+ // các trang cuối
251
+ if (showEllipsis) {
252
+ if (idx === 0) {
253
+ return renderButton(idx, idx);
254
+ } else {
255
+ return renderButton(idx, lastPage - count + idx, lastPage - count + idx === currentPageState, idx === 1 && '...');
256
+ }
257
+ } else {
258
+ return renderButton(idx, lastPage - count + idx, lastPage - count + idx === currentPageState);
259
+ }
260
+ } else {
261
+ // các trang giữa
262
+ if (idx < quarterArrLength) {
263
+ if (showEllipsis) {
264
+ if (idx === 0) {
265
+ return renderButton(idx, idx);
266
+ } else {
267
+ return renderButton(idx, currentPageState - quarterArrLength + idx, false, idx === 1 && '...');
268
+ }
269
+ } else return renderButton(idx, currentPageState - quarterArrLength + idx);
270
+ } else if (idx === quarterArrLength) {
271
+ return renderButton(idx, currentPageState, true);
272
+ } else {
273
+ if (showEllipsis) {
274
+ if (idx === count - 1) {
275
+ return renderButton(idx, lastPage - 1);
276
+ } else {
277
+ return renderButton(idx, currentPageState - quarterArrLength + idx, false, idx === count - 2 && '...');
278
+ }
279
+ } else return renderButton(idx, currentPageState - quarterArrLength + idx);
280
+ }
281
+ }
282
+ }), jsx(ButtonIcon, {
283
+ circular: true,
284
+ viewType: 'text',
285
+ size: 'small',
286
+ name: 'ArrowRight',
287
+ disabled: disableNextState,
288
+ onClick: e => {
289
+ currentPageRef.current = currentPageRef.current === lastPage ? lastPage : currentPageRef.current + 1;
290
+ if (timer.current) {
291
+ clearTimeout(timer.current);
292
+ }
293
+ timer.current = setTimeout(() => {
294
+ _onChangePage({
295
+ page: currentPageRef.current,
296
+ e: e
297
+ });
298
+ }, delayOnInput);
299
+ }
300
+ }));
301
+ }, [className, hideEllipsis, hideWithPage, id, style, componentWidth, currentPageState, disableNextState, disablePrevState, itemsPerPageState, totalItemsState]);
231
302
  }));
303
+ const PageSelectorCSS = css`
304
+ ${displayFlex};
305
+ ${flexRow};
306
+ ${itemsCenter};
307
+ ${boxBorder};
308
+ ${parseWidthHeight('100%', 40)};
309
+ ${pd([1])};
310
+ ${bgColor('system/white')};
311
+ ${borderTop(1, 'line/system')};
312
+ `;
232
313
  PagingSelector.defaultProps = {
233
- size: 'medium',
234
- currentPage: 1,
235
- totalItems: 0,
236
- itemsPerPage: 10
314
+ className: '',
315
+ currentPage: 0,
316
+ hideEllipsis: false,
317
+ hideWithPage: 'none',
318
+ style: {},
319
+ totalItems: 0
237
320
  };
238
321
  PagingSelector.propTypes = {
239
- /** size of paging */
240
- size: PropTypes.oneOf(['small', 'medium']),
241
- /** current of paging */
322
+ /** Class for component. */
323
+ className: PropTypes.string,
324
+ /** Specifies the current page. */
242
325
  currentPage: PropTypes.number,
243
- /** total items of paging */
244
- totalItems: PropTypes.number.isRequired,
245
- /** the quantity of items on per page */
326
+ /** If `true`, hide ellipsis. */
327
+ hideEllipsis: PropTypes.bool,
328
+ /** Hidden mode for page selector. */
329
+ hideWithPage: PropTypes.oneOf(['none', 'empty', 'single']),
330
+ /** The quantity of items per page. */
246
331
  itemsPerPage: PropTypes.number,
247
- /** style inline for PagingSelector */
332
+ /** Callback fired when page number changed. */
333
+ onChangePage: PropTypes.func,
334
+ /** Callback fired when quantity on per page changed. */
335
+ onChangePerPage: PropTypes.func,
336
+ /**
337
+ * Callback fired when page number is changing.
338
+ *
339
+ * * prevPage: Page before changed
340
+ * * newPage: Page after changed
341
+ * * cancel(value): Function cancel change page
342
+ * * @param {value} - bool
343
+ */
344
+ onChangingPage: PropTypes.func,
345
+ /**
346
+ * Callback fired when quantity on item per page is changing.
347
+ *
348
+ * * prevPerPage: Items per page before changed
349
+ * * newPerPage: Items per page after changed
350
+ * * cancel(value): Function cancel change items per page
351
+ * * @param {value} - bool
352
+ */
353
+ onChangingPerPage: PropTypes.func,
354
+ /** Style inline of component. */
248
355
  style: PropTypes.object,
249
- /** style inline for page number element */
250
- pageItemStyle: PropTypes.object,
251
- /** the function will run after change page number */
252
- onChangePage: PropTypes.func
356
+ /** Total items. */
357
+ totalItems: PropTypes.number,
358
+ /**
359
+ * ref methods (ref.current.instance.*method*)
360
+ *
361
+ * * onChangePage(page): Change page number
362
+ * * @param {page} - number
363
+ * * onChangePerPage(per): Change quantity on per page
364
+ * * @param {per} - number
365
+ * * setTotalItems(items): Set total items
366
+ * * @param {items} - number
367
+ *
368
+ * * option(): Gets all UI component properties
369
+ * * Returns value - object
370
+ * * option(optionName): Gets the value of a single property
371
+ * * @param {optionName} - string
372
+ * * Returns value - any
373
+ * * option(optionName, optionValue): Updates the value of a single property
374
+ * * @param {optionName} - string
375
+ * * @param {optionValue} - any
376
+ * * option(options): Updates the values of several properties
377
+ * * @param {options} - object
378
+ */
379
+ ref: ref
253
380
  };
254
- export default PagingSelector;
381
+ export { PagingSelector };
382
+ export default OptionWrapper(PagingSelector);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "diginet-core-ui",
3
- "version": "1.3.94-beta.4",
3
+ "version": "1.3.94-beta.5",
4
4
  "description": "",
5
5
  "main": "index.js",
6
6
  "license": "UNLICENSED",
@@ -1,384 +0,0 @@
1
- /** @jsxRuntime classic */
2
- /** @jsx jsx */
3
- import { css, jsx } from '@emotion/core';
4
- import { ButtonIcon } from "./..";
5
- import OptionWrapper from "../others/option-wrapper";
6
- import { getGlobal } from "../../global";
7
- import PropTypes from 'prop-types';
8
- import { forwardRef, memo, useEffect, useImperativeHandle, useMemo, useRef, useState } from 'react';
9
- import { boxBorder, flexRow, itemsCenter, parseHeight } from "../../styles/general";
10
- import { useTheme } from "../../theme";
11
- import { refType as ref } from "../../utils";
12
- import { useElementSize } from "../../utils/useElementSize";
13
- const {
14
- colors: {
15
- system: {
16
- white
17
- },
18
- line: {
19
- system
20
- }
21
- },
22
- typography: {
23
- paragraph2
24
- },
25
- spacing
26
- } = useTheme();
27
- const getLastPage = (totalItems, itemsPerPage) => {
28
- let _lastPage = 0;
29
- if (totalItems && totalItems >= 0 && itemsPerPage && itemsPerPage > 0) {
30
- _lastPage = Math.ceil(totalItems / itemsPerPage);
31
- return _lastPage;
32
- }
33
- return _lastPage + 1;
34
- };
35
- const delayOnInput = getGlobal('delayOnInput');
36
- const PagingSelector = /*#__PURE__*/memo( /*#__PURE__*/forwardRef(({
37
- action = {},
38
- className,
39
- currentPage,
40
- hideEllipsis,
41
- hideWithPage,
42
- id,
43
- itemsPerPage,
44
- onChangePage,
45
- onChangePerPage,
46
- onChangingPage,
47
- onChangingPerPage,
48
- style,
49
- totalItems
50
- }, reference) => {
51
- const ref = useRef(null);
52
- const timer = useRef(null);
53
- const currentPageRef = useRef(currentPage);
54
- const [totalItemsState, setTotalItemsState] = useState(totalItems);
55
- const [itemsPerPageState, setItemsPerPageState] = useState(itemsPerPage || 10);
56
- const [currentPageState, setCurrentPageState] = useState(currentPage);
57
- const [disablePrevState, setDisablePrevState] = useState(true);
58
- const [disableNextState, setDisableNextState] = useState(!(totalItems > itemsPerPage));
59
- const [refSize, {
60
- width: componentWidth
61
- }] = useElementSize();
62
- const lastPage = getLastPage(totalItemsState, itemsPerPageState);
63
- const _onChangePage = async ({
64
- page,
65
- e,
66
- changeBy
67
- }) => {
68
- let disablePrev = false;
69
- let disableNext = false;
70
-
71
- // If last page = 1. Disable all
72
- if (lastPage === 1) {
73
- disablePrev = true;
74
- disableNext = true;
75
- }
76
-
77
- // If current page = last page. Disable Next and Last
78
- if (page + 1 >= lastPage) {
79
- disableNext = true;
80
- }
81
-
82
- // If current page = 0. Disable Previous and First
83
- if (page === 0) {
84
- disablePrev = true;
85
- }
86
- setDisablePrevState(disablePrev);
87
- setDisableNextState(disableNext);
88
- if (page === currentPageState) return;
89
- if (onChangingPage) {
90
- const event = {
91
- ...e,
92
- cancel: false,
93
- prevPage: currentPageState,
94
- newPage: page,
95
- changeBy: changeBy
96
- };
97
- await onChangingPage(event);
98
- if (event.cancel) {
99
- if (e !== null && e !== void 0 && e.reset) e.reset();
100
- currentPageRef.current = currentPageState;
101
- setCurrentPageState(currentPageState);
102
- return;
103
- }
104
- }
105
- currentPageRef.current = page;
106
- setCurrentPageState(page);
107
- if (onChangePage) onChangePage(page);
108
- };
109
- const _onChangePerPage = async per => {
110
- if (per === itemsPerPageState) return;
111
- const lastPage = getLastPage(totalItemsState, per);
112
- per = parseInt(per, 10);
113
- let disableNext = false;
114
-
115
- // If last page = 1. Disable all
116
- if (lastPage === 1) {
117
- disableNext = true;
118
- }
119
- if (onChangingPerPage) {
120
- const event = {
121
- cancel: false,
122
- prevPerPage: itemsPerPageState,
123
- newPerPage: per
124
- };
125
- await onChangingPerPage(event);
126
- if (event.cancel) {
127
- return;
128
- }
129
- }
130
- setItemsPerPageState(per);
131
- // currentPageRef.current = 0;
132
- // setCurrentPageState(0);
133
- _onChangePage({
134
- page: 0
135
- });
136
- setDisablePrevState(true);
137
- setDisableNextState(disableNext);
138
- if (onChangePerPage) onChangePerPage(per);
139
- };
140
- useEffect(() => {
141
- setTotalItemsState(totalItems);
142
- }, [totalItems]);
143
- useEffect(() => {
144
- _onChangePerPage(itemsPerPage);
145
- }, [itemsPerPage]);
146
- useEffect(() => {
147
- _onChangePage({
148
- page: currentPage,
149
- changeBy: 'currentPage'
150
- });
151
- }, [currentPage]);
152
- useEffect(() => {
153
- _onChangePage({
154
- page: totalItemsState / itemsPerPageState < currentPageState - 1 ? 0 : currentPageState,
155
- changeBy: 'totalItems'
156
- });
157
- }, [totalItemsState]);
158
- useEffect(() => {
159
- _onChangePage({
160
- page: totalItemsState / itemsPerPageState < currentPageState - 1 ? 0 : currentPageState,
161
- changeBy: 'itemsPerPage'
162
- });
163
- }, [itemsPerPageState]);
164
- useImperativeHandle(reference, () => {
165
- const currentRef = ref.current || {};
166
- currentRef.element = ref.current;
167
- const _instance = {
168
- onChangePage: page => _onChangePage({
169
- page: page,
170
- changeBy: 'ref'
171
- }),
172
- onChangePerPage: per => _onChangePerPage(per),
173
- setTotalItems: items => setTotalItemsState(items),
174
- ...action
175
- }; // methods
176
- _instance.__proto__ = {}; // hidden methods
177
- currentRef.instance = _instance;
178
- currentRef.onChangePage = page => _onChangePage({
179
- page: page,
180
- changeBy: 'ref'
181
- });
182
- currentRef.onChangePerPage = per => _onChangePerPage(per);
183
- currentRef.setTotalItems = items => setTotalItemsState(items);
184
- return currentRef;
185
- });
186
- const renderButton = (key, page, isSelected, customDisplay) => {
187
- if (page >= lastPage || page < 0) return;
188
- return jsx(ButtonIcon, {
189
- key: key,
190
- css: ButtonIconCSS,
191
- circular: true,
192
- viewType: isSelected ? 'filled' : 'text',
193
- size: 'small',
194
- color: 'primary',
195
- style: {
196
- fontWeight: 'normal'
197
- },
198
- onClick: e => page !== currentPageState && _onChangePage({
199
- page: page,
200
- e: e
201
- })
202
- }, customDisplay || page + 1);
203
- };
204
- const checkShowSelector = () => {
205
- switch (hideWithPage) {
206
- case 'empty':
207
- return totalItemsState !== 0;
208
- case 'single':
209
- return totalItemsState / itemsPerPageState > 1;
210
- case 'none':
211
- default:
212
- return true;
213
- }
214
- };
215
- return useMemo(() => {
216
- const count = componentWidth >= 368 ? 10 : componentWidth >= 208 ? 5 : 1;
217
- const pageArr = [...Array(count).keys()];
218
- const halfArrLength = Math.floor(count / 2);
219
- const quarterArrLength = Math.floor(count / 2.5);
220
- const showEllipsis = !hideEllipsis && count !== lastPage && count >= 5;
221
- const showSelector = checkShowSelector();
222
- return showSelector && jsx("div", {
223
- css: PageSelectorCSS,
224
- ref: node => {
225
- ref.current = node;
226
- refSize(node);
227
- },
228
- id: id,
229
- style: style,
230
- className: ['DGN-UI-PagingSelector', className].join(' ').trim().replace(/\s+/g, ' ')
231
- }, jsx(ButtonIcon, {
232
- circular: true,
233
- viewType: 'text',
234
- size: 'small',
235
- name: 'ArrowLeft',
236
- disabled: disablePrevState,
237
- onClick: e => {
238
- currentPageRef.current = currentPageRef.current === 0 ? 0 : currentPageRef.current - 1;
239
- if (timer.current) {
240
- clearTimeout(timer.current);
241
- }
242
- timer.current = setTimeout(() => {
243
- _onChangePage({
244
- page: currentPageRef.current,
245
- e: e
246
- });
247
- }, delayOnInput);
248
- }
249
- }), pageArr.map((_, idx) => {
250
- if (currentPageState < halfArrLength) {
251
- // các trang đầu
252
- if (showEllipsis) {
253
- if (idx === count - 1 && lastPage - 1 >= 10) {
254
- return renderButton(idx, lastPage - 1);
255
- } else {
256
- return renderButton(idx, idx, idx === currentPageState, idx === count - 2 && '...');
257
- }
258
- } else return renderButton(idx, idx, idx === currentPageState);
259
- } else if (currentPageState >= lastPage - halfArrLength) {
260
- // các trang cuối
261
- if (showEllipsis) {
262
- if (idx === 0) {
263
- return renderButton(idx, idx);
264
- } else {
265
- return renderButton(idx, lastPage - count + idx, lastPage - count + idx === currentPageState, idx === 1 && '...');
266
- }
267
- } else {
268
- return renderButton(idx, lastPage - count + idx, lastPage - count + idx === currentPageState);
269
- }
270
- } else {
271
- // các trang giữa
272
- if (idx < quarterArrLength) {
273
- if (showEllipsis) {
274
- if (idx === 0) {
275
- return renderButton(idx, idx);
276
- } else {
277
- return renderButton(idx, currentPageState - quarterArrLength + idx, false, idx === 1 && '...');
278
- }
279
- } else return renderButton(idx, currentPageState - quarterArrLength + idx);
280
- } else if (idx === quarterArrLength) {
281
- return renderButton(idx, currentPageState, true);
282
- } else {
283
- if (showEllipsis) {
284
- if (idx === count - 1) {
285
- return renderButton(idx, lastPage - 1);
286
- } else {
287
- return renderButton(idx, currentPageState - quarterArrLength + idx, false, idx === count - 2 && '...');
288
- }
289
- } else return renderButton(idx, currentPageState - quarterArrLength + idx);
290
- }
291
- }
292
- }), jsx(ButtonIcon, {
293
- circular: true,
294
- viewType: 'text',
295
- size: 'small',
296
- name: 'ArrowRight',
297
- disabled: disableNextState,
298
- onClick: e => {
299
- currentPageRef.current = currentPageRef.current === lastPage ? lastPage : currentPageRef.current + 1;
300
- if (timer.current) {
301
- clearTimeout(timer.current);
302
- }
303
- timer.current = setTimeout(() => {
304
- _onChangePage({
305
- page: currentPageRef.current,
306
- e: e
307
- });
308
- }, delayOnInput);
309
- }
310
- }));
311
- }, [id, style, className, hideEllipsis, hideWithPage, totalItemsState, itemsPerPageState, currentPageState, disablePrevState, disableNextState, componentWidth]);
312
- }));
313
- const PageSelectorCSS = css`
314
- ${flexRow};
315
- ${itemsCenter};
316
- ${boxBorder};
317
- ${parseHeight(40)};
318
- padding: ${spacing([1])};
319
- background: ${white};
320
- width: 100%;
321
- border-top: 1px solid ${system};
322
- `;
323
- const ButtonIconCSS = css`
324
- ${paragraph2};
325
- `;
326
- PagingSelector.defaultProps = {
327
- style: {},
328
- className: '',
329
- totalItems: 0,
330
- currentPage: 0,
331
- hideEllipsis: false,
332
- hideWithPage: 'none'
333
- };
334
- PagingSelector.propTypes = {
335
- /** Class for component. */
336
- className: PropTypes.string,
337
- /** Specifies the current page. */
338
- currentPage: PropTypes.number,
339
- /** If `true`, hide ellipsis. */
340
- hideEllipsis: PropTypes.bool,
341
- /** Hidden mode for page selector. */
342
- hideWithPage: PropTypes.oneOf(['none', 'empty', 'single']),
343
- /** The quantity of items per page. */
344
- itemsPerPage: PropTypes.number,
345
- /** Callback fired when page number changed. */
346
- onChangePage: PropTypes.func,
347
- /** Callback fired when quantity on per page changed. */
348
- onChangePerPage: PropTypes.func,
349
- /**
350
- * Callback fired when page number is changing.
351
- *
352
- * * prevPage: Page before changed
353
- * * newPage: Page after changed
354
- * * cancel(value): Function cancel change page
355
- * * @param {value} - bool
356
- */
357
- onChangingPage: PropTypes.func,
358
- /**
359
- * Callback fired when quantity on item per page is changing.
360
- *
361
- * * prevPerPage: Items per page before changed
362
- * * newPerPage: Items per page after changed
363
- * * cancel(value): Function cancel change items per page
364
- * * @param {value} - bool
365
- */
366
- onChangingPerPage: PropTypes.func,
367
- /** Style inline of component. */
368
- style: PropTypes.object,
369
- /** Total items. */
370
- totalItems: PropTypes.number,
371
- /**
372
- * ref methods (ref.current.instance.*method*)
373
- *
374
- * * onChangePage(page): Change page number
375
- * * @param {page} - number
376
- * * onChangePerPage(per): Change quantity on per page
377
- * * @param {per} - number
378
- * * setTotalItems(items): Set total items
379
- * * @param {items} - number
380
- */
381
- ref: ref
382
- };
383
- export { PagingSelector };
384
- export default OptionWrapper(PagingSelector);