@instructure/ui-modal 11.6.0 → 11.6.1-snapshot-129

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 (184) hide show
  1. package/CHANGELOG.md +49 -311
  2. package/es/Modal/{ModalBody → v1/ModalBody}/index.js +2 -2
  3. package/es/Modal/{ModalFooter → v1/ModalFooter}/index.js +1 -1
  4. package/es/Modal/{ModalHeader → v1/ModalHeader}/index.js +2 -2
  5. package/es/Modal/{index.js → v1/index.js} +2 -2
  6. package/es/Modal/v2/ModalBody/index.js +132 -0
  7. package/es/Modal/v2/ModalBody/props.js +26 -0
  8. package/es/Modal/v2/ModalBody/styles.js +68 -0
  9. package/es/Modal/v2/ModalContext.js +34 -0
  10. package/es/Modal/v2/ModalFooter/index.js +73 -0
  11. package/es/{index.js → Modal/v2/ModalFooter/props.js} +3 -1
  12. package/es/Modal/v2/ModalFooter/styles.js +60 -0
  13. package/es/Modal/v2/ModalHeader/index.js +112 -0
  14. package/es/Modal/v2/ModalHeader/props.js +26 -0
  15. package/es/Modal/v2/ModalHeader/styles.js +76 -0
  16. package/es/Modal/v2/index.js +259 -0
  17. package/es/Modal/v2/props.js +26 -0
  18. package/es/Modal/v2/styles.js +119 -0
  19. package/es/exports/a.js +24 -0
  20. package/es/exports/b.js +24 -0
  21. package/lib/Modal/{ModalBody → v1/ModalBody}/index.js +4 -4
  22. package/lib/Modal/{ModalFooter → v1/ModalFooter}/index.js +1 -1
  23. package/lib/Modal/{ModalHeader → v1/ModalHeader}/index.js +3 -3
  24. package/lib/Modal/v1/index.js +285 -0
  25. package/lib/Modal/v2/ModalBody/index.js +137 -0
  26. package/lib/Modal/v2/ModalBody/props.js +31 -0
  27. package/lib/Modal/v2/ModalBody/styles.js +74 -0
  28. package/lib/Modal/v2/ModalContext.js +39 -0
  29. package/lib/Modal/v2/ModalFooter/index.js +78 -0
  30. package/lib/Modal/v2/ModalFooter/props.js +31 -0
  31. package/lib/Modal/v2/ModalFooter/styles.js +66 -0
  32. package/lib/Modal/v2/ModalHeader/index.js +118 -0
  33. package/lib/Modal/v2/ModalHeader/props.js +31 -0
  34. package/lib/Modal/v2/ModalHeader/styles.js +82 -0
  35. package/lib/Modal/{index.js → v2/index.js} +3 -4
  36. package/lib/Modal/v2/props.js +31 -0
  37. package/lib/Modal/v2/styles.js +125 -0
  38. package/lib/{index.js → exports/a.js} +5 -5
  39. package/lib/exports/b.js +30 -0
  40. package/package.json +46 -24
  41. package/src/Modal/{ModalBody → v1/ModalBody}/index.tsx +2 -2
  42. package/src/Modal/{ModalFooter → v1/ModalFooter}/index.tsx +1 -1
  43. package/src/Modal/{ModalHeader → v1/ModalHeader}/index.tsx +3 -3
  44. package/src/Modal/{index.tsx → v1/index.tsx} +2 -2
  45. package/src/Modal/v2/ModalBody/index.tsx +164 -0
  46. package/src/Modal/v2/ModalBody/props.ts +72 -0
  47. package/src/Modal/v2/ModalBody/styles.ts +76 -0
  48. package/src/Modal/v2/ModalContext.ts +46 -0
  49. package/src/Modal/v2/ModalFooter/index.tsx +80 -0
  50. package/src/Modal/v2/ModalFooter/props.ts +50 -0
  51. package/src/Modal/v2/ModalFooter/styles.ts +74 -0
  52. package/src/Modal/v2/ModalHeader/index.tsx +138 -0
  53. package/src/Modal/v2/ModalHeader/props.ts +54 -0
  54. package/src/Modal/v2/ModalHeader/styles.ts +99 -0
  55. package/src/Modal/v2/README.md +673 -0
  56. package/src/Modal/v2/index.tsx +326 -0
  57. package/src/Modal/v2/props.ts +235 -0
  58. package/src/Modal/v2/styles.ts +130 -0
  59. package/src/{index.ts → exports/a.ts} +5 -5
  60. package/src/exports/b.ts +29 -0
  61. package/tsconfig.build.tsbuildinfo +1 -1
  62. package/types/Modal/v1/ModalBody/index.d.ts.map +1 -0
  63. package/types/Modal/v1/ModalBody/props.d.ts.map +1 -0
  64. package/types/Modal/v1/ModalBody/styles.d.ts.map +1 -0
  65. package/types/Modal/v1/ModalBody/theme.d.ts.map +1 -0
  66. package/types/Modal/v1/ModalContext.d.ts.map +1 -0
  67. package/types/Modal/v1/ModalFooter/index.d.ts.map +1 -0
  68. package/types/Modal/v1/ModalFooter/props.d.ts.map +1 -0
  69. package/types/Modal/v1/ModalFooter/styles.d.ts.map +1 -0
  70. package/types/Modal/v1/ModalFooter/theme.d.ts.map +1 -0
  71. package/types/Modal/v1/ModalHeader/index.d.ts.map +1 -0
  72. package/types/Modal/v1/ModalHeader/props.d.ts.map +1 -0
  73. package/types/Modal/v1/ModalHeader/styles.d.ts.map +1 -0
  74. package/types/Modal/v1/ModalHeader/theme.d.ts.map +1 -0
  75. package/types/Modal/v1/index.d.ts.map +1 -0
  76. package/types/Modal/v1/props.d.ts.map +1 -0
  77. package/types/Modal/v1/styles.d.ts.map +1 -0
  78. package/types/Modal/v1/theme.d.ts.map +1 -0
  79. package/types/Modal/v2/ModalBody/index.d.ts +36 -0
  80. package/types/Modal/v2/ModalBody/index.d.ts.map +1 -0
  81. package/types/Modal/v2/ModalBody/props.d.ts +23 -0
  82. package/types/Modal/v2/ModalBody/props.d.ts.map +1 -0
  83. package/types/Modal/v2/ModalBody/styles.d.ts +16 -0
  84. package/types/Modal/v2/ModalBody/styles.d.ts.map +1 -0
  85. package/types/Modal/v2/ModalContext.d.ts +17 -0
  86. package/types/Modal/v2/ModalContext.d.ts.map +1 -0
  87. package/types/Modal/v2/ModalFooter/index.d.ts +27 -0
  88. package/types/Modal/v2/ModalFooter/index.d.ts.map +1 -0
  89. package/types/Modal/v2/ModalFooter/props.d.ts +16 -0
  90. package/types/Modal/v2/ModalFooter/props.d.ts.map +1 -0
  91. package/types/Modal/v2/ModalFooter/styles.d.ts +16 -0
  92. package/types/Modal/v2/ModalFooter/styles.d.ts.map +1 -0
  93. package/types/Modal/v2/ModalHeader/index.d.ts +37 -0
  94. package/types/Modal/v2/ModalHeader/index.d.ts.map +1 -0
  95. package/types/Modal/v2/ModalHeader/props.d.ts +19 -0
  96. package/types/Modal/v2/ModalHeader/props.d.ts.map +1 -0
  97. package/types/Modal/v2/ModalHeader/styles.d.ts +16 -0
  98. package/types/Modal/v2/ModalHeader/styles.d.ts.map +1 -0
  99. package/types/Modal/v2/index.d.ts +54 -0
  100. package/types/Modal/v2/index.d.ts.map +1 -0
  101. package/types/Modal/v2/props.d.ts +132 -0
  102. package/types/Modal/v2/props.d.ts.map +1 -0
  103. package/types/Modal/v2/styles.d.ts +16 -0
  104. package/types/Modal/v2/styles.d.ts.map +1 -0
  105. package/types/exports/a.d.ts +6 -0
  106. package/types/exports/a.d.ts.map +1 -0
  107. package/types/exports/b.d.ts +6 -0
  108. package/types/exports/b.d.ts.map +1 -0
  109. package/types/Modal/ModalBody/index.d.ts.map +0 -1
  110. package/types/Modal/ModalBody/props.d.ts.map +0 -1
  111. package/types/Modal/ModalBody/styles.d.ts.map +0 -1
  112. package/types/Modal/ModalBody/theme.d.ts.map +0 -1
  113. package/types/Modal/ModalContext.d.ts.map +0 -1
  114. package/types/Modal/ModalFooter/index.d.ts.map +0 -1
  115. package/types/Modal/ModalFooter/props.d.ts.map +0 -1
  116. package/types/Modal/ModalFooter/styles.d.ts.map +0 -1
  117. package/types/Modal/ModalFooter/theme.d.ts.map +0 -1
  118. package/types/Modal/ModalHeader/index.d.ts.map +0 -1
  119. package/types/Modal/ModalHeader/props.d.ts.map +0 -1
  120. package/types/Modal/ModalHeader/styles.d.ts.map +0 -1
  121. package/types/Modal/ModalHeader/theme.d.ts.map +0 -1
  122. package/types/Modal/index.d.ts.map +0 -1
  123. package/types/Modal/props.d.ts.map +0 -1
  124. package/types/Modal/styles.d.ts.map +0 -1
  125. package/types/Modal/theme.d.ts.map +0 -1
  126. package/types/index.d.ts +0 -6
  127. package/types/index.d.ts.map +0 -1
  128. /package/es/Modal/{ModalBody → v1/ModalBody}/props.js +0 -0
  129. /package/es/Modal/{ModalBody → v1/ModalBody}/styles.js +0 -0
  130. /package/es/Modal/{ModalBody → v1/ModalBody}/theme.js +0 -0
  131. /package/es/Modal/{ModalContext.js → v1/ModalContext.js} +0 -0
  132. /package/es/Modal/{ModalFooter → v1/ModalFooter}/props.js +0 -0
  133. /package/es/Modal/{ModalFooter → v1/ModalFooter}/styles.js +0 -0
  134. /package/es/Modal/{ModalFooter → v1/ModalFooter}/theme.js +0 -0
  135. /package/es/Modal/{ModalHeader → v1/ModalHeader}/props.js +0 -0
  136. /package/es/Modal/{ModalHeader → v1/ModalHeader}/styles.js +0 -0
  137. /package/es/Modal/{ModalHeader → v1/ModalHeader}/theme.js +0 -0
  138. /package/es/Modal/{props.js → v1/props.js} +0 -0
  139. /package/es/Modal/{styles.js → v1/styles.js} +0 -0
  140. /package/es/Modal/{theme.js → v1/theme.js} +0 -0
  141. /package/lib/Modal/{ModalBody → v1/ModalBody}/props.js +0 -0
  142. /package/lib/Modal/{ModalBody → v1/ModalBody}/styles.js +0 -0
  143. /package/lib/Modal/{ModalBody → v1/ModalBody}/theme.js +0 -0
  144. /package/lib/Modal/{ModalContext.js → v1/ModalContext.js} +0 -0
  145. /package/lib/Modal/{ModalFooter → v1/ModalFooter}/props.js +0 -0
  146. /package/lib/Modal/{ModalFooter → v1/ModalFooter}/styles.js +0 -0
  147. /package/lib/Modal/{ModalFooter → v1/ModalFooter}/theme.js +0 -0
  148. /package/lib/Modal/{ModalHeader → v1/ModalHeader}/props.js +0 -0
  149. /package/lib/Modal/{ModalHeader → v1/ModalHeader}/styles.js +0 -0
  150. /package/lib/Modal/{ModalHeader → v1/ModalHeader}/theme.js +0 -0
  151. /package/lib/Modal/{props.js → v1/props.js} +0 -0
  152. /package/lib/Modal/{styles.js → v1/styles.js} +0 -0
  153. /package/lib/Modal/{theme.js → v1/theme.js} +0 -0
  154. /package/src/Modal/{ModalBody → v1/ModalBody}/props.ts +0 -0
  155. /package/src/Modal/{ModalBody → v1/ModalBody}/styles.ts +0 -0
  156. /package/src/Modal/{ModalBody → v1/ModalBody}/theme.ts +0 -0
  157. /package/src/Modal/{ModalContext.ts → v1/ModalContext.ts} +0 -0
  158. /package/src/Modal/{ModalFooter → v1/ModalFooter}/props.ts +0 -0
  159. /package/src/Modal/{ModalFooter → v1/ModalFooter}/styles.ts +0 -0
  160. /package/src/Modal/{ModalFooter → v1/ModalFooter}/theme.ts +0 -0
  161. /package/src/Modal/{ModalHeader → v1/ModalHeader}/props.ts +0 -0
  162. /package/src/Modal/{ModalHeader → v1/ModalHeader}/styles.ts +0 -0
  163. /package/src/Modal/{ModalHeader → v1/ModalHeader}/theme.ts +0 -0
  164. /package/src/Modal/{README.md → v1/README.md} +0 -0
  165. /package/src/Modal/{props.ts → v1/props.ts} +0 -0
  166. /package/src/Modal/{styles.ts → v1/styles.ts} +0 -0
  167. /package/src/Modal/{theme.ts → v1/theme.ts} +0 -0
  168. /package/types/Modal/{ModalBody → v1/ModalBody}/index.d.ts +0 -0
  169. /package/types/Modal/{ModalBody → v1/ModalBody}/props.d.ts +0 -0
  170. /package/types/Modal/{ModalBody → v1/ModalBody}/styles.d.ts +0 -0
  171. /package/types/Modal/{ModalBody → v1/ModalBody}/theme.d.ts +0 -0
  172. /package/types/Modal/{ModalContext.d.ts → v1/ModalContext.d.ts} +0 -0
  173. /package/types/Modal/{ModalFooter → v1/ModalFooter}/index.d.ts +0 -0
  174. /package/types/Modal/{ModalFooter → v1/ModalFooter}/props.d.ts +0 -0
  175. /package/types/Modal/{ModalFooter → v1/ModalFooter}/styles.d.ts +0 -0
  176. /package/types/Modal/{ModalFooter → v1/ModalFooter}/theme.d.ts +0 -0
  177. /package/types/Modal/{ModalHeader → v1/ModalHeader}/index.d.ts +0 -0
  178. /package/types/Modal/{ModalHeader → v1/ModalHeader}/props.d.ts +0 -0
  179. /package/types/Modal/{ModalHeader → v1/ModalHeader}/styles.d.ts +0 -0
  180. /package/types/Modal/{ModalHeader → v1/ModalHeader}/theme.d.ts +0 -0
  181. /package/types/Modal/{index.d.ts → v1/index.d.ts} +0 -0
  182. /package/types/Modal/{props.d.ts → v1/props.d.ts} +0 -0
  183. /package/types/Modal/{styles.d.ts → v1/styles.d.ts} +0 -0
  184. /package/types/Modal/{theme.d.ts → v1/theme.d.ts} +0 -0
@@ -0,0 +1,326 @@
1
+ /*
2
+ * The MIT License (MIT)
3
+ *
4
+ * Copyright (c) 2015 - present Instructure, Inc.
5
+ *
6
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
7
+ * of this software and associated documentation files (the "Software"), to deal
8
+ * in the Software without restriction, including without limitation the rights
9
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10
+ * copies of the Software, and to permit persons to whom the Software is
11
+ * furnished to do so, subject to the following conditions:
12
+ *
13
+ * The above copyright notice and this permission notice shall be included in all
14
+ * copies or substantial portions of the Software.
15
+ *
16
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22
+ * SOFTWARE.
23
+ */
24
+
25
+ import { Children, Component, isValidElement } from 'react'
26
+
27
+ import { passthroughProps, safeCloneElement } from '@instructure/ui-react-utils'
28
+ import { createChainedFunction } from '@instructure/ui-utils'
29
+
30
+ import { Transition } from '@instructure/ui-motion'
31
+ import { Portal } from '@instructure/ui-portal'
32
+ import type { PortalNode } from '@instructure/ui-portal'
33
+ import { Dialog } from '@instructure/ui-dialog'
34
+ import { Mask } from '@instructure/ui-overlays/latest'
35
+
36
+ import { ModalHeader } from './ModalHeader'
37
+ import { ModalBody } from './ModalBody'
38
+ import { ModalFooter } from './ModalFooter'
39
+
40
+ import { withStyle } from '@instructure/emotion'
41
+
42
+ import generateStyle from './styles'
43
+
44
+ import { allowedProps } from './props'
45
+ import type {
46
+ ModalProps,
47
+ ModalState,
48
+ ModalPropsForPortal,
49
+ ModalPropsForTransition
50
+ } from './props'
51
+ import ModalContext from './ModalContext'
52
+
53
+ /**
54
+ ---
55
+ category: components
56
+ tags: overlay, portal, dialog
57
+ ---
58
+ **/
59
+ @withStyle(generateStyle)
60
+ class Modal extends Component<ModalProps, ModalState> {
61
+ static readonly componentId = 'Modal'
62
+
63
+ static allowedProps = allowedProps
64
+ static defaultProps = {
65
+ open: false,
66
+ size: 'auto',
67
+ variant: 'default',
68
+ transition: 'fade',
69
+ insertAt: 'bottom',
70
+ shouldCloseOnDocumentClick: true,
71
+ shouldReturnFocus: true,
72
+ constrain: 'window',
73
+ overflow: 'scroll'
74
+ }
75
+
76
+ static Header = ModalHeader
77
+ static Body = ModalBody
78
+ static Footer = ModalFooter
79
+
80
+ constructor(props: ModalProps) {
81
+ super(props)
82
+
83
+ this.state = {
84
+ transitioning: false,
85
+ open: props.open ?? false,
86
+ windowHeight: 99999,
87
+ bodyScrollAriaLabel: undefined
88
+ }
89
+ }
90
+
91
+ _DOMNode: PortalNode = null
92
+ _content: Dialog | null = null
93
+
94
+ ref?: Element | null = null
95
+
96
+ handleRef = (el?: Element | null) => {
97
+ this.ref = el
98
+ }
99
+
100
+ componentDidMount() {
101
+ this.props.makeStyles?.()
102
+ window.addEventListener('resize', this.updateHeight)
103
+ }
104
+
105
+ componentDidUpdate(prevProps: ModalProps) {
106
+ if (this.props.open !== prevProps.open) {
107
+ this.setState({ transitioning: true, open: !!this.props.open })
108
+ }
109
+ this.props.makeStyles?.()
110
+ }
111
+
112
+ componentWillUnmount() {
113
+ window.removeEventListener('resize', this.updateHeight)
114
+ }
115
+
116
+ updateHeight = () => {
117
+ this.setState({ windowHeight: window.innerHeight })
118
+ }
119
+
120
+ get defaultFocusElement() {
121
+ return this.props.defaultFocusElement
122
+ }
123
+
124
+ get DOMNode() {
125
+ return this._DOMNode
126
+ }
127
+ set DOMNode(el: PortalNode) {
128
+ this._DOMNode = el
129
+ }
130
+
131
+ get maskPlacement() {
132
+ if (this.props.overflow === 'fit') {
133
+ return 'stretch'
134
+ } else {
135
+ return 'center'
136
+ }
137
+ }
138
+
139
+ handlePortalOpen = (DOMNode: PortalNode) => {
140
+ this.DOMNode = DOMNode
141
+ }
142
+
143
+ handleTransitionComplete: ModalProps['onEntered' | 'onExited'] = () => {
144
+ this.setState({
145
+ transitioning: false
146
+ })
147
+ }
148
+
149
+ contentRef = (el: Dialog | null) => {
150
+ this._content = el
151
+ if (typeof this.props.contentRef === 'function') {
152
+ this.props.contentRef(el)
153
+ }
154
+ }
155
+
156
+ renderChildren() {
157
+ const { children, variant, overflow, styles } = this.props
158
+ const childrenArray = Children.toArray(children)
159
+
160
+ const headerAndBody: React.ReactNode[] = []
161
+ const others: React.ReactNode[] = []
162
+
163
+ childrenArray.forEach((child) => {
164
+ if (isValidElement(child)) {
165
+ if (child.type === Modal.Header || child.type === Modal.Body) {
166
+ headerAndBody.push(this.cloneChildWithProps(child, variant, overflow))
167
+ } else {
168
+ others.push(this.cloneChildWithProps(child, variant, overflow))
169
+ }
170
+ }
171
+ })
172
+
173
+ // Putting ModalHeader and ModalBody into the same container, so they can be styled together;
174
+ // this is needed to apply a media query breakpoint
175
+ const wrappedHeaderAndBody =
176
+ headerAndBody.length > 0 ? (
177
+ <div key="header-and-body" css={styles?.joinedHeaderAndBody}>
178
+ {headerAndBody}
179
+ </div>
180
+ ) : null
181
+
182
+ return [...(wrappedHeaderAndBody ? [wrappedHeaderAndBody] : []), ...others]
183
+ }
184
+
185
+ cloneChildWithProps(
186
+ child: React.ReactNode,
187
+ variant: string | undefined,
188
+ overflow: string | undefined
189
+ ) {
190
+ if (isValidElement(child)) {
191
+ return safeCloneElement(child, {
192
+ variant: variant,
193
+ overflow: (child?.props as { overflow: string })?.overflow || overflow
194
+ })
195
+ } else {
196
+ return child
197
+ }
198
+ }
199
+
200
+ renderDialog(
201
+ props: Omit<
202
+ ModalProps,
203
+ | keyof ModalPropsForPortal
204
+ | keyof ModalPropsForTransition
205
+ | 'constrain'
206
+ | 'overflow'
207
+ >
208
+ ) {
209
+ const {
210
+ onDismiss,
211
+ label,
212
+ shouldCloseOnDocumentClick,
213
+ shouldReturnFocus,
214
+ liveRegion,
215
+ size,
216
+ constrain,
217
+ as,
218
+ styles
219
+ } = this.props
220
+
221
+ const isFullScreen = size === 'fullscreen'
222
+ const dialog = (
223
+ <Dialog
224
+ {...passthroughProps(props)}
225
+ as={as}
226
+ open
227
+ label={label}
228
+ defaultFocusElement={this.defaultFocusElement}
229
+ shouldCloseOnDocumentClick={shouldCloseOnDocumentClick}
230
+ shouldCloseOnEscape
231
+ shouldContainFocus
232
+ shouldReturnFocus={shouldReturnFocus}
233
+ liveRegion={liveRegion}
234
+ onDismiss={onDismiss}
235
+ css={styles?.modal}
236
+ ref={this.contentRef}
237
+ // aria-modal="true" see VO bug https://bugs.webkit.org/show_bug.cgi?id=174667
238
+ >
239
+ {this.renderChildren()}
240
+ </Dialog>
241
+ )
242
+
243
+ return (
244
+ <Mask
245
+ placement={this.maskPlacement}
246
+ fullscreen={constrain === 'window'}
247
+ themeOverride={
248
+ isFullScreen ? { borderRadius: '0em', borderWidth: '0em' } : {}
249
+ }
250
+ >
251
+ {dialog}
252
+ </Mask>
253
+ )
254
+ }
255
+
256
+ render() {
257
+ const {
258
+ open,
259
+ onOpen,
260
+ onClose,
261
+ mountNode,
262
+ insertAt,
263
+ transition,
264
+ onEnter,
265
+ onEntering,
266
+ onEntered,
267
+ onExit,
268
+ onExiting,
269
+ onExited,
270
+ constrain,
271
+ overflow, // TODO this is not used currently
272
+ ...passthroughProps
273
+ } = this.props
274
+
275
+ const portalIsOpen = this.state.open || this.state.transitioning
276
+
277
+ return (
278
+ <Portal
279
+ mountNode={mountNode}
280
+ insertAt={insertAt}
281
+ open={portalIsOpen}
282
+ onOpen={this.handlePortalOpen}
283
+ data-cid="Modal"
284
+ >
285
+ <Transition
286
+ in={open}
287
+ transitionOnMount
288
+ type={transition}
289
+ onEnter={onEnter}
290
+ onEntering={onEntering}
291
+ onEntered={createChainedFunction(
292
+ this.handleTransitionComplete,
293
+ onEntered,
294
+ onOpen
295
+ )}
296
+ onExit={onExit}
297
+ onExiting={onExiting}
298
+ onExited={createChainedFunction(
299
+ this.handleTransitionComplete,
300
+ onExited,
301
+ onClose
302
+ )}
303
+ >
304
+ <ModalContext.Provider
305
+ value={{
306
+ bodyScrollAriaLabel: this.state.bodyScrollAriaLabel,
307
+ setBodyScrollAriaLabel: (txt: string) =>
308
+ this.setState({ bodyScrollAriaLabel: txt })
309
+ }}
310
+ >
311
+ {constrain === 'parent' ? (
312
+ <span css={this.props.styles?.constrainContext}>
313
+ {this.renderDialog(passthroughProps)}
314
+ </span>
315
+ ) : (
316
+ this.renderDialog(passthroughProps)
317
+ )}
318
+ </ModalContext.Provider>
319
+ </Transition>
320
+ </Portal>
321
+ )
322
+ }
323
+ }
324
+
325
+ export default Modal
326
+ export { Modal, ModalHeader, ModalBody, ModalFooter }
@@ -0,0 +1,235 @@
1
+ /*
2
+ * The MIT License (MIT)
3
+ *
4
+ * Copyright (c) 2015 - present Instructure, Inc.
5
+ *
6
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
7
+ * of this software and associated documentation files (the "Software"), to deal
8
+ * in the Software without restriction, including without limitation the rights
9
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10
+ * copies of the Software, and to permit persons to whom the Software is
11
+ * furnished to do so, subject to the following conditions:
12
+ *
13
+ * The above copyright notice and this permission notice shall be included in all
14
+ * copies or substantial portions of the Software.
15
+ *
16
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22
+ * SOFTWARE.
23
+ */
24
+
25
+ import React from 'react'
26
+ import { Dialog } from '@instructure/ui-dialog'
27
+
28
+ import type {
29
+ AsElementType,
30
+ ModalTheme,
31
+ OtherHTMLAttributes,
32
+ LiveRegion,
33
+ UIElement
34
+ } from '@instructure/shared-types'
35
+ import type { PositionMountNode } from '@instructure/ui-position'
36
+ import type { TransitionType } from '@instructure/ui-motion'
37
+ import type { WithStyleProps, ComponentStyle } from '@instructure/emotion'
38
+
39
+ type ModalPropsForPortal = {
40
+ /**
41
+ * Whether or not the `<Modal />` is open
42
+ */
43
+ open?: boolean
44
+
45
+ /**
46
+ * An element or a function returning an element to use as the mount node
47
+ * for the `<Modal />` (defaults to `document.body`)
48
+ */
49
+ mountNode?: PositionMountNode
50
+
51
+ /**
52
+ * Insert the element at the 'top' of the mountNode or at the 'bottom'
53
+ */
54
+ insertAt?: 'bottom' | 'top'
55
+
56
+ /**
57
+ * Callback fired when `<Modal />` content has been mounted in the DOM
58
+ */
59
+ onOpen?: (type?: TransitionType) => void
60
+
61
+ /**
62
+ * Callback fired when `<Modal />` has been unmounted from the DOM
63
+ */
64
+ onClose?: () => void
65
+ }
66
+
67
+ type ModalPropsForTransition = {
68
+ transition?: TransitionType
69
+
70
+ /**
71
+ * Callback fired before the <Modal /> transitions in
72
+ */
73
+ onEnter?: () => void
74
+
75
+ /**
76
+ * Callback fired as the <Modal /> begins to transition in
77
+ */
78
+ onEntering?: () => void
79
+
80
+ /**
81
+ * Callback fired after the <Modal /> finishes transitioning in
82
+ */
83
+ onEntered?: (type?: TransitionType) => void
84
+
85
+ /**
86
+ * Callback fired right before the <Modal /> transitions out
87
+ */
88
+ onExit?: () => void
89
+
90
+ /**
91
+ * Callback fired as the <Modal /> begins to transition out
92
+ */
93
+ onExiting?: () => void
94
+
95
+ /**
96
+ * Callback fired after the <Modal /> finishes transitioning out
97
+ */
98
+ onExited?: (type?: TransitionType) => void
99
+ }
100
+
101
+ type ModalPropsForDialog = {
102
+ /**
103
+ * An accessible label for the `<Modal />` content
104
+ */
105
+ label: string
106
+
107
+ /**
108
+ * The element to render the dialog as, `span` by default
109
+ */
110
+ as?: AsElementType
111
+
112
+ /**
113
+ * Whether focus should be returned to the trigger when the `<Modal/>` is closed
114
+ */
115
+ shouldReturnFocus?: boolean
116
+
117
+ /**
118
+ * Whether the `<Modal/>` should request close when the document is clicked
119
+ */
120
+ shouldCloseOnDocumentClick?: boolean
121
+
122
+ /**
123
+ * Callback fired when the `<Modal />` is requesting to be closed
124
+ */
125
+ onDismiss?: (
126
+ event: React.UIEvent | React.FocusEvent,
127
+ documentClick?: boolean
128
+ ) => void
129
+
130
+ /**
131
+ * An element, function returning an element, or array of elements that will not be hidden from
132
+ * the screen reader when the `<Modal />` is open
133
+ */
134
+ liveRegion?: LiveRegion
135
+
136
+ /**
137
+ * An element or a function returning an element to focus by default
138
+ */
139
+ defaultFocusElement?: UIElement
140
+ }
141
+
142
+ type ModalOwnProps = {
143
+ /**
144
+ * Recommended children types are: `Modal.Header`, `Modal.Body`, `Modal.Footer`. Custom children can be used as well. `Variant` and `overflow` properties are always passed down to children.
145
+ */
146
+ children: React.ReactNode
147
+
148
+ /**
149
+ * The size of the `<Modal />` content
150
+ */
151
+ size?: 'auto' | 'small' | 'medium' | 'large' | 'fullscreen'
152
+
153
+ /**
154
+ * Designates the background style of the `<Modal />`
155
+ */
156
+ variant?: 'default' | 'inverse'
157
+
158
+ /**
159
+ *
160
+ * A function that returns a reference to the content element
161
+ */
162
+ contentRef?: (dialog: Dialog | null) => void
163
+
164
+ /**
165
+ * Constrain the Modal to the document window or its closest positioned parent
166
+ */
167
+ constrain?: 'window' | 'parent'
168
+
169
+ /**
170
+ * Should ModalBody handle overflow with scrollbars, or fit its
171
+ * content within its own height?
172
+ */
173
+ overflow?: 'scroll' | 'fit'
174
+ } & ModalPropsForPortal &
175
+ ModalPropsForTransition &
176
+ ModalPropsForDialog
177
+
178
+ type PropKeys = keyof ModalOwnProps
179
+
180
+ type AllowedPropKeys = Readonly<Array<PropKeys>>
181
+
182
+ type ModalProps = ModalOwnProps &
183
+ WithStyleProps<ModalTheme, ModalStyle> &
184
+ OtherHTMLAttributes<ModalOwnProps>
185
+
186
+ type ModalStyle = ComponentStyle<
187
+ 'modal' | 'constrainContext' | 'joinedHeaderAndBody'
188
+ >
189
+
190
+ type ModalState = {
191
+ transitioning: boolean
192
+ open: boolean
193
+ windowHeight: number
194
+ /**
195
+ * The `aria-label` on the Modal's body if it's scrollable.
196
+ */
197
+ bodyScrollAriaLabel?: string
198
+ }
199
+ const allowedProps: AllowedPropKeys = [
200
+ 'label',
201
+ 'children',
202
+ 'as',
203
+ 'size',
204
+ 'variant',
205
+ 'open',
206
+ 'defaultFocusElement',
207
+ 'shouldReturnFocus',
208
+ 'shouldCloseOnDocumentClick',
209
+ 'onOpen',
210
+ 'onClose',
211
+ 'onDismiss',
212
+ 'contentRef',
213
+ 'mountNode',
214
+ 'insertAt',
215
+ 'liveRegion',
216
+ 'transition',
217
+ 'onEnter',
218
+ 'onEntering',
219
+ 'onEntered',
220
+ 'onExit',
221
+ 'onExiting',
222
+ 'onExited',
223
+ 'constrain',
224
+ 'overflow'
225
+ ]
226
+
227
+ export type {
228
+ ModalProps,
229
+ ModalStyle,
230
+ ModalState,
231
+ ModalPropsForPortal,
232
+ ModalPropsForTransition,
233
+ ModalPropsForDialog
234
+ }
235
+ export { allowedProps }
@@ -0,0 +1,130 @@
1
+ /*
2
+ * The MIT License (MIT)
3
+ *
4
+ * Copyright (c) 2015 - present Instructure, Inc.
5
+ *
6
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
7
+ * of this software and associated documentation files (the "Software"), to deal
8
+ * in the Software without restriction, including without limitation the rights
9
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10
+ * copies of the Software, and to permit persons to whom the Software is
11
+ * furnished to do so, subject to the following conditions:
12
+ *
13
+ * The above copyright notice and this permission notice shall be included in all
14
+ * copies or substantial portions of the Software.
15
+ *
16
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22
+ * SOFTWARE.
23
+ */
24
+
25
+ import { boxShadowObjectsToCSSString } from '@instructure/ui-themes'
26
+ import type { NewComponentTypes } from '@instructure/ui-themes'
27
+ import type { ModalProps, ModalStyle } from './props'
28
+
29
+ /**
30
+ * ---
31
+ * private: true
32
+ * ---
33
+ * Generates the style object from the theme and provided additional information
34
+ * @param {Object} componentTheme The theme variable object.
35
+ * @param {Object} props the props of the component, the style is applied to
36
+ * @param {Object} sharedTokens Shared theme token object
37
+ * @param {Object} state the state of the component, the style is applied to
38
+ * @return {Object} The final style object, which will be used in the component
39
+ */
40
+ const generateStyle = (
41
+ componentTheme: NewComponentTypes['Modal'],
42
+ props: ModalProps
43
+ ): ModalStyle => {
44
+ const { size, variant, overflow } = props
45
+
46
+ const commonSizeStyleExceptForFullscreen = {
47
+ maxWidth: '95%',
48
+ maxHeight: '95%',
49
+ ...(overflow === 'fit' && {
50
+ transform: 'translateY(2.5%)'
51
+ })
52
+ }
53
+
54
+ const sizeStyles = {
55
+ auto: {
56
+ flex: '0 1 auto',
57
+ minWidth: componentTheme.autoMinWidth,
58
+ ...commonSizeStyleExceptForFullscreen
59
+ },
60
+ small: {
61
+ flex: `0 1 ${componentTheme.smallMaxWidth}`,
62
+ ...commonSizeStyleExceptForFullscreen
63
+ },
64
+ medium: {
65
+ flex: `0 1 ${componentTheme.mediumMaxWidth}`,
66
+ ...commonSizeStyleExceptForFullscreen
67
+ },
68
+ large: {
69
+ flex: `0 1 ${componentTheme.largeMaxWidth}`,
70
+ ...commonSizeStyleExceptForFullscreen
71
+ },
72
+ fullscreen: {
73
+ flex: 1,
74
+ width: '100%',
75
+ height: '100%',
76
+ boxShadow: 'none',
77
+ border: 'none',
78
+ borderRadius: 0
79
+ }
80
+ }
81
+ const backgroundStyles =
82
+ variant === 'inverse'
83
+ ? {
84
+ background: componentTheme.inverseBackgroundColor,
85
+ border: `${componentTheme.borderWidth} solid ${componentTheme.inverseBorderColor}`,
86
+ color: componentTheme.inverseTextColor
87
+ }
88
+ : {
89
+ background: componentTheme.backgroundColor,
90
+ border: `${componentTheme.borderWidth} solid ${componentTheme.borderColor}`,
91
+ color: componentTheme.textColor
92
+ }
93
+
94
+ return {
95
+ modal: {
96
+ label: 'modal',
97
+ fontFamily: componentTheme.fontFamily,
98
+ display: 'flex',
99
+ minWidth: '1px',
100
+ flexDirection: 'column',
101
+ position: 'relative',
102
+ boxSizing: 'border-box',
103
+ boxShadow: boxShadowObjectsToCSSString(componentTheme.boxShadow),
104
+ borderRadius: componentTheme.borderRadius,
105
+ overflow: 'hidden',
106
+ ...sizeStyles[size!],
107
+ ...backgroundStyles
108
+ },
109
+ constrainContext: {
110
+ label: 'modal__constrainContext',
111
+ display: 'block',
112
+ position: 'relative',
113
+ width: '100%',
114
+ height: '100%'
115
+ },
116
+ joinedHeaderAndBody: {
117
+ display: 'flex',
118
+ flexDirection: 'column',
119
+ overflow: 'hidden',
120
+ flex: '1 1 auto',
121
+ // ModalHeader and ModalBody is set to scrollable above 20rem height instead of just the ModalBody
122
+ '@media (max-height: 20rem)': {
123
+ overflowY: 'auto',
124
+ maxHeight: '20rem'
125
+ }
126
+ }
127
+ }
128
+ }
129
+
130
+ export default generateStyle
@@ -21,9 +21,9 @@
21
21
  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22
22
  * SOFTWARE.
23
23
  */
24
- export { Modal, ModalBody, ModalFooter, ModalHeader } from './Modal'
24
+ export { Modal, ModalBody, ModalFooter, ModalHeader } from '../Modal/v1'
25
25
 
26
- export type { ModalProps } from './Modal/props'
27
- export type { ModalBodyProps } from './Modal/ModalBody/props'
28
- export type { ModalFooterProps } from './Modal/ModalFooter/props'
29
- export type { ModalHeaderProps } from './Modal/ModalHeader/props'
26
+ export type { ModalProps } from '../Modal/v1/props'
27
+ export type { ModalBodyProps } from '../Modal/v1/ModalBody/props'
28
+ export type { ModalFooterProps } from '../Modal/v1/ModalFooter/props'
29
+ export type { ModalHeaderProps } from '../Modal/v1/ModalHeader/props'