@devtron-labs/devtron-fe-common-lib 0.0.55-beta-2 → 0.0.55-beta-4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.eslintignore +96 -0
- package/.eslintrc.js +84 -16
- package/.github/pull_request_template.md +25 -0
- package/.github/workflows/ci.yml +31 -0
- package/.github/workflows/release-package.yml +2 -3
- package/.husky/pre-commit +5 -0
- package/.lintstagedrc +3 -0
- package/.nvmrc +1 -0
- package/dist/Common/Common.service.d.ts +2 -2
- package/dist/Common/Constants.d.ts +1 -0
- package/dist/Common/DebouncedSearch/DebouncedSearch.d.ts +2 -0
- package/dist/Common/DraggableWrapper/DraggableButton.d.ts +3 -0
- package/dist/Common/DraggableWrapper/DraggableWrapper.d.ts +9 -0
- package/dist/Common/DraggableWrapper/index.d.ts +3 -0
- package/dist/Common/DraggableWrapper/types.d.ts +23 -0
- package/dist/Common/Grid/Grid.d.ts +1 -1
- package/dist/Common/Grid/types.d.ts +2 -2
- package/dist/Common/Helper.d.ts +5 -4
- package/dist/Common/Hooks/UseWindowSize/UseWindowSize.d.ts +4 -0
- package/dist/Common/Hooks/index.d.ts +1 -0
- package/dist/Common/Pagination/PageSizeSelector.d.ts +4 -0
- package/dist/Common/Pagination/Pagination.d.ts +5 -0
- package/dist/Common/Pagination/constants.d.ts +5 -0
- package/dist/Common/Pagination/index.d.ts +1 -0
- package/dist/Common/Pagination/types.d.ts +51 -0
- package/dist/Common/Pagination/utils.d.ts +6 -0
- package/dist/Common/SearchBar/SearchBar.component.d.ts +39 -0
- package/dist/Common/SearchBar/index.d.ts +2 -0
- package/dist/Common/SearchBar/types.d.ts +35 -0
- package/dist/Common/Types.d.ts +16 -2
- package/dist/Common/index.d.ts +3 -0
- package/dist/index.js +594 -233
- package/package.json +21 -3
- package/src/Assets/Icon/ic-drag.svg +8 -0
- package/src/Assets/Icon/ic-search.svg +6 -0
- package/src/Common/Constants.ts +3 -1
- package/src/Common/DebouncedSearch/DebouncedSearch.tsx +2 -0
- package/src/Common/DraggableWrapper/DraggableButton.tsx +14 -0
- package/src/Common/DraggableWrapper/DraggableWrapper.tsx +116 -0
- package/src/Common/DraggableWrapper/index.ts +3 -0
- package/src/Common/DraggableWrapper/types.ts +27 -0
- package/src/Common/Grid/Grid.tsx +7 -2
- package/src/Common/Grid/types.ts +3 -1
- package/src/Common/Helper.tsx +31 -23
- package/src/Common/Hooks/UseWindowSize/UseWindowSize.tsx +31 -0
- package/src/Common/Hooks/index.ts +1 -0
- package/src/Common/Pagination/PageSizeSelector.tsx +78 -0
- package/src/Common/Pagination/Pagination.tsx +127 -0
- package/src/Common/Pagination/constants.ts +6 -0
- package/src/Common/Pagination/index.tsx +3 -0
- package/src/Common/Pagination/pagination.scss +123 -0
- package/src/Common/Pagination/types.ts +55 -0
- package/src/Common/Pagination/utils.tsx +24 -0
- package/src/Common/SearchBar/SearchBar.component.tsx +120 -0
- package/src/Common/SearchBar/index.ts +2 -0
- package/src/Common/SearchBar/searchBar.scss +31 -0
- package/src/Common/SearchBar/types.ts +36 -0
- package/src/Common/Types.ts +16 -2
- package/src/Common/index.ts +3 -0
package/package.json
CHANGED
|
@@ -1,12 +1,16 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@devtron-labs/devtron-fe-common-lib",
|
|
3
|
-
"version": "0.0.55-beta-
|
|
3
|
+
"version": "0.0.55-beta-4",
|
|
4
4
|
"description": "Supporting common component library",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"scripts": {
|
|
7
|
+
"prepare": "node -e \"try { require('husky').install() } catch (e) {}\"",
|
|
8
|
+
"lint": "tsc --noEmit && eslint 'src/**/*.{js,jsx,ts,tsx}' --max-warnings 0",
|
|
9
|
+
"lint-fix": "eslint 'src/**/*.{js,jsx,ts,tsx}' --fix",
|
|
7
10
|
"test": "exit 0",
|
|
8
11
|
"build-lib": "rollup -c --bundleConfigAsCjs",
|
|
9
|
-
"build-watch": "rollup -c --bundleConfigAsCjs -w"
|
|
12
|
+
"build-watch": "rollup -c --bundleConfigAsCjs -w",
|
|
13
|
+
"lint-staged": "lint-staged"
|
|
10
14
|
},
|
|
11
15
|
"repository": {
|
|
12
16
|
"type": "git",
|
|
@@ -30,7 +34,20 @@
|
|
|
30
34
|
"@types/react": "17.0.39",
|
|
31
35
|
"@types/react-dom": "17.0.13",
|
|
32
36
|
"@types/react-router-dom": "^5.3.3",
|
|
37
|
+
"@typescript-eslint/eslint-plugin": "^6.17.0",
|
|
38
|
+
"@typescript-eslint/parser": "^6.17.0",
|
|
39
|
+
"eslint": "^8.56.0",
|
|
40
|
+
"eslint-config-airbnb": "^19.0.4",
|
|
41
|
+
"eslint-config-prettier": "^9.1.0",
|
|
42
|
+
"eslint-plugin-import": "^2.29.1",
|
|
43
|
+
"eslint-plugin-jsx-a11y": "^6.8.0",
|
|
44
|
+
"eslint-plugin-prettier": "^5.1.2",
|
|
45
|
+
"eslint-plugin-react": "^7.33.2",
|
|
46
|
+
"eslint-plugin-react-hooks": "^4.6.0",
|
|
47
|
+
"husky": "^7.0.4",
|
|
48
|
+
"lint-staged": "^12.5.0",
|
|
33
49
|
"moment": "^2.29.4",
|
|
50
|
+
"prettier": "^3.1.1",
|
|
34
51
|
"react-select": "5.2.2",
|
|
35
52
|
"react-toastify": "^8.2.0",
|
|
36
53
|
"rollup": "^3.10.0",
|
|
@@ -47,7 +64,8 @@
|
|
|
47
64
|
"react": "^17.0.2",
|
|
48
65
|
"react-dom": "^17.0.2",
|
|
49
66
|
"react-router": "^5.3.0",
|
|
50
|
-
"react-router-dom": "^5.3.0"
|
|
67
|
+
"react-router-dom": "^5.3.0",
|
|
68
|
+
"react-draggable": "^4.4.5"
|
|
51
69
|
},
|
|
52
70
|
"dependencies": {
|
|
53
71
|
"fast-json-patch": "^3.1.1",
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
|
|
2
|
+
<path d="M10.3335 9.33325C10.8858 9.33325 11.3335 8.88554 11.3335 8.33325C11.3335 7.78097 10.8858 7.33325 10.3335 7.33325C9.78121 7.33325 9.3335 7.78097 9.3335 8.33325C9.3335 8.88554 9.78121 9.33325 10.3335 9.33325Z" fill="#596168"/>
|
|
3
|
+
<path d="M5.66675 9.33325C6.21903 9.33325 6.66675 8.88554 6.66675 8.33325C6.66675 7.78097 6.21903 7.33325 5.66675 7.33325C5.11446 7.33325 4.66675 7.78097 4.66675 8.33325C4.66675 8.88554 5.11446 9.33325 5.66675 9.33325Z" fill="#596168"/>
|
|
4
|
+
<path d="M10.3335 5.33325C10.8858 5.33325 11.3335 4.88554 11.3335 4.33325C11.3335 3.78097 10.8858 3.33325 10.3335 3.33325C9.78121 3.33325 9.3335 3.78097 9.3335 4.33325C9.3335 4.88554 9.78121 5.33325 10.3335 5.33325Z" fill="#596168"/>
|
|
5
|
+
<path d="M5.66675 5.33325C6.21903 5.33325 6.66675 4.88554 6.66675 4.33325C6.66675 3.78097 6.21903 3.33325 5.66675 3.33325C5.11446 3.33325 4.66675 3.78097 4.66675 4.33325C4.66675 4.88554 5.11446 5.33325 5.66675 5.33325Z" fill="#596168"/>
|
|
6
|
+
<path d="M10.3335 13.3333C10.8858 13.3333 11.3335 12.8855 11.3335 12.3333C11.3335 11.781 10.8858 11.3333 10.3335 11.3333C9.78121 11.3333 9.3335 11.781 9.3335 12.3333C9.3335 12.8855 9.78121 13.3333 10.3335 13.3333Z" fill="#596168"/>
|
|
7
|
+
<path d="M5.66675 13.3333C6.21903 13.3333 6.66675 12.8855 6.66675 12.3333C6.66675 11.781 6.21903 11.3333 5.66675 11.3333C5.11446 11.3333 4.66675 11.781 4.66675 12.3333C4.66675 12.8855 5.11446 13.3333 5.66675 13.3333Z" fill="#596168"/>
|
|
8
|
+
</svg>
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
|
|
2
|
+
<g fill="none" fill-rule="evenodd">
|
|
3
|
+
<path d="M0 0h24v24H0z"/>
|
|
4
|
+
<path fill="#959BA1" d="M10 2a8 8 0 0 1 6.32 12.905l5.044 5.045a1 1 0 1 1-1.414 1.414l-5.045-5.043A8 8 0 1 1 10 2zm0 2a6 6 0 1 0 0 12 6 6 0 0 0 0-12z"/>
|
|
5
|
+
</g>
|
|
6
|
+
</svg>
|
package/src/Common/Constants.ts
CHANGED
|
@@ -4,6 +4,8 @@ import { ReactComponent as ICClear } from '../../Assets/Icon/ic-error-cross.svg'
|
|
|
4
4
|
import { DebouncedSearchProps } from './Types'
|
|
5
5
|
|
|
6
6
|
/**
|
|
7
|
+
* @deprecated Use `SearchBar` component instead.
|
|
8
|
+
*
|
|
7
9
|
* @param onSearch - Callback function to be called on search
|
|
8
10
|
* @param Icon - (Optional) Icon to be shown before the input
|
|
9
11
|
* @param iconClass - (Optional) Class for the icon
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import React from 'react'
|
|
2
|
+
import { ReactComponent as ICDrag } from '../../Assets/Icon/ic-drag.svg'
|
|
3
|
+
import { DraggableButtonProps } from './types'
|
|
4
|
+
|
|
5
|
+
export default function DraggableButton({ dragClassName }: DraggableButtonProps) {
|
|
6
|
+
return (
|
|
7
|
+
<button
|
|
8
|
+
type="button"
|
|
9
|
+
className={`${dragClassName} dc__outline-none-imp dc__no-border p-0 dc__transparent h-20`}
|
|
10
|
+
>
|
|
11
|
+
<ICDrag className="dc__grabbable icon-dim-20 fcn-6" />
|
|
12
|
+
</button>
|
|
13
|
+
)
|
|
14
|
+
}
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
import React, { useEffect, useRef, useState } from 'react'
|
|
2
|
+
import Draggable, { ControlPosition, DraggableData } from 'react-draggable'
|
|
3
|
+
import { DraggableWrapperProps, DraggablePositionVariant } from './types'
|
|
4
|
+
import { useWindowSize } from '../Hooks'
|
|
5
|
+
import { MAX_Z_INDEX } from '../Constants'
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* TODO: import it as lazy, after it is supported in common
|
|
9
|
+
* 1. If using react select please use menuPlacement='auto'
|
|
10
|
+
* 2. dragSelector will be used to identify the grabbable button that will grab the div to drag
|
|
11
|
+
* 3. parentRef is the reference point from which we will derive the base top:0 ,left: 0 position
|
|
12
|
+
*/
|
|
13
|
+
export default function DraggableWrapper({
|
|
14
|
+
children,
|
|
15
|
+
zIndex = MAX_Z_INDEX,
|
|
16
|
+
positionVariant,
|
|
17
|
+
dragSelector,
|
|
18
|
+
parentRef,
|
|
19
|
+
boundaryGap = 16,
|
|
20
|
+
childDivProps = {},
|
|
21
|
+
}: DraggableWrapperProps) {
|
|
22
|
+
const windowSize = useWindowSize()
|
|
23
|
+
const nodeRef = useRef<HTMLDivElement>(null)
|
|
24
|
+
|
|
25
|
+
const [position, setPosition] = useState<ControlPosition>({
|
|
26
|
+
x: 0,
|
|
27
|
+
y: 0,
|
|
28
|
+
})
|
|
29
|
+
|
|
30
|
+
const getDefaultPosition = (positionVariant: DraggablePositionVariant): ControlPosition => {
|
|
31
|
+
// if this return x: 0, y: 0 then it will be top left corner of parentDiv
|
|
32
|
+
const parentRect =
|
|
33
|
+
parentRef?.current?.getBoundingClientRect() ??
|
|
34
|
+
({
|
|
35
|
+
width: 0,
|
|
36
|
+
height: 0,
|
|
37
|
+
top: 0,
|
|
38
|
+
left: 0,
|
|
39
|
+
bottom: 0,
|
|
40
|
+
} as DOMRect)
|
|
41
|
+
const nodeRefHeight = nodeRef.current?.getBoundingClientRect().height ?? 0
|
|
42
|
+
const nodeRefWidth = nodeRef.current?.getBoundingClientRect().width ?? 0
|
|
43
|
+
|
|
44
|
+
switch (positionVariant) {
|
|
45
|
+
case DraggablePositionVariant.PARENT_BOTTOM_CENTER: {
|
|
46
|
+
// currently at parentRect.x and need to start to the center of its width and half of node should lie on left of center and other half on right
|
|
47
|
+
const x = (parentRect.width - nodeRefWidth) / 2
|
|
48
|
+
// currently at parentRect.y now parent height can be greater than windowSize.height so taking min
|
|
49
|
+
// subtracting parentRect.top since window height already contains that
|
|
50
|
+
const baseY =
|
|
51
|
+
parentRect.height > windowSize.height ? windowSize.height - parentRect.top : parentRect.height
|
|
52
|
+
const y = baseY - nodeRefHeight - boundaryGap
|
|
53
|
+
return { x, y }
|
|
54
|
+
}
|
|
55
|
+
// Add more cases for other variants if needed
|
|
56
|
+
default: {
|
|
57
|
+
// Since need node to be in center of screen so subtracting width/2 by left of parentRect it will start the node from center but want node's midpoint at center so subtracting node's width from it.
|
|
58
|
+
const x = windowSize.width / 2 - parentRect.left - nodeRefWidth / 2
|
|
59
|
+
// subtracting top since windowSize already contains that
|
|
60
|
+
const y = windowSize.height - parentRect.top - nodeRefHeight - boundaryGap
|
|
61
|
+
|
|
62
|
+
return { x, y }
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
// On change of windowSize we will reset the position to default
|
|
68
|
+
useEffect(() => {
|
|
69
|
+
const defaultPosition = getDefaultPosition(positionVariant)
|
|
70
|
+
setPosition(defaultPosition)
|
|
71
|
+
}, [nodeRef, positionVariant, windowSize])
|
|
72
|
+
|
|
73
|
+
// Would be called on drag and will not update the state if the new position is out of window screen
|
|
74
|
+
function handlePositionChange(e, data: DraggableData) {
|
|
75
|
+
const offsetX = parentRef?.current?.getBoundingClientRect().left ?? 0
|
|
76
|
+
const offsetY = parentRef?.current?.getBoundingClientRect().top ?? 0
|
|
77
|
+
|
|
78
|
+
const nodeRefHeight = nodeRef.current?.getBoundingClientRect().height ?? 0
|
|
79
|
+
const nodeRefWidth = nodeRef.current?.getBoundingClientRect().width ?? 0
|
|
80
|
+
|
|
81
|
+
if (
|
|
82
|
+
offsetX + data.x + nodeRefWidth + boundaryGap > windowSize.width ||
|
|
83
|
+
offsetY + data.y + nodeRefHeight + boundaryGap > windowSize.height ||
|
|
84
|
+
offsetX + data.x < 0 ||
|
|
85
|
+
offsetY + data.y < 0
|
|
86
|
+
) {
|
|
87
|
+
return
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
setPosition({
|
|
91
|
+
x: data.x,
|
|
92
|
+
y: data.y,
|
|
93
|
+
})
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
return (
|
|
97
|
+
<div
|
|
98
|
+
className="dc__position-fixed"
|
|
99
|
+
style={{
|
|
100
|
+
zIndex,
|
|
101
|
+
}}
|
|
102
|
+
>
|
|
103
|
+
<Draggable handle={dragSelector} nodeRef={nodeRef} position={position} onDrag={handlePositionChange}>
|
|
104
|
+
<div
|
|
105
|
+
ref={nodeRef}
|
|
106
|
+
{...childDivProps}
|
|
107
|
+
style={{
|
|
108
|
+
zIndex,
|
|
109
|
+
}}
|
|
110
|
+
>
|
|
111
|
+
{children}
|
|
112
|
+
</div>
|
|
113
|
+
</Draggable>
|
|
114
|
+
</div>
|
|
115
|
+
)
|
|
116
|
+
}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { HTMLAttributes, ReactNode, RefObject } from 'react'
|
|
2
|
+
|
|
3
|
+
export enum DraggablePositionVariant {
|
|
4
|
+
PARENT_BOTTOM_CENTER = 'PARENT_BOTTOM_CENTER',
|
|
5
|
+
SCREEN_BOTTOM_CENTER = 'SCREEN_BOTTOM_CENTER',
|
|
6
|
+
// Can add more based on requirement
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
export interface DraggableWrapperProps {
|
|
10
|
+
children: ReactNode
|
|
11
|
+
zIndex?: number
|
|
12
|
+
positionVariant?: DraggablePositionVariant
|
|
13
|
+
/**
|
|
14
|
+
* dragSelector (class - (append with .), id, etc) will be used to identify the grabbable button that will grab the div to drag
|
|
15
|
+
*/
|
|
16
|
+
dragSelector: string
|
|
17
|
+
parentRef?: RefObject<HTMLDivElement>
|
|
18
|
+
boundaryGap?: number
|
|
19
|
+
childDivProps?: HTMLAttributes<HTMLDivElement>
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* dragClassName is the class that we feed to Draggable to identify dragging buttons
|
|
24
|
+
*/
|
|
25
|
+
export interface DraggableButtonProps {
|
|
26
|
+
dragClassName: string
|
|
27
|
+
}
|
package/src/Common/Grid/Grid.tsx
CHANGED
|
@@ -10,6 +10,7 @@ export default function Grid({
|
|
|
10
10
|
containerClass = '',
|
|
11
11
|
itemClass = '',
|
|
12
12
|
children,
|
|
13
|
+
...containerDivProps
|
|
13
14
|
}: GridProps) {
|
|
14
15
|
const containerStyles = container ? { gap: spacing + 'px' } : {}
|
|
15
16
|
|
|
@@ -25,14 +26,18 @@ export default function Grid({
|
|
|
25
26
|
}
|
|
26
27
|
|
|
27
28
|
return (
|
|
28
|
-
<div className={`p-0 ${itemClass}`} style={itemStyles}>
|
|
29
|
+
<div className={`p-0 ${itemClass}`} style={itemStyles} {...containerDivProps}>
|
|
29
30
|
{children}
|
|
30
31
|
</div>
|
|
31
32
|
)
|
|
32
33
|
}
|
|
33
34
|
|
|
34
35
|
return (
|
|
35
|
-
<div
|
|
36
|
+
<div
|
|
37
|
+
className={`flex-wrap flexbox ${container ? containerClass : ''}`}
|
|
38
|
+
style={containerStyles}
|
|
39
|
+
{...containerDivProps}
|
|
40
|
+
>
|
|
36
41
|
{children}
|
|
37
42
|
</div>
|
|
38
43
|
)
|
package/src/Common/Grid/types.ts
CHANGED
package/src/Common/Helper.tsx
CHANGED
|
@@ -499,20 +499,18 @@ export const getFilteredChartVersions = (charts, selectedChartType) => {
|
|
|
499
499
|
chartRefId: item.chartRefId,
|
|
500
500
|
}))
|
|
501
501
|
}
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
// It recursively removes empty object keys and empty array keys
|
|
502
|
+
function removeEmptyObjectKeysAndNullValues(obj) {
|
|
503
|
+
// It recursively removes empty object keys and array values that are null
|
|
505
504
|
for (let key in obj) {
|
|
506
505
|
if (Array.isArray(obj[key])) {
|
|
506
|
+
if (obj[key].length === 0) continue
|
|
507
|
+
obj[key] = obj[key].filter((item) => item !== null)
|
|
507
508
|
// Check if the array is empty
|
|
508
|
-
if (obj[key].length === 0
|
|
509
|
-
delete obj[key]
|
|
509
|
+
if (obj[key].length === 0) {
|
|
510
|
+
delete obj[key] // Delete the key if the array is empty
|
|
510
511
|
}
|
|
511
512
|
} else if (obj[key] && typeof obj[key] === 'object') {
|
|
512
|
-
if (
|
|
513
|
-
removeEmptyObjectKeysAndNullValues(obj[key], originaljsonCopy[key]) &&
|
|
514
|
-
Object.keys(originaljsonCopy[key]).length !== 0
|
|
515
|
-
) {
|
|
513
|
+
if (removeEmptyObjectKeysAndNullValues(obj[key])) {
|
|
516
514
|
delete obj[key]
|
|
517
515
|
}
|
|
518
516
|
} else if (obj[key] === undefined) {
|
|
@@ -522,25 +520,20 @@ function removeEmptyObjectKeysAndNullValues(obj, originaljsonCopy) {
|
|
|
522
520
|
return Object.keys(obj).length === 0
|
|
523
521
|
}
|
|
524
522
|
|
|
525
|
-
export function getUnlockedJSON(json, jsonPathArray
|
|
526
|
-
if (!jsonPathArray.length) return { newDocument: json, removedPatches: [] }
|
|
527
|
-
|
|
523
|
+
export function getUnlockedJSON(json, jsonPathArray) {
|
|
528
524
|
const jsonCopy = JSON.parse(JSON.stringify(json))
|
|
529
|
-
const originaljsonCopy = JSON.parse(JSON.stringify(json))
|
|
530
|
-
let removedPatches = []
|
|
531
525
|
let patches = jsonPathArray.flatMap((jsonPath) => {
|
|
532
526
|
const pathsToRemove = JSONPath({ path: jsonPath, json: jsonCopy, resultType: 'all' })
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
return { op: 'remove', path: result.pointer }
|
|
539
|
-
})
|
|
527
|
+
return pathsToRemove.map((result) =>
|
|
528
|
+
Array.isArray(result.parent)
|
|
529
|
+
? { op: 'replace', path: result.pointer, value: null }
|
|
530
|
+
: { op: 'remove', path: result.pointer },
|
|
531
|
+
)
|
|
540
532
|
})
|
|
541
533
|
let newDocument = jsonpatch.applyPatch(jsonCopy, patches).newDocument
|
|
542
|
-
|
|
543
|
-
|
|
534
|
+
|
|
535
|
+
removeEmptyObjectKeysAndNullValues(newDocument)
|
|
536
|
+
return newDocument
|
|
544
537
|
}
|
|
545
538
|
|
|
546
539
|
export function getLockedJSON(json, jsonPathArray: string[]) {
|
|
@@ -566,3 +559,18 @@ export function getLockedJSON(json, jsonPathArray: string[]) {
|
|
|
566
559
|
return resultJson['$']
|
|
567
560
|
}
|
|
568
561
|
|
|
562
|
+
/**
|
|
563
|
+
* Returns a debounced variant of the function
|
|
564
|
+
*/
|
|
565
|
+
export const debounce = (func, timeout = 500) => {
|
|
566
|
+
let timer
|
|
567
|
+
|
|
568
|
+
return function (this, ...args) {
|
|
569
|
+
const context = this
|
|
570
|
+
if (timer) clearTimeout(timer)
|
|
571
|
+
timer = setTimeout(() => {
|
|
572
|
+
timer = null
|
|
573
|
+
func.apply(context, args)
|
|
574
|
+
}, timeout)
|
|
575
|
+
}
|
|
576
|
+
}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { useState, useEffect } from 'react'
|
|
2
|
+
|
|
3
|
+
let timeout
|
|
4
|
+
|
|
5
|
+
const getSize = () => {
|
|
6
|
+
return {
|
|
7
|
+
width: window.innerWidth,
|
|
8
|
+
height: window.innerHeight,
|
|
9
|
+
}
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export const useWindowSize = () => {
|
|
13
|
+
const [windowSize, setWindowSize] = useState(getSize)
|
|
14
|
+
|
|
15
|
+
function handleResize(e) {
|
|
16
|
+
if (timeout) {
|
|
17
|
+
window.cancelAnimationFrame(timeout)
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
timeout = window.requestAnimationFrame(function () {
|
|
21
|
+
setWindowSize(getSize())
|
|
22
|
+
})
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
useEffect(() => {
|
|
26
|
+
window.addEventListener('resize', handleResize, false)
|
|
27
|
+
return () => window.removeEventListener('resize', handleResize, false)
|
|
28
|
+
}, [])
|
|
29
|
+
|
|
30
|
+
return windowSize
|
|
31
|
+
}
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
import { useRef, useState } from 'react'
|
|
2
|
+
import { PageSizeItemsProps, PageSizeOption, PageSizeSelectorProps } from './types'
|
|
3
|
+
import { useClickOutside } from '../Hooks'
|
|
4
|
+
import { getDefaultPageValueOptions } from './utils'
|
|
5
|
+
|
|
6
|
+
const PageSizeItems = ({
|
|
7
|
+
optionValue,
|
|
8
|
+
options,
|
|
9
|
+
setOptions,
|
|
10
|
+
handleCloseDropdown,
|
|
11
|
+
changePageSize,
|
|
12
|
+
}: PageSizeItemsProps) => {
|
|
13
|
+
const handlePageSizeSelect = () => {
|
|
14
|
+
const newOptions = options.map((_option) => ({
|
|
15
|
+
value: _option.value,
|
|
16
|
+
selected: _option.value === optionValue,
|
|
17
|
+
}))
|
|
18
|
+
setOptions([...newOptions])
|
|
19
|
+
handleCloseDropdown()
|
|
20
|
+
changePageSize(optionValue)
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
return (
|
|
24
|
+
<div key={optionValue} className="select__item" onClick={handlePageSizeSelect}>
|
|
25
|
+
{optionValue}
|
|
26
|
+
</div>
|
|
27
|
+
)
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
const PageSizeSelector = ({ pageSizeOptions, pageSize, changePageSize }: PageSizeSelectorProps) => {
|
|
31
|
+
const defaultOptions = getDefaultPageValueOptions(pageSizeOptions).map((option) => ({
|
|
32
|
+
value: option.value,
|
|
33
|
+
selected: option.value === pageSize,
|
|
34
|
+
}))
|
|
35
|
+
|
|
36
|
+
const [isDropdownOpen, setIsDropdownOpen] = useState<boolean>(false)
|
|
37
|
+
const [options, setOptions] = useState<PageSizeOption[]>(defaultOptions)
|
|
38
|
+
|
|
39
|
+
const handleCloseDropdown = () => {
|
|
40
|
+
setIsDropdownOpen(false)
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
const handleDropdownToggle = () => {
|
|
44
|
+
setIsDropdownOpen(!isDropdownOpen)
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
const dropdownRef = useRef<HTMLDivElement>(null)
|
|
48
|
+
useClickOutside(dropdownRef, handleCloseDropdown)
|
|
49
|
+
|
|
50
|
+
const selectedOption = options.find((option) => option.selected)
|
|
51
|
+
const font = isDropdownOpen ? 'fa fa-caret-up' : 'fa fa-caret-down'
|
|
52
|
+
return (
|
|
53
|
+
<div ref={dropdownRef}>
|
|
54
|
+
{isDropdownOpen && (
|
|
55
|
+
<div className="pagination__select-menu">
|
|
56
|
+
{options.map((_option) => (
|
|
57
|
+
<PageSizeItems
|
|
58
|
+
optionValue={_option.value}
|
|
59
|
+
options={options}
|
|
60
|
+
setOptions={setOptions}
|
|
61
|
+
handleCloseDropdown={handleCloseDropdown}
|
|
62
|
+
changePageSize={changePageSize}
|
|
63
|
+
/>
|
|
64
|
+
))}
|
|
65
|
+
</div>
|
|
66
|
+
)}
|
|
67
|
+
|
|
68
|
+
<button type="button" className="select__button" onClick={handleDropdownToggle}>
|
|
69
|
+
<span>{selectedOption ? selectedOption.value : ''}</span>
|
|
70
|
+
<span className="select__icon">
|
|
71
|
+
<i className={font} />
|
|
72
|
+
</span>
|
|
73
|
+
</button>
|
|
74
|
+
</div>
|
|
75
|
+
)
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
export default PageSizeSelector
|
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
import { useEffect, useState } from 'react'
|
|
2
|
+
import PageSizeSelector from './PageSizeSelector'
|
|
3
|
+
import { Page, PageValueItemProps, PaginationProps } from './types'
|
|
4
|
+
import { createPageArr } from './utils'
|
|
5
|
+
import './pagination.scss'
|
|
6
|
+
|
|
7
|
+
const PageValueItem = ({ value, isSelected, selectPage }: PageValueItemProps) => {
|
|
8
|
+
const classes = isSelected ? 'page__button page__button--selected' : 'page__button'
|
|
9
|
+
const handlePageSelection = () => {
|
|
10
|
+
selectPage(value)
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
return (
|
|
14
|
+
<li className="page">
|
|
15
|
+
<button className={classes} onClick={handlePageSelection} type="button">
|
|
16
|
+
{value}
|
|
17
|
+
</button>
|
|
18
|
+
</li>
|
|
19
|
+
)
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
// NOTE: This component will heavily benefit from data normalization
|
|
23
|
+
const Pagination = ({
|
|
24
|
+
size,
|
|
25
|
+
pageSize,
|
|
26
|
+
offset,
|
|
27
|
+
changePage,
|
|
28
|
+
changePageSize,
|
|
29
|
+
isPageSizeFix,
|
|
30
|
+
pageSizeOptions,
|
|
31
|
+
rootClassName,
|
|
32
|
+
}: PaginationProps) => {
|
|
33
|
+
const [pages, setPages] = useState<Page[]>([])
|
|
34
|
+
|
|
35
|
+
useEffect(() => {
|
|
36
|
+
const _pages = createPageArr({ size, pageSize, selectedPageNo: 1 })
|
|
37
|
+
setPages(_pages)
|
|
38
|
+
}, [size, pageSize])
|
|
39
|
+
|
|
40
|
+
useEffect(() => {
|
|
41
|
+
const selectedPageNo = 1 + Math.floor(offset / pageSize)
|
|
42
|
+
const _pages = createPageArr({ size, pageSize, selectedPageNo })
|
|
43
|
+
setPages(_pages)
|
|
44
|
+
}, [offset])
|
|
45
|
+
|
|
46
|
+
const selectPage = (selectedPageNo: number): void => {
|
|
47
|
+
const _pages = createPageArr({ size, pageSize, selectedPageNo })
|
|
48
|
+
setPages(_pages)
|
|
49
|
+
changePage(selectedPageNo)
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
const selectPreviousPage = (): void => {
|
|
53
|
+
const currPage = pages.find((page) => page.selected)
|
|
54
|
+
const selectedPageNo = currPage.value - 1
|
|
55
|
+
selectPage(selectedPageNo)
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
const selectNextPage = (): void => {
|
|
59
|
+
const currPage = pages.find((page) => page.selected)
|
|
60
|
+
const selectedPageNo = currPage.value + 1
|
|
61
|
+
selectPage(selectedPageNo)
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
const lastPageNo = offset + pageSize < size ? offset + pageSize : size
|
|
65
|
+
const pageNoIndex = pages.findIndex((page) => page.selected)
|
|
66
|
+
const visiblePages = pages.filter((page) => page.isVisible)
|
|
67
|
+
return (
|
|
68
|
+
// TODO: Remove pagination-wrapper from dashboard
|
|
69
|
+
<div className={rootClassName || ''}>
|
|
70
|
+
<div className="page-number">
|
|
71
|
+
{offset + 1} - {lastPageNo} of {size}
|
|
72
|
+
</div>
|
|
73
|
+
|
|
74
|
+
<ul className="pagination">
|
|
75
|
+
<li className="page">
|
|
76
|
+
<button
|
|
77
|
+
className="page__button page__button--icon"
|
|
78
|
+
disabled={!pageNoIndex}
|
|
79
|
+
onClick={selectPreviousPage}
|
|
80
|
+
type="button"
|
|
81
|
+
aria-label="Previous page"
|
|
82
|
+
>
|
|
83
|
+
<span className="left-icon">
|
|
84
|
+
<i className="fa fa-chevron-left" />
|
|
85
|
+
</span>
|
|
86
|
+
</button>
|
|
87
|
+
</li>
|
|
88
|
+
|
|
89
|
+
{visiblePages.map((page) => (
|
|
90
|
+
<PageValueItem
|
|
91
|
+
value={page.value}
|
|
92
|
+
isSelected={page.selected}
|
|
93
|
+
key={page.value}
|
|
94
|
+
selectPage={selectPage}
|
|
95
|
+
/>
|
|
96
|
+
))}
|
|
97
|
+
|
|
98
|
+
<li className="page">
|
|
99
|
+
<button
|
|
100
|
+
className="page__button page__button--icon"
|
|
101
|
+
disabled={pageNoIndex === pages.length - 1}
|
|
102
|
+
onClick={selectNextPage}
|
|
103
|
+
type="button"
|
|
104
|
+
aria-label="Next page"
|
|
105
|
+
>
|
|
106
|
+
<span className="left-icon">
|
|
107
|
+
<i className="fa fa-chevron-right" />
|
|
108
|
+
</span>
|
|
109
|
+
</button>
|
|
110
|
+
</li>
|
|
111
|
+
</ul>
|
|
112
|
+
|
|
113
|
+
{!isPageSizeFix && (
|
|
114
|
+
<div className="select">
|
|
115
|
+
<span className="select__placeholder">Rows per page</span>
|
|
116
|
+
<PageSizeSelector
|
|
117
|
+
pageSizeOptions={pageSizeOptions}
|
|
118
|
+
pageSize={pageSize}
|
|
119
|
+
changePageSize={changePageSize}
|
|
120
|
+
/>
|
|
121
|
+
</div>
|
|
122
|
+
)}
|
|
123
|
+
</div>
|
|
124
|
+
)
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
export default Pagination
|