feat(netlify-cms-widget-map): add map widget (#2051)

This commit is contained in:
friedjoff
2019-02-05 23:41:21 +01:00
committed by Shawn Erquhart
parent 627e600d29
commit 18f34d2aca
13 changed files with 352 additions and 2 deletions

View File

@ -0,0 +1,13 @@
import React from 'react';
import PropTypes from 'prop-types';
import { WidgetPreviewContainer } from 'netlify-cms-ui-default';
const MapPreview = ({ value }) => (
<WidgetPreviewContainer>{value ? value.toString() : null}</WidgetPreviewContainer>
);
MapPreview.propTypes = {
value: PropTypes.string,
};
export default MapPreview;

View File

@ -0,0 +1,5 @@
import withMapControl from './withMapControl';
export { withMapControl };
export const MapControl = withMapControl();
export MapPreview from './MapPreview';

View File

@ -0,0 +1,76 @@
import olStyles from 'ol/ol.css';
import Map from 'ol/Map.js';
import View from 'ol/View.js';
import GeoJSON from 'ol/format/GeoJSON';
import Draw from 'ol/interaction/Draw.js';
import TileLayer from 'ol/layer/Tile.js';
import VectorLayer from 'ol/layer/Vector.js';
import OSMSource from 'ol/source/OSM.js';
import VectorSource from 'ol/source/Vector.js';
import PropTypes from 'prop-types';
import React from 'react';
import { injectGlobal } from 'react-emotion';
injectGlobal`
${olStyles}
`;
const formatOptions = {
dataProjection: 'EPSG:4326',
featureProjection: 'EPSG:3857',
};
const getDefaultFormat = () => new GeoJSON(formatOptions);
const getDefaultMap = (target, featuresLayer) =>
new Map({
target,
layers: [new TileLayer({ source: new OSMSource() }), featuresLayer],
view: new View({ center: [0, 0], zoom: 2 }),
});
export default function withMapControl({ getFormat, getMap } = {}) {
return class MapControl extends React.Component {
static propTypes = {
onChange: PropTypes.func.isRequired,
field: PropTypes.object.isRequired,
value: PropTypes.node,
};
static defaultProps = {
value: '',
};
constructor(props) {
super(props);
this.mapContainer = React.createRef();
}
componentDidMount() {
const { field, onChange, value } = this.props;
const format = getFormat ? getFormat(field) : getDefaultFormat(field);
const features = value ? [format.readFeature(value)] : [];
const featuresSource = new VectorSource({ features, wrapX: false });
const featuresLayer = new VectorLayer({ source: featuresSource });
const target = this.mapContainer.current;
const map = getMap ? getMap(target, featuresLayer) : getDefaultMap(target, featuresLayer);
if (features.length > 0) {
map.getView().fit(featuresSource.getExtent(), { maxZoom: 16, padding: [80, 80, 80, 80] });
}
const draw = new Draw({ source: featuresSource, type: field.get('type', 'Point') });
map.addInteraction(draw);
const writeOptions = { decimals: field.get('decimals', 7) };
draw.on('drawend', ({ feature }) => {
featuresSource.clear();
onChange(format.writeGeometry(feature.getGeometry(), writeOptions));
});
}
render() {
return <div ref={this.mapContainer}> </div>;
}
};
}