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

Leaflet Map Panes #227

Merged
merged 29 commits into from
Oct 4, 2016
Merged

Leaflet Map Panes #227

merged 29 commits into from
Oct 4, 2016

Conversation

rjdestigter
Copy link
Contributor

@rjdestigter rjdestigter commented Sep 23, 2016

Developed component implementing Leaflet's new 'panes' concept.

Component name: MapPane

Accepted props:

  • className
  • style
  • name (Not required, unique id wiill be created if not passed.)

Does Leaflet allow to override its existing panes?
No it does not seem like that. I've allowed passing a "blacklisted" name as prop.

Lifecycle:

  • constructor will create a unique id if no name is passed
  • componentDidMount will check if the pane exists or not and call map.createPane if not.
  • componentWillUnmount will remove the pane from the DOM and map
  • Added documentation and example.

@rjdestigter
Copy link
Contributor Author

Conflicts with next have been resolved.

Copy link
Owner

@PaulLeCam PaulLeCam left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for this PR, I think it's going in the right direction but it seems like many parts could be simplified.

@@ -30,6 +30,7 @@ Check Leaflet documentation for the events associated to each component.
- [BaseTileLayer](Components.md#basetilelayer)
- [Path](Components.md#path)
- [Map](Components.md#map)
- [MapPanes](Components.md#map-pane)
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could you rename it to just Pane to match Leaflet API please?

Copy link
Contributor Author

@rjdestigter rjdestigter Sep 29, 2016

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

  • Done

@@ -11,6 +11,7 @@ You can directly access the Leaflet element created by a component using `this.l
- [MapLayer](#maplayer)
- [Path](#path)
- [Map](#map)
- [MapPane](#map-pane)
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

MapPane -> Pane

Copy link
Contributor Author

@rjdestigter rjdestigter Sep 29, 2016

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

  • Done

/* @flow */
import { PropTypes } from 'react'

export default PropTypes.string
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No need to create an extra PropType if it is as simple as a string I think.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

  • Done

@@ -24,3 +24,4 @@ export TileLayer from './TileLayer'
export Tooltip from './Tooltip'
export WMSTileLayer from './WMSTileLayer'
export ZoomControl from './ZoomControl'
export MapPane from './MapPane'
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

MapPane -> Pane

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

  • Done

_isMounted: false,
}

this._name = name || `pane-${uniqueId()}`
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No need for this._name and this.state._isMounted, we could just have this.state.name to check if the children should be rendered.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

  • Done

const map = this.context.map || this.props.map

if (this._name && map && map._panes) {
map._panes = omit(map._panes, this._name)
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Seems a bit of a hack, is there any other way to cleanup the pane from Leaflet using more public APIs?

Copy link
Contributor Author

@rjdestigter rjdestigter Sep 29, 2016

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There isn't at the moment as far I can find. Browsing Leaflet's source code I could only find:

  • createPane, getPane, and getPanes

Calling L.DomUtil.remove(pane) only removes it from the DOM but not the references leaflet keeps in map._panes and map._paneRenderers

I had to dig for _paneRenderers. It was keeping a reference to the detached pane node causing leaflet to render layers inside it if the pane was added back again.

I find this 'hacky' as well but I couldn't find a cleaner method or api for it at the moment.

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

OK thanks for the explanation, hopefully Leaflet won't remove these anytime soon!

name: PropTypes.string,
children: childrenType,
map: mapType,
zIndex: PropTypes.number,
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

zIndex should be part of the style, unless there is a particular reason to treat it differently?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Syntactic sugar. Since the main purpose of leaflet panes is to have more control over of zIndexes I included it as a prop. Let me know if you want this dropped.

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes I think it's better to just rely on style, I prefer to make the components as "dumb" as possible with no specific logic and let the applications implement custom behavior on top of them.

this.setStyle(props)
}

setStyle ({ style, zIndex } = this.props) {
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Shouldn't need that, React can render the styles itself.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, but React in this case is not in charge of the DOM node right?

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, sorry I missed that from Leaflet's createPane() implementation.

}
}

componentWillReceiveProps (props) {
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Shouldn't be needed.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since React is unaware of the actual DOM node, I have to apply incoming style changes to the pane node.

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I see, yes I missed the fact Leaflet is creating a new DOM node rather than using the one provided. It's too bad it doesn't allow it as we could have completely leveraged React for this, but in that case I think your implementation is needed as such.

@@ -72,4 +72,17 @@ export default class MapComponent extends Component<any, any, any> {
const el = this.leafletElement
if (el) el.fire(type, data)
}

getInstanceOptions (props = {}) {
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could be simply named getOptions()

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

  • Done

@PaulLeCam
Copy link
Owner

@rjdestigter thanks for the changes and explanations.
My only major concern remaining is with allowing to override an existing pane, whether it's a default Leaflet one or another one already created, but I'm happy to merge this PR as-is and work on it myself if you prefer?

@PaulLeCam PaulLeCam mentioned this pull request Sep 30, 2016
@rjdestigter
Copy link
Contributor Author

rjdestigter commented Sep 30, 2016

I suggest we still allow using default leaflet panes but don't set their style in that case. Pane doesn't actually really override existing panes, it just receives them from map.getPane and thus won't create them since it already exists.

Todo's:

  • Remove zIndex prop
  • Disallow usage of existing panes unless they are default leaflet panes
  • Don't apply style to default panes. (Disallow style props for default panes)

@rjdestigter
Copy link
Contributor Author

I also realised className was being completely ignored so I added that in as well. Proper behaviour was added to deal with this.props.name changes.

Also, yes, let's disallow using default names. I thought I needed that feature, but if I want other components such as Popup or any component for that matter to be added to a default pane even if it is nested inside a Pane component, I can just use prop.pane for them.

<Pane name='paris'>
  <GeoJSON data={...}>
    <Popup pane={'popupPane'}

I will disallow default pane names.

@rjdestigter
Copy link
Contributor Author

Ok, done, using default pane names or that of existing panes will throw an error.

@rjdestigter
Copy link
Contributor Author

rjdestigter commented Sep 30, 2016

but I'm happy to merge this PR as-is and work on it myself if you prefer?

I'm good with that if you approve of today's changes. The only thing that I'd like to revisit myself is getChildren, but you are welcome to that as well.

I'm not sure if React.cloneElement is really necessary. map is available through context and if apps using Pane need it to pass down props, they can always extend Pane and override getChildren

@rjdestigter
Copy link
Contributor Author

Also, thanks by the way for letting me collaborate on this project!

@rjdestigter
Copy link
Contributor Author

I acutally went ahead and removed the usage of cloneElement. I tested it using nested Panes and it's working fine.

Also when map.createPane is called I store the used name in this.paneName. This was necessary because in some cases componentWillUnmount is called before this.setState({name}) is persisted to local state causing Pane not to remove the pane.

@PaulLeCam PaulLeCam merged commit c2be3d1 into PaulLeCam:next Oct 4, 2016
@PaulLeCam
Copy link
Owner

Thanks for this PR!
I'll need to play a bit with it to make sure I catch possible edge cases before releasing, but hopefully it should make it to the next RC.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants