-
Notifications
You must be signed in to change notification settings - Fork 44
/
BaseWindowPlugin.js
262 lines (234 loc) · 9.11 KB
/
BaseWindowPlugin.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
import React from "react";
import propTypes from "prop-types";
import { isMobile } from "./../utils/IsMobile";
import { createPortal } from "react-dom";
import { withStyles } from "@material-ui/core/styles";
import { withTheme } from "@material-ui/styles";
import {
Hidden,
ListItem,
ListItemIcon,
ListItemText,
} from "@material-ui/core";
import Window from "../components/Window.js";
import Card from "../components/Card.js";
import PluginControlButton from "../components/PluginControlButton";
const styles = (theme) => {
return {};
};
class BaseWindowPlugin extends React.PureComponent {
static propTypes = {
app: propTypes.object.isRequired,
children: propTypes.object.isRequired,
classes: propTypes.object.isRequired,
custom: propTypes.object.isRequired,
map: propTypes.object.isRequired,
options: propTypes.object.isRequired,
theme: propTypes.object.isRequired,
type: propTypes.string.isRequired,
};
constructor(props) {
super(props);
// 'type' is basically a unique identifier for each plugin
this.type = props.type.toLowerCase() || undefined;
// There will be defaults in props.custom, so that each plugin has own default title/description
this.description = props.options.description || props.custom.description;
// Title and Color are kept in state and not as class properties. Keeping them in state
// ensures re-render when new props arrive and update the state variables (see componentDidUpdate() too).
this.state = {
title: props.options.title || props.custom.title || "Unnamed plugin",
color: props.options.color || props.custom.color || null,
windowVisible: false, // Does not have anything to do with color or title, but must also be set initially
};
// Title is a special case: we want to use the state.title and pass on to Window in order
// to update Window's title dynamically. At the same time, we want all other occurrences,
// e.g. Widget or Drawer button's label to remain the same.
this.title = props.options.title || props.custom.title || "Unnamed plugin";
// Try to get values from admin's option. Fallback to customs from Plugin defaults, or finally to hard-coded values.
this.width = props.options.width || props.custom.width || 400;
this.height = props.options.height || props.custom.height || "auto";
this.position = props.options.position || props.custom.position || "left";
// Register Window in our global register
props.app.registerWindowPlugin(this);
// Subscribe to a global event that makes it possible to show/hide Windows.
// First we prepare a unique event name for each plugin so it looks like '{pluginName}.showWindow'.
const eventName = `${this.type}.showWindow`;
// Next, subscribe to that event, expect 'opts' array.
// To find all places where this event is publish, search for 'globalObserver.publish("show'
props.app.globalObserver.subscribe(eventName, (opts) => {
this.showWindow(opts);
});
// Same as above, but to close the window.
const closeEventName = `${this.type}.closeWindow`;
props.app.globalObserver.subscribe(closeEventName, () => {
this.closeWindow();
});
}
// Runs on initial render.
componentDidMount() {
//If on a mobile, and there is a specific visibleAtStart setting for mobile, check the mobile setting.
//visibleAtStart is false by default. Change to true only if visibleAtStartMobile really is 'true'.
//Otherwise, if not on mobile, or there is no specific mobile setting, change to true only if visibleAtStart option really is 'true'.
if (isMobile && this.props.options.visibleAtStartMobile !== undefined) {
if (this.props.options.visibleAtStartMobile === true) {
this.setState({
windowVisible: true,
});
}
} else if (this.props.options.visibleAtStart === true) {
this.setState({
windowVisible: true,
});
}
}
// Does not run on initial render, but runs on subsequential re-renders.
componentDidUpdate(prevProps) {
// Window's title and color can be updated on-the-flight, so we keep them
// in state and ensure that state is updated when new props arrive.
prevProps.custom.title !== this.props.custom.title &&
this.setState({ title: this.props.custom.title });
prevProps.custom.color !== this.props.custom.color &&
this.setState({ color: this.props.custom.color });
}
handleButtonClick = (e) => {
this.showWindow({
hideOtherPluginWindows: true,
runCallback: true,
});
this.props.app.globalObserver.publish("core.onlyHideDrawerIfNeeded");
};
showWindow = (opts) => {
const hideOtherPluginWindows = opts.hideOtherPluginWindows || true,
runCallback = opts.runCallback || true;
// Let the App know which tool is currently active
this.props.app.activeTool = this.type;
// Don't continue if visibility hasn't changed
if (this.state.windowVisible === true) {
return null;
}
hideOtherPluginWindows === true && this.props.app.onWindowOpen(this);
this.setState(
{
windowVisible: true,
},
() => {
// If there's a callback defined in custom, run it
runCallback === true &&
typeof this.props.custom.onWindowShow === "function" &&
this.props.custom.onWindowShow();
}
);
};
closeWindow = () => {
// If closeWindow was initiated by the tool that is currently
// active, we should unset the activeTool property
if (this.type === this.props.app.activeTool)
this.props.app.activeTool = undefined;
this.setState(
{
windowVisible: false,
},
() => {
typeof this.props.custom.onWindowHide === "function" &&
this.props.custom.onWindowHide();
}
);
};
renderWindow(mode = "window") {
return (
<>
<Window
globalObserver={this.props.app.globalObserver}
title={this.state.title}
color={this.state.color}
onClose={this.closeWindow}
open={this.state.windowVisible}
onResize={this.props.custom.onResize}
onMaximize={this.props.custom.onMaximize}
onMinimize={this.props.custom.onMinimize}
draggingEnabled={this.props.custom.draggingEnabled}
customPanelHeaderButtons={this.props.custom.customPanelHeaderButtons}
resizingEnabled={this.props.custom.resizingEnabled}
scrollable={this.props.custom.scrollable}
allowMaximizedWindow={this.props.custom.allowMaximizedWindow}
width={this.width}
height={this.height}
position={this.position}
mode={mode}
layerswitcherConfig={this.props.app.config.mapConfig.tools.find(
(t) => t.type === "layerswitcher"
)}
>
{this.props.children}
</Window>
{this.renderDrawerButton()}
{this.props.options.target === "left" &&
this.renderWidgetButton("left-column")}
{this.props.options.target === "right" &&
this.renderWidgetButton("right-column")}
{this.props.options.target === "control" && this.renderControlButton()}
</>
);
}
/**
* This is a bit of a special case. This method will render
* not only plugins specified as Drawer plugins (target===toolbar),
* but it will also render Widget plugins - given some special condition.
*
* Those special conditions are small screens, were there's no screen
* estate to render the Widget button in Map Overlay.
*
* There is another special case needed to be taken care of: the "hidden"
* value on target should not render any button at all, but still load the plugin.
*/
renderDrawerButton() {
return this.props.options.target === "hidden"
? null
: createPortal(
<Hidden mdUp={this.props.options.target !== "toolbar"}>
<ListItem
button
divider={true}
selected={this.state.windowVisible}
onClick={this.handleButtonClick}
>
<ListItemIcon>{this.props.custom.icon}</ListItemIcon>
<ListItemText primary={this.title} />
</ListItem>
</Hidden>,
document.getElementById("plugin-buttons")
);
}
renderWidgetButton(id) {
return createPortal(
// Hide Widget button on small screens, see renderDrawerButton too
<Hidden smDown>
<Card
icon={this.props.custom.icon}
onClick={this.handleButtonClick}
title={this.title}
abstract={this.description}
/>
</Hidden>,
document.getElementById(id)
);
}
renderControlButton() {
return createPortal(
<PluginControlButton
icon={this.props.custom.icon}
onClick={this.handleButtonClick}
title={this.title}
abstract={this.description}
/>,
document.getElementById("plugin-control-buttons")
);
}
render() {
// Don't render if "clean" query param is specified, otherwise go on
return (
this.props.app.config.mapConfig.map.clean !== true && this.renderWindow()
);
}
}
export default withStyles(styles)(withTheme(BaseWindowPlugin));