import React, { Component } from 'react'
import PropTypes from 'prop-types'
import Event from 'ol/events/Event'
import en from 'locales/en'
import { sanitizeProperties } from '../utils'
import { connectToContext } from 'Provider'
import { PopupActionCopyWkt } from 'Popup/PopupActions/PopupActionCopyWkt'
import { PopupActionGoogleMaps } from 'Popup/PopupActions/PopupActionGoogleMaps'
import { PopupActionRemove } from 'Popup/PopupActions/PopupActionRemove'
import { PopupActionDuplicate } from 'Popup/PopupActions/PopupActionDuplicate'
import { PopupActionCut } from 'Popup/PopupActions/PopupActionCut'
import { PopupActionEdit } from 'Popup/PopupActions/PopupActionEdit'
import { PopupActionZoomToExtent } from 'Popup/PopupActions/PopupActionZoomToExtent'
import PopupDefaultPage from './PopupDefaultPage'
import PopupPageLayout from './PopupPageLayout'
import olGeomPoint from 'ol/geom/Point'
class SelectEvent extends Event {
constructor (type, selected, deselected, mapBrowserEvent) {
super(type)
this.selected = selected
this.deselected = deselected
this.mapBrowserEvent = mapBrowserEvent
}
}
/**
* @component
* @category Popup
*/
class PopupDefaultInsert extends Component {
constructor (props) {
super(props)
this.state = {
selectedIdx: 0,
}
}
componentDidMount () {
const { features } = this.props
if (features.length) {
this.setState({ selectedIdx: 0 })
this.selectFeature(features[0])
}
}
UNSAFE_componentWillReceiveProps (nextProps) { // eslint-disable-line camelcase
const selectedIdx = typeof nextProps.selectedIdx === 'number' ? nextProps.selectedIdx : this.state.selectedIdx
if (selectedIdx !== this.state.selectedIdx) {
// if props update paging/selected feature, keep state in sync
this.onPageChange(this.state.selectedIdx, selectedIdx)
}
if (nextProps.features.length && nextProps.features.length !== this.props.features.length) {
// safeSelectIdx prevents a bug of setting an index out of range of the features available to select
const safeSelectIdx = selectedIdx > nextProps.features.length ? 0 : selectedIdx
this.setState({ selectedIdx: safeSelectIdx })
this.selectFeature(nextProps.features[safeSelectIdx])
}
}
selectFeature = feature => {
const { selectInteraction } = this.props
const deselected = selectInteraction.getFeatures().getArray()
const selected = [feature]
const event = new SelectEvent('select', selected, deselected)
// clear the previously selected feature before adding newly selected feature so only one feature is "selected" at a time
selectInteraction.getFeatures().clear()
selectInteraction.getFeatures().push(feature)
selectInteraction.dispatchEvent(event)
this.props.onSelectFeature(feature, this.state.selectedIdx)
}
onPageChange = (_, nextIdx) => {
const { features } = this.props
if (features.length) {
// select new feature when paging & update state
this.selectFeature(features[nextIdx])
this.setState({ selectedIdx: nextIdx })
}
}
render () {
const { actions, features, loading, onClose, onSettingsClick, propertiesFilter, translations, onEdit } = this.props
const { selectedIdx } = this.state
const getChildren = feature => {
const pointGeom = feature.getGeometry() instanceof olGeomPoint
let defaultActions = [<PopupActionCopyWkt key={'wkt'} />,
<PopupActionDuplicate key='dupe' />,
<PopupActionRemove key='remove' />,
<PopupActionGoogleMaps key='nav' />,
<PopupActionEdit key='edit' onEdit={onEdit} />,
<PopupActionZoomToExtent key='zoom' />]
if (!pointGeom) defaultActions = [...defaultActions, <PopupActionCut key='cut' />]
return React.Children.map(actions || defaultActions, c => React.cloneElement(c, { feature }))
}
// dedupe the features to remove possible duplicates introduced in ol6
const dedupedFeatures = [...new Set(features).values()]
return (
<PopupPageLayout selectedIdx={selectedIdx} onPageChange={this.onPageChange} data-testid='popup-insert-default'>
{dedupedFeatures.length
? dedupedFeatures.map((f, i) => (
<PopupDefaultPage
attributes={propertiesFilter(f.getProperties())}
key={i}
loading={loading}
onClose={onClose}
onSettingsClick={onSettingsClick}
title={f.get('title') || `Feature ${i + 1}`}
translations={translations}
subtitle={f.get('featuretype') || null}>
{getChildren(f)}
</PopupDefaultPage>
))
: <PopupDefaultPage
title={loading ? 'Loading features' : 'Select a feature'}
loading={loading}
onClose={onClose}
translations={translations} />
}
</PopupPageLayout>
)
}
}
PopupDefaultInsert.defaultProps = {
features: [],
onClose: () => {},
onSelectFeature: () => {},
propertiesFilter: sanitizeProperties,
translations: en,
onEdit: () => false,
}
PopupDefaultInsert.propTypes = {
/** components passed to render as actions */
actions: PropTypes.oneOfType([
PropTypes.arrayOf(PropTypes.node),
PropTypes.node
]),
/** array from which to select a feature */
features: PropTypes.array,
/** put ui into loading state */
loading: PropTypes.bool,
/** callback fired when popup closed with x button */
onClose: PropTypes.func,
/** fired whenever a new feature is selected */
onSelectFeature: PropTypes.func,
/** callback fired if no children passed and default page settings cog is clicked */
onSettingsClick: PropTypes.func,
/** filter properties displayed for a feature */
propertiesFilter: PropTypes.func,
/** index of currently selected page in popup */
selectedIdx: PropTypes.number,
/** reference to openlayers select interaction which */
selectInteraction: PropTypes.object.isRequired,
/** object with key/value pairs for translated strings */
translations: PropTypes.shape({
'_ol_kit.PopupDefaultPage.details': PropTypes.string,
'_ol_kit.PopupDefaultPage.actions': PropTypes.string,
'_ol_kit.PopupDefaultPage.customize': PropTypes.string
}).isRequired,
onEdit: PropTypes.func,
}
export default connectToContext(PopupDefaultInsert)