import React from 'react'
import PropTypes from 'prop-types'
import en from 'locales/en'
import ugh from 'ugh'
import { ErrorBoundary } from 'ErrorBoundary'
// context is only created when <Provider> is implemented (see constructor)
export let ProviderContext = null
/** A higher order component that provides context to connectToContext wrapped children
* @component
* @example ./example.md
* @category Provider
* @since 1.0.0
*/
class Provider extends React.Component {
constructor (props) {
super(props)
// state becomes an object of persistedStateKeys (or component names) with their persisted states'
this.state = {
mapContext: {}
}
// create context when <Provider> is included in component tree
ProviderContext = React.createContext()
}
persistState = (persistedState, persistedStateKey) => {
if (!persistedStateKey || typeof persistedStateKey !== 'string') return ugh.error('persistedStateKey (string; usually "ComponentName") must be passed as second arg to persistState(state, persistedStateKey)') // eslint-disable-line no-console
this.setState({ [persistedStateKey]: persistedState })
}
addMapToContext = mapContext => {
this.setState({ mapContext })
}
addEditFeatureToContext = editFeature => {
this.setState({ editFeature })
}
getContextValue = () => {
const { contextProps, map: mapProp, maps: mapsProp, translations } = this.props
const { mapContext } = this.state
const activeMap = mapProp || mapContext.map // map prop will override context map
// if (!activeMap && !mapsProp.length) return ugh.throw('Provider requires either a \'map\' or \'maps\' prop to work!')
// support multi-map components
// default map reference to zeroith map from mapsProp if an array of maps has been passed
const map = mapsProp.length ? mapsProp[0] : activeMap
// default an array with single value of map prop if maps array has not been passed
const maps = mapsProp.length ? mapsProp : [activeMap]
return {
addMapToContext: this.addMapToContext,
map,
maps,
editFeature: this.state.editFeature,
addEditFeatureToContext: this.addEditFeatureToContext,
persistedState: this.state,
persistState: this.persistState,
translations,
...this.state.mapContext,
...contextProps
}
}
render () {
return (
<ErrorBoundary floating={true}>
<ProviderContext.Provider value={this.getContextValue()}>
{this.props.children}
</ProviderContext.Provider>
</ErrorBoundary>
)
}
}
Provider.defaultProps = {
contextProps: {},
maps: [],
translations: en
}
Provider.propTypes = {
/** Pass components as children of Provider component */
children: PropTypes.oneOfType([
PropTypes.arrayOf(PropTypes.node),
PropTypes.node
]).isRequired,
/** Add any custom props to context and they will be passed to all components wrapped by connectToContext */
contextProps: PropTypes.object,
/** OL map object (required if maps array is not passed) */
map: PropTypes.object,
/** Array of Openlayers map objects for components that support multi-map (this will override anything passed to map prop) */
maps: PropTypes.array,
/** Object with key/value pairs for component translation strings */
translations: PropTypes.object,
/** An Ol feature object that is being edited */
editFeature: PropTypes.object
}
export default Provider