@arcblock/ux 1.16.0 → 1.16.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/lib/CodeBlock/index.js +3 -1
- package/package.json +6 -5
- package/src/ActionButton/index.js +65 -0
- package/src/ActivityIndicator/index.js +186 -0
- package/src/Alert/index.js +104 -0
- package/src/Async/index.js +39 -0
- package/src/Badge/index.js +71 -0
- package/src/Blocklet/index.js +335 -0
- package/src/Button/index.js +4 -0
- package/src/Button/wrap.js +88 -0
- package/src/ButtonGroup/index.js +19 -0
- package/src/Center/index.js +17 -0
- package/src/ClickToCopy/index.js +90 -0
- package/src/CodeBlock/index.js +160 -0
- package/src/Colors/index.js +1 -0
- package/src/Colors/themes/default.js +53 -0
- package/src/ContactForm/index.js +240 -0
- package/src/CookieConsent/index.js +90 -0
- package/src/CountDown/index.js +151 -0
- package/src/Dialog/confirm.js +76 -0
- package/src/Dialog/dialog.js +162 -0
- package/src/Dialog/index.js +2 -0
- package/src/DriftBot/index.js +81 -0
- package/src/Earth/countries.json +8057 -0
- package/src/Earth/index.js +511 -0
- package/src/Earth/util.js +69 -0
- package/src/Empty/index.js +41 -0
- package/src/Footer/index.js +84 -0
- package/src/Icon/image.js +55 -0
- package/src/Icon/index.js +69 -0
- package/src/Img/index.js +172 -0
- package/src/InfoRow/index.js +83 -0
- package/src/Layout/dashboard/header.js +145 -0
- package/src/Layout/dashboard/index.js +140 -0
- package/src/Layout/dashboard/sidebar.js +120 -0
- package/src/Layout/index.js +318 -0
- package/src/Locale/browser-lang.js +63 -0
- package/src/Locale/context.js +88 -0
- package/src/Locale/images/globe-dark.png +0 -0
- package/src/Locale/images/globe-light.png +0 -0
- package/src/Locale/selector.js +138 -0
- package/src/Logo/images/logo-dark-text.svg +3 -0
- package/src/Logo/images/logo-dark-top.svg +6 -0
- package/src/Logo/images/logo-light-text.svg +3 -0
- package/src/Logo/images/logo-light-top.svg +6 -0
- package/src/Logo/index.js +47 -0
- package/src/Metric/index.js +115 -0
- package/src/NFTDisplay/README.md +59 -0
- package/src/NFTDisplay/aspect-ratio-container.js +34 -0
- package/src/NFTDisplay/broken.js +18 -0
- package/src/NFTDisplay/index.js +230 -0
- package/src/NFTDisplay/loading.js +17 -0
- package/src/NFTDisplay/svg-embedder/img.js +36 -0
- package/src/NFTDisplay/svg-embedder/inline-svg.js +37 -0
- package/src/PageScroller/index.js +342 -0
- package/src/PageScroller/usePrevValue.js +12 -0
- package/src/PricingTable/PricingPlan.js +112 -0
- package/src/PricingTable/index.js +43 -0
- package/src/Screenshot/devices.css +1366 -0
- package/src/Screenshot/index.js +181 -0
- package/src/Spinner/index.js +33 -0
- package/src/Switch/index.js +78 -0
- package/src/Tabs/index.js +46 -0
- package/src/Tag/index.js +73 -0
- package/src/Terminal/Player.js +364 -0
- package/src/Terminal/index.js +150 -0
- package/src/Terminal/player.css +378 -0
- package/src/Terminal/util.js +167 -0
- package/src/Terminal/xterm.css +171 -0
- package/src/TextCollapse/index.js +92 -0
- package/src/Theme/index.js +169 -0
- package/src/Theme/responsiveFontSizes.js +94 -0
- package/src/Toast/index.js +118 -0
- package/src/Util/index.js +264 -0
- package/src/Video/index.js +72 -0
- package/src/Wallet/Action.js +105 -0
- package/src/Wallet/Download.js +130 -0
- package/src/Wallet/Open.js +50 -0
- package/src/Wallet/images/abtwallet.png +0 -0
- package/src/Wallet/images/android_download.svg +23 -0
- package/src/Wallet/images/app-store.svg +20 -0
- package/src/Wallet/images/google-play.svg +70 -0
- package/src/WechatPrompt/images/android.png +0 -0
- package/src/WechatPrompt/images/ios.png +0 -0
- package/src/WechatPrompt/index.js +81 -0
- package/src/index.js +63 -0
- package/src/withTheme/index.js +72 -0
- package/src/withTracker/README.md +34 -0
- package/src/withTracker/error_boundary.js +34 -0
- package/src/withTracker/index.js +70 -0
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import styled from 'styled-components';
|
|
3
|
+
|
|
4
|
+
export default function Loading() {
|
|
5
|
+
return <Root className="nft-display__loading">loading...</Root>;
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
const Root = styled.span`
|
|
9
|
+
display: flex;
|
|
10
|
+
justify-content: center;
|
|
11
|
+
align-items: center;
|
|
12
|
+
position: absolute;
|
|
13
|
+
width: 100%;
|
|
14
|
+
height: 100%;
|
|
15
|
+
color: #ccc;
|
|
16
|
+
background-color: #eee;
|
|
17
|
+
`;
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import PropTypes from 'prop-types';
|
|
3
|
+
import InlineSvgEmbedder from './inline-svg';
|
|
4
|
+
|
|
5
|
+
const svgToImgUrl = svg => {
|
|
6
|
+
// fix: #225, https://stackoverflow.com/a/52135328)
|
|
7
|
+
const blob = new Blob([svg], { type: 'image/svg+xml' });
|
|
8
|
+
return URL.createObjectURL(blob);
|
|
9
|
+
};
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* 基于 <img> 嵌入 svg
|
|
13
|
+
*/
|
|
14
|
+
function ImgEmbedder({ svg, alt, fallback, ...rest }) {
|
|
15
|
+
// 包含 foreignObject 的 svg, fallback 到 shadow dom
|
|
16
|
+
if (fallback && svg.indexOf('</foreignObject>') > -1) {
|
|
17
|
+
return <InlineSvgEmbedder svg={svg} />;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
const url = svgToImgUrl(svg);
|
|
21
|
+
return <img src={url} onLoad={() => URL.revokeObjectURL(url)} alt={alt} {...rest} />;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
ImgEmbedder.propTypes = {
|
|
25
|
+
svg: PropTypes.string.isRequired,
|
|
26
|
+
alt: PropTypes.string,
|
|
27
|
+
// 对于包含 foreignObject 的 svg, fallback 到 inline svg + shadow DOM
|
|
28
|
+
fallback: PropTypes.bool,
|
|
29
|
+
};
|
|
30
|
+
|
|
31
|
+
ImgEmbedder.defaultProps = {
|
|
32
|
+
alt: '',
|
|
33
|
+
fallback: true,
|
|
34
|
+
};
|
|
35
|
+
|
|
36
|
+
export default ImgEmbedder;
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import PropTypes from 'prop-types';
|
|
3
|
+
import styled from 'styled-components';
|
|
4
|
+
import root from 'react-shadow/styled-components';
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* inline svg 的方式嵌入 svg, 使用 shadow DOM 避免样式污染
|
|
8
|
+
*/
|
|
9
|
+
function InlineSvg({ svg, ...rest }) {
|
|
10
|
+
return (
|
|
11
|
+
<Root {...rest}>
|
|
12
|
+
<Inner dangerouslySetInnerHTML={{ __html: svg }} />
|
|
13
|
+
</Root>
|
|
14
|
+
);
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
InlineSvg.propTypes = {
|
|
18
|
+
svg: PropTypes.string.isRequired,
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
const Root = styled(root.span)`
|
|
22
|
+
display: block;
|
|
23
|
+
width: 100%;
|
|
24
|
+
height: 100%;
|
|
25
|
+
`;
|
|
26
|
+
|
|
27
|
+
const Inner = styled.div`
|
|
28
|
+
&,
|
|
29
|
+
& > svg {
|
|
30
|
+
height: 100%;
|
|
31
|
+
width: 100%;
|
|
32
|
+
min-width: 100%;
|
|
33
|
+
max-width: 100%;
|
|
34
|
+
}
|
|
35
|
+
`;
|
|
36
|
+
|
|
37
|
+
export default InlineSvg;
|
|
@@ -0,0 +1,342 @@
|
|
|
1
|
+
/* eslint-disable jsx-a11y/no-noninteractive-tabindex */
|
|
2
|
+
import React, { useCallback, useEffect, useRef, useState } from 'react';
|
|
3
|
+
import PropTypes from 'prop-types';
|
|
4
|
+
import isEqual from 'lodash/isEqual';
|
|
5
|
+
import isNil from 'lodash/isNil';
|
|
6
|
+
import isNull from 'lodash/isNull';
|
|
7
|
+
|
|
8
|
+
import usePrevious from './usePrevValue';
|
|
9
|
+
|
|
10
|
+
const Events = {
|
|
11
|
+
TOUCHMOVE: 'touchmove',
|
|
12
|
+
KEYDOWN: 'keydown',
|
|
13
|
+
};
|
|
14
|
+
|
|
15
|
+
const DEFAULT_ANIMATION_TIMER = 500;
|
|
16
|
+
const DEFAULT_ANIMATION = 'ease-in-out';
|
|
17
|
+
const DEFAULT_CONTAINER_HEIGHT = '100vh';
|
|
18
|
+
const DEFAULT_CONTAINER_WIDTH = '100vw';
|
|
19
|
+
const DEFAULT_COMPONENT_INDEX = 0;
|
|
20
|
+
const DEFAULT_COMPONENTS_TO_RENDER_LENGTH = 0;
|
|
21
|
+
|
|
22
|
+
const ANIMATION_TIMER_BUFFER = 100;
|
|
23
|
+
const KEY_UP = 38;
|
|
24
|
+
const KEY_DOWN = 40;
|
|
25
|
+
const DISABLED_CLASS_NAME = 'rps-scroll--disabled';
|
|
26
|
+
|
|
27
|
+
let previousTouchMove = null;
|
|
28
|
+
let isScrolling = false;
|
|
29
|
+
let isMounted = false;
|
|
30
|
+
let isBodyScrollEnabled = true;
|
|
31
|
+
let isTransitionAfterComponentsToRenderChanged = false;
|
|
32
|
+
const containers = [];
|
|
33
|
+
|
|
34
|
+
const PageScroller = ({
|
|
35
|
+
animationTimer,
|
|
36
|
+
blockScrollDown,
|
|
37
|
+
blockScrollUp,
|
|
38
|
+
children,
|
|
39
|
+
height,
|
|
40
|
+
width,
|
|
41
|
+
customPageNumber,
|
|
42
|
+
onScrollUnavailable,
|
|
43
|
+
onChange,
|
|
44
|
+
renderAllPagesOnFirstRender,
|
|
45
|
+
transitionTimingFunction,
|
|
46
|
+
}) => {
|
|
47
|
+
const [componentIndex, setComponentIndex] = useState(DEFAULT_COMPONENT_INDEX);
|
|
48
|
+
const [componentsToRenderLength, setComponentsToRenderLength] = useState(
|
|
49
|
+
DEFAULT_COMPONENTS_TO_RENDER_LENGTH
|
|
50
|
+
);
|
|
51
|
+
const prevComponentIndex = usePrevious(componentIndex);
|
|
52
|
+
const pageContainer = useRef(null);
|
|
53
|
+
|
|
54
|
+
const addNextComponent = useCallback(
|
|
55
|
+
componentsToRenderOnMountLength => {
|
|
56
|
+
let tempComponentsToRenderLength = 0;
|
|
57
|
+
|
|
58
|
+
if (!isNil(componentsToRenderOnMountLength)) {
|
|
59
|
+
tempComponentsToRenderLength = componentsToRenderOnMountLength;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
tempComponentsToRenderLength = Math.max(
|
|
63
|
+
tempComponentsToRenderLength,
|
|
64
|
+
componentsToRenderLength
|
|
65
|
+
);
|
|
66
|
+
|
|
67
|
+
if (tempComponentsToRenderLength <= componentIndex + 1) {
|
|
68
|
+
if (!isNil(children[componentIndex + 1])) {
|
|
69
|
+
tempComponentsToRenderLength++;
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
setComponentsToRenderLength(tempComponentsToRenderLength);
|
|
74
|
+
},
|
|
75
|
+
[children, componentIndex, componentsToRenderLength]
|
|
76
|
+
);
|
|
77
|
+
|
|
78
|
+
const checkRenderOnMount = useCallback(() => {
|
|
79
|
+
if (renderAllPagesOnFirstRender) {
|
|
80
|
+
setComponentsToRenderLength(React.Children.count(children));
|
|
81
|
+
} else if (!isNil(children[DEFAULT_COMPONENT_INDEX + 1])) {
|
|
82
|
+
addNextComponent(DEFAULT_COMPONENTS_TO_RENDER_LENGTH + 1);
|
|
83
|
+
}
|
|
84
|
+
}, [addNextComponent, children, renderAllPagesOnFirstRender]);
|
|
85
|
+
|
|
86
|
+
const disableScroll = useCallback(() => {
|
|
87
|
+
if (isBodyScrollEnabled) {
|
|
88
|
+
isBodyScrollEnabled = false;
|
|
89
|
+
window.scrollTo({
|
|
90
|
+
left: 0,
|
|
91
|
+
top: 0,
|
|
92
|
+
behavior: 'smooth',
|
|
93
|
+
});
|
|
94
|
+
document.body.classList.add(DISABLED_CLASS_NAME);
|
|
95
|
+
document.documentElement.classList.add(DISABLED_CLASS_NAME);
|
|
96
|
+
}
|
|
97
|
+
}, []);
|
|
98
|
+
|
|
99
|
+
const enableDocumentScroll = useCallback(() => {
|
|
100
|
+
if (!isBodyScrollEnabled) {
|
|
101
|
+
isBodyScrollEnabled = true;
|
|
102
|
+
document.body.classList.remove(DISABLED_CLASS_NAME);
|
|
103
|
+
document.documentElement.classList.remove(DISABLED_CLASS_NAME);
|
|
104
|
+
}
|
|
105
|
+
}, []);
|
|
106
|
+
|
|
107
|
+
const setRenderComponents = useCallback(() => {
|
|
108
|
+
const newComponentsToRender = [];
|
|
109
|
+
|
|
110
|
+
let i = 0;
|
|
111
|
+
|
|
112
|
+
while (i < componentsToRenderLength && !isNil(children[i])) {
|
|
113
|
+
containers[i] = true;
|
|
114
|
+
newComponentsToRender.push(
|
|
115
|
+
<div key={i} style={{ height: '100%', width: '100%' }}>
|
|
116
|
+
{React.cloneElement(children[i], { ...children[i].props })}
|
|
117
|
+
</div>
|
|
118
|
+
);
|
|
119
|
+
i++;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
return newComponentsToRender;
|
|
123
|
+
}, [children, componentsToRenderLength]);
|
|
124
|
+
|
|
125
|
+
const scrollWindowDown = useCallback(() => {
|
|
126
|
+
if (!isScrolling && !blockScrollDown) {
|
|
127
|
+
if (!isNil(containers[componentIndex + 1])) {
|
|
128
|
+
disableScroll();
|
|
129
|
+
isScrolling = true;
|
|
130
|
+
pageContainer.current.style.transform = `translate3d(0, ${(componentIndex + 1) * -100}%, 0)`; // prettier-ignore
|
|
131
|
+
|
|
132
|
+
setTimeout(() => {
|
|
133
|
+
if (isMounted) {
|
|
134
|
+
setComponentIndex(prevState => prevState + 1);
|
|
135
|
+
}
|
|
136
|
+
}, animationTimer + ANIMATION_TIMER_BUFFER);
|
|
137
|
+
} else {
|
|
138
|
+
enableDocumentScroll();
|
|
139
|
+
if (onScrollUnavailable) {
|
|
140
|
+
onScrollUnavailable();
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
}, [
|
|
145
|
+
animationTimer,
|
|
146
|
+
blockScrollDown,
|
|
147
|
+
componentIndex,
|
|
148
|
+
disableScroll,
|
|
149
|
+
enableDocumentScroll,
|
|
150
|
+
onScrollUnavailable,
|
|
151
|
+
]);
|
|
152
|
+
|
|
153
|
+
const scrollWindowUp = useCallback(() => {
|
|
154
|
+
if (!isScrolling && !blockScrollUp) {
|
|
155
|
+
if (!isNil(containers[componentIndex - 1])) {
|
|
156
|
+
disableScroll();
|
|
157
|
+
isScrolling = true;
|
|
158
|
+
pageContainer.current.style.transform = `translate3d(0, ${(componentIndex - 1) * -100}%, 0)`; // prettier-ignore
|
|
159
|
+
|
|
160
|
+
setTimeout(() => {
|
|
161
|
+
if (isMounted) {
|
|
162
|
+
setComponentIndex(prevState => prevState - 1);
|
|
163
|
+
}
|
|
164
|
+
}, animationTimer + ANIMATION_TIMER_BUFFER);
|
|
165
|
+
} else {
|
|
166
|
+
enableDocumentScroll();
|
|
167
|
+
if (onScrollUnavailable) {
|
|
168
|
+
onScrollUnavailable();
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
}, [
|
|
173
|
+
animationTimer,
|
|
174
|
+
blockScrollUp,
|
|
175
|
+
componentIndex,
|
|
176
|
+
disableScroll,
|
|
177
|
+
enableDocumentScroll,
|
|
178
|
+
onScrollUnavailable,
|
|
179
|
+
]);
|
|
180
|
+
|
|
181
|
+
const touchMove = useCallback(
|
|
182
|
+
event => {
|
|
183
|
+
if (!isNull(previousTouchMove)) {
|
|
184
|
+
if (event.touches[0].clientY > previousTouchMove) {
|
|
185
|
+
scrollWindowUp();
|
|
186
|
+
} else {
|
|
187
|
+
scrollWindowDown();
|
|
188
|
+
}
|
|
189
|
+
} else {
|
|
190
|
+
previousTouchMove = event.touches[0].clientY;
|
|
191
|
+
}
|
|
192
|
+
},
|
|
193
|
+
[scrollWindowDown, scrollWindowUp]
|
|
194
|
+
);
|
|
195
|
+
|
|
196
|
+
const wheelScroll = useCallback(
|
|
197
|
+
event => {
|
|
198
|
+
if (event.deltaY < 0) {
|
|
199
|
+
scrollWindowUp();
|
|
200
|
+
} else {
|
|
201
|
+
scrollWindowDown();
|
|
202
|
+
}
|
|
203
|
+
},
|
|
204
|
+
[scrollWindowDown, scrollWindowUp]
|
|
205
|
+
);
|
|
206
|
+
|
|
207
|
+
const keyPress = useCallback(
|
|
208
|
+
event => {
|
|
209
|
+
if (isEqual(event.keyCode, KEY_UP)) {
|
|
210
|
+
scrollWindowUp();
|
|
211
|
+
}
|
|
212
|
+
if (isEqual(event.keyCode, KEY_DOWN)) {
|
|
213
|
+
scrollWindowDown();
|
|
214
|
+
}
|
|
215
|
+
},
|
|
216
|
+
[scrollWindowDown, scrollWindowUp]
|
|
217
|
+
);
|
|
218
|
+
|
|
219
|
+
useEffect(() => {
|
|
220
|
+
pageContainer.current.addEventListener(Events.TOUCHMOVE, touchMove);
|
|
221
|
+
pageContainer.current.addEventListener(Events.KEYDOWN, keyPress);
|
|
222
|
+
return () => {
|
|
223
|
+
pageContainer.current.removeEventListener(Events.TOUCHMOVE, touchMove);
|
|
224
|
+
pageContainer.current.removeEventListener(Events.KEYDOWN, keyPress);
|
|
225
|
+
};
|
|
226
|
+
}, [touchMove, keyPress]);
|
|
227
|
+
|
|
228
|
+
useEffect(() => {
|
|
229
|
+
isMounted = true;
|
|
230
|
+
|
|
231
|
+
checkRenderOnMount();
|
|
232
|
+
return () => {
|
|
233
|
+
isMounted = false;
|
|
234
|
+
};
|
|
235
|
+
}, []);
|
|
236
|
+
|
|
237
|
+
useEffect(() => {
|
|
238
|
+
isScrolling = false;
|
|
239
|
+
previousTouchMove = null;
|
|
240
|
+
if (componentIndex > prevComponentIndex) {
|
|
241
|
+
addNextComponent();
|
|
242
|
+
}
|
|
243
|
+
}, [addNextComponent, componentIndex, prevComponentIndex]);
|
|
244
|
+
|
|
245
|
+
useEffect(() => {
|
|
246
|
+
if (onChange) {
|
|
247
|
+
onChange(componentIndex);
|
|
248
|
+
}
|
|
249
|
+
}, [onChange, componentIndex]);
|
|
250
|
+
|
|
251
|
+
useEffect(() => {
|
|
252
|
+
if (!isNil(customPageNumber) && !isEqual(customPageNumber, componentIndex)) {
|
|
253
|
+
let newComponentsToRenderLength = componentsToRenderLength;
|
|
254
|
+
|
|
255
|
+
if (!isEqual(componentIndex, customPageNumber)) {
|
|
256
|
+
if (!isNil(containers[customPageNumber]) && !isScrolling) {
|
|
257
|
+
isScrolling = true;
|
|
258
|
+
pageContainer.current.style.transform = `translate3d(0, ${customPageNumber * -100}%, 0)`;
|
|
259
|
+
|
|
260
|
+
if (isNil(containers[customPageNumber]) && !isNil(children[customPageNumber])) {
|
|
261
|
+
newComponentsToRenderLength++;
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
setTimeout(() => {
|
|
265
|
+
setComponentIndex(customPageNumber);
|
|
266
|
+
setComponentsToRenderLength(newComponentsToRenderLength);
|
|
267
|
+
}, animationTimer + ANIMATION_TIMER_BUFFER);
|
|
268
|
+
} else if (!isScrolling && !isNil(children[customPageNumber])) {
|
|
269
|
+
for (let i = componentsToRenderLength; i <= customPageNumber; i++) {
|
|
270
|
+
newComponentsToRenderLength++;
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
if (!isNil(children[customPageNumber])) {
|
|
274
|
+
newComponentsToRenderLength++;
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
isScrolling = true;
|
|
278
|
+
isTransitionAfterComponentsToRenderChanged = true;
|
|
279
|
+
setComponentsToRenderLength(newComponentsToRenderLength);
|
|
280
|
+
}
|
|
281
|
+
}
|
|
282
|
+
}
|
|
283
|
+
}, [customPageNumber]);
|
|
284
|
+
|
|
285
|
+
useEffect(() => {
|
|
286
|
+
if (isTransitionAfterComponentsToRenderChanged) {
|
|
287
|
+
isTransitionAfterComponentsToRenderChanged = false;
|
|
288
|
+
|
|
289
|
+
pageContainer.current.style.transform = `translate3d(0, ${customPageNumber * -100}%, 0)`;
|
|
290
|
+
|
|
291
|
+
setTimeout(() => {
|
|
292
|
+
setComponentIndex(customPageNumber);
|
|
293
|
+
}, animationTimer + ANIMATION_TIMER_BUFFER);
|
|
294
|
+
}
|
|
295
|
+
}, [animationTimer, componentsToRenderLength, customPageNumber]);
|
|
296
|
+
|
|
297
|
+
return (
|
|
298
|
+
<div style={{ height, width, overflow: 'hidden' }}>
|
|
299
|
+
<div
|
|
300
|
+
ref={pageContainer}
|
|
301
|
+
onWheel={wheelScroll}
|
|
302
|
+
style={{
|
|
303
|
+
height: '100%',
|
|
304
|
+
width: '100%',
|
|
305
|
+
transition: `transform ${animationTimer}ms ${transitionTimingFunction}`,
|
|
306
|
+
outline: 'none',
|
|
307
|
+
}}
|
|
308
|
+
tabIndex={0}>
|
|
309
|
+
{setRenderComponents()}
|
|
310
|
+
</div>
|
|
311
|
+
</div>
|
|
312
|
+
);
|
|
313
|
+
};
|
|
314
|
+
|
|
315
|
+
PageScroller.propTypes = {
|
|
316
|
+
animationTimer: PropTypes.number,
|
|
317
|
+
blockScrollDown: PropTypes.bool,
|
|
318
|
+
blockScrollUp: PropTypes.bool,
|
|
319
|
+
children: PropTypes.any.isRequired,
|
|
320
|
+
height: PropTypes.oneOfType([PropTypes.number, PropTypes.string]),
|
|
321
|
+
width: PropTypes.oneOfType([PropTypes.number, PropTypes.string]),
|
|
322
|
+
customPageNumber: PropTypes.number,
|
|
323
|
+
onScrollUnavailable: PropTypes.func,
|
|
324
|
+
onChange: PropTypes.func,
|
|
325
|
+
renderAllPagesOnFirstRender: PropTypes.bool,
|
|
326
|
+
transitionTimingFunction: PropTypes.string,
|
|
327
|
+
};
|
|
328
|
+
|
|
329
|
+
PageScroller.defaultProps = {
|
|
330
|
+
animationTimer: DEFAULT_ANIMATION_TIMER,
|
|
331
|
+
transitionTimingFunction: DEFAULT_ANIMATION,
|
|
332
|
+
height: DEFAULT_CONTAINER_HEIGHT,
|
|
333
|
+
width: DEFAULT_CONTAINER_WIDTH,
|
|
334
|
+
onChange: undefined,
|
|
335
|
+
onScrollUnavailable: undefined,
|
|
336
|
+
blockScrollUp: false,
|
|
337
|
+
blockScrollDown: false,
|
|
338
|
+
renderAllPagesOnFirstRender: false,
|
|
339
|
+
customPageNumber: undefined,
|
|
340
|
+
};
|
|
341
|
+
|
|
342
|
+
export default PageScroller;
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
// eslint-disable-next-line no-unused-vars
|
|
2
|
+
import React, { useEffect, useRef } from 'react';
|
|
3
|
+
|
|
4
|
+
export default function usePrevious(value) {
|
|
5
|
+
const ref = useRef({});
|
|
6
|
+
|
|
7
|
+
useEffect(() => {
|
|
8
|
+
ref.current = value;
|
|
9
|
+
}, [value]);
|
|
10
|
+
|
|
11
|
+
return ref.current;
|
|
12
|
+
}
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import PropTypes from 'prop-types';
|
|
3
|
+
import styled from 'styled-components';
|
|
4
|
+
|
|
5
|
+
import Card from '@material-ui/core/Card';
|
|
6
|
+
import Button from '@material-ui/core/Button';
|
|
7
|
+
import CardContent from '@material-ui/core/CardContent';
|
|
8
|
+
import Typography from '@material-ui/core/Typography';
|
|
9
|
+
|
|
10
|
+
const PricingPlan = ({ plan }) => (
|
|
11
|
+
<PlanCard shadow>
|
|
12
|
+
<div className="card-header">
|
|
13
|
+
<div className="title">{plan.name}</div>
|
|
14
|
+
</div>
|
|
15
|
+
<CardContent className="card-content">
|
|
16
|
+
<div className="plan-content">
|
|
17
|
+
<div className="plan-pricing">
|
|
18
|
+
<Typography component="h2" variant="h5" className="price-number">
|
|
19
|
+
{plan.price}
|
|
20
|
+
</Typography>
|
|
21
|
+
</div>
|
|
22
|
+
<div className="plan-services">
|
|
23
|
+
{plan.featureList.map(line => (
|
|
24
|
+
<Typography component="p" variant="body1" align="center" key={line}>
|
|
25
|
+
{line}
|
|
26
|
+
</Typography>
|
|
27
|
+
))}
|
|
28
|
+
</div>
|
|
29
|
+
</div>
|
|
30
|
+
<div className="plan-actions">
|
|
31
|
+
{plan.showButton && (
|
|
32
|
+
<Button fullWidth variant="outlined" color="primary" component="a" href={plan.buttonLink}>
|
|
33
|
+
{plan.buttonText}
|
|
34
|
+
</Button>
|
|
35
|
+
)}
|
|
36
|
+
</div>
|
|
37
|
+
</CardContent>
|
|
38
|
+
</PlanCard>
|
|
39
|
+
);
|
|
40
|
+
|
|
41
|
+
export default PricingPlan;
|
|
42
|
+
|
|
43
|
+
PricingPlan.propTypes = {
|
|
44
|
+
plan: PropTypes.object.isRequired,
|
|
45
|
+
};
|
|
46
|
+
|
|
47
|
+
const PlanCard = styled(Card)`
|
|
48
|
+
height: 500px;
|
|
49
|
+
display: flex;
|
|
50
|
+
flex-direction: column;
|
|
51
|
+
@media (max-width: 320px) {
|
|
52
|
+
margin-top: 20px;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
&& {
|
|
56
|
+
${props => (props.shadow ? '' : 'box-shadow: 0px 0px 1px 0px rgba(0, 0, 0, 0.1)')}
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
.card-header {
|
|
60
|
+
height: 80px;
|
|
61
|
+
background-color: #f1fbfb;
|
|
62
|
+
display: flex;
|
|
63
|
+
flex-flow: column;
|
|
64
|
+
align-items: center;
|
|
65
|
+
justify-content: center;
|
|
66
|
+
.title {
|
|
67
|
+
font-size: 18px;
|
|
68
|
+
font-weight: 600;
|
|
69
|
+
text-align: center;
|
|
70
|
+
color: #404040;
|
|
71
|
+
margin: 0;
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
.card-content {
|
|
76
|
+
display: flex;
|
|
77
|
+
flex-direction: column;
|
|
78
|
+
justify-content: space-between;
|
|
79
|
+
align-items: center;
|
|
80
|
+
flex-grow: 1;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
.plan-content {
|
|
84
|
+
display: flex;
|
|
85
|
+
flex-direction: column;
|
|
86
|
+
flex-grow: 1;
|
|
87
|
+
|
|
88
|
+
.plan-pricing {
|
|
89
|
+
display: flex;
|
|
90
|
+
justify-content: center;
|
|
91
|
+
align-items: baseline;
|
|
92
|
+
margin-bottom: 12px;
|
|
93
|
+
color: #4e6af6;
|
|
94
|
+
text-align: center;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
.price-number {
|
|
98
|
+
font-size: 30px;
|
|
99
|
+
font-weight: 600;
|
|
100
|
+
color: #4e6af6;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
.plan-services strong {
|
|
104
|
+
color: #4e6af6;
|
|
105
|
+
font-weight: 500;
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
.plan-actions {
|
|
110
|
+
width: 100%;
|
|
111
|
+
}
|
|
112
|
+
`;
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import PropTypes from 'prop-types';
|
|
3
|
+
import styled from 'styled-components';
|
|
4
|
+
import Grid from '@material-ui/core/Grid';
|
|
5
|
+
|
|
6
|
+
import PricingPlan from './PricingPlan';
|
|
7
|
+
|
|
8
|
+
const PricingTable = ({ plans }) => {
|
|
9
|
+
if (plans && plans.length > 0) {
|
|
10
|
+
return (
|
|
11
|
+
<Div variant="even">
|
|
12
|
+
<Grid container spacing={2} justify="center">
|
|
13
|
+
{plans.map(x => (
|
|
14
|
+
<Grid item className="plan-item" key={x.id} xs={12} sm={6} md={3}>
|
|
15
|
+
<PricingPlan plan={x} />
|
|
16
|
+
</Grid>
|
|
17
|
+
))}
|
|
18
|
+
</Grid>
|
|
19
|
+
</Div>
|
|
20
|
+
);
|
|
21
|
+
}
|
|
22
|
+
return null;
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
export default PricingTable;
|
|
26
|
+
|
|
27
|
+
PricingTable.propTypes = {
|
|
28
|
+
plans: PropTypes.array.isRequired,
|
|
29
|
+
};
|
|
30
|
+
|
|
31
|
+
const Div = styled.div`
|
|
32
|
+
padding: 100px 0;
|
|
33
|
+
text-align: center;
|
|
34
|
+
background-color: ${props => (props.variant === 'even' ? '#fbfbfb' : '#ffffff')};
|
|
35
|
+
@media (max-width: 320px) {
|
|
36
|
+
padding: 50px 0;
|
|
37
|
+
}
|
|
38
|
+
.plan-item {
|
|
39
|
+
@media (max-width: 320px) {
|
|
40
|
+
margin-bottom: 30px;
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
`;
|