@khanacademy/wonder-blocks-modal 7.1.12 → 7.1.13

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/dist/es/index.js CHANGED
@@ -1,4 +1,4 @@
1
- import _extends from '@babel/runtime/helpers/extends';
1
+ import { jsx, jsxs } from 'react/jsx-runtime';
2
2
  import * as React from 'react';
3
3
  import { View, Id } from '@khanacademy/wonder-blocks-core';
4
4
  import { mergeTheme, createThemeContext, ThemeSwitcherContext, useScopedTheme, useStyles, withScopedTheme } from '@khanacademy/wonder-blocks-theming';
@@ -12,839 +12,40 @@ import xIcon from '@phosphor-icons/core/regular/x.svg';
12
12
  import IconButton from '@khanacademy/wonder-blocks-icon-button';
13
13
  import { MediaLayout } from '@khanacademy/wonder-blocks-layout';
14
14
 
15
- const theme$1 = {
16
- root: {
17
- color: {
18
- inverse: {
19
- background: semanticColor.surface.inverse,
20
- foreground: semanticColor.text.inverse
21
- }
22
- },
23
- border: {
24
- radius: border.radius.radius_040
25
- }
26
- },
27
- backdrop: {
28
- color: {
29
- background: semanticColor.surface.overlay
30
- }
31
- },
32
- dialog: {
33
- spacing: {
34
- padding: spacing.medium_16
35
- }
36
- },
37
- footer: {
38
- color: {
39
- border: semanticColor.border.primary
40
- }
41
- },
42
- header: {
43
- color: {
44
- border: semanticColor.border.primary,
45
- secondary: semanticColor.text.secondary
46
- },
47
- spacing: {
48
- paddingBlockMd: spacing.large_24,
49
- paddingInlineMd: spacing.xLarge_32,
50
- paddingInlineSm: spacing.medium_16,
51
- gap: spacing.xSmall_8,
52
- titleGapMd: spacing.medium_16,
53
- titleGapSm: spacing.xLarge_32
54
- }
55
- },
56
- panel: {
57
- spacing: {
58
- gap: spacing.xLarge_32
59
- }
60
- },
61
- closeButton: {
62
- spacing: {
63
- gap: spacing.xSmall_8
64
- }
65
- }
66
- };
15
+ const theme$1={root:{color:{inverse:{background:semanticColor.surface.inverse,foreground:semanticColor.text.inverse}},border:{radius:border.radius.radius_040}},backdrop:{color:{background:semanticColor.surface.overlay}},dialog:{spacing:{padding:spacing.medium_16}},footer:{color:{border:semanticColor.border.primary}},header:{color:{border:semanticColor.border.primary,secondary:semanticColor.text.secondary},spacing:{paddingBlockMd:spacing.large_24,paddingInlineMd:spacing.xLarge_32,paddingInlineSm:spacing.medium_16,gap:spacing.xSmall_8,titleGapMd:spacing.medium_16,titleGapSm:spacing.xLarge_32}},panel:{spacing:{gap:spacing.xLarge_32}},closeButton:{spacing:{gap:spacing.xSmall_8}}};
67
16
 
68
- const theme = mergeTheme(theme$1, {
69
- root: {
70
- color: {
71
- inverse: {
72
- background: semanticColor.khanmigo.primary
73
- }
74
- }
75
- }
76
- });
17
+ const theme=mergeTheme(theme$1,{root:{color:{inverse:{background:semanticColor.khanmigo.primary}}}});
77
18
 
78
- const themes = {
79
- default: theme$1,
80
- khanmigo: theme
81
- };
82
- const ModalDialogThemeContext = createThemeContext(theme$1);
83
- function ThemeModalDialog(props) {
84
- const currentTheme = React.useContext(ThemeSwitcherContext);
85
- const theme = themes[currentTheme] || theme$1;
86
- return React.createElement(ModalDialogThemeContext.Provider, {
87
- value: theme
88
- }, props.children);
89
- }
19
+ const themes={default:theme$1,khanmigo:theme};const ModalDialogThemeContext=createThemeContext(theme$1);function ThemeModalDialog(props){const currentTheme=React.useContext(ThemeSwitcherContext);const theme=themes[currentTheme]||theme$1;return jsx(ModalDialogThemeContext.Provider,{value:theme,children:props.children})}
90
20
 
91
- const ModalDialogCore = React.forwardRef(function ModalDialogCore(props, ref) {
92
- const {
93
- above,
94
- below,
95
- role = "dialog",
96
- style,
97
- children,
98
- testId,
99
- "aria-labelledby": ariaLabelledBy,
100
- "aria-describedby": ariaDescribedBy
101
- } = props;
102
- const {
103
- theme
104
- } = useScopedTheme(ModalDialogThemeContext);
105
- const styles = useStyles(themedStylesFn$5, theme);
106
- return React.createElement(View, {
107
- style: [styles.wrapper, style]
108
- }, below && React.createElement(View, {
109
- style: styles.below
110
- }, below), React.createElement(View, {
111
- role: role,
112
- "aria-modal": "true",
113
- "aria-labelledby": ariaLabelledBy,
114
- "aria-describedby": ariaDescribedBy,
115
- ref: ref,
116
- style: styles.dialog,
117
- testId: testId
118
- }, children), above && React.createElement(View, {
119
- style: styles.above
120
- }, above));
121
- });
122
- const ModalDialog = React.forwardRef(function ModalDialog(props, ref) {
123
- return React.createElement(ThemeModalDialog, null, React.createElement(ModalDialogCore, _extends({}, props, {
124
- ref: ref
125
- })));
126
- });
127
- const small$2 = "@media (max-width: 767px)";
128
- const themedStylesFn$5 = theme => ({
129
- wrapper: {
130
- display: "flex",
131
- flexDirection: "row",
132
- alignItems: "stretch",
133
- width: "100%",
134
- height: "100%",
135
- position: "relative",
136
- [small$2]: {
137
- padding: theme.dialog.spacing.padding,
138
- flexDirection: "column"
139
- }
140
- },
141
- dialog: {
142
- width: "100%",
143
- height: "100%",
144
- borderRadius: theme.root.border.radius,
145
- overflow: "hidden"
146
- },
147
- above: {
148
- pointerEvents: "none",
149
- position: "absolute",
150
- top: 0,
151
- left: 0,
152
- bottom: 0,
153
- right: 0,
154
- zIndex: 1
155
- },
156
- below: {
157
- pointerEvents: "none",
158
- position: "absolute",
159
- top: 0,
160
- left: 0,
161
- bottom: 0,
162
- right: 0,
163
- zIndex: -1
164
- }
165
- });
166
- ModalDialog.displayName = "ModalDialog";
21
+ const ModalDialogCore=React.forwardRef(function ModalDialogCore(props,ref){const{above,below,role="dialog",style,children,testId,"aria-labelledby":ariaLabelledBy,"aria-describedby":ariaDescribedBy}=props;const{theme}=useScopedTheme(ModalDialogThemeContext);const styles=useStyles(themedStylesFn$5,theme);return jsxs(View,{style:[styles.wrapper,style],children:[below&&jsx(View,{style:styles.below,children:below}),jsx(View,{role:role,"aria-modal":"true","aria-labelledby":ariaLabelledBy,"aria-describedby":ariaDescribedBy,ref:ref,style:styles.dialog,testId:testId,children:children}),above&&jsx(View,{style:styles.above,children:above})]})});const ModalDialog=React.forwardRef(function ModalDialog(props,ref){return jsx(ThemeModalDialog,{children:jsx(ModalDialogCore,{...props,ref:ref})})});const small$2="@media (max-width: 767px)";const themedStylesFn$5=theme=>({wrapper:{display:"flex",flexDirection:"row",alignItems:"stretch",width:"100%",height:"100%",position:"relative",[small$2]:{padding:theme.dialog.spacing.padding,flexDirection:"column"}},dialog:{width:"100%",height:"100%",borderRadius:theme.root.border.radius,overflow:"hidden"},above:{pointerEvents:"none",position:"absolute",top:0,left:0,bottom:0,right:0,zIndex:1},below:{pointerEvents:"none",position:"absolute",top:0,left:0,bottom:0,right:0,zIndex:-1}});ModalDialog.displayName="ModalDialog";
167
22
 
168
- function ModalFooter({
169
- children
170
- }) {
171
- const {
172
- theme
173
- } = useScopedTheme(ModalDialogThemeContext);
174
- const styles = useStyles(themedStylesFn$4, theme);
175
- return React.createElement(View, {
176
- style: styles.footer
177
- }, children);
178
- }
179
- ModalFooter.__IS_MODAL_FOOTER__ = true;
180
- ModalFooter.isComponentOf = instance => {
181
- return instance && instance.type && instance.type.__IS_MODAL_FOOTER__;
182
- };
183
- const themedStylesFn$4 = theme => ({
184
- footer: {
185
- flex: "0 0 auto",
186
- boxSizing: "border-box",
187
- minHeight: spacing.xxxLarge_64,
188
- paddingLeft: spacing.medium_16,
189
- paddingRight: spacing.medium_16,
190
- paddingTop: spacing.xSmall_8,
191
- paddingBottom: spacing.xSmall_8,
192
- display: "flex",
193
- flexDirection: "row",
194
- alignItems: "center",
195
- justifyContent: "flex-end",
196
- boxShadow: `0px -1px 0px ${theme.footer.color.border}`
197
- }
198
- });
23
+ function ModalFooter({children}){const{theme}=useScopedTheme(ModalDialogThemeContext);const styles=useStyles(themedStylesFn$4,theme);return jsx(View,{style:styles.footer,children:children})}ModalFooter.__IS_MODAL_FOOTER__=true;ModalFooter.isComponentOf=instance=>{return instance&&instance.type&&instance.type.__IS_MODAL_FOOTER__};const themedStylesFn$4=theme=>({footer:{flex:"0 0 auto",boxSizing:"border-box",minHeight:spacing.xxxLarge_64,paddingLeft:spacing.medium_16,paddingRight:spacing.medium_16,paddingTop:spacing.xSmall_8,paddingBottom:spacing.xSmall_8,display:"flex",flexDirection:"row",alignItems:"center",justifyContent:"flex-end",boxShadow:`0px -1px 0px ${theme.footer.color.border}`}});
199
24
 
200
- function ModalHeader(props) {
201
- const {
202
- breadcrumbs = undefined,
203
- light,
204
- subtitle = undefined,
205
- testId,
206
- title,
207
- titleId
208
- } = props;
209
- if (subtitle && breadcrumbs) {
210
- throw new Error("'subtitle' and 'breadcrumbs' can't be used together");
211
- }
212
- const {
213
- theme
214
- } = useScopedTheme(ModalDialogThemeContext);
215
- const styles = useStyles(themedStylesFn$3, theme);
216
- return React.createElement(View, {
217
- style: [styles.header, !light && styles.dark],
218
- testId: testId
219
- }, breadcrumbs && React.createElement(View, {
220
- style: styles.breadcrumbs
221
- }, breadcrumbs), React.createElement(HeadingMedium, {
222
- tag: "h2",
223
- style: styles.title,
224
- id: titleId,
225
- testId: testId && `${testId}-title`
226
- }, title), subtitle && React.createElement(LabelSmall, {
227
- style: light && styles.subtitle,
228
- testId: testId && `${testId}-subtitle`
229
- }, subtitle));
230
- }
231
- const small$1 = "@media (max-width: 767px)";
232
- const themedStylesFn$3 = theme => ({
233
- header: {
234
- boxShadow: `0px 1px 0px ${theme.header.color.border}`,
235
- display: "flex",
236
- flexDirection: "column",
237
- minHeight: 66,
238
- paddingBlock: theme.header.spacing.paddingBlockMd,
239
- paddingInline: theme.header.spacing.paddingInlineMd,
240
- position: "relative",
241
- width: "100%",
242
- [small$1]: {
243
- paddingInline: theme.header.spacing.paddingInlineSm
244
- }
245
- },
246
- dark: {
247
- background: theme.root.color.inverse.background,
248
- color: theme.root.color.inverse.foreground
249
- },
250
- breadcrumbs: {
251
- color: theme.header.color.secondary,
252
- marginBottom: theme.header.spacing.gap
253
- },
254
- title: {
255
- paddingRight: theme.header.spacing.titleGapMd,
256
- [small$1]: {
257
- paddingRight: theme.header.spacing.titleGapSm
258
- }
259
- },
260
- subtitle: {
261
- color: theme.header.color.secondary,
262
- marginTop: theme.header.spacing.gap
263
- }
264
- });
265
- ModalHeader.defaultProps = {
266
- light: true
267
- };
25
+ function ModalHeader(props){const{breadcrumbs=undefined,light,subtitle=undefined,testId,title,titleId}=props;if(subtitle&&breadcrumbs){throw new Error("'subtitle' and 'breadcrumbs' can't be used together")}const{theme}=useScopedTheme(ModalDialogThemeContext);const styles=useStyles(themedStylesFn$3,theme);return jsxs(View,{style:[styles.header,!light&&styles.dark],testId:testId,children:[breadcrumbs&&jsx(View,{style:styles.breadcrumbs,children:breadcrumbs}),jsx(HeadingMedium,{tag:"h2",style:styles.title,id:titleId,testId:testId&&`${testId}-title`,children:title}),subtitle&&jsx(LabelSmall,{style:light&&styles.subtitle,testId:testId&&`${testId}-subtitle`,children:subtitle})]})}const small$1="@media (max-width: 767px)";const themedStylesFn$3=theme=>({header:{boxShadow:`0px 1px 0px ${theme.header.color.border}`,display:"flex",flexDirection:"column",minHeight:66,paddingBlock:theme.header.spacing.paddingBlockMd,paddingInline:theme.header.spacing.paddingInlineMd,position:"relative",width:"100%",[small$1]:{paddingInline:theme.header.spacing.paddingInlineSm}},dark:{background:theme.root.color.inverse.background,color:theme.root.color.inverse.foreground},breadcrumbs:{color:theme.header.color.secondary,marginBottom:theme.header.spacing.gap},title:{paddingRight:theme.header.spacing.titleGapMd,[small$1]:{paddingRight:theme.header.spacing.titleGapSm}},subtitle:{color:theme.header.color.secondary,marginTop:theme.header.spacing.gap}});ModalHeader.defaultProps={light:true};
268
26
 
269
- const FOCUSABLE_ELEMENTS$1 = 'button, [href], input, select, textarea, [tabindex]:not([tabindex="-1"])';
270
- class FocusTrap extends React.Component {
271
- constructor(...args) {
272
- super(...args);
273
- this.modalRoot = void 0;
274
- this.getModalRoot = node => {
275
- if (!node) {
276
- return;
277
- }
278
- const modalRoot = ReactDOM.findDOMNode(node);
279
- if (!modalRoot) {
280
- throw new Error("Assertion error: modal root should exist after mount");
281
- }
282
- this.modalRoot = modalRoot;
283
- };
284
- this.handleFocusMoveToLast = () => {
285
- this.focusElementIn(false);
286
- };
287
- this.handleFocusMoveToFirst = () => {
288
- this.focusElementIn(true);
289
- };
290
- }
291
- tryToFocus(node) {
292
- if (node instanceof HTMLElement) {
293
- try {
294
- node.focus();
295
- } catch (e) {}
296
- return document.activeElement === node;
297
- }
298
- }
299
- focusElementIn(isLast) {
300
- const modalRootAsHtmlEl = this.modalRoot;
301
- const focusableNodes = Array.from(modalRootAsHtmlEl.querySelectorAll(FOCUSABLE_ELEMENTS$1));
302
- const nodeIndex = !isLast ? focusableNodes.length - 1 : 0;
303
- const focusableNode = focusableNodes[nodeIndex];
304
- this.tryToFocus(focusableNode);
305
- }
306
- render() {
307
- const {
308
- style
309
- } = this.props;
310
- return React.createElement(React.Fragment, null, React.createElement("div", {
311
- tabIndex: 0,
312
- className: "modal-focus-trap-first",
313
- onFocus: this.handleFocusMoveToLast,
314
- style: {
315
- position: "fixed"
316
- }
317
- }), React.createElement(View, {
318
- style: style,
319
- ref: this.getModalRoot
320
- }, this.props.children), React.createElement("div", {
321
- tabIndex: 0,
322
- className: "modal-focus-trap-last",
323
- onFocus: this.handleFocusMoveToFirst,
324
- style: {
325
- position: "fixed"
326
- }
327
- }));
328
- }
329
- }
27
+ const FOCUSABLE_ELEMENTS$1='button, [href], input, select, textarea, [tabindex]:not([tabindex="-1"])';class FocusTrap extends React.Component{tryToFocus(node){if(node instanceof HTMLElement){try{node.focus();}catch(e){}return document.activeElement===node}}focusElementIn(isLast){const modalRootAsHtmlEl=this.modalRoot;const focusableNodes=Array.from(modalRootAsHtmlEl.querySelectorAll(FOCUSABLE_ELEMENTS$1));const nodeIndex=!isLast?focusableNodes.length-1:0;const focusableNode=focusableNodes[nodeIndex];this.tryToFocus(focusableNode);}render(){const{style}=this.props;return jsxs(React.Fragment,{children:[jsx("div",{tabIndex:0,className:"modal-focus-trap-first",onFocus:this.handleFocusMoveToLast,style:{position:"fixed"}}),jsx(View,{style:style,ref:this.getModalRoot,children:this.props.children}),jsx("div",{tabIndex:0,className:"modal-focus-trap-last",onFocus:this.handleFocusMoveToFirst,style:{position:"fixed"}})]})}constructor(...args){super(...args),this.getModalRoot=node=>{if(!node){return}const modalRoot=ReactDOM.findDOMNode(node);if(!modalRoot){throw new Error("Assertion error: modal root should exist after mount")}this.modalRoot=modalRoot;},this.handleFocusMoveToLast=()=>{this.focusElementIn(false);},this.handleFocusMoveToFirst=()=>{this.focusElementIn(true);};}}
330
28
 
331
- const ModalLauncherPortalAttributeName = "data-modal-launcher-portal";
29
+ const ModalLauncherPortalAttributeName="data-modal-launcher-portal";
332
30
 
333
- const FOCUSABLE_ELEMENTS = "a[href], details, input, textarea, select, button";
334
- function findFocusableNodes(root) {
335
- return Array.from(root.querySelectorAll(FOCUSABLE_ELEMENTS));
336
- }
31
+ const FOCUSABLE_ELEMENTS="a[href], details, input, textarea, select, button";function findFocusableNodes(root){return Array.from(root.querySelectorAll(FOCUSABLE_ELEMENTS))}
337
32
 
338
- class ModalBackdrop extends React.Component {
339
- constructor(...args) {
340
- super(...args);
341
- this._mousePressedOutside = false;
342
- this.handleMouseDown = e => {
343
- this._mousePressedOutside = e.target === e.currentTarget;
344
- };
345
- this.handleMouseUp = e => {
346
- if (e.target === e.currentTarget && this._mousePressedOutside) {
347
- this.props.onCloseModal();
348
- }
349
- this._mousePressedOutside = false;
350
- };
351
- }
352
- componentDidMount() {
353
- const node = ReactDOM.findDOMNode(this);
354
- if (!node) {
355
- return;
356
- }
357
- const firstFocusableElement = this._getInitialFocusElement(node) || this._getFirstFocusableElement(node) || this._getDialogElement(node);
358
- setTimeout(() => {
359
- firstFocusableElement.focus();
360
- }, 0);
361
- }
362
- _getInitialFocusElement(node) {
363
- const {
364
- initialFocusId
365
- } = this.props;
366
- if (!initialFocusId) {
367
- return null;
368
- }
369
- return ReactDOM.findDOMNode(node.querySelector(`#${initialFocusId}`));
370
- }
371
- _getFirstFocusableElement(node) {
372
- const focusableElements = findFocusableNodes(node);
373
- if (!focusableElements) {
374
- return null;
375
- }
376
- return focusableElements[0];
377
- }
378
- _getDialogElement(node) {
379
- const dialogElement = ReactDOM.findDOMNode(node.querySelector('[role="dialog"]'));
380
- dialogElement.tabIndex = -1;
381
- return dialogElement;
382
- }
383
- render() {
384
- const {
385
- children,
386
- testId
387
- } = this.props;
388
- const backdropProps = {
389
- [ModalLauncherPortalAttributeName]: true
390
- };
391
- return React.createElement(View, _extends({
392
- style: this.props.wbThemeStyles.modalPositioner,
393
- onMouseDown: this.handleMouseDown,
394
- onMouseUp: this.handleMouseUp,
395
- testId: testId
396
- }, backdropProps), children);
397
- }
398
- }
399
- const themedStylesFn$2 = theme => ({
400
- modalPositioner: {
401
- position: "fixed",
402
- left: 0,
403
- top: 0,
404
- width: "100%",
405
- height: "100%",
406
- alignItems: "center",
407
- justifyContent: "center",
408
- overflow: "auto",
409
- background: theme.backdrop.color.background
410
- }
411
- });
412
- var ModalBackdrop$1 = withScopedTheme(themedStylesFn$2, ModalDialogThemeContext)(ModalBackdrop);
33
+ class ModalBackdrop extends React.Component{componentDidMount(){const node=ReactDOM.findDOMNode(this);if(!node){return}const firstFocusableElement=this._getInitialFocusElement(node)||this._getFirstFocusableElement(node)||this._getDialogElement(node);setTimeout(()=>{firstFocusableElement.focus();},0);}_getInitialFocusElement(node){const{initialFocusId}=this.props;if(!initialFocusId){return null}return ReactDOM.findDOMNode(node.querySelector(`#${initialFocusId}`))}_getFirstFocusableElement(node){const focusableElements=findFocusableNodes(node);if(!focusableElements){return null}return focusableElements[0]}_getDialogElement(node){const dialogElement=ReactDOM.findDOMNode(node.querySelector('[role="dialog"]'));dialogElement.tabIndex=-1;return dialogElement}render(){const{children,testId}=this.props;const backdropProps={[ModalLauncherPortalAttributeName]:true};return jsx(View,{style:this.props.wbThemeStyles.modalPositioner,onMouseDown:this.handleMouseDown,onMouseUp:this.handleMouseUp,testId:testId,...backdropProps,children:children})}constructor(...args){super(...args),this._mousePressedOutside=false,this.handleMouseDown=e=>{this._mousePressedOutside=e.target===e.currentTarget;},this.handleMouseUp=e=>{if(e.target===e.currentTarget&&this._mousePressedOutside){this.props.onCloseModal();}this._mousePressedOutside=false;};}}const themedStylesFn$2=theme=>({modalPositioner:{position:"fixed",left:0,top:0,width:"100%",height:"100%",alignItems:"center",justifyContent:"center",overflow:"auto",background:theme.backdrop.color.background}});var ModalBackdrop$1 = withScopedTheme(themedStylesFn$2,ModalDialogThemeContext)(ModalBackdrop);
413
34
 
414
- const needsHackyMobileSafariScrollDisabler = (() => {
415
- if (typeof window === "undefined") {
416
- return false;
417
- }
418
- const userAgent = window.navigator.userAgent;
419
- return userAgent.indexOf("iPad") > -1 || userAgent.indexOf("iPhone") > -1;
420
- })();
421
- class ScrollDisabler extends React.Component {
422
- componentDidMount() {
423
- if (ScrollDisabler.numModalsOpened === 0) {
424
- const body = document.body;
425
- if (!body) {
426
- throw new Error("couldn't find document.body");
427
- }
428
- ScrollDisabler.oldOverflow = body.style.overflow;
429
- ScrollDisabler.oldScrollY = window.scrollY;
430
- if (needsHackyMobileSafariScrollDisabler) {
431
- ScrollDisabler.oldPosition = body.style.position;
432
- ScrollDisabler.oldWidth = body.style.width;
433
- ScrollDisabler.oldTop = body.style.top;
434
- }
435
- body.style.overflow = "hidden";
436
- if (needsHackyMobileSafariScrollDisabler) {
437
- body.style.position = "fixed";
438
- body.style.width = "100%";
439
- body.style.top = `${-ScrollDisabler.oldScrollY}px`;
440
- }
441
- }
442
- ScrollDisabler.numModalsOpened++;
443
- }
444
- componentWillUnmount() {
445
- ScrollDisabler.numModalsOpened--;
446
- if (ScrollDisabler.numModalsOpened === 0) {
447
- const body = document.body;
448
- if (!body) {
449
- throw new Error("couldn't find document.body");
450
- }
451
- body.style.overflow = ScrollDisabler.oldOverflow;
452
- if (needsHackyMobileSafariScrollDisabler) {
453
- body.style.position = ScrollDisabler.oldPosition;
454
- body.style.width = ScrollDisabler.oldWidth;
455
- body.style.top = ScrollDisabler.oldTop;
456
- }
457
- if (typeof window !== "undefined" && window.scrollTo) {
458
- window.scrollTo(0, ScrollDisabler.oldScrollY);
459
- }
460
- }
461
- }
462
- render() {
463
- return null;
464
- }
465
- }
466
- ScrollDisabler.oldOverflow = void 0;
467
- ScrollDisabler.oldPosition = void 0;
468
- ScrollDisabler.oldScrollY = void 0;
469
- ScrollDisabler.oldWidth = void 0;
470
- ScrollDisabler.oldTop = void 0;
471
- ScrollDisabler.numModalsOpened = 0;
35
+ const needsHackyMobileSafariScrollDisabler=(()=>{if(typeof window==="undefined"){return false}const userAgent=window.navigator.userAgent;return userAgent.indexOf("iPad")>-1||userAgent.indexOf("iPhone")>-1})();class ScrollDisabler extends React.Component{componentDidMount(){if(ScrollDisabler.numModalsOpened===0){const body=document.body;if(!body){throw new Error("couldn't find document.body")}ScrollDisabler.oldOverflow=body.style.overflow;ScrollDisabler.oldScrollY=window.scrollY;if(needsHackyMobileSafariScrollDisabler){ScrollDisabler.oldPosition=body.style.position;ScrollDisabler.oldWidth=body.style.width;ScrollDisabler.oldTop=body.style.top;}body.style.overflow="hidden";if(needsHackyMobileSafariScrollDisabler){body.style.position="fixed";body.style.width="100%";body.style.top=`${-ScrollDisabler.oldScrollY}px`;}}ScrollDisabler.numModalsOpened++;}componentWillUnmount(){ScrollDisabler.numModalsOpened--;if(ScrollDisabler.numModalsOpened===0){const body=document.body;if(!body){throw new Error("couldn't find document.body")}body.style.overflow=ScrollDisabler.oldOverflow;if(needsHackyMobileSafariScrollDisabler){body.style.position=ScrollDisabler.oldPosition;body.style.width=ScrollDisabler.oldWidth;body.style.top=ScrollDisabler.oldTop;}if(typeof window!=="undefined"&&window.scrollTo){window.scrollTo(0,ScrollDisabler.oldScrollY);}}}render(){return null}}ScrollDisabler.numModalsOpened=0;
472
36
 
473
- const defaultContext = {
474
- closeModal: undefined
475
- };
476
- const ModalContext = React.createContext(defaultContext);
477
- ModalContext.displayName = "ModalContext";
37
+ const defaultContext={closeModal:undefined};const ModalContext=React.createContext(defaultContext);ModalContext.displayName="ModalContext";
478
38
 
479
- class ModalLauncher extends React.Component {
480
- constructor(...args) {
481
- super(...args);
482
- this.lastElementFocusedOutsideModal = void 0;
483
- this.state = {
484
- opened: false
485
- };
486
- this._saveLastElementFocused = () => {
487
- this.lastElementFocusedOutsideModal = document.activeElement;
488
- };
489
- this._openModal = () => {
490
- this._saveLastElementFocused();
491
- this.setState({
492
- opened: true
493
- });
494
- };
495
- this._returnFocus = () => {
496
- const {
497
- closedFocusId,
498
- schedule
499
- } = this.props;
500
- const lastElement = this.lastElementFocusedOutsideModal;
501
- if (closedFocusId) {
502
- const focusElement = ReactDOM.findDOMNode(document.getElementById(closedFocusId));
503
- if (focusElement) {
504
- schedule.animationFrame(() => {
505
- focusElement.focus();
506
- });
507
- return;
508
- }
509
- }
510
- if (lastElement != null) {
511
- schedule.animationFrame(() => {
512
- lastElement.focus();
513
- });
514
- }
515
- };
516
- this.handleCloseModal = () => {
517
- this.setState({
518
- opened: false
519
- }, () => {
520
- const {
521
- onClose
522
- } = this.props;
523
- onClose == null || onClose();
524
- this._returnFocus();
525
- });
526
- };
527
- }
528
- static getDerivedStateFromProps(props, state) {
529
- if (typeof props.opened === "boolean" && props.children) {
530
- console.warn("'children' and 'opened' can't be used together");
531
- }
532
- if (typeof props.opened === "boolean" && !props.onClose) {
533
- console.warn("'onClose' should be used with 'opened'");
534
- }
535
- if (typeof props.opened !== "boolean" && !props.children) {
536
- console.warn("either 'children' or 'opened' must be set");
537
- }
538
- return {
539
- opened: typeof props.opened === "boolean" ? props.opened : state.opened
540
- };
541
- }
542
- componentDidUpdate(prevProps) {
543
- if (!prevProps.opened && this.props.opened) {
544
- this._saveLastElementFocused();
545
- }
546
- }
547
- _renderModal() {
548
- if (typeof this.props.modal === "function") {
549
- return this.props.modal({
550
- closeModal: this.handleCloseModal
551
- });
552
- } else {
553
- return this.props.modal;
554
- }
555
- }
556
- render() {
557
- const renderedChildren = this.props.children ? this.props.children({
558
- openModal: this._openModal
559
- }) : null;
560
- const {
561
- body
562
- } = document;
563
- if (!body) {
564
- return null;
565
- }
566
- return React.createElement(ModalContext.Provider, {
567
- value: {
568
- closeModal: this.handleCloseModal
569
- }
570
- }, renderedChildren, this.state.opened && ReactDOM.createPortal(React.createElement(FocusTrap, {
571
- style: styles.container
572
- }, React.createElement(ModalBackdrop$1, {
573
- initialFocusId: this.props.initialFocusId,
574
- testId: this.props.testId,
575
- onCloseModal: this.props.backdropDismissEnabled ? this.handleCloseModal : () => {}
576
- }, this._renderModal())), body), this.state.opened && React.createElement(ModalLauncherKeypressListener, {
577
- onClose: this.handleCloseModal
578
- }), this.state.opened && React.createElement(ScrollDisabler, null));
579
- }
580
- }
581
- ModalLauncher.defaultProps = {
582
- backdropDismissEnabled: true
583
- };
584
- class ModalLauncherKeypressListener extends React.Component {
585
- constructor(...args) {
586
- super(...args);
587
- this._handleKeyup = e => {
588
- if (e.key === "Escape") {
589
- e.preventDefault();
590
- e.stopPropagation();
591
- this.props.onClose();
592
- }
593
- };
594
- }
595
- componentDidMount() {
596
- window.addEventListener("keyup", this._handleKeyup);
597
- }
598
- componentWillUnmount() {
599
- window.removeEventListener("keyup", this._handleKeyup);
600
- }
601
- render() {
602
- return null;
603
- }
604
- }
605
- const styles = StyleSheet.create({
606
- container: {
607
- zIndex: 1080
608
- }
609
- });
610
- var modalLauncher = withActionScheduler(ModalLauncher);
39
+ class ModalLauncher extends React.Component{static getDerivedStateFromProps(props,state){if(typeof props.opened==="boolean"&&props.children){console.warn("'children' and 'opened' can't be used together");}if(typeof props.opened==="boolean"&&!props.onClose){console.warn("'onClose' should be used with 'opened'");}if(typeof props.opened!=="boolean"&&!props.children){console.warn("either 'children' or 'opened' must be set");}return {opened:typeof props.opened==="boolean"?props.opened:state.opened}}componentDidUpdate(prevProps){if(!prevProps.opened&&this.props.opened){this._saveLastElementFocused();}}_renderModal(){if(typeof this.props.modal==="function"){return this.props.modal({closeModal:this.handleCloseModal})}else {return this.props.modal}}render(){const renderedChildren=this.props.children?this.props.children({openModal:this._openModal}):null;const{body}=document;if(!body){return null}return jsxs(ModalContext.Provider,{value:{closeModal:this.handleCloseModal},children:[renderedChildren,this.state.opened&&ReactDOM.createPortal(jsx(FocusTrap,{style:styles.container,children:jsx(ModalBackdrop$1,{initialFocusId:this.props.initialFocusId,testId:this.props.testId,onCloseModal:this.props.backdropDismissEnabled?this.handleCloseModal:()=>{},children:this._renderModal()})}),body),this.state.opened&&jsx(ModalLauncherKeypressListener,{onClose:this.handleCloseModal}),this.state.opened&&jsx(ScrollDisabler,{})]})}constructor(...args){super(...args),this.state={opened:false},this._saveLastElementFocused=()=>{this.lastElementFocusedOutsideModal=document.activeElement;},this._openModal=()=>{this._saveLastElementFocused();this.setState({opened:true});},this._returnFocus=()=>{const{closedFocusId,schedule}=this.props;const lastElement=this.lastElementFocusedOutsideModal;if(closedFocusId){const focusElement=ReactDOM.findDOMNode(document.getElementById(closedFocusId));if(focusElement){schedule.animationFrame(()=>{focusElement.focus();});return}}if(lastElement!=null){schedule.animationFrame(()=>{lastElement.focus();});}},this.handleCloseModal=()=>{this.setState({opened:false},()=>{const{onClose}=this.props;onClose?.();this._returnFocus();});};}}ModalLauncher.defaultProps={backdropDismissEnabled:true};class ModalLauncherKeypressListener extends React.Component{componentDidMount(){window.addEventListener("keyup",this._handleKeyup);}componentWillUnmount(){window.removeEventListener("keyup",this._handleKeyup);}render(){return null}constructor(...args){super(...args),this._handleKeyup=e=>{if(e.key==="Escape"){e.preventDefault();e.stopPropagation();this.props.onClose();}};}}const styles=StyleSheet.create({container:{zIndex:1080}});var modalLauncher = withActionScheduler(ModalLauncher);
611
40
 
612
- function ModalContent(props) {
613
- const {
614
- scrollOverflow,
615
- style,
616
- children
617
- } = props;
618
- const {
619
- theme
620
- } = useScopedTheme(ModalDialogThemeContext);
621
- const styles = useStyles(themedStylesFn$1, theme);
622
- return React.createElement(View, {
623
- style: [styles.wrapper, scrollOverflow && styles.scrollOverflow]
624
- }, React.createElement(View, {
625
- style: [styles.content, style]
626
- }, children));
627
- }
628
- ModalContent.__IS_MODAL_CONTENT__ = true;
629
- ModalContent.isComponentOf = instance => {
630
- return instance && instance.type && instance.type.__IS_MODAL_CONTENT__;
631
- };
632
- const small = "@media (max-width: 767px)";
633
- const themedStylesFn$1 = theme => ({
634
- wrapper: {
635
- flex: 1,
636
- display: "block"
637
- },
638
- scrollOverflow: {
639
- overflow: "auto"
640
- },
641
- content: {
642
- flex: 1,
643
- minHeight: "100%",
644
- padding: spacing.xLarge_32,
645
- boxSizing: "border-box",
646
- [small]: {
647
- padding: `${spacing.xLarge_32}px ${spacing.medium_16}px`
648
- }
649
- }
650
- });
651
- ModalContent.defaultProps = {
652
- scrollOverflow: true
653
- };
41
+ function ModalContent(props){const{scrollOverflow,style,children}=props;const{theme}=useScopedTheme(ModalDialogThemeContext);const styles=useStyles(themedStylesFn$1,theme);return jsx(View,{style:[styles.wrapper,scrollOverflow&&styles.scrollOverflow],children:jsx(View,{style:[styles.content,style],children:children})})}ModalContent.__IS_MODAL_CONTENT__=true;ModalContent.isComponentOf=instance=>{return instance&&instance.type&&instance.type.__IS_MODAL_CONTENT__};const small="@media (max-width: 767px)";const themedStylesFn$1=theme=>({wrapper:{flex:1,display:"block"},scrollOverflow:{overflow:"auto"},content:{flex:1,minHeight:"100%",padding:spacing.xLarge_32,boxSizing:"border-box",[small]:{padding:`${spacing.xLarge_32}px ${spacing.medium_16}px`}}});ModalContent.defaultProps={scrollOverflow:true};
654
42
 
655
- class CloseButton extends React.Component {
656
- render() {
657
- const {
658
- onClick,
659
- style,
660
- testId
661
- } = this.props;
662
- return React.createElement(ModalContext.Consumer, null, ({
663
- closeModal
664
- }) => {
665
- if (closeModal && onClick) {
666
- throw new Error("You've specified 'onClose' on a modal when using ModalLauncher. Please specify 'onClose' on the ModalLauncher instead");
667
- }
668
- return React.createElement(IconButton, {
669
- icon: xIcon,
670
- "aria-label": "Close modal",
671
- onClick: onClick || closeModal,
672
- kind: "tertiary",
673
- actionType: "neutral",
674
- style: style,
675
- testId: testId
676
- });
677
- });
678
- }
679
- }
43
+ class CloseButton extends React.Component{render(){const{onClick,style,testId}=this.props;return jsx(ModalContext.Consumer,{children:({closeModal})=>{if(closeModal&&onClick){throw new Error("You've specified 'onClose' on a modal when using ModalLauncher. Please specify 'onClose' on the ModalLauncher instead")}return jsx(IconButton,{icon:xIcon,"aria-label":"Close modal",onClick:onClick||closeModal,kind:"tertiary",actionType:"neutral",style:style,testId:testId})}})}}
680
44
 
681
- function ModalPanel({
682
- closeButtonVisible = true,
683
- scrollOverflow = true,
684
- light = true,
685
- content,
686
- footer,
687
- header,
688
- onClose,
689
- style,
690
- testId
691
- }) {
692
- const {
693
- theme
694
- } = useScopedTheme(ModalDialogThemeContext);
695
- const styles = useStyles(themedStylesFn, theme);
696
- const renderMainContent = React.useCallback(() => {
697
- const mainContent = ModalContent.isComponentOf(content) ? content : React.createElement(ModalContent, null, content);
698
- if (!mainContent) {
699
- return mainContent;
700
- }
701
- return React.cloneElement(mainContent, {
702
- scrollOverflow,
703
- style: [!!footer && styles.hasFooter, mainContent.props.style]
704
- });
705
- }, [content, footer, scrollOverflow, styles.hasFooter]);
706
- const mainContent = renderMainContent();
707
- const isInverse = !light;
708
- return React.createElement(View, {
709
- style: [styles.wrapper, isInverse && styles.dark, style],
710
- testId: testId && `${testId}-panel`
711
- }, closeButtonVisible && React.createElement(CloseButton, {
712
- onClick: onClose,
713
- style: [styles.closeButton, isInverse && actionStyles.inverse],
714
- testId: testId && `${testId}-close`
715
- }), header, mainContent, !footer || ModalFooter.isComponentOf(footer) ? footer : React.createElement(ModalFooter, null, footer));
716
- }
717
- ModalPanel.defaultProps = {
718
- closeButtonVisible: true,
719
- scrollOverflow: true,
720
- light: true
721
- };
722
- const themedStylesFn = theme => ({
723
- wrapper: {
724
- flex: "1 1 auto",
725
- position: "relative",
726
- display: "flex",
727
- flexDirection: "column",
728
- background: "white",
729
- boxSizing: "border-box",
730
- overflow: "hidden",
731
- height: "100%",
732
- width: "100%"
733
- },
734
- closeButton: {
735
- position: "absolute",
736
- right: theme.closeButton.spacing.gap,
737
- top: theme.closeButton.spacing.gap,
738
- zIndex: 1,
739
- ":focus": _extends({}, focusStyles.focus[":focus-visible"])
740
- },
741
- dark: {
742
- background: theme.root.color.inverse.background,
743
- color: theme.root.color.inverse.foreground
744
- },
745
- hasFooter: {
746
- paddingBlockEnd: theme.panel.spacing.gap
747
- }
748
- });
45
+ function ModalPanel({closeButtonVisible=true,scrollOverflow=true,light=true,content,footer,header,onClose,style,testId}){const{theme}=useScopedTheme(ModalDialogThemeContext);const styles=useStyles(themedStylesFn,theme);const renderMainContent=React.useCallback(()=>{const mainContent=ModalContent.isComponentOf(content)?content:jsx(ModalContent,{children:content});if(!mainContent){return mainContent}return React.cloneElement(mainContent,{scrollOverflow,style:[!!footer&&styles.hasFooter,mainContent.props.style]})},[content,footer,scrollOverflow,styles.hasFooter]);const mainContent=renderMainContent();const isInverse=!light;return jsxs(View,{style:[styles.wrapper,isInverse&&styles.dark,style],testId:testId&&`${testId}-panel`,children:[closeButtonVisible&&jsx(CloseButton,{onClick:onClose,style:[styles.closeButton,isInverse&&actionStyles.inverse],testId:testId&&`${testId}-close`}),header,mainContent,!footer||ModalFooter.isComponentOf(footer)?footer:jsx(ModalFooter,{children:footer})]})}ModalPanel.defaultProps={closeButtonVisible:true,scrollOverflow:true,light:true};const themedStylesFn=theme=>({wrapper:{flex:"1 1 auto",position:"relative",display:"flex",flexDirection:"column",background:"white",boxSizing:"border-box",overflow:"hidden",height:"100%",width:"100%"},closeButton:{position:"absolute",right:theme.closeButton.spacing.gap,top:theme.closeButton.spacing.gap,zIndex:1,":focus":{...focusStyles.focus[":focus-visible"]}},dark:{background:theme.root.color.inverse.background,color:theme.root.color.inverse.foreground},hasFooter:{paddingBlockEnd:theme.panel.spacing.gap}});
749
46
 
750
- class OnePaneDialog extends React.Component {
751
- renderHeader(uniqueId) {
752
- const {
753
- title,
754
- breadcrumbs = undefined,
755
- subtitle = undefined,
756
- testId
757
- } = this.props;
758
- if (breadcrumbs) {
759
- return React.createElement(ModalHeader, {
760
- title: title,
761
- breadcrumbs: breadcrumbs,
762
- titleId: uniqueId,
763
- testId: testId && `${testId}-header`
764
- });
765
- } else if (subtitle) {
766
- return React.createElement(ModalHeader, {
767
- title: title,
768
- subtitle: subtitle,
769
- titleId: uniqueId,
770
- testId: testId && `${testId}-header`
771
- });
772
- } else {
773
- return React.createElement(ModalHeader, {
774
- title: title,
775
- titleId: uniqueId,
776
- testId: testId && `${testId}-header`
777
- });
778
- }
779
- }
780
- render() {
781
- const {
782
- onClose,
783
- footer,
784
- content,
785
- above,
786
- below,
787
- style,
788
- closeButtonVisible,
789
- testId,
790
- titleId,
791
- role,
792
- "aria-describedby": ariaDescribedBy
793
- } = this.props;
794
- return React.createElement(MediaLayout, {
795
- styleSheets: styleSheets
796
- }, ({
797
- styles
798
- }) => React.createElement(Id, {
799
- id: titleId
800
- }, uniqueId => React.createElement(ModalDialog, {
801
- style: [styles.dialog, style],
802
- above: above,
803
- below: below,
804
- testId: testId,
805
- "aria-labelledby": uniqueId,
806
- "aria-describedby": ariaDescribedBy,
807
- role: role
808
- }, React.createElement(ModalPanel, {
809
- onClose: onClose,
810
- header: this.renderHeader(uniqueId),
811
- content: content,
812
- footer: footer,
813
- closeButtonVisible: closeButtonVisible,
814
- testId: testId
815
- }))));
816
- }
817
- }
818
- OnePaneDialog.defaultProps = {
819
- closeButtonVisible: true
820
- };
821
- const styleSheets = {
822
- small: StyleSheet.create({
823
- dialog: {
824
- width: "100%",
825
- height: "100%",
826
- overflow: "hidden"
827
- }
828
- }),
829
- mdOrLarger: StyleSheet.create({
830
- dialog: {
831
- width: "93.75%",
832
- maxWidth: 576,
833
- height: "81.25%",
834
- maxHeight: 624
835
- }
836
- })
837
- };
47
+ class OnePaneDialog extends React.Component{renderHeader(uniqueId){const{title,breadcrumbs=undefined,subtitle=undefined,testId}=this.props;if(breadcrumbs){return jsx(ModalHeader,{title:title,breadcrumbs:breadcrumbs,titleId:uniqueId,testId:testId&&`${testId}-header`})}else if(subtitle){return jsx(ModalHeader,{title:title,subtitle:subtitle,titleId:uniqueId,testId:testId&&`${testId}-header`})}else {return jsx(ModalHeader,{title:title,titleId:uniqueId,testId:testId&&`${testId}-header`})}}render(){const{onClose,footer,content,above,below,style,closeButtonVisible,testId,titleId,role,"aria-describedby":ariaDescribedBy}=this.props;return jsx(MediaLayout,{styleSheets:styleSheets,children:({styles})=>jsx(Id,{id:titleId,children:uniqueId=>jsx(ModalDialog,{style:[styles.dialog,style],above:above,below:below,testId:testId,"aria-labelledby":uniqueId,"aria-describedby":ariaDescribedBy,role:role,children:jsx(ModalPanel,{onClose:onClose,header:this.renderHeader(uniqueId),content:content,footer:footer,closeButtonVisible:closeButtonVisible,testId:testId})})})})}}OnePaneDialog.defaultProps={closeButtonVisible:true};const styleSheets={small:StyleSheet.create({dialog:{width:"100%",height:"100%",overflow:"hidden"}}),mdOrLarger:StyleSheet.create({dialog:{width:"93.75%",maxWidth:576,height:"81.25%",maxHeight:624}})};
838
48
 
839
- function maybeGetNextAncestorModalLauncherPortal(element) {
840
- let candidateElement = element && element.parentElement;
841
- while (candidateElement && !candidateElement.hasAttribute(ModalLauncherPortalAttributeName)) {
842
- candidateElement = candidateElement.parentElement;
843
- }
844
- return candidateElement;
845
- }
846
- function maybeGetPortalMountedModalHostElement(element) {
847
- return maybeGetNextAncestorModalLauncherPortal(element);
848
- }
49
+ function maybeGetNextAncestorModalLauncherPortal(element){let candidateElement=element&&element.parentElement;while(candidateElement&&!candidateElement.hasAttribute(ModalLauncherPortalAttributeName)){candidateElement=candidateElement.parentElement;}return candidateElement}function maybeGetPortalMountedModalHostElement(element){return maybeGetNextAncestorModalLauncherPortal(element)}
849
50
 
850
51
  export { ModalDialog, ModalFooter, ModalHeader, modalLauncher as ModalLauncher, ModalPanel, OnePaneDialog, maybeGetPortalMountedModalHostElement };