Skip to content

Commit 6768a7d

Browse files
qoalulayershifter
andauthored
feat(Modal): impliment Dimmer shorthand (#1739)
* feat: add ModalDimmer * add static * final fixes * Update test/specs/modules/Modal/Modal-test.js * update UTs Co-authored-by: Oleksandr Fediashov <[email protected]>
1 parent 0f9c691 commit 6768a7d

File tree

9 files changed

+275
-64
lines changed

9 files changed

+275
-64
lines changed

index.d.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -504,6 +504,11 @@ export {
504504
ModalDescriptionProps,
505505
StrictModalDescriptionProps,
506506
} from './dist/commonjs/modules/Modal/ModalDescription'
507+
export {
508+
default as ModalDimmer,
509+
ModalDimmerProps,
510+
StrictModalDimmerProps,
511+
} from './dist/commonjs/modules/Modal/ModalDimmer'
507512
export {
508513
default as ModalHeader,
509514
ModalHeaderProps,

src/index.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -142,6 +142,7 @@ export Modal from './modules/Modal'
142142
export ModalActions from './modules/Modal/ModalActions'
143143
export ModalContent from './modules/Modal/ModalContent'
144144
export ModalDescription from './modules/Modal/ModalDescription'
145+
export ModalDimmer from './modules/Modal/ModalDimmer'
145146
export ModalHeader from './modules/Modal/ModalHeader'
146147

147148
export Popup from './modules/Popup'

src/modules/Modal/Modal.d.ts

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import { StrictPortalProps } from '../../addons/Portal'
55
import ModalActions, { ModalActionsProps } from './ModalActions'
66
import ModalContent, { ModalContentProps } from './ModalContent'
77
import ModalDescription from './ModalDescription'
8+
import ModalDimmer, { ModalDimmerProps } from './ModalDimmer'
89
import ModalHeader, { ModalHeaderProps } from './ModalHeader'
910

1011
export interface ModalProps extends StrictModalProps {
@@ -21,7 +22,7 @@ export interface StrictModalProps extends StrictPortalProps {
2122
/** A Modal can reduce its complexity */
2223
basic?: boolean
2324

24-
/** A modal can be vertically centered in the viewport */
25+
/** A modal can be vertically centered in the viewport. */
2526
centered?: boolean
2627

2728
/** Primary content. */
@@ -46,7 +47,7 @@ export interface StrictModalProps extends StrictPortalProps {
4647
defaultOpen?: boolean
4748

4849
/** A modal can appear in a dimmer. */
49-
dimmer?: true | 'blurring' | 'inverted'
50+
dimmer?: true | 'blurring' | 'inverted' | SemanticShorthandItem<ModalDimmerProps>
5051

5152
/** Event pool namespace that is used to handle component events */
5253
eventPool?: string
@@ -114,6 +115,7 @@ interface ModalComponent extends React.ComponentClass<ModalProps> {
114115
Actions: typeof ModalActions
115116
Content: typeof ModalContent
116117
Description: typeof ModalDescription
118+
Dimmer: typeof ModalDimmer
117119
Header: typeof ModalHeader
118120
}
119121

src/modules/Modal/Modal.js

Lines changed: 30 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -18,12 +18,12 @@ import {
1818
useKeyOnly,
1919
} from '../../lib'
2020
import Icon from '../../elements/Icon'
21-
import MountNode from '../../addons/MountNode'
2221
import Portal from '../../addons/Portal'
23-
import ModalHeader from './ModalHeader'
24-
import ModalContent from './ModalContent'
2522
import ModalActions from './ModalActions'
23+
import ModalContent from './ModalContent'
2624
import ModalDescription from './ModalDescription'
25+
import ModalDimmer from './ModalDimmer'
26+
import ModalHeader from './ModalHeader'
2727
import { canFit, getLegacyStyles, isLegacy } from './utils'
2828

2929
const debug = makeDebugger('modal')
@@ -132,17 +132,8 @@ class Modal extends Component {
132132
_.invoke(this.props, 'onUnmount', e, this.props)
133133
}
134134

135-
setDimmerNodeStyle = () => {
136-
debug('setDimmerNodeStyle()')
137-
const { current } = this.dimmerRef
138-
139-
if (current && current.style && current.style.display !== 'flex') {
140-
current.style.setProperty('display', 'flex', 'important')
141-
}
142-
}
143-
144135
setPositionAndClassNames = () => {
145-
const { centered, dimmer } = this.props
136+
const { centered } = this.props
146137

147138
let scrolling
148139
const newState = {}
@@ -164,18 +155,8 @@ class Modal extends Component {
164155
}
165156
}
166157

167-
const classes = cx(
168-
useKeyOnly(dimmer, 'dimmable dimmed'),
169-
useKeyOnly(dimmer === 'blurring', ' blurring'),
170-
useKeyOnly(scrolling, ' scrolling'),
171-
)
172-
173-
if (this.state.mountClasses !== classes) newState.mountClasses = classes
174158
if (!_.isEmpty(newState)) this.setState(newState)
175-
176159
this.animationRequestId = requestAnimationFrame(this.setPositionAndClassNames)
177-
178-
this.setDimmerNodeStyle()
179160
}
180161

181162
renderContent = (rest) => {
@@ -187,11 +168,10 @@ class Modal extends Component {
187168
closeIcon,
188169
content,
189170
header,
190-
mountNode,
191171
size,
192172
style,
193173
} = this.props
194-
const { legacyStyles, mountClasses, scrolling } = this.state
174+
const { legacyStyles, scrolling } = this.state
195175

196176
const classes = cx(
197177
'ui',
@@ -210,8 +190,6 @@ class Modal extends Component {
210190
return (
211191
<Ref innerRef={this.ref}>
212192
<ElementType {...rest} className={classes} style={{ ...legacyStyles, ...style }}>
213-
<MountNode className={mountClasses} node={mountNode} />
214-
215193
{closeIconJSX}
216194
{childrenUtils.isNil(children) ? (
217195
<>
@@ -228,8 +206,8 @@ class Modal extends Component {
228206
}
229207

230208
render() {
231-
const { open } = this.state
232209
const { centered, closeOnDocumentClick, dimmer, eventPool, trigger } = this.props
210+
const { open, scrolling } = this.state
233211
const mountNode = this.getMountNode()
234212

235213
// Short circuit when server side rendering
@@ -251,14 +229,6 @@ class Modal extends Component {
251229
)
252230
const portalProps = _.pick(unhandled, portalPropNames)
253231

254-
// wrap dimmer modals
255-
const dimmerClasses = cx(
256-
'ui',
257-
dimmer === 'inverted' && 'inverted',
258-
!centered && 'top aligned',
259-
'page modals dimmer transition visible active',
260-
)
261-
262232
// Heads up!
263233
//
264234
// The SUI CSS selector to prevent the modal itself from blurring requires an immediate .dimmer child:
@@ -283,9 +253,21 @@ class Modal extends Component {
283253
onOpen={this.handleOpen}
284254
onUnmount={this.handlePortalUnmount}
285255
>
286-
<div className={dimmerClasses} ref={this.dimmerRef}>
287-
{this.renderContent(rest)}
288-
</div>
256+
<Ref innerRef={this.dimmerRef}>
257+
{ModalDimmer.create(_.isPlainObject(dimmer) ? dimmer : {}, {
258+
autoGenerateKey: false,
259+
defaultProps: {
260+
blurring: dimmer === 'blurring',
261+
inverted: dimmer === 'inverted',
262+
},
263+
overrideProps: {
264+
children: this.renderContent(rest),
265+
centered,
266+
mountNode,
267+
scrolling,
268+
},
269+
})}
270+
</Ref>
289271
</Portal>
290272
)
291273
}
@@ -326,7 +308,12 @@ Modal.propTypes = {
326308
defaultOpen: PropTypes.bool,
327309

328310
/** A Modal can appear in a dimmer. */
329-
dimmer: PropTypes.oneOf([true, 'inverted', 'blurring']),
311+
dimmer: PropTypes.oneOfType([
312+
PropTypes.bool,
313+
PropTypes.func,
314+
PropTypes.object,
315+
PropTypes.oneOf(['inverted', 'blurring']),
316+
]),
330317

331318
/** Event pool namespace that is used to handle component events */
332319
eventPool: PropTypes.string,
@@ -405,9 +392,10 @@ Modal.defaultProps = {
405392

406393
Modal.autoControlledProps = ['open']
407394

408-
Modal.Header = ModalHeader
395+
Modal.Actions = ModalActions
409396
Modal.Content = ModalContent
410397
Modal.Description = ModalDescription
411-
Modal.Actions = ModalActions
398+
Modal.Dimmer = ModalDimmer
399+
Modal.Header = ModalHeader
412400

413401
export default Modal

src/modules/Modal/ModalDimmer.d.ts

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
import * as React from 'react'
2+
import { SemanticShorthandContent } from '../../generic'
3+
4+
export interface ModalDimmerProps extends StrictModalDimmerProps {
5+
[key: string]: any
6+
}
7+
8+
export interface StrictModalDimmerProps {
9+
/** An element type to render as (string or function). */
10+
as?: any
11+
12+
/** A dimmer can be blurred. */
13+
blurring?: boolean
14+
15+
/** Primary content. */
16+
children?: React.ReactNode
17+
18+
/** Additional classes. */
19+
className?: string
20+
21+
/** A dimmer can center its contents in the viewport. */
22+
centered?: boolean
23+
24+
/** Shorthand for primary content. */
25+
content?: SemanticShorthandContent
26+
27+
/** A dimmer can be inverted. */
28+
inverted?: boolean
29+
30+
/** The node where the modal should mount. Defaults to document.body. */
31+
mountNode?: any
32+
33+
/** A dimmer can make body scrollable. */
34+
scrolling?: boolean
35+
}
36+
37+
declare const ModalDimmer: React.StatelessComponent<ModalDimmerProps>
38+
39+
export default ModalDimmer

src/modules/Modal/ModalDimmer.js

Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
import { Ref } from '@stardust-ui/react-component-ref'
2+
import cx from 'clsx'
3+
import PropTypes from 'prop-types'
4+
import React from 'react'
5+
6+
import MountNode from '../../addons/MountNode'
7+
import {
8+
childrenUtils,
9+
createShorthandFactory,
10+
customPropTypes,
11+
getElementType,
12+
getUnhandledProps,
13+
useKeyOnly,
14+
} from '../../lib'
15+
16+
/**
17+
* A modal has a dimmer.
18+
*/
19+
function ModalDimmer(props) {
20+
const { blurring, children, className, centered, content, inverted, mountNode, scrolling } = props
21+
const ref = React.useRef()
22+
23+
const classes = cx(
24+
'ui',
25+
useKeyOnly(inverted, 'inverted'),
26+
useKeyOnly(!centered, 'top aligned'),
27+
'page modals dimmer transition visible active',
28+
className,
29+
)
30+
const bodyClasses = cx(
31+
'dimmable dimmed',
32+
useKeyOnly(blurring, 'blurring'),
33+
useKeyOnly(scrolling, 'scrolling'),
34+
)
35+
36+
const rest = getUnhandledProps(ModalDimmer, props)
37+
const ElementType = getElementType(ModalDimmer, props)
38+
39+
React.useEffect(() => {
40+
if (ref.current && ref.current.style) {
41+
ref.current.style.setProperty('display', 'flex', 'important')
42+
}
43+
}, [])
44+
45+
return (
46+
<Ref innerRef={ref}>
47+
<ElementType {...rest} className={classes}>
48+
{childrenUtils.isNil(children) ? content : children}
49+
50+
<MountNode className={bodyClasses} node={mountNode} />
51+
</ElementType>
52+
</Ref>
53+
)
54+
}
55+
56+
ModalDimmer.propTypes = {
57+
/** An element type to render as (string or function). */
58+
as: PropTypes.elementType,
59+
60+
/** A dimmer can be blurred. */
61+
blurring: PropTypes.bool,
62+
63+
/** Primary content. */
64+
children: PropTypes.node,
65+
66+
/** Additional classes. */
67+
className: PropTypes.string,
68+
69+
/** A dimmer can center its contents in the viewport. */
70+
centered: PropTypes.bool,
71+
72+
/** Shorthand for primary content. */
73+
content: customPropTypes.contentShorthand,
74+
75+
/** A dimmer can be inverted. */
76+
inverted: PropTypes.bool,
77+
78+
/** The node where the modal should mount. Defaults to document.body. */
79+
mountNode: PropTypes.any,
80+
81+
/** A dimmer can make body scrollable. */
82+
scrolling: PropTypes.bool,
83+
}
84+
85+
ModalDimmer.create = createShorthandFactory(ModalDimmer, (content) => ({ content }))
86+
87+
export default ModalDimmer

src/modules/Modal/ModalHeader.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ import {
1515
*/
1616
function ModalHeader(props) {
1717
const { children, className, content } = props
18-
const classes = cx(className, 'header')
18+
const classes = cx('header', className)
1919
const rest = getUnhandledProps(ModalHeader, props)
2020
const ElementType = getElementType(ModalHeader, props)
2121

0 commit comments

Comments
 (0)