mirror of
https://github.com/maputnik/editor.git
synced 2026-08-24 14:07:33 +00:00
Merge remote-tracking branch 'upstream/master' into feature/ol-experimental-support
This commit is contained in:
@@ -9,12 +9,14 @@ class Button extends React.Component {
|
|||||||
onClick: PropTypes.func,
|
onClick: PropTypes.func,
|
||||||
style: PropTypes.object,
|
style: PropTypes.object,
|
||||||
className: PropTypes.string,
|
className: PropTypes.string,
|
||||||
children: PropTypes.node
|
children: PropTypes.node,
|
||||||
|
disabled: PropTypes.bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
return <button
|
return <button
|
||||||
onClick={this.props.onClick}
|
onClick={this.props.onClick}
|
||||||
|
disabled={this.props.disabled}
|
||||||
aria-label={this.props["aria-label"]}
|
aria-label={this.props["aria-label"]}
|
||||||
className={classnames("maputnik-button", this.props.className)}
|
className={classnames("maputnik-button", this.props.className)}
|
||||||
data-wd-key={this.props["data-wd-key"]}
|
data-wd-key={this.props["data-wd-key"]}
|
||||||
|
|||||||
@@ -124,7 +124,8 @@ export default class Toolbar extends React.Component {
|
|||||||
className="maputnik-toolbar-logo"
|
className="maputnik-toolbar-logo"
|
||||||
>
|
>
|
||||||
<img src={logoImage} alt="Maputnik" />
|
<img src={logoImage} alt="Maputnik" />
|
||||||
<h1>Maputnik
|
<h1>
|
||||||
|
<span className="maputnik-toolbar-name">{pkgJson.name}</span>
|
||||||
<span className="maputnik-toolbar-version">v{pkgJson.version}</span>
|
<span className="maputnik-toolbar-version">v{pkgJson.version}</span>
|
||||||
</h1>
|
</h1>
|
||||||
</a>
|
</a>
|
||||||
|
|||||||
@@ -32,6 +32,16 @@ export default class FunctionSpecProperty extends React.Component {
|
|||||||
]),
|
]),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
getFieldFunctionType(fieldSpec) {
|
||||||
|
if (fieldSpec.expression.interpolated) {
|
||||||
|
return "exponential"
|
||||||
|
}
|
||||||
|
if (fieldSpec.type === "number") {
|
||||||
|
return "interval"
|
||||||
|
}
|
||||||
|
return "categorical"
|
||||||
|
}
|
||||||
|
|
||||||
addStop = () => {
|
addStop = () => {
|
||||||
const stops = this.props.value.stops.slice(0)
|
const stops = this.props.value.stops.slice(0)
|
||||||
const lastStop = stops[stops.length - 1]
|
const lastStop = stops[stops.length - 1]
|
||||||
@@ -80,12 +90,14 @@ export default class FunctionSpecProperty extends React.Component {
|
|||||||
}
|
}
|
||||||
|
|
||||||
makeDataFunction = () => {
|
makeDataFunction = () => {
|
||||||
|
const functionType = this.getFieldFunctionType(this.props.fieldSpec);
|
||||||
|
const stopValue = functionType === 'categorical' ? '' : 0;
|
||||||
const dataFunc = {
|
const dataFunc = {
|
||||||
property: "",
|
property: "",
|
||||||
type: "categorical",
|
type: functionType,
|
||||||
stops: [
|
stops: [
|
||||||
[{zoom: 6, value: 0}, this.props.value],
|
[{zoom: 6, value: stopValue}, this.props.value || stopValue],
|
||||||
[{zoom: 10, value: 0}, this.props.value]
|
[{zoom: 10, value: stopValue}, this.props.value || stopValue]
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
this.props.onChange(this.props.fieldName, dataFunc)
|
this.props.onChange(this.props.fieldName, dataFunc)
|
||||||
|
|||||||
@@ -30,7 +30,7 @@ export default class DataProperty extends React.Component {
|
|||||||
}
|
}
|
||||||
|
|
||||||
getFieldFunctionType(fieldSpec) {
|
getFieldFunctionType(fieldSpec) {
|
||||||
if (fieldSpec.function === "interpolated") {
|
if (fieldSpec.expression.interpolated) {
|
||||||
return "exponential"
|
return "exponential"
|
||||||
}
|
}
|
||||||
if (fieldSpec.type === "number") {
|
if (fieldSpec.type === "number") {
|
||||||
@@ -39,8 +39,8 @@ export default class DataProperty extends React.Component {
|
|||||||
return "categorical"
|
return "categorical"
|
||||||
}
|
}
|
||||||
|
|
||||||
getDataFunctionTypes(functionType) {
|
getDataFunctionTypes(fieldSpec) {
|
||||||
if (functionType === "interpolated") {
|
if (fieldSpec.expression.interpolated) {
|
||||||
return ["categorical", "interval", "exponential"]
|
return ["categorical", "interval", "exponential"]
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
@@ -150,7 +150,7 @@ export default class DataProperty extends React.Component {
|
|||||||
<SelectInput
|
<SelectInput
|
||||||
value={this.props.value.type}
|
value={this.props.value.type}
|
||||||
onChange={propVal => this.changeDataProperty("type", propVal)}
|
onChange={propVal => this.changeDataProperty("type", propVal)}
|
||||||
options={this.getDataFunctionTypes(this.props.fieldSpec.function)}
|
options={this.getDataFunctionTypes(this.props.fieldSpec)}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -28,7 +28,7 @@ export default class FunctionButtons extends React.Component {
|
|||||||
/>
|
/>
|
||||||
</Button>
|
</Button>
|
||||||
|
|
||||||
if (this.props.fieldSpec['property-function'] && ['piecewise-constant', 'interpolated'].indexOf(this.props.fieldSpec['function']) !== -1) {
|
if (this.props.fieldSpec['property-type'] === 'data-driven') {
|
||||||
makeDataButton = <Button
|
makeDataButton = <Button
|
||||||
className="maputnik-make-data-function"
|
className="maputnik-make-data-function"
|
||||||
onClick={this.props.onDataClick}
|
onClick={this.props.onDataClick}
|
||||||
|
|||||||
@@ -10,6 +10,10 @@ export default class CollapseAlt extends React.Component {
|
|||||||
children: PropTypes.element.isRequired
|
children: PropTypes.element.isRequired
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static defaultProps = {
|
||||||
|
isActive: true
|
||||||
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
if (accessibility.reducedMotionEnabled()) {
|
if (accessibility.reducedMotionEnabled()) {
|
||||||
return (
|
return (
|
||||||
|
|||||||
@@ -31,7 +31,9 @@ class IconAction extends React.Component {
|
|||||||
static propTypes = {
|
static propTypes = {
|
||||||
action: PropTypes.string.isRequired,
|
action: PropTypes.string.isRequired,
|
||||||
onClick: PropTypes.func.isRequired,
|
onClick: PropTypes.func.isRequired,
|
||||||
wdKey: PropTypes.string
|
wdKey: PropTypes.string,
|
||||||
|
classBlockName: PropTypes.string,
|
||||||
|
classBlockModifier: PropTypes.string,
|
||||||
}
|
}
|
||||||
|
|
||||||
renderIcon() {
|
renderIcon() {
|
||||||
@@ -44,10 +46,21 @@ class IconAction extends React.Component {
|
|||||||
}
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
|
const {classBlockName, classBlockModifier} = this.props;
|
||||||
|
|
||||||
|
let classAdditions = '';
|
||||||
|
if (classBlockName) {
|
||||||
|
classAdditions = `maputnik-layer-list-icon-action__${classBlockName}`;
|
||||||
|
|
||||||
|
if (classBlockModifier) {
|
||||||
|
classAdditions += ` maputnik-layer-list-icon-action__${classBlockName}--${classBlockModifier}`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return <button
|
return <button
|
||||||
tabIndex="-1"
|
tabIndex="-1"
|
||||||
title={this.props.action}
|
title={this.props.action}
|
||||||
className="maputnik-layer-list-icon-action"
|
className={`maputnik-layer-list-icon-action ${classAdditions}`}
|
||||||
data-wd-key={this.props.wdKey}
|
data-wd-key={this.props.wdKey}
|
||||||
onClick={this.props.onClick}
|
onClick={this.props.onClick}
|
||||||
>
|
>
|
||||||
@@ -90,6 +103,8 @@ class LayerListItem extends React.Component {
|
|||||||
}
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
|
const visibilityAction = this.props.visibility === 'visible' ? 'show' : 'hide';
|
||||||
|
|
||||||
return <li
|
return <li
|
||||||
key={this.props.layerId}
|
key={this.props.layerId}
|
||||||
onClick={e => this.props.onLayerSelect(this.props.layerId)}
|
onClick={e => this.props.onLayerSelect(this.props.layerId)}
|
||||||
@@ -105,16 +120,20 @@ class LayerListItem extends React.Component {
|
|||||||
<IconAction
|
<IconAction
|
||||||
wdKey={"layer-list-item:"+this.props.layerId+":delete"}
|
wdKey={"layer-list-item:"+this.props.layerId+":delete"}
|
||||||
action={'delete'}
|
action={'delete'}
|
||||||
|
classBlockName="delete"
|
||||||
onClick={e => this.props.onLayerDestroy(this.props.layerId)}
|
onClick={e => this.props.onLayerDestroy(this.props.layerId)}
|
||||||
/>
|
/>
|
||||||
<IconAction
|
<IconAction
|
||||||
wdKey={"layer-list-item:"+this.props.layerId+":copy"}
|
wdKey={"layer-list-item:"+this.props.layerId+":copy"}
|
||||||
action={'duplicate'}
|
action={'duplicate'}
|
||||||
|
classBlockName="duplicate"
|
||||||
onClick={e => this.props.onLayerCopy(this.props.layerId)}
|
onClick={e => this.props.onLayerCopy(this.props.layerId)}
|
||||||
/>
|
/>
|
||||||
<IconAction
|
<IconAction
|
||||||
wdKey={"layer-list-item:"+this.props.layerId+":toggle-visibility"}
|
wdKey={"layer-list-item:"+this.props.layerId+":toggle-visibility"}
|
||||||
action={this.props.visibility === 'visible' ? 'show' : 'hide'}
|
action={visibilityAction}
|
||||||
|
classBlockName="visibility"
|
||||||
|
classBlockModifier={visibilityAction}
|
||||||
onClick={e => this.props.onLayerVisibilityToggle(this.props.layerId)}
|
onClick={e => this.props.onLayerVisibilityToggle(this.props.layerId)}
|
||||||
/>
|
/>
|
||||||
</li>
|
</li>
|
||||||
|
|||||||
@@ -51,7 +51,9 @@ class OpenModal extends React.Component {
|
|||||||
|
|
||||||
constructor(props) {
|
constructor(props) {
|
||||||
super(props);
|
super(props);
|
||||||
this.state = {};
|
this.state = {
|
||||||
|
styleUrl: ""
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
clearError() {
|
clearError() {
|
||||||
@@ -102,6 +104,7 @@ class OpenModal extends React.Component {
|
|||||||
})
|
})
|
||||||
.catch((err) => {
|
.catch((err) => {
|
||||||
this.setState({
|
this.setState({
|
||||||
|
error: `Failed to load: '${styleUrl}'`,
|
||||||
activeRequest: null,
|
activeRequest: null,
|
||||||
activeRequestUrl: null
|
activeRequestUrl: null
|
||||||
});
|
});
|
||||||
@@ -150,10 +153,19 @@ class OpenModal extends React.Component {
|
|||||||
}
|
}
|
||||||
|
|
||||||
onOpenToggle() {
|
onOpenToggle() {
|
||||||
|
this.setState({
|
||||||
|
styleUrl: ""
|
||||||
|
});
|
||||||
this.clearError();
|
this.clearError();
|
||||||
this.props.onOpenToggle();
|
this.props.onOpenToggle();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
onChangeUrl = () => {
|
||||||
|
this.setState({
|
||||||
|
styleUrl: this.styleUrlElement.value
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
const styleOptions = publicStyles.map(style => {
|
const styleOptions = publicStyles.map(style => {
|
||||||
return <PublicStyle
|
return <PublicStyle
|
||||||
@@ -197,9 +209,22 @@ class OpenModal extends React.Component {
|
|||||||
<p>
|
<p>
|
||||||
Load from a URL. Note that the URL must have <a href="https://enable-cors.org" target="_blank" rel="noopener noreferrer">CORS enabled</a>.
|
Load from a URL. Note that the URL must have <a href="https://enable-cors.org" target="_blank" rel="noopener noreferrer">CORS enabled</a>.
|
||||||
</p>
|
</p>
|
||||||
<input data-wd-key="open-modal.url.input" type="text" ref={(input) => this.styleUrlElement = input} className="maputnik-input" placeholder="Enter URL..."/>
|
<input
|
||||||
|
data-wd-key="open-modal.url.input"
|
||||||
|
type="text"
|
||||||
|
ref={(input) => this.styleUrlElement = input}
|
||||||
|
className="maputnik-input"
|
||||||
|
placeholder="Enter URL..."
|
||||||
|
value={this.state.styleUrl}
|
||||||
|
onChange={this.onChangeUrl}
|
||||||
|
/>
|
||||||
<div>
|
<div>
|
||||||
<Button data-wd-key="open-modal.url.button" className="maputnik-big-button" onClick={this.onOpenUrl}>Open URL</Button>
|
<Button
|
||||||
|
data-wd-key="open-modal.url.button"
|
||||||
|
className="maputnik-big-button"
|
||||||
|
onClick={this.onOpenUrl}
|
||||||
|
disabled={this.state.styleUrl.length < 1}
|
||||||
|
>Open URL</Button>
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
|
|||||||
@@ -34,5 +34,29 @@
|
|||||||
"title": "Empty Style",
|
"title": "Empty Style",
|
||||||
"url": "https://rawgit.com/maputnik/editor/master/src/config/empty-style.json",
|
"url": "https://rawgit.com/maputnik/editor/master/src/config/empty-style.json",
|
||||||
"thumbnail": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAYAAAAECAQAAAAHDYbIAAAAEUlEQVR42mP8/58BDhiJ4wAA974H/U5Xe1oAAAAASUVORK5CYII="
|
"thumbnail": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAYAAAAECAQAAAAHDYbIAAAAEUlEQVR42mP8/58BDhiJ4wAA974H/U5Xe1oAAAAASUVORK5CYII="
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "os-zoomstack-outdoor",
|
||||||
|
"title": "Zoomstack Outdoor",
|
||||||
|
"url": "https://s3-eu-west-1.amazonaws.com/tiles.os.uk/styles/open-zoomstack-outdoor/style.json",
|
||||||
|
"thumbnail": "https://maputnik.github.io/thumbnails/os-zoomstack-outdoor.png"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "os-zoomstack-road",
|
||||||
|
"title": "Zoomstack Road",
|
||||||
|
"url": "https://s3-eu-west-1.amazonaws.com/tiles.os.uk/styles/open-zoomstack-road/style.json",
|
||||||
|
"thumbnail": "https://maputnik.github.io/thumbnails/os-zoomstack-road.png"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "os-zoomstack-light",
|
||||||
|
"title": "Zoomstack Light",
|
||||||
|
"url": "https://s3-eu-west-1.amazonaws.com/tiles.os.uk/styles/open-zoomstack-light/style.json",
|
||||||
|
"thumbnail": "https://maputnik.github.io/thumbnails/os-zoomstack-light.png"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "os-zoomstack-night",
|
||||||
|
"title": "Zoomstack Night",
|
||||||
|
"url": "https://s3-eu-west-1.amazonaws.com/tiles.os.uk/styles/open-zoomstack-night/style.json",
|
||||||
|
"thumbnail": "https://maputnik.github.io/thumbnails/os-zoomstack-night.png"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"openmaptiles": {
|
"openmaptiles": {
|
||||||
"type": "vector",
|
"type": "vector",
|
||||||
"url": "https://free.tilehosting.com/data/v3.json?key={key}",
|
"url": "https://maps.tilehosting.com/data/v3.json?key={key}",
|
||||||
"title": "OpenMapTiles"
|
"title": "OpenMapTiles"
|
||||||
},
|
},
|
||||||
"thunderforest_transport": {
|
"thunderforest_transport": {
|
||||||
@@ -13,5 +13,10 @@
|
|||||||
"type": "vector",
|
"type": "vector",
|
||||||
"url": "https://tile.thunderforest.com/thunderforest.outdoors-v1.json?apikey={key}",
|
"url": "https://tile.thunderforest.com/thunderforest.outdoors-v1.json?apikey={key}",
|
||||||
"title": "Thunderforest Outdoors (heavy)"
|
"title": "Thunderforest Outdoors (heavy)"
|
||||||
|
},
|
||||||
|
"open_zoomstack": {
|
||||||
|
"type": "vector",
|
||||||
|
"url": "https://s3-eu-west-1.amazonaws.com/tiles.os.uk/data/vector/open-zoomstack/config.json",
|
||||||
|
"title": "OS Open Zoomstack"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -77,6 +77,12 @@
|
|||||||
background-color: lighten($color-midgray, 12);
|
background-color: lighten($color-midgray, 12);
|
||||||
color: $color-white;
|
color: $color-white;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
&:disabled {
|
||||||
|
background-color: darken($color-midgray, 5);
|
||||||
|
color: $color-midgray;
|
||||||
|
cursor: not-allowed;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.maputnik-big-button {
|
.maputnik-big-button {
|
||||||
|
|||||||
+20
-9
@@ -58,22 +58,33 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.maputnik-layer-list-icon-action {
|
||||||
|
background: initial;
|
||||||
|
border: none;
|
||||||
|
padding: 0 2px;
|
||||||
|
|
||||||
|
svg {
|
||||||
|
fill: darken($color-lowgray, 20);
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
fill: $color-white;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.maputnik-layer-list-icon-action__visibility--hide {
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
|
||||||
.maputnik-layer-list-item:hover,
|
.maputnik-layer-list-item:hover,
|
||||||
.maputnik-layer-list-item-selected {
|
.maputnik-layer-list-item-selected {
|
||||||
background-color: lighten($color-black, 2);
|
background-color: lighten($color-black, 2);
|
||||||
|
|
||||||
.maputnik-layer-list-icon-action {
|
.maputnik-layer-list-icon-action {
|
||||||
display: block;
|
display: block;
|
||||||
background: initial;
|
|
||||||
border: none;
|
|
||||||
padding: 0 2px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.maputnik-layer-list-icon-action svg {
|
svg {
|
||||||
fill: darken($color-lowgray, 0.5);
|
fill: darken($color-lowgray, 0.5);
|
||||||
|
|
||||||
&:hover {
|
|
||||||
fill: $color-white;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -80,6 +80,10 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.maputnik-toolbar-name {
|
||||||
|
text-transform: capitalize;
|
||||||
|
}
|
||||||
|
|
||||||
.maputnik-toolbar-version {
|
.maputnik-toolbar-version {
|
||||||
font-size: 10px;
|
font-size: 10px;
|
||||||
margin-left: 4px;
|
margin-left: 4px;
|
||||||
|
|||||||
Reference in New Issue
Block a user