Skip to content

Commit

Permalink
feat(MountNode): add component
Browse files Browse the repository at this point in the history
  • Loading branch information
layershifter committed Jan 5, 2018
1 parent 4f2f3fc commit c4c3dec
Show file tree
Hide file tree
Showing 9 changed files with 155 additions and 31 deletions.
12 changes: 6 additions & 6 deletions gulp/tasks/docs.js
Original file line number Diff line number Diff line change
Expand Up @@ -29,12 +29,12 @@ task('clean:docs', (cb) => {
// ----------------------------------------

task('build:docs:docgen', () => src([
`${config.paths.src()}/addons/**/*.js`,
`${config.paths.src()}/behaviors/**/*.js`,
`${config.paths.src()}/elements/**/*.js`,
`${config.paths.src()}/collections/**/*.js`,
`${config.paths.src()}/modules/**/*.js`,
`${config.paths.src()}/views/**/*.js`,
`${config.paths.src()}/addons/*/*.js`,
`${config.paths.src()}/behaviors/*/*.js`,
`${config.paths.src()}/elements/*/*.js`,
`${config.paths.src()}/collections/*/*.js`,
`${config.paths.src()}/modules/*/*.js`,
`${config.paths.src()}/views/*/*.js`,
'!**/index.js',
])
// do not remove the function keyword
Expand Down
13 changes: 13 additions & 0 deletions src/addons/MountNode/MountNode.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import * as React from 'react';

export interface MountNodeProps {
[key: string]: any;

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

declare class MountNode extends React.Component<MountNodeProps, {}> {
}

export default MountNode;
55 changes: 55 additions & 0 deletions src/addons/MountNode/MountNode.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
import PropTypes from 'prop-types'
import React, {Component} from 'react'

import {TYPES} from '../../lib/META'
import handleChange from './lib/handleChange'
import NodeRegistry from './lib/NodeRegistry'

const nodeRegistry = new NodeRegistry()

/**
* Component.
*/
export default class MountNode extends Component {
static propTypes = {
/** Primary content. */
className: PropTypes.string,

node: PropTypes.node,
}

static _meta = {
name: 'MountNode',
type: TYPES.ADDON,
}

shouldComponentUpdate({className: next}) {
const {className: current} = this.props

return next !== current
}

componentWillMount() {
const { node } = this.props

nodeRegistry.add(node, this)
nodeRegistry.emit(node, handleChange)
}

componentDidUpdate() {
const { node } = this.props

nodeRegistry.emit(node, handleChange)
}

componentWillUnmount() {
const { node } = this.props

nodeRegistry.del(node, this)
nodeRegistry.emit(node, handleChange)
}

render() {
return null
}
}
1 change: 1 addition & 0 deletions src/addons/MountNode/index.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export { default, MountNodeProps } from './MountNode';
1 change: 1 addition & 0 deletions src/addons/MountNode/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export default from './MountNode'
34 changes: 34 additions & 0 deletions src/addons/MountNode/lib/NodeRegistry.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
export default class NodeRegistry {
constructor() {
this.nodes = new Map()
}

add = (node, component) => {
if(this.nodes.has(node)) {
const set = this.nodes.get(node)

set.add(component)
return
}

const set = new Set()
set.add(component)

this.nodes.set(node, set)
}

del = (node, component) => {
const set = this.nodes.get(node)

if(set.size === 1) {
this.nodes.delete(node)
return
}

set.delete(component)
}

emit = (node, callback) => {
callback(node, this.nodes.get(node))
}
}
11 changes: 11 additions & 0 deletions src/addons/MountNode/lib/computeClasses.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import _ from 'lodash/fp'

const computeClasses = (components) => components && _.flow(
_.map('props.className'),
_.filter(_.isString),
_.map(_.split(/\s+/)),
_.flatten,
_.uniq,
)([...components])

export default computeClasses
17 changes: 17 additions & 0 deletions src/addons/MountNode/lib/handleChange.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import _ from 'lodash'
import computeClasses from './computeClasses'

let prevClasses = []

const handleChange = (node, components) => {
const currentClasses = computeClasses(components)
const forAdd = _.difference(currentClasses, prevClasses)
const forRemoval = _.difference(prevClasses, currentClasses)

_.forEach(forAdd, className => node.classList.add(className))
_.forEach(forRemoval, className => node.classList.remove(className))

prevClasses = currentClasses
}

export default handleChange
42 changes: 17 additions & 25 deletions src/modules/Modal/Modal.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ 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'
Expand Down Expand Up @@ -208,17 +209,6 @@ class Modal extends Component {
handlePortalUnmount = (e) => {
debug('handlePortalUnmount()')

// Always remove all dimmer classes.
// If the dimmer value changes while the modal is open, then removing its
// current value could leave cruft classes previously added.
const mountNode = this.getMountNode()

// Heads up, IE doesn't support second argument in remove()
mountNode.classList.remove('blurring')
mountNode.classList.remove('dimmable')
mountNode.classList.remove('dimmed')
mountNode.classList.remove('scrolling')

cancelAnimationFrame(this.animationRequestId)
_.invoke(this.props, 'onUnmount', e, this.props)
}
Expand All @@ -227,42 +217,39 @@ class Modal extends Component {

setPositionAndClassNames = () => {
const { dimmer } = this.props
const mountNode = this.getMountNode()
let classes

if (dimmer) {
mountNode.classList.add('dimmable')
mountNode.classList.add('dimmed')
classes = 'dimmable dimmed'

if (dimmer === 'blurring') {
mountNode.classList.add('blurring')
classes += ' blurring'
}
}

const newState = {}

if (this.ref) {
const { height } = this.ref.getBoundingClientRect()

const marginTop = -Math.round(height / 2)
const scrolling = height >= window.innerHeight

const newState = {}

if (this.state.marginTop !== marginTop) {
newState.marginTop = marginTop
}

if (this.state.scrolling !== scrolling) {
newState.scrolling = scrolling

if (scrolling) {
mountNode.classList.add('scrolling')
} else {
mountNode.classList.remove('scrolling')
}
}

if (Object.keys(newState).length > 0) this.setState(newState)
if (scrolling) classes += ' scrolling'
}

if (this.state.mountClasses !== classes) newState.mountClasses = classes
console.log(classes)
if (Object.keys(newState).length > 0) this.setState(newState)

this.animationRequestId = requestAnimationFrame(this.setPositionAndClassNames)
}

Expand All @@ -275,10 +262,11 @@ class Modal extends Component {
closeIcon,
content,
header,
mountNode,
size,
style,
} = this.props
const { marginTop, scrolling } = this.state
const { marginTop, mountClasses, scrolling } = this.state

const classes = cx(
'ui',
Expand All @@ -296,6 +284,8 @@ class Modal extends Component {
if (!childrenUtils.isNil(children)) {
return (
<ElementType {...rest} className={classes} style={{ marginTop, ...style }} ref={this.handleRef}>
<MountNode className={mountClasses} node={mountNode || document.body} />

{closeIconJSX}
{children}
</ElementType>
Expand All @@ -304,6 +294,8 @@ class Modal extends Component {

return (
<ElementType {...rest} className={classes} style={{ marginTop, ...style }} ref={this.handleRef}>
<MountNode className={mountClasses} node={mountNode || document.body} />

{closeIconJSX}
{ModalHeader.create(header)}
{ModalContent.create(content)}
Expand Down

0 comments on commit c4c3dec

Please sign in to comment.