newspack-components 4.6.3 → 4.7.0-alpha.2

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 (148) hide show
  1. package/CHANGELOG.md +7 -0
  2. package/colors/colors.module.scss +13 -7
  3. package/dist/cjs/autocomplete-tokenfield/style.scss +4 -2
  4. package/dist/cjs/breadcrumbs/format-count.js +33 -0
  5. package/dist/cjs/breadcrumbs/index.js +78 -20
  6. package/dist/cjs/breadcrumbs/index.test.js +110 -0
  7. package/dist/cjs/breadcrumbs/style.scss +6 -0
  8. package/dist/cjs/button/index.js +41 -17
  9. package/dist/cjs/button/index.test.js +273 -0
  10. package/dist/cjs/card/core-card.js +9 -4
  11. package/dist/cjs/card/core-card.test.js +29 -0
  12. package/dist/cjs/card/index.js +4 -2
  13. package/dist/cjs/card/style-core.scss +59 -0
  14. package/dist/cjs/card-feature/index.js +7 -1
  15. package/dist/cjs/card-feature/index.test.js +92 -0
  16. package/dist/cjs/color-picker/index.js +1 -1
  17. package/dist/cjs/confirm-dialog/index.js +26 -9
  18. package/dist/cjs/confirm-dialog/index.test.js +176 -0
  19. package/dist/cjs/dataviews/index.js +11 -2
  20. package/dist/cjs/divider/index.js +13 -0
  21. package/dist/cjs/drawer/action.js +74 -0
  22. package/dist/cjs/drawer/body.js +79 -0
  23. package/dist/cjs/drawer/close-icon.js +45 -0
  24. package/dist/cjs/drawer/content.js +72 -0
  25. package/dist/cjs/drawer/context.js +23 -0
  26. package/dist/cjs/drawer/context.test.js +43 -0
  27. package/dist/cjs/drawer/divider.js +24 -0
  28. package/dist/cjs/drawer/footer.js +26 -0
  29. package/dist/cjs/drawer/header.js +26 -0
  30. package/dist/cjs/drawer/index.js +42 -0
  31. package/dist/cjs/drawer/index.test.js +1287 -0
  32. package/dist/cjs/drawer/root.js +350 -0
  33. package/dist/cjs/drawer/slots.test.js +422 -0
  34. package/dist/cjs/drawer/style.scss +205 -0
  35. package/dist/cjs/drawer/title.js +54 -0
  36. package/dist/cjs/drawer/types.js +5 -0
  37. package/dist/cjs/drawer/use-exit-animation.js +59 -0
  38. package/dist/cjs/hooks/use-confirm-dialog.js +15 -3
  39. package/dist/cjs/hooks/use-unsaved-changes-dialog.js +19 -4
  40. package/dist/cjs/index.js +28 -1
  41. package/dist/cjs/page/index.js +1 -1
  42. package/dist/cjs/page-control/index.js +82 -0
  43. package/dist/cjs/plugin-settings/SettingsSection.js +7 -1
  44. package/dist/cjs/section-header/index.js +5 -2
  45. package/dist/cjs/section-header/style.scss +25 -12
  46. package/dist/cjs/table-card/index.js +67 -0
  47. package/dist/cjs/table-card/index.test.js +153 -0
  48. package/dist/cjs/with-wizard-screen/style.scss +8 -0
  49. package/dist/cjs/wizard/index.js +102 -38
  50. package/dist/esm/autocomplete-tokenfield/style.scss +4 -2
  51. package/dist/esm/breadcrumbs/format-count.js +27 -0
  52. package/dist/esm/breadcrumbs/index.js +81 -20
  53. package/dist/esm/breadcrumbs/index.test.js +110 -0
  54. package/dist/esm/breadcrumbs/style.scss +6 -0
  55. package/dist/esm/button/index.js +42 -18
  56. package/dist/esm/button/index.test.js +273 -0
  57. package/dist/esm/card/core-card.js +9 -4
  58. package/dist/esm/card/core-card.test.js +29 -0
  59. package/dist/esm/card/index.js +4 -2
  60. package/dist/esm/card/style-core.scss +59 -0
  61. package/dist/esm/card-feature/index.js +8 -2
  62. package/dist/esm/card-feature/index.test.js +96 -0
  63. package/dist/esm/color-picker/index.js +1 -1
  64. package/dist/esm/confirm-dialog/index.js +26 -9
  65. package/dist/esm/confirm-dialog/index.test.js +176 -0
  66. package/dist/esm/dataviews/index.js +11 -2
  67. package/dist/esm/divider/index.js +14 -0
  68. package/dist/esm/drawer/action.js +68 -0
  69. package/dist/esm/drawer/body.js +70 -0
  70. package/dist/esm/drawer/close-icon.js +37 -0
  71. package/dist/esm/drawer/content.js +63 -0
  72. package/dist/esm/drawer/context.js +17 -0
  73. package/dist/esm/drawer/context.test.js +40 -0
  74. package/dist/esm/drawer/divider.js +16 -0
  75. package/dist/esm/drawer/footer.js +18 -0
  76. package/dist/esm/drawer/header.js +18 -0
  77. package/dist/esm/drawer/index.js +35 -0
  78. package/dist/esm/drawer/index.test.js +1285 -0
  79. package/dist/esm/drawer/root.js +342 -0
  80. package/dist/esm/drawer/slots.test.js +422 -0
  81. package/dist/esm/drawer/style.scss +205 -0
  82. package/dist/esm/drawer/title.js +46 -0
  83. package/dist/esm/drawer/types.js +1 -0
  84. package/dist/esm/drawer/use-exit-animation.js +52 -0
  85. package/dist/esm/hooks/use-confirm-dialog.js +15 -3
  86. package/dist/esm/hooks/use-unsaved-changes-dialog.js +20 -5
  87. package/dist/esm/index.js +4 -0
  88. package/dist/esm/page/index.js +1 -1
  89. package/dist/esm/page-control/index.js +82 -0
  90. package/dist/esm/plugin-settings/SettingsSection.js +8 -2
  91. package/dist/esm/section-header/index.js +5 -2
  92. package/dist/esm/section-header/style.scss +25 -12
  93. package/dist/esm/table-card/index.js +60 -0
  94. package/dist/esm/table-card/index.test.js +150 -0
  95. package/dist/esm/with-wizard-screen/style.scss +8 -0
  96. package/dist/esm/wizard/index.js +104 -38
  97. package/package.json +2 -2
  98. package/src/autocomplete-tokenfield/style.scss +4 -2
  99. package/src/breadcrumbs/format-count.js +27 -0
  100. package/src/breadcrumbs/index.js +68 -8
  101. package/src/breadcrumbs/index.test.js +57 -0
  102. package/src/breadcrumbs/style.scss +6 -0
  103. package/src/button/index.test.js +143 -0
  104. package/src/button/index.tsx +32 -7
  105. package/src/card/core-card.js +15 -2
  106. package/src/card/core-card.test.js +19 -0
  107. package/src/card/index.js +3 -0
  108. package/src/card/style-core.scss +59 -0
  109. package/src/card-feature/README.md +4 -0
  110. package/src/card-feature/index.test.js +65 -0
  111. package/src/card-feature/index.tsx +13 -2
  112. package/src/color-picker/index.js +1 -1
  113. package/src/confirm-dialog/README.md +3 -2
  114. package/src/confirm-dialog/index.test.js +146 -0
  115. package/src/confirm-dialog/index.tsx +23 -9
  116. package/src/dataviews/index.tsx +11 -2
  117. package/src/divider/index.js +13 -0
  118. package/src/drawer/README.md +454 -0
  119. package/src/drawer/action.tsx +67 -0
  120. package/src/drawer/body.tsx +60 -0
  121. package/src/drawer/close-icon.tsx +37 -0
  122. package/src/drawer/content.tsx +54 -0
  123. package/src/drawer/context.test.js +31 -0
  124. package/src/drawer/context.ts +25 -0
  125. package/src/drawer/divider.tsx +13 -0
  126. package/src/drawer/footer.tsx +15 -0
  127. package/src/drawer/header.tsx +15 -0
  128. package/src/drawer/index.test.js +1012 -0
  129. package/src/drawer/index.tsx +33 -0
  130. package/src/drawer/root.tsx +328 -0
  131. package/src/drawer/slots.test.js +314 -0
  132. package/src/drawer/style.scss +205 -0
  133. package/src/drawer/title.tsx +40 -0
  134. package/src/drawer/types.ts +79 -0
  135. package/src/drawer/use-exit-animation.ts +46 -0
  136. package/src/hooks/use-confirm-dialog.tsx +14 -3
  137. package/src/hooks/use-unsaved-changes-dialog.tsx +17 -5
  138. package/src/index.js +4 -0
  139. package/src/page/index.js +1 -1
  140. package/src/page-control/index.js +63 -0
  141. package/src/plugin-settings/SettingsSection.js +8 -2
  142. package/src/section-header/index.js +5 -2
  143. package/src/section-header/style.scss +25 -12
  144. package/src/table-card/README.md +100 -0
  145. package/src/table-card/index.test.js +112 -0
  146. package/src/table-card/index.tsx +56 -0
  147. package/src/with-wizard-screen/style.scss +8 -0
  148. package/src/wizard/index.js +95 -34
@@ -0,0 +1,342 @@
1
+ import _toConsumableArray from "@babel/runtime/helpers/toConsumableArray";
2
+ import _slicedToArray from "@babel/runtime/helpers/slicedToArray";
3
+ /**
4
+ * WordPress dependencies.
5
+ */
6
+ import { Modal, Popover, SlotFillProvider } from '@wordpress/components';
7
+ import { useMergeRefs } from '@wordpress/compose';
8
+ import { Children, forwardRef, isValidElement, useCallback, useEffect, useLayoutEffect, useMemo, useRef, useState } from '@wordpress/element';
9
+ import { __ } from '@wordpress/i18n';
10
+
11
+ /**
12
+ * External dependencies.
13
+ */
14
+ import classnames from 'classnames';
15
+
16
+ /**
17
+ * Internal dependencies.
18
+ */
19
+ import useConfirmDialog from '../hooks/use-confirm-dialog';
20
+ import { DrawerContext } from './context';
21
+ import Body from './body';
22
+ import Content from './content';
23
+ import Divider from './divider';
24
+ import useExitAnimation from './use-exit-animation';
25
+ import './style.scss';
26
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
27
+ var sizeClassMap = {
28
+ small: 'newspack-drawer--size-small',
29
+ medium: 'newspack-drawer--size-medium',
30
+ large: 'newspack-drawer--size-large',
31
+ 'x-large': 'newspack-drawer--size-x-large',
32
+ full: 'newspack-drawer--size-full'
33
+ };
34
+ var captureOptions = {
35
+ capture: true
36
+ };
37
+ var Root = forwardRef(function Root(_ref, forwardedRef) {
38
+ var _ref$isOpen = _ref.isOpen,
39
+ isOpen = _ref$isOpen === void 0 ? false : _ref$isOpen,
40
+ _ref$size = _ref.size,
41
+ size = _ref$size === void 0 ? 'medium' : _ref$size,
42
+ onRequestClose = _ref.onRequestClose,
43
+ _ref$isDirty = _ref.isDirty,
44
+ isDirty = _ref$isDirty === void 0 ? false : _ref$isDirty,
45
+ _ref$confirmCloseMess = _ref.confirmCloseMessage,
46
+ confirmCloseMessage = _ref$confirmCloseMess === void 0 ? __('You have unsaved changes that will be lost. Discard changes?', 'newspack-plugin') : _ref$confirmCloseMess,
47
+ _ref$confirmButtonTex = _ref.confirmButtonText,
48
+ confirmButtonText = _ref$confirmButtonTex === void 0 ? __('Discard Changes', 'newspack-plugin') : _ref$confirmButtonTex,
49
+ requestConfirm = _ref.requestConfirm,
50
+ contentLabel = _ref.contentLabel,
51
+ describedBy = _ref.describedBy,
52
+ className = _ref.className,
53
+ style = _ref.style,
54
+ children = _ref.children;
55
+ var _useState = useState(null),
56
+ _useState2 = _slicedToArray(_useState, 2),
57
+ title = _useState2[0],
58
+ setTitle = _useState2[1];
59
+ var titleRef = useRef(null);
60
+ var titlesRef = useRef([]);
61
+ var registerTitle = useCallback(function (info, forId) {
62
+ var _titlesRef$current;
63
+ if (info) {
64
+ var at = titlesRef.current.findIndex(function (entry) {
65
+ return entry.id === info.id;
66
+ });
67
+ if (at === -1) {
68
+ titlesRef.current = [].concat(_toConsumableArray(titlesRef.current), [info]);
69
+ } else {
70
+ titlesRef.current = titlesRef.current.map(function (entry) {
71
+ return entry.id === info.id ? info : entry;
72
+ });
73
+ }
74
+ } else if (forId) {
75
+ titlesRef.current = titlesRef.current.filter(function (entry) {
76
+ return entry.id !== forId;
77
+ });
78
+ } else {
79
+ titlesRef.current = [];
80
+ }
81
+ var current = (_titlesRef$current = titlesRef.current[titlesRef.current.length - 1]) !== null && _titlesRef$current !== void 0 ? _titlesRef$current : null;
82
+ titleRef.current = current;
83
+ setTitle(current);
84
+ }, []);
85
+ useEffect(function () {
86
+ if ('production' === process.env.NODE_ENV || !isOpen) {
87
+ return;
88
+ }
89
+ if (!titleRef.current && !contentLabel) {
90
+ // eslint-disable-next-line no-console
91
+ console.warn('Drawer: render a Drawer.Title or pass `contentLabel`. Without one the panel has no accessible name.');
92
+ }
93
+ }, [isOpen, title, contentLabel]);
94
+ useEffect(function () {
95
+ if ('production' === process.env.NODE_ENV || sizeClassMap[size]) {
96
+ return;
97
+ }
98
+ // eslint-disable-next-line no-console
99
+ console.warn("Drawer: unknown size \"".concat(size, "\", falling back to medium. Use one of ").concat(Object.keys(sizeClassMap).join(', '), "."));
100
+ }, [size]);
101
+
102
+ // No `when`: it installs a history.block guard that fights the wizard's own.
103
+ var _useConfirmDialog = useConfirmDialog({
104
+ message: confirmCloseMessage,
105
+ confirmButtonText: confirmButtonText,
106
+ // Hidden, but still the dialog's accessible name.
107
+ title: __('Unsaved changes', 'newspack-plugin'),
108
+ hideTitle: true
109
+ }),
110
+ confirmDialog = _useConfirmDialog.confirmDialog,
111
+ ownRequestConfirm = _useConfirmDialog.requestConfirm,
112
+ ownCancelConfirm = _useConfirmDialog.cancelConfirm;
113
+ var confirm = requestConfirm || ownRequestConfirm;
114
+ var requestClose = useCallback(function () {
115
+ if (!isOpen) {
116
+ return;
117
+ }
118
+ if (isDirty) {
119
+ var prompted = true;
120
+ confirm(function () {
121
+ prompted = false;
122
+ onRequestClose();
123
+ });
124
+ if ('production' !== process.env.NODE_ENV && !prompted) {
125
+ // eslint-disable-next-line no-console
126
+ console.warn('Drawer: `requestConfirm` closed the panel without prompting, so `isDirty` is set while its own `when` is not. ' + 'Unsaved changes are being discarded silently. Drive both from the same state.');
127
+ }
128
+ return;
129
+ }
130
+ onRequestClose();
131
+ }, [isOpen, isDirty, confirm, onRequestClose]);
132
+
133
+ // Core fills its dismisser ref in a passive effect and siblings call it from
134
+ // theirs, so a closure would go stale by tree order. Layout effects run first.
135
+ var dismissRef = useRef({
136
+ isOpen: isOpen,
137
+ isDirty: isDirty,
138
+ onRequestClose: onRequestClose
139
+ });
140
+ useLayoutEffect(function () {
141
+ dismissRef.current = {
142
+ isOpen: isOpen,
143
+ isDirty: isDirty,
144
+ onRequestClose: onRequestClose
145
+ };
146
+ });
147
+
148
+ // Core calls this as another Modal mounts. A dirty panel cannot answer: its own
149
+ // confirmation would land under that Modal, and a delegated one is that Modal.
150
+ var dismiss = useCallback(function () {
151
+ var current = dismissRef.current;
152
+ if (current.isOpen && !current.isDirty) {
153
+ current.onRequestClose();
154
+ }
155
+ }, []);
156
+ var onKeyDown = useCallback(function (event) {
157
+ var _event$nativeEvent;
158
+ // Cancelling a CJK composition raises an Escape of its own.
159
+ if ((_event$nativeEvent = event.nativeEvent) !== null && _event$nativeEvent !== void 0 && _event$nativeEvent.isComposing || 229 === event.keyCode) {
160
+ return;
161
+ }
162
+ if ('Escape' !== event.key && 'Escape' !== event.code || event.defaultPrevented) {
163
+ return;
164
+ }
165
+ event.preventDefault();
166
+ requestClose();
167
+ }, [requestClose]);
168
+ var _useState3 = useState(null),
169
+ _useState4 = _slicedToArray(_useState3, 2),
170
+ node = _useState4[0],
171
+ setNode = _useState4[1];
172
+ var frameRefs = useMergeRefs([setNode, forwardedRef]);
173
+ var _useExitAnimation = useExitAnimation(isOpen, node),
174
+ isRendered = _useExitAnimation.isRendered,
175
+ isExiting = _useExitAnimation.isExiting;
176
+ var openerRef = useRef(null);
177
+ var _useState5 = useState(false),
178
+ _useState6 = _slicedToArray(_useState5, 2),
179
+ wasOpenForFocus = _useState6[0],
180
+ setWasOpenForFocus = _useState6[1];
181
+ if (wasOpenForFocus !== isOpen) {
182
+ setWasOpenForFocus(isOpen);
183
+ if (isOpen) {
184
+ // eslint-disable-next-line @wordpress/no-global-active-element
185
+ openerRef.current = typeof document === 'undefined' ? null : document.activeElement;
186
+ } else {
187
+ // During render, so it is gone in the commit that starts the exit rather
188
+ // than holding focus while the panel goes inert. Only ours to withdraw.
189
+ ownCancelConfirm();
190
+ }
191
+ }
192
+ var movedFocusOut = useRef(false);
193
+ useEffect(function () {
194
+ var frame = node === null || node === void 0 ? void 0 : node.querySelector('.components-modal__frame');
195
+ if (!frame) {
196
+ if (node && 'production' !== process.env.NODE_ENV) {
197
+ // eslint-disable-next-line no-console
198
+ console.warn('Drawer: no .components-modal__frame inside the overlay. Focus return and `inert` are both off. ' + 'Check this against the installed @wordpress/components.');
199
+ }
200
+ return;
201
+ }
202
+ if (isExiting) {
203
+ // Focus goes home before `inert` blurs it, unless parked elsewhere:
204
+ // reclaiming it from a sibling frame would take it from an open modal.
205
+ var active = frame.ownerDocument.activeElement;
206
+ if (frame.contains(active) || !active || active === frame.ownerDocument.body) {
207
+ movedFocusOut.current = true;
208
+ var opener = openerRef.current;
209
+ if (opener !== null && opener !== void 0 && opener.isConnected) {
210
+ var _opener$focus;
211
+ (_opener$focus = opener.focus) === null || _opener$focus === void 0 || _opener$focus.call(opener);
212
+ }
213
+ }
214
+ frame.toggleAttribute('inert', true);
215
+ return;
216
+ }
217
+ frame.toggleAttribute('inert', false);
218
+ // Reopened mid-exit: the same Modal instance, so focus-on-mount never re-runs.
219
+ var restore = movedFocusOut.current;
220
+ movedFocusOut.current = false;
221
+ if (restore && frame.isConnected && !frame.contains(frame.ownerDocument.activeElement)) {
222
+ frame.focus({
223
+ preventScroll: true
224
+ });
225
+ }
226
+ }, [node, isExiting]);
227
+
228
+ // Reduced motion has no exit to hand focus back during.
229
+ useLayoutEffect(function () {
230
+ if (!isRendered) {
231
+ return;
232
+ }
233
+ return function () {
234
+ var _opener$focus2;
235
+ var opener = openerRef.current;
236
+ if (!(opener !== null && opener !== void 0 && opener.isConnected)) {
237
+ return;
238
+ }
239
+ var active = opener.ownerDocument.activeElement;
240
+ if (active && active !== opener.ownerDocument.body && active.isConnected) {
241
+ return;
242
+ }
243
+ (_opener$focus2 = opener.focus) === null || _opener$focus2 === void 0 || _opener$focus2.call(opener);
244
+ };
245
+ }, [isRendered]);
246
+ var pressedOverlay = useRef(false);
247
+ // On commit, not after paint: a release in the gap would close a drawer that
248
+ // has just turned dirty.
249
+ var requestCloseRef = useRef(requestClose);
250
+ useLayoutEffect(function () {
251
+ requestCloseRef.current = requestClose;
252
+ }, [requestClose]);
253
+ useEffect(function () {
254
+ if (!node) {
255
+ return;
256
+ }
257
+ var ownerDocument = node.ownerDocument;
258
+ pressedOverlay.current = false;
259
+ var onPointerDown = function onPointerDown(event) {
260
+ pressedOverlay.current = 0 === event.button && event.target === node;
261
+ if (pressedOverlay.current) {
262
+ // Touch and pen capture implicitly, reporting the release on the overlay.
263
+ if ('function' === typeof node.hasPointerCapture && node.hasPointerCapture(event.pointerId)) {
264
+ node.releasePointerCapture(event.pointerId);
265
+ }
266
+ event.preventDefault();
267
+ }
268
+ };
269
+ var onPointerUp = function onPointerUp(event) {
270
+ var closes = pressedOverlay.current && 0 === event.button && event.target === node;
271
+ pressedOverlay.current = false;
272
+ if (closes) {
273
+ requestCloseRef.current();
274
+ }
275
+ };
276
+ var onPointerCancel = function onPointerCancel() {
277
+ pressedOverlay.current = false;
278
+ };
279
+ node.addEventListener('pointerdown', onPointerDown);
280
+ ownerDocument.addEventListener('pointerup', onPointerUp, captureOptions);
281
+ ownerDocument.addEventListener('pointercancel', onPointerCancel, captureOptions);
282
+ return function () {
283
+ pressedOverlay.current = false;
284
+ node.removeEventListener('pointerdown', onPointerDown);
285
+ ownerDocument.removeEventListener('pointerup', onPointerUp, captureOptions);
286
+ ownerDocument.removeEventListener('pointercancel', onPointerCancel, captureOptions);
287
+ };
288
+ }, [node]);
289
+ var contextValue = useMemo(function () {
290
+ return {
291
+ requestClose: requestClose,
292
+ title: title,
293
+ setTitle: registerTitle
294
+ };
295
+ }, [requestClose, title, registerTitle]);
296
+ if (!isRendered) {
297
+ return null;
298
+ }
299
+ var grouped = [];
300
+ var run = [];
301
+ var bodyIndex = 0;
302
+ var flushRun = function flushRun() {
303
+ if (run.length) {
304
+ grouped.push(/*#__PURE__*/_jsx(Body, {
305
+ children: run
306
+ }, "body-".concat(bodyIndex++)));
307
+ run = [];
308
+ }
309
+ };
310
+ Children.toArray(children).forEach(function (child) {
311
+ if (isValidElement(child) && (child.type === Content || child.type === Divider)) {
312
+ run.push(child);
313
+ } else {
314
+ flushRun();
315
+ grouped.push(child);
316
+ }
317
+ });
318
+ flushRun();
319
+ return /*#__PURE__*/_jsx(DrawerContext.Provider, {
320
+ value: contextValue,
321
+ children: /*#__PURE__*/_jsx(Modal, {
322
+ __experimentalHideHeader: true,
323
+ ref: frameRefs,
324
+ className: classnames('newspack-drawer', sizeClassMap[size] || sizeClassMap.medium, isExiting && 'is-exiting', className),
325
+ overlayClassName: classnames('newspack-drawer__overlay', isExiting && 'is-exiting'),
326
+ style: style,
327
+ contentLabel: title ? undefined : contentLabel,
328
+ aria: {
329
+ labelledby: title ? title.id : undefined,
330
+ describedby: describedBy
331
+ },
332
+ shouldCloseOnEsc: false,
333
+ shouldCloseOnClickOutside: false,
334
+ onRequestClose: dismiss,
335
+ onKeyDown: onKeyDown,
336
+ children: /*#__PURE__*/_jsxs(SlotFillProvider, {
337
+ children: [grouped, !requestConfirm && confirmDialog, /*#__PURE__*/_jsx(Popover.Slot, {})]
338
+ })
339
+ })
340
+ });
341
+ });
342
+ export default Root;