Add code editor for maputnik (#1426)

## Launch Checklist

This PR adds the ability to look at the entire style and edit it in a
code editor that supports syntax highlight, errors, search and more.

- Resolves #820

CC: @Kanahiro as I know you did something similar, probably has better
performance...

After:
<img width="1920" height="937" alt="image"
src="https://github.com/user-attachments/assets/f925cf92-2623-4390-8f75-14d7f6a79171"
/>


 - [x] Briefly describe the changes in this PR.
 - [x] Link to related issues.
- [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>
Co-authored-by: Frank Elsinga <frank@elsinga.de>
This commit is contained in:
Harel M
2025-10-05 16:38:03 +03:00
committed by GitHub
parent 454d8d8b10
commit 39d63ec7b1
15 changed files with 169 additions and 41 deletions

View File

@@ -10,6 +10,7 @@
- Added global state modal to allow editing the global state
- Added color highlight for problematic properties
- Upgraded codemirror from version 5 to version 6
- Add code editor to allow editing the entire style
- _...Add new stuff here..._
### 🐞 Bug fixes

View File

@@ -18,10 +18,10 @@ describe("accessibility", () => {
then(get.skipTargetLayerList()).shouldBeFocused();
});
// This fails for some reason only in Chrome, but passes in firefox. Adding a skip here to allow merge and later on we'll decide if we want to fix this or not.
it.skip("skip link to layer editor", () => {
it("skip link to layer editor", () => {
const selector = "root:skip:layer-editor";
then(get.elementByTestId(selector)).shouldExist();
then(get.elementByTestId("skip-target-layer-editor")).shouldExist();
when.tab().tab();
then(get.elementByTestId(selector)).shouldBeFocused();
when.click(selector);

View File

@@ -0,0 +1,18 @@
import { MaputnikDriver } from "./maputnik-driver";
describe("code editor", () => {
const { beforeAndAfter, when, get, then } = new MaputnikDriver();
beforeAndAfter();
it("open code editor", () => {
when.click("nav:code-editor");
then(get.element(".maputnik-code-editor")).shouldExist();
});
it("closes code editor", () => {
when.click("nav:code-editor");
then(get.element(".maputnik-code-editor")).shouldExist();
when.click("nav:code-editor");
then(get.element(".maputnik-code-editor")).shouldNotExist();
});
});

View File

@@ -13,6 +13,7 @@ import latest from "@maplibre/maplibre-gl-style-spec/dist/latest.json";
import MapMaplibreGl from "./MapMaplibreGl";
import MapOpenLayers from "./MapOpenLayers";
import CodeEditor from "./CodeEditor";
import LayerList from "./LayerList";
import LayerEditor from "./LayerEditor";
import AppToolbar, { type MapState } from "./AppToolbar";
@@ -116,6 +117,7 @@ type AppState = {
export: boolean
debug: boolean
globalState: boolean
codeEditor: boolean
}
fileHandle: FileSystemFileHandle | null
};
@@ -155,6 +157,7 @@ export default class App extends React.Component<any, AppState> {
export: false,
debug: false,
globalState: false,
codeEditor: false
},
maplibreGlDebugOptions: {
showTileBoundaries: false,
@@ -856,9 +859,15 @@ export default class App extends React.Component<any, AppState> {
onStyleChanged={this.onStyleChanged}
onStyleOpen={this.onStyleChanged}
onSetMapState={this.setMapState}
onToggleModal={this.toggleModal.bind(this)}
onToggleModal={(modal: keyof AppState["isOpen"]) => this.toggleModal(modal)}
/>;
const codeEditor = this.state.isOpen.codeEditor ? <CodeEditor
value={this.state.mapStyle}
onChange={(style) => this.onStyleChanged(style)}
onClose={() => this.setModal("codeEditor", false)}
/> : undefined;
const layerList = <LayerList
onMoveLayer={this.onMoveLayer}
onLayerDestroy={this.onLayerDestroy}
@@ -954,6 +963,7 @@ export default class App extends React.Component<any, AppState> {
toolbar={toolbar}
layerList={layerList}
layerEditor={layerEditor}
codeEditor={codeEditor}
map={this.mapRenderer()}
bottom={bottomPanel}
modals={modals}

View File

@@ -7,6 +7,7 @@ type AppLayoutInternalProps = {
toolbar: React.ReactElement
layerList: React.ReactElement
layerEditor?: React.ReactElement
codeEditor?: React.ReactElement
map: React.ReactElement
bottom?: React.ReactElement
modals?: React.ReactNode
@@ -21,14 +22,22 @@ class AppLayoutInternal extends React.Component<AppLayoutInternalProps> {
<div className="maputnik-layout">
{this.props.toolbar}
<div className="maputnik-layout-main">
<div className="maputnik-layout-list">
{this.props.layerList}
</div>
<div className="maputnik-layout-drawer">
{this.props.codeEditor && <div className="maputnik-layout-code-editor">
<ScrollContainer>
{this.props.layerEditor}
{this.props.codeEditor}
</ScrollContainer>
</div>
}
{!this.props.codeEditor && <>
<div className="maputnik-layout-list">
{this.props.layerList}
</div>
<div className="maputnik-layout-drawer">
<ScrollContainer>
{this.props.layerEditor}
</ScrollContainer>
</div>
</>}
{this.props.map}
</div>
{this.props.bottom && <div className="maputnik-layout-bottom">

View File

@@ -10,7 +10,8 @@ import {
MdFindInPage,
MdLanguage,
MdSave,
MdPublic
MdPublic,
MdCode
} from "react-icons/md";
import pkgJson from "../../package.json";
//@ts-ignore
@@ -23,6 +24,7 @@ import type { OnStyleChangedCallback } from "../libs/definitions";
const browser = detect();
const colorAccessibilityFiltersEnabled = ["chrome", "firefox"].indexOf(browser!.name) > -1;
export type ModalTypes = "settings" | "sources" | "open" | "shortcuts" | "export" | "debug" | "globalState" | "codeEditor";
type IconTextProps = {
children?: React.ReactNode
@@ -39,7 +41,6 @@ type ToolbarLinkProps = {
className?: string
children?: React.ReactNode
href?: string
onToggleModal?(...args: unknown[]): unknown
};
class ToolbarLink extends React.Component<ToolbarLinkProps> {
@@ -101,7 +102,7 @@ type AppToolbarInternalProps = {
// A dict of source id's and the available source layers
sources: object
children?: React.ReactNode
onToggleModal(...args: unknown[]): unknown
onToggleModal(modal: ModalTypes): void
onSetMapState(mapState: MapState): unknown
mapState?: MapState
renderer?: string
@@ -221,23 +222,27 @@ class AppToolbarInternal extends React.Component<AppToolbarInternalProps> {
</a>
</div>
<div className="maputnik-toolbar__actions" role="navigation" aria-label="Toolbar">
<ToolbarAction wdKey="nav:open" onClick={this.props.onToggleModal.bind(this, "open")}>
<ToolbarAction wdKey="nav:open" onClick={() => this.props.onToggleModal("open")}>
<MdOpenInBrowser />
<IconText>{t("Open")}</IconText>
</ToolbarAction>
<ToolbarAction wdKey="nav:export" onClick={this.props.onToggleModal.bind(this, "export")}>
<ToolbarAction wdKey="nav:export" onClick={() => this.props.onToggleModal("export")}>
<MdSave />
<IconText>{t("Save")}</IconText>
</ToolbarAction>
<ToolbarAction wdKey="nav:sources" onClick={this.props.onToggleModal.bind(this, "sources")}>
<ToolbarAction wdKey="nav:code-editor" onClick={() => this.props.onToggleModal("codeEditor")}>
<MdCode />
<IconText>{t("Code Editor")}</IconText>
</ToolbarAction>
<ToolbarAction wdKey="nav:sources" onClick={() => this.props.onToggleModal("sources")}>
<MdLayers />
<IconText>{t("Data Sources")}</IconText>
</ToolbarAction>
<ToolbarAction wdKey="nav:settings" onClick={this.props.onToggleModal.bind(this, "settings")}>
<ToolbarAction wdKey="nav:settings" onClick={() => this.props.onToggleModal("settings")}>
<MdSettings />
<IconText>{t("Style Settings")}</IconText>
</ToolbarAction>
<ToolbarAction wdKey="nav:global-state" onClick={this.props.onToggleModal.bind(this, "globalState")}>
<ToolbarAction wdKey="nav:global-state" onClick={() => this.props.onToggleModal("globalState")}>
<MdPublic />
<IconText>{t("Global State")}</IconText>
</ToolbarAction>

View File

@@ -0,0 +1,28 @@
import InputJson from "./InputJson";
import React from "react";
import { withTranslation, type WithTranslation } from "react-i18next";
import { type StyleSpecification } from "maplibre-gl";
import { type StyleSpecificationWithId } from "../libs/definitions";
export type CodeEditorProps = {
value: StyleSpecification;
onChange: (value: StyleSpecificationWithId) => void;
onClose: () => void;
} & WithTranslation;
const CodeEditorInternal: React.FC<CodeEditorProps> = (props) => {
return <>
<button className="maputnik-button" onClick={props.onClose} aria-label={props.t("Close")} style={{ position: "sticky", top: "0", zIndex: 1 }}>{props.t("Click to close the editor")}</button>
<InputJson
lintType="style"
value={props.value}
onChange={props.onChange}
className={"maputnik-code-editor"}
/>;
</>;
};
const CodeEditor = withTranslation()(CodeEditorInternal);
export default CodeEditor;

View File

@@ -31,11 +31,15 @@
"Maputnik on GitHub": "Maputnik auf GitHub",
"Open": "Öffnen",
"Save": "Speichern",
"Code Editor": "Code-Editor",
"Data Sources": "Datenquellen",
"Style Settings": "Stileinstellungen",
"Global State": "Globaler Zustand",
"View": "Ansicht",
"Color accessibility": "Farbzugänglichkeit",
"Help": "Hilfe",
"Close": "Schließen",
"Click to close the editor": "Klicken Sie hier, um den Editor zu schließen",
"Comments for the current layer. This is non-standard and not in the spec.": "Kommentare zur aktuellen Ebene. Das ist nicht standardmäßig und nicht in der Spezifikation.",
"Comments": "Kommentare",
"Comment...": "Dein Kommentar...",
@@ -56,7 +60,6 @@
"Delete filter block": "Filterblock löschen",
"Add value": "Wert hinzufügen",
"Remove array item": "Element aus Array entfernen",
"Press <1>ESC</1> to lose focus": "Drück <1>ESC</1>, um den Fokus zu verlieren",
"Must provide protocol: <1>https://</1>": "Protokoll erforderlich: <1>https://</1>",
"Must provide protocol: <1>http://</1> or <3>https://</3>": "Protokoll erforderlich: <1>http://</1> oder <3>https://</3>",
"CORS policy won't allow fetching resources served over http from https, use a <1>https://</1> domain": "Die CORS-Politik erlaubt es nicht, Ressourcen über http von https zu laden, benutze eine <1>https://</1>-Domain",
@@ -96,6 +99,12 @@
"Save the JSON style to your computer.": "Speichere den JSON Stil auf deinem Computer.",
"Save as": "Speichern unter",
"Create HTML": "HTML erstellen",
"Key": "Schlüssel",
"Value": "Wert",
"Remove variable": "Variable entfernen",
"Global State Variables": "Globale Zustandsvariablen",
"No global state variables defined. Add variables to create reusable values in your style.": "Keine globalen Zustandsvariablen definiert. Füge Variablen hinzu, um wiederverwendbare Werte in deinem Stil zu erstellen.",
"Add Variable": "Variable hinzufügen",
"Cancel": "Abbrechen",
"Open Style": "Stil öffnen",
"Open local Style": "Lokalen Stil öffnen",
@@ -121,9 +130,10 @@
"Light intensity": "Lichtintensität",
"Light position": "Lichtposition",
"Terrain source": "Geländequelle",
"Terrain exaggeration": "Geländebertreibung",
"Terrain exaggeration": "Geländeüberhöhung",
"Transition delay": "Übergangsverzögerung",
"Transition duration": "Übergangsdauer",
"Projection": "Projektion",
"Open Layers (experimental)": "Ebenen öffnen (experimentell)",
"Shortcuts menu": "Shortcuts-Menü",
"Open modal": "Modale Fenster öffnen",
@@ -201,10 +211,8 @@
"Radius": "Radius",
"Color": "Farbe",
"Outline color": "Umrissfarbe",
"Language": "Sprache",
"Width": "Breite",
"Height": "Höhe",
"Field": "Feld",
"Size": "Größe",
"Press <1>ESC</1> to lose focus of any input box": "Drück <1>ESC</1>, um den Fokus von jedem Eingabefeld zu verlieren"
"Size": "Größe"
}

View File

@@ -31,11 +31,15 @@
"Maputnik on GitHub": "Maputnik sur GitHub",
"Open": "Ouvrir",
"Save": "Enregistrer",
"Code Editor": "Éditeur de code",
"Data Sources": "Sources de données",
"Style Settings": "Paramètres du style",
"Global State": "État global",
"View": "Vue",
"Color accessibility": "Accessibilité des couleurs",
"Help": "Aide",
"Close": "Fermer",
"Click to close the editor": "Cliquez pour fermer l'éditeur",
"Comments for the current layer. This is non-standard and not in the spec.": "Commentaires pour le calque actuel. Ceci n'est pas standard et n'est pas dans la spécification.",
"Comments": "Commentaires",
"Comment...": "Votre commentaire...",
@@ -56,7 +60,6 @@
"Delete filter block": "Supprimer le bloc de filtre",
"Add value": "Ajouter une valeur",
"Remove array item": "Supprimer l'élément de tableau",
"Press <1>ESC</1> to lose focus": "Appuyez sur <1>Échap</1> pour perdre le focus",
"Must provide protocol: <1>https://</1>": "Protocole requis : <1>https://</1>",
"Must provide protocol: <1>http://</1> or <3>https://</3>": "Protocole requis : <1>http://</1> ou <3>https://</3>",
"CORS policy won't allow fetching resources served over http from https, use a <1>https://</1> domain": "La politique CORS ne permet pas de récupérer des ressources servies sur http depuis https, utilisez un protocole <1>https://</1>",
@@ -96,6 +99,12 @@
"Save the JSON style to your computer.": "Enregistrer le style JSON sur votre ordinateur.",
"Save as": "Enregistrer sous",
"Create HTML": "Créer le HTML",
"Key": "Clé",
"Value": "Valeur",
"Remove variable": "Supprimer la variable",
"Global State Variables": "Variables d'état global",
"No global state variables defined. Add variables to create reusable values in your style.": "Aucune variable d'état global définie. Ajoutez des variables pour créer des valeurs réutilisables dans votre style.",
"Add Variable": "Ajouter une variable",
"Cancel": "Annuler",
"Open Style": "Ouvrir le style",
"Open local Style": "Ouvrir un style local",
@@ -124,6 +133,7 @@
"Terrain exaggeration": "Exagération du terrain",
"Transition delay": "Délai de transition",
"Transition duration": "Durée de transition",
"Projection": "Projection",
"Open Layers (experimental)": "Open Layers (expérimental)",
"Shortcuts menu": "Menu des raccourcis",
"Open modal": "Ouvrir (modale)",
@@ -196,7 +206,5 @@
"LocationIQ Access Token": "Jeton d'accès LocationIQ",
"Public access token for LocationIQ services.": "Jeton d'accès public pour les services LocationIQ.",
"Style Renderer": "Moteur de rendu pour le style",
"Choose the default Maputnik renderer for this style.": "Choisissez le moteur de rendu Maputnik par défaut pour ce style.",
"Language": "Langue",
"Layer options": "Options du calque"
"Choose the default Maputnik renderer for this style.": "Choisissez le moteur de rendu Maputnik par défaut pour ce style."
}

View File

@@ -31,11 +31,15 @@
"Maputnik on GitHub": "מפוטניק בגיטהב",
"Open": "פתיחה",
"Save": "שמור",
"Code Editor": "עורך קוד",
"Data Sources": "מקורות מידע",
"Style Settings": "הגדרות הסטייל",
"Global State": "מצב גלובלי",
"View": "תצוגה",
"Color accessibility": "נגישות צבעים",
"Help": "עזרה",
"Close": "סגירה",
"Click to close the editor": "לחץ לסגירת העורך",
"Comments for the current layer. This is non-standard and not in the spec.": "הערות על השכבה הנוכחית. זה לא חלק מהספסיפיקציות",
"Comments": "הערות",
"Comment...": "הערה...",
@@ -56,7 +60,6 @@
"Delete filter block": "מחיקת גוש מסנן",
"Add value": "הוספת ערך",
"Remove array item": "הסרת איבר מערך",
"Press <1>ESC</1> to lose focus": "לחצו על <1>ESC</1> על מנת לאבד פוקוס",
"Must provide protocol: <1>https://</1>": "נדרש פרוטוקול: <1>https://</1>",
"Must provide protocol: <1>http://</1> or <3>https://</3>": "נדרשים פרוטוקולים: <1>http://</1> או <3>https://</3>",
"CORS policy won't allow fetching resources served over http from https, use a <1>https://</1> domain": "CORS policy won't allow fetching resources served over http from https, use a <1>https://</1> domain",
@@ -96,6 +99,12 @@
"Save the JSON style to your computer.": "שמירת הסטייל JSON במחשב שלך.",
"Save as": "שמירה בשם",
"Create HTML": "צור HTML",
"Key": "מפתח",
"Value": "ערך",
"Remove variable": "הסר משתנה",
"Global State Variables": "משתני מצב גלובלי",
"No global state variables defined. Add variables to create reusable values in your style.": "לא הוגדרו משתני מצב גלובלי. הוסף משתנים כדי ליצור ערכים שניתן לעשות בהם שימוש חוזר בסטייל שלך.",
"Add Variable": "הוסף משתנה",
"Cancel": "ביטול",
"Open Style": "פתיחת סטייל",
"Open local Style": "פתיחת סטייל מקומי",
@@ -124,6 +133,7 @@
"Terrain exaggeration": "כמה \"להגזים\"",
"Transition delay": "זמן לפני מעבר",
"Transition duration": "זמן מעבר",
"Projection": "הטלה",
"Open Layers (experimental)": "Open Layers (experimental)",
"Shortcuts menu": "תפריט קיצורי דרך",
"Open modal": "פתיחת חלונית",
@@ -196,6 +206,5 @@
"LocationIQ Access Token": "LocationIQ Access Token",
"Public access token for LocationIQ services.": "Public access token for LocationIQ services.",
"Style Renderer": "צייר הסטייל",
"Choose the default Maputnik renderer for this style.": "בחירת צייר ברירת המחדל של מפוטניק עבור הסטייל הזה",
"Language": "שפה"
"Choose the default Maputnik renderer for this style.": "בחירת צייר ברירת המחדל של מפוטניק עבור הסטייל הזה"
}

View File

@@ -31,11 +31,15 @@
"Maputnik on GitHub": "Maputnik su GitHub",
"Open": "Apri",
"Save": "Salva",
"Code Editor": "Editor codice",
"Data Sources": "Fonti dati",
"Style Settings": "Impostazioni stile",
"Global State": "Stato globale",
"View": "Vista",
"Color accessibility": "Accessibilità colori",
"Help": "Aiuto",
"Close": "Chiudi",
"Click to close the editor": "Clicca per chiudere l'editor",
"Comments for the current layer. This is non-standard and not in the spec.": "Commenti per il livello attuale. Questo non è standard e non è nella specifica.",
"Comments": "Commenti",
"Comment...": "Il tuo commento...",
@@ -56,7 +60,6 @@
"Delete filter block": "Elimina blocco filtro",
"Add value": "Aggiungi valore",
"Remove array item": "Rimuovi elemento dall'array",
"Press <1>ESC</1> to lose focus": "Premi <1>ESC</1> per perdere il focus",
"Must provide protocol: <1>https://</1>": "Protocollo richiesto: <1>https://</1>",
"Must provide protocol: <1>http://</1> or <3>https://</3>": "Protocollo richiesto: <1>http://</1> o <3>https://</3>",
"CORS policy won't allow fetching resources served over http from https, use a <1>https://</1> domain": "La politica CORS non permette di caricare risorse tramite http da https, utilizza un dominio <1>https://</1>.",
@@ -96,6 +99,12 @@
"Save the JSON style to your computer.": "Salva lo style JSON nel tuo computer.",
"Save as": "Salva con nome",
"Create HTML": "Crea HTML",
"Key": "Chiave",
"Value": "Valore",
"Remove variable": "Rimuovi variabile",
"Global State Variables": "Variabili di stato globale",
"No global state variables defined. Add variables to create reusable values in your style.": "Nessuna variabile di stato globale definita. Aggiungi variabili per creare valori riutilizzabili nel tuo stile.",
"Add Variable": "Aggiungi variabile",
"Cancel": "Annulla",
"Open Style": "Apri stile",
"Open local Style": "Apri stile locale",
@@ -124,6 +133,7 @@
"Terrain exaggeration": "Esagerazione del terreno",
"Transition delay": "Ritardo della transizione",
"Transition duration": "Durate della transizione",
"Projection": "Proiezione",
"Open Layers (experimental)": "Apri livelli (sperimentale)",
"Shortcuts menu": "Scorciatoie del menu",
"Open modal": "Apri finestra modale",
@@ -196,7 +206,5 @@
"LocationIQ Access Token": "Token di accesso LocationIQ",
"Public access token for LocationIQ services.": "Token di accesso pubblico per i servizi LocationIQ.",
"Style Renderer": "Renderer dello stile",
"Choose the default Maputnik renderer for this style.": "Scegli il renderer predefinito di Maputnik per questo stile.",
"Language": "Lingua",
"Press <1>ESC</1> to lose focus of any input box": "Premi <1>ESC</1> per perdere il focus su qualsiasi campo di input."
"Choose the default Maputnik renderer for this style.": "Scegli il renderer predefinito di Maputnik per questo stile."
}

View File

@@ -31,11 +31,15 @@
"Maputnik on GitHub": "GitHubのMaputnik",
"Open": "開く",
"Save": "保存",
"Code Editor": "コードエディタ",
"Data Sources": "データソース",
"Style Settings": "スタイル設定",
"Global State": "グローバルステート",
"View": "表示",
"Color accessibility": "色のアクセシビリティ",
"Help": "ヘルプ",
"Close": "閉じる",
"Click to close the editor": "エディタを閉じるにはクリックしてください",
"Comments for the current layer. This is non-standard and not in the spec.": "現在のレイヤーのコメント。注意:この機能は標準ではないため、他のライブラリとの互換性状況はわかりません。",
"Comments": "コメント",
"Comment...": "コメントを書く",
@@ -56,7 +60,6 @@
"Delete filter block": "フィルタブロックを削除",
"Add value": "値を追加する",
"Remove array item": "配列の項目を削除",
"Press <1>ESC</1> to lose focus": "<1>ESC</1>を押すとフォーカスが外れます",
"Must provide protocol: <1>https://</1>": "プロトコルを指定してください: <1>https://</1>",
"Must provide protocol: <1>http://</1> or <3>https://</3>": "プロトコルを指定してください: <1>http://</1> または <3>https://</3>",
"CORS policy won't allow fetching resources served over http from https, use a <1>https://</1> domain": "CORS使用時は、http経由で提供されるリソースをhttpsから取得することはできません。<1>https://</1> ドメインを使用してください。",
@@ -96,6 +99,12 @@
"Save the JSON style to your computer.": "JSONスタイルをコンピュータに保存します。",
"Save as": "名前を付けて保存",
"Create HTML": "HTMLを作成",
"Key": "キー",
"Value": "値",
"Remove variable": "変数を削除",
"Global State Variables": "グローバルステート変数",
"No global state variables defined. Add variables to create reusable values in your style.": "グローバルステート変数が定義されていません。スタイルで再利用可能な値を作成するには、変数を追加してください。",
"Add Variable": "変数を追加",
"Cancel": "キャンセル",
"Open Style": "スタイルを開く",
"Open local Style": "ローカルスタイルを開く",
@@ -124,6 +133,7 @@
"Terrain exaggeration": "地形の誇張",
"Transition delay": "遷移遅延",
"Transition duration": "遷移期間",
"Projection": "投影",
"Open Layers (experimental)": "Open Layers (実験的)",
"Shortcuts menu": "ショートカットメニュー",
"Open modal": "モーダルを開く",
@@ -196,7 +206,5 @@
"LocationIQ Access Token": "LocationIQ アクセストークン",
"Public access token for LocationIQ services.": "LocationIQ サービス用のパブリックアクセストークン。",
"Style Renderer": "スタイルレンダラ",
"Choose the default Maputnik renderer for this style.": "このスタイルのデフォルトの Maputnik レンダラを選択してください",
"Language": "言語",
"Layer options": "レイヤー設定"
"Choose the default Maputnik renderer for this style.": "このスタイルのデフォルトの Maputnik レンダラを選択してください"
}

View File

@@ -31,11 +31,15 @@
"Maputnik on GitHub": "GitHub上的Maputnik",
"Open": "打开",
"Save": "保存",
"Code Editor": "代码编辑器",
"Data Sources": "数据源",
"Style Settings": "样式设置",
"Global State": "全局状态",
"View": "视图",
"Color accessibility": "颜色可访问性",
"Help": "帮助",
"Close": "关闭",
"Click to close the editor": "点击关闭编辑器",
"Comments for the current layer. This is non-standard and not in the spec.": "当前图层的注释。注意:这不是标准功能,可能与其他库不兼容。",
"Comments": "注释",
"Comment...": "写注释...",
@@ -56,7 +60,6 @@
"Delete filter block": "删除过滤器块",
"Add value": "添加值",
"Remove array item": "移除数组项目",
"Press <1>ESC</1> to lose focus": "按 <1>ESC</1> 键退出焦点",
"Must provide protocol: <1>https://</1>": "必须提供协议:<1>https://</1>",
"Must provide protocol: <1>http://</1> or <3>https://</3>": "必须提供协议:<1>http://</1> 或 <3>https://</3>",
"CORS policy won't allow fetching resources served over http from https, use a <1>https://</1> domain": "CORS 策略不允许从 https 访问通过 http 提供的资源,请使用 <1>https://</1> 域名。",
@@ -96,6 +99,12 @@
"Save the JSON style to your computer.": "将JSON样式保存到您的计算机。",
"Save as": "另存为",
"Create HTML": "创建HTML",
"Key": "键",
"Value": "值",
"Remove variable": "移除变量",
"Global State Variables": "全局状态变量",
"No global state variables defined. Add variables to create reusable values in your style.": "未定义全局状态变量。添加变量以在样式中创建可重用的值。",
"Add Variable": "添加变量",
"Cancel": "取消",
"Open Style": "打开样式",
"Open local Style": "打开本地样式",
@@ -124,6 +133,7 @@
"Terrain exaggeration": "地形夸张",
"Transition delay": "过渡延迟",
"Transition duration": "过渡持续时间",
"Projection": "投影",
"Open Layers (experimental)": "开放图层(实验性)",
"Shortcuts menu": "快捷方式菜单",
"Open modal": "打开模态框",
@@ -196,7 +206,5 @@
"LocationIQ Access Token": "LocationIQ 访问令牌",
"Public access token for LocationIQ services.": "LocationIQ 服务的公共访问令牌。",
"Style Renderer": "样式渲染器",
"Choose the default Maputnik renderer for this style.": "为这种样式选择默认的Maputnik渲染器。",
"Language": "语言",
"Layer options": "图层选项"
"Choose the default Maputnik renderer for this style.": "为这种样式选择默认的Maputnik渲染器。"
}

View File

@@ -40,6 +40,13 @@
position: relative;
}
&-code-editor {
width: 570px;
background-color: vars.$color-black;
// scroll-container is position: absolute
position: relative;
}
&-bottom {
position: fixed;
bottom: 0;

View File

@@ -43,7 +43,8 @@
.maputnik-toolbar-link {
vertical-align: top;
height: vars.$toolbar-height;
display: inline-block;
display: inline-flex;
align-items: center;
padding: vars.$margin-3;
font-size: vars.$font-size-5;
cursor: pointer;