Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(Modal): impliment Dimmer shorthand #1739

Merged
merged 7 commits into from
Aug 4, 2020
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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'),
!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