Skip to content

Commit

Permalink
Attribute table is now a full screen dialog:
Browse files Browse the repository at this point in the history
- This is part of #595.
- Upon clicking on the 'show attribute table' button, a full screen dialog is shown. I've experimented with different solutions, including non-fullscreen. I'm still open for another implementation in floating Window though, if someone would like to give it a go.
- SimpleDialog is _outside_ LayerSwitcher component. I started out with having it inside, but eventually I figured that there could be reasons to call this from other parts of code than just LayerSwitcher. So it's placed in components, subscribes on global observer and renders directly in <App />.
- I'll add a flag, on a per-layer-basis, that will control the visibility of the attribute table button. I'll call it 'showAttributeTableButton'.
- We might like to make it a map setting too, we'll see.
- Finally, added some Tooltips to LayerSwitcherItem.
  • Loading branch information
jacobwod committed Mar 14, 2022
1 parent ad4fffc commit 042960e
Show file tree
Hide file tree
Showing 5 changed files with 195 additions and 63 deletions.
73 changes: 59 additions & 14 deletions new-client/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 2 additions & 1 deletion new-client/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -47,10 +47,11 @@
"@mui/icons-material": "^5.3.1",
"@mui/lab": "^5.0.0-alpha.67",
"@mui/material": "^5.4.0",
"@mui/x-data-grid": "^5.6.1",
"@nieuwlandgeo/sldreader": "^0.2.13",
"@turf/buffer": "^6.5.0",
"@turf/union": "^6.5.0",
"@turf/transform-translate": "^6.5.0",
"@turf/union": "^6.5.0",
"abortcontroller-polyfill": "^1.7.3",
"allsettled-polyfill": "^1.0.4",
"date-fns": "^2.28.0",
Expand Down
2 changes: 2 additions & 0 deletions new-client/src/components/App.js
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import Introduction from "./Introduction";
import Announcement from "./Announcement/Announcement";
import Alert from "./Alert";
import PluginWindows from "./PluginWindows";
import SimpleDialog from "./SimpleDialog";

import Search from "./Search/Search.js";

Expand Down Expand Up @@ -966,6 +967,7 @@ class App extends React.PureComponent {
<PluginWindows
plugins={this.appModel.getBothDrawerAndWidgetPlugins()}
/>
<SimpleDialog globalObserver={this.globalObserver} />
</WindowsContainer>
{clean !== true && ( // NB: Special case here, important with !== true, because there is an edge-case where clean===undefined, and we don't want to match on that!
<Drawer
Expand Down
74 changes: 74 additions & 0 deletions new-client/src/components/SimpleDialog.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
import * as React from "react";
import PropTypes from "prop-types";

import Dialog from "@mui/material/Dialog";
import AppBar from "@mui/material/AppBar";
import Toolbar from "@mui/material/Toolbar";
import IconButton from "@mui/material/IconButton";
import Typography from "@mui/material/Typography";
import CloseIcon from "@mui/icons-material/Close";
import Slide from "@mui/material/Slide";

import { DataGrid, GridToolbar } from "@mui/x-data-grid";
SimpleDialog.propTypes = {
globalObserver: PropTypes.object.isRequired,
};

const Transition = React.forwardRef(function Transition(props, ref) {
return <Slide direction="up" ref={ref} {...props} />;
});

export default function SimpleDialog({ globalObserver }) {
const [open, setOpen] = React.useState(false);
const [content, setContent] = React.useState({ columns: [], rows: [] });
const [title, setTitle] = React.useState("");

React.useEffect(() => {
globalObserver.subscribe(
"core.showAttributeTable",
({ title, content }) => {
setTitle(title);
setContent(content);
setOpen(true);
}
);
}, [globalObserver]);

const handleClose = () => {
setOpen(false);
setTitle("");
setContent({ columns: [], rows: [] });
};

return (
<Dialog
onClose={handleClose}
open={open}
fullScreen
TransitionComponent={Transition}
>
<AppBar sx={{ position: "relative" }}>
<Toolbar>
<IconButton
edge="start"
color="inherit"
onClick={handleClose}
aria-label="close"
>
<CloseIcon />
</IconButton>
<Typography sx={{ ml: 2, flex: 1 }} variant="h6" component="div">
{title}
</Typography>
</Toolbar>
</AppBar>
<div style={{ height: "100%", width: "100%" }}>
<DataGrid
rows={content.rows}
columns={content.columns}
components={{ Toolbar: GridToolbar }}
></DataGrid>
</div>
</Dialog>
);
}
106 changes: 58 additions & 48 deletions new-client/src/plugins/LayerSwitcher/components/LayerItem.js
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,7 @@ class LayerItem extends React.PureComponent {
this.infoUrlText = layerInfo.infoUrlText;
this.infoOwner = layerInfo.infoOwner;
this.localObserver = layer.localObserver;
this.showAttributeTableButton = layer.showAttributeTableButton || false;
this.usesMinMaxZoom = this.layerUsesMinMaxZoom();
this.minMaxZoomAlertOnToggleOnly = layer.get("minMaxZoomAlertOnToggleOnly");

Expand Down Expand Up @@ -296,33 +297,37 @@ class LayerItem extends React.PureComponent {

renderInfoButton = () => {
return this.isInfoEmpty() ? null : (
<LayerButtonWrapper>
{this.state.infoVisible ? (
<RemoveCircleIcon onClick={this.toggleInfo} />
) : (
<InfoIcon
onClick={this.toggleInfo}
sx={{
boxShadow: this.state.infoVisible
? "rgb(204, 204, 204) 2px 3px 1px"
: "inherit",
borderRadius: "100%",
}}
/>
)}
</LayerButtonWrapper>
<Tooltip title="Mer information om lagret">
<LayerButtonWrapper>
{this.state.infoVisible ? (
<RemoveCircleIcon onClick={this.toggleInfo} />
) : (
<InfoIcon
onClick={this.toggleInfo}
sx={{
boxShadow: this.state.infoVisible
? "rgb(204, 204, 204) 2px 3px 1px"
: "inherit",
borderRadius: "100%",
}}
/>
)}
</LayerButtonWrapper>
</Tooltip>
);
};

renderMoreButton = () => {
return (
<LayerButtonWrapper>
{this.state.toggleSettings ? (
<CloseIcon onClick={this.toggleSettings} />
) : (
<MoreHorizIcon onClick={this.toggleSettings} />
)}
</LayerButtonWrapper>
<Tooltip title="Fler inställningar">
<LayerButtonWrapper>
{this.state.toggleSettings ? (
<CloseIcon onClick={this.toggleSettings} />
) : (
<MoreHorizIcon onClick={this.toggleSettings} />
)}
</LayerButtonWrapper>
</Tooltip>
);
};

Expand Down Expand Up @@ -510,12 +515,18 @@ class LayerItem extends React.PureComponent {
return <LayerTogglerButtonWrapper>{icon}</LayerTogglerButtonWrapper>;
};

showAttributeTable = async () => {
#showAttributeTable = async () => {
try {
const url = this.props.layer.getSource().get("url").replace("wms", "wfs");
const { LAYERS } = this.props.layer.getSource().getParams();
const getFeatureUrl = `${url}?service=WFS&version=1.0.0&request=GetFeature&typeName=${LAYERS}&maxFeatures=5000&outputFormat=application%2Fjson`;
const describeFeatureTypeUrl = `${url}?service=WFS&version=1.0.0&request=DescribeFeatureType&typeName=${LAYERS}&outputFormat=application%2Fjson`;
// If URL already contains a query string part, we want to glue them together.
const glue = url.includes("?") ? "&" : "?";
const getFeatureUrl = `${url}${glue}service=WFS&version=1.0.0&request=GetFeature&typeName=${LAYERS}&maxFeatures=5000&outputFormat=application%2Fjson`;
const describeFeatureTypeUrl = `${url}${glue}service=WFS&version=1.0.0&request=DescribeFeatureType&typeName=${LAYERS}&outputFormat=application%2Fjson`;
// TODO: QGIS Server doesn't support JSON response for DescribeFeatureType. We must
// fetch the result as GML2 and then parse it accordingly. This will require
// some more work than the current approach.
// const describeFeatureTypeUrl = `${url}${glue}service=WFS&version=1.0.0&request=DescribeFeatureType&typeName=${LAYERS}`;
const r1 = await fetch(getFeatureUrl);
const features = await r1.json();
const r2 = await fetch(describeFeatureTypeUrl);
Expand All @@ -530,31 +541,25 @@ class LayerItem extends React.PureComponent {
field: c.name,
headerName: c.name,
type: c.localType === "int" ? "number" : c.localType, // DataGrid wants 'number', not 'int', see https://mui.com/components/data-grid/columns/#column-types
flex: 1,
};
});

const rows = features.features.map((r) => r.properties);

// These are now ready for MUI's DataGrid:
console.log("columns: ", columns);
console.log("rows: ", rows);

/**
* TODO:
* Proposed next steps:
* Add a AttributeDialog.js component
* It should listen for an event on localObserver
* From here, we send an event with some payload:
* this.localObserver.publish("showAttributeTable", {columns, rows});
*
* The Dialog takes care of closing itself, so we don't need to do anything more here.
*
* That's of course just one way. Another could be showing AttributeTable in
* a separate Window, hence allowing for displaying multiple tables next
* to each other.
*/
const rows = features.features.map((r, i) => {
return { ...r.properties, id: i };
});

this.props.app.globalObserver.publish("core.showAttributeTable", {
title: `${this.caption} (${LAYERS})`,
content: { columns, rows },
});
} catch (error) {
console.error(error);
console.log(this);
this.props.enqueueSnackbar(
`Serverfel: attributtabellen för lagret "${this.caption}" kunde inte visas`,
{ variant: "error" }
);
}
};

Expand Down Expand Up @@ -615,10 +620,15 @@ class LayerItem extends React.PureComponent {
)}
{this.renderStatusButton()}
{this.renderInfoButton()}

{this.showAttributeTableButton && (
<Tooltip title="Visa lagrets attributtabell">
<LayerButtonWrapper>
<TableViewIcon onClick={this.#showAttributeTable} />
</LayerButtonWrapper>
</Tooltip>
)}
{this.renderMoreButton()}
<LayerButtonWrapper>
<TableViewIcon onClick={this.showAttributeTable} />
</LayerButtonWrapper>
</LayerButtonsContainer>
</LayerItemWrapper>
<div>
Expand Down

0 comments on commit 042960e

Please sign in to comment.