Compare commits

...

3 Commits

Author SHA1 Message Date
Harel M 5312d61598 Add globe support in Maputnik UI (#1379)
## Launch Checklist

Add a small drop down to select mercator or globe.
This isn't a fully covered field as one can set an expression there, but
I believe this is good enough for most cases.

Before:
<img width="645" height="254" alt="image"
src="https://github.com/user-attachments/assets/19a7ec50-a0bb-4ea3-b9fc-3abc5572c47e"
/>
After:
<img width="770" height="462" alt="image"
src="https://github.com/user-attachments/assets/f4774020-1cc8-45fe-88f9-f77ad7c53140"
/>


 - [x] Briefly describe the changes in this PR.
- [x] Include before/after visuals or gifs if this PR includes visual
changes.
 - [x] Write tests for all new functionality.
 - [x] Add an entry to `CHANGELOG.md` under the `## main` section.

---------

Co-authored-by: Birk Skyum <birk.skyum@pm.me>
Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
2025-09-14 11:48:29 +02:00
Harel M 56cdfd23df Add react-markdown to better show the docs (#1378)
## Launch Checklist

This improves how the docs are being presented, since we added some
markdown in the docs it is better to have full support for this here as
well

Before:
<img width="645" height="254" alt="image"
src="https://github.com/user-attachments/assets/ce099ab9-eabd-4721-9550-5ea251439d93"
/>

After:
<img width="645" height="254" alt="image"
src="https://github.com/user-attachments/assets/884d1d69-4238-412e-b620-f9c0640723ca"
/>

The table below that is also stretched is fixes in the following PR:

https://github.com/maplibre/maputnik/pull/1377/files#diff-8d22a87c6893aa31e22f1db0804e1fe93534f38d9fb4433e71cd3878f8daa954

 - [x] Briefly describe the changes in this PR.
- [x] Include before/after visuals or gifs if this PR includes visual
changes.
 - [x] Write tests for all new functionality.
 - [x] Add an entry to `CHANGELOG.md` under the `## main` section.
2025-09-14 12:12:21 +03:00
Harel M 69143ea5d6 Add global state modal (#1377)
## Launch Checklist

This adds the ability to edit the global state.
I think it deserves a modal of its own since I don't think it should be
part of other modals...
Here are some images:
<img width="1274" height="254" alt="image"
src="https://github.com/user-attachments/assets/4b6f2564-6c71-47da-9f8c-3bd2b97e1163"
/>

Initial dialog with no variable:
<img width="640" height="254" alt="image"
src="https://github.com/user-attachments/assets/b813b540-cae9-4c80-b2c0-4d965c022cb8"
/>
After you click add a few times:
<img width="640" height="254" alt="image"
src="https://github.com/user-attachments/assets/125cb978-90dc-4047-9694-b0ffc6eaa469"
/>

The state is updated as you change thing in the dialog.
I didn't complicated it to select the type of the variable, but this can
be added later of if there's a requirement to do so, I meant to keep it
simple for now.

 - [x] Briefly describe the changes in this PR.
- [x] Include before/after visuals or gifs if this PR includes visual
changes.
 - [x] Write tests for all new functionality.
 - [x] Add an entry to `CHANGELOG.md` under the `## main` section.

---------

Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
2025-09-14 11:13:52 +03:00
18 changed files with 1524 additions and 60 deletions
+3
View File
@@ -5,6 +5,9 @@
- Add support for hillshade's color arrays and relief-color elevation expression
- Change layers icons to make them a bit more distinct
- 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
- _...Add new stuff here..._
### 🐞 Bug fixes
+14
View File
@@ -491,6 +491,20 @@ describe("layers", () => {
when.click("field-doc-button-Offset", 0);
then(get.elementByTestId("spec-field-doc")).shouldContainText("Offset distance");
});
it.only("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", () => {
+74
View File
@@ -236,6 +236,29 @@ describe("modals", () => {
).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", () => {
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");
@@ -304,6 +327,57 @@ describe("modals", () => {
it("toggle");
});
describe("global state", () => {
beforeEach(() => {
when.click("nav:global-state");
});
it("add variable", () => {
when.click("global-state-add-variable");
then(get.styleFromLocalStorage()).shouldDeepNestedInclude({
state: { key1: { default: "value" } },
});
});
it("add multiple variables", () => {
when.click("global-state-add-variable");
when.click("global-state-add-variable");
when.click("global-state-add-variable");
then(get.styleFromLocalStorage()).shouldDeepNestedInclude({
state: { key1: { default: "value" }, key2: { default: "value" }, key3: { default: "value" } },
});
});
it("remove 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);
then(get.styleFromLocalStorage()).shouldDeepNestedInclude({
state: { key2: { default: "value" }, key3: { default: "value" } },
});
});
it("edit variable key", () => {
when.click("global-state-add-variable");
when.setValue("global-state-variable-key:0", "mykey");
when.typeKeys("{enter}");
then(get.styleFromLocalStorage()).shouldDeepNestedInclude({
state: { mykey: { default: "value" } },
});
});
it("edit variable value", () => {
when.click("global-state-add-variable");
when.setValue("global-state-variable-value:0", "myvalue");
when.typeKeys("{enter}");
then(get.styleFromLocalStorage()).shouldDeepNestedInclude({
state: { key1: { default: "myvalue" } },
});
});
});
describe("Handle localStorage QuotaExceededError", () => {
it("handles quota exceeded error when opening style from URL", () => {
// Clear localStorage to start fresh
+1169 -16
View File
File diff suppressed because it is too large Load Diff
+1
View File
@@ -69,6 +69,7 @@
"react-file-reader-input": "^2.0.0",
"react-i18next": "^15.7.3",
"react-icons": "^5.5.0",
"react-markdown": "^10.1.0",
"reconnecting-websocket": "^4.4.0",
"slugify": "^1.6.6",
"string-hash": "^1.1.3",
+21 -6
View File
@@ -25,6 +25,7 @@ import ModalSources from "./modals/ModalSources";
import ModalOpen from "./modals/ModalOpen";
import ModalShortcuts from "./modals/ModalShortcuts";
import ModalDebug from "./modals/ModalDebug";
import ModalGlobalState from "./modals/ModalGlobalState";
import {downloadGlyphsMetadata, downloadSpriteMetadata} from "../libs/metadata";
import style from "../libs/style";
@@ -126,6 +127,7 @@ type AppState = {
shortcuts: boolean
export: boolean
debug: boolean
globalState: boolean
}
fileHandle: FileSystemFileHandle | null
};
@@ -164,6 +166,7 @@ export default class App extends React.Component<any, AppState> {
shortcuts: false,
export: false,
debug: false,
globalState: false,
},
maplibreGlDebugOptions: {
showTileBoundaries: false,
@@ -213,6 +216,12 @@ export default class App extends React.Component<any, AppState> {
this.toggleModal("settings");
}
},
{
key: "g",
handler: () => {
this.toggleModal("globalState");
}
},
{
key: "i",
handler: () => {
@@ -911,39 +920,45 @@ export default class App extends React.Component<any, AppState> {
onChangeMaplibreGlDebug={this.onChangeMaplibreGlDebug}
onChangeOpenlayersDebug={this.onChangeOpenlayersDebug}
isOpen={this.state.isOpen.debug}
onOpenToggle={this.toggleModal.bind(this, "debug")}
onOpenToggle={() => this.toggleModal("debug")}
mapView={this.state.mapView}
/>
<ModalShortcuts
isOpen={this.state.isOpen.shortcuts}
onOpenToggle={this.toggleModal.bind(this, "shortcuts")}
onOpenToggle={() => this.toggleModal("shortcuts")}
/>
<ModalSettings
mapStyle={this.state.mapStyle}
onStyleChanged={this.onStyleChanged}
onChangeMetadataProperty={this.onChangeMetadataProperty}
isOpen={this.state.isOpen.settings}
onOpenToggle={this.toggleModal.bind(this, "settings")}
onOpenToggle={() => this.toggleModal("settings")}
/>
<ModalExport
mapStyle={this.state.mapStyle}
onStyleChanged={this.onStyleChanged}
isOpen={this.state.isOpen.export}
onOpenToggle={this.toggleModal.bind(this, "export")}
onOpenToggle={() => this.toggleModal("export")}
fileHandle={this.state.fileHandle}
onSetFileHandle={this.onSetFileHandle}
/>
<ModalOpen
isOpen={this.state.isOpen.open}
onStyleOpen={this.openStyle}
onOpenToggle={this.toggleModal.bind(this, "open")}
onOpenToggle={() => this.toggleModal("open")}
fileHandle={this.state.fileHandle}
/>
<ModalSources
mapStyle={this.state.mapStyle}
onStyleChanged={this.onStyleChanged}
isOpen={this.state.isOpen.sources}
onOpenToggle={this.toggleModal.bind(this, "sources")}
onOpenToggle={() => this.toggleModal("sources")}
/>
<ModalGlobalState
mapStyle={this.state.mapStyle}
onStyleChanged={this.onStyleChanged}
isOpen={this.state.isOpen.globalState}
onOpenToggle={() => this.toggleModal("globalState")}
/>
</div>;
+6 -1
View File
@@ -9,7 +9,8 @@ import {
MdHelpOutline,
MdFindInPage,
MdLanguage,
MdSave
MdSave,
MdPublic
} from "react-icons/md";
import pkgJson from "../../package.json";
//@ts-ignore
@@ -236,6 +237,10 @@ class AppToolbarInternal extends React.Component<AppToolbarInternalProps> {
<MdSettings />
<IconText>{t("Style Settings")}</IconText>
</ToolbarAction>
<ToolbarAction wdKey="nav:global-state" onClick={this.props.onToggleModal.bind(this, "globalState")}>
<MdPublic />
<IconText>{t("Global State")}</IconText>
</ToolbarAction>
<ToolbarSelect wdKey="nav:inspect">
<MdFindInPage />
+16 -4
View File
@@ -1,10 +1,10 @@
import React from "react";
import Markdown from "react-markdown";
const headers = {
js: "JS",
android: "Android",
ios: "iOS",
macos: "macOS",
ios: "iOS"
};
type DocProps = {
@@ -37,11 +37,23 @@ export default class Doc extends React.Component<DocProps> {
!Array.isArray(values)
);
const sdkSupportToJsx = (value: string) => {
const supportValue = value.toLowerCase();
if (supportValue.startsWith("https://")) {
return <a href={supportValue} target="_blank" rel="noreferrer">{"#" + supportValue.split("/").pop()}</a>;
}
return value;
};
return (
<>
{doc &&
<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 &&
<ul className="SpecDoc__values">
{Object.entries(values).map(([key, value]) => {
@@ -74,7 +86,7 @@ export default class Doc extends React.Component<DocProps> {
<td>{key}</td>
{Object.keys(headers).map((k) => {
if (Object.prototype.hasOwnProperty.call(supportObj, k)) {
return <td key={k}>{supportObj[k as keyof typeof headers]}</td>;
return <td key={k}>{sdkSupportToJsx(supportObj[k as keyof typeof headers])}</td>;
}
else {
return <td key={k}>no</td>;
+2 -2
View File
@@ -8,7 +8,7 @@ type ModalInternalProps = PropsWithChildren & {
"data-wd-key"?: string
isOpen: boolean
title: string
onOpenToggle(value: boolean): unknown
onOpenToggle(): void
underlayClickExits?: boolean
className?: string
} & WithTranslation;
@@ -26,7 +26,7 @@ class ModalInternal extends React.Component<ModalInternalProps> {
}
setTimeout(() => {
this.props.onOpenToggle(false);
this.props.onOpenToggle();
}, 0);
};
+2 -2
View File
@@ -14,7 +14,7 @@ type ModalAddInternalProps = {
layers: LayerSpecification[]
onLayersChange(layers: LayerSpecification[]): unknown
isOpen: boolean
onOpenToggle(open: boolean): unknown
onOpenToggle(): void
// A dict of source id's and the available source layers
sources: Record<string, SourceSpecification & {layers: string[]}>;
} & WithTranslation;
@@ -50,7 +50,7 @@ class ModalAddInternal extends React.Component<ModalAddInternalProps, ModalAddSt
changedLayers.push(layer as LayerSpecification);
this.setState({ error: null }, () => {
this.props.onLayersChange(changedLayers);
this.props.onOpenToggle(false);
this.props.onOpenToggle();
});
};
+1 -1
View File
@@ -9,7 +9,7 @@ type ModalDebugInternalProps = {
renderer: string
onChangeMaplibreGlDebug(key: string, checked: boolean): unknown
onChangeOpenlayersDebug(key: string, checked: boolean): unknown
onOpenToggle(value: boolean): unknown
onOpenToggle(): void
maplibreGlDebugOptions?: object
openlayersDebugOptions?: object
mapView: {
+1 -1
View File
@@ -22,7 +22,7 @@ type ModalExportInternalProps = {
mapStyle: StyleSpecificationWithId
onStyleChanged: OnStyleChangedCallback
isOpen: boolean
onOpenToggle(...args: unknown[]): unknown
onOpenToggle(): void
onSetFileHandle(fileHandle: FileSystemFileHandle | null): unknown
fileHandle: FileSystemFileHandle | null
} & WithTranslation;
+155
View File
@@ -0,0 +1,155 @@
import React from "react";
import { withTranslation, type WithTranslation } from "react-i18next";
import { MdDelete } from "react-icons/md";
import latest from "@maplibre/maplibre-gl-style-spec/dist/latest.json";
import Modal from "./Modal";
import FieldString from "../FieldString";
import InputButton from "../InputButton";
import { PiListPlusBold } from "react-icons/pi";
import { type StyleSpecificationWithId } from "../../libs/definitions";
import { type SchemaSpecification } from "maplibre-gl";
import Doc from "../Doc";
type ModalGlobalStateInternalProps = {
mapStyle: StyleSpecificationWithId;
isOpen: boolean;
onStyleChanged(style: StyleSpecificationWithId): void;
onOpenToggle(): void
} & WithTranslation;
type GlobalStateVariable = {
key: string;
value: any;
};
const ModalGlobalStateInternal: React.FC<ModalGlobalStateInternalProps> = (props) => {
const getGlobalStateVariables = (): GlobalStateVariable[] => {
const style = props.mapStyle;
const globalState = style.state || {};
return Object.entries(globalState).map(([key, value]) => ({
key,
value: value.default
}));
};
const setGlobalStateVariables = (variables: GlobalStateVariable[]) => {
const style = { ...props.mapStyle };
// Create the globalState object from the variables array
const globalState: Record<string, SchemaSpecification> = {};
for (const variable of variables) {
if (variable.key.trim() !== "") {
globalState[variable.key] = {
default: variable.value
};
}
}
style.state = Object.keys(globalState).length > 0 ? globalState : undefined;
props.onStyleChanged(style);
};
const onAddVariable = () => {
const variables = getGlobalStateVariables();
let index = 1;
while (variables.find(v => v.key === `key${index}`)) {
index++;
}
variables.push({ key: `key${index}`, value: "value" });
setGlobalStateVariables(variables);
};
const onRemoveVariable = (index: number) => {
const variables = getGlobalStateVariables();
variables.splice(index, 1);
setGlobalStateVariables(variables);
};
const onChangeVariableKey = (index: number, newKey: string) => {
const variables = getGlobalStateVariables();
variables[index].key = newKey || "";
setGlobalStateVariables(variables);
};
const onChangeVariableValue = (index: number, newValue: string) => {
const variables = getGlobalStateVariables();
variables[index].value = newValue || "";
setGlobalStateVariables(variables);
};
const variables = getGlobalStateVariables();
const variableFields = variables.map((variable, index) => (
<tr key={index}>
<td>
<FieldString
label={props.t("Key")}
value={variable.key}
onChange={(value) => onChangeVariableKey(index, value || "")}
data-wd-key={"global-state-variable-key:" + index}
/>
</td>
<td>
<FieldString
label={props.t("Value")}
value={variable.value}
onChange={(value) => onChangeVariableValue(index, value || "")}
data-wd-key={"global-state-variable-value:" + index}
/>
</td>
<td style={{ verticalAlign: "middle"}}>
<InputButton
onClick={() => onRemoveVariable(index)}
title={props.t("Remove variable")}
data-wd-key="global-state-remove-variable"
>
<MdDelete />
</InputButton>
</td>
</tr>
));
return (
<Modal
data-wd-key="modal:global-state"
isOpen={props.isOpen}
onOpenToggle={props.onOpenToggle}
title={props.t("Global State Variables")}
>
{variables.length === 0 &&
<div>
<p>{props.t("No global state variables defined. Add variables to create reusable values in your style.")}</p>
<div key="doc" className="maputnik-doc-inline">
<Doc fieldSpec={latest.$root.state} />
</div>
</div>
}
{variables.length > 0 &&
<table>
<thead>
</thead>
<tbody>
{variableFields}
</tbody>
</table>
}
<div>
<InputButton
onClick={onAddVariable}
data-wd-key="global-state-add-variable"
>
<PiListPlusBold />
{props.t("Add Variable")}
</InputButton>
</div>
</Modal>
);
};
const ModalGlobalState = withTranslation()(ModalGlobalStateInternal);
export default ModalGlobalState;
+1 -1
View File
@@ -45,7 +45,7 @@ class PublicStyle extends React.Component<PublicStyleProps> {
type ModalOpenInternalProps = {
isOpen: boolean
onOpenToggle(...args: unknown[]): unknown
onOpenToggle(): void
onStyleOpen(...args: unknown[]): unknown
fileHandle: FileSystemFileHandle | null
} & WithTranslation;
+55 -23
View File
@@ -1,6 +1,6 @@
import React from "react";
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 FieldArray from "../FieldArray";
@@ -19,7 +19,7 @@ type ModalSettingsInternalProps = {
onStyleChanged: OnStyleChangedCallback
onChangeMetadataProperty(...args: unknown[]): unknown
isOpen: boolean
onOpenToggle(...args: unknown[]): unknown
onOpenToggle(): void
} & WithTranslation;
class ModalSettingsInternal extends React.Component<ModalSettingsInternalProps> {
@@ -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) {
const changedStyle = {
...this.props.mapStyle,
@@ -103,6 +121,7 @@ class ModalSettingsInternal extends React.Component<ModalSettingsInternalProps>
const light = this.props.mapStyle.light || {};
const transition = this.props.mapStyle.transition || {};
const terrain = this.props.mapStyle.terrain || {} as TerrainSpecification;
const projection = this.props.mapStyle.projection || {} as ProjectionSpecification;
return <Modal
data-wd-key="modal:settings"
@@ -116,21 +135,21 @@ class ModalSettingsInternal extends React.Component<ModalSettingsInternalProps>
fieldSpec={latest.$root.name}
data-wd-key="modal:settings.name"
value={this.props.mapStyle.name}
onChange={this.changeStyleProperty.bind(this, "name")}
onChange={(value) => this.changeStyleProperty("name", value)}
/>
<FieldString
label={t("Owner")}
fieldSpec={{doc: t("Owner ID of the style. Used by Mapbox or future style APIs.")}}
data-wd-key="modal:settings.owner"
value={(this.props.mapStyle as any).owner}
onChange={this.changeStyleProperty.bind(this, "owner")}
onChange={(value) => this.changeStyleProperty("owner", value)}
/>
<FieldUrl
fieldSpec={latest.$root.sprite}
label={t("Sprite URL")}
data-wd-key="modal:settings.sprite"
value={this.props.mapStyle.sprite as string}
onChange={this.changeStyleProperty.bind(this, "sprite")}
onChange={(value) => this.changeStyleProperty("sprite", value)}
/>
<FieldUrl
@@ -138,7 +157,7 @@ class ModalSettingsInternal extends React.Component<ModalSettingsInternalProps>
fieldSpec={latest.$root.glyphs}
data-wd-key="modal:settings.glyphs"
value={this.props.mapStyle.glyphs as string}
onChange={this.changeStyleProperty.bind(this, "glyphs")}
onChange={(value) => this.changeStyleProperty("glyphs", value)}
/>
<FieldString
@@ -146,7 +165,7 @@ class ModalSettingsInternal extends React.Component<ModalSettingsInternalProps>
fieldSpec={fsa.maputnik.maptiler_access_token}
data-wd-key="modal:settings.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
@@ -154,7 +173,7 @@ class ModalSettingsInternal extends React.Component<ModalSettingsInternalProps>
fieldSpec={fsa.maputnik.thunderforest_access_token}
data-wd-key="modal:settings.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
@@ -162,7 +181,7 @@ class ModalSettingsInternal extends React.Component<ModalSettingsInternalProps>
fieldSpec={fsa.maputnik.stadia_access_token}
data-wd-key="modal:settings.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
@@ -170,7 +189,7 @@ class ModalSettingsInternal extends React.Component<ModalSettingsInternalProps>
fieldSpec={fsa.maputnik.locationiq_access_token}
data-wd-key="modal:settings.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
@@ -180,7 +199,7 @@ class ModalSettingsInternal extends React.Component<ModalSettingsInternalProps>
type="number"
value={mapStyle.center || []}
default={[0, 0]}
onChange={this.changeStyleProperty.bind(this, "center")}
onChange={(value) => this.changeStyleProperty("center", value)}
/>
<FieldNumber
@@ -188,7 +207,7 @@ class ModalSettingsInternal extends React.Component<ModalSettingsInternalProps>
fieldSpec={latest.$root.zoom}
value={mapStyle.zoom}
default={0}
onChange={this.changeStyleProperty.bind(this, "zoom")}
onChange={(value) => this.changeStyleProperty("zoom", value)}
/>
<FieldNumber
@@ -196,7 +215,7 @@ class ModalSettingsInternal extends React.Component<ModalSettingsInternalProps>
fieldSpec={latest.$root.bearing}
value={mapStyle.bearing}
default={latest.$root.bearing.default}
onChange={this.changeStyleProperty.bind(this, "bearing")}
onChange={(value) => this.changeStyleProperty("bearing", value)}
/>
<FieldNumber
@@ -204,7 +223,7 @@ class ModalSettingsInternal extends React.Component<ModalSettingsInternalProps>
fieldSpec={latest.$root.pitch}
value={mapStyle.pitch}
default={latest.$root.pitch.default}
onChange={this.changeStyleProperty.bind(this, "pitch")}
onChange={(value) => this.changeStyleProperty("pitch", value)}
/>
<FieldEnum
@@ -214,7 +233,7 @@ class ModalSettingsInternal extends React.Component<ModalSettingsInternalProps>
value={light.anchor as string}
options={Object.keys(latest.light.anchor.values)}
default={latest.light.anchor.default}
onChange={this.changeLightProperty.bind(this, "anchor")}
onChange={(value) => this.changeLightProperty("anchor", value)}
/>
<FieldColor
@@ -222,7 +241,7 @@ class ModalSettingsInternal extends React.Component<ModalSettingsInternalProps>
fieldSpec={latest.light.color}
value={light.color as string}
default={latest.light.color.default}
onChange={this.changeLightProperty.bind(this, "color")}
onChange={(value) => this.changeLightProperty("color", value)}
/>
<FieldNumber
@@ -230,7 +249,7 @@ class ModalSettingsInternal extends React.Component<ModalSettingsInternalProps>
fieldSpec={latest.light.intensity}
value={light.intensity as number}
default={latest.light.intensity.default}
onChange={this.changeLightProperty.bind(this, "intensity")}
onChange={(value) => this.changeLightProperty("intensity", value)}
/>
<FieldArray
@@ -240,7 +259,7 @@ class ModalSettingsInternal extends React.Component<ModalSettingsInternalProps>
length={latest.light.position.length}
value={light.position as number[]}
default={latest.light.position.default}
onChange={this.changeLightProperty.bind(this, "position")}
onChange={(value) => this.changeLightProperty("position", value)}
/>
<FieldString
@@ -248,7 +267,7 @@ class ModalSettingsInternal extends React.Component<ModalSettingsInternalProps>
fieldSpec={latest.terrain.source}
data-wd-key="modal:settings.maputnik:terrain_source"
value={terrain.source}
onChange={this.changeTerrainProperty.bind(this, "source")}
onChange={(value) => this.changeTerrainProperty("source", value)}
/>
<FieldNumber
@@ -256,7 +275,7 @@ class ModalSettingsInternal extends React.Component<ModalSettingsInternalProps>
fieldSpec={latest.terrain.exaggeration}
value={terrain.exaggeration}
default={latest.terrain.exaggeration.default}
onChange={this.changeTerrainProperty.bind(this, "exaggeration")}
onChange={(value) => this.changeTerrainProperty("exaggeration", value)}
/>
<FieldNumber
@@ -264,7 +283,7 @@ class ModalSettingsInternal extends React.Component<ModalSettingsInternalProps>
fieldSpec={latest.transition.delay}
value={transition.delay}
default={latest.transition.delay.default}
onChange={this.changeTransitionProperty.bind(this, "delay")}
onChange={(value) => this.changeTransitionProperty("delay", value)}
/>
<FieldNumber
@@ -272,7 +291,20 @@ class ModalSettingsInternal extends React.Component<ModalSettingsInternalProps>
fieldSpec={latest.transition.duration}
value={transition.duration}
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
@@ -284,7 +316,7 @@ class ModalSettingsInternal extends React.Component<ModalSettingsInternalProps>
["ol", t("Open Layers (experimental)")],
]}
value={metadata["maputnik:renderer"] || "mlgljs"}
onChange={onChangeMetadataProperty.bind(this, "maputnik:renderer")}
onChange={(value) => onChangeMetadataProperty("maputnik:renderer", value)}
/>
</div>
</Modal>;
+1 -1
View File
@@ -6,7 +6,7 @@ import Modal from "./Modal";
type ModalShortcutsInternalProps = {
isOpen: boolean
onOpenToggle(...args: unknown[]): unknown
onOpenToggle(): void
} & WithTranslation;
+1 -1
View File
@@ -273,7 +273,7 @@ class AddSource extends React.Component<AddSourceProps, AddSourceState> {
type ModalSourcesInternalProps = {
mapStyle: StyleSpecificationWithId
isOpen: boolean
onOpenToggle(...args: unknown[]): unknown
onOpenToggle(): void
onStyleChanged: OnStyleChangedCallback
} & WithTranslation;
+1 -1
View File
@@ -252,7 +252,7 @@
margin-top: vars.$margin-3;
}
.SpecDoc__values code {
.SpecDoc__values code, .SpecDoc__doc code {
background: vars.$color-midgray;
padding: 0.1em 0.3em;
border-radius: 2px;