mirror of
https://github.com/maputnik/editor.git
synced 2026-07-29 09:17:28 +00:00
Fix tests, types, added data-wd-key
This commit is contained in:
@@ -11,7 +11,7 @@ describe("accessibility", () => {
|
|||||||
|
|
||||||
it("skip link to layer list", () => {
|
it("skip link to layer list", () => {
|
||||||
const selector = "root:skip:layer-list";
|
const selector = "root:skip:layer-list";
|
||||||
should.isExists(selector);
|
should.exist(selector);
|
||||||
when.tab();
|
when.tab();
|
||||||
should.beFocused(selector);
|
should.beFocused(selector);
|
||||||
when.click(selector);
|
when.click(selector);
|
||||||
@@ -20,7 +20,7 @@ describe("accessibility", () => {
|
|||||||
|
|
||||||
it("skip link to layer editor", () => {
|
it("skip link to layer editor", () => {
|
||||||
const selector = "root:skip:layer-editor";
|
const selector = "root:skip:layer-editor";
|
||||||
should.isExists(selector);
|
should.exist(selector);
|
||||||
when.tab().tab();
|
when.tab().tab();
|
||||||
should.beFocused(selector);
|
should.beFocused(selector);
|
||||||
when.click(selector);
|
when.click(selector);
|
||||||
@@ -29,7 +29,7 @@ describe("accessibility", () => {
|
|||||||
|
|
||||||
it("skip link to map view", () => {
|
it("skip link to map view", () => {
|
||||||
const selector = "root:skip:map-view";
|
const selector = "root:skip:map-view";
|
||||||
should.isExists(selector);
|
should.exist(selector);
|
||||||
when.tab().tab().tab();
|
when.tab().tab().tab();
|
||||||
should.beFocused(selector);
|
should.beFocused(selector);
|
||||||
when.click(selector);
|
when.click(selector);
|
||||||
|
|||||||
+29
-17
@@ -73,7 +73,7 @@ export default class MaputnikDriver {
|
|||||||
}
|
}
|
||||||
cy.get(".maputnik-toolbar-link").should("be.visible");
|
cy.get(".maputnik-toolbar-link").should("be.visible");
|
||||||
},
|
},
|
||||||
fillLayersModal: (opts: any) => {
|
fillLayersModal: (opts: {type: string, layer?: string, id?: string}) => {
|
||||||
var type = opts.type;
|
var type = opts.type;
|
||||||
var layer = opts.layer;
|
var layer = opts.layer;
|
||||||
var id;
|
var id;
|
||||||
@@ -84,12 +84,12 @@ export default class MaputnikDriver {
|
|||||||
}
|
}
|
||||||
|
|
||||||
cy.get(
|
cy.get(
|
||||||
this.get.getDataAttribute("add-layer.layer-type", "select")
|
this.get.dataAttribute("add-layer.layer-type", "select")
|
||||||
).select(type);
|
).select(type);
|
||||||
cy.get(this.get.getDataAttribute("add-layer.layer-id", "input")).type(id);
|
cy.get(this.get.dataAttribute("add-layer.layer-id", "input")).type(id);
|
||||||
if (layer) {
|
if (layer) {
|
||||||
cy.get(
|
cy.get(
|
||||||
this.get.getDataAttribute("add-layer.layer-source-block", "input")
|
this.get.dataAttribute("add-layer.layer-source-block", "input")
|
||||||
).type(layer);
|
).type(layer);
|
||||||
}
|
}
|
||||||
this.when.click("add-layer");
|
this.when.click("add-layer");
|
||||||
@@ -103,11 +103,20 @@ export default class MaputnikDriver {
|
|||||||
|
|
||||||
click: (selector: string) => {
|
click: (selector: string) => {
|
||||||
this.helper.when.click(selector);
|
this.helper.when.click(selector);
|
||||||
// cy.get(selector).click({ force: true });
|
},
|
||||||
|
|
||||||
|
clickZoomin: () => {
|
||||||
|
cy.get(".maplibregl-ctrl-zoom-in").click();
|
||||||
|
},
|
||||||
|
|
||||||
|
selectWithin: (selector: string, value: string) => {
|
||||||
|
this.when.within(selector, () => {
|
||||||
|
cy.get("select").select(value);
|
||||||
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
select: (selector: string, value: string) => {
|
select: (selector: string, value: string) => {
|
||||||
cy.get(selector).select(value);
|
this.helper.get.element(selector).select(value);
|
||||||
},
|
},
|
||||||
|
|
||||||
focus: (selector: string) => {
|
focus: (selector: string) => {
|
||||||
@@ -126,8 +135,8 @@ export default class MaputnikDriver {
|
|||||||
openLayersModal: () => {
|
openLayersModal: () => {
|
||||||
this.helper.when.click("layer-list:add-layer");
|
this.helper.when.click("layer-list:add-layer");
|
||||||
|
|
||||||
cy.get(this.get.getDataAttribute("modal:add-layer")).should("exist");
|
cy.get(this.get.dataAttribute("modal:add-layer")).should("exist");
|
||||||
cy.get(this.get.getDataAttribute("modal:add-layer")).should("be.visible");
|
cy.get(this.get.dataAttribute("modal:add-layer")).should("be.visible");
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -135,16 +144,16 @@ export default class MaputnikDriver {
|
|||||||
isMac: () => {
|
isMac: () => {
|
||||||
return Cypress.platform === "darwin";
|
return Cypress.platform === "darwin";
|
||||||
},
|
},
|
||||||
getStyleFromWindow: (win: Window) => {
|
styleFromWindow: (win: Window) => {
|
||||||
const styleId = win.localStorage.getItem("maputnik:latest_style");
|
const styleId = win.localStorage.getItem("maputnik:latest_style");
|
||||||
const styleItem = win.localStorage.getItem(`maputnik:style:${styleId}`);
|
const styleItem = win.localStorage.getItem(`maputnik:style:${styleId}`);
|
||||||
const obj = JSON.parse(styleItem || "");
|
const obj = JSON.parse(styleItem || "");
|
||||||
return obj;
|
return obj;
|
||||||
},
|
},
|
||||||
getExampleFileUrl: () => {
|
exampleFileUrl: () => {
|
||||||
return "http://localhost:8888/example-style.json";
|
return "http://localhost:8888/example-style.json";
|
||||||
},
|
},
|
||||||
getDataAttribute: (key: string, selector?: string): string => {
|
dataAttribute: (key: string, selector?: string): string => {
|
||||||
return `*[data-wd-key='${key}'] ${selector || ""}`;
|
return `*[data-wd-key='${key}'] ${selector || ""}`;
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
@@ -176,23 +185,26 @@ export default class MaputnikDriver {
|
|||||||
|
|
||||||
equalStyleStore: (getter: (obj: any) => any, styleObj: any) => {
|
equalStyleStore: (getter: (obj: any) => any, styleObj: any) => {
|
||||||
cy.window().then((win: any) => {
|
cy.window().then((win: any) => {
|
||||||
const obj = this.get.getStyleFromWindow(win);
|
const obj = this.get.styleFromWindow(win);
|
||||||
assert.deepEqual(getter(obj), styleObj);
|
assert.deepEqual(getter(obj), styleObj);
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
isStyleStoreEqualToExampleFileData: () => {
|
styleStoreEqualToExampleFileData: () => {
|
||||||
cy.window().then((win: any) => {
|
cy.window().then((win: any) => {
|
||||||
const obj = this.get.getStyleFromWindow(win);
|
const obj = this.get.styleFromWindow(win);
|
||||||
cy.fixture("example-style.json").should("deep.equal", obj);
|
cy.fixture("example-style.json").should("deep.equal", obj);
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
isExists: (selector: string) => {
|
exist: (selector: string) => {
|
||||||
this.helper.get.element(selector).should("exist");
|
this.helper.get.element(selector).should("exist");
|
||||||
},
|
},
|
||||||
isSelected: (selector: string, value: string) => {
|
beSelected: (selector: string, value: string) => {
|
||||||
cy.get(selector).find(`option[value="${value}"]`).should("be.selected");
|
this.helper.get.element(selector).find(`option[value="${value}"]`).should("be.selected");
|
||||||
},
|
},
|
||||||
|
containText: (selector: string, text: string) => {
|
||||||
|
this.helper.get.element(selector).should("contain.text", text);
|
||||||
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { default as MaputnikDriver } from "./driver";
|
import MaputnikDriver from "./driver";
|
||||||
|
|
||||||
describe("keyboard", () => {
|
describe("keyboard", () => {
|
||||||
let { beforeAndAfter, given, when, get, should } = new MaputnikDriver();
|
let { beforeAndAfter, given, when, get, should } = new MaputnikDriver();
|
||||||
@@ -45,12 +45,12 @@ describe("keyboard", () => {
|
|||||||
|
|
||||||
it("'i' should change map to inspect mode", () => {
|
it("'i' should change map to inspect mode", () => {
|
||||||
when.typeKeys("i");
|
when.typeKeys("i");
|
||||||
should.isSelected(get.getDataAttribute("nav:inspect"), "inspect");
|
should.beSelected("nav:inspect", "inspect");
|
||||||
});
|
});
|
||||||
|
|
||||||
it("'m' should focus map", () => {
|
it("'m' should focus map", () => {
|
||||||
when.typeKeys("m");
|
when.typeKeys("m");
|
||||||
should.beFocused(".maplibregl-canvas");
|
should.canvasBeFocused();
|
||||||
});
|
});
|
||||||
|
|
||||||
it("'!' should show debug modal", () => {
|
it("'!' should show debug modal", () => {
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
var assert = require("assert");
|
|
||||||
import { v1 as uuid } from "uuid";
|
import { v1 as uuid } from "uuid";
|
||||||
import MaputnikDriver from "./driver";
|
import MaputnikDriver from "./driver";
|
||||||
|
|
||||||
@@ -134,12 +133,9 @@ describe("layers", () => {
|
|||||||
// Setup
|
// Setup
|
||||||
var id = uuid();
|
var id = uuid();
|
||||||
|
|
||||||
when.select(
|
when.selectWithin("add-layer.layer-type", "background");
|
||||||
get.getDataAttribute("add-layer.layer-type", "select"),
|
|
||||||
"background"
|
|
||||||
);
|
|
||||||
when.setValue(
|
when.setValue(
|
||||||
get.getDataAttribute("add-layer.layer-id", "input"),
|
get.dataAttribute("add-layer.layer-id", "input"),
|
||||||
"background:" + id
|
"background:" + id
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -167,7 +163,7 @@ describe("layers", () => {
|
|||||||
|
|
||||||
var id = uuid();
|
var id = uuid();
|
||||||
when.setValue(
|
when.setValue(
|
||||||
get.getDataAttribute("layer-editor.layer-id", "input"),
|
get.dataAttribute("layer-editor.layer-id", "input"),
|
||||||
"foobar:" + id
|
"foobar:" + id
|
||||||
);
|
);
|
||||||
when.click("min-zoom");
|
when.click("min-zoom");
|
||||||
@@ -188,7 +184,7 @@ describe("layers", () => {
|
|||||||
|
|
||||||
when.click("layer-list-item:background:" + bgId);
|
when.click("layer-list-item:background:" + bgId);
|
||||||
when.setValue(
|
when.setValue(
|
||||||
get.getDataAttribute("min-zoom", 'input[type="text"]'),
|
get.dataAttribute("min-zoom", 'input[type="text"]'),
|
||||||
"1"
|
"1"
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -206,8 +202,8 @@ describe("layers", () => {
|
|||||||
);
|
);
|
||||||
|
|
||||||
// AND RESET!
|
// AND RESET!
|
||||||
// driver.setValue(driver.getDataAttribute("min-zoom", "input"), "")
|
// driver.setValue(driver.get.dataAttribute("min-zoom", "input"), "")
|
||||||
// driver.click(driver.getDataAttribute("max-zoom", "input"));
|
// driver.click(driver.get.dataAttribute("max-zoom", "input"));
|
||||||
|
|
||||||
// driver.isStyleStoreEqual((a: any) => a.layers, [
|
// driver.isStyleStoreEqual((a: any) => a.layers, [
|
||||||
// {
|
// {
|
||||||
@@ -222,7 +218,7 @@ describe("layers", () => {
|
|||||||
|
|
||||||
when.click("layer-list-item:background:" + bgId);
|
when.click("layer-list-item:background:" + bgId);
|
||||||
when.setValue(
|
when.setValue(
|
||||||
get.getDataAttribute("max-zoom", 'input[type="text"]'),
|
get.dataAttribute("max-zoom", 'input[type="text"]'),
|
||||||
"1"
|
"1"
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -245,7 +241,7 @@ describe("layers", () => {
|
|||||||
var id = uuid();
|
var id = uuid();
|
||||||
|
|
||||||
when.click("layer-list-item:background:" + bgId);
|
when.click("layer-list-item:background:" + bgId);
|
||||||
when.setValue(get.getDataAttribute("layer-comment", "textarea"), id);
|
when.setValue(get.dataAttribute("layer-comment", "textarea"), id);
|
||||||
|
|
||||||
when.click("layer-editor.layer-id");
|
when.click("layer-editor.layer-id");
|
||||||
|
|
||||||
@@ -324,9 +320,7 @@ describe("layers", () => {
|
|||||||
when.typeKeys(
|
when.typeKeys(
|
||||||
"\uE013\uE013\uE013\uE013\uE013\uE013\uE013\uE013\uE013\uE013\uE013\uE013 {"
|
"\uE013\uE013\uE013\uE013\uE013\uE013\uE013\uE013\uE013\uE013\uE013\uE013 {"
|
||||||
);
|
);
|
||||||
should.isExists(errorSelector);
|
should.exist(errorSelector);
|
||||||
|
|
||||||
when.click("layer-editor.layer-id");
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -8,18 +8,16 @@ describe("map", () => {
|
|||||||
var zoomLevel = 12.37;
|
var zoomLevel = 12.37;
|
||||||
when.setStyle("geojson", zoomLevel);
|
when.setStyle("geojson", zoomLevel);
|
||||||
should.beVisible("maplibre:ctrl-zoom");
|
should.beVisible("maplibre:ctrl-zoom");
|
||||||
// HM TODO
|
should.containText("maplibre:ctrl-zoom", "Zoom: " + zoomLevel);
|
||||||
//driver.getText(".maplibregl-ctrl-zoom") === "Zoom "+(zoomLevel);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it("via map controls", () => {
|
it("via map controls", () => {
|
||||||
var zoomLevel = 12.37;
|
var zoomLevel = 12.37;
|
||||||
when.setStyle("geojson", zoomLevel);
|
when.setStyle("geojson", zoomLevel);
|
||||||
|
|
||||||
when.click("maplibre:ctrl-zoom");
|
|
||||||
should.beVisible("maplibre:ctrl-zoom");
|
should.beVisible("maplibre:ctrl-zoom");
|
||||||
// HM TODO
|
when.clickZoomin();
|
||||||
//driver.getText(".maplibregl-ctrl-zoom") === "Zoom "+(zoomLevel + 1);
|
should.containText("maplibre:ctrl-zoom", "Zoom: "+(zoomLevel + 1));
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
+13
-19
@@ -20,17 +20,17 @@ describe("modals", () => {
|
|||||||
// HM: I was not able to make the following choose file actually to select a file and close the modal...
|
// HM: I was not able to make the following choose file actually to select a file and close the modal...
|
||||||
when.chooseExampleFile();
|
when.chooseExampleFile();
|
||||||
|
|
||||||
should.isStyleStoreEqualToExampleFileData();
|
should.styleStoreEqualToExampleFileData();
|
||||||
});
|
});
|
||||||
|
|
||||||
it("load from url", () => {
|
it("load from url", () => {
|
||||||
var styleFileUrl = get.getExampleFileUrl();
|
var styleFileUrl = get.exampleFileUrl();
|
||||||
|
|
||||||
when.setValue(get.getDataAttribute("modal:open.url.input"), styleFileUrl);
|
when.setValue(get.dataAttribute("modal:open.url.input"), styleFileUrl);
|
||||||
when.click("modal:open.url.button");
|
when.click("modal:open.url.button");
|
||||||
when.waitForExampleFileRequset();
|
when.waitForExampleFileRequset();
|
||||||
|
|
||||||
should.isStyleStoreEqualToExampleFileData();
|
should.styleStoreEqualToExampleFileData();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -67,7 +67,7 @@ describe("modals", () => {
|
|||||||
it("toggle", () => {
|
it("toggle", () => {
|
||||||
when.setStyle("geojson");
|
when.setStyle("geojson");
|
||||||
|
|
||||||
when.select(get.getDataAttribute("nav:inspect", "select"), "inspect");
|
when.selectWithin("nav:inspect", "inspect");
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -77,20 +77,20 @@ describe("modals", () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it("name", () => {
|
it("name", () => {
|
||||||
when.setValue(get.getDataAttribute("modal:settings.name"), "foobar");
|
when.setValue(get.dataAttribute("modal:settings.name"), "foobar");
|
||||||
when.click("modal:settings.owner");
|
when.click("modal:settings.owner");
|
||||||
|
|
||||||
should.equalStyleStore((obj) => obj.name, "foobar");
|
should.equalStyleStore((obj) => obj.name, "foobar");
|
||||||
});
|
});
|
||||||
it("owner", () => {
|
it("owner", () => {
|
||||||
when.setValue(get.getDataAttribute("modal:settings.owner"), "foobar");
|
when.setValue(get.dataAttribute("modal:settings.owner"), "foobar");
|
||||||
when.click("modal:settings.name");
|
when.click("modal:settings.name");
|
||||||
|
|
||||||
should.equalStyleStore((obj) => obj.owner, "foobar");
|
should.equalStyleStore((obj) => obj.owner, "foobar");
|
||||||
});
|
});
|
||||||
it("sprite url", () => {
|
it("sprite url", () => {
|
||||||
when.setValue(
|
when.setValue(
|
||||||
get.getDataAttribute("modal:settings.sprite"),
|
get.dataAttribute("modal:settings.sprite"),
|
||||||
"http://example.com"
|
"http://example.com"
|
||||||
);
|
);
|
||||||
when.click("modal:settings.name");
|
when.click("modal:settings.name");
|
||||||
@@ -99,7 +99,7 @@ describe("modals", () => {
|
|||||||
});
|
});
|
||||||
it("glyphs url", () => {
|
it("glyphs url", () => {
|
||||||
var glyphsUrl = "http://example.com/{fontstack}/{range}.pbf";
|
var glyphsUrl = "http://example.com/{fontstack}/{range}.pbf";
|
||||||
when.setValue(get.getDataAttribute("modal:settings.glyphs"), glyphsUrl);
|
when.setValue(get.dataAttribute("modal:settings.glyphs"), glyphsUrl);
|
||||||
when.click("modal:settings.name");
|
when.click("modal:settings.name");
|
||||||
|
|
||||||
should.equalStyleStore((obj) => obj.glyphs, glyphsUrl);
|
should.equalStyleStore((obj) => obj.glyphs, glyphsUrl);
|
||||||
@@ -108,7 +108,7 @@ describe("modals", () => {
|
|||||||
it("maptiler access token", () => {
|
it("maptiler access token", () => {
|
||||||
var apiKey = "testing123";
|
var apiKey = "testing123";
|
||||||
when.setValue(
|
when.setValue(
|
||||||
get.getDataAttribute(
|
get.dataAttribute(
|
||||||
"modal:settings.maputnik:openmaptiles_access_token"
|
"modal:settings.maputnik:openmaptiles_access_token"
|
||||||
),
|
),
|
||||||
apiKey
|
apiKey
|
||||||
@@ -124,7 +124,7 @@ describe("modals", () => {
|
|||||||
it("thunderforest access token", () => {
|
it("thunderforest access token", () => {
|
||||||
var apiKey = "testing123";
|
var apiKey = "testing123";
|
||||||
when.setValue(
|
when.setValue(
|
||||||
get.getDataAttribute(
|
get.dataAttribute(
|
||||||
"modal:settings.maputnik:thunderforest_access_token"
|
"modal:settings.maputnik:thunderforest_access_token"
|
||||||
),
|
),
|
||||||
apiKey
|
apiKey
|
||||||
@@ -139,14 +139,8 @@ describe("modals", () => {
|
|||||||
|
|
||||||
it("style renderer", () => {
|
it("style renderer", () => {
|
||||||
cy.on("uncaught:exception", () => false); // this is due to the fact that this is an invalid style for openlayers
|
cy.on("uncaught:exception", () => false); // this is due to the fact that this is an invalid style for openlayers
|
||||||
when.select(
|
when.select("modal:settings.maputnik:renderer", "ol");
|
||||||
get.getDataAttribute("modal:settings.maputnik:renderer"),
|
should.beSelected("modal:settings.maputnik:renderer", "ol");
|
||||||
"ol"
|
|
||||||
);
|
|
||||||
should.isSelected(
|
|
||||||
get.getDataAttribute("modal:settings.maputnik:renderer"),
|
|
||||||
"ol"
|
|
||||||
);
|
|
||||||
|
|
||||||
when.click("modal:settings.name");
|
when.click("modal:settings.name");
|
||||||
|
|
||||||
|
|||||||
+412
-478
File diff suppressed because it is too large
Load Diff
+152
-203
@@ -1,33 +1,24 @@
|
|||||||
import classnames from "classnames";
|
import React from 'react'
|
||||||
import { detect } from "detect-browser";
|
import PropTypes from 'prop-types'
|
||||||
import PropTypes from "prop-types";
|
import classnames from 'classnames'
|
||||||
import React from "react";
|
import {detect} from 'detect-browser';
|
||||||
|
|
||||||
import {
|
import {MdFileDownload, MdOpenInBrowser, MdSettings, MdLayers, MdHelpOutline, MdFindInPage, MdAssignmentTurnedIn} from 'react-icons/md'
|
||||||
MdAssignmentTurnedIn,
|
import pkgJson from '../../package.json'
|
||||||
MdFileDownload,
|
|
||||||
MdFindInPage,
|
|
||||||
MdHelpOutline,
|
|
||||||
MdLayers,
|
|
||||||
MdOpenInBrowser,
|
|
||||||
MdSettings,
|
|
||||||
} from "react-icons/md";
|
|
||||||
|
|
||||||
import logoImage from "maputnik-design/logos/logo-color.svg";
|
|
||||||
import pkgJson from "../../package.json";
|
|
||||||
|
|
||||||
// This is required because of <https://stackoverflow.com/a/49846426>, there isn't another way to detect support that I'm aware of.
|
// This is required because of <https://stackoverflow.com/a/49846426>, there isn't another way to detect support that I'm aware of.
|
||||||
const browser = detect();
|
const browser = detect();
|
||||||
const colorAccessibilityFiltersEnabled =
|
const colorAccessibilityFiltersEnabled = ['chrome', 'firefox'].indexOf(browser.name) > -1;
|
||||||
["chrome", "firefox"].indexOf(browser.name) > -1;
|
|
||||||
|
|
||||||
class IconText extends React.Component {
|
class IconText extends React.Component {
|
||||||
static propTypes = {
|
static propTypes = {
|
||||||
children: PropTypes.node,
|
children: PropTypes.node,
|
||||||
};
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
return <span className="maputnik-icon-text">{this.props.children}</span>;
|
return <span className="maputnik-icon-text">{this.props.children}</span>
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -37,19 +28,17 @@ class ToolbarLink extends React.Component {
|
|||||||
children: PropTypes.node,
|
children: PropTypes.node,
|
||||||
href: PropTypes.string,
|
href: PropTypes.string,
|
||||||
onToggleModal: PropTypes.func,
|
onToggleModal: PropTypes.func,
|
||||||
};
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
return (
|
return <a
|
||||||
<a
|
className={classnames('maputnik-toolbar-link', this.props.className)}
|
||||||
className={classnames("maputnik-toolbar-link", this.props.className)}
|
href={this.props.href}
|
||||||
href={this.props.href}
|
rel="noopener noreferrer"
|
||||||
rel="noopener noreferrer"
|
target="_blank"
|
||||||
target="_blank"
|
>
|
||||||
>
|
{this.props.children}
|
||||||
{this.props.children}
|
</a>
|
||||||
</a>
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -58,41 +47,36 @@ class ToolbarLinkHighlighted extends React.Component {
|
|||||||
className: PropTypes.string,
|
className: PropTypes.string,
|
||||||
children: PropTypes.node,
|
children: PropTypes.node,
|
||||||
href: PropTypes.string,
|
href: PropTypes.string,
|
||||||
onToggleModal: PropTypes.func,
|
onToggleModal: PropTypes.func
|
||||||
};
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
return (
|
return <a
|
||||||
<a
|
className={classnames('maputnik-toolbar-link', "maputnik-toolbar-link--highlighted", this.props.className)}
|
||||||
className={classnames(
|
href={this.props.href}
|
||||||
"maputnik-toolbar-link",
|
rel="noopener noreferrer"
|
||||||
"maputnik-toolbar-link--highlighted",
|
target="_blank"
|
||||||
this.props.className
|
>
|
||||||
)}
|
<span className="maputnik-toolbar-link-wrapper">
|
||||||
href={this.props.href}
|
{this.props.children}
|
||||||
rel="noopener noreferrer"
|
</span>
|
||||||
target="_blank"
|
</a>
|
||||||
>
|
|
||||||
<span className="maputnik-toolbar-link-wrapper">
|
|
||||||
{this.props.children}
|
|
||||||
</span>
|
|
||||||
</a>
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class ToolbarSelect extends React.Component {
|
class ToolbarSelect extends React.Component {
|
||||||
static propTypes = {
|
static propTypes = {
|
||||||
children: PropTypes.node,
|
children: PropTypes.node,
|
||||||
wdKey: PropTypes.string,
|
wdKey: PropTypes.string
|
||||||
};
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
return (
|
return <div
|
||||||
<div className="maputnik-toolbar-select" data-wd-key={this.props.wdKey}>
|
className='maputnik-toolbar-select'
|
||||||
{this.props.children}
|
data-wd-key={this.props.wdKey}
|
||||||
</div>
|
>
|
||||||
);
|
{this.props.children}
|
||||||
|
</div>
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -100,19 +84,17 @@ class ToolbarAction extends React.Component {
|
|||||||
static propTypes = {
|
static propTypes = {
|
||||||
children: PropTypes.node,
|
children: PropTypes.node,
|
||||||
onClick: PropTypes.func,
|
onClick: PropTypes.func,
|
||||||
wdKey: PropTypes.string,
|
wdKey: PropTypes.string
|
||||||
};
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
return (
|
return <button
|
||||||
<button
|
className='maputnik-toolbar-action'
|
||||||
className="maputnik-toolbar-action"
|
data-wd-key={this.props.wdKey}
|
||||||
data-wd-key={this.props.wdKey}
|
onClick={this.props.onClick}
|
||||||
onClick={this.props.onClick}
|
>
|
||||||
>
|
{this.props.children}
|
||||||
{this.props.children}
|
</button>
|
||||||
</button>
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -130,7 +112,7 @@ export default class AppToolbar extends React.Component {
|
|||||||
onSetMapState: PropTypes.func,
|
onSetMapState: PropTypes.func,
|
||||||
mapState: PropTypes.string,
|
mapState: PropTypes.string,
|
||||||
renderer: PropTypes.string,
|
renderer: PropTypes.string,
|
||||||
};
|
}
|
||||||
|
|
||||||
state = {
|
state = {
|
||||||
isOpen: {
|
isOpen: {
|
||||||
@@ -139,8 +121,8 @@ export default class AppToolbar extends React.Component {
|
|||||||
open: false,
|
open: false,
|
||||||
add: false,
|
add: false,
|
||||||
export: false,
|
export: false,
|
||||||
},
|
}
|
||||||
};
|
}
|
||||||
|
|
||||||
handleSelection(val) {
|
handleSelection(val) {
|
||||||
this.props.onSetMapState(val);
|
this.props.onSetMapState(val);
|
||||||
@@ -149,11 +131,12 @@ export default class AppToolbar extends React.Component {
|
|||||||
onSkip = (target) => {
|
onSkip = (target) => {
|
||||||
if (target === "map") {
|
if (target === "map") {
|
||||||
document.querySelector(".maplibregl-canvas").focus();
|
document.querySelector(".maplibregl-canvas").focus();
|
||||||
} else {
|
}
|
||||||
const el = document.querySelector("#skip-target-" + target);
|
else {
|
||||||
|
const el = document.querySelector("#skip-target-"+target);
|
||||||
el.focus();
|
el.focus();
|
||||||
}
|
}
|
||||||
};
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
const views = [
|
const views = [
|
||||||
@@ -166,7 +149,7 @@ export default class AppToolbar extends React.Component {
|
|||||||
id: "inspect",
|
id: "inspect",
|
||||||
group: "general",
|
group: "general",
|
||||||
title: "Inspect",
|
title: "Inspect",
|
||||||
disabled: this.props.renderer === "ol",
|
disabled: this.props.renderer === 'ol',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: "filter-deuteranopia",
|
id: "filter-deuteranopia",
|
||||||
@@ -198,137 +181,103 @@ export default class AppToolbar extends React.Component {
|
|||||||
return view.id === this.props.mapState;
|
return view.id === this.props.mapState;
|
||||||
});
|
});
|
||||||
|
|
||||||
return (
|
return <nav className='maputnik-toolbar'>
|
||||||
<nav className="maputnik-toolbar">
|
<div className="maputnik-toolbar__inner">
|
||||||
<div className="maputnik-toolbar__inner">
|
<div
|
||||||
<div className="maputnik-toolbar-logo-container">
|
className="maputnik-toolbar-logo-container"
|
||||||
{/* Keyboard accessible quick links */}
|
>
|
||||||
<button
|
{/* Keyboard accessible quick links */}
|
||||||
data-wd-key="root:skip:layer-list"
|
<button
|
||||||
className="maputnik-toolbar-skip"
|
data-wd-key="root:skip:layer-list"
|
||||||
onClick={(e) => this.onSkip("layer-list")}
|
className="maputnik-toolbar-skip"
|
||||||
>
|
onClick={e => this.onSkip("layer-list")}
|
||||||
Layers list
|
|
||||||
</button>
|
|
||||||
<button
|
|
||||||
data-wd-key="root:skip:layer-editor"
|
|
||||||
className="maputnik-toolbar-skip"
|
|
||||||
onClick={(e) => this.onSkip("layer-editor")}
|
|
||||||
>
|
|
||||||
Layer editor
|
|
||||||
</button>
|
|
||||||
<button
|
|
||||||
data-wd-key="root:skip:map-view"
|
|
||||||
className="maputnik-toolbar-skip"
|
|
||||||
onClick={(e) => this.onSkip("map")}
|
|
||||||
>
|
|
||||||
Map view
|
|
||||||
</button>
|
|
||||||
<a
|
|
||||||
className="maputnik-toolbar-logo"
|
|
||||||
target="blank"
|
|
||||||
rel="noreferrer noopener"
|
|
||||||
href="https://github.com/maputnik/editor"
|
|
||||||
>
|
|
||||||
<span dangerouslySetInnerHTML={{ __html: logoImage }} />
|
|
||||||
<h1>
|
|
||||||
<span className="maputnik-toolbar-name">{pkgJson.name}</span>
|
|
||||||
<span className="maputnik-toolbar-version">
|
|
||||||
v{pkgJson.version}
|
|
||||||
</span>
|
|
||||||
</h1>
|
|
||||||
</a>
|
|
||||||
</div>
|
|
||||||
<div
|
|
||||||
className="maputnik-toolbar__actions"
|
|
||||||
role="navigation"
|
|
||||||
aria-label="Toolbar"
|
|
||||||
>
|
>
|
||||||
<ToolbarAction
|
Layers list
|
||||||
wdKey="nav:open"
|
</button>
|
||||||
onClick={this.props.onToggleModal.bind(this, "open")}
|
<button
|
||||||
>
|
data-wd-key="root:skip:layer-editor"
|
||||||
<MdOpenInBrowser />
|
className="maputnik-toolbar-skip"
|
||||||
<IconText>Open</IconText>
|
onClick={e => this.onSkip("layer-editor")}
|
||||||
</ToolbarAction>
|
>
|
||||||
<ToolbarAction
|
Layer editor
|
||||||
wdKey="nav:export"
|
</button>
|
||||||
onClick={this.props.onToggleModal.bind(this, "export")}
|
<button
|
||||||
>
|
data-wd-key="root:skip:map-view"
|
||||||
<MdFileDownload />
|
className="maputnik-toolbar-skip"
|
||||||
<IconText>Export</IconText>
|
onClick={e => this.onSkip("map")}
|
||||||
</ToolbarAction>
|
>
|
||||||
<ToolbarAction
|
Map view
|
||||||
wdKey="nav:sources"
|
</button>
|
||||||
onClick={this.props.onToggleModal.bind(this, "sources")}
|
<a
|
||||||
>
|
className="maputnik-toolbar-logo"
|
||||||
<MdLayers />
|
target="blank"
|
||||||
<IconText>Data Sources</IconText>
|
rel="noreferrer noopener"
|
||||||
</ToolbarAction>
|
href="https://github.com/maputnik/editor"
|
||||||
<ToolbarAction
|
>
|
||||||
wdKey="nav:settings"
|
<img src="node_modules/maputnik-design/logos/logo-color.svg" />
|
||||||
onClick={this.props.onToggleModal.bind(this, "settings")}
|
<h1>
|
||||||
>
|
<span className="maputnik-toolbar-name">{pkgJson.name}</span>
|
||||||
<MdSettings />
|
<span className="maputnik-toolbar-version">v{pkgJson.version}</span>
|
||||||
<IconText>Style Settings</IconText>
|
</h1>
|
||||||
</ToolbarAction>
|
</a>
|
||||||
|
|
||||||
<ToolbarSelect wdKey="nav:inspect">
|
|
||||||
<MdFindInPage />
|
|
||||||
<label>
|
|
||||||
View
|
|
||||||
<select
|
|
||||||
className="maputnik-select"
|
|
||||||
data-wd-key="maputnik-select"
|
|
||||||
onChange={(e) => this.handleSelection(e.target.value)}
|
|
||||||
value={currentView.id}
|
|
||||||
>
|
|
||||||
{views
|
|
||||||
.filter((v) => v.group === "general")
|
|
||||||
.map((item) => {
|
|
||||||
return (
|
|
||||||
<option
|
|
||||||
key={item.id}
|
|
||||||
value={item.id}
|
|
||||||
disabled={item.disabled}
|
|
||||||
data-wd-key={item.id}
|
|
||||||
>
|
|
||||||
{item.title}
|
|
||||||
</option>
|
|
||||||
);
|
|
||||||
})}
|
|
||||||
<optgroup label="Color accessibility">
|
|
||||||
{views
|
|
||||||
.filter((v) => v.group === "color-accessibility")
|
|
||||||
.map((item) => {
|
|
||||||
return (
|
|
||||||
<option
|
|
||||||
key={item.id}
|
|
||||||
value={item.id}
|
|
||||||
disabled={item.disabled}
|
|
||||||
>
|
|
||||||
{item.title}
|
|
||||||
</option>
|
|
||||||
);
|
|
||||||
})}
|
|
||||||
</optgroup>
|
|
||||||
</select>
|
|
||||||
</label>
|
|
||||||
</ToolbarSelect>
|
|
||||||
|
|
||||||
<ToolbarLink href={"https://github.com/maputnik/editor/wiki"}>
|
|
||||||
<MdHelpOutline />
|
|
||||||
<IconText>Help</IconText>
|
|
||||||
</ToolbarLink>
|
|
||||||
<ToolbarLinkHighlighted
|
|
||||||
href={"https://gregorywolanski.typeform.com/to/cPgaSY"}
|
|
||||||
>
|
|
||||||
<MdAssignmentTurnedIn />
|
|
||||||
<IconText>Take the Maputnik Survey</IconText>
|
|
||||||
</ToolbarLinkHighlighted>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</nav>
|
<div className="maputnik-toolbar__actions" role="navigation" aria-label="Toolbar">
|
||||||
);
|
<ToolbarAction wdKey="nav:open" onClick={this.props.onToggleModal.bind(this, 'open')}>
|
||||||
|
<MdOpenInBrowser />
|
||||||
|
<IconText>Open</IconText>
|
||||||
|
</ToolbarAction>
|
||||||
|
<ToolbarAction wdKey="nav:export" onClick={this.props.onToggleModal.bind(this, 'export')}>
|
||||||
|
<MdFileDownload />
|
||||||
|
<IconText>Export</IconText>
|
||||||
|
</ToolbarAction>
|
||||||
|
<ToolbarAction wdKey="nav:sources" onClick={this.props.onToggleModal.bind(this, 'sources')}>
|
||||||
|
<MdLayers />
|
||||||
|
<IconText>Data Sources</IconText>
|
||||||
|
</ToolbarAction>
|
||||||
|
<ToolbarAction wdKey="nav:settings" onClick={this.props.onToggleModal.bind(this, 'settings')}>
|
||||||
|
<MdSettings />
|
||||||
|
<IconText>Style Settings</IconText>
|
||||||
|
</ToolbarAction>
|
||||||
|
|
||||||
|
<ToolbarSelect wdKey="nav:inspect">
|
||||||
|
<MdFindInPage />
|
||||||
|
<label>View
|
||||||
|
<select
|
||||||
|
className="maputnik-select"
|
||||||
|
data-wd-key="maputnik-select"
|
||||||
|
onChange={(e) => this.handleSelection(e.target.value)}
|
||||||
|
value={currentView.id}
|
||||||
|
>
|
||||||
|
{views.filter(v => v.group === "general").map((item) => {
|
||||||
|
return (
|
||||||
|
<option key={item.id} value={item.id} disabled={item.disabled} data-wd-key={item.id}>
|
||||||
|
{item.title}
|
||||||
|
</option>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
<optgroup label="Color accessibility">
|
||||||
|
{views.filter(v => v.group === "color-accessibility").map((item) => {
|
||||||
|
return (
|
||||||
|
<option key={item.id} value={item.id} disabled={item.disabled}>
|
||||||
|
{item.title}
|
||||||
|
</option>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
</optgroup>
|
||||||
|
</select>
|
||||||
|
</label>
|
||||||
|
</ToolbarSelect>
|
||||||
|
|
||||||
|
<ToolbarLink href={"https://github.com/maputnik/editor/wiki"}>
|
||||||
|
<MdHelpOutline />
|
||||||
|
<IconText>Help</IconText>
|
||||||
|
</ToolbarLink>
|
||||||
|
<ToolbarLinkHighlighted href={"https://gregorywolanski.typeform.com/to/cPgaSY"}>
|
||||||
|
<MdAssignmentTurnedIn />
|
||||||
|
<IconText>Take the Maputnik Survey</IconText>
|
||||||
|
</ToolbarLinkHighlighted>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</nav>
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+207
-249
@@ -1,46 +1,47 @@
|
|||||||
import PropTypes from "prop-types";
|
import React from 'react'
|
||||||
import React from "react";
|
import PropTypes from 'prop-types'
|
||||||
import { Button, Menu, MenuItem, Wrapper } from "react-aria-menubutton";
|
import { Wrapper, Button, Menu, MenuItem } from 'react-aria-menubutton'
|
||||||
|
|
||||||
import { Accordion } from "react-accessible-accordion";
|
import FieldJson from './FieldJson'
|
||||||
import FieldComment from "./FieldComment";
|
import FilterEditor from './FilterEditor'
|
||||||
import FieldId from "./FieldId";
|
import PropertyGroup from './PropertyGroup'
|
||||||
import FieldJson from "./FieldJson";
|
import LayerEditorGroup from './LayerEditorGroup'
|
||||||
import FieldMaxZoom from "./FieldMaxZoom";
|
import FieldType from './FieldType'
|
||||||
import FieldMinZoom from "./FieldMinZoom";
|
import FieldId from './FieldId'
|
||||||
import FieldSource from "./FieldSource";
|
import FieldMinZoom from './FieldMinZoom'
|
||||||
import FieldSourceLayer from "./FieldSourceLayer";
|
import FieldMaxZoom from './FieldMaxZoom'
|
||||||
import FieldType from "./FieldType";
|
import FieldComment from './FieldComment'
|
||||||
import FilterEditor from "./FilterEditor";
|
import FieldSource from './FieldSource'
|
||||||
import LayerEditorGroup from "./LayerEditorGroup";
|
import FieldSourceLayer from './FieldSourceLayer'
|
||||||
import PropertyGroup from "./PropertyGroup";
|
import {Accordion} from 'react-accessible-accordion';
|
||||||
|
|
||||||
import { MdMoreVert } from "react-icons/md";
|
import {MdMoreVert} from 'react-icons/md'
|
||||||
|
|
||||||
import layout from "../config/layout.json";
|
import { changeType, changeProperty } from '../libs/layer'
|
||||||
import { changeProperty, changeType } from "../libs/layer";
|
import layout from '../config/layout.json'
|
||||||
import { formatLayerId } from "../util/format";
|
import {formatLayerId} from '../util/format';
|
||||||
|
|
||||||
function getLayoutForType(type) {
|
|
||||||
|
function getLayoutForType (type) {
|
||||||
return layout[type] ? layout[type] : layout.invalid;
|
return layout[type] ? layout[type] : layout.invalid;
|
||||||
}
|
}
|
||||||
|
|
||||||
function layoutGroups(layerType) {
|
function layoutGroups(layerType) {
|
||||||
const layerGroup = {
|
const layerGroup = {
|
||||||
title: "Layer",
|
title: 'Layer',
|
||||||
type: "layer",
|
type: 'layer'
|
||||||
};
|
}
|
||||||
const filterGroup = {
|
const filterGroup = {
|
||||||
title: "Filter",
|
title: 'Filter',
|
||||||
type: "filter",
|
type: 'filter'
|
||||||
};
|
}
|
||||||
const editorGroup = {
|
const editorGroup = {
|
||||||
title: "JSON Editor",
|
title: 'JSON Editor',
|
||||||
type: "jsoneditor",
|
type: 'jsoneditor'
|
||||||
};
|
}
|
||||||
return [layerGroup, filterGroup]
|
return [layerGroup, filterGroup]
|
||||||
.concat(getLayoutForType(layerType).groups)
|
.concat(getLayoutForType(layerType).groups)
|
||||||
.concat([editorGroup]);
|
.concat([editorGroup])
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Layer editor supporting multiple types of layers. */
|
/** Layer editor supporting multiple types of layers. */
|
||||||
@@ -60,320 +61,277 @@ export default class LayerEditor extends React.Component {
|
|||||||
isLastLayer: PropTypes.bool,
|
isLastLayer: PropTypes.bool,
|
||||||
layerIndex: PropTypes.number,
|
layerIndex: PropTypes.number,
|
||||||
errors: PropTypes.array,
|
errors: PropTypes.array,
|
||||||
};
|
}
|
||||||
|
|
||||||
static defaultProps = {
|
static defaultProps = {
|
||||||
onLayerChanged: () => {},
|
onLayerChanged: () => {},
|
||||||
onLayerIdChange: () => {},
|
onLayerIdChange: () => {},
|
||||||
onLayerDestroyed: () => {},
|
onLayerDestroyed: () => {},
|
||||||
};
|
}
|
||||||
|
|
||||||
static childContextTypes = {
|
static childContextTypes = {
|
||||||
reactIconBase: PropTypes.object,
|
reactIconBase: PropTypes.object
|
||||||
};
|
}
|
||||||
|
|
||||||
constructor(props) {
|
constructor(props) {
|
||||||
super(props);
|
super(props)
|
||||||
|
|
||||||
//TODO: Clean this up and refactor into function
|
//TODO: Clean this up and refactor into function
|
||||||
const editorGroups = {};
|
const editorGroups = {}
|
||||||
layoutGroups(this.props.layer.type).forEach((group) => {
|
layoutGroups(this.props.layer.type).forEach(group => {
|
||||||
editorGroups[group.title] = true;
|
editorGroups[group.title] = true
|
||||||
});
|
})
|
||||||
|
|
||||||
this.state = { editorGroups };
|
this.state = { editorGroups }
|
||||||
}
|
}
|
||||||
|
|
||||||
static getDerivedStateFromProps(props, state) {
|
static getDerivedStateFromProps(props, state) {
|
||||||
const additionalGroups = { ...state.editorGroups };
|
const additionalGroups = { ...state.editorGroups }
|
||||||
|
|
||||||
getLayoutForType(props.layer.type).groups.forEach((group) => {
|
getLayoutForType(props.layer.type).groups.forEach(group => {
|
||||||
if (!(group.title in additionalGroups)) {
|
if(!(group.title in additionalGroups)) {
|
||||||
additionalGroups[group.title] = true;
|
additionalGroups[group.title] = true
|
||||||
}
|
}
|
||||||
});
|
})
|
||||||
|
|
||||||
return {
|
return {
|
||||||
editorGroups: additionalGroups,
|
editorGroups: additionalGroups
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
getChildContext() {
|
getChildContext () {
|
||||||
return {
|
return {
|
||||||
reactIconBase: {
|
reactIconBase: {
|
||||||
size: 14,
|
size: 14,
|
||||||
color: "#8e8e8e",
|
color: '#8e8e8e',
|
||||||
},
|
}
|
||||||
};
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
changeProperty(group, property, newValue) {
|
changeProperty(group, property, newValue) {
|
||||||
this.props.onLayerChanged(
|
this.props.onLayerChanged(
|
||||||
this.props.layerIndex,
|
this.props.layerIndex,
|
||||||
changeProperty(this.props.layer, group, property, newValue)
|
changeProperty(this.props.layer, group, property, newValue)
|
||||||
);
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
onGroupToggle(groupTitle, active) {
|
onGroupToggle(groupTitle, active) {
|
||||||
const changedActiveGroups = {
|
const changedActiveGroups = {
|
||||||
...this.state.editorGroups,
|
...this.state.editorGroups,
|
||||||
[groupTitle]: active,
|
[groupTitle]: active,
|
||||||
};
|
}
|
||||||
this.setState({
|
this.setState({
|
||||||
editorGroups: changedActiveGroups,
|
editorGroups: changedActiveGroups
|
||||||
});
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
renderGroupType(type, fields) {
|
renderGroupType(type, fields) {
|
||||||
let comment = "";
|
let comment = ""
|
||||||
if (this.props.layer.metadata) {
|
if(this.props.layer.metadata) {
|
||||||
comment = this.props.layer.metadata["maputnik:comment"];
|
comment = this.props.layer.metadata['maputnik:comment']
|
||||||
}
|
}
|
||||||
const { errors, layerIndex } = this.props;
|
const {errors, layerIndex} = this.props;
|
||||||
|
|
||||||
const errorData = {};
|
const errorData = {};
|
||||||
errors.forEach((error) => {
|
errors.forEach(error => {
|
||||||
if (
|
if (
|
||||||
error.parsed &&
|
error.parsed &&
|
||||||
error.parsed.type === "layer" &&
|
error.parsed.type === "layer" &&
|
||||||
error.parsed.data.index == layerIndex
|
error.parsed.data.index == layerIndex
|
||||||
) {
|
) {
|
||||||
errorData[error.parsed.data.key] = {
|
errorData[error.parsed.data.key] = {
|
||||||
message: error.parsed.data.message,
|
message: error.parsed.data.message
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
});
|
})
|
||||||
|
|
||||||
let sourceLayerIds;
|
let sourceLayerIds;
|
||||||
if (this.props.sources.hasOwnProperty(this.props.layer.source)) {
|
if(this.props.sources.hasOwnProperty(this.props.layer.source)) {
|
||||||
sourceLayerIds = this.props.sources[this.props.layer.source].layers;
|
sourceLayerIds = this.props.sources[this.props.layer.source].layers;
|
||||||
}
|
}
|
||||||
|
|
||||||
switch (type) {
|
switch(type) {
|
||||||
case "layer":
|
case 'layer': return <div>
|
||||||
return (
|
<FieldId
|
||||||
<div>
|
value={this.props.layer.id}
|
||||||
<FieldId
|
wdKey="layer-editor.layer-id"
|
||||||
value={this.props.layer.id}
|
error={errorData.id}
|
||||||
wdKey="layer-editor.layer-id"
|
onChange={newId => this.props.onLayerIdChange(this.props.layerIndex, this.props.layer.id, newId)}
|
||||||
error={errorData.id}
|
/>
|
||||||
onChange={(newId) =>
|
<FieldType
|
||||||
this.props.onLayerIdChange(
|
disabled={true}
|
||||||
this.props.layerIndex,
|
error={errorData.type}
|
||||||
this.props.layer.id,
|
value={this.props.layer.type}
|
||||||
newId
|
onChange={newType => this.props.onLayerChanged(
|
||||||
)
|
this.props.layerIndex,
|
||||||
}
|
changeType(this.props.layer, newType)
|
||||||
/>
|
)}
|
||||||
<FieldType
|
/>
|
||||||
disabled={true}
|
{this.props.layer.type !== 'background' && <FieldSource
|
||||||
error={errorData.type}
|
error={errorData.source}
|
||||||
value={this.props.layer.type}
|
sourceIds={Object.keys(this.props.sources)}
|
||||||
onChange={(newType) =>
|
value={this.props.layer.source}
|
||||||
this.props.onLayerChanged(
|
onChange={v => this.changeProperty(null, 'source', v)}
|
||||||
this.props.layerIndex,
|
/>
|
||||||
changeType(this.props.layer, newType)
|
}
|
||||||
)
|
{['background', 'raster', 'hillshade', 'heatmap'].indexOf(this.props.layer.type) < 0 &&
|
||||||
}
|
<FieldSourceLayer
|
||||||
/>
|
error={errorData['source-layer']}
|
||||||
{this.props.layer.type !== "background" && (
|
sourceLayerIds={sourceLayerIds}
|
||||||
<FieldSource
|
value={this.props.layer['source-layer']}
|
||||||
error={errorData.source}
|
onChange={v => this.changeProperty(null, 'source-layer', v)}
|
||||||
sourceIds={Object.keys(this.props.sources)}
|
/>
|
||||||
value={this.props.layer.source}
|
}
|
||||||
onChange={(v) => this.changeProperty(null, "source", v)}
|
<FieldMinZoom
|
||||||
/>
|
error={errorData.minzoom}
|
||||||
)}
|
value={this.props.layer.minzoom}
|
||||||
{["background", "raster", "hillshade", "heatmap"].indexOf(
|
onChange={v => this.changeProperty(null, 'minzoom', v)}
|
||||||
this.props.layer.type
|
/>
|
||||||
) < 0 && (
|
<FieldMaxZoom
|
||||||
<FieldSourceLayer
|
error={errorData.maxzoom}
|
||||||
error={errorData["source-layer"]}
|
value={this.props.layer.maxzoom}
|
||||||
sourceLayerIds={sourceLayerIds}
|
onChange={v => this.changeProperty(null, 'maxzoom', v)}
|
||||||
value={this.props.layer["source-layer"]}
|
/>
|
||||||
onChange={(v) => this.changeProperty(null, "source-layer", v)}
|
<FieldComment
|
||||||
/>
|
error={errorData.comment}
|
||||||
)}
|
value={comment}
|
||||||
<FieldMinZoom
|
onChange={v => this.changeProperty('metadata', 'maputnik:comment', v == "" ? undefined : v)}
|
||||||
error={errorData.minzoom}
|
/>
|
||||||
value={this.props.layer.minzoom}
|
</div>
|
||||||
onChange={(v) => this.changeProperty(null, "minzoom", v)}
|
case 'filter': return <div>
|
||||||
/>
|
<div className="maputnik-filter-editor-wrapper">
|
||||||
<FieldMaxZoom
|
<FilterEditor
|
||||||
error={errorData.maxzoom}
|
|
||||||
value={this.props.layer.maxzoom}
|
|
||||||
onChange={(v) => this.changeProperty(null, "maxzoom", v)}
|
|
||||||
/>
|
|
||||||
<FieldComment
|
|
||||||
error={errorData.comment}
|
|
||||||
value={comment}
|
|
||||||
onChange={(v) =>
|
|
||||||
this.changeProperty(
|
|
||||||
"metadata",
|
|
||||||
"maputnik:comment",
|
|
||||||
v == "" ? undefined : v
|
|
||||||
)
|
|
||||||
}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
case "filter":
|
|
||||||
return (
|
|
||||||
<div>
|
|
||||||
<div className="maputnik-filter-editor-wrapper">
|
|
||||||
<FilterEditor
|
|
||||||
errors={errorData}
|
|
||||||
filter={this.props.layer.filter}
|
|
||||||
properties={
|
|
||||||
this.props.vectorLayers[this.props.layer["source-layer"]]
|
|
||||||
}
|
|
||||||
onChange={(f) => this.changeProperty(null, "filter", f)}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
case "properties":
|
|
||||||
return (
|
|
||||||
<PropertyGroup
|
|
||||||
errors={errorData}
|
errors={errorData}
|
||||||
layer={this.props.layer}
|
filter={this.props.layer.filter}
|
||||||
groupFields={fields}
|
properties={this.props.vectorLayers[this.props.layer['source-layer']]}
|
||||||
spec={this.props.spec}
|
onChange={f => this.changeProperty(null, 'filter', f)}
|
||||||
onChange={this.changeProperty.bind(this)}
|
|
||||||
/>
|
/>
|
||||||
);
|
</div>
|
||||||
case "jsoneditor":
|
</div>
|
||||||
return (
|
case 'properties':
|
||||||
<FieldJson
|
return <PropertyGroup
|
||||||
layer={this.props.layer}
|
errors={errorData}
|
||||||
onChange={(layer) => {
|
layer={this.props.layer}
|
||||||
this.props.onLayerChanged(this.props.layerIndex, layer);
|
groupFields={fields}
|
||||||
}}
|
spec={this.props.spec}
|
||||||
/>
|
onChange={this.changeProperty.bind(this)}
|
||||||
);
|
/>
|
||||||
|
case 'jsoneditor':
|
||||||
|
return <FieldJson
|
||||||
|
layer={this.props.layer}
|
||||||
|
onChange={(layer) => {
|
||||||
|
this.props.onLayerChanged(
|
||||||
|
this.props.layerIndex,
|
||||||
|
layer
|
||||||
|
);
|
||||||
|
}}
|
||||||
|
/>
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
moveLayer(offset) {
|
moveLayer(offset) {
|
||||||
this.props.onMoveLayer({
|
this.props.onMoveLayer({
|
||||||
oldIndex: this.props.layerIndex,
|
oldIndex: this.props.layerIndex,
|
||||||
newIndex: this.props.layerIndex + offset,
|
newIndex: this.props.layerIndex+offset
|
||||||
});
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
const groupIds = [];
|
const groupIds = [];
|
||||||
const layerType = this.props.layer.type;
|
const layerType = this.props.layer.type
|
||||||
const groups = layoutGroups(layerType)
|
const groups = layoutGroups(layerType).filter(group => {
|
||||||
.filter((group) => {
|
return !(layerType === 'background' && group.type === 'source')
|
||||||
return !(layerType === "background" && group.type === "source");
|
}).map(group => {
|
||||||
})
|
const groupId = group.title.replace(/ /g, "_");
|
||||||
.map((group) => {
|
groupIds.push(groupId);
|
||||||
const groupId = group.title.replace(/ /g, "_");
|
return <LayerEditorGroup
|
||||||
groupIds.push(groupId);
|
data-wd-key={group.title}
|
||||||
return (
|
id={groupId}
|
||||||
<LayerEditorGroup
|
key={group.title}
|
||||||
data-wd-key={group.title}
|
title={group.title}
|
||||||
id={groupId}
|
isActive={this.state.editorGroups[group.title]}
|
||||||
key={group.title}
|
onActiveToggle={this.onGroupToggle.bind(this, group.title)}
|
||||||
title={group.title}
|
>
|
||||||
isActive={this.state.editorGroups[group.title]}
|
{this.renderGroupType(group.type, group.fields)}
|
||||||
onActiveToggle={this.onGroupToggle.bind(this, group.title)}
|
</LayerEditorGroup>
|
||||||
>
|
})
|
||||||
{this.renderGroupType(group.type, group.fields)}
|
|
||||||
</LayerEditorGroup>
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
const layout = this.props.layer.layout || {};
|
const layout = this.props.layer.layout || {}
|
||||||
|
|
||||||
const items = {
|
const items = {
|
||||||
delete: {
|
delete: {
|
||||||
text: "Delete",
|
text: "Delete",
|
||||||
handler: () => this.props.onLayerDestroy(this.props.layerIndex),
|
handler: () => this.props.onLayerDestroy(this.props.layerIndex)
|
||||||
},
|
},
|
||||||
duplicate: {
|
duplicate: {
|
||||||
text: "Duplicate",
|
text: "Duplicate",
|
||||||
handler: () => this.props.onLayerCopy(this.props.layerIndex),
|
handler: () => this.props.onLayerCopy(this.props.layerIndex)
|
||||||
},
|
},
|
||||||
hide: {
|
hide: {
|
||||||
text: layout.visibility === "none" ? "Show" : "Hide",
|
text: (layout.visibility === "none") ? "Show" : "Hide",
|
||||||
handler: () =>
|
handler: () => this.props.onLayerVisibilityToggle(this.props.layerIndex)
|
||||||
this.props.onLayerVisibilityToggle(this.props.layerIndex),
|
|
||||||
},
|
},
|
||||||
moveLayerUp: {
|
moveLayerUp: {
|
||||||
text: "Move layer up",
|
text: "Move layer up",
|
||||||
// Not actually used...
|
// Not actually used...
|
||||||
disabled: this.props.isFirstLayer,
|
disabled: this.props.isFirstLayer,
|
||||||
handler: () => this.moveLayer(-1),
|
handler: () => this.moveLayer(-1)
|
||||||
},
|
},
|
||||||
moveLayerDown: {
|
moveLayerDown: {
|
||||||
text: "Move layer down",
|
text: "Move layer down",
|
||||||
// Not actually used...
|
// Not actually used...
|
||||||
disabled: this.props.isLastLayer,
|
disabled: this.props.isLastLayer,
|
||||||
handler: () => this.moveLayer(+1),
|
handler: () => this.moveLayer(+1)
|
||||||
},
|
}
|
||||||
};
|
}
|
||||||
|
|
||||||
function handleSelection(id, event) {
|
function handleSelection(id, event) {
|
||||||
event.stopPropagation;
|
event.stopPropagation;
|
||||||
items[id].handler();
|
items[id].handler();
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return <section className="maputnik-layer-editor"
|
||||||
<section
|
role="main"
|
||||||
className="maputnik-layer-editor"
|
aria-label="Layer editor"
|
||||||
role="main"
|
>
|
||||||
aria-label="Layer editor"
|
<header>
|
||||||
>
|
<div className="layer-header">
|
||||||
<header>
|
<h2 className="layer-header__title">
|
||||||
<div className="layer-header">
|
Layer: {formatLayerId(this.props.layer.id)}
|
||||||
<h2 className="layer-header__title">
|
</h2>
|
||||||
Layer: {formatLayerId(this.props.layer.id)}
|
<div className="layer-header__info">
|
||||||
</h2>
|
<Wrapper
|
||||||
<div className="layer-header__info">
|
className='more-menu'
|
||||||
<Wrapper
|
onSelection={handleSelection}
|
||||||
className="more-menu"
|
closeOnSelection={false}
|
||||||
onSelection={handleSelection}
|
>
|
||||||
closeOnSelection={false}
|
<Button id="skip-target-layer-editor" data-wd-key="skip-target-layer-editor" className='more-menu__button' title="Layer options">
|
||||||
>
|
<MdMoreVert className="more-menu__button__svg" />
|
||||||
<Button
|
</Button>
|
||||||
data-wd-key="skip-target-layer-editor"
|
<Menu>
|
||||||
id="skip-target-layer-editor"
|
<ul className="more-menu__menu">
|
||||||
className="more-menu__button"
|
{Object.keys(items).map((id, idx) => {
|
||||||
title="Layer options"
|
const item = items[id];
|
||||||
>
|
return <li key={id}>
|
||||||
<MdMoreVert className="more-menu__button__svg" />
|
<MenuItem value={id} className='more-menu__menu__item'>
|
||||||
</Button>
|
{item.text}
|
||||||
<Menu>
|
</MenuItem>
|
||||||
<ul className="more-menu__menu">
|
</li>
|
||||||
{Object.keys(items).map((id, idx) => {
|
})}
|
||||||
const item = items[id];
|
</ul>
|
||||||
return (
|
</Menu>
|
||||||
<li key={id}>
|
</Wrapper>
|
||||||
<MenuItem
|
|
||||||
value={id}
|
|
||||||
className="more-menu__menu__item"
|
|
||||||
>
|
|
||||||
{item.text}
|
|
||||||
</MenuItem>
|
|
||||||
</li>
|
|
||||||
);
|
|
||||||
})}
|
|
||||||
</ul>
|
|
||||||
</Menu>
|
|
||||||
</Wrapper>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</header>
|
</div>
|
||||||
<Accordion
|
|
||||||
allowMultipleExpanded={true}
|
</header>
|
||||||
allowZeroExpanded={true}
|
<Accordion
|
||||||
preExpanded={groupIds}
|
allowMultipleExpanded={true}
|
||||||
>
|
allowZeroExpanded={true}
|
||||||
{groups}
|
preExpanded={groupIds}
|
||||||
</Accordion>
|
>
|
||||||
</section>
|
{groups}
|
||||||
);
|
</Accordion>
|
||||||
|
</section>
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+156
-176
@@ -1,13 +1,13 @@
|
|||||||
import classnames from "classnames";
|
import React from 'react'
|
||||||
import lodash from "lodash";
|
import PropTypes from 'prop-types'
|
||||||
import PropTypes from "prop-types";
|
import classnames from 'classnames'
|
||||||
import React from "react";
|
import lodash from 'lodash';
|
||||||
|
|
||||||
import LayerListGroup from "./LayerListGroup";
|
import LayerListGroup from './LayerListGroup'
|
||||||
import LayerListItem from "./LayerListItem";
|
import LayerListItem from './LayerListItem'
|
||||||
import ModalAdd from "./ModalAdd";
|
import ModalAdd from './ModalAdd'
|
||||||
|
|
||||||
import { SortableContainer } from "react-sortable-hoc";
|
import {SortableContainer} from 'react-sortable-hoc';
|
||||||
|
|
||||||
const layerListPropTypes = {
|
const layerListPropTypes = {
|
||||||
layers: PropTypes.array.isRequired,
|
layers: PropTypes.array.isRequired,
|
||||||
@@ -15,34 +15,34 @@ const layerListPropTypes = {
|
|||||||
onLayersChange: PropTypes.func.isRequired,
|
onLayersChange: PropTypes.func.isRequired,
|
||||||
onLayerSelect: PropTypes.func,
|
onLayerSelect: PropTypes.func,
|
||||||
sources: PropTypes.object.isRequired,
|
sources: PropTypes.object.isRequired,
|
||||||
};
|
}
|
||||||
|
|
||||||
function layerPrefix(name) {
|
function layerPrefix(name) {
|
||||||
return name.replace(" ", "-").replace("_", "-").split("-")[0];
|
return name.replace(' ', '-').replace('_', '-').split('-')[0]
|
||||||
}
|
}
|
||||||
|
|
||||||
function findClosestCommonPrefix(layers, idx) {
|
function findClosestCommonPrefix(layers, idx) {
|
||||||
const currentLayerPrefix = layerPrefix(layers[idx].id);
|
const currentLayerPrefix = layerPrefix(layers[idx].id)
|
||||||
let closestIdx = idx;
|
let closestIdx = idx
|
||||||
for (let i = idx; i > 0; i--) {
|
for (let i = idx; i > 0; i--) {
|
||||||
const previousLayerPrefix = layerPrefix(layers[i - 1].id);
|
const previousLayerPrefix = layerPrefix(layers[i-1].id)
|
||||||
if (previousLayerPrefix === currentLayerPrefix) {
|
if(previousLayerPrefix === currentLayerPrefix) {
|
||||||
closestIdx = i - 1;
|
closestIdx = i - 1
|
||||||
} else {
|
} else {
|
||||||
return closestIdx;
|
return closestIdx
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return closestIdx;
|
return closestIdx
|
||||||
}
|
}
|
||||||
|
|
||||||
let UID = 0;
|
let UID = 0;
|
||||||
|
|
||||||
// List of collapsible layer editors
|
// List of collapsible layer editors
|
||||||
class LayerListContainer extends React.Component {
|
class LayerListContainer extends React.Component {
|
||||||
static propTypes = { ...layerListPropTypes };
|
static propTypes = {...layerListPropTypes}
|
||||||
static defaultProps = {
|
static defaultProps = {
|
||||||
onLayerSelect: () => {},
|
onLayerSelect: () => {},
|
||||||
};
|
}
|
||||||
|
|
||||||
constructor(props) {
|
constructor(props) {
|
||||||
super(props);
|
super(props);
|
||||||
@@ -56,8 +56,8 @@ class LayerListContainer extends React.Component {
|
|||||||
},
|
},
|
||||||
isOpen: {
|
isOpen: {
|
||||||
add: false,
|
add: false,
|
||||||
},
|
}
|
||||||
};
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
toggleModal(modalName) {
|
toggleModal(modalName) {
|
||||||
@@ -68,82 +68,79 @@ class LayerListContainer extends React.Component {
|
|||||||
},
|
},
|
||||||
isOpen: {
|
isOpen: {
|
||||||
...this.state.isOpen,
|
...this.state.isOpen,
|
||||||
[modalName]: !this.state.isOpen[modalName],
|
[modalName]: !this.state.isOpen[modalName]
|
||||||
},
|
}
|
||||||
});
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
toggleLayers = () => {
|
toggleLayers = () => {
|
||||||
let idx = 0;
|
let idx=0
|
||||||
|
|
||||||
let newGroups = [];
|
let newGroups=[]
|
||||||
|
|
||||||
|
this.groupedLayers().forEach(layers => {
|
||||||
|
const groupPrefix = layerPrefix(layers[0].id)
|
||||||
|
const lookupKey = [groupPrefix, idx].join('-')
|
||||||
|
|
||||||
this.groupedLayers().forEach((layers) => {
|
|
||||||
const groupPrefix = layerPrefix(layers[0].id);
|
|
||||||
const lookupKey = [groupPrefix, idx].join("-");
|
|
||||||
|
|
||||||
if (layers.length > 1) {
|
if (layers.length > 1) {
|
||||||
newGroups[lookupKey] = this.state.areAllGroupsExpanded;
|
newGroups[lookupKey] = this.state.areAllGroupsExpanded
|
||||||
}
|
}
|
||||||
|
|
||||||
layers.forEach((layer) => {
|
layers.forEach((layer) => {
|
||||||
idx += 1;
|
idx += 1
|
||||||
});
|
})
|
||||||
});
|
});
|
||||||
|
|
||||||
this.setState({
|
this.setState({
|
||||||
collapsedGroups: newGroups,
|
collapsedGroups: newGroups,
|
||||||
areAllGroupsExpanded: !this.state.areAllGroupsExpanded,
|
areAllGroupsExpanded: !this.state.areAllGroupsExpanded
|
||||||
});
|
})
|
||||||
};
|
}
|
||||||
|
|
||||||
groupedLayers() {
|
groupedLayers() {
|
||||||
const groups = [];
|
const groups = []
|
||||||
const layerIdCount = new Map();
|
const layerIdCount = new Map();
|
||||||
|
|
||||||
for (let i = 0; i < this.props.layers.length; i++) {
|
for (let i = 0; i < this.props.layers.length; i++) {
|
||||||
const origLayer = this.props.layers[i];
|
const origLayer = this.props.layers[i];
|
||||||
const previousLayer = this.props.layers[i - 1];
|
const previousLayer = this.props.layers[i-1]
|
||||||
layerIdCount.set(
|
layerIdCount.set(origLayer.id,
|
||||||
origLayer.id,
|
|
||||||
layerIdCount.has(origLayer.id) ? layerIdCount.get(origLayer.id) + 1 : 0
|
layerIdCount.has(origLayer.id) ? layerIdCount.get(origLayer.id) + 1 : 0
|
||||||
);
|
);
|
||||||
const layer = {
|
const layer = {
|
||||||
...origLayer,
|
...origLayer,
|
||||||
key: `layers-list-${origLayer.id}-${layerIdCount.get(origLayer.id)}`,
|
key: `layers-list-${origLayer.id}-${layerIdCount.get(origLayer.id)}`,
|
||||||
};
|
}
|
||||||
if (
|
if(previousLayer && layerPrefix(previousLayer.id) == layerPrefix(layer.id)) {
|
||||||
previousLayer &&
|
const lastGroup = groups[groups.length - 1]
|
||||||
layerPrefix(previousLayer.id) == layerPrefix(layer.id)
|
lastGroup.push(layer)
|
||||||
) {
|
|
||||||
const lastGroup = groups[groups.length - 1];
|
|
||||||
lastGroup.push(layer);
|
|
||||||
} else {
|
} else {
|
||||||
groups.push([layer]);
|
groups.push([layer])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return groups;
|
return groups
|
||||||
}
|
}
|
||||||
|
|
||||||
toggleLayerGroup(groupPrefix, idx) {
|
toggleLayerGroup(groupPrefix, idx) {
|
||||||
const lookupKey = [groupPrefix, idx].join("-");
|
const lookupKey = [groupPrefix, idx].join('-')
|
||||||
const newGroups = { ...this.state.collapsedGroups };
|
const newGroups = { ...this.state.collapsedGroups }
|
||||||
if (lookupKey in this.state.collapsedGroups) {
|
if(lookupKey in this.state.collapsedGroups) {
|
||||||
newGroups[lookupKey] = !this.state.collapsedGroups[lookupKey];
|
newGroups[lookupKey] = !this.state.collapsedGroups[lookupKey]
|
||||||
} else {
|
} else {
|
||||||
newGroups[lookupKey] = false;
|
newGroups[lookupKey] = false
|
||||||
}
|
}
|
||||||
this.setState({
|
this.setState({
|
||||||
collapsedGroups: newGroups,
|
collapsedGroups: newGroups
|
||||||
});
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
isCollapsed(groupPrefix, idx) {
|
isCollapsed(groupPrefix, idx) {
|
||||||
const collapsed = this.state.collapsedGroups[[groupPrefix, idx].join("-")];
|
const collapsed = this.state.collapsedGroups[[groupPrefix, idx].join('-')]
|
||||||
return collapsed === undefined ? true : collapsed;
|
return collapsed === undefined ? true : collapsed
|
||||||
}
|
}
|
||||||
|
|
||||||
shouldComponentUpdate(nextProps, nextState) {
|
shouldComponentUpdate (nextProps, nextState) {
|
||||||
// Always update on state change
|
// Always update on state change
|
||||||
if (this.state !== nextState) {
|
if (this.state !== nextState) {
|
||||||
return true;
|
return true;
|
||||||
@@ -151,28 +148,28 @@ class LayerListContainer extends React.Component {
|
|||||||
|
|
||||||
// This component tree only requires id and visibility from the layers
|
// This component tree only requires id and visibility from the layers
|
||||||
// objects
|
// objects
|
||||||
function getRequiredProps(layer) {
|
function getRequiredProps (layer) {
|
||||||
const out = {
|
const out = {
|
||||||
id: layer.id,
|
id: layer.id,
|
||||||
};
|
};
|
||||||
|
|
||||||
if (layer.layout) {
|
if (layer.layout) {
|
||||||
out.layout = {
|
out.layout = {
|
||||||
visibility: layer.layout.visibility,
|
visibility: layer.layout.visibility
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
return out;
|
return out;
|
||||||
}
|
}
|
||||||
const layersEqual = lodash.isEqual(
|
const layersEqual = lodash.isEqual(
|
||||||
nextProps.layers.map(getRequiredProps),
|
nextProps.layers.map(getRequiredProps),
|
||||||
this.props.layers.map(getRequiredProps)
|
this.props.layers.map(getRequiredProps),
|
||||||
);
|
);
|
||||||
|
|
||||||
function withoutLayers(props) {
|
function withoutLayers (props) {
|
||||||
const out = {
|
const out = {
|
||||||
...props,
|
...props
|
||||||
};
|
};
|
||||||
delete out["layers"];
|
delete out['layers'];
|
||||||
return out;
|
return out;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -187,16 +184,16 @@ class LayerListContainer extends React.Component {
|
|||||||
return propsChanged;
|
return propsChanged;
|
||||||
}
|
}
|
||||||
|
|
||||||
componentDidUpdate(prevProps) {
|
componentDidUpdate (prevProps) {
|
||||||
if (prevProps.selectedLayerIndex !== this.props.selectedLayerIndex) {
|
if (prevProps.selectedLayerIndex !== this.props.selectedLayerIndex) {
|
||||||
const selectedItemNode = this.selectedItemRef.current;
|
const selectedItemNode = this.selectedItemRef.current;
|
||||||
if (selectedItemNode && selectedItemNode.node) {
|
if (selectedItemNode && selectedItemNode.node) {
|
||||||
const target = selectedItemNode.node;
|
const target = selectedItemNode.node;
|
||||||
const options = {
|
const options = {
|
||||||
root: this.scrollContainerRef.current,
|
root: this.scrollContainerRef.current,
|
||||||
threshold: 1.0,
|
threshold: 1.0
|
||||||
};
|
}
|
||||||
const observer = new IntersectionObserver((entries) => {
|
const observer = new IntersectionObserver(entries => {
|
||||||
observer.unobserve(target);
|
observer.unobserve(target);
|
||||||
if (entries.length > 0 && entries[0].intersectionRatio < 1) {
|
if (entries.length > 0 && entries[0].intersectionRatio < 1) {
|
||||||
target.scrollIntoView();
|
target.scrollIntoView();
|
||||||
@@ -209,32 +206,28 @@ class LayerListContainer extends React.Component {
|
|||||||
}
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
const listItems = [];
|
|
||||||
let idx = 0;
|
const listItems = []
|
||||||
|
let idx = 0
|
||||||
const layersByGroup = this.groupedLayers();
|
const layersByGroup = this.groupedLayers();
|
||||||
layersByGroup.forEach((layers) => {
|
layersByGroup.forEach(layers => {
|
||||||
const groupPrefix = layerPrefix(layers[0].id);
|
const groupPrefix = layerPrefix(layers[0].id)
|
||||||
if (layers.length > 1) {
|
if(layers.length > 1) {
|
||||||
const grp = (
|
const grp = <LayerListGroup
|
||||||
<LayerListGroup
|
data-wd-key={[groupPrefix, idx].join('-')}
|
||||||
data-wd-key={[groupPrefix, idx].join("-")}
|
aria-controls={layers.map(l => l.key).join(" ")}
|
||||||
aria-controls={layers.map((l) => l.key).join(" ")}
|
key={`group-${groupPrefix}-${idx}`}
|
||||||
key={`group-${groupPrefix}-${idx}`}
|
title={groupPrefix}
|
||||||
title={groupPrefix}
|
isActive={!this.isCollapsed(groupPrefix, idx) || idx === this.props.selectedLayerIndex}
|
||||||
isActive={
|
onActiveToggle={this.toggleLayerGroup.bind(this, groupPrefix, idx)}
|
||||||
!this.isCollapsed(groupPrefix, idx) ||
|
/>
|
||||||
idx === this.props.selectedLayerIndex
|
listItems.push(grp)
|
||||||
}
|
|
||||||
onActiveToggle={this.toggleLayerGroup.bind(this, groupPrefix, idx)}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
listItems.push(grp);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
layers.forEach((layer, idxInGroup) => {
|
layers.forEach((layer, idxInGroup) => {
|
||||||
const groupIdx = findClosestCommonPrefix(this.props.layers, idx);
|
const groupIdx = findClosestCommonPrefix(this.props.layers, idx)
|
||||||
|
|
||||||
const layerError = this.props.errors.find((error) => {
|
const layerError = this.props.errors.find(error => {
|
||||||
return (
|
return (
|
||||||
error.parsed &&
|
error.parsed &&
|
||||||
error.parsed.type === "layer" &&
|
error.parsed.type === "layer" &&
|
||||||
@@ -247,107 +240,94 @@ class LayerListContainer extends React.Component {
|
|||||||
additionalProps.ref = this.selectedItemRef;
|
additionalProps.ref = this.selectedItemRef;
|
||||||
}
|
}
|
||||||
|
|
||||||
const listItem = (
|
const listItem = <LayerListItem
|
||||||
<LayerListItem
|
className={classnames({
|
||||||
className={classnames({
|
'maputnik-layer-list-item-collapsed': layers.length > 1 && this.isCollapsed(groupPrefix, groupIdx) && idx !== this.props.selectedLayerIndex,
|
||||||
"maputnik-layer-list-item-collapsed":
|
'maputnik-layer-list-item-group-last': idxInGroup == layers.length - 1 && layers.length > 1,
|
||||||
layers.length > 1 &&
|
'maputnik-layer-list-item--error': !!layerError
|
||||||
this.isCollapsed(groupPrefix, groupIdx) &&
|
})}
|
||||||
idx !== this.props.selectedLayerIndex,
|
index={idx}
|
||||||
"maputnik-layer-list-item-group-last":
|
key={layer.key}
|
||||||
idxInGroup == layers.length - 1 && layers.length > 1,
|
id={layer.key}
|
||||||
"maputnik-layer-list-item--error": !!layerError,
|
layerId={layer.id}
|
||||||
})}
|
layerIndex={idx}
|
||||||
index={idx}
|
layerType={layer.type}
|
||||||
key={layer.key}
|
visibility={(layer.layout || {}).visibility}
|
||||||
id={layer.key}
|
isSelected={idx === this.props.selectedLayerIndex}
|
||||||
layerId={layer.id}
|
onLayerSelect={this.props.onLayerSelect}
|
||||||
layerIndex={idx}
|
onLayerDestroy={this.props.onLayerDestroy.bind(this)}
|
||||||
layerType={layer.type}
|
onLayerCopy={this.props.onLayerCopy.bind(this)}
|
||||||
visibility={(layer.layout || {}).visibility}
|
onLayerVisibilityToggle={this.props.onLayerVisibilityToggle.bind(this)}
|
||||||
isSelected={idx === this.props.selectedLayerIndex}
|
{...additionalProps}
|
||||||
onLayerSelect={this.props.onLayerSelect}
|
/>
|
||||||
onLayerDestroy={this.props.onLayerDestroy.bind(this)}
|
listItems.push(listItem)
|
||||||
onLayerCopy={this.props.onLayerCopy.bind(this)}
|
idx += 1
|
||||||
onLayerVisibilityToggle={this.props.onLayerVisibilityToggle.bind(
|
})
|
||||||
this
|
})
|
||||||
)}
|
|
||||||
{...additionalProps}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
listItems.push(listItem);
|
|
||||||
idx += 1;
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
return (
|
return <section
|
||||||
<section
|
className="maputnik-layer-list"
|
||||||
className="maputnik-layer-list"
|
role="complementary"
|
||||||
role="complementary"
|
aria-label="Layers list"
|
||||||
aria-label="Layers list"
|
ref={this.scrollContainerRef}
|
||||||
ref={this.scrollContainerRef}
|
>
|
||||||
>
|
<ModalAdd
|
||||||
<ModalAdd
|
|
||||||
key={this.state.keys.add}
|
key={this.state.keys.add}
|
||||||
layers={this.props.layers}
|
layers={this.props.layers}
|
||||||
sources={this.props.sources}
|
sources={this.props.sources}
|
||||||
isOpen={this.state.isOpen.add}
|
isOpen={this.state.isOpen.add}
|
||||||
onOpenToggle={this.toggleModal.bind(this, "add")}
|
onOpenToggle={this.toggleModal.bind(this, 'add')}
|
||||||
onLayersChange={this.props.onLayersChange}
|
onLayersChange={this.props.onLayersChange}
|
||||||
/>
|
/>
|
||||||
<header className="maputnik-layer-list-header">
|
<header className="maputnik-layer-list-header">
|
||||||
<span className="maputnik-layer-list-header-title">Layers</span>
|
<span className="maputnik-layer-list-header-title">Layers</span>
|
||||||
<span className="maputnik-space" />
|
<span className="maputnik-space" />
|
||||||
<div className="maputnik-default-property">
|
<div className="maputnik-default-property">
|
||||||
<div className="maputnik-multibutton">
|
<div className="maputnik-multibutton">
|
||||||
<button
|
<button
|
||||||
id="skip-target-layer-list"
|
id="skip-target-layer-list"
|
||||||
data-wd-key="skip-target-layer-list"
|
data-wd-key="skip-target-layer-list"
|
||||||
onClick={this.toggleLayers}
|
onClick={this.toggleLayers}
|
||||||
className="maputnik-button"
|
className="maputnik-button">
|
||||||
>
|
{this.state.areAllGroupsExpanded === true ? "Collapse" : "Expand"}
|
||||||
{this.state.areAllGroupsExpanded === true
|
</button>
|
||||||
? "Collapse"
|
|
||||||
: "Expand"}
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
<div className="maputnik-default-property">
|
|
||||||
<div className="maputnik-multibutton">
|
|
||||||
<button
|
|
||||||
onClick={this.toggleModal.bind(this, "add")}
|
|
||||||
data-wd-key="layer-list:add-layer"
|
|
||||||
className="maputnik-button maputnik-button-selected"
|
|
||||||
>
|
|
||||||
Add Layer
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</header>
|
|
||||||
<div role="navigation" aria-label="Layers list">
|
|
||||||
<ul className="maputnik-layer-list-container">{listItems}</ul>
|
|
||||||
</div>
|
</div>
|
||||||
</section>
|
<div className="maputnik-default-property">
|
||||||
);
|
<div className="maputnik-multibutton">
|
||||||
|
<button
|
||||||
|
onClick={this.toggleModal.bind(this, 'add')}
|
||||||
|
data-wd-key="layer-list:add-layer"
|
||||||
|
className="maputnik-button maputnik-button-selected">
|
||||||
|
Add Layer
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</header>
|
||||||
|
<div
|
||||||
|
role="navigation"
|
||||||
|
aria-label="Layers list"
|
||||||
|
>
|
||||||
|
<ul className="maputnik-layer-list-container">
|
||||||
|
{listItems}
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const LayerListContainerSortable = SortableContainer((props) => (
|
const LayerListContainerSortable = SortableContainer((props) => <LayerListContainer {...props} />)
|
||||||
<LayerListContainer {...props} />
|
|
||||||
));
|
|
||||||
|
|
||||||
export default class LayerList extends React.Component {
|
export default class LayerList extends React.Component {
|
||||||
static propTypes = { ...layerListPropTypes };
|
static propTypes = {...layerListPropTypes}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
return (
|
return <LayerListContainerSortable
|
||||||
<LayerListContainerSortable
|
{...this.props}
|
||||||
{...this.props}
|
helperClass='sortableHelper'
|
||||||
helperClass="sortableHelper"
|
onSortEnd={this.props.onMoveLayer.bind(this)}
|
||||||
onSortEnd={this.props.onMoveLayer.bind(this)}
|
useDragHandle={true}
|
||||||
useDragHandle={true}
|
shouldCancelStart={() => false}
|
||||||
shouldCancelStart={() => false}
|
/>
|
||||||
/>
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+101
-123
@@ -1,17 +1,19 @@
|
|||||||
import Color from "color";
|
import React from 'react'
|
||||||
import MapboxInspect from "mapbox-gl-inspect";
|
import PropTypes from 'prop-types'
|
||||||
import colors from "mapbox-gl-inspect/lib/colors";
|
import ReactDOM from 'react-dom'
|
||||||
import MapLibreGl from "maplibre-gl";
|
import MapLibreGl from 'maplibre-gl'
|
||||||
import "maplibre-gl/dist/maplibre-gl.css";
|
import MapboxInspect from 'mapbox-gl-inspect'
|
||||||
import PropTypes from "prop-types";
|
import MapMaplibreGlLayerPopup from './MapMaplibreGlLayerPopup'
|
||||||
import React from "react";
|
import MapMaplibreGlFeaturePropertyPopup from './MapMaplibreGlFeaturePropertyPopup'
|
||||||
import ReactDOM from "react-dom";
|
import tokens from '../config/tokens.json'
|
||||||
import { colorHighlightedLayer } from "../libs/highlight";
|
import colors from 'mapbox-gl-inspect/lib/colors'
|
||||||
import "../libs/maplibre-rtl";
|
import Color from 'color'
|
||||||
import ZoomControl from "../libs/zoomcontrol";
|
import ZoomControl from '../libs/zoomcontrol'
|
||||||
import "../maplibregl.css";
|
import { colorHighlightedLayer } from '../libs/highlight'
|
||||||
import MapMaplibreGlFeaturePropertyPopup from "./MapMaplibreGlFeaturePropertyPopup";
|
import 'maplibre-gl/dist/maplibre-gl.css'
|
||||||
import MapMaplibreGlLayerPopup from "./MapMaplibreGlLayerPopup";
|
import '../maplibregl.css'
|
||||||
|
import '../libs/maplibre-rtl'
|
||||||
|
|
||||||
|
|
||||||
const IS_SUPPORTED = MapLibreGl.supported();
|
const IS_SUPPORTED = MapLibreGl.supported();
|
||||||
|
|
||||||
@@ -22,32 +24,32 @@ function renderPopup(popup, mountNode) {
|
|||||||
|
|
||||||
function buildInspectStyle(originalMapStyle, coloredLayers, highlightedLayer) {
|
function buildInspectStyle(originalMapStyle, coloredLayers, highlightedLayer) {
|
||||||
const backgroundLayer = {
|
const backgroundLayer = {
|
||||||
id: "background",
|
"id": "background",
|
||||||
type: "background",
|
"type": "background",
|
||||||
paint: {
|
"paint": {
|
||||||
"background-color": "#1c1f24",
|
"background-color": '#1c1f24',
|
||||||
},
|
}
|
||||||
};
|
|
||||||
|
|
||||||
const layer = colorHighlightedLayer(highlightedLayer);
|
|
||||||
if (layer) {
|
|
||||||
coloredLayers.push(layer);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const sources = {};
|
const layer = colorHighlightedLayer(highlightedLayer)
|
||||||
Object.keys(originalMapStyle.sources).forEach((sourceId) => {
|
if(layer) {
|
||||||
const source = originalMapStyle.sources[sourceId];
|
coloredLayers.push(layer)
|
||||||
if (source.type !== "raster" && source.type !== "raster-dem") {
|
}
|
||||||
sources[sourceId] = source;
|
|
||||||
|
const sources = {}
|
||||||
|
Object.keys(originalMapStyle.sources).forEach(sourceId => {
|
||||||
|
const source = originalMapStyle.sources[sourceId]
|
||||||
|
if(source.type !== 'raster' && source.type !== 'raster-dem') {
|
||||||
|
sources[sourceId] = source
|
||||||
}
|
}
|
||||||
});
|
})
|
||||||
|
|
||||||
const inspectStyle = {
|
const inspectStyle = {
|
||||||
...originalMapStyle,
|
...originalMapStyle,
|
||||||
sources: sources,
|
sources: sources,
|
||||||
layers: [backgroundLayer].concat(coloredLayers),
|
layers: [backgroundLayer].concat(coloredLayers)
|
||||||
};
|
}
|
||||||
return inspectStyle;
|
return inspectStyle
|
||||||
}
|
}
|
||||||
|
|
||||||
export default class MapMaplibreGl extends React.Component {
|
export default class MapMaplibreGl extends React.Component {
|
||||||
@@ -60,7 +62,7 @@ export default class MapMaplibreGl extends React.Component {
|
|||||||
options: PropTypes.object,
|
options: PropTypes.object,
|
||||||
replaceAccessTokens: PropTypes.func.isRequired,
|
replaceAccessTokens: PropTypes.func.isRequired,
|
||||||
onChange: PropTypes.func.isRequired,
|
onChange: PropTypes.func.isRequired,
|
||||||
};
|
}
|
||||||
|
|
||||||
static defaultProps = {
|
static defaultProps = {
|
||||||
onMapLoaded: () => {},
|
onMapLoaded: () => {},
|
||||||
@@ -68,55 +70,51 @@ export default class MapMaplibreGl extends React.Component {
|
|||||||
onLayerSelect: () => {},
|
onLayerSelect: () => {},
|
||||||
onChange: () => {},
|
onChange: () => {},
|
||||||
options: {},
|
options: {},
|
||||||
};
|
}
|
||||||
|
|
||||||
constructor(props) {
|
constructor(props) {
|
||||||
super(props);
|
super(props)
|
||||||
this.state = {
|
this.state = {
|
||||||
map: null,
|
map: null,
|
||||||
inspect: null,
|
inspect: null,
|
||||||
};
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
updateMapFromProps(props) {
|
updateMapFromProps(props) {
|
||||||
if (!IS_SUPPORTED) return;
|
if(!IS_SUPPORTED) return;
|
||||||
|
|
||||||
if (!this.state.map) return;
|
if(!this.state.map) return
|
||||||
|
|
||||||
//Maplibre GL now does diffing natively so we don't need to calculate
|
//Maplibre GL now does diffing natively so we don't need to calculate
|
||||||
//the necessary operations ourselves!
|
//the necessary operations ourselves!
|
||||||
this.state.map.setStyle(this.props.replaceAccessTokens(props.mapStyle), {
|
this.state.map.setStyle(
|
||||||
diff: true,
|
this.props.replaceAccessTokens(props.mapStyle),
|
||||||
});
|
{diff: true}
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
shouldComponentUpdate(nextProps, nextState) {
|
shouldComponentUpdate(nextProps, nextState) {
|
||||||
let should = false;
|
let should = false;
|
||||||
try {
|
try {
|
||||||
should =
|
should = JSON.stringify(this.props) !== JSON.stringify(nextProps) || JSON.stringify(this.state) !== JSON.stringify(nextState);
|
||||||
JSON.stringify(this.props) !== JSON.stringify(nextProps) ||
|
} catch(e) {
|
||||||
JSON.stringify(this.state) !== JSON.stringify(nextState);
|
|
||||||
} catch (e) {
|
|
||||||
// no biggie, carry on
|
// no biggie, carry on
|
||||||
}
|
}
|
||||||
return should;
|
return should;
|
||||||
}
|
}
|
||||||
|
|
||||||
componentDidUpdate(prevProps, prevState, snapshot) {
|
componentDidUpdate(prevProps, prevState, snapshot) {
|
||||||
if (!IS_SUPPORTED) return;
|
if(!IS_SUPPORTED) return;
|
||||||
|
|
||||||
const map = this.state.map;
|
const map = this.state.map;
|
||||||
|
|
||||||
this.updateMapFromProps(this.props);
|
this.updateMapFromProps(this.props);
|
||||||
|
|
||||||
if (
|
if(this.state.inspect && this.props.inspectModeEnabled !== this.state.inspect._showInspectMap) {
|
||||||
this.state.inspect &&
|
|
||||||
this.props.inspectModeEnabled !== this.state.inspect._showInspectMap
|
|
||||||
) {
|
|
||||||
// HACK: Fix for <https://github.com/maputnik/editor/issues/576>, while we wait for a proper fix.
|
// HACK: Fix for <https://github.com/maputnik/editor/issues/576>, while we wait for a proper fix.
|
||||||
// eslint-disable-next-line
|
// eslint-disable-next-line
|
||||||
this.state.inspect._popupBlocked = false;
|
this.state.inspect._popupBlocked = false;
|
||||||
this.state.inspect.toggleInspector();
|
this.state.inspect.toggleInspector()
|
||||||
}
|
}
|
||||||
if (map) {
|
if (map) {
|
||||||
if (this.props.inspectModeEnabled) {
|
if (this.props.inspectModeEnabled) {
|
||||||
@@ -125,7 +123,7 @@ export default class MapMaplibreGl extends React.Component {
|
|||||||
// mapbox-gl-inspect.
|
// mapbox-gl-inspect.
|
||||||
try {
|
try {
|
||||||
this.state.inspect.render();
|
this.state.inspect.render();
|
||||||
} catch (err) {
|
} catch(err) {
|
||||||
console.error("FIXME: Caught error", err);
|
console.error("FIXME: Caught error", err);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -137,40 +135,40 @@ export default class MapMaplibreGl extends React.Component {
|
|||||||
}
|
}
|
||||||
|
|
||||||
componentDidMount() {
|
componentDidMount() {
|
||||||
if (!IS_SUPPORTED) return;
|
if(!IS_SUPPORTED) return;
|
||||||
|
|
||||||
const mapOpts = {
|
const mapOpts = {
|
||||||
...this.props.options,
|
...this.props.options,
|
||||||
container: this.container,
|
container: this.container,
|
||||||
style: this.props.mapStyle,
|
style: this.props.mapStyle,
|
||||||
hash: true,
|
hash: true,
|
||||||
maxZoom: 24,
|
maxZoom: 24
|
||||||
};
|
}
|
||||||
|
|
||||||
const map = new MapLibreGl.Map(mapOpts);
|
const map = new MapLibreGl.Map(mapOpts);
|
||||||
|
|
||||||
const mapViewChange = () => {
|
const mapViewChange = () => {
|
||||||
const center = map.getCenter();
|
const center = map.getCenter();
|
||||||
const zoom = map.getZoom();
|
const zoom = map.getZoom();
|
||||||
this.props.onChange({ center, zoom });
|
this.props.onChange({center, zoom});
|
||||||
};
|
}
|
||||||
mapViewChange();
|
mapViewChange();
|
||||||
|
|
||||||
map.showTileBoundaries = mapOpts.showTileBoundaries;
|
map.showTileBoundaries = mapOpts.showTileBoundaries;
|
||||||
map.showCollisionBoxes = mapOpts.showCollisionBoxes;
|
map.showCollisionBoxes = mapOpts.showCollisionBoxes;
|
||||||
map.showOverdrawInspector = mapOpts.showOverdrawInspector;
|
map.showOverdrawInspector = mapOpts.showOverdrawInspector;
|
||||||
|
|
||||||
const zoomControl = new ZoomControl();
|
const zoomControl = new ZoomControl;
|
||||||
map.addControl(zoomControl, "top-right");
|
map.addControl(zoomControl, 'top-right');
|
||||||
|
|
||||||
const nav = new MapLibreGl.NavigationControl({ visualizePitch: true });
|
const nav = new MapLibreGl.NavigationControl({visualizePitch:true});
|
||||||
map.addControl(nav, "top-right");
|
map.addControl(nav, 'top-right');
|
||||||
|
|
||||||
const tmpNode = document.createElement("div");
|
const tmpNode = document.createElement('div');
|
||||||
|
|
||||||
const inspect = new MapboxInspect({
|
const inspect = new MapboxInspect({
|
||||||
popup: new MapLibreGl.Popup({
|
popup: new MapLibreGl.Popup({
|
||||||
closeOnClick: false,
|
closeOnClick: false
|
||||||
}),
|
}),
|
||||||
showMapPopup: true,
|
showMapPopup: true,
|
||||||
showMapPopupOnHover: false,
|
showMapPopupOnHover: false,
|
||||||
@@ -178,58 +176,41 @@ export default class MapMaplibreGl extends React.Component {
|
|||||||
showInspectButton: false,
|
showInspectButton: false,
|
||||||
blockHoverPopupOnClick: true,
|
blockHoverPopupOnClick: true,
|
||||||
assignLayerColor: (layerId, alpha) => {
|
assignLayerColor: (layerId, alpha) => {
|
||||||
return Color(colors.brightColor(layerId, alpha))
|
return Color(colors.brightColor(layerId, alpha)).desaturate(0.5).string()
|
||||||
.desaturate(0.5)
|
|
||||||
.string();
|
|
||||||
},
|
},
|
||||||
buildInspectStyle: (originalMapStyle, coloredLayers) =>
|
buildInspectStyle: (originalMapStyle, coloredLayers) => buildInspectStyle(originalMapStyle, coloredLayers, this.props.highlightedLayer),
|
||||||
buildInspectStyle(
|
renderPopup: features => {
|
||||||
originalMapStyle,
|
if(this.props.inspectModeEnabled) {
|
||||||
coloredLayers,
|
return renderPopup(<MapMaplibreGlFeaturePropertyPopup features={features} />, tmpNode);
|
||||||
this.props.highlightedLayer
|
|
||||||
),
|
|
||||||
renderPopup: (features) => {
|
|
||||||
if (this.props.inspectModeEnabled) {
|
|
||||||
return renderPopup(
|
|
||||||
<MapMaplibreGlFeaturePropertyPopup features={features} />,
|
|
||||||
tmpNode
|
|
||||||
);
|
|
||||||
} else {
|
} else {
|
||||||
return renderPopup(
|
return renderPopup(<MapMaplibreGlLayerPopup features={features} onLayerSelect={this.onLayerSelectById} zoom={this.state.zoom} />, tmpNode);
|
||||||
<MapMaplibreGlLayerPopup
|
|
||||||
features={features}
|
|
||||||
onLayerSelect={this.onLayerSelectById}
|
|
||||||
zoom={this.state.zoom}
|
|
||||||
/>,
|
|
||||||
tmpNode
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
},
|
}
|
||||||
});
|
})
|
||||||
map.addControl(inspect);
|
map.addControl(inspect)
|
||||||
|
|
||||||
map.on("style.load", () => {
|
map.on("style.load", () => {
|
||||||
this.setState({
|
this.setState({
|
||||||
map,
|
map,
|
||||||
inspect,
|
inspect,
|
||||||
zoom: map.getZoom(),
|
zoom: map.getZoom()
|
||||||
});
|
});
|
||||||
});
|
})
|
||||||
|
|
||||||
map.on("data", (e) => {
|
map.on("data", e => {
|
||||||
if (e.dataType !== "tile") return;
|
if(e.dataType !== 'tile') return
|
||||||
this.props.onDataChange({
|
this.props.onDataChange({
|
||||||
map: this.state.map,
|
map: this.state.map
|
||||||
});
|
})
|
||||||
});
|
})
|
||||||
|
|
||||||
map.on("error", (e) => {
|
map.on("error", e => {
|
||||||
console.log("ERROR", e);
|
console.log("ERROR", e);
|
||||||
});
|
})
|
||||||
|
|
||||||
map.on("zoom", (e) => {
|
map.on("zoom", e => {
|
||||||
this.setState({
|
this.setState({
|
||||||
zoom: map.getZoom(),
|
zoom: map.getZoom()
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -238,32 +219,29 @@ export default class MapMaplibreGl extends React.Component {
|
|||||||
}
|
}
|
||||||
|
|
||||||
onLayerSelectById = (id) => {
|
onLayerSelectById = (id) => {
|
||||||
const index = this.props.mapStyle.layers.findIndex(
|
const index = this.props.mapStyle.layers.findIndex(layer => layer.id === id);
|
||||||
(layer) => layer.id === id
|
|
||||||
);
|
|
||||||
this.props.onLayerSelect(index);
|
this.props.onLayerSelect(index);
|
||||||
};
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
if (IS_SUPPORTED) {
|
if(IS_SUPPORTED) {
|
||||||
return (
|
return <div
|
||||||
<div
|
className="maputnik-map__map"
|
||||||
className="maputnik-map__map"
|
role="region"
|
||||||
role="region"
|
aria-label="Map view"
|
||||||
aria-label="Map view"
|
ref={x => this.container = x}
|
||||||
ref={(x) => (this.container = x)}
|
data-wd-key="maplibre:map"
|
||||||
data-wd-key="maplibre:map"
|
></div>
|
||||||
></div>
|
}
|
||||||
);
|
else {
|
||||||
} else {
|
return <div
|
||||||
return (
|
className="maputnik-map maputnik-map--error"
|
||||||
<div className="maputnik-map maputnik-map--error">
|
>
|
||||||
<div className="maputnik-map__error-message">
|
<div className="maputnik-map__error-message">
|
||||||
Error: Cannot load MaplibreGL, WebGL is either unsupported or
|
Error: Cannot load MaplibreGL, WebGL is either unsupported or disabled
|
||||||
disabled
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
);
|
</div>
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,12 +1,16 @@
|
|||||||
import querystring from 'querystring'
|
interface DebugStore {
|
||||||
|
[namespace: string]: {
|
||||||
|
[key: string]: any
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const debugStore: DebugStore = {};
|
||||||
const debugStore = {};
|
|
||||||
|
|
||||||
function enabled() {
|
function enabled() {
|
||||||
const qs = querystring.parse(window.location.search.slice(1));
|
const qs = new URL(window.location.href).searchParams;
|
||||||
if(qs.hasOwnProperty("debug")) {
|
const debugQs = qs.get("debug");
|
||||||
return !!qs.debug.match(/^(|1|true)$/);
|
if(debugQs) {
|
||||||
|
return !!debugQs.match(/^(|1|true)$/);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
return false;
|
return false;
|
||||||
@@ -17,7 +21,7 @@ function genErr() {
|
|||||||
return new Error("Debug not enabled, enable by appending '?debug' to your query string");
|
return new Error("Debug not enabled, enable by appending '?debug' to your query string");
|
||||||
}
|
}
|
||||||
|
|
||||||
function set(namespace, key, value) {
|
function set(namespace: keyof DebugStore, key: string, value: any) {
|
||||||
if(!enabled()) {
|
if(!enabled()) {
|
||||||
throw genErr();
|
throw genErr();
|
||||||
}
|
}
|
||||||
@@ -25,7 +29,7 @@ function set(namespace, key, value) {
|
|||||||
debugStore[namespace][key] = value;
|
debugStore[namespace][key] = value;
|
||||||
}
|
}
|
||||||
|
|
||||||
function get(namespace, key) {
|
function get(namespace: keyof DebugStore, key: string) {
|
||||||
if(!enabled()) {
|
if(!enabled()) {
|
||||||
throw genErr();
|
throw genErr();
|
||||||
}
|
}
|
||||||
@@ -38,7 +42,7 @@ const mod = {
|
|||||||
enabled,
|
enabled,
|
||||||
get,
|
get,
|
||||||
set
|
set
|
||||||
}
|
};
|
||||||
|
|
||||||
window.debug = mod;
|
(window as any).debug = mod;
|
||||||
export default mod;
|
export default mod;
|
||||||
@@ -1,9 +1,8 @@
|
|||||||
export default class ZoomControl {
|
export default class ZoomControl {
|
||||||
onAdd(map) {
|
onAdd(map) {
|
||||||
this._map = map;
|
this._map = map;
|
||||||
this._container = document.createElement("div");
|
this._container = document.createElement('div');
|
||||||
this._container.className =
|
this._container.className = 'maplibregl-ctrl maplibregl-ctrl-group maplibregl-ctrl-zoom';
|
||||||
"maplibregl-ctrl maplibregl-ctrl-group maplibregl-ctrl-zoom";
|
|
||||||
this._container.setAttribute("data-wd-key", "maplibre:ctrl-zoom");
|
this._container.setAttribute("data-wd-key", "maplibre:ctrl-zoom");
|
||||||
this._container.innerHTML = `
|
this._container.innerHTML = `
|
||||||
Zoom: <span></span>
|
Zoom: <span></span>
|
||||||
@@ -19,10 +18,10 @@ export default class ZoomControl {
|
|||||||
this._textEl.innerHTML = this._map.getZoom().toFixed(2);
|
this._textEl.innerHTML = this._map.getZoom().toFixed(2);
|
||||||
}
|
}
|
||||||
|
|
||||||
addEventListeners() {
|
addEventListeners (){
|
||||||
this._map.on("render", this.updateZoomLevel.bind(this));
|
this._map.on('render', this.updateZoomLevel.bind(this) );
|
||||||
this._map.on("zoomIn", this.updateZoomLevel.bind(this));
|
this._map.on('zoomIn', this.updateZoomLevel.bind(this) );
|
||||||
this._map.on("zoomOut", this.updateZoomLevel.bind(this));
|
this._map.on('zoomOut', this.updateZoomLevel.bind(this) );
|
||||||
}
|
}
|
||||||
|
|
||||||
onRemove() {
|
onRemove() {
|
||||||
|
|||||||
+12
-1
@@ -1,11 +1,22 @@
|
|||||||
import { defineConfig } from "vite";
|
import { defineConfig } from "vite";
|
||||||
import react from "@vitejs/plugin-react";
|
import react from "@vitejs/plugin-react";
|
||||||
|
import replace from '@rollup/plugin-replace';
|
||||||
|
|
||||||
export default defineConfig({
|
export default defineConfig({
|
||||||
server: {
|
server: {
|
||||||
port: 8888
|
port: 8888
|
||||||
},
|
},
|
||||||
plugins: [react()],
|
plugins: [
|
||||||
|
replace({
|
||||||
|
preventAssignment: true,
|
||||||
|
include: /\/jsonlint-lines-primitives\/lib\/jsonlint.js/,
|
||||||
|
delimiters: ['', ''],
|
||||||
|
values: {
|
||||||
|
'_token_stack:': ''
|
||||||
|
}
|
||||||
|
}) as any,
|
||||||
|
react()
|
||||||
|
],
|
||||||
define: {
|
define: {
|
||||||
global: "window",
|
global: "window",
|
||||||
},
|
},
|
||||||
|
|||||||
Reference in New Issue
Block a user