|
5 | 5 | * LICENSE file in the root directory of this source tree.
|
6 | 6 | */
|
7 | 7 |
|
8 |
| -import PropTypes from 'prop-types'; |
9 |
| -import React from 'react'; |
10 |
| -import classNames from 'classnames'; |
11 | 8 | import { settings } from 'carbon-components';
|
| 9 | +import cx from 'classnames'; |
| 10 | +import PropTypes from 'prop-types'; |
| 11 | +import React, { useRef } from 'react'; |
12 | 12 | import setupGetInstanceId from '../../tools/setupGetInstanceId';
|
13 | 13 |
|
14 | 14 | const { prefix } = settings;
|
15 |
| - |
16 | 15 | const getInstanceId = setupGetInstanceId();
|
17 | 16 |
|
18 |
| -export default class Loading extends React.Component { |
19 |
| - constructor(props) { |
20 |
| - super(props); |
21 |
| - this.instanceId = getInstanceId(); |
22 |
| - } |
23 |
| - |
24 |
| - static propTypes = { |
25 |
| - /** |
26 |
| - * Specify whether you want the loading indicator to be spinning or not |
27 |
| - */ |
28 |
| - active: PropTypes.bool, |
29 |
| - |
30 |
| - /** |
31 |
| - * Provide an optional className to be applied to the containing node |
32 |
| - */ |
33 |
| - className: PropTypes.string, |
34 |
| - |
35 |
| - /** |
36 |
| - * Specify whether you want the loader to be applied with an overlay |
37 |
| - */ |
38 |
| - withOverlay: PropTypes.bool, |
39 |
| - |
40 |
| - /** |
41 |
| - * Specify whether you would like the small variant of <Loading> |
42 |
| - */ |
43 |
| - small: PropTypes.bool, |
44 |
| - |
45 |
| - /** |
46 |
| - * Specify an description that would be used to best describe the loading state |
47 |
| - */ |
48 |
| - description: PropTypes.string, |
49 |
| - }; |
50 |
| - |
51 |
| - static defaultProps = { |
52 |
| - active: true, |
53 |
| - withOverlay: true, |
54 |
| - small: false, |
55 |
| - description: 'Active loading indicator', |
56 |
| - }; |
57 |
| - |
58 |
| - render() { |
59 |
| - const { |
60 |
| - active, |
61 |
| - className, |
62 |
| - withOverlay, |
63 |
| - small, |
64 |
| - description, |
65 |
| - ...other |
66 |
| - } = this.props; |
67 |
| - |
68 |
| - const loadingClasses = classNames(`${prefix}--loading`, className, { |
69 |
| - [`${prefix}--loading--small`]: small, |
70 |
| - [`${prefix}--loading--stop`]: !active, |
71 |
| - }); |
72 |
| - |
73 |
| - const overlayClasses = classNames(`${prefix}--loading-overlay`, { |
74 |
| - [`${prefix}--loading-overlay--stop`]: !active, |
75 |
| - }); |
76 |
| - |
77 |
| - const loadingId = `loading-id-${this.instanceId}`; |
78 |
| - const spinnerRadius = small ? '26.8125' : '37.5'; |
79 |
| - |
80 |
| - /** |
81 |
| - * Various screenreaders (JAWS, VoiceOver, NVDA...) interpret live regions differently |
82 |
| - * and change their interpretations over time. The aria on the div and the label |
83 |
| - * associated with the div are currently necessary for the loading state to be properly |
84 |
| - * read by all screenreaders. [0] |
85 |
| - * |
86 |
| - * JAWS does not read the loading state unless aria-atomic is set to true and the visually |
87 |
| - * hidden label is required for the loading state to be read in VoiceOver on iOS. Please |
88 |
| - * do not remove without testing on these platforms. |
89 |
| - * |
90 |
| - * [0] https://developer.paciellogroup.com/blog/2014/03/screen-reader-support-aria-live-regions/ |
91 |
| - * */ |
92 |
| - const loading = ( |
93 |
| - <div |
94 |
| - {...other} |
95 |
| - aria-atomic="true" |
96 |
| - aria-labelledby={loadingId} |
97 |
| - aria-live={active ? 'assertive' : 'off'} |
98 |
| - className={loadingClasses}> |
99 |
| - <label id={loadingId} className={`${prefix}--visually-hidden`}> |
100 |
| - {description} |
101 |
| - </label> |
102 |
| - <svg className={`${prefix}--loading__svg`} viewBox="-75 -75 150 150"> |
103 |
| - <title>{description}</title> |
104 |
| - {small ? ( |
105 |
| - <circle |
106 |
| - className={`${prefix}--loading__background`} |
107 |
| - cx="0" |
108 |
| - cy="0" |
109 |
| - r={spinnerRadius} |
110 |
| - /> |
111 |
| - ) : null} |
| 17 | +function Loading({ |
| 18 | + active, |
| 19 | + className: customClassName, |
| 20 | + withOverlay, |
| 21 | + small, |
| 22 | + description, |
| 23 | + ...rest |
| 24 | +}) { |
| 25 | + const { current: instanceId } = useRef(getInstanceId()); |
| 26 | + const loadingClassName = cx(customClassName, { |
| 27 | + [`${prefix}--loading`]: true, |
| 28 | + [`${prefix}--loading--small`]: small, |
| 29 | + [`${prefix}--loading--stop`]: !active, |
| 30 | + }); |
| 31 | + const overlayClassName = cx({ |
| 32 | + [`${prefix}--loading-overlay`]: true, |
| 33 | + [`${prefix}--loading-overlay--stop`]: !active, |
| 34 | + }); |
| 35 | + const loadingId = `loading-id-${instanceId}`; |
| 36 | + const spinnerRadius = small ? '26.8125' : '37.5'; |
| 37 | + |
| 38 | + const loading = ( |
| 39 | + <div |
| 40 | + {...rest} |
| 41 | + aria-atomic="true" |
| 42 | + aria-labelledby={loadingId} |
| 43 | + aria-live={active ? 'assertive' : 'off'} |
| 44 | + className={loadingClassName}> |
| 45 | + <label id={loadingId} className={`${prefix}--visually-hidden`}> |
| 46 | + {description} |
| 47 | + </label> |
| 48 | + <svg className={`${prefix}--loading__svg`} viewBox="-75 -75 150 150"> |
| 49 | + <title>{description}</title> |
| 50 | + {small ? ( |
112 | 51 | <circle
|
113 |
| - className={`${prefix}--loading__stroke`} |
| 52 | + className={`${prefix}--loading__background`} |
114 | 53 | cx="0"
|
115 | 54 | cy="0"
|
116 | 55 | r={spinnerRadius}
|
117 | 56 | />
|
118 |
| - </svg> |
119 |
| - </div> |
120 |
| - ); |
121 |
| - |
122 |
| - return withOverlay ? ( |
123 |
| - <div className={overlayClasses}>{loading}</div> |
124 |
| - ) : ( |
125 |
| - loading |
126 |
| - ); |
127 |
| - } |
| 57 | + ) : null} |
| 58 | + <circle |
| 59 | + className={`${prefix}--loading__stroke`} |
| 60 | + cx="0" |
| 61 | + cy="0" |
| 62 | + r={spinnerRadius} |
| 63 | + /> |
| 64 | + </svg> |
| 65 | + </div> |
| 66 | + ); |
| 67 | + |
| 68 | + return withOverlay ? ( |
| 69 | + <div className={overlayClassName}>{loading}</div> |
| 70 | + ) : ( |
| 71 | + loading |
| 72 | + ); |
128 | 73 | }
|
| 74 | + |
| 75 | +Loading.propTypes = { |
| 76 | + /** |
| 77 | + * Specify whether you want the loading indicator to be spinning or not |
| 78 | + */ |
| 79 | + active: PropTypes.bool, |
| 80 | + |
| 81 | + /** |
| 82 | + * Provide an optional className to be applied to the containing node |
| 83 | + */ |
| 84 | + className: PropTypes.string, |
| 85 | + |
| 86 | + /** |
| 87 | + * Specify whether you want the loader to be applied with an overlay |
| 88 | + */ |
| 89 | + withOverlay: PropTypes.bool, |
| 90 | + |
| 91 | + /** |
| 92 | + * Specify whether you would like the small variant of <Loading> |
| 93 | + */ |
| 94 | + small: PropTypes.bool, |
| 95 | + |
| 96 | + /** |
| 97 | + * Specify an description that would be used to best describe the loading state |
| 98 | + */ |
| 99 | + description: PropTypes.string, |
| 100 | +}; |
| 101 | + |
| 102 | +Loading.defaultProps = { |
| 103 | + active: true, |
| 104 | + withOverlay: true, |
| 105 | + small: false, |
| 106 | + description: 'Active loading indicator', |
| 107 | +}; |
| 108 | + |
| 109 | +export default Loading; |
0 commit comments