Leaflet plugin inside react-leaflet - leaflet

I would like to use Leaflet.VectorGrid plugin and I have a question. Is there an example how to create custom component in react-leaflet?

React-Leaflet aims to provide all the controls and layers provided by Leaflet, but it does not support any Leaflet plugin.
To create a custom components requires the following steps,
1.) Extend an Abstract class provided by React-Leaflet
2.) Implement createLeafletElement (props: Object): Object method to create Leaflet-element. For example,
createLeafletElement(opts) {
const MapInfo = L.Control.extend({
onAdd: (map) => {
this.panelDiv = L.DomUtil.create('div', 'info');
return this.panelDiv;
}
});
return new MapInfo({ position: 'bottomleft' });
}
3.) Using withLeaflet - Wrap your component. For example,
export default withLeaflet(MapInfo);
This Example will help you - https://codesandbox.io/s/p5zzmnlk8j
Also Please refer to this documentation https://react-leaflet.js.org/docs/en/custom-components.html

Related

Clearing markers in cluster before update in Leaflet Marker Cluster react way

I am using react-leaflet and leaflet.markercluster to create custom MarkerCluster component. The idea is to pass Markers as children to MarkerCluster component. Markers are React components as well. I want to update my markers dynamically, meaning that some will be removed and added to/from array of Markers and rendered as children. LocationMarkers is an array of JSX.Elements
<MarkerCluster maxClusterRadius={100} showCoverageOnHover={false}>
{locationMarkers}
</MarkerCluster>
Removing marker removes it visually from map until I re-add it. After that two markers appear instead of one. Cluster icon also shows wrong count, never decreasing, always increasing. Is that expected behavior with code provided? I tried to clear layers and create markers using new L.Marker() after each update and it worked, but I want to stay with Markers rendering in Map component and passing them between MarkerCluster tags as children.
import { PropsWithChildren } from "react";
import "leaflet.markercluster";
import L, { MarkerClusterGroupOptions } from "leaflet";
type MarkerClusterProps = PropsWithChildren<MarkerClusterGroupOptions>;
const createMarkerCluster = ({ children, ...props }: MarkerClusterProps, context: LeafletContextInterface) => {
const instance = new L.MarkerClusterGroup(props);
return { instance, context: { ...context, layerContainer: instance } }
}
const updateMarkerCluster = (instance: L.MarkerClusterGroup, props: MarkerClusterProps, prevProps: MarkerClusterProps) => {
if (prevProps.children !== props.children) {
// What should go here?
}
}
const MarkerCluster = createPathComponent(createMarkerCluster, updateMarkerCluster);
export default MarkerCluster;
P.S. I've seen solutions like providig unique key to MarkerCluster component on each render so it gets re-rendered completely, but that makes all map blink for a moment and I guess it is not the best way for performance.

Style-optimized vector tiles in react-leaflet v3?

did recently anyone try to render Style-optimized vector tiles in react-leaflet v3?
I tried following the good examples found here in an old post , but they worked only in v2.
With v3 I get
Attempted import error: 'GridLayer' is not exported from 'react-leaflet'.
And GridLayer is no more there indeed. Any idea?
Thanks
Luca
I solved like that
I created this custom component "StyleLayer"
import { useEffect } from "react";
import { useMap } from "react-leaflet";
import L from "leaflet";
import {} from "mapbox-gl-leaflet";
export default function StyleLayer({ styleUrl }) {
const map = useMap();
useEffect(() => {
L.mapboxGL({
style: `${styleUrl}`,
}).addTo(map);
});
return null;
}
And then I used it inside my "Map" component
<MapContainer center={coordinates} zoom={zoom} scrollWheelZoom={scroll}>
<StyleLayer styleUrl="https://myStyleCloudEndpoint.com"></StyleLayer>
</MapContainer>
The main differences with the old examples linked above hence are:
functional component instead of class (and "useEffect" hook)
"useMap" hook by react-leaflet v3, which gives direct access to the map
I made an NPM package for this: https://www.npmjs.com/package/react-leaflet-vector-tile-layer
It exposes a <VectorTileLayer> component which can also be embedded inside the react-leaflet <LayersControl>

Putting Mapbox in React-Starter-Kit

Putting Mapbox in React-Starter-Kit , Im having trouble implementing MapBox in react-starter-kit. I dont know how I could make a component out of a client.
Im using this for npm package of mapbox
I wish I could do
MapBoxComponent extends Component{
static propTypes = {
path: PropTypes.string.isRequired,
content: PropTypes.string.isRequired,
title: PropTypes.string,
};
static contextTypes = {
onSetTitle: PropTypes.func.isRequired,
};
}
Is this possible?
Disclaimer: This is my first time actually, so please bear with me.
There are a couple of React components that wrap mapbox.js and mapboxgl-js:
react-mapbox
react-map-gl
react-leaflet
Here is a related discussion on github: https://github.com/mapbox/mapbox.js/issues/951

In AEM6, How do I hide a specific component field based on pages for certain country only?

In AEM6, How do I hide a specific component field based on pages for certain country only ?
You can write custom dialog/widget plugin to do that. This is how you attach plugin to your widget:
<title jcr:primaryType="cq:Widget"
fieldLabel="Field to hide"
plugins="hideFieldPlugin"
name="./fieldToHide"
xtype="textfield" />
Next, we need to write plugin and register it:
(function ($) {
var plugin = CQ.Ext.extend(CQ.Ext.emptyFn, {
init: function (fieldToHide) {
var url = CQ.HTTP.getPath();
if (this.shouldBeHidden(url)) {
fieldToHide.hide().disable();
}
},
shouldBeHidden: function (url) {
// some logic
return true;
}
});
CQ.Ext.ComponentMgr.registerPlugin("hideFieldPlugin", plugin);
}($CQ));
JavaScript file needs to be included in Classic UI edit mode. Best way to do that is to use your own custom clientlib or use already existing category, cq.wcm.edit.
If you have more complex logic which goes across multiple widgets, you can attach plugin on dialog level and navigate to the widget objects using dialog.find method.

Create jQuery ui resizable instance using selector added to DOM by jQuery

I'm trying to start a jquery ui resizable instance, but using a selector added to the DOM by jquery itself. This is a basic example of my script:
HTML:
<div class='lyr'></div>
jQuery:
// Add class
$('lyr').addClass('fixed');
// Resizable
$('.fixed').resizable({
aspectRatio: true,
handles: 'all'
});
I've thought about using something along the lines of live() or bind() but I have no event to bind to. Any help appreciated.
I have used the LiveQuery plugin - http://brandonaaron.net/code/livequery/docs in the past to be able to target elements added to the dom, like in your case.
If I've got this right, you want anything on the page which has the class "fixed" to be resizable, even if the class is added after the page has loaded? You're right that live, bind and delegate won't help here.
I can think of two possibilities, neither lovely.
First, set up a live "mouseenter" event which will make the element resizable if it wasn't before:
$(body).delegate(".fixed", "mouseenter", function(ev) {
var target = $(ev.target);
if (target.data("resizable")) return;
target.resizable({
aspectRatio: true,
handles: 'all'
});
})
This gets us round the problem of having no event to bind to.
Alternatively, you could monkeypatch jQuery.fn.addClass:
var classRe = new RegExp(c + className + \b);
._addClass = jQuery.fn.addClass;
jQuery.fn.addClass = function(className) {
if (classRe.test(classname)) {
if (this.data("resizable")) return;
this.resizable({
aspectRatio: true,
handles: 'all'
});
}
jQuery.fn._addClass.apply(this, arguments);
}
Of course this will only work if the class is added through the addClass method.
Also in your example,
$('lyr').addClass('fixed');
Should probably be:
$('.lyr').addClass('fixed');