mirror of
https://github.com/maputnik/editor.git
synced 2026-08-24 05:57:25 +00:00
Merge branch 'main' into codemirror-upgrade
This commit is contained in:
@@ -5,6 +5,8 @@
|
|||||||
- Add support for hillshade's color arrays and relief-color elevation expression
|
- Add support for hillshade's color arrays and relief-color elevation expression
|
||||||
- Change layers icons to make them a bit more distinct
|
- Change layers icons to make them a bit more distinct
|
||||||
- Remove `@mdi` packages in favor of `react-icons`
|
- Remove `@mdi` packages in favor of `react-icons`
|
||||||
|
- Add ability to control the projection of the map - either globe or mercator
|
||||||
|
- Add markdown support for doc related to the style-spec fields
|
||||||
- Added global state modal to allow editing the global state
|
- Added global state modal to allow editing the global state
|
||||||
- _...Add new stuff here..._
|
- _...Add new stuff here..._
|
||||||
|
|
||||||
@@ -14,6 +16,7 @@
|
|||||||
- Fix modal close button possition
|
- Fix modal close button possition
|
||||||
- Fixed an issue with the generation of tranlations
|
- Fixed an issue with the generation of tranlations
|
||||||
- Fix missing spec info when clicking next to a property
|
- Fix missing spec info when clicking next to a property
|
||||||
|
- Fix Firefox open file that stopped working due to react upgrade
|
||||||
- _...Add new stuff here..._
|
- _...Add new stuff here..._
|
||||||
|
|
||||||
## 3.0.0
|
## 3.0.0
|
||||||
|
|||||||
+50
-21
@@ -491,6 +491,20 @@ describe("layers", () => {
|
|||||||
when.click("field-doc-button-Offset", 0);
|
when.click("field-doc-button-Offset", 0);
|
||||||
then(get.elementByTestId("spec-field-doc")).shouldContainText("Offset distance");
|
then(get.elementByTestId("spec-field-doc")).shouldContainText("Offset distance");
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it("should hide spec info when clicking a second time", () => {
|
||||||
|
when.modal.fillLayers({
|
||||||
|
type: "symbol",
|
||||||
|
layer: "example",
|
||||||
|
});
|
||||||
|
|
||||||
|
when.hover("spec-field-container:text-rotate");
|
||||||
|
then(get.elementByTestId("field-doc-button-Rotate")).shouldBeVisible();
|
||||||
|
when.click("field-doc-button-Rotate", 0);
|
||||||
|
when.wait(200);
|
||||||
|
when.click("field-doc-button-Rotate", 0);
|
||||||
|
then(get.elementByTestId("spec-field-doc")).shouldNotBeVisible();
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe("raster", () => {
|
describe("raster", () => {
|
||||||
@@ -684,33 +698,48 @@ describe("layers", () => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe("layereditor jsonlint should error", ()=>{
|
describe("layers editor", () => {
|
||||||
it("add", () => {
|
describe("property fields", () => {
|
||||||
const id = when.modal.fillLayers({
|
it("should show error", () => {
|
||||||
type: "circle",
|
when.modal.fillLayers({
|
||||||
layer: "example",
|
type: "circle",
|
||||||
|
layer: "invalid",
|
||||||
|
});
|
||||||
|
|
||||||
|
then(get.element(".maputnik-input-block--error .maputnik-input-block-label")).shouldHaveCss("color", "rgb(207, 74, 74)");
|
||||||
});
|
});
|
||||||
|
});
|
||||||
|
|
||||||
then(get.styleFromLocalStorage()).shouldDeepNestedInclude({
|
describe("jsonlint should error", ()=>{
|
||||||
layers: [
|
it("add", () => {
|
||||||
{
|
const id = when.modal.fillLayers({
|
||||||
id: id,
|
type: "circle",
|
||||||
type: "circle",
|
layer: "example",
|
||||||
source: "example",
|
});
|
||||||
},
|
|
||||||
],
|
then(get.styleFromLocalStorage()).shouldDeepNestedInclude({
|
||||||
|
layers: [
|
||||||
|
{
|
||||||
|
id: id,
|
||||||
|
type: "circle",
|
||||||
|
source: "example",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
});
|
||||||
|
|
||||||
|
const sourceText = get.elementByText('"source"');
|
||||||
|
|
||||||
|
sourceText.click();
|
||||||
|
sourceText.type("\"");
|
||||||
|
|
||||||
|
const error = get.element(".CodeMirror-lint-marker-error");
|
||||||
|
error.should("exist");
|
||||||
});
|
});
|
||||||
|
|
||||||
const sourceText = get.elementByText('"source"');
|
|
||||||
|
|
||||||
sourceText.click();
|
|
||||||
sourceText.type("\"");
|
|
||||||
|
|
||||||
const error = get.element(".CodeMirror-lint-marker-error");
|
|
||||||
error.should("exist");
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
describe("drag and drop", () => {
|
describe("drag and drop", () => {
|
||||||
it("move layer should update local storage", () => {
|
it("move layer should update local storage", () => {
|
||||||
when.modal.open();
|
when.modal.open();
|
||||||
|
|||||||
@@ -26,6 +26,22 @@ export default class MaputnikCypressHelper {
|
|||||||
this.helper.when.wait(200);
|
this.helper.when.wait(200);
|
||||||
this.helper.get.elementByTestId(element).realMouseUp();
|
this.helper.get.elementByTestId(element).realMouseUp();
|
||||||
},
|
},
|
||||||
|
openFileByFixture: (fixture: string, buttonTestId: string, inputTestId: string) => {
|
||||||
|
cy.window().then((win) => {
|
||||||
|
const file = {
|
||||||
|
text: cy.stub().resolves(cy.fixture(fixture).then(JSON.stringify)),
|
||||||
|
};
|
||||||
|
const fileHandle = {
|
||||||
|
getFile: cy.stub().resolves(file),
|
||||||
|
};
|
||||||
|
if (!win.showOpenFilePicker) {
|
||||||
|
this.helper.get.elementByTestId(inputTestId).selectFile("cypress/fixtures/" + fixture, { force: true });
|
||||||
|
} else {
|
||||||
|
cy.stub(win, "showOpenFilePicker").resolves([fileHandle]);
|
||||||
|
this.helper.get.elementByTestId(buttonTestId).click();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
},
|
||||||
...this.helper.when,
|
...this.helper.when,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -109,9 +109,9 @@ export class MaputnikDriver {
|
|||||||
this.helper.when.waitForResponse("example-style.json");
|
this.helper.when.waitForResponse("example-style.json");
|
||||||
},
|
},
|
||||||
chooseExampleFile: () => {
|
chooseExampleFile: () => {
|
||||||
this.helper.get
|
this.helper.given.fixture("example-style.json", "example-style.json");
|
||||||
.bySelector("type", "file")
|
this.helper.when.openFileByFixture("example-style.json", "modal:open.file.button", "modal:open.file.input");
|
||||||
.selectFile("cypress/fixtures/example-style.json", { force: true });
|
this.helper.when.wait(200);
|
||||||
},
|
},
|
||||||
setStyle: (
|
setStyle: (
|
||||||
styleProperties: "geojson" | "raster" | "both" | "layer" | "rectangles" | "",
|
styleProperties: "geojson" | "raster" | "both" | "layer" | "rectangles" | "",
|
||||||
|
|||||||
@@ -18,10 +18,9 @@ describe("modals", () => {
|
|||||||
then(get.elementByTestId("modal:open")).shouldNotExist();
|
then(get.elementByTestId("modal:open")).shouldNotExist();
|
||||||
});
|
});
|
||||||
|
|
||||||
it.skip("upload", () => {
|
it("upload", () => {
|
||||||
// HM: I was not able to make the following choose file actually to select a file and close the modal...
|
|
||||||
when.chooseExampleFile();
|
when.chooseExampleFile();
|
||||||
then(get.responseBody("example-style.json")).shouldEqualToStoredStyle();
|
then(get.fixture("example-style.json")).shouldEqualToStoredStyle();
|
||||||
});
|
});
|
||||||
|
|
||||||
describe("when click open url", () => {
|
describe("when click open url", () => {
|
||||||
@@ -236,6 +235,29 @@ describe("modals", () => {
|
|||||||
).shouldInclude({ "maputnik:locationiq_access_token": apiKey });
|
).shouldInclude({ "maputnik:locationiq_access_token": apiKey });
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it("style projection mercator", () => {
|
||||||
|
when.select("modal:settings.projection", "mercator");
|
||||||
|
then(
|
||||||
|
get.styleFromLocalStorage().then((style) => style.projection)
|
||||||
|
).shouldInclude({ type: "mercator" });
|
||||||
|
});
|
||||||
|
|
||||||
|
it("style projection globe", () => {
|
||||||
|
when.select("modal:settings.projection", "globe");
|
||||||
|
then(
|
||||||
|
get.styleFromLocalStorage().then((style) => style.projection)
|
||||||
|
).shouldInclude({ type: "globe" });
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
it("style projection vertical-perspective", () => {
|
||||||
|
when.select("modal:settings.projection", "vertical-perspective");
|
||||||
|
then(
|
||||||
|
get.styleFromLocalStorage().then((style) => style.projection)
|
||||||
|
).shouldInclude({ type: "vertical-perspective" });
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
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("modal:settings.maputnik:renderer", "ol");
|
when.select("modal:settings.maputnik:renderer", "ol");
|
||||||
@@ -311,6 +333,7 @@ describe("modals", () => {
|
|||||||
|
|
||||||
it("add variable", () => {
|
it("add variable", () => {
|
||||||
when.click("global-state-add-variable");
|
when.click("global-state-add-variable");
|
||||||
|
when.wait(100);
|
||||||
then(get.styleFromLocalStorage()).shouldDeepNestedInclude({
|
then(get.styleFromLocalStorage()).shouldDeepNestedInclude({
|
||||||
state: { key1: { default: "value" } },
|
state: { key1: { default: "value" } },
|
||||||
});
|
});
|
||||||
@@ -321,6 +344,7 @@ describe("modals", () => {
|
|||||||
when.click("global-state-add-variable");
|
when.click("global-state-add-variable");
|
||||||
when.click("global-state-add-variable");
|
when.click("global-state-add-variable");
|
||||||
when.click("global-state-add-variable");
|
when.click("global-state-add-variable");
|
||||||
|
when.wait(100);
|
||||||
then(get.styleFromLocalStorage()).shouldDeepNestedInclude({
|
then(get.styleFromLocalStorage()).shouldDeepNestedInclude({
|
||||||
state: { key1: { default: "value" }, key2: { default: "value" }, key3: { default: "value" } },
|
state: { key1: { default: "value" }, key2: { default: "value" }, key3: { default: "value" } },
|
||||||
});
|
});
|
||||||
@@ -331,6 +355,7 @@ describe("modals", () => {
|
|||||||
when.click("global-state-add-variable");
|
when.click("global-state-add-variable");
|
||||||
when.click("global-state-add-variable");
|
when.click("global-state-add-variable");
|
||||||
when.click("global-state-remove-variable", 0);
|
when.click("global-state-remove-variable", 0);
|
||||||
|
when.wait(100);
|
||||||
then(get.styleFromLocalStorage()).shouldDeepNestedInclude({
|
then(get.styleFromLocalStorage()).shouldDeepNestedInclude({
|
||||||
state: { key2: { default: "value" }, key3: { default: "value" } },
|
state: { key2: { default: "value" }, key3: { default: "value" } },
|
||||||
});
|
});
|
||||||
@@ -340,6 +365,7 @@ describe("modals", () => {
|
|||||||
when.click("global-state-add-variable");
|
when.click("global-state-add-variable");
|
||||||
when.setValue("global-state-variable-key:0", "mykey");
|
when.setValue("global-state-variable-key:0", "mykey");
|
||||||
when.typeKeys("{enter}");
|
when.typeKeys("{enter}");
|
||||||
|
when.wait(100);
|
||||||
then(get.styleFromLocalStorage()).shouldDeepNestedInclude({
|
then(get.styleFromLocalStorage()).shouldDeepNestedInclude({
|
||||||
state: { mykey: { default: "value" } },
|
state: { mykey: { default: "value" } },
|
||||||
});
|
});
|
||||||
@@ -349,12 +375,28 @@ describe("modals", () => {
|
|||||||
when.click("global-state-add-variable");
|
when.click("global-state-add-variable");
|
||||||
when.setValue("global-state-variable-value:0", "myvalue");
|
when.setValue("global-state-variable-value:0", "myvalue");
|
||||||
when.typeKeys("{enter}");
|
when.typeKeys("{enter}");
|
||||||
|
when.wait(100);
|
||||||
then(get.styleFromLocalStorage()).shouldDeepNestedInclude({
|
then(get.styleFromLocalStorage()).shouldDeepNestedInclude({
|
||||||
state: { key1: { default: "myvalue" } },
|
state: { key1: { default: "myvalue" } },
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe("error panel", () => {
|
||||||
|
it("not visible when no errors", () => {
|
||||||
|
then(get.element("maputnik-message-panel-error")).shouldNotExist();
|
||||||
|
});
|
||||||
|
|
||||||
|
it("visible on style error", () => {
|
||||||
|
when.modal.open();
|
||||||
|
when.modal.fillLayers({
|
||||||
|
type: "circle",
|
||||||
|
layer: "invalid",
|
||||||
|
});
|
||||||
|
then(get.element(".maputnik-message-panel-error")).shouldBeVisible();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
describe("Handle localStorage QuotaExceededError", () => {
|
describe("Handle localStorage QuotaExceededError", () => {
|
||||||
it("handles quota exceeded error when opening style from URL", () => {
|
it("handles quota exceeded error when opening style from URL", () => {
|
||||||
// Clear localStorage to start fresh
|
// Clear localStorage to start fresh
|
||||||
|
|||||||
Generated
+1239
-1032
File diff suppressed because it is too large
Load Diff
+4
-5
@@ -37,7 +37,7 @@
|
|||||||
"buffer": "^6.0.3",
|
"buffer": "^6.0.3",
|
||||||
"classnames": "^2.5.1",
|
"classnames": "^2.5.1",
|
||||||
"codemirror": "^6.0.2",
|
"codemirror": "^6.0.2",
|
||||||
"color": "^5.0.0",
|
"color": "^5.0.2",
|
||||||
"detect-browser": "^5.3.0",
|
"detect-browser": "^5.3.0",
|
||||||
"downshift": "^9.0.10",
|
"downshift": "^9.0.10",
|
||||||
"events": "^3.3.0",
|
"events": "^3.3.0",
|
||||||
@@ -67,9 +67,9 @@
|
|||||||
"react-collapse": "^5.1.1",
|
"react-collapse": "^5.1.1",
|
||||||
"react-color": "^2.19.3",
|
"react-color": "^2.19.3",
|
||||||
"react-dom": "^19.1.1",
|
"react-dom": "^19.1.1",
|
||||||
"react-file-reader-input": "^2.0.0",
|
|
||||||
"react-i18next": "^15.7.3",
|
"react-i18next": "^15.7.3",
|
||||||
"react-icons": "^5.5.0",
|
"react-icons": "^5.5.0",
|
||||||
|
"react-markdown": "^10.1.0",
|
||||||
"reconnecting-websocket": "^4.4.0",
|
"reconnecting-websocket": "^4.4.0",
|
||||||
"slugify": "^1.6.6",
|
"slugify": "^1.6.6",
|
||||||
"string-hash": "^1.1.3",
|
"string-hash": "^1.1.3",
|
||||||
@@ -112,13 +112,12 @@
|
|||||||
"@types/lodash.isequal": "^4.5.8",
|
"@types/lodash.isequal": "^4.5.8",
|
||||||
"@types/lodash.throttle": "^4.1.9",
|
"@types/lodash.throttle": "^4.1.9",
|
||||||
"@types/randomcolor": "^0.5.9",
|
"@types/randomcolor": "^0.5.9",
|
||||||
"@types/react": "^19.1.12",
|
"@types/react": "^19.1.13",
|
||||||
"@types/react-aria-menubutton": "^6.2.14",
|
"@types/react-aria-menubutton": "^6.2.14",
|
||||||
"@types/react-aria-modal": "^5.0.0",
|
"@types/react-aria-modal": "^5.0.0",
|
||||||
"@types/react-collapse": "^5.0.4",
|
"@types/react-collapse": "^5.0.4",
|
||||||
"@types/react-color": "^3.0.13",
|
"@types/react-color": "^3.0.13",
|
||||||
"@types/react-dom": "^19.1.9",
|
"@types/react-dom": "^19.1.9",
|
||||||
"@types/react-file-reader-input": "^2.0.4",
|
|
||||||
"@types/string-hash": "^1.1.3",
|
"@types/string-hash": "^1.1.3",
|
||||||
"@types/uuid": "^10.0.0",
|
"@types/uuid": "^10.0.0",
|
||||||
"@types/wicg-file-system-access": "^2023.10.6",
|
"@types/wicg-file-system-access": "^2023.10.6",
|
||||||
@@ -140,7 +139,7 @@
|
|||||||
"stylelint-config-recommended-scss": "^16.0.1",
|
"stylelint-config-recommended-scss": "^16.0.1",
|
||||||
"stylelint-scss": "^6.12.1",
|
"stylelint-scss": "^6.12.1",
|
||||||
"typescript": "^5.9.2",
|
"typescript": "^5.9.2",
|
||||||
"typescript-eslint": "^8.43.0",
|
"typescript-eslint": "^8.44.0",
|
||||||
"uuid": "^13.0.0",
|
"uuid": "^13.0.0",
|
||||||
"vite": "^7.1.5",
|
"vite": "^7.1.5",
|
||||||
"vite-plugin-istanbul": "^7.1.0"
|
"vite-plugin-istanbul": "^7.1.0"
|
||||||
|
|||||||
+3
-15
@@ -36,7 +36,7 @@ import LayerWatcher from "../libs/layerwatcher";
|
|||||||
import tokens from "../config/tokens.json";
|
import tokens from "../config/tokens.json";
|
||||||
import isEqual from "lodash.isequal";
|
import isEqual from "lodash.isequal";
|
||||||
import { type MapOptions } from "maplibre-gl";
|
import { type MapOptions } from "maplibre-gl";
|
||||||
import { type OnStyleChangedOpts, type StyleSpecificationWithId } from "../libs/definitions";
|
import { type MappedError, type OnStyleChangedOpts, type StyleSpecificationWithId } from "../libs/definitions";
|
||||||
|
|
||||||
// Buffer must be defined globally for @maplibre/maplibre-gl-style-spec validate() function to succeed.
|
// Buffer must be defined globally for @maplibre/maplibre-gl-style-spec validate() function to succeed.
|
||||||
window.Buffer = buffer.Buffer;
|
window.Buffer = buffer.Buffer;
|
||||||
@@ -82,20 +82,8 @@ function updateRootSpec(spec: any, fieldName: string, newValues: any) {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
type MappedErrors = {
|
|
||||||
message: string
|
|
||||||
parsed?: {
|
|
||||||
type: string
|
|
||||||
data: {
|
|
||||||
index: number
|
|
||||||
key: string
|
|
||||||
message: string
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
type AppState = {
|
type AppState = {
|
||||||
errors: MappedErrors[],
|
errors: MappedError[],
|
||||||
infos: string[],
|
infos: string[],
|
||||||
mapStyle: StyleSpecificationWithId,
|
mapStyle: StyleSpecificationWithId,
|
||||||
dirtyMapStyle?: StyleSpecification,
|
dirtyMapStyle?: StyleSpecification,
|
||||||
@@ -383,7 +371,7 @@ export default class App extends React.Component<any, AppState> {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
const mappedErrors = layerErrors.concat(errors).map(error => {
|
const mappedErrors: MappedError[] = layerErrors.concat(errors).map(error => {
|
||||||
// Special case: Duplicate layer id
|
// Special case: Duplicate layer id
|
||||||
const dupMatch = error.message.match(/layers\[(\d+)\]: (duplicate layer id "?(.*)"?, previously used)/);
|
const dupMatch = error.message.match(/layers\[(\d+)\]: (duplicate layer id "?(.*)"?, previously used)/);
|
||||||
if (dupMatch) {
|
if (dupMatch) {
|
||||||
|
|||||||
@@ -2,9 +2,10 @@ import React from "react";
|
|||||||
import {formatLayerId} from "../libs/format";
|
import {formatLayerId} from "../libs/format";
|
||||||
import {type LayerSpecification, type StyleSpecification} from "maplibre-gl";
|
import {type LayerSpecification, type StyleSpecification} from "maplibre-gl";
|
||||||
import { Trans, type WithTranslation, withTranslation } from "react-i18next";
|
import { Trans, type WithTranslation, withTranslation } from "react-i18next";
|
||||||
|
import { type MappedError } from "../libs/definitions";
|
||||||
|
|
||||||
type AppMessagePanelInternalProps = {
|
type AppMessagePanelInternalProps = {
|
||||||
errors?: unknown[]
|
errors?: MappedError[]
|
||||||
infos?: string[]
|
infos?: string[]
|
||||||
mapStyle?: StyleSpecification
|
mapStyle?: StyleSpecification
|
||||||
onLayerSelect?(index: number): void;
|
onLayerSelect?(index: number): void;
|
||||||
@@ -19,7 +20,7 @@ class AppMessagePanelInternal extends React.Component<AppMessagePanelInternalPro
|
|||||||
|
|
||||||
render() {
|
render() {
|
||||||
const {t, selectedLayerIndex} = this.props;
|
const {t, selectedLayerIndex} = this.props;
|
||||||
const errors = this.props.errors?.map((error: any, idx) => {
|
const errors = this.props.errors?.map((error, idx) => {
|
||||||
let content;
|
let content;
|
||||||
if (error.parsed && error.parsed.type === "layer") {
|
if (error.parsed && error.parsed.type === "layer") {
|
||||||
const {parsed} = error;
|
const {parsed} = error;
|
||||||
|
|||||||
@@ -1,13 +1,13 @@
|
|||||||
import React, {type PropsWithChildren, type SyntheticEvent} from "react";
|
import React, {type CSSProperties, type PropsWithChildren, type SyntheticEvent} from "react";
|
||||||
import classnames from "classnames";
|
import classnames from "classnames";
|
||||||
import FieldDocLabel from "./FieldDocLabel";
|
import FieldDocLabel from "./FieldDocLabel";
|
||||||
import Doc from "./Doc";
|
import Doc from "./Doc";
|
||||||
|
|
||||||
type BlockProps = PropsWithChildren & {
|
export type BlockProps = PropsWithChildren & {
|
||||||
"data-wd-key"?: string
|
"data-wd-key"?: string
|
||||||
label?: string
|
label?: string
|
||||||
action?: React.ReactElement
|
action?: React.ReactElement
|
||||||
style?: object
|
style?: CSSProperties
|
||||||
onChange?(...args: unknown[]): unknown
|
onChange?(...args: unknown[]): unknown
|
||||||
fieldSpec?: object
|
fieldSpec?: object
|
||||||
wideMode?: boolean
|
wideMode?: boolean
|
||||||
@@ -65,7 +65,8 @@ export default class Block extends React.Component<BlockProps, BlockState> {
|
|||||||
className={classnames({
|
className={classnames({
|
||||||
"maputnik-input-block": true,
|
"maputnik-input-block": true,
|
||||||
"maputnik-input-block--wide": this.props.wideMode,
|
"maputnik-input-block--wide": this.props.wideMode,
|
||||||
"maputnik-action-block": this.props.action
|
"maputnik-action-block": this.props.action,
|
||||||
|
"maputnik-input-block--error": this.props.error
|
||||||
})}
|
})}
|
||||||
onClick={this.onLabelClick}
|
onClick={this.onLabelClick}
|
||||||
>
|
>
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
import React from "react";
|
import React from "react";
|
||||||
|
import Markdown from "react-markdown";
|
||||||
|
|
||||||
const headers = {
|
const headers = {
|
||||||
js: "JS",
|
js: "JS",
|
||||||
@@ -48,7 +49,11 @@ export default class Doc extends React.Component<DocProps> {
|
|||||||
<>
|
<>
|
||||||
{doc &&
|
{doc &&
|
||||||
<div className="SpecDoc">
|
<div className="SpecDoc">
|
||||||
<div className="SpecDoc__doc" data-wd-key='spec-field-doc'>{doc}</div>
|
<div className="SpecDoc__doc" data-wd-key='spec-field-doc'>
|
||||||
|
<Markdown components={{
|
||||||
|
a: ({node: _node, href, children, ...props}) => <a href={href} target="_blank" {...props}>{children}</a>,
|
||||||
|
}}>{doc}</Markdown>
|
||||||
|
</div>
|
||||||
{renderValues &&
|
{renderValues &&
|
||||||
<ul className="SpecDoc__values">
|
<ul className="SpecDoc__values">
|
||||||
{Object.entries(values).map(([key, value]) => {
|
{Object.entries(values).map(([key, value]) => {
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ import ZoomProperty from "./_ZoomProperty";
|
|||||||
import ExpressionProperty from "./_ExpressionProperty";
|
import ExpressionProperty from "./_ExpressionProperty";
|
||||||
import {function as styleFunction} from "@maplibre/maplibre-gl-style-spec";
|
import {function as styleFunction} from "@maplibre/maplibre-gl-style-spec";
|
||||||
import {findDefaultFromSpec} from "../libs/spec-helper";
|
import {findDefaultFromSpec} from "../libs/spec-helper";
|
||||||
|
import { type MappedLayerErrors } from "../libs/definitions";
|
||||||
|
|
||||||
|
|
||||||
function isLiteralExpression(value: any) {
|
function isLiteralExpression(value: any) {
|
||||||
@@ -119,7 +120,7 @@ type FieldFunctionProps = {
|
|||||||
fieldName: string
|
fieldName: string
|
||||||
fieldType: string
|
fieldType: string
|
||||||
fieldSpec: any
|
fieldSpec: any
|
||||||
errors?: {[key: string]: {message: string}}
|
errors?: MappedLayerErrors
|
||||||
value?: any
|
value?: any
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import Block from "./Block";
|
import Block, { type BlockProps } from "./Block";
|
||||||
import InputSpec, { type FieldSpecType, type InputSpecProps } from "./InputSpec";
|
import InputSpec, { type FieldSpecType, type InputSpecProps } from "./InputSpec";
|
||||||
import Fieldset from "./Fieldset";
|
import Fieldset, { type FieldsetProps } from "./Fieldset";
|
||||||
|
|
||||||
function getElementFromType(fieldSpec: { type?: FieldSpecType, values?: unknown[] }): typeof Fieldset | typeof Block {
|
function getElementFromType(fieldSpec: { type?: FieldSpecType, values?: unknown[] }): typeof Fieldset | typeof Block {
|
||||||
switch(fieldSpec.type) {
|
switch(fieldSpec.type) {
|
||||||
@@ -34,15 +34,13 @@ function getElementFromType(fieldSpec: { type?: FieldSpecType, values?: unknown[
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export type FieldSpecProps = InputSpecProps & {
|
export type FieldSpecProps = InputSpecProps & BlockProps & FieldsetProps;
|
||||||
name?: string
|
|
||||||
};
|
|
||||||
|
|
||||||
const FieldSpec: React.FC<FieldSpecProps> = (props) => {
|
const FieldSpec: React.FC<FieldSpecProps> = (props) => {
|
||||||
const TypeBlock = getElementFromType(props.fieldSpec!);
|
const TypeBlock = getElementFromType(props.fieldSpec!);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<TypeBlock label={props.label} action={props.action} fieldSpec={props.fieldSpec}>
|
<TypeBlock label={props.label} action={props.action} fieldSpec={props.fieldSpec} error={props.error}>
|
||||||
<InputSpec {...props} />
|
<InputSpec {...props} />
|
||||||
</TypeBlock>
|
</TypeBlock>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -1,12 +1,14 @@
|
|||||||
import React, { type PropsWithChildren, type ReactElement } from "react";
|
import React, { type PropsWithChildren, type ReactElement } from "react";
|
||||||
|
import classnames from "classnames";
|
||||||
import FieldDocLabel from "./FieldDocLabel";
|
import FieldDocLabel from "./FieldDocLabel";
|
||||||
import Doc from "./Doc";
|
import Doc from "./Doc";
|
||||||
import generateUniqueId from "../libs/document-uid";
|
import generateUniqueId from "../libs/document-uid";
|
||||||
|
|
||||||
type FieldsetProps = PropsWithChildren & {
|
export type FieldsetProps = PropsWithChildren & {
|
||||||
label?: string,
|
label?: string,
|
||||||
fieldSpec?: { doc?: string },
|
fieldSpec?: { doc?: string },
|
||||||
action?: ReactElement,
|
action?: ReactElement,
|
||||||
|
error?: {message: string}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
@@ -30,7 +32,10 @@ const Fieldset: React.FC<FieldsetProps> = (props) => {
|
|||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
{!props.fieldSpec && (
|
{!props.fieldSpec && (
|
||||||
<div className="maputnik-input-block-label">
|
<div className={classnames({
|
||||||
|
"maputnik-input-block-label": true,
|
||||||
|
"maputnik-input-block--error": props.error
|
||||||
|
})}>
|
||||||
{props.label}
|
{props.label}
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|||||||
@@ -14,7 +14,7 @@ import InputButton from "./InputButton";
|
|||||||
import Doc from "./Doc";
|
import Doc from "./Doc";
|
||||||
import ExpressionProperty from "./_ExpressionProperty";
|
import ExpressionProperty from "./_ExpressionProperty";
|
||||||
import { type WithTranslation, withTranslation } from "react-i18next";
|
import { type WithTranslation, withTranslation } from "react-i18next";
|
||||||
import type { StyleSpecificationWithId } from "../libs/definitions";
|
import type { MappedLayerErrors, StyleSpecificationWithId } from "../libs/definitions";
|
||||||
|
|
||||||
|
|
||||||
function combiningFilter(props: FilterEditorInternalProps): LegacyFilterSpecification | ExpressionSpecification {
|
function combiningFilter(props: FilterEditorInternalProps): LegacyFilterSpecification | ExpressionSpecification {
|
||||||
@@ -95,7 +95,7 @@ type FilterEditorInternalProps = {
|
|||||||
/** Properties of the vector layer and the available fields */
|
/** Properties of the vector layer and the available fields */
|
||||||
properties?: {[key:string]: any}
|
properties?: {[key:string]: any}
|
||||||
filter?: any[]
|
filter?: any[]
|
||||||
errors?: {[key:string]: any}
|
errors?: MappedLayerErrors
|
||||||
onChange(value: LegacyFilterSpecification | ExpressionSpecification): unknown
|
onChange(value: LegacyFilterSpecification | ExpressionSpecification): unknown
|
||||||
} & WithTranslation;
|
} & WithTranslation;
|
||||||
|
|
||||||
|
|||||||
@@ -31,7 +31,6 @@ export type InputSpecProps = {
|
|||||||
/** Override the style of the field */
|
/** Override the style of the field */
|
||||||
style?: object
|
style?: object
|
||||||
"aria-label"?: string
|
"aria-label"?: string
|
||||||
error?: unknown[]
|
|
||||||
label?: string
|
label?: string
|
||||||
action?: ReactElement
|
action?: ReactElement
|
||||||
};
|
};
|
||||||
@@ -43,7 +42,6 @@ export default class InputSpec extends React.Component<InputSpecProps> {
|
|||||||
|
|
||||||
childNodes() {
|
childNodes() {
|
||||||
const commonProps = {
|
const commonProps = {
|
||||||
error: this.props.error,
|
|
||||||
fieldSpec: this.props.fieldSpec,
|
fieldSpec: this.props.fieldSpec,
|
||||||
label: this.props.label,
|
label: this.props.label,
|
||||||
action: this.props.action,
|
action: this.props.action,
|
||||||
|
|||||||
@@ -22,7 +22,7 @@ import {formatLayerId} from "../libs/format";
|
|||||||
import { type WithTranslation, withTranslation } from "react-i18next";
|
import { type WithTranslation, withTranslation } from "react-i18next";
|
||||||
import { type TFunction } from "i18next";
|
import { type TFunction } from "i18next";
|
||||||
import { NON_SOURCE_LAYERS } from "../libs/non-source-layers";
|
import { NON_SOURCE_LAYERS } from "../libs/non-source-layers";
|
||||||
import { type OnMoveLayerCallback } from "../libs/definitions";
|
import { type MappedError, type MappedLayerErrors, type OnMoveLayerCallback } from "../libs/definitions";
|
||||||
|
|
||||||
type MaputnikLayoutGroup = {
|
type MaputnikLayoutGroup = {
|
||||||
id: string;
|
id: string;
|
||||||
@@ -128,7 +128,7 @@ type LayerEditorInternalProps = {
|
|||||||
isFirstLayer?: boolean
|
isFirstLayer?: boolean
|
||||||
isLastLayer?: boolean
|
isLastLayer?: boolean
|
||||||
layerIndex: number
|
layerIndex: number
|
||||||
errors?: any[]
|
errors?: MappedError[]
|
||||||
} & WithTranslation;
|
} & WithTranslation;
|
||||||
|
|
||||||
type LayerEditorState = {
|
type LayerEditorState = {
|
||||||
@@ -193,7 +193,7 @@ class LayerEditorInternal extends React.Component<LayerEditorInternalProps, Laye
|
|||||||
}
|
}
|
||||||
const {errors, layerIndex} = this.props;
|
const {errors, layerIndex} = this.props;
|
||||||
|
|
||||||
const errorData: {[key in LayerSpecification as string]: {message: string}} = {};
|
const errorData: MappedLayerErrors = {};
|
||||||
errors!.forEach(error => {
|
errors!.forEach(error => {
|
||||||
if (
|
if (
|
||||||
error.parsed &&
|
error.parsed &&
|
||||||
|
|||||||
@@ -22,7 +22,7 @@ import type {LayerSpecification, SourceSpecification} from "maplibre-gl";
|
|||||||
import generateUniqueId from "../libs/document-uid";
|
import generateUniqueId from "../libs/document-uid";
|
||||||
import { findClosestCommonPrefix, layerPrefix } from "../libs/layer";
|
import { findClosestCommonPrefix, layerPrefix } from "../libs/layer";
|
||||||
import { type WithTranslation, withTranslation } from "react-i18next";
|
import { type WithTranslation, withTranslation } from "react-i18next";
|
||||||
import { type OnMoveLayerCallback } from "../libs/definitions";
|
import { type MappedError, type OnMoveLayerCallback } from "../libs/definitions";
|
||||||
|
|
||||||
type LayerListContainerProps = {
|
type LayerListContainerProps = {
|
||||||
layers: LayerSpecification[]
|
layers: LayerSpecification[]
|
||||||
@@ -33,7 +33,7 @@ type LayerListContainerProps = {
|
|||||||
onLayerCopy(...args: unknown[]): unknown
|
onLayerCopy(...args: unknown[]): unknown
|
||||||
onLayerVisibilityToggle(...args: unknown[]): unknown
|
onLayerVisibilityToggle(...args: unknown[]): unknown
|
||||||
sources: Record<string, SourceSpecification & {layers: string[]}>;
|
sources: Record<string, SourceSpecification & {layers: string[]}>;
|
||||||
errors: any[]
|
errors: MappedError[]
|
||||||
};
|
};
|
||||||
type LayerListContainerInternalProps = LayerListContainerProps & WithTranslation;
|
type LayerListContainerInternalProps = LayerListContainerProps & WithTranslation;
|
||||||
|
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ import React from "react";
|
|||||||
|
|
||||||
import FieldFunction from "./FieldFunction";
|
import FieldFunction from "./FieldFunction";
|
||||||
import type {LayerSpecification} from "maplibre-gl";
|
import type {LayerSpecification} from "maplibre-gl";
|
||||||
|
import { type MappedLayerErrors } from "../libs/definitions";
|
||||||
|
|
||||||
const iconProperties = ["background-pattern", "fill-pattern", "line-pattern", "fill-extrusion-pattern", "icon-image"];
|
const iconProperties = ["background-pattern", "fill-pattern", "line-pattern", "fill-extrusion-pattern", "icon-image"];
|
||||||
|
|
||||||
@@ -36,7 +37,7 @@ type PropertyGroupProps = {
|
|||||||
groupFields: string[]
|
groupFields: string[]
|
||||||
onChange(...args: unknown[]): unknown
|
onChange(...args: unknown[]): unknown
|
||||||
spec: any
|
spec: any
|
||||||
errors?: {[key: string]: {message: string}}
|
errors?: MappedLayerErrors
|
||||||
};
|
};
|
||||||
|
|
||||||
export default class PropertyGroup extends React.Component<PropertyGroupProps> {
|
export default class PropertyGroup extends React.Component<PropertyGroupProps> {
|
||||||
|
|||||||
@@ -16,6 +16,7 @@ import { type WithTranslation, withTranslation } from "react-i18next";
|
|||||||
|
|
||||||
import labelFromFieldName from "../libs/label-from-field-name";
|
import labelFromFieldName from "../libs/label-from-field-name";
|
||||||
import DeleteStopButton from "./_DeleteStopButton";
|
import DeleteStopButton from "./_DeleteStopButton";
|
||||||
|
import { type MappedLayerErrors } from "../libs/definitions";
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -47,7 +48,7 @@ type DataPropertyInternalProps = {
|
|||||||
fieldType?: string
|
fieldType?: string
|
||||||
fieldSpec?: object
|
fieldSpec?: object
|
||||||
value?: DataPropertyValue
|
value?: DataPropertyValue
|
||||||
errors?: object
|
errors?: MappedLayerErrors
|
||||||
} & WithTranslation;
|
} & WithTranslation;
|
||||||
|
|
||||||
type DataPropertyState = {
|
type DataPropertyState = {
|
||||||
|
|||||||
@@ -7,13 +7,14 @@ import InputButton from "./InputButton";
|
|||||||
import labelFromFieldName from "../libs/label-from-field-name";
|
import labelFromFieldName from "../libs/label-from-field-name";
|
||||||
import FieldJson from "./FieldJson";
|
import FieldJson from "./FieldJson";
|
||||||
import type { StylePropertySpecification } from "maplibre-gl";
|
import type { StylePropertySpecification } from "maplibre-gl";
|
||||||
|
import { type MappedLayerErrors } from "../libs/definitions";
|
||||||
|
|
||||||
|
|
||||||
type ExpressionPropertyInternalProps = {
|
type ExpressionPropertyInternalProps = {
|
||||||
fieldName: string
|
fieldName: string
|
||||||
fieldSpec?: StylePropertySpecification
|
fieldSpec?: StylePropertySpecification
|
||||||
value?: any
|
value?: any
|
||||||
errors?: {[key: string]: {message: string}}
|
errors?: MappedLayerErrors
|
||||||
onDelete?(...args: unknown[]): unknown
|
onDelete?(...args: unknown[]): unknown
|
||||||
onChange(value: object): void
|
onChange(value: object): void
|
||||||
onUndo?(...args: unknown[]): unknown
|
onUndo?(...args: unknown[]): unknown
|
||||||
|
|||||||
@@ -15,6 +15,7 @@ import labelFromFieldName from "../libs/label-from-field-name";
|
|||||||
|
|
||||||
import docUid from "../libs/document-uid";
|
import docUid from "../libs/document-uid";
|
||||||
import sortNumerically from "../libs/sort-numerically";
|
import sortNumerically from "../libs/sort-numerically";
|
||||||
|
import { type MappedLayerErrors } from "../libs/definitions";
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -59,7 +60,7 @@ type ZoomPropertyInternalProps = {
|
|||||||
"property-type"?: string
|
"property-type"?: string
|
||||||
"function-type"?: string
|
"function-type"?: string
|
||||||
}
|
}
|
||||||
errors?: object
|
errors?: MappedLayerErrors
|
||||||
value?: ZoomWithStops
|
value?: ZoomWithStops
|
||||||
} & WithTranslation;
|
} & WithTranslation;
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
import React, { type FormEvent } from "react";
|
import React, { type FormEvent } from "react";
|
||||||
import {MdFileUpload} from "react-icons/md";
|
import {MdFileUpload} from "react-icons/md";
|
||||||
import {MdAddCircleOutline} from "react-icons/md";
|
import {MdAddCircleOutline} from "react-icons/md";
|
||||||
import FileReaderInput, { type Result } from "react-file-reader-input";
|
|
||||||
import { Trans, type WithTranslation, withTranslation } from "react-i18next";
|
import { Trans, type WithTranslation, withTranslation } from "react-i18next";
|
||||||
|
|
||||||
import ModalLoading from "./ModalLoading";
|
import ModalLoading from "./ModalLoading";
|
||||||
@@ -171,8 +170,10 @@ class ModalOpenInternal extends React.Component<ModalOpenInternalProps, ModalOpe
|
|||||||
|
|
||||||
// it is not guaranteed that the File System Access API is available on all
|
// it is not guaranteed that the File System Access API is available on all
|
||||||
// browsers. If the function is not available, a fallback behavior is used.
|
// browsers. If the function is not available, a fallback behavior is used.
|
||||||
onFileChanged = async (_: any, files: Result[]) => {
|
onFileChanged = (files: FileList | null) => {
|
||||||
const [, file] = files[0];
|
if (!files) return;
|
||||||
|
if (files.length === 0) return;
|
||||||
|
const file = files[0];
|
||||||
const reader = new FileReader();
|
const reader = new FileReader();
|
||||||
this.clearError();
|
this.clearError();
|
||||||
|
|
||||||
@@ -246,13 +247,15 @@ class ModalOpenInternal extends React.Component<ModalOpenInternalProps, ModalOpe
|
|||||||
<div>
|
<div>
|
||||||
{typeof window.showOpenFilePicker === "function" ? (
|
{typeof window.showOpenFilePicker === "function" ? (
|
||||||
<InputButton
|
<InputButton
|
||||||
|
data-wd-key="modal:open.file.button"
|
||||||
className="maputnik-big-button"
|
className="maputnik-big-button"
|
||||||
onClick={this.onOpenFile}><MdFileUpload/> {t("Open Style")}
|
onClick={this.onOpenFile}><MdFileUpload/> {t("Open Style")}
|
||||||
</InputButton>
|
</InputButton>
|
||||||
) : (
|
) : (
|
||||||
<FileReaderInput onChange={this.onFileChanged} tabIndex={-1} aria-label={t("Open Style")}>
|
<label>
|
||||||
<InputButton className="maputnik-upload-button"><MdFileUpload /> {t("Open Style")}</InputButton>
|
<a className="maputnik-button maputnik-upload-button" aria-label={t("Open Style")}><MdFileUpload /> {t("Open Style")}</a>
|
||||||
</FileReaderInput>
|
<input data-wd-key="modal:open.file.input" type="file" style={{ display: "none" }} onChange={(e) => this.onFileChanged(e.target.files)} />
|
||||||
|
</label>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import React from "react";
|
import React from "react";
|
||||||
import latest from "@maplibre/maplibre-gl-style-spec/dist/latest.json";
|
import latest from "@maplibre/maplibre-gl-style-spec/dist/latest.json";
|
||||||
import type {LightSpecification, StyleSpecification, TerrainSpecification, TransitionSpecification} from "maplibre-gl";
|
import type {LightSpecification, ProjectionSpecification, StyleSpecification, TerrainSpecification, TransitionSpecification} from "maplibre-gl";
|
||||||
import { type WithTranslation, withTranslation } from "react-i18next";
|
import { type WithTranslation, withTranslation } from "react-i18next";
|
||||||
|
|
||||||
import FieldArray from "../FieldArray";
|
import FieldArray from "../FieldArray";
|
||||||
@@ -79,6 +79,24 @@ class ModalSettingsInternal extends React.Component<ModalSettingsInternalProps>
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
changeProjectionType(value: any) {
|
||||||
|
const projection = {
|
||||||
|
...this.props.mapStyle.projection,
|
||||||
|
} as ProjectionSpecification;
|
||||||
|
|
||||||
|
if (value === undefined) {
|
||||||
|
delete projection.type;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
projection.type = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.props.onStyleChanged({
|
||||||
|
...this.props.mapStyle,
|
||||||
|
projection,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
changeStyleProperty(property: keyof StyleSpecification | "owner", value: any) {
|
changeStyleProperty(property: keyof StyleSpecification | "owner", value: any) {
|
||||||
const changedStyle = {
|
const changedStyle = {
|
||||||
...this.props.mapStyle,
|
...this.props.mapStyle,
|
||||||
@@ -103,6 +121,7 @@ class ModalSettingsInternal extends React.Component<ModalSettingsInternalProps>
|
|||||||
const light = this.props.mapStyle.light || {};
|
const light = this.props.mapStyle.light || {};
|
||||||
const transition = this.props.mapStyle.transition || {};
|
const transition = this.props.mapStyle.transition || {};
|
||||||
const terrain = this.props.mapStyle.terrain || {} as TerrainSpecification;
|
const terrain = this.props.mapStyle.terrain || {} as TerrainSpecification;
|
||||||
|
const projection = this.props.mapStyle.projection || {} as ProjectionSpecification;
|
||||||
|
|
||||||
return <Modal
|
return <Modal
|
||||||
data-wd-key="modal:settings"
|
data-wd-key="modal:settings"
|
||||||
@@ -116,21 +135,21 @@ class ModalSettingsInternal extends React.Component<ModalSettingsInternalProps>
|
|||||||
fieldSpec={latest.$root.name}
|
fieldSpec={latest.$root.name}
|
||||||
data-wd-key="modal:settings.name"
|
data-wd-key="modal:settings.name"
|
||||||
value={this.props.mapStyle.name}
|
value={this.props.mapStyle.name}
|
||||||
onChange={this.changeStyleProperty.bind(this, "name")}
|
onChange={(value) => this.changeStyleProperty("name", value)}
|
||||||
/>
|
/>
|
||||||
<FieldString
|
<FieldString
|
||||||
label={t("Owner")}
|
label={t("Owner")}
|
||||||
fieldSpec={{doc: t("Owner ID of the style. Used by Mapbox or future style APIs.")}}
|
fieldSpec={{doc: t("Owner ID of the style. Used by Mapbox or future style APIs.")}}
|
||||||
data-wd-key="modal:settings.owner"
|
data-wd-key="modal:settings.owner"
|
||||||
value={(this.props.mapStyle as any).owner}
|
value={(this.props.mapStyle as any).owner}
|
||||||
onChange={this.changeStyleProperty.bind(this, "owner")}
|
onChange={(value) => this.changeStyleProperty("owner", value)}
|
||||||
/>
|
/>
|
||||||
<FieldUrl
|
<FieldUrl
|
||||||
fieldSpec={latest.$root.sprite}
|
fieldSpec={latest.$root.sprite}
|
||||||
label={t("Sprite URL")}
|
label={t("Sprite URL")}
|
||||||
data-wd-key="modal:settings.sprite"
|
data-wd-key="modal:settings.sprite"
|
||||||
value={this.props.mapStyle.sprite as string}
|
value={this.props.mapStyle.sprite as string}
|
||||||
onChange={this.changeStyleProperty.bind(this, "sprite")}
|
onChange={(value) => this.changeStyleProperty("sprite", value)}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<FieldUrl
|
<FieldUrl
|
||||||
@@ -138,7 +157,7 @@ class ModalSettingsInternal extends React.Component<ModalSettingsInternalProps>
|
|||||||
fieldSpec={latest.$root.glyphs}
|
fieldSpec={latest.$root.glyphs}
|
||||||
data-wd-key="modal:settings.glyphs"
|
data-wd-key="modal:settings.glyphs"
|
||||||
value={this.props.mapStyle.glyphs as string}
|
value={this.props.mapStyle.glyphs as string}
|
||||||
onChange={this.changeStyleProperty.bind(this, "glyphs")}
|
onChange={(value) => this.changeStyleProperty("glyphs", value)}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<FieldString
|
<FieldString
|
||||||
@@ -146,7 +165,7 @@ class ModalSettingsInternal extends React.Component<ModalSettingsInternalProps>
|
|||||||
fieldSpec={fsa.maputnik.maptiler_access_token}
|
fieldSpec={fsa.maputnik.maptiler_access_token}
|
||||||
data-wd-key="modal:settings.maputnik:openmaptiles_access_token"
|
data-wd-key="modal:settings.maputnik:openmaptiles_access_token"
|
||||||
value={metadata["maputnik:openmaptiles_access_token"]}
|
value={metadata["maputnik:openmaptiles_access_token"]}
|
||||||
onChange={onChangeMetadataProperty.bind(this, "maputnik:openmaptiles_access_token")}
|
onChange={(value) => onChangeMetadataProperty("maputnik:openmaptiles_access_token", value)}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<FieldString
|
<FieldString
|
||||||
@@ -154,7 +173,7 @@ class ModalSettingsInternal extends React.Component<ModalSettingsInternalProps>
|
|||||||
fieldSpec={fsa.maputnik.thunderforest_access_token}
|
fieldSpec={fsa.maputnik.thunderforest_access_token}
|
||||||
data-wd-key="modal:settings.maputnik:thunderforest_access_token"
|
data-wd-key="modal:settings.maputnik:thunderforest_access_token"
|
||||||
value={metadata["maputnik:thunderforest_access_token"]}
|
value={metadata["maputnik:thunderforest_access_token"]}
|
||||||
onChange={onChangeMetadataProperty.bind(this, "maputnik:thunderforest_access_token")}
|
onChange={(value) => onChangeMetadataProperty("maputnik:thunderforest_access_token", value)}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<FieldString
|
<FieldString
|
||||||
@@ -162,7 +181,7 @@ class ModalSettingsInternal extends React.Component<ModalSettingsInternalProps>
|
|||||||
fieldSpec={fsa.maputnik.stadia_access_token}
|
fieldSpec={fsa.maputnik.stadia_access_token}
|
||||||
data-wd-key="modal:settings.maputnik:stadia_access_token"
|
data-wd-key="modal:settings.maputnik:stadia_access_token"
|
||||||
value={metadata["maputnik:stadia_access_token"]}
|
value={metadata["maputnik:stadia_access_token"]}
|
||||||
onChange={onChangeMetadataProperty.bind(this, "maputnik:stadia_access_token")}
|
onChange={(value) => onChangeMetadataProperty("maputnik:stadia_access_token", value)}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<FieldString
|
<FieldString
|
||||||
@@ -170,7 +189,7 @@ class ModalSettingsInternal extends React.Component<ModalSettingsInternalProps>
|
|||||||
fieldSpec={fsa.maputnik.locationiq_access_token}
|
fieldSpec={fsa.maputnik.locationiq_access_token}
|
||||||
data-wd-key="modal:settings.maputnik:locationiq_access_token"
|
data-wd-key="modal:settings.maputnik:locationiq_access_token"
|
||||||
value={metadata["maputnik:locationiq_access_token"]}
|
value={metadata["maputnik:locationiq_access_token"]}
|
||||||
onChange={onChangeMetadataProperty.bind(this, "maputnik:locationiq_access_token")}
|
onChange={(value) => onChangeMetadataProperty("maputnik:locationiq_access_token", value)}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<FieldArray
|
<FieldArray
|
||||||
@@ -180,7 +199,7 @@ class ModalSettingsInternal extends React.Component<ModalSettingsInternalProps>
|
|||||||
type="number"
|
type="number"
|
||||||
value={mapStyle.center || []}
|
value={mapStyle.center || []}
|
||||||
default={[0, 0]}
|
default={[0, 0]}
|
||||||
onChange={this.changeStyleProperty.bind(this, "center")}
|
onChange={(value) => this.changeStyleProperty("center", value)}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<FieldNumber
|
<FieldNumber
|
||||||
@@ -188,7 +207,7 @@ class ModalSettingsInternal extends React.Component<ModalSettingsInternalProps>
|
|||||||
fieldSpec={latest.$root.zoom}
|
fieldSpec={latest.$root.zoom}
|
||||||
value={mapStyle.zoom}
|
value={mapStyle.zoom}
|
||||||
default={0}
|
default={0}
|
||||||
onChange={this.changeStyleProperty.bind(this, "zoom")}
|
onChange={(value) => this.changeStyleProperty("zoom", value)}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<FieldNumber
|
<FieldNumber
|
||||||
@@ -196,7 +215,7 @@ class ModalSettingsInternal extends React.Component<ModalSettingsInternalProps>
|
|||||||
fieldSpec={latest.$root.bearing}
|
fieldSpec={latest.$root.bearing}
|
||||||
value={mapStyle.bearing}
|
value={mapStyle.bearing}
|
||||||
default={latest.$root.bearing.default}
|
default={latest.$root.bearing.default}
|
||||||
onChange={this.changeStyleProperty.bind(this, "bearing")}
|
onChange={(value) => this.changeStyleProperty("bearing", value)}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<FieldNumber
|
<FieldNumber
|
||||||
@@ -204,7 +223,7 @@ class ModalSettingsInternal extends React.Component<ModalSettingsInternalProps>
|
|||||||
fieldSpec={latest.$root.pitch}
|
fieldSpec={latest.$root.pitch}
|
||||||
value={mapStyle.pitch}
|
value={mapStyle.pitch}
|
||||||
default={latest.$root.pitch.default}
|
default={latest.$root.pitch.default}
|
||||||
onChange={this.changeStyleProperty.bind(this, "pitch")}
|
onChange={(value) => this.changeStyleProperty("pitch", value)}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<FieldEnum
|
<FieldEnum
|
||||||
@@ -214,7 +233,7 @@ class ModalSettingsInternal extends React.Component<ModalSettingsInternalProps>
|
|||||||
value={light.anchor as string}
|
value={light.anchor as string}
|
||||||
options={Object.keys(latest.light.anchor.values)}
|
options={Object.keys(latest.light.anchor.values)}
|
||||||
default={latest.light.anchor.default}
|
default={latest.light.anchor.default}
|
||||||
onChange={this.changeLightProperty.bind(this, "anchor")}
|
onChange={(value) => this.changeLightProperty("anchor", value)}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<FieldColor
|
<FieldColor
|
||||||
@@ -222,7 +241,7 @@ class ModalSettingsInternal extends React.Component<ModalSettingsInternalProps>
|
|||||||
fieldSpec={latest.light.color}
|
fieldSpec={latest.light.color}
|
||||||
value={light.color as string}
|
value={light.color as string}
|
||||||
default={latest.light.color.default}
|
default={latest.light.color.default}
|
||||||
onChange={this.changeLightProperty.bind(this, "color")}
|
onChange={(value) => this.changeLightProperty("color", value)}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<FieldNumber
|
<FieldNumber
|
||||||
@@ -230,7 +249,7 @@ class ModalSettingsInternal extends React.Component<ModalSettingsInternalProps>
|
|||||||
fieldSpec={latest.light.intensity}
|
fieldSpec={latest.light.intensity}
|
||||||
value={light.intensity as number}
|
value={light.intensity as number}
|
||||||
default={latest.light.intensity.default}
|
default={latest.light.intensity.default}
|
||||||
onChange={this.changeLightProperty.bind(this, "intensity")}
|
onChange={(value) => this.changeLightProperty("intensity", value)}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<FieldArray
|
<FieldArray
|
||||||
@@ -240,7 +259,7 @@ class ModalSettingsInternal extends React.Component<ModalSettingsInternalProps>
|
|||||||
length={latest.light.position.length}
|
length={latest.light.position.length}
|
||||||
value={light.position as number[]}
|
value={light.position as number[]}
|
||||||
default={latest.light.position.default}
|
default={latest.light.position.default}
|
||||||
onChange={this.changeLightProperty.bind(this, "position")}
|
onChange={(value) => this.changeLightProperty("position", value)}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<FieldString
|
<FieldString
|
||||||
@@ -248,7 +267,7 @@ class ModalSettingsInternal extends React.Component<ModalSettingsInternalProps>
|
|||||||
fieldSpec={latest.terrain.source}
|
fieldSpec={latest.terrain.source}
|
||||||
data-wd-key="modal:settings.maputnik:terrain_source"
|
data-wd-key="modal:settings.maputnik:terrain_source"
|
||||||
value={terrain.source}
|
value={terrain.source}
|
||||||
onChange={this.changeTerrainProperty.bind(this, "source")}
|
onChange={(value) => this.changeTerrainProperty("source", value)}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<FieldNumber
|
<FieldNumber
|
||||||
@@ -256,7 +275,7 @@ class ModalSettingsInternal extends React.Component<ModalSettingsInternalProps>
|
|||||||
fieldSpec={latest.terrain.exaggeration}
|
fieldSpec={latest.terrain.exaggeration}
|
||||||
value={terrain.exaggeration}
|
value={terrain.exaggeration}
|
||||||
default={latest.terrain.exaggeration.default}
|
default={latest.terrain.exaggeration.default}
|
||||||
onChange={this.changeTerrainProperty.bind(this, "exaggeration")}
|
onChange={(value) => this.changeTerrainProperty("exaggeration", value)}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<FieldNumber
|
<FieldNumber
|
||||||
@@ -264,7 +283,7 @@ class ModalSettingsInternal extends React.Component<ModalSettingsInternalProps>
|
|||||||
fieldSpec={latest.transition.delay}
|
fieldSpec={latest.transition.delay}
|
||||||
value={transition.delay}
|
value={transition.delay}
|
||||||
default={latest.transition.delay.default}
|
default={latest.transition.delay.default}
|
||||||
onChange={this.changeTransitionProperty.bind(this, "delay")}
|
onChange={(value) => this.changeTransitionProperty("delay", value)}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<FieldNumber
|
<FieldNumber
|
||||||
@@ -272,7 +291,20 @@ class ModalSettingsInternal extends React.Component<ModalSettingsInternalProps>
|
|||||||
fieldSpec={latest.transition.duration}
|
fieldSpec={latest.transition.duration}
|
||||||
value={transition.duration}
|
value={transition.duration}
|
||||||
default={latest.transition.duration.default}
|
default={latest.transition.duration.default}
|
||||||
onChange={this.changeTransitionProperty.bind(this, "duration")}
|
onChange={(value) => this.changeTransitionProperty("duration", value)}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<FieldSelect
|
||||||
|
label={t("Projection")}
|
||||||
|
data-wd-key="modal:settings.projection"
|
||||||
|
options={[
|
||||||
|
["", "Undefined"],
|
||||||
|
["mercator", "Mercator"],
|
||||||
|
["globe", "Globe"],
|
||||||
|
["vertical-perspective", "Vertical Perspective"]
|
||||||
|
]}
|
||||||
|
value={projection?.type?.toString() || ""}
|
||||||
|
onChange={(value) => this.changeProjectionType(value)}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<FieldSelect
|
<FieldSelect
|
||||||
@@ -284,7 +316,7 @@ class ModalSettingsInternal extends React.Component<ModalSettingsInternalProps>
|
|||||||
["ol", t("Open Layers (experimental)")],
|
["ol", t("Open Layers (experimental)")],
|
||||||
]}
|
]}
|
||||||
value={metadata["maputnik:renderer"] || "mlgljs"}
|
value={metadata["maputnik:renderer"] || "mlgljs"}
|
||||||
onChange={onChangeMetadataProperty.bind(this, "maputnik:renderer")}
|
onChange={(value) => onChangeMetadataProperty("maputnik:renderer", value)}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</Modal>;
|
</Modal>;
|
||||||
|
|||||||
Vendored
+16
@@ -16,3 +16,19 @@ export interface IStyleStore {
|
|||||||
getLatestStyle(): Promise<StyleSpecificationWithId>;
|
getLatestStyle(): Promise<StyleSpecificationWithId>;
|
||||||
save(mapStyle: StyleSpecificationWithId): StyleSpecificationWithId;
|
save(mapStyle: StyleSpecificationWithId): StyleSpecificationWithId;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export type MappedError = {
|
||||||
|
message: string
|
||||||
|
parsed?: {
|
||||||
|
type: "layer"
|
||||||
|
data: {
|
||||||
|
index: number
|
||||||
|
key: string
|
||||||
|
message: string
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
export type MappedLayerErrors = {
|
||||||
|
[key in LayerSpecification as string]: {message: string}
|
||||||
|
};
|
||||||
|
|||||||
@@ -290,6 +290,12 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.maputnik-input-block--error {
|
||||||
|
.maputnik-input-block-label {
|
||||||
|
color: vars.$color-red;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
.maputnik-expr-infobox {
|
.maputnik-expr-infobox {
|
||||||
font-size: vars.$font-size-6;
|
font-size: vars.$font-size-6;
|
||||||
background: vars.$color-midgray;
|
background: vars.$color-midgray;
|
||||||
|
|||||||
@@ -252,7 +252,7 @@
|
|||||||
margin-top: vars.$margin-3;
|
margin-top: vars.$margin-3;
|
||||||
}
|
}
|
||||||
|
|
||||||
.SpecDoc__values code {
|
.SpecDoc__values code, .SpecDoc__doc code {
|
||||||
background: vars.$color-midgray;
|
background: vars.$color-midgray;
|
||||||
padding: 0.1em 0.3em;
|
padding: 0.1em 0.3em;
|
||||||
border-radius: 2px;
|
border-radius: 2px;
|
||||||
|
|||||||
@@ -44,7 +44,7 @@
|
|||||||
position: fixed;
|
position: fixed;
|
||||||
bottom: 0;
|
bottom: 0;
|
||||||
right: 0;
|
right: 0;
|
||||||
z-index: 1;
|
z-index: 10;
|
||||||
width: vars.$layout-map-width;
|
width: vars.$layout-map-width;
|
||||||
background-color: vars.$color-black;
|
background-color: vars.$color-black;
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user