Skip to content

Commit

Permalink
implement popup support for @wq/map-gl
Browse files Browse the repository at this point in the history
  • Loading branch information
sheppard committed Feb 17, 2022
1 parent 29ec39b commit 59d3884
Show file tree
Hide file tree
Showing 19 changed files with 280 additions and 6 deletions.
58 changes: 58 additions & 0 deletions packages/map-gl/src/components/MapIdentify.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
import { useEffect } from 'react';
import { usePluginReducer } from '@wq/react';

export default function MapIdentify() {
const [{ instance: map, overlays }, { setHighlight }] = usePluginReducer(
'map'
);

useEffect(() => {
if (!map || map._alreadyConfiguredHandlers) {
return;
}
map._alreadyConfiguredHandlers = true;

overlays.forEach(overlay => {
getIdentifyLayers(overlay).forEach(layer => {
map.on('mouseenter', layer, onMouseEnter);
map.on('mouseleave', layer, onMouseLeave);
map.on('click', layer, evt => updateHighlight(evt, overlay));
});
});

function onMouseEnter() {
map.getCanvas().style.cursor = 'pointer';
}
function onMouseLeave() {
map.getCanvas().style.cursor = '';
}

function updateHighlight(evt, overlay) {
const features = evt.features.map(feature => {
feature.popup = overlay.popup;
return feature;
});
setHighlight({ type: 'FeatureCollection', features });
}
}, [map, setHighlight]);

return null;
}

function getIdentifyLayers(overlay) {
if (overlay.identifyLayers) {
return overlay.identifyLayers;
}
if (!overlay.popup) {
return [];
}
if (overlay.type === 'geojson') {
return ['symbol', 'line', 'fill', 'fill-extrusion', 'circle'].map(
type => `${overlay.name}-${type}`
);
} else if (overlay.type === 'vector-tile') {
return ((overlay.style || {}).layers || []).map(layer => layer.id);
} else {
return [];
}
}
3 changes: 3 additions & 0 deletions packages/map-gl/src/components/MapIdentify.native.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
export default function FIXME() {
return null;
}
2 changes: 2 additions & 0 deletions packages/map-gl/src/components/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import Legend from './Legend';
import Map from './Map';
import MapInteraction from './MapInteraction';
import MapAutoZoom from './MapAutoZoom';
import MapIdentify from './MapIdentify';
import OverlayToggle from './OverlayToggle';

export {
Expand All @@ -11,5 +12,6 @@ export {
Map,
MapInteraction,
MapAutoZoom,
MapIdentify,
OverlayToggle
};
3 changes: 3 additions & 0 deletions packages/map-gl/src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import {
Map,
MapInteraction,
MapAutoZoom,
MapIdentify,
Legend,
BasemapToggle,
OverlayToggle
Expand All @@ -29,6 +30,7 @@ export default {
Map,
MapInteraction,
MapAutoZoom,
MapIdentify,
Legend,
BasemapToggle,
OverlayToggle
Expand All @@ -52,6 +54,7 @@ export {
Map,
MapInteraction,
MapAutoZoom,
MapIdentify,
Legend,
BasemapToggle,
OverlayToggle,
Expand Down
4 changes: 4 additions & 0 deletions packages/map/src/components/AutoMap.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ export default function AutoMap({
Map,
MapInteraction,
MapAutoZoom,
MapIdentify,
AutoBasemap,
AutoOverlay,
Legend,
Expand All @@ -40,6 +41,8 @@ export default function AutoMap({
highlight
} = state;

const identify = overlays.some(overlay => !!overlay.popup);

return (
<Map
name={name}
Expand All @@ -51,6 +54,7 @@ export default function AutoMap({
{!!autoZoom && (
<MapAutoZoom name={name} context={context} {...autoZoom} />
)}
{identify && <MapIdentify name={name} context={context} />}
<Legend>
{basemaps.map((conf, i) => (
<BasemapToggle
Expand Down
51 changes: 51 additions & 0 deletions packages/map/src/components/HighlightPopup.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
import React from 'react';
import { useComponents, useViewComponents, usePluginReducer } from '@wq/react';
import PropTypes from 'prop-types';

export default function HighlightPopup() {
const { Popup, View, ScrollView, IconButton } = useComponents(),
[{ highlight }, { clearHighlight }] = usePluginReducer('map'),
features = (highlight && highlight.features) || [];
return (
<View style={{ position: 'absolute', bottom: 0 }}>
<Popup
open={features.length > 0}
onClose={clearHighlight}
variant="persistent"
>
<IconButton
icon="close"
onClick={clearHighlight}
style={{ position: 'absolute', right: 0, top: 0 }}
/>
<ScrollView style={{ maxHeight: '33vh' }}>
{features.map(feature => (
<PopupContent key={feature.id} feature={feature} />
))}
</ScrollView>
</Popup>
</View>
);
}

function PopupContent({ feature }) {
const popupName = feature.popup
? `${feature.popup}-popup`
: 'default-popup',
views = useViewComponents();

let View = views[popupName];
if (!View) {
console.warn(`No popup view named ${popupName}, using default.`);
View = views['default-popup'];
if (!View) {
throw new Error('No popup view named default-popup!');
}
}

return <View feature={feature} />;
}

PopupContent.propTypes = {
feature: PropTypes.object
};
26 changes: 26 additions & 0 deletions packages/map/src/components/PropertyTable.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import React from 'react';
import { PropertyTable } from '@wq/react';
import PropTypes from 'prop-types';

export default function PropertyTableWithoutGeometry({ form, values }) {
return <PropertyTable form={withoutGeometry(form)} values={values} />;
}

function withoutGeometry(form) {
const nform = [];
form.forEach(field => {
if (field.type && field.type.startsWith('geo')) {
return;
}
if (field.type === 'repeat' || field.type === 'group') {
field = { ...field, children: withoutGeometry(field.children) };
}
nform.push(field);
});
return nform;
}

PropertyTableWithoutGeometry.propTypes = {
form: PropTypes.arrayOf(PropTypes.object),
values: PropTypes.object
};
4 changes: 4 additions & 0 deletions packages/map/src/components/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@ import AutoBasemap from './AutoBasemap';
import AutoOverlay from './AutoOverlay';
import StickyMap from './StickyMap';
import OffscreenMaps from './OffscreenMaps';
import HighlightPopup from './HighlightPopup';
import PropertyTable from './PropertyTable';
import Map from './Map';
import Legend, { BasemapToggle, OverlayToggle } from './Legend';
import GeoTools from './GeoTools';
Expand All @@ -13,6 +15,8 @@ export {
AutoOverlay,
StickyMap,
OffscreenMaps,
HighlightPopup,
PropertyTable,
Map,
Legend,
BasemapToggle,
Expand Down
4 changes: 4 additions & 0 deletions packages/map/src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@ import {
AutoOverlay,
StickyMap,
OffscreenMaps,
HighlightPopup,
PropertyTable,
GeoTools
} from './components/index';
import { Geo, EmbeddedGeo } from './inputs/index';
Expand All @@ -43,6 +45,8 @@ export {
AutoOverlay,
StickyMap,
OffscreenMaps,
HighlightPopup,
PropertyTable,
GeoTools,
Geo,
EmbeddedGeo,
Expand Down
9 changes: 7 additions & 2 deletions packages/map/src/map.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ import {
AutoOverlay,
StickyMap,
OffscreenMaps,
HighlightPopup,
PropertyTable,
Map,
Legend,
BasemapToggle,
Expand All @@ -12,7 +14,7 @@ import {
} from './components/index';
import { Geo, EmbeddedGeo } from './inputs/index';
import { GeoHelp, GeoLocate, GeoCode, GeoCoords } from './geotools/index';
import { DefaultList, DefaultDetail } from './views/index';
import { DefaultList, DefaultDetail, DefaultPopup } from './views/index';
import reducer, {
MAP_READY,
MAP_SET_STICKY_PROPS,
Expand Down Expand Up @@ -121,9 +123,12 @@ const map = {
AutoOverlay,
StickyMap,
OffscreenMaps,
HighlightPopup,
PropertyTable,
Map,
MapInteraction: () => null,
MapAutoZoom: () => null,
MapIdentify: () => null,
Legend,
BasemapToggle,
OverlayToggle,
Expand All @@ -135,7 +140,7 @@ const map = {
geotrace: Geo,
geoshape: Geo
},
views: { DefaultList, DefaultDetail },
views: { DefaultList, DefaultDetail, DefaultPopup },
config: {
maps: {}, // Auto-populated from app.config.pages where map == true
bounds: [
Expand Down
4 changes: 3 additions & 1 deletion packages/map/src/views/DefaultList.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import { DefaultList, useComponents, useList } from '@wq/react';

export default function DefaultListWithMap() {
const mapState = useMapState(),
{ AutoMap, StickyMap } = useComponents(),
{ AutoMap, StickyMap, HighlightPopup } = useComponents(),
context = useList();
if (mapState) {
const { mapId } = mapState;
Expand All @@ -13,13 +13,15 @@ export default function DefaultListWithMap() {
<>
<DefaultList />
<StickyMap mapId={mapId} context={context} />
<HighlightPopup />
</>
);
} else {
return (
<>
<DefaultList />
<AutoMap context={context} />
<HighlightPopup />
</>
);
}
Expand Down
51 changes: 51 additions & 0 deletions packages/map/src/views/DefaultPopup.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
import React from 'react';
import {
useComponents,
useReverse,
useConfig,
usePluginState
} from '@wq/react';
import PropTypes from 'prop-types';

export default function DefaultPopup({ feature }) {
const reverse = useReverse(),
{ PropertyTable, View, Fab } = useComponents(),
config = useConfig(),
authState = usePluginState('auth'),
page_config = feature.popup ? config.pages[feature.popup] : null,
perms =
page_config &&
authState &&
authState.config &&
authState.config.pages &&
authState.config.pages[feature.popup];

let form, editUrl;
if (page_config) {
form = page_config.form || [{ name: 'label' }];
if (perms && perms.can_change) {
editUrl = reverse(`${feature.popup}_edit`, feature.id);
}
} else {
form = Object.keys(feature.properties).map(name => ({
name
}));
}
return (
<View
style={{
maxWidth: '70em',
position: 'relative',
marginLeft: 'auto',
marginRight: 'auto'
}}
>
<PropertyTable form={form} values={feature.properties} />
{editUrl && <Fab icon="edit" to={editUrl} />}
</View>
);
}

DefaultPopup.propTypes = {
feature: PropTypes.object
};
3 changes: 2 additions & 1 deletion packages/map/src/views/index.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import DefaultList from './DefaultList';
import DefaultDetail from './DefaultDetail';
import DefaultPopup from './DefaultPopup';

export { DefaultList, DefaultDetail };
export { DefaultList, DefaultDetail, DefaultPopup };
24 changes: 24 additions & 0 deletions packages/material/src/components/Popup.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import React from 'react';
import Drawer from '@material-ui/core/Drawer';
import PropTypes from 'prop-types';

export default function Popup({
anchor = 'bottom',
children,
open,
onClose,
...rest
}) {
return (
<Drawer anchor={anchor} open={open} onClose={onClose} {...rest}>
{children}
</Drawer>
);
}

Popup.propTypes = {
anchor: PropTypes.string,
children: PropTypes.node,
open: PropTypes.bool,
onClose: PropTypes.func
};
3 changes: 3 additions & 0 deletions packages/material/src/components/Popup.native.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
export default function NotImplemented() {
return null; // FIXME
}
Loading

0 comments on commit 59d3884

Please sign in to comment.