@splunk/react-ui 5.7.1 → 5.9.0

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.
Files changed (152) hide show
  1. package/Accordion.js +6 -6
  2. package/Anchor.js +2 -1
  3. package/Box.js +83 -34
  4. package/CHANGELOG.md +51 -0
  5. package/Calendar.js +134 -134
  6. package/Clickable.js +131 -94
  7. package/CollapsiblePanel.js +175 -137
  8. package/ComboBox.js +32 -27
  9. package/ControlGroup.js +92 -91
  10. package/DefinitionList.js +9 -9
  11. package/Drawer.d.ts +2 -0
  12. package/Drawer.js +679 -0
  13. package/Dropdown.js +27 -18
  14. package/DualListbox.js +1 -1
  15. package/File.js +35 -35
  16. package/JSONTree.js +73 -72
  17. package/Link.js +2 -2
  18. package/MIGRATION.md +10 -0
  19. package/Menu.js +403 -261
  20. package/Modal.js +263 -252
  21. package/Monogram.js +2 -2
  22. package/Multiselect.js +551 -385
  23. package/Number.js +2 -1
  24. package/Paginator.js +14 -12
  25. package/Popover.js +4 -1
  26. package/README.md +11 -0
  27. package/RadioBar.js +1 -1
  28. package/Search.js +111 -95
  29. package/Select.js +42 -40
  30. package/SelectBase.js +819 -715
  31. package/SidePanel.js +346 -167
  32. package/SlidingPanels.js +11 -11
  33. package/StepBar.js +7 -7
  34. package/Switch.js +5 -5
  35. package/Table.js +116 -119
  36. package/Text.js +48 -48
  37. package/TextArea.js +7 -7
  38. package/TransitionOpen.js +188 -169
  39. package/docs-llm/Accordion.md +267 -0
  40. package/docs-llm/Anchor Menu.md +115 -0
  41. package/docs-llm/Anchor.md +54 -0
  42. package/docs-llm/AnimationToggle.md +254 -0
  43. package/docs-llm/Avatar.md +292 -0
  44. package/docs-llm/Badge.md +212 -0
  45. package/docs-llm/Breadcrumbs.md +306 -0
  46. package/docs-llm/Button Group.md +53 -0
  47. package/docs-llm/Button.md +361 -0
  48. package/docs-llm/Card Layout.md +286 -0
  49. package/docs-llm/Card.md +619 -0
  50. package/docs-llm/Checkbox.md +218 -0
  51. package/docs-llm/Chip.md +291 -0
  52. package/docs-llm/Clickable.md +160 -0
  53. package/docs-llm/Code.md +292 -0
  54. package/docs-llm/Collapsible Panel.md +744 -0
  55. package/docs-llm/Color.md +253 -0
  56. package/docs-llm/Column Layout.md +391 -0
  57. package/docs-llm/Combo Box.md +540 -0
  58. package/docs-llm/Control Group.md +594 -0
  59. package/docs-llm/Date.md +270 -0
  60. package/docs-llm/Definition List.md +278 -0
  61. package/docs-llm/Divider.md +216 -0
  62. package/docs-llm/Drawer.md +414 -0
  63. package/docs-llm/Dropdown.md +472 -0
  64. package/docs-llm/Dual Listbox.md +325 -0
  65. package/docs-llm/File.md +653 -0
  66. package/docs-llm/Form Rows.md +374 -0
  67. package/docs-llm/Heading.md +179 -0
  68. package/docs-llm/Image.md +109 -0
  69. package/docs-llm/JSON Tree.md +260 -0
  70. package/docs-llm/Layer.md +74 -0
  71. package/docs-llm/Layout.md +50 -0
  72. package/docs-llm/Link.md +318 -0
  73. package/docs-llm/List.md +189 -0
  74. package/docs-llm/Markdown.md +179 -0
  75. package/docs-llm/Menu.md +735 -0
  76. package/docs-llm/Message Bar.md +236 -0
  77. package/docs-llm/Message.md +248 -0
  78. package/docs-llm/Modal.md +443 -0
  79. package/docs-llm/Monogram.md +159 -0
  80. package/docs-llm/Multiselect.md +939 -0
  81. package/docs-llm/Notifications.md +46 -0
  82. package/docs-llm/Number.md +298 -0
  83. package/docs-llm/Paginator.md +395 -0
  84. package/docs-llm/Paragraph.md +148 -0
  85. package/docs-llm/Phone Number.md +254 -0
  86. package/docs-llm/Popover.md +166 -0
  87. package/docs-llm/Progress.md +141 -0
  88. package/docs-llm/Radio Bar.md +303 -0
  89. package/docs-llm/Radio List.md +350 -0
  90. package/docs-llm/Resize.md +362 -0
  91. package/docs-llm/Screen Reader Content.md +73 -0
  92. package/docs-llm/Scroll Container Context.md +155 -0
  93. package/docs-llm/Scroll.md +152 -0
  94. package/docs-llm/Search.md +381 -0
  95. package/docs-llm/Select.md +985 -0
  96. package/docs-llm/Side Panel.md +777 -0
  97. package/docs-llm/Slider.md +339 -0
  98. package/docs-llm/Sliding Panels.md +340 -0
  99. package/docs-llm/Split Button.md +295 -0
  100. package/docs-llm/Static Content.md +90 -0
  101. package/docs-llm/Step Bar.md +292 -0
  102. package/docs-llm/Switch.md +268 -0
  103. package/docs-llm/Tab Bar.md +439 -0
  104. package/docs-llm/Tab Layout.md +398 -0
  105. package/docs-llm/Table.md +2642 -0
  106. package/docs-llm/Text Area.md +253 -0
  107. package/docs-llm/Text.md +339 -0
  108. package/docs-llm/Tooltip.md +325 -0
  109. package/docs-llm/Transition Open.md +406 -0
  110. package/docs-llm/Tree.md +591 -0
  111. package/docs-llm/Typography.md +125 -0
  112. package/docs-llm/Wait Spinner.md +121 -0
  113. package/docs-llm/llms.txt +101 -0
  114. package/package.json +6 -5
  115. package/types/src/Box/Box.d.ts +2 -10
  116. package/types/src/Drawer/Body.d.ts +17 -0
  117. package/types/src/Drawer/Drawer.d.ts +114 -0
  118. package/types/src/Drawer/DrawerContext.d.ts +11 -0
  119. package/types/src/Drawer/Footer.d.ts +25 -0
  120. package/types/src/Drawer/Header.d.ts +41 -0
  121. package/types/src/Drawer/docs/examples/Basic.d.ts +6 -0
  122. package/types/src/Drawer/docs/examples/ContainerPosition.d.ts +7 -0
  123. package/types/src/Drawer/docs/examples/InitialFocus.d.ts +9 -0
  124. package/types/src/Drawer/docs/examples/InlinePosition.d.ts +7 -0
  125. package/types/src/Drawer/docs/examples/PagePosition.d.ts +7 -0
  126. package/types/src/Drawer/index.d.ts +2 -0
  127. package/types/src/JSONTree/JSONTree.d.ts +12 -5
  128. package/types/src/JSONTree/renderTreeItems.d.ts +2 -1
  129. package/types/src/Menu/Item.d.ts +2 -1
  130. package/types/src/Menu/docs/examples/SelectableCheckbox.d.ts +7 -0
  131. package/types/src/Modal/Modal.d.ts +1 -2
  132. package/types/src/Multiselect/Compact.d.ts +8 -3
  133. package/types/src/Multiselect/Multiselect.d.ts +8 -3
  134. package/types/src/Multiselect/Normal.d.ts +8 -3
  135. package/types/src/Multiselect/Option.d.ts +6 -3
  136. package/types/src/Multiselect/docs/examples/Disabled.d.ts +1 -0
  137. package/types/src/Select/Option.d.ts +6 -3
  138. package/types/src/Select/Select.d.ts +8 -5
  139. package/types/src/Select/docs/examples/Dimmed.d.ts +7 -0
  140. package/types/src/SelectBase/OptionBase.d.ts +6 -3
  141. package/types/src/SelectBase/SelectBase.d.ts +8 -3
  142. package/types/src/SidePanel/SidePanel.d.ts +43 -2
  143. package/types/src/SidePanel/docs/examples/DockLayout.d.ts +17 -0
  144. package/types/src/SidePanel/docs/examples/InitialFocus.d.ts +9 -0
  145. package/types/src/TransitionOpen/TransitionOpen.d.ts +29 -4
  146. package/types/src/useKeyPress/index.d.ts +9 -2
  147. package/types/src/useOnClickOutside/index.d.ts +2 -0
  148. package/types/src/useOnClickOutside/useOnClickOutside.d.ts +4 -0
  149. package/useKeyPress.js +23 -18
  150. package/useOnClickOutside.d.ts +2 -0
  151. package/useOnClickOutside.js +79 -0
  152. package/types/src/RadioList/docs/examples/Row.d.ts +0 -6
@@ -0,0 +1,362 @@
1
+ # Resize
2
+
3
+ ## Examples
4
+
5
+
6
+ ### Basic
7
+
8
+ The onRequestResize handler must handle any desired constraints on size.
9
+
10
+ ```typescript
11
+ import React, { useState } from 'react';
12
+
13
+ import Resize, { ResizeRequestResizeHandler } from '@splunk/react-ui/Resize';
14
+
15
+
16
+ function Basic() {
17
+ const [state, setState] = useState({
18
+ height: 200,
19
+ width: 400,
20
+ });
21
+
22
+ const onRequestResize: ResizeRequestResizeHandler = (e, { height, width }) => {
23
+ const constrainedHeight = Math.min(Math.max(height, 100), 400);
24
+ const constrainedWidth = Math.min(Math.max(width, 300), 600);
25
+ setState({ height: constrainedHeight, width: constrainedWidth });
26
+ };
27
+
28
+ return (
29
+ <Resize
30
+ resizeHandles={['se']}
31
+ onRequestResize={onRequestResize}
32
+ style={{
33
+ height: state.height,
34
+ width: state.width,
35
+ padding: 20,
36
+ border: '1px solid #ccc',
37
+ }}
38
+ >
39
+ I am content.
40
+ </Resize>
41
+ );
42
+ }
43
+
44
+ export default Basic;
45
+ ```
46
+
47
+
48
+
49
+ ### Appearance
50
+
51
+ Resize handles can be placed on any edge or corner. Note that margin cannot be overridden when using the "border" appearance.
52
+
53
+ ```typescript
54
+ import React, { useState } from 'react';
55
+
56
+ import Multiselect, { MultiselectChangeHandler } from '@splunk/react-ui/Multiselect';
57
+ import RadioBar, { RadioBarChangeHandler } from '@splunk/react-ui/RadioBar';
58
+ import Resize, { ResizeDirection, ResizeRequestResizeHandler } from '@splunk/react-ui/Resize';
59
+ import { useSplunkTheme } from '@splunk/themes';
60
+
61
+ interface State {
62
+ appearance: 'border' | 'overlay' | 'separator';
63
+ height: number;
64
+ resizeHandles: ResizeDirection[];
65
+ showHandles: 'always' | 'on-hover';
66
+ width: number;
67
+ }
68
+
69
+
70
+ function Appearance() {
71
+ const [state, setState] = useState<State>({
72
+ height: 200,
73
+ width: 200,
74
+ appearance: 'border',
75
+ showHandles: 'always',
76
+ resizeHandles: ['nw', 'n', 'ne', 'w', 'e', 'sw', 's', 'se'],
77
+ });
78
+
79
+ const onRequestResize: ResizeRequestResizeHandler = (e, { height, width }) => {
80
+ setState({ ...state, height, width });
81
+ };
82
+
83
+ const onChangeAppearance: RadioBarChangeHandler = (e, { value }) => {
84
+ setState({ ...state, appearance: value });
85
+ };
86
+
87
+ const onChangeShowHandles: RadioBarChangeHandler = (e, { value }) => {
88
+ setState({ ...state, showHandles: value });
89
+ };
90
+
91
+ const onChangeHandles: MultiselectChangeHandler = (e, { values }) => {
92
+ setState({ ...state, resizeHandles: values as ResizeDirection[] });
93
+ };
94
+
95
+ const { spacingSmall, spacingXLarge } = useSplunkTheme();
96
+
97
+ return (
98
+ <div>
99
+ <div
100
+ style={{
101
+ marginBottom: spacingXLarge,
102
+ display: 'flex',
103
+ gap: spacingSmall,
104
+ }}
105
+ >
106
+ <RadioBar onChange={onChangeAppearance} value={state.appearance}>
107
+ <RadioBar.Option value="overlay" label="overlay" />
108
+ <RadioBar.Option value="border" label="border" />
109
+ <RadioBar.Option value="separator" label="separator" />
110
+ </RadioBar>
111
+ <RadioBar onChange={onChangeShowHandles} value={state.showHandles}>
112
+ <RadioBar.Option value="always" label="always" />
113
+ <RadioBar.Option value="on-hover" label="on-hover" />
114
+ </RadioBar>
115
+ <Multiselect onChange={onChangeHandles} values={state.resizeHandles}>
116
+ <Multiselect.Option value="n" label="n" />
117
+ <Multiselect.Option value="ne" label="ne" />
118
+ <Multiselect.Option value="e" label="e" />
119
+ <Multiselect.Option value="se" label="se" />
120
+ <Multiselect.Option value="s" label="s" />
121
+ <Multiselect.Option value="sw" label="sw" />
122
+ <Multiselect.Option value="w" label="w" />
123
+ <Multiselect.Option value="nw" label="nw" />
124
+ </Multiselect>
125
+ </div>
126
+ <div
127
+ style={{
128
+ border: '1px solid #d6d6d6',
129
+ display: 'inline-block',
130
+ }}
131
+ >
132
+ <Resize
133
+ resizeHandles={state.resizeHandles}
134
+ onRequestResize={onRequestResize}
135
+ appearance={state.appearance}
136
+ showHandles={state.showHandles}
137
+ style={{
138
+ height: state.height,
139
+ width: state.width,
140
+ }}
141
+ >
142
+ <div
143
+ style={{
144
+ left: '50%',
145
+ top: '50%',
146
+ transform: 'translate(-50%, -50%)',
147
+ position: 'absolute',
148
+ textAlign: 'center',
149
+ }}
150
+ >
151
+ I am content.
152
+ </div>
153
+ </Resize>
154
+ </div>
155
+ </div>
156
+ );
157
+ }
158
+
159
+ export default Appearance;
160
+ ```
161
+
162
+
163
+
164
+ ### Sizing outer
165
+
166
+ Resize does not dictate how it is sized. In this example, styles are placed on an outer container.
167
+
168
+ ```typescript
169
+ import React, { useState } from 'react';
170
+
171
+ import Resize, { ResizeRequestResizeHandler } from '@splunk/react-ui/Resize';
172
+
173
+
174
+ function SizingOuter() {
175
+ const [state, setState] = useState({
176
+ height: 200,
177
+ width: 200,
178
+ });
179
+ const onRequestResize: ResizeRequestResizeHandler = (e, { height, width }) => {
180
+ setState({ height, width });
181
+ };
182
+
183
+ return (
184
+ <div // Width and height on outer container
185
+ style={{
186
+ height: state.height,
187
+ width: state.width,
188
+ border: '1px solid #ccc',
189
+ backgroundColor: 'rgba(128, 128, 128, 0.2)',
190
+ position: 'relative',
191
+ }}
192
+ >
193
+ <Resize
194
+ resizeHandles={['se']}
195
+ onRequestResize={onRequestResize}
196
+ style={{
197
+ position: 'absolute',
198
+ top: 0,
199
+ right: 0,
200
+ bottom: 0,
201
+ left: 0,
202
+ padding: 20,
203
+ }}
204
+ >
205
+ Width and height on outer container
206
+ </Resize>
207
+ </div>
208
+ );
209
+ }
210
+
211
+ export default SizingOuter;
212
+ ```
213
+
214
+
215
+
216
+ ### Sizing inner
217
+
218
+ In this example, styles are placed on the inner content.
219
+
220
+ ```typescript
221
+ import React, { useState } from 'react';
222
+
223
+ import Resize, { ResizeRequestResizeHandler } from '@splunk/react-ui/Resize';
224
+
225
+
226
+ function SizingInner() {
227
+ const [state, setState] = useState({
228
+ height: 200,
229
+ width: 200,
230
+ });
231
+
232
+ const onRequestResize: ResizeRequestResizeHandler = (e, { height, width }) => {
233
+ setState({ height: height - 2, width: width - 2 }); // border adjustment
234
+ };
235
+
236
+ return (
237
+ <Resize // Width and height on inner container
238
+ resizeHandles={['se']}
239
+ onRequestResize={onRequestResize}
240
+ style={{
241
+ border: '1px solid #ccc',
242
+ display: 'inline-block',
243
+ }}
244
+ >
245
+ <div
246
+ style={{
247
+ width: state.width,
248
+ height: state.height,
249
+ backgroundColor: 'rgba(128, 128, 128, 0.2)',
250
+ padding: 20,
251
+ boxSizing: 'border-box',
252
+ }}
253
+ >
254
+ Width and height on inner container
255
+ </div>
256
+ </Resize>
257
+ );
258
+ }
259
+
260
+ export default SizingInner;
261
+ ```
262
+
263
+
264
+
265
+ ### Percentage
266
+
267
+ Sizes don't need to be defined in pixels or with height and width styles. Here, flex-basis is calculated as a percentage.
268
+
269
+ ```typescript
270
+ import React, { useRef, useState } from 'react';
271
+
272
+ import Resize, { ResizeRequestResizeHandler } from '@splunk/react-ui/Resize';
273
+
274
+
275
+ function Percentage() {
276
+ const el = useRef<HTMLDivElement | null>(null);
277
+
278
+ const [percentage, setPercentage] = useState(50);
279
+
280
+ const onRequestResize: ResizeRequestResizeHandler = (e, { width }) => {
281
+ if (el.current) {
282
+ const calculatedPercentage = Math.max((width / el.current.clientWidth) * 100);
283
+ const percentageValue = Math.min(Math.max(calculatedPercentage, 10), 90);
284
+
285
+ setPercentage(percentageValue);
286
+ }
287
+ };
288
+
289
+ return (
290
+ <div
291
+ ref={el}
292
+ style={{
293
+ minHeight: 100,
294
+ display: 'flex',
295
+ }}
296
+ >
297
+ <Resize
298
+ resizeHandles={['e']}
299
+ onRequestResize={onRequestResize}
300
+ appearance="separator"
301
+ style={{
302
+ flexBasis: `${percentage}%`,
303
+ backgroundColor: 'rgba(128, 128, 128, 0.2)',
304
+ }}
305
+ >
306
+ <div
307
+ style={{
308
+ padding: 20,
309
+ }}
310
+ >
311
+ Left side
312
+ </div>
313
+ </Resize>
314
+ <div
315
+ style={{
316
+ flexGrow: 1,
317
+ backgroundColor: 'rgba(128, 200, 128, 0.2)',
318
+ padding: 20,
319
+ }}
320
+ >
321
+ Right side
322
+ </div>
323
+ </div>
324
+ );
325
+ }
326
+
327
+ export default Percentage;
328
+ ```
329
+
330
+
331
+
332
+
333
+ ## API
334
+
335
+
336
+ ### Resize API
337
+
338
+ Resize is a utility container with drag handles for resizing.
339
+
340
+ #### Props
341
+
342
+ | Name | Type | Required | Default | Description |
343
+ |------|------|------|------|------|
344
+ | appearance | 'border' \| 'overlay' \| 'separator' | no | 'overlay' | The appearance of the resize handles. Note: When appearance is 'separator', active full length borders will only appear for 'n', 's', 'e', or 'w' `resizeHandles`. |
345
+ | children | React.ReactNode | no | | |
346
+ | elementRef | React.Ref<HTMLDivElement> | no | | A React ref which is set to the DOM element when the component mounts and null when it unmounts. |
347
+ | keyIncrement | number | no | 10 | When focused on a resize handle, the arrow keys will adjust the height or width by this amount with each press. |
348
+ | onRequestResize | ResizeRequestResizeHandler | yes | | A callback which is passed the event and an object with the requested height and width. |
349
+ | resizeHandles | ResizeDirection[] | yes | | An array of resize handles placements. |
350
+ | showHandles | 'always' \| 'on-hover' | no | 'always' | The appearance of the resize handles. |
351
+
352
+ #### Types
353
+
354
+ | Name | Type | Description |
355
+ |------|------|------|
356
+ | ResizeDirection | 'nw' \| 'n' \| 'ne' \| 'w' \| 'e' \| 'sw' \| 's' \| 'se' | |
357
+ | ResizeRequestResizeHandler | ( event: MouseEvent \| React.KeyboardEvent<HTMLButtonElement>, data: { height: number; width: number } ) => void | |
358
+
359
+
360
+
361
+
362
+
@@ -0,0 +1,73 @@
1
+ # Screen Reader Content
2
+
3
+ ## Examples
4
+
5
+
6
+ ### Basic
7
+
8
+ Click 'Show Code' to view the hidden Screen Reader Content.
9
+
10
+ ```typescript
11
+ import React from 'react';
12
+
13
+ import Button from '@splunk/react-ui/Button';
14
+ import ScreenReaderContent from '@splunk/react-ui/ScreenReaderContent';
15
+
16
+
17
+ function Basic() {
18
+ return (
19
+ <Button>
20
+ Learn more <ScreenReaderContent>about advanced search options</ScreenReaderContent>
21
+ </Button>
22
+ );
23
+ }
24
+
25
+ export default Basic;
26
+ ```
27
+
28
+
29
+
30
+ ### Skip-link
31
+
32
+ ```typescript
33
+ import React from 'react';
34
+
35
+ import styled from 'styled-components';
36
+
37
+ import Link from '@splunk/react-ui/Link';
38
+ import { mixins } from '@splunk/themes';
39
+
40
+ const StyledLink = styled(Link)`
41
+ ${mixins.skipLink()}
42
+ `;
43
+
44
+
45
+ function SkipLink() {
46
+ return <StyledLink to="#main">Skip navigation</StyledLink>;
47
+ }
48
+
49
+ export default SkipLink;
50
+ ```
51
+
52
+
53
+
54
+
55
+ ## API
56
+
57
+
58
+ ### ScreenReaderContent API
59
+
60
+ The screen reader text is used to wrap content that
61
+ is only accessible through screen readers.
62
+
63
+ #### Props
64
+
65
+ | Name | Type | Required | Default | Description |
66
+ |------|------|------|------|------|
67
+ | children | React.ReactNode | yes | | |
68
+ | elementRef | React.Ref<HTMLSpanElement> | no | | A React ref which is set to the DOM element when the component mounts and null when it unmounts. |
69
+
70
+
71
+
72
+
73
+
@@ -0,0 +1,155 @@
1
+ # Scroll Container Context
2
+
3
+ ## Examples
4
+
5
+
6
+ ### Provider
7
+
8
+ A Dropdown inside a custom Scroll Container. The Dropdown (when open) scrolls with the custom container, not the window.
9
+
10
+ ```typescript
11
+ import React, { useCallback, useState } from 'react';
12
+
13
+ import Button from '@splunk/react-ui/Button';
14
+ import Dropdown from '@splunk/react-ui/Dropdown';
15
+ import P from '@splunk/react-ui/Paragraph';
16
+ import { ScrollContainerProvider } from '@splunk/react-ui/ScrollContainerContext';
17
+
18
+
19
+ function Provider() {
20
+ const [scrollContainer, setScrollContainer] = useState<HTMLDivElement | null>();
21
+ const scrollContainerRef = useCallback((el: HTMLDivElement | null) => {
22
+ setScrollContainer(el);
23
+ }, []);
24
+
25
+ const toggle = <Button label="Open dropdown in custom container" isMenu />;
26
+ return (
27
+ <div
28
+ ref={scrollContainerRef}
29
+ style={{
30
+ border: '1px dashed #ccc',
31
+ height: '100px',
32
+ width: '500px',
33
+ margin: '20px 20px 0px 0px',
34
+ padding: '30px 20px 10px 20px',
35
+ overflow: 'scroll',
36
+ }}
37
+ >
38
+ <ScrollContainerProvider value={scrollContainer}>
39
+ <Dropdown retainFocus toggle={toggle}>
40
+ <P style={{ padding: 20, maxWidth: 300 }}>
41
+ This dropdown is inside a custom scroll container.
42
+ </P>
43
+ </Dropdown>
44
+ <P style={{ marginTop: 20 }}>Scroll to see more content.</P>
45
+ <P>Additional content for demonstration.</P>
46
+ </ScrollContainerProvider>
47
+ </div>
48
+ );
49
+ }
50
+
51
+ export default Provider;
52
+ ```
53
+
54
+
55
+
56
+ ### measureProgress
57
+
58
+ ```typescript
59
+ import React, { useCallback, useContext, useEffect, useState } from 'react';
60
+
61
+ import P from '@splunk/react-ui/Paragraph';
62
+ import {
63
+ ScrollContainerContext,
64
+ ScrollContainerProvider,
65
+ } from '@splunk/react-ui/ScrollContainerContext';
66
+
67
+ type Container = Document | HTMLElement | Window;
68
+
69
+ function measureProgress(scrollContainer: Container): number {
70
+ const measureEl =
71
+ scrollContainer instanceof HTMLElement ? scrollContainer : document.documentElement;
72
+ const height = measureEl.scrollHeight - measureEl.clientHeight;
73
+ const elasticProgress = height === 0 ? 0 : measureEl.scrollTop / height;
74
+ const progress = Math.min(Math.max(elasticProgress, 0), 1);
75
+ return Math.round(progress * 100);
76
+ }
77
+
78
+
79
+ function ScrollProgress() {
80
+ const scrollContainer: Document | HTMLElement | Window =
81
+ useContext(ScrollContainerContext) || window;
82
+ const [progress, setProgress] = useState(0);
83
+
84
+ const updateProgress = useCallback(() => {
85
+ setProgress(measureProgress(scrollContainer));
86
+ }, [scrollContainer]);
87
+
88
+ useEffect(updateProgress, [scrollContainer, updateProgress]);
89
+
90
+ useEffect(() => {
91
+ scrollContainer.addEventListener('scroll', updateProgress);
92
+
93
+ return () => {
94
+ scrollContainer.removeEventListener('scroll', updateProgress);
95
+ };
96
+ }, [scrollContainer, updateProgress]);
97
+
98
+ return <P>Scroll progress: {progress}%</P>;
99
+ }
100
+
101
+ function Consumer() {
102
+ const [scrollContainer, setScrollContainer] = useState<HTMLDivElement | null>();
103
+ const scrollContainerRef = useCallback((el: HTMLDivElement | null) => {
104
+ setScrollContainer(el);
105
+ }, []);
106
+
107
+ return (
108
+ <>
109
+ <ScrollContainerProvider value={scrollContainer}>
110
+ <ScrollProgress />
111
+ </ScrollContainerProvider>
112
+ <div
113
+ ref={scrollContainerRef}
114
+ style={{
115
+ border: '1px dashed #ccc',
116
+ height: '100px',
117
+ width: '500px',
118
+ margin: '20px 20px 0px 0px',
119
+ padding: '30px 20px 10px 20px',
120
+ overflow: 'scroll',
121
+ }}
122
+ >
123
+ <ScrollContainerProvider value={scrollContainer}>
124
+ <P>This is the first paragraph in the scroll container.</P>
125
+ <P>This is the second paragraph in the scroll container.</P>
126
+ <P>This is the third paragraph in the scroll container.</P>
127
+ <P>This is the fourth paragraph in the scroll container.</P>
128
+ <P>This is the fifth paragraph in the scroll container.</P>
129
+ </ScrollContainerProvider>
130
+ </div>
131
+ </>
132
+ );
133
+ }
134
+
135
+ export default Consumer;
136
+ ```
137
+
138
+
139
+
140
+
141
+ ## API
142
+
143
+ import React from 'react';
144
+
145
+ import UtilAPI from '@splunk/react-docs/UtilAPI';
146
+
147
+ import docs from '!!@splunk/jsdoc-loader!@splunk/react-ui/ScrollContainerContext/ScrollContainerContext';
148
+
149
+ function DevelopSection() {
150
+ return [<UtilAPI key="ScrollContainerContext" docs={docs} />];
151
+ }
152
+
153
+ export default DevelopSection;
154
+
155
+