@khanacademy/wonder-blocks-modal 7.1.11 → 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/CHANGELOG.md +24 -0
- package/dist/es/index.js +19 -818
- package/dist/index.js +19 -819
- package/package.json +8 -9
package/dist/es/index.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import
|
|
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
|
|
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
|
|
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
|
|
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
|
|
29
|
+
const ModalLauncherPortalAttributeName="data-modal-launcher-portal";
|
|
332
30
|
|
|
333
|
-
const FOCUSABLE_ELEMENTS
|
|
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 };
|