Skip to content

Commit

Permalink
feat(Modal): impliment Dimmer shorthand (#1739)
Browse files Browse the repository at this point in the history
* feat: add ModalDimmer

* add static

* final fixes

* Update test/specs/modules/Modal/Modal-test.js

* update UTs

Co-authored-by: Oleksandr Fediashov <[email protected]>
  • Loading branch information
qoalu and layershifter authored Aug 4, 2020
1 parent 0f9c691 commit 6768a7d
Show file tree
Hide file tree
Showing 9 changed files with 275 additions and 64 deletions.
5 changes: 5 additions & 0 deletions index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -504,6 +504,11 @@ export {
ModalDescriptionProps,
StrictModalDescriptionProps,
} from './dist/commonjs/modules/Modal/ModalDescription'
export {
default as ModalDimmer,
ModalDimmerProps,
StrictModalDimmerProps,
} from './dist/commonjs/modules/Modal/ModalDimmer'
export {
default as ModalHeader,
ModalHeaderProps,
Expand Down
1 change: 1 addition & 0 deletions src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -142,6 +142,7 @@ export Modal from './modules/Modal'
export ModalActions from './modules/Modal/ModalActions'
export ModalContent from './modules/Modal/ModalContent'
export ModalDescription from './modules/Modal/ModalDescription'
export ModalDimmer from './modules/Modal/ModalDimmer'
export ModalHeader from './modules/Modal/ModalHeader'

export Popup from './modules/Popup'
Expand Down
6 changes: 4 additions & 2 deletions src/modules/Modal/Modal.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import { StrictPortalProps } from '../../addons/Portal'
import ModalActions, { ModalActionsProps } from './ModalActions'
import ModalContent, { ModalContentProps } from './ModalContent'
import ModalDescription from './ModalDescription'
import ModalDimmer, { ModalDimmerProps } from './ModalDimmer'
import ModalHeader, { ModalHeaderProps } from './ModalHeader'

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

/** A modal can be vertically centered in the viewport */
/** A modal can be vertically centered in the viewport. */
centered?: boolean

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

/** A modal can appear in a dimmer. */
dimmer?: true | 'blurring' | 'inverted'
dimmer?: true | 'blurring' | 'inverted' | SemanticShorthandItem<ModalDimmerProps>

/** Event pool namespace that is used to handle component events */
eventPool?: string
Expand Down Expand Up @@ -114,6 +115,7 @@ interface ModalComponent extends React.ComponentClass<ModalProps> {
Actions: typeof ModalActions
Content: typeof ModalContent
Description: typeof ModalDescription
Dimmer: typeof ModalDimmer
Header: typeof ModalHeader
}

Expand Down
72 changes: 30 additions & 42 deletions src/modules/Modal/Modal.js
Original file line number Diff line number Diff line change
Expand Up @@ -18,12 +18,12 @@ import {
useKeyOnly,
} from '../../lib'
import Icon from '../../elements/Icon'
import MountNode from '../../addons/MountNode'
import Portal from '../../addons/Portal'
import ModalHeader from './ModalHeader'
import ModalContent from './ModalContent'
import ModalActions from './ModalActions'
import ModalContent from './ModalContent'
import ModalDescription from './ModalDescription'
import ModalDimmer from './ModalDimmer'
import ModalHeader from './ModalHeader'
import { canFit, getLegacyStyles, isLegacy } from './utils'

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

setDimmerNodeStyle = () => {
debug('setDimmerNodeStyle()')
const { current } = this.dimmerRef

if (current && current.style && current.style.display !== 'flex') {
current.style.setProperty('display', 'flex', 'important')
}
}

setPositionAndClassNames = () => {
const { centered, dimmer } = this.props
const { centered } = this.props

let scrolling
const newState = {}
Expand All @@ -164,18 +155,8 @@ class Modal extends Component {
}
}

const classes = cx(
useKeyOnly(dimmer, 'dimmable dimmed'),
useKeyOnly(dimmer === 'blurring', ' blurring'),
useKeyOnly(scrolling, ' scrolling'),
)

if (this.state.mountClasses !== classes) newState.mountClasses = classes
if (!_.isEmpty(newState)) this.setState(newState)

this.animationRequestId = requestAnimationFrame(this.setPositionAndClassNames)

this.setDimmerNodeStyle()
}

renderContent = (rest) => {
Expand All @@ -187,11 +168,10 @@ class Modal extends Component {
closeIcon,
content,
header,
mountNode,
size,
style,
} = this.props
const { legacyStyles, mountClasses, scrolling } = this.state
const { legacyStyles, scrolling } = this.state

const classes = cx(
'ui',
Expand All @@ -210,8 +190,6 @@ class Modal extends Component {
return (
<Ref innerRef={this.ref}>
<ElementType {...rest} className={classes} style={{ ...legacyStyles, ...style }}>
<MountNode className={mountClasses} node={mountNode} />

{closeIconJSX}
{childrenUtils.isNil(children) ? (
<>
Expand All @@ -228,8 +206,8 @@ class Modal extends Component {
}

render() {
const { open } = this.state
const { centered, closeOnDocumentClick, dimmer, eventPool, trigger } = this.props
const { open, scrolling } = this.state
const mountNode = this.getMountNode()

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

// wrap dimmer modals
const dimmerClasses = cx(
'ui',
dimmer === 'inverted' && 'inverted',
!centered && 'top aligned',
'page modals dimmer transition visible active',
)

// Heads up!
//
// The SUI CSS selector to prevent the modal itself from blurring requires an immediate .dimmer child:
Expand All @@ -283,9 +253,21 @@ class Modal extends Component {
onOpen={this.handleOpen}
onUnmount={this.handlePortalUnmount}
>
<div className={dimmerClasses} ref={this.dimmerRef}>
{this.renderContent(rest)}
</div>
<Ref innerRef={this.dimmerRef}>
{ModalDimmer.create(_.isPlainObject(dimmer) ? dimmer : {}, {
autoGenerateKey: false,
defaultProps: {
blurring: dimmer === 'blurring',
inverted: dimmer === 'inverted',
},
overrideProps: {
children: this.renderContent(rest),
centered,
mountNode,
scrolling,
},
})}
</Ref>
</Portal>
)
}
Expand Down Expand Up @@ -326,7 +308,12 @@ Modal.propTypes = {
defaultOpen: PropTypes.bool,

/** A Modal can appear in a dimmer. */
dimmer: PropTypes.oneOf([true, 'inverted', 'blurring']),
dimmer: PropTypes.oneOfType([
PropTypes.bool,
PropTypes.func,
PropTypes.object,
PropTypes.oneOf(['inverted', 'blurring']),
]),

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

Modal.autoControlledProps = ['open']

Modal.Header = ModalHeader
Modal.Actions = ModalActions
Modal.Content = ModalContent
Modal.Description = ModalDescription
Modal.Actions = ModalActions
Modal.Dimmer = ModalDimmer
Modal.Header = ModalHeader

export default Modal
39 changes: 39 additions & 0 deletions src/modules/Modal/ModalDimmer.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
import * as React from 'react'
import { SemanticShorthandContent } from '../../generic'

export interface ModalDimmerProps extends StrictModalDimmerProps {
[key: string]: any
}

export interface StrictModalDimmerProps {
/** An element type to render as (string or function). */
as?: any

/** A dimmer can be blurred. */
blurring?: boolean

/** Primary content. */
children?: React.ReactNode

/** Additional classes. */
className?: string

/** A dimmer can center its contents in the viewport. */
centered?: boolean

/** Shorthand for primary content. */
content?: SemanticShorthandContent

/** A dimmer can be inverted. */
inverted?: boolean

/** The node where the modal should mount. Defaults to document.body. */
mountNode?: any

/** A dimmer can make body scrollable. */
scrolling?: boolean
}

declare const ModalDimmer: React.StatelessComponent<ModalDimmerProps>

export default ModalDimmer
87 changes: 87 additions & 0 deletions src/modules/Modal/ModalDimmer.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
import { Ref } from '@stardust-ui/react-component-ref'
import cx from 'clsx'
import PropTypes from 'prop-types'
import React from 'react'

import MountNode from '../../addons/MountNode'
import {
childrenUtils,
createShorthandFactory,
customPropTypes,
getElementType,
getUnhandledProps,
useKeyOnly,
} from '../../lib'

/**
* A modal has a dimmer.
*/
function ModalDimmer(props) {
const { blurring, children, className, centered, content, inverted, mountNode, scrolling } = props
const ref = React.useRef()

const classes = cx(
'ui',
useKeyOnly(inverted, 'inverted'),
useKeyOnly(!centered, 'top aligned'),
'page modals dimmer transition visible active',
className,
)
const bodyClasses = cx(
'dimmable dimmed',
useKeyOnly(blurring, 'blurring'),
useKeyOnly(scrolling, 'scrolling'),
)

const rest = getUnhandledProps(ModalDimmer, props)
const ElementType = getElementType(ModalDimmer, props)

React.useEffect(() => {
if (ref.current && ref.current.style) {
ref.current.style.setProperty('display', 'flex', 'important')
}
}, [])

return (
<Ref innerRef={ref}>
<ElementType {...rest} className={classes}>
{childrenUtils.isNil(children) ? content : children}

<MountNode className={bodyClasses} node={mountNode} />
</ElementType>
</Ref>
)
}

ModalDimmer.propTypes = {
/** An element type to render as (string or function). */
as: PropTypes.elementType,

/** A dimmer can be blurred. */
blurring: PropTypes.bool,

/** Primary content. */
children: PropTypes.node,

/** Additional classes. */
className: PropTypes.string,

/** A dimmer can center its contents in the viewport. */
centered: PropTypes.bool,

/** Shorthand for primary content. */
content: customPropTypes.contentShorthand,

/** A dimmer can be inverted. */
inverted: PropTypes.bool,

/** The node where the modal should mount. Defaults to document.body. */
mountNode: PropTypes.any,

/** A dimmer can make body scrollable. */
scrolling: PropTypes.bool,
}

ModalDimmer.create = createShorthandFactory(ModalDimmer, (content) => ({ content }))

export default ModalDimmer
2 changes: 1 addition & 1 deletion src/modules/Modal/ModalHeader.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ import {
*/
function ModalHeader(props) {
const { children, className, content } = props
const classes = cx(className, 'header')
const classes = cx('header', className)
const rest = getUnhandledProps(ModalHeader, props)
const ElementType = getElementType(ModalHeader, props)

Expand Down
Loading

0 comments on commit 6768a7d

Please sign in to comment.