mirror of
https://github.com/maputnik/editor.git
synced 2026-09-11 23:07:41 +00:00
Compare commits
42 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| b8267508c4 | |||
| 80c0d08179 | |||
| 1f475cf715 | |||
| 1301dc03e7 | |||
| 0a4c1710e2 | |||
| 1dc80b1313 | |||
| 139cf07991 | |||
| 8d400b73e3 | |||
| 6e1a0c19c3 | |||
| 4cf63c1573 | |||
| a16d38abba | |||
| ce171f1ce2 | |||
| fa5a88c658 | |||
| 636cdc6cc5 | |||
| 31cda1dbe7 | |||
| 7f83abf773 | |||
| 07d02058e8 | |||
| 858b0b5ac2 | |||
| 35585bbeef | |||
| c0f76dfff8 | |||
| 64b4e5a67c | |||
| fce879bfaa | |||
| 01bd751d5f | |||
| d1ddd91172 | |||
| 18e2d00bdc | |||
| d4a090722d | |||
| 36967426d8 | |||
| 57e5a446a0 | |||
| 9ac26b6fc9 | |||
| f8022d9b95 | |||
| 981e075e7c | |||
| e5cf62f3b5 | |||
| 380251c170 | |||
| 97e6c8cfff | |||
| 3543f0e372 | |||
| e76d948d3e | |||
| ceb2bb54cd | |||
| 32ed92b7ec | |||
| 3c9cf15bf0 | |||
| 2b7a092ad3 | |||
| 705233241f | |||
| 3c1c3b6a74 |
@@ -31,6 +31,10 @@ updates:
|
|||||||
directory: "/"
|
directory: "/"
|
||||||
schedule:
|
schedule:
|
||||||
interval: "daily"
|
interval: "daily"
|
||||||
|
groups:
|
||||||
|
codeql-action:
|
||||||
|
patterns:
|
||||||
|
- "github/codeql-action*"
|
||||||
cooldown:
|
cooldown:
|
||||||
default-days: 3
|
default-days: 3
|
||||||
# no semver support for github-actions
|
# no semver support for github-actions
|
||||||
|
|||||||
@@ -6,6 +6,10 @@ on:
|
|||||||
push:
|
push:
|
||||||
branches: [ main ]
|
branches: [ main ]
|
||||||
|
|
||||||
|
concurrency:
|
||||||
|
group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}
|
||||||
|
cancel-in-progress: ${{ github.event_name == 'pull_request' }}
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
|
|
||||||
build-node:
|
build-node:
|
||||||
|
|||||||
@@ -20,6 +20,10 @@ on:
|
|||||||
schedule:
|
schedule:
|
||||||
- cron: '17 0 * * 6'
|
- cron: '17 0 * * 6'
|
||||||
|
|
||||||
|
concurrency:
|
||||||
|
group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}
|
||||||
|
cancel-in-progress: ${{ github.event_name == 'pull_request' }}
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
analyze:
|
analyze:
|
||||||
name: Analyze
|
name: Analyze
|
||||||
@@ -42,7 +46,7 @@ jobs:
|
|||||||
|
|
||||||
# Initializes the CodeQL tools for scanning.
|
# Initializes the CodeQL tools for scanning.
|
||||||
- name: Initialize CodeQL
|
- name: Initialize CodeQL
|
||||||
uses: github/codeql-action/init@db488ddef3bf6cb639b32c2e9a7c0a7ea8271d28 # v4.37.8
|
uses: github/codeql-action/init@cdf488f595d80d6e07e03d4674febd5ab45fa938 # v4.37.9
|
||||||
with:
|
with:
|
||||||
languages: ${{ matrix.language }}
|
languages: ${{ matrix.language }}
|
||||||
# If you wish to specify custom queries, you can do so here or in a config file.
|
# If you wish to specify custom queries, you can do so here or in a config file.
|
||||||
@@ -53,7 +57,7 @@ jobs:
|
|||||||
# Autobuild attempts to build any compiled languages (C/C++, C#, or Java).
|
# Autobuild attempts to build any compiled languages (C/C++, C#, or Java).
|
||||||
# If this step fails, then you should remove it and run the build manually (see below)
|
# If this step fails, then you should remove it and run the build manually (see below)
|
||||||
- name: Autobuild
|
- name: Autobuild
|
||||||
uses: github/codeql-action/autobuild@db488ddef3bf6cb639b32c2e9a7c0a7ea8271d28 # v4.37.8
|
uses: github/codeql-action/autobuild@cdf488f595d80d6e07e03d4674febd5ab45fa938 # v4.37.9
|
||||||
|
|
||||||
# ℹ️ Command-line programs to run using the OS shell.
|
# ℹ️ Command-line programs to run using the OS shell.
|
||||||
# 📚 https://git.io/JvXDl
|
# 📚 https://git.io/JvXDl
|
||||||
@@ -67,4 +71,4 @@ jobs:
|
|||||||
# make release
|
# make release
|
||||||
|
|
||||||
- name: Perform CodeQL Analysis
|
- name: Perform CodeQL Analysis
|
||||||
uses: github/codeql-action/analyze@db488ddef3bf6cb639b32c2e9a7c0a7ea8271d28 # v4.37.8
|
uses: github/codeql-action/analyze@cdf488f595d80d6e07e03d4674febd5ab45fa938 # v4.37.9
|
||||||
|
|||||||
@@ -5,9 +5,13 @@
|
|||||||
- Upgrade to MapLibre GL JS 6 and Vite 8, and update the remaining dependencies
|
- Upgrade to MapLibre GL JS 6 and Vite 8, and update the remaining dependencies
|
||||||
- Serve the RTL text plugin from `@mapbox/mapbox-gl-rtl-text` instead of a pinned CDN URL, so its version is tracked in `package.json`
|
- Serve the RTL text plugin from `@mapbox/mapbox-gl-rtl-text` instead of a pinned CDN URL, so its version is tracked in `package.json`
|
||||||
- Exported HTML now loads MapLibre GL JS as an ES module, since v6 no longer ships a UMD bundle
|
- Exported HTML now loads MapLibre GL JS as an ES module, since v6 no longer ships a UMD bundle
|
||||||
|
- The sidebar can now be resized, both as a whole and in the split between the layer list and the layer editor
|
||||||
- _...Add new stuff here..._
|
- _...Add new stuff here..._
|
||||||
|
|
||||||
### 🐞 Bug fixes
|
### 🐞 Bug fixes
|
||||||
|
- Prevent a crash when converting a function to an expression
|
||||||
|
- Prevent a crash when deleting either stop from a function with two stops
|
||||||
|
- Preserve expanded layer groups when deleting layers, including the first layer of a group
|
||||||
- The map's data listener now fires on tile loads again, so source and vector layer field autocompletion is populated
|
- The map's data listener now fires on tile loads again, so source and vector layer field autocompletion is populated
|
||||||
- The `maputnik` desktop binary now opens the default browser automatically on startup (opt out with `--no-browser`)
|
- The `maputnik` desktop binary now opens the default browser automatically on startup (opt out with `--no-browser`)
|
||||||
- _...Add new stuff here..._
|
- _...Add new stuff here..._
|
||||||
|
|||||||
@@ -0,0 +1,16 @@
|
|||||||
|
{
|
||||||
|
"version": 8,
|
||||||
|
"name": "Grouped layers",
|
||||||
|
"sources": {},
|
||||||
|
"layers": [
|
||||||
|
{ "id": "background", "type": "background" },
|
||||||
|
{ "id": "park-a", "type": "background" },
|
||||||
|
{ "id": "park-b", "type": "background" },
|
||||||
|
{ "id": "park-c", "type": "background" },
|
||||||
|
{ "id": "road", "type": "background" },
|
||||||
|
{ "id": "park-d", "type": "background" },
|
||||||
|
{ "id": "park-e", "type": "background" },
|
||||||
|
{ "id": "water-a", "type": "background" },
|
||||||
|
{ "id": "water-b", "type": "background" }
|
||||||
|
]
|
||||||
|
}
|
||||||
@@ -289,6 +289,20 @@ describe("layer editor", () => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test("should leave a plain editable value after deleting a stop", async () => {
|
||||||
|
await when.setFunctionStopValue("circle-radius", "Output value", 1, "0");
|
||||||
|
await when.deleteFunctionStop("circle-radius");
|
||||||
|
await then(get.elementByTestId("spec-field:circle-radius")).shouldBeVisible();
|
||||||
|
await then(get.styleFromLocalStorage()).shouldDeepNestedInclude({
|
||||||
|
layers: [{ id, paint: { "circle-radius": 0 } }],
|
||||||
|
});
|
||||||
|
|
||||||
|
await when.setValue("spec-field-input:circle-radius", "7");
|
||||||
|
await then(get.styleFromLocalStorage()).shouldDeepNestedInclude({
|
||||||
|
layers: [{ id, paint: { "circle-radius": 7 } }],
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
test("should set the base", async () => {
|
test("should set the base", async () => {
|
||||||
await when.setFunctionBase("circle-radius", "2");
|
await when.setFunctionBase("circle-radius", "2");
|
||||||
await then(get.styleFromLocalStorage()).shouldDeepNestedInclude({
|
await then(get.styleFromLocalStorage()).shouldDeepNestedInclude({
|
||||||
@@ -310,8 +324,9 @@ describe("layer editor", () => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
test("should convert to an expression", async () => {
|
test("should convert to an expression without crashing", async () => {
|
||||||
await when.makeExpression("circle-radius");
|
await when.makeExpression("circle-radius");
|
||||||
|
await then(get.element("[data-wd-key='spec-field-container:circle-radius'] .maputnik-expression-editor")).shouldBeVisible();
|
||||||
await then(get.styleFromLocalStorage()).shouldDeepNestedInclude({
|
await then(get.styleFromLocalStorage()).shouldDeepNestedInclude({
|
||||||
layers: [{ id, paint: { "circle-radius": ["interpolate", ["linear"], ["zoom"], 6, 5, 10, 5] } }],
|
layers: [{ id, paint: { "circle-radius": ["interpolate", ["linear"], ["zoom"], 6, 5, 10, 5] } }],
|
||||||
});
|
});
|
||||||
|
|||||||
+78
-1
@@ -1,4 +1,4 @@
|
|||||||
import { beforeEach, describe, test } from "./utils/fixtures";
|
import { beforeEach, describe, expect, test } from "./utils/fixtures";
|
||||||
import { MaputnikDriver } from "./maputnik-driver";
|
import { MaputnikDriver } from "./maputnik-driver";
|
||||||
|
|
||||||
describe("layers list", () => {
|
describe("layers list", () => {
|
||||||
@@ -47,6 +47,14 @@ describe("layers list", () => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test("should title the visibility button with the action it performs", async () => {
|
||||||
|
const key = "layer-list-item:" + id + ":toggle-visibility";
|
||||||
|
// A visible layer is about to be hidden, and a hidden one to be shown.
|
||||||
|
await expect(get.elementByTestId(key)).toHaveAttribute("title", "hide");
|
||||||
|
await when.click(key);
|
||||||
|
await expect(get.elementByTestId(key)).toHaveAttribute("title", "show");
|
||||||
|
});
|
||||||
|
|
||||||
describe("when clicking hide", () => {
|
describe("when clicking hide", () => {
|
||||||
beforeEach(async () => {
|
beforeEach(async () => {
|
||||||
await when.click("layer-list-item:" + id + ":toggle-visibility");
|
await when.click("layer-list-item:" + id + ":toggle-visibility");
|
||||||
@@ -298,6 +306,75 @@ describe("layers list", () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
describe("groups", () => {
|
describe("groups", () => {
|
||||||
|
for (const control of ["menu", "trash"] as const) {
|
||||||
|
describe(`after deletion using ${control}`, () => {
|
||||||
|
beforeEach(async () => {
|
||||||
|
await when.setStyle("grouped_layers");
|
||||||
|
});
|
||||||
|
|
||||||
|
test("keeps all groups expanded when an earlier layer is deleted", async () => {
|
||||||
|
await when.click("skip-target-layer-list");
|
||||||
|
await then(get.elementByTestId("layer-list-item:park-b")).shouldBeVisible();
|
||||||
|
await when.deleteLayer("background", control);
|
||||||
|
await then(get.elementByTestId("layer-list-item:background")).shouldNotExist();
|
||||||
|
await then(get.elementByTestId("layer-list-group:park-0")).shouldExist();
|
||||||
|
await expect(get.elementByTestId("layer-list-group:water-6").getByRole("button")).toHaveAttribute("aria-expanded", "true");
|
||||||
|
await then(get.elementByTestId("layer-list-item:park-b")).shouldBeVisible();
|
||||||
|
await then(get.elementByTestId("layer-list-item:park-e")).shouldBeVisible();
|
||||||
|
await then(get.elementByTestId("layer-list-item:water-b")).shouldBeVisible();
|
||||||
|
});
|
||||||
|
|
||||||
|
test("preserves separate states for groups with the same prefix", async () => {
|
||||||
|
await when.click("layer-list-group:park-1");
|
||||||
|
await when.deleteLayer("background", control);
|
||||||
|
await then(get.elementByTestId("layer-list-group:park-0")).shouldExist();
|
||||||
|
await when.click("layer-list-item:road");
|
||||||
|
await expect(get.elementByTestId("layer-list-group:park-0").getByRole("button")).toHaveAttribute("aria-expanded", "true");
|
||||||
|
await expect(get.elementByTestId("layer-list-group:park-4").getByRole("button")).toHaveAttribute("aria-expanded", "false");
|
||||||
|
await then(get.elementByTestId("layer-list-item:park-b")).shouldBeVisible();
|
||||||
|
await then(get.elementByTestId("layer-list-item:park-e")).shouldNotBeVisible();
|
||||||
|
});
|
||||||
|
|
||||||
|
test("keeps a group expanded when its first layer is deleted", async () => {
|
||||||
|
await when.click("layer-list-group:park-1");
|
||||||
|
await when.deleteLayer("park-a", control);
|
||||||
|
await then(get.elementByTestId("layer-list-item:park-a")).shouldNotExist();
|
||||||
|
await when.click("layer-list-item:road");
|
||||||
|
await expect(get.elementByTestId("layer-list-group:park-1").getByRole("button")).toHaveAttribute("aria-expanded", "true");
|
||||||
|
await then(get.elementByTestId("layer-list-item:park-c")).shouldBeVisible();
|
||||||
|
});
|
||||||
|
|
||||||
|
for (const expandedGroup of ["park-1", "park-5"]) {
|
||||||
|
test(`keeps a merged group expanded when ${expandedGroup} was expanded`, async () => {
|
||||||
|
await when.click("layer-list-group:" + expandedGroup);
|
||||||
|
await when.deleteLayer("road", control);
|
||||||
|
await then(get.elementByTestId("layer-list-item:road")).shouldNotExist();
|
||||||
|
await expect(get.elementByTestId("layer-list-group:park-1").getByRole("button")).toHaveAttribute("aria-expanded", "true");
|
||||||
|
await then(get.elementByTestId("layer-list-item:park-b")).shouldBeVisible();
|
||||||
|
await then(get.elementByTestId("layer-list-item:park-e")).shouldBeVisible();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
test("keeps a merged group collapsed when both groups were collapsed", async () => {
|
||||||
|
await when.deleteLayer("road", control);
|
||||||
|
await then(get.elementByTestId("layer-list-item:road")).shouldNotExist();
|
||||||
|
await when.click("layer-list-item:background");
|
||||||
|
await expect(get.elementByTestId("layer-list-group:park-1").getByRole("button")).toHaveAttribute("aria-expanded", "false");
|
||||||
|
await then(get.elementByTestId("layer-list-item:park-b")).shouldNotBeVisible();
|
||||||
|
await then(get.elementByTestId("layer-list-item:park-e")).shouldNotBeVisible();
|
||||||
|
});
|
||||||
|
|
||||||
|
test("preserves later groups after deleting an entire group", async () => {
|
||||||
|
await when.click("skip-target-layer-list");
|
||||||
|
await when.deleteLayer("park-a", control);
|
||||||
|
await when.deleteLayer("park-b", control);
|
||||||
|
await when.deleteLayer("park-c", control);
|
||||||
|
await expect(get.elementByTestId("layer-list-group:water-4").getByRole("button")).toHaveAttribute("aria-expanded", "true");
|
||||||
|
await then(get.elementByTestId("layer-list-item:water-b")).shouldBeVisible();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
test("simple", async () => {
|
test("simple", async () => {
|
||||||
await when.setStyle("geojson");
|
await when.setStyle("geojson");
|
||||||
|
|
||||||
|
|||||||
@@ -39,6 +39,7 @@ export class MaputnikDriver {
|
|||||||
"example-style-with-zoom-7-and-center-0-51.json",
|
"example-style-with-zoom-7-and-center-0-51.json",
|
||||||
"example-style-with-zoom-5-and-center-50-50.json",
|
"example-style-with-zoom-5-and-center-50-50.json",
|
||||||
"access-token-style.json",
|
"access-token-style.json",
|
||||||
|
"grouped-layers-style.json",
|
||||||
];
|
];
|
||||||
for (const fixture of styleFixtures) {
|
for (const fixture of styleFixtures) {
|
||||||
await this.helper.given.interceptAndMockResponse({
|
await this.helper.given.interceptAndMockResponse({
|
||||||
@@ -63,6 +64,18 @@ export class MaputnikDriver {
|
|||||||
|
|
||||||
modal: this.modalDriver.when,
|
modal: this.modalDriver.when,
|
||||||
|
|
||||||
|
deleteLayer: async (id: string, control: "menu" | "trash" = "menu") => {
|
||||||
|
if (control === "trash") {
|
||||||
|
await this.helper.when.hover("layer-list-item:" + id);
|
||||||
|
await this.helper.when.click("layer-list-item:" + id + ":delete");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
await this.helper.when.click("layer-list-item:" + id);
|
||||||
|
await this.helper.when.click("skip-target-layer-editor");
|
||||||
|
await this.helper.when.click("menu-delete-layer");
|
||||||
|
},
|
||||||
|
|
||||||
setStyle: async (
|
setStyle: async (
|
||||||
styleProperties:
|
styleProperties:
|
||||||
| "geojson"
|
| "geojson"
|
||||||
@@ -73,6 +86,7 @@ export class MaputnikDriver {
|
|||||||
| "font"
|
| "font"
|
||||||
| "zoom_7_center_0_51"
|
| "zoom_7_center_0_51"
|
||||||
| "access_tokens"
|
| "access_tokens"
|
||||||
|
| "grouped_layers"
|
||||||
| "",
|
| "",
|
||||||
zoom?: number
|
zoom?: number
|
||||||
) => {
|
) => {
|
||||||
@@ -85,6 +99,7 @@ export class MaputnikDriver {
|
|||||||
font: "example-style-with-fonts.json",
|
font: "example-style-with-fonts.json",
|
||||||
zoom_7_center_0_51: "example-style-with-zoom-7-and-center-0-51.json",
|
zoom_7_center_0_51: "example-style-with-zoom-7-and-center-0-51.json",
|
||||||
access_tokens: "access-token-style.json",
|
access_tokens: "access-token-style.json",
|
||||||
|
grouped_layers: "grouped-layers-style.json",
|
||||||
};
|
};
|
||||||
|
|
||||||
const url = new URL(baseUrl);
|
const url = new URL(baseUrl);
|
||||||
|
|||||||
@@ -122,6 +122,8 @@ export class Assertable<T> {
|
|||||||
// Value assertions (auto-retrying for Query targets).
|
// Value assertions (auto-retrying for Query targets).
|
||||||
shouldEqual = (value: any) => this.assertValue((actual) => expect(actual).toBe(value));
|
shouldEqual = (value: any) => this.assertValue((actual) => expect(actual).toBe(value));
|
||||||
|
|
||||||
|
shouldBeGreaterThan = (value: number) => this.assertValue((actual) => expect(actual).toBeGreaterThan(value));
|
||||||
|
|
||||||
shouldInclude = (value: any) =>
|
shouldInclude = (value: any) =>
|
||||||
this.assertValue((actual) => {
|
this.assertValue((actual) => {
|
||||||
if (typeof value === "object" && value !== null) {
|
if (typeof value === "object" && value !== null) {
|
||||||
@@ -349,6 +351,15 @@ export class PlaywrightHelper {
|
|||||||
await this.page.mouse.up();
|
await this.page.mouse.up();
|
||||||
},
|
},
|
||||||
|
|
||||||
|
/** Presses at the centre of an element and drags it by the given offset. */
|
||||||
|
dragBy: async (testId: string, deltaX: number, deltaY = 0) => {
|
||||||
|
const { x, y } = await centerOf(this.testId(testId));
|
||||||
|
await this.page.mouse.move(x, y);
|
||||||
|
await this.page.mouse.down();
|
||||||
|
await this.page.mouse.move(x + deltaX, y + deltaY, { steps: 10 });
|
||||||
|
await this.page.mouse.up();
|
||||||
|
},
|
||||||
|
|
||||||
clickCenter: async (testId: string) => {
|
clickCenter: async (testId: string) => {
|
||||||
const { x, y } = await centerOf(this.testId(testId));
|
const { x, y } = await centerOf(this.testId(testId));
|
||||||
await this.page.mouse.move(x, y);
|
await this.page.mouse.move(x, y);
|
||||||
@@ -446,6 +457,13 @@ export class PlaywrightHelper {
|
|||||||
|
|
||||||
inputValue: (testId: string) => new Query<string>(() => this.testId(testId).first().inputValue()),
|
inputValue: (testId: string) => new Query<string>(() => this.testId(testId).first().inputValue()),
|
||||||
|
|
||||||
|
elementWidth: (testId: string) =>
|
||||||
|
new Query<number>(async () => {
|
||||||
|
const box = await this.testId(testId).first().boundingBox();
|
||||||
|
if (!box) throw new Error(`Element "${testId}" has no bounding box`);
|
||||||
|
return box.width;
|
||||||
|
}),
|
||||||
|
|
||||||
elementsText: (testId: string) => new Query<string>(() => this.testId(testId).first().innerText()),
|
elementsText: (testId: string) => new Query<string>(() => this.testId(testId).first().innerText()),
|
||||||
|
|
||||||
locationHash: () => new Query<string>(async () => new URL(this.page.url()).hash),
|
locationHash: () => new Query<string>(async () => new URL(this.page.url()).hash),
|
||||||
|
|||||||
@@ -0,0 +1,27 @@
|
|||||||
|
import { beforeEach, describe, test } from "./utils/fixtures";
|
||||||
|
import { MaputnikDriver } from "./maputnik-driver";
|
||||||
|
|
||||||
|
describe("sidebar resize", () => {
|
||||||
|
const { given, get, when, then } = new MaputnikDriver();
|
||||||
|
|
||||||
|
beforeEach(async () => {
|
||||||
|
await given.setupMockBackedResponses();
|
||||||
|
await when.setStyle("layer");
|
||||||
|
});
|
||||||
|
|
||||||
|
test("dragging the outer handle widens the sidebar", async () => {
|
||||||
|
const initialWidth = await get.elementWidth("sidebar-panel").get();
|
||||||
|
|
||||||
|
await when.dragBy("sidebar-resize-handle", 100);
|
||||||
|
|
||||||
|
await then(get.elementWidth("sidebar-panel")).shouldBeGreaterThan(initialWidth + 50);
|
||||||
|
});
|
||||||
|
|
||||||
|
test("dragging the inner handle widens the layer list", async () => {
|
||||||
|
const initialWidth = await get.elementWidth("layer-list-panel").get();
|
||||||
|
|
||||||
|
await when.dragBy("inner-resize-handle", 50);
|
||||||
|
|
||||||
|
await then(get.elementWidth("layer-list-panel")).shouldBeGreaterThan(initialWidth + 20);
|
||||||
|
});
|
||||||
|
});
|
||||||
+1
-1
@@ -1,7 +1,7 @@
|
|||||||
import { defineConfig } from "i18next-cli";
|
import { defineConfig } from "i18next-cli";
|
||||||
|
|
||||||
export default defineConfig({
|
export default defineConfig({
|
||||||
locales: ["de", "fr", "he", "it", "ja", "ko", "tr", "zh"],
|
locales: ["az", "de", "fr", "he", "it", "ja", "ko", "tr", "zh"],
|
||||||
extract: {
|
extract: {
|
||||||
input: ["src/**/*.{js,jsx,ts,tsx}"],
|
input: ["src/**/*.{js,jsx,ts,tsx}"],
|
||||||
output: "src/locales/{{language}}/{{namespace}}.json",
|
output: "src/locales/{{language}}/{{namespace}}.json",
|
||||||
|
|||||||
Generated
+405
-546
File diff suppressed because it is too large
Load Diff
+19
-18
@@ -29,16 +29,16 @@
|
|||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@codemirror/lang-json": "^6.0.2",
|
"@codemirror/lang-json": "^6.0.2",
|
||||||
"@codemirror/lint": "^6.9.7",
|
"@codemirror/lint": "^6.9.7",
|
||||||
"@codemirror/state": "^6.7.1",
|
"@codemirror/state": "^6.7.4",
|
||||||
"@codemirror/theme-one-dark": "^6.1.3",
|
"@codemirror/theme-one-dark": "^6.1.3",
|
||||||
"@codemirror/view": "^6.43.9",
|
"@codemirror/view": "^6.43.11",
|
||||||
"@dnd-kit/core": "^6.3.1",
|
"@dnd-kit/core": "^6.3.1",
|
||||||
"@dnd-kit/sortable": "^10.0.0",
|
"@dnd-kit/sortable": "^10.0.0",
|
||||||
"@dnd-kit/utilities": "^3.2.2",
|
"@dnd-kit/utilities": "^3.2.2",
|
||||||
"@mapbox/mapbox-gl-rtl-text": "^0.4.0",
|
"@mapbox/mapbox-gl-rtl-text": "^0.4.0",
|
||||||
"@maplibre/maplibre-gl-geocoder": "^1.9.4",
|
"@maplibre/maplibre-gl-geocoder": "^1.9.4",
|
||||||
"@maplibre/maplibre-gl-inspect": "^1.9.0",
|
"@maplibre/maplibre-gl-inspect": "^1.9.0",
|
||||||
"@maplibre/maplibre-gl-style-spec": "^26.4.1",
|
"@maplibre/maplibre-gl-style-spec": "^26.4.2",
|
||||||
"array-move": "^4.0.0",
|
"array-move": "^4.0.0",
|
||||||
"buffer": "^6.0.3",
|
"buffer": "^6.0.3",
|
||||||
"classnames": "^2.5.1",
|
"classnames": "^2.5.1",
|
||||||
@@ -48,7 +48,7 @@
|
|||||||
"downshift": "^9.4.0",
|
"downshift": "^9.4.0",
|
||||||
"events": "^3.3.0",
|
"events": "^3.3.0",
|
||||||
"file-saver": "^2.0.5",
|
"file-saver": "^2.0.5",
|
||||||
"i18next": "^26.4.0",
|
"i18next": "^26.4.2",
|
||||||
"i18next-browser-languagedetector": "^8.2.1",
|
"i18next-browser-languagedetector": "^8.2.1",
|
||||||
"i18next-resources-to-backend": "^1.2.3",
|
"i18next-resources-to-backend": "^1.2.3",
|
||||||
"json-stringify-pretty-compact": "^4.0.0",
|
"json-stringify-pretty-compact": "^4.0.0",
|
||||||
@@ -60,7 +60,7 @@
|
|||||||
"lodash.get": "^4.4.2",
|
"lodash.get": "^4.4.2",
|
||||||
"lodash.isequal": "^4.5.0",
|
"lodash.isequal": "^4.5.0",
|
||||||
"lodash.throttle": "^4.1.1",
|
"lodash.throttle": "^4.1.1",
|
||||||
"maplibre-gl": "^6.6.0",
|
"maplibre-gl": "^6.7.0",
|
||||||
"maputnik-design": "github:maputnik/design#172b06c",
|
"maputnik-design": "github:maputnik/design#172b06c",
|
||||||
"ol": "^10.10.0",
|
"ol": "^10.10.0",
|
||||||
"ol-mapbox-style": "^13.4.3",
|
"ol-mapbox-style": "^13.4.3",
|
||||||
@@ -73,9 +73,10 @@
|
|||||||
"react-collapse": "^5.1.1",
|
"react-collapse": "^5.1.1",
|
||||||
"react-color": "^2.19.3",
|
"react-color": "^2.19.3",
|
||||||
"react-dom": "^19.2.8",
|
"react-dom": "^19.2.8",
|
||||||
"react-i18next": "^17.0.12",
|
"react-i18next": "^17.0.13",
|
||||||
"react-icons": "^5.7.0",
|
"react-icons": "^5.7.0",
|
||||||
"react-markdown": "^10.1.0",
|
"react-markdown": "^10.1.0",
|
||||||
|
"react-resizable-panels": "^4.11.0",
|
||||||
"reconnecting-websocket": "^4.4.0",
|
"reconnecting-websocket": "^4.4.0",
|
||||||
"slugify": "^1.6.9",
|
"slugify": "^1.6.9",
|
||||||
"string-hash": "^1.1.3",
|
"string-hash": "^1.1.3",
|
||||||
@@ -101,7 +102,7 @@
|
|||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@eslint/js": "^10.0.1",
|
"@eslint/js": "^10.0.1",
|
||||||
"@istanbuljs/nyc-config-typescript": "^1.0.2",
|
"@istanbuljs/nyc-config-typescript": "^1.0.2",
|
||||||
"@playwright/test": "^1.62.1",
|
"@playwright/test": "^1.63.0",
|
||||||
"@stylistic/eslint-plugin": "^5.10.0",
|
"@stylistic/eslint-plugin": "^5.10.0",
|
||||||
"@types/codemirror": "^5.60.18",
|
"@types/codemirror": "^5.60.18",
|
||||||
"@types/color": "^4.2.1",
|
"@types/color": "^4.2.1",
|
||||||
@@ -121,31 +122,31 @@
|
|||||||
"@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.2.5",
|
"@types/react-dom": "^19.2.7",
|
||||||
"@types/string-hash": "^1.1.3",
|
"@types/string-hash": "^1.1.3",
|
||||||
"@types/wicg-file-system-access": "^2023.10.7",
|
"@types/wicg-file-system-access": "^2023.10.7",
|
||||||
"@vitejs/plugin-react": "^6.1.0",
|
"@vitejs/plugin-react": "^6.1.1",
|
||||||
"@vitest/coverage-v8": "^4.1.11",
|
"@vitest/coverage-v8": "^5.0.0",
|
||||||
"cors": "^2.8.6",
|
"cors": "^2.8.6",
|
||||||
"eslint": "^10.9.1",
|
"eslint": "^10.10.0",
|
||||||
"eslint-plugin-react": "^7.37.5",
|
"eslint-plugin-react": "^7.37.5",
|
||||||
"eslint-plugin-react-hooks": "^7.1.1",
|
"eslint-plugin-react-hooks": "^7.1.1",
|
||||||
"eslint-plugin-react-refresh": "^0.5.4",
|
"eslint-plugin-react-refresh": "^0.5.6",
|
||||||
"i18next-cli": "^1.71.2",
|
"i18next-cli": "^1.73.1",
|
||||||
"istanbul": "^0.4.5",
|
"istanbul": "^0.4.5",
|
||||||
"istanbul-lib-coverage": "^3.2.2",
|
"istanbul-lib-coverage": "^3.2.2",
|
||||||
"nyc": "^18.0.0",
|
"nyc": "^18.0.0",
|
||||||
"postcss": "^8.5.26",
|
"postcss": "^8.5.28",
|
||||||
"react-hot-loader": "^4.13.1",
|
"react-hot-loader": "^4.13.1",
|
||||||
"sass": "^1.103.1",
|
"sass": "^1.104.0",
|
||||||
"stylelint": "^17.14.1",
|
"stylelint": "^17.15.0",
|
||||||
"stylelint-config-recommended-scss": "^17.0.1",
|
"stylelint-config-recommended-scss": "^17.0.1",
|
||||||
"stylelint-scss": "^7.2.0",
|
"stylelint-scss": "^7.2.0",
|
||||||
"typescript": "^6.0.3",
|
"typescript": "^6.0.3",
|
||||||
"typescript-eslint": "^8.68.0",
|
"typescript-eslint": "^8.69.0",
|
||||||
"uuid": "^14.0.2",
|
"uuid": "^14.0.2",
|
||||||
"vite": "^8.2.2",
|
"vite": "^8.2.2",
|
||||||
"vite-plugin-istanbul": "^9.0.1",
|
"vite-plugin-istanbul": "^9.0.1",
|
||||||
"vitest": "^4.1.11"
|
"vitest": "^5.0.0"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+117
-38
@@ -1,9 +1,23 @@
|
|||||||
import React from "react";
|
import React, { useEffect, useState } from "react";
|
||||||
|
import { Group, Panel, Separator, useDefaultLayout } from "react-resizable-panels";
|
||||||
import { ScrollContainer } from "./ScrollContainer";
|
import { ScrollContainer } from "./ScrollContainer";
|
||||||
import { type WithTranslation, withTranslation } from "react-i18next";
|
import { useTranslation } from "react-i18next";
|
||||||
import { IconContext } from "react-icons";
|
import { IconContext } from "react-icons";
|
||||||
|
|
||||||
type AppLayoutInternalProps = {
|
// Keep these in sync with $layout-list-width/$layout-editor-width in _vars.scss
|
||||||
|
const DEFAULT_LIST_WIDTH = 200;
|
||||||
|
const DEFAULT_DRAWER_WIDTH = 370;
|
||||||
|
const DEFAULT_SIDEBAR_WIDTH = DEFAULT_LIST_WIDTH + DEFAULT_DRAWER_WIDTH;
|
||||||
|
const PANEL_STYLE: React.CSSProperties = { overflow: "hidden" };
|
||||||
|
|
||||||
|
const SIDEBAR_LAYOUT_ID = "maputnik:sidebar-layout";
|
||||||
|
const SIDEBAR_INNER_LAYOUT_ID = "maputnik:sidebar-inner-layout";
|
||||||
|
const SIDEBAR_PANEL_ID = "sidebar";
|
||||||
|
const MAP_PANEL_ID = "map";
|
||||||
|
const LIST_PANEL_ID = "list";
|
||||||
|
const DRAWER_PANEL_ID = "drawer";
|
||||||
|
|
||||||
|
type AppLayoutProps = {
|
||||||
toolbar: React.ReactElement
|
toolbar: React.ReactElement
|
||||||
layerList: React.ReactElement
|
layerList: React.ReactElement
|
||||||
layerEditor?: React.ReactElement
|
layerEditor?: React.ReactElement
|
||||||
@@ -11,43 +25,108 @@ type AppLayoutInternalProps = {
|
|||||||
map: React.ReactElement
|
map: React.ReactElement
|
||||||
bottom?: React.ReactElement
|
bottom?: React.ReactElement
|
||||||
modals?: React.ReactNode
|
modals?: React.ReactNode
|
||||||
} & WithTranslation;
|
};
|
||||||
|
|
||||||
class AppLayoutInternal extends React.Component<AppLayoutInternalProps> {
|
export const AppLayout: React.FC<AppLayoutProps> = (props) => {
|
||||||
|
const { t, i18n } = useTranslation();
|
||||||
|
|
||||||
render() {
|
const sidebarLayout = useDefaultLayout({
|
||||||
document.body.dir = this.props.i18n.dir();
|
id: SIDEBAR_LAYOUT_ID,
|
||||||
|
panelIds: [SIDEBAR_PANEL_ID, MAP_PANEL_ID],
|
||||||
|
});
|
||||||
|
const innerLayout = useDefaultLayout({
|
||||||
|
id: SIDEBAR_INNER_LAYOUT_ID,
|
||||||
|
panelIds: [LIST_PANEL_ID, DRAWER_PANEL_ID],
|
||||||
|
});
|
||||||
|
|
||||||
return <IconContext.Provider value={{size: "14px"}}>
|
// The bottom panel is position: fixed, so it can't be a flex sibling of the
|
||||||
<div className="maputnik-layout">
|
// map panel; it follows the sidebar through this custom property instead.
|
||||||
{this.props.toolbar}
|
const [sidebarWidth, setSidebarWidth] = useState(DEFAULT_SIDEBAR_WIDTH);
|
||||||
<div className="maputnik-layout-main">
|
|
||||||
{this.props.codeEditor && <div className="maputnik-layout-code-editor">
|
useEffect(() => {
|
||||||
<ScrollContainer>
|
document.body.dir = i18n.dir();
|
||||||
{this.props.codeEditor}
|
}, [i18n, i18n.language]);
|
||||||
|
|
||||||
|
return <IconContext.Provider value={{size: "14px"}}>
|
||||||
|
<div
|
||||||
|
className="maputnik-layout"
|
||||||
|
style={{"--sidebar-width": `${sidebarWidth}px`} as React.CSSProperties}
|
||||||
|
>
|
||||||
|
{props.toolbar}
|
||||||
|
<div className="maputnik-layout-main">
|
||||||
|
<Group
|
||||||
|
className="maputnik-layout-panels"
|
||||||
|
orientation="horizontal"
|
||||||
|
id={SIDEBAR_LAYOUT_ID}
|
||||||
|
defaultLayout={sidebarLayout.defaultLayout}
|
||||||
|
onLayoutChanged={sidebarLayout.onLayoutChanged}
|
||||||
|
>
|
||||||
|
<Panel
|
||||||
|
id={SIDEBAR_PANEL_ID}
|
||||||
|
data-wd-key="sidebar-panel"
|
||||||
|
className={props.codeEditor ? "maputnik-layout-code-editor" : "maputnik-layout-sidebar"}
|
||||||
|
style={PANEL_STYLE}
|
||||||
|
defaultSize={`${DEFAULT_SIDEBAR_WIDTH}px`}
|
||||||
|
minSize="280px"
|
||||||
|
onResize={({inPixels}) => setSidebarWidth(inPixels)}
|
||||||
|
>
|
||||||
|
{props.codeEditor && <ScrollContainer>
|
||||||
|
{props.codeEditor}
|
||||||
</ScrollContainer>
|
</ScrollContainer>
|
||||||
</div>
|
}
|
||||||
}
|
{!props.codeEditor && <Group
|
||||||
{!this.props.codeEditor && <>
|
className="maputnik-layout-sidebar-panels"
|
||||||
<div className="maputnik-layout-list">
|
orientation="horizontal"
|
||||||
{this.props.layerList}
|
id={SIDEBAR_INNER_LAYOUT_ID}
|
||||||
</div>
|
defaultLayout={innerLayout.defaultLayout}
|
||||||
<div className="maputnik-layout-drawer">
|
onLayoutChanged={innerLayout.onLayoutChanged}
|
||||||
<ScrollContainer>
|
>
|
||||||
{this.props.layerEditor}
|
<Panel
|
||||||
</ScrollContainer>
|
id={LIST_PANEL_ID}
|
||||||
</div>
|
data-wd-key="layer-list-panel"
|
||||||
</>}
|
className="maputnik-layout-list"
|
||||||
{this.props.map}
|
style={PANEL_STYLE}
|
||||||
</div>
|
defaultSize={`${DEFAULT_LIST_WIDTH}px`}
|
||||||
{this.props.bottom && <div className="maputnik-layout-bottom">
|
minSize="100px"
|
||||||
{this.props.bottom}
|
>
|
||||||
</div>
|
{props.layerList}
|
||||||
}
|
</Panel>
|
||||||
{this.props.modals}
|
<Separator
|
||||||
|
className="maputnik-layout-resize-handle"
|
||||||
|
data-wd-key="inner-resize-handle"
|
||||||
|
title={t("Drag to resize the layer list")}
|
||||||
|
aria-label={t("Drag to resize the layer list")}
|
||||||
|
/>
|
||||||
|
<Panel
|
||||||
|
id={DRAWER_PANEL_ID}
|
||||||
|
className="maputnik-layout-drawer"
|
||||||
|
style={PANEL_STYLE}
|
||||||
|
defaultSize={`${DEFAULT_DRAWER_WIDTH}px`}
|
||||||
|
minSize="150px"
|
||||||
|
>
|
||||||
|
<ScrollContainer>
|
||||||
|
{props.layerEditor}
|
||||||
|
</ScrollContainer>
|
||||||
|
</Panel>
|
||||||
|
</Group>
|
||||||
|
}
|
||||||
|
</Panel>
|
||||||
|
<Separator
|
||||||
|
className="maputnik-layout-resize-handle"
|
||||||
|
data-wd-key="sidebar-resize-handle"
|
||||||
|
title={t("Drag to resize the sidebar")}
|
||||||
|
aria-label={t("Drag to resize the sidebar")}
|
||||||
|
/>
|
||||||
|
<Panel id={MAP_PANEL_ID} className="maputnik-layout-map" style={PANEL_STYLE} minSize="200px">
|
||||||
|
{props.map}
|
||||||
|
</Panel>
|
||||||
|
</Group>
|
||||||
</div>
|
</div>
|
||||||
</IconContext.Provider>;
|
{props.bottom && <div className="maputnik-layout-bottom">
|
||||||
}
|
{props.bottom}
|
||||||
}
|
</div>
|
||||||
|
}
|
||||||
export const AppLayout = withTranslation()(AppLayoutInternal);
|
{props.modals}
|
||||||
|
</div>
|
||||||
|
</IconContext.Provider>;
|
||||||
|
};
|
||||||
|
|||||||
@@ -129,16 +129,10 @@ type FieldFunctionProps = {
|
|||||||
* https://www.mapbox.com/mapbox-gl-style-spec/#types-function-zoom-property
|
* https://www.mapbox.com/mapbox-gl-style-spec/#types-function-zoom-property
|
||||||
*/
|
*/
|
||||||
export const FieldFunction: React.FC<FieldFunctionProps> = (props) => {
|
export const FieldFunction: React.FC<FieldFunctionProps> = (props) => {
|
||||||
const [dataType, setDataType] = React.useState(
|
|
||||||
getDataType(props.value, props.fieldSpec)
|
|
||||||
);
|
|
||||||
const [isEditing, setIsEditing] = React.useState(false);
|
const [isEditing, setIsEditing] = React.useState(false);
|
||||||
|
// Keep the expression editor mounted while typing, but otherwise select the
|
||||||
React.useEffect(() => {
|
// editor from the current value so a collapsed function never renders as stops.
|
||||||
if (!isEditing) {
|
const dataType = isEditing ? "expression" : getDataType(props.value, props.fieldSpec);
|
||||||
setDataType(getDataType(props.value, props.fieldSpec));
|
|
||||||
}
|
|
||||||
}, [props.value, props.fieldSpec, isEditing]);
|
|
||||||
|
|
||||||
const getFieldFunctionType = (fieldSpec: any) => {
|
const getFieldFunctionType = (fieldSpec: any) => {
|
||||||
if (fieldSpec.expression.interpolated) {
|
if (fieldSpec.expression.interpolated) {
|
||||||
@@ -173,7 +167,7 @@ export const FieldFunction: React.FC<FieldFunctionProps> = (props) => {
|
|||||||
const deleteExpression = () => {
|
const deleteExpression = () => {
|
||||||
const { fieldSpec, fieldName } = props;
|
const { fieldSpec, fieldName } = props;
|
||||||
props.onChange(fieldName, fieldSpec.default);
|
props.onChange(fieldName, fieldSpec.default);
|
||||||
setDataType("value");
|
setIsEditing(false);
|
||||||
};
|
};
|
||||||
|
|
||||||
const deleteStop = (stopIdx: number) => {
|
const deleteStop = (stopIdx: number) => {
|
||||||
@@ -233,10 +227,10 @@ export const FieldFunction: React.FC<FieldFunctionProps> = (props) => {
|
|||||||
type: "identity",
|
type: "identity",
|
||||||
property: value[1],
|
property: value[1],
|
||||||
});
|
});
|
||||||
setDataType("value");
|
setIsEditing(false);
|
||||||
} else if (isLiteralExpression(value)) {
|
} else if (isLiteralExpression(value)) {
|
||||||
props.onChange(fieldName, value[1]);
|
props.onChange(fieldName, value[1]);
|
||||||
setDataType("value");
|
setIsEditing(false);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -106,13 +106,13 @@ class LayerListContainerInternal extends React.Component<LayerListContainerInter
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
groupedLayers(): (LayerSpecification & {key: string})[][] {
|
groupedLayers(layers = this.props.layers): (LayerSpecification & {key: string})[][] {
|
||||||
const groups = [];
|
const groups = [];
|
||||||
const layerIdCount = new Map();
|
const layerIdCount = new Map();
|
||||||
|
|
||||||
for (let i = 0; i < this.props.layers.length; i++) {
|
for (let i = 0; i < layers.length; i++) {
|
||||||
const origLayer = this.props.layers[i];
|
const origLayer = layers[i];
|
||||||
const previousLayer = this.props.layers[i-1];
|
const previousLayer = layers[i-1];
|
||||||
layerIdCount.set(origLayer.id,
|
layerIdCount.set(origLayer.id,
|
||||||
layerIdCount.has(origLayer.id) ? layerIdCount.get(origLayer.id) + 1 : 0
|
layerIdCount.has(origLayer.id) ? layerIdCount.get(origLayer.id) + 1 : 0
|
||||||
);
|
);
|
||||||
@@ -192,7 +192,39 @@ class LayerListContainerInternal extends React.Component<LayerListContainerInter
|
|||||||
return propsChanged;
|
return propsChanged;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Preserves group state through surviving layer IDs when group indices change.
|
||||||
|
* If deletion joins groups, the result stays expanded when either group was.
|
||||||
|
*/
|
||||||
|
private preserveLayerGroupState(previousLayers: LayerSpecification[]) {
|
||||||
|
if (previousLayers === this.props.layers) return;
|
||||||
|
|
||||||
|
const collapsedByLayer = new Map<string, boolean>();
|
||||||
|
let idx = 0;
|
||||||
|
for (const layers of this.groupedLayers(previousLayers)) {
|
||||||
|
const collapsed = this.isCollapsed(layerPrefix(layers[0].id), idx);
|
||||||
|
for (const layer of layers) {
|
||||||
|
collapsedByLayer.set(layer.id, collapsed);
|
||||||
|
}
|
||||||
|
idx += layers.length;
|
||||||
|
}
|
||||||
|
|
||||||
|
const collapsedGroups: {[key: string]: boolean} = {};
|
||||||
|
idx = 0;
|
||||||
|
for (const layers of this.groupedLayers()) {
|
||||||
|
const lookupKey = [layerPrefix(layers[0].id), idx].join("-");
|
||||||
|
collapsedGroups[lookupKey] = layers.every(layer => collapsedByLayer.get(layer.id) !== false);
|
||||||
|
idx += layers.length;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!lodash.isEqual(collapsedGroups, this.state.collapsedGroups)) {
|
||||||
|
this.setState({ collapsedGroups });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
componentDidUpdate (prevProps: LayerListContainerProps) {
|
componentDidUpdate (prevProps: LayerListContainerProps) {
|
||||||
|
this.preserveLayerGroupState(prevProps.layers);
|
||||||
|
|
||||||
if (prevProps.selectedLayerIndex !== this.props.selectedLayerIndex) {
|
if (prevProps.selectedLayerIndex !== this.props.selectedLayerIndex) {
|
||||||
const selectedItemNode = this.selectedItemRef.current;
|
const selectedItemNode = this.selectedItemRef.current;
|
||||||
if (selectedItemNode && selectedItemNode.node) {
|
if (selectedItemNode && selectedItemNode.node) {
|
||||||
|
|||||||
@@ -32,6 +32,8 @@ const DraggableLabel: React.FC<DraggableLabelProps> = (props) => {
|
|||||||
|
|
||||||
type IconActionProps = {
|
type IconActionProps = {
|
||||||
action: string
|
action: string
|
||||||
|
/** Tooltip text, for buttons whose action reads differently from their icon. */
|
||||||
|
title?: string
|
||||||
onClick(...args: unknown[]): unknown
|
onClick(...args: unknown[]): unknown
|
||||||
wdKey?: string
|
wdKey?: string
|
||||||
classBlockName?: string
|
classBlockName?: string
|
||||||
@@ -62,7 +64,7 @@ class IconAction extends React.Component<IconActionProps> {
|
|||||||
|
|
||||||
return <button
|
return <button
|
||||||
tabIndex={-1}
|
tabIndex={-1}
|
||||||
title={this.props.action}
|
title={this.props.title ?? this.props.action}
|
||||||
className={`maputnik-layer-list-icon-action ${classAdditions}`}
|
className={`maputnik-layer-list-icon-action ${classAdditions}`}
|
||||||
data-wd-key={this.props.wdKey}
|
data-wd-key={this.props.wdKey}
|
||||||
onClick={this.props.onClick}
|
onClick={this.props.onClick}
|
||||||
@@ -111,7 +113,12 @@ export const LayerListItem = React.forwardRef<HTMLLIElement, LayerListItemProps>
|
|||||||
opacity: isDragging ? 0.5 : 1,
|
opacity: isDragging ? 0.5 : 1,
|
||||||
};
|
};
|
||||||
|
|
||||||
const visibilityAction = visibility === "visible" ? "show" : "hide";
|
// The icon and the CSS modifier describe the layer's current visibility,
|
||||||
|
// while the tooltip has to describe what clicking the button does, which is
|
||||||
|
// the opposite of it.
|
||||||
|
const isVisible = visibility === "visible";
|
||||||
|
const visibilityAction = isVisible ? "show" : "hide";
|
||||||
|
const visibilityTitle = isVisible ? "hide" : "show";
|
||||||
|
|
||||||
// Cast ref to MutableRefObject since we know from the codebase that's what's always passed
|
// Cast ref to MutableRefObject since we know from the codebase that's what's always passed
|
||||||
const refObject = ref as React.MutableRefObject<HTMLLIElement | null> | null;
|
const refObject = ref as React.MutableRefObject<HTMLLIElement | null> | null;
|
||||||
@@ -155,6 +162,7 @@ export const LayerListItem = React.forwardRef<HTMLLIElement, LayerListItemProps>
|
|||||||
<IconAction
|
<IconAction
|
||||||
wdKey={"layer-list-item:" + props.layerId + ":toggle-visibility"}
|
wdKey={"layer-list-item:" + props.layerId + ":toggle-visibility"}
|
||||||
action={visibilityAction}
|
action={visibilityAction}
|
||||||
|
title={visibilityTitle}
|
||||||
classBlockName="visibility"
|
classBlockName="visibility"
|
||||||
classBlockModifier={visibilityAction}
|
classBlockModifier={visibilityAction}
|
||||||
onClick={_e => onLayerVisibilityToggle!(props.layerIndex)}
|
onClick={_e => onLayerVisibilityToggle!(props.layerIndex)}
|
||||||
|
|||||||
@@ -322,7 +322,7 @@ class ModalSettingsInternal extends React.Component<ModalSettingsInternalProps>
|
|||||||
data-wd-key="modal:settings.maputnik:renderer"
|
data-wd-key="modal:settings.maputnik:renderer"
|
||||||
options={[
|
options={[
|
||||||
["mlgljs", "MapLibreGL JS"],
|
["mlgljs", "MapLibreGL JS"],
|
||||||
["ol", t("Open Layers (experimental)")],
|
["ol", t("OpenLayers (experimental)")],
|
||||||
]}
|
]}
|
||||||
value={metadata["maputnik:renderer"] || "mlgljs"}
|
value={metadata["maputnik:renderer"] || "mlgljs"}
|
||||||
onChange={(value) => onChangeMetadataProperty("maputnik:renderer", value)}
|
onChange={(value) => onChangeMetadataProperty("maputnik:renderer", value)}
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ import resourcesToBackend from "i18next-resources-to-backend";
|
|||||||
import { initReactI18next } from "react-i18next";
|
import { initReactI18next } from "react-i18next";
|
||||||
|
|
||||||
export const supportedLanguages = {
|
export const supportedLanguages = {
|
||||||
|
"az": "Azərbaycan",
|
||||||
"de": "Deutsch",
|
"de": "Deutsch",
|
||||||
"en": "English",
|
"en": "English",
|
||||||
"fr": "Français",
|
"fr": "Français",
|
||||||
|
|||||||
@@ -46,6 +46,7 @@ The following users can help you with the relevant languages:
|
|||||||
|
|
||||||
| ISO Code | Language | User |
|
| ISO Code | Language | User |
|
||||||
|----------|--------------------|--------------------------------------------|
|
|----------|--------------------|--------------------------------------------|
|
||||||
|
| az | Azerbaijani | [@jamalkamaladdin](https://github.com/jamalkamaladdin) |
|
||||||
| de | German | [@josxha](https://github.com/josxha) |
|
| de | German | [@josxha](https://github.com/josxha) |
|
||||||
| en | English | [@HarelM](https://github.com/HarelM) |
|
| en | English | [@HarelM](https://github.com/HarelM) |
|
||||||
| fr | French | [@lhapaipai](https://github.com/lhapaipai) |
|
| fr | French | [@lhapaipai](https://github.com/lhapaipai) |
|
||||||
|
|||||||
@@ -0,0 +1,204 @@
|
|||||||
|
{
|
||||||
|
"<0>Open in OSM</0>. Opens the current view on openstreetmap.org": "<0>OSM-də aç</0>. Cari görünüşü openstreetmap.org saytında açır",
|
||||||
|
"Achromatopsia filter": "Axromatopsiya süzgəci",
|
||||||
|
"Active Sources": "Aktiv Mənbələr",
|
||||||
|
"Add a new source to your style. You can only choose the source type and id at creation time!": "Üslubunuza yeni mənbə əlavə edin. Mənbə növünü və ID-ni yalnız yaradılma zamanı seçə bilərsiniz!",
|
||||||
|
"Add filter": "Süzgəc əlavə et",
|
||||||
|
"Add Layer": "Qat Əlavə Et",
|
||||||
|
"Add New Source": "Yeni Mənbə Əlavə Et",
|
||||||
|
"Add one of the publicly available sources to your style.": "Üslubunuza ictimai mənbələrdən birini əlavə edin.",
|
||||||
|
"Add Source": "Mənbə Əlavə Et",
|
||||||
|
"Add stop": "Dayanacaq əlavə et",
|
||||||
|
"Add value": "Dəyər əlavə et",
|
||||||
|
"Add Variable": "Dəyişən Əlavə Et",
|
||||||
|
"any filter matches": "istənilən süzgəc uyğun gəlirsə",
|
||||||
|
"API key for Stadia Maps.": "Stadia Maps üçün API açarı.",
|
||||||
|
"Base": "Baza",
|
||||||
|
"Bearing": "İstiqamət",
|
||||||
|
"Cancel": "Ləğv et",
|
||||||
|
"Center": "Mərkəz",
|
||||||
|
"Choose Public Source": "İctimai Mənbə Seç",
|
||||||
|
"Choose the default Maputnik renderer for this style.": "Bu üslub üçün defolt Maputnik renderer-ini seçin.",
|
||||||
|
"Click to close the editor": "Redaktoru bağlamaq üçün klikləyin",
|
||||||
|
"Close": "Bağla",
|
||||||
|
"Close modal": "Modalı bağla",
|
||||||
|
"Close popup": "Popapı bağla",
|
||||||
|
"Cluster": "Klaster",
|
||||||
|
"Code Editor": "Kod Redaktoru",
|
||||||
|
"Collapse": "Yığ",
|
||||||
|
"Color accessibility": "Rəng əlçatanlığı",
|
||||||
|
"Comment...": "Şərh...",
|
||||||
|
"Comments": "Şərhlər",
|
||||||
|
"Comments for the current layer. This is non-standard and not in the spec.": "Cari qat üçün şərhlər. Bu, standart deyil və spesifikasiyada yoxdur.",
|
||||||
|
"Convert property into a elevation function": "Xassəni hündürlük funksiyasına çevir",
|
||||||
|
"Convert property into a zoom function": "Xassəni yaxınlaşma funksiyasına çevir",
|
||||||
|
"Convert property to data function": "Xassəni məlumat funksiyasına çevir",
|
||||||
|
"Convert to expression": "İfadəyə çevir",
|
||||||
|
"Coord bottom left": "Koordinat aşağı sol",
|
||||||
|
"Coord bottom right": "Koordinat aşağı sağ",
|
||||||
|
"Coord top left": "Koordinat yuxarı sol",
|
||||||
|
"Coord top right": "Koordinat yuxarı sağ",
|
||||||
|
"CORS policy won't allow fetching resources served over http from https, use a <1>https://</1> domain": "CORS siyasəti https-dən http üzərindən verilən resursların alınmasına icazə vermir, <1>https://</1> domenindən istifadə edin",
|
||||||
|
"Create HTML": "HTML Yarat",
|
||||||
|
"Data Sources": "Məlumat Mənbələri",
|
||||||
|
"Data Sources modal": "Məlumat Mənbələri modalı",
|
||||||
|
"Data value": "Məlumat dəyəri",
|
||||||
|
"Debug": "Sazlama",
|
||||||
|
"Debug modal": "Sazlama modalı",
|
||||||
|
"Decrease the pitch by 10 degrees.": "Meyli 10 dərəcə azalt.",
|
||||||
|
"Decrease the rotation by 15 degrees.": "Fırlanmanı 15 dərəcə azalt.",
|
||||||
|
"Decrease the zoom level by 1.": "Yaxınlaşma səviyyəsini 1 azalt.",
|
||||||
|
"Decrease the zoom level by 2.": "Yaxınlaşma səviyyəsini 2 azalt.",
|
||||||
|
"Default": "Defolt",
|
||||||
|
"Delete": "Sil",
|
||||||
|
"Delete expression": "İfadəni sil",
|
||||||
|
"Delete filter block": "Süzgəc blokunu sil",
|
||||||
|
"Deuteranopia filter": "Deuteranopiya süzgəci",
|
||||||
|
"Drag and drop a style JSON file here or click to browse": "Üslub JSON faylını buraya sürükləyib buraxın və ya seçmək üçün klikləyin",
|
||||||
|
"Duplicate": "Dublikat et",
|
||||||
|
"Encoding": "Kodlaşdırma",
|
||||||
|
"Enter URL...": "URL daxil edin...",
|
||||||
|
"every filter matches": "bütün süzgəclər uyğun gəlirsə",
|
||||||
|
"Expand": "Genişlət",
|
||||||
|
"Export modal": "İxrac modalı",
|
||||||
|
"Filter": "Süzgəc",
|
||||||
|
"Focus map": "Xəritəyə fokuslan",
|
||||||
|
"Function": "Funksiya",
|
||||||
|
"Gallery Styles": "Qalereya Üslubları",
|
||||||
|
"General layout properties": "Ümumi düzülüş xassələri",
|
||||||
|
"GeoJSON": "GeoJSON",
|
||||||
|
"GeoJSON (JSON)": "GeoJSON (JSON)",
|
||||||
|
"GeoJSON (URL)": "GeoJSON (URL)",
|
||||||
|
"GeoJSON URL": "GeoJSON URL",
|
||||||
|
"Global State": "Qlobal Vəziyyət",
|
||||||
|
"Global State Variables": "Qlobal Vəziyyət Dəyişənləri",
|
||||||
|
"Glyphs URL": "Şriftlər URL-i",
|
||||||
|
"Help": "Kömək",
|
||||||
|
"Hide": "Gizlət",
|
||||||
|
"Icon layout properties": "İkon düzülüş xassələri",
|
||||||
|
"Icon paint properties": "İkon boyama xassələri",
|
||||||
|
"If the Map is in focused you can use the following shortcuts": "Xəritə fokusdadırsa aşağıdakı qısayollardan istifadə edə bilərsiniz",
|
||||||
|
"Image": "Şəkil",
|
||||||
|
"Image URL": "Şəkil URL-i",
|
||||||
|
"Increase the pitch by 10 degrees.": "Meyli 10 dərəcə artır.",
|
||||||
|
"Increase the rotation by 15 degrees.": "Fırlanmanı 15 dərəcə artır.",
|
||||||
|
"Increase the zoom level by 1.": "Yaxınlaşma səviyyəsini 1 artır.",
|
||||||
|
"Increase the zoom level by 2.": "Yaxınlaşma səviyyəsini 2 artır.",
|
||||||
|
"Input a data property to base styles off of.": "Üslubların əsaslanacağı məlumat xassəsini daxil edin.",
|
||||||
|
"Input value": "Giriş dəyəri",
|
||||||
|
"Inspect": "Yoxla",
|
||||||
|
"JSON Editor": "JSON Redaktoru",
|
||||||
|
"Key": "Açar",
|
||||||
|
"Layer": "Qat",
|
||||||
|
"Layer editor": "Qat redaktoru",
|
||||||
|
"Layer ID already exists": "Qat ID artıq mövcuddur",
|
||||||
|
"Layers": "Qatlar",
|
||||||
|
"Layers list": "Qatlar siyahısı",
|
||||||
|
"Layout properties": "Düzülüş xassələri",
|
||||||
|
"Learn More": "Ətraflı Öyrən",
|
||||||
|
"Light anchor": "İşıq lövbəri",
|
||||||
|
"Light color": "İşıq rəngi",
|
||||||
|
"Light intensity": "İşıq intensivliyi",
|
||||||
|
"Light position": "İşıq mövqeyi",
|
||||||
|
"Links": "Keçidlər",
|
||||||
|
"Load from a URL. Note that the URL must have <1>CORS enabled</1>.": "URL-dən yüklə. Qeyd edin ki, URL-də <1>CORS aktiv olmalıdır</1>.",
|
||||||
|
"Load from URL": "URL-dən yüklə",
|
||||||
|
"Loading": "Yüklənir",
|
||||||
|
"Loading style": "Üslub yüklənir",
|
||||||
|
"LocationIQ Access Token": "LocationIQ Giriş Tokeni",
|
||||||
|
"Map": "Xəritə",
|
||||||
|
"Map view": "Xəritə görünüşü",
|
||||||
|
"MapTiler Access Token": "MapTiler Giriş Tokeni",
|
||||||
|
"Maputnik on GitHub": "GitHub-da Maputnik",
|
||||||
|
"Max Zoom": "Maks. Yaxınlaşma",
|
||||||
|
"Min Zoom": "Min. Yaxınlaşma",
|
||||||
|
"Move layer down": "Qatı aşağı köçür",
|
||||||
|
"Move layer up": "Qatı yuxarı köçür",
|
||||||
|
"Must provide protocol: <1>http://</1> or <3>https://</3>": "Protokol göstərilməlidir: <1>http://</1> və ya <3>https://</3>",
|
||||||
|
"Must provide protocol: <1>https://</1>": "Protokol göstərilməlidir: <1>https://</1>",
|
||||||
|
"Name": "Ad",
|
||||||
|
"Nested filters are not supported.": "İç-içə süzgəclər dəstəklənmir.",
|
||||||
|
"no filter matches": "heç bir süzgəc uyğun gəlmirsə",
|
||||||
|
"No global state variables defined. Add variables to create reusable values in your style.": "Heç bir qlobal vəziyyət dəyişəni təyin edilməyib. Üslubunuzda təkrar istifadə oluna bilən dəyərlər yaratmaq üçün dəyişən əlavə edin.",
|
||||||
|
"Open": "Aç",
|
||||||
|
"Open a local JSON style from your computer.": "Kompüterinizdən yerli JSON üslubunu açın.",
|
||||||
|
"OpenLayers (experimental)": "OpenLayers (eksperimental)",
|
||||||
|
"Open local Style": "Yerli Üslubu Aç",
|
||||||
|
"Open modal": "Modalı aç",
|
||||||
|
"Open one of the publicly available styles to start from.": "Başlamaq üçün ictimai üslublardan birini açın.",
|
||||||
|
"Open Style": "Üslubu Aç",
|
||||||
|
"Options": "Seçimlər",
|
||||||
|
"Output value": "Çıxış dəyəri",
|
||||||
|
"Owner": "Sahib",
|
||||||
|
"Owner ID of the style. Used by Mapbox or future style APIs.": "Üslubun sahib ID-si. Mapbox və ya gələcək üslub API-ləri tərəfindən istifadə olunur.",
|
||||||
|
"Paint properties": "Boyama xassələri",
|
||||||
|
"Pan down by 100 pixels.": "100 piksel aşağı sürüşdür.",
|
||||||
|
"Pan left by 100 pixels.": "100 piksel sola sürüşdür.",
|
||||||
|
"Pan right by 100 pixels.": "100 piksel sağa sürüşdür.",
|
||||||
|
"Pan up by 100 pixels.": "100 piksel yuxarı sürüşdür.",
|
||||||
|
"Pitch": "Meyl",
|
||||||
|
"PMTiles URL": "PMTiles URL",
|
||||||
|
"Press <1>ESC</1> to lose focus of any active elements, then press one of:": "İstənilən aktiv elementin fokusunu itirmək üçün <1>ESC</1> düyməsini basın, sonra bunlardan birini basın:",
|
||||||
|
"Projection": "Proyeksiya",
|
||||||
|
"Protanopia filter": "Protanopiya süzgəci",
|
||||||
|
"Public access token for LocationIQ services.": "LocationIQ xidmətləri üçün ictimai giriş tokeni.",
|
||||||
|
"Public access token for MapTiler Cloud.": "MapTiler Cloud üçün ictimai giriş tokeni.",
|
||||||
|
"Public access token for Thunderforest services.": "Thunderforest xidmətləri üçün ictimai giriş tokeni.",
|
||||||
|
"Raster (Tile URLs)": "Rastr (Kafel URL-ləri)",
|
||||||
|
"Raster (TileJSON URL)": "Rastr (TileJSON URL)",
|
||||||
|
"Raster DEM (TileJSON URL)": "Rastr DEM (TileJSON URL)",
|
||||||
|
"Raster DEM (XYZ URLs)": "Rastr DEM (XYZ URL-ləri)",
|
||||||
|
"Remove '{{sourceId}}' source": "'{{sourceId}}' mənbəsini sil",
|
||||||
|
"Remove array item": "Massiv elementini sil",
|
||||||
|
"Remove variable": "Dəyişəni sil",
|
||||||
|
"Remove zoom level from stop": "Dayanacaqdan yaxınlaşma səviyyəsini sil",
|
||||||
|
"Revert from expression": "İfadədən geri qaytar",
|
||||||
|
"Save": "Yadda saxla",
|
||||||
|
"Save as": "Fərqli yadda saxla",
|
||||||
|
"Save Style": "Üslubu Yadda Saxla",
|
||||||
|
"Save the JSON style to your computer.": "JSON üslubunu kompüterinizə yadda saxlayın.",
|
||||||
|
"Scheme Type": "Sxem Növü",
|
||||||
|
"Search": "Axtar",
|
||||||
|
"Select a type of data scale (default is 'categorical').": "Məlumat şkalasının növünü seçin (defolt 'categorical'dır).",
|
||||||
|
"Shortcuts": "Qısayollar",
|
||||||
|
"Shortcuts menu": "Qısayollar menyusu",
|
||||||
|
"Show": "Göstər",
|
||||||
|
"Source": "Mənbə",
|
||||||
|
"Source ID": "Mənbə ID",
|
||||||
|
"Source Layer": "Mənbə Qatı",
|
||||||
|
"Source Type": "Mənbə Növü",
|
||||||
|
"Sources": "Mənbələr",
|
||||||
|
"Sprite URL": "Sprite URL-i",
|
||||||
|
"Stadia Maps API Key": "Stadia Maps API Açarı",
|
||||||
|
"Stops": "Dayanacaqlar",
|
||||||
|
"Style Renderer": "Üslub Renderer-i",
|
||||||
|
"Style Settings": "Üslub Parametrləri",
|
||||||
|
"Style Settings modal": "Üslub Parametrləri modalı",
|
||||||
|
"Style URL": "Üslub URL-i",
|
||||||
|
"Switch to filter editor.": "Süzgəc redaktoruna keç.",
|
||||||
|
"switch to layer": "qata keç",
|
||||||
|
"Terrain exaggeration": "Relyef şişirdilməsi",
|
||||||
|
"Terrain source": "Relyef mənbəyi",
|
||||||
|
"Text layout properties": "Mətn düzülüş xassələri",
|
||||||
|
"Text paint properties": "Mətn boyama xassələri",
|
||||||
|
"Thunderforest Access Token": "Thunderforest Giriş Tokeni",
|
||||||
|
"Tile Size": "Kafel Ölçüsü",
|
||||||
|
"Tile URL": "Kafel URL-i",
|
||||||
|
"TileJSON URL": "TileJSON URL",
|
||||||
|
"Toggle inspect": "Yoxlamanı aç/bağla",
|
||||||
|
"Transition delay": "Keçid gecikməsi",
|
||||||
|
"Transition duration": "Keçid müddəti",
|
||||||
|
"Tritanopia filter": "Tritanopiya süzgəci",
|
||||||
|
"Type": "Növ",
|
||||||
|
"Unique ID that identifies the source and is used in the layer to reference the source.": "Mənbəni müəyyən edən və qatda mənbəyə istinad üçün istifadə olunan unikal ID.",
|
||||||
|
"Upgrade to expression": "İfadəyə yüksəlt",
|
||||||
|
"Value": "Dəyər",
|
||||||
|
"Vector (PMTiles)": "Vektor (PMTiles)",
|
||||||
|
"Vector (Tile URLs)": "Vektor (Kafel URL-ləri)",
|
||||||
|
"Vector (TileJSON URL)": "Vektor (TileJSON URL)",
|
||||||
|
"Video": "Video",
|
||||||
|
"Video URL": "Video URL-i",
|
||||||
|
"View": "Görünüş",
|
||||||
|
"You've entered an old style filter.": "Köhnə üslub süzgəci daxil etmisiniz.",
|
||||||
|
"Zoom": "Yaxınlaşma"
|
||||||
|
}
|
||||||
@@ -54,6 +54,8 @@
|
|||||||
"Delete expression": "Ausdruck löschen",
|
"Delete expression": "Ausdruck löschen",
|
||||||
"Delete filter block": "Filterblock löschen",
|
"Delete filter block": "Filterblock löschen",
|
||||||
"Deuteranopia filter": "Deuteranopie-Filter",
|
"Deuteranopia filter": "Deuteranopie-Filter",
|
||||||
|
"Drag to resize the layer list": "Ziehen, um die Ebenenliste zu skalieren",
|
||||||
|
"Drag to resize the sidebar": "Ziehen, um die Seitenleiste zu skalieren",
|
||||||
"Duplicate": "Duplizieren",
|
"Duplicate": "Duplizieren",
|
||||||
"Encoding": "Kodierung",
|
"Encoding": "Kodierung",
|
||||||
"Enter URL...": "URL eingeben...",
|
"Enter URL...": "URL eingeben...",
|
||||||
@@ -121,7 +123,7 @@
|
|||||||
"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.",
|
"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.",
|
||||||
"Open": "Öffnen",
|
"Open": "Öffnen",
|
||||||
"Open a local JSON style from your computer.": "Öffne einen lokalen JSON Stil von deinem Computer.",
|
"Open a local JSON style from your computer.": "Öffne einen lokalen JSON Stil von deinem Computer.",
|
||||||
"Open Layers (experimental)": "Ebenen öffnen (experimentell)",
|
"OpenLayers (experimental)": "OpenLayers (experimentell)",
|
||||||
"Open local Style": "Lokalen Stil öffnen",
|
"Open local Style": "Lokalen Stil öffnen",
|
||||||
"Open modal": "Modale Fenster öffnen",
|
"Open modal": "Modale Fenster öffnen",
|
||||||
"Open one of the publicly available styles to start from.": "Öffne einen der öffentlich verfügbaren Stile, um zu starten.",
|
"Open one of the publicly available styles to start from.": "Öffne einen der öffentlich verfügbaren Stile, um zu starten.",
|
||||||
|
|||||||
@@ -54,6 +54,8 @@
|
|||||||
"Delete expression": "Supprimer l'expression",
|
"Delete expression": "Supprimer l'expression",
|
||||||
"Delete filter block": "Supprimer le bloc de filtre",
|
"Delete filter block": "Supprimer le bloc de filtre",
|
||||||
"Deuteranopia filter": "Filtre Deutéranopie",
|
"Deuteranopia filter": "Filtre Deutéranopie",
|
||||||
|
"Drag to resize the layer list": "Glisser pour redimensionner la liste des calques",
|
||||||
|
"Drag to resize the sidebar": "Glisser pour redimensionner la barre latérale",
|
||||||
"Duplicate": "Dupliquer",
|
"Duplicate": "Dupliquer",
|
||||||
"Encoding": "Encodage",
|
"Encoding": "Encodage",
|
||||||
"Enter URL...": "Entrez l'URL...",
|
"Enter URL...": "Entrez l'URL...",
|
||||||
@@ -121,7 +123,7 @@
|
|||||||
"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.",
|
"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.",
|
||||||
"Open": "Ouvrir",
|
"Open": "Ouvrir",
|
||||||
"Open a local JSON style from your computer.": "Ouvrir un style JSON local depuis votre ordinateur.",
|
"Open a local JSON style from your computer.": "Ouvrir un style JSON local depuis votre ordinateur.",
|
||||||
"Open Layers (experimental)": "Open Layers (expérimental)",
|
"OpenLayers (experimental)": "OpenLayers (expérimental)",
|
||||||
"Open local Style": "Ouvrir un style local",
|
"Open local Style": "Ouvrir un style local",
|
||||||
"Open modal": "Ouvrir (modale)",
|
"Open modal": "Ouvrir (modale)",
|
||||||
"Open one of the publicly available styles to start from.": "Ouvrez l'un des styles publics disponibles pour commencer.",
|
"Open one of the publicly available styles to start from.": "Ouvrez l'un des styles publics disponibles pour commencer.",
|
||||||
|
|||||||
@@ -54,6 +54,8 @@
|
|||||||
"Delete expression": "מחיקת ביטוי",
|
"Delete expression": "מחיקת ביטוי",
|
||||||
"Delete filter block": "מחיקת גוש מסנן",
|
"Delete filter block": "מחיקת גוש מסנן",
|
||||||
"Deuteranopia filter": "Deuteranopia filter",
|
"Deuteranopia filter": "Deuteranopia filter",
|
||||||
|
"Drag to resize the layer list": "יש לגרור כדי לשנות את גודל רשימת השכבות",
|
||||||
|
"Drag to resize the sidebar": "יש לגרור כדי לשנות את גודל סרגל הצד",
|
||||||
"Duplicate": "שכפול",
|
"Duplicate": "שכפול",
|
||||||
"Encoding": "קידוד",
|
"Encoding": "קידוד",
|
||||||
"Enter URL...": "הכנסו כתובת",
|
"Enter URL...": "הכנסו כתובת",
|
||||||
@@ -121,7 +123,7 @@
|
|||||||
"No global state variables defined. Add variables to create reusable values in your style.": "לא הוגדרו משתני מצב גלובלי. הוסף משתנים כדי ליצור ערכים שניתן לעשות בהם שימוש חוזר בסטייל שלך.",
|
"No global state variables defined. Add variables to create reusable values in your style.": "לא הוגדרו משתני מצב גלובלי. הוסף משתנים כדי ליצור ערכים שניתן לעשות בהם שימוש חוזר בסטייל שלך.",
|
||||||
"Open": "פתיחה",
|
"Open": "פתיחה",
|
||||||
"Open a local JSON style from your computer.": "פתיחת סטייל JSON מקומי מהמחשב שלך.",
|
"Open a local JSON style from your computer.": "פתיחת סטייל JSON מקומי מהמחשב שלך.",
|
||||||
"Open Layers (experimental)": "Open Layers (experimental)",
|
"OpenLayers (experimental)": "OpenLayers (experimental)",
|
||||||
"Open local Style": "פתיחת סטייל מקומי",
|
"Open local Style": "פתיחת סטייל מקומי",
|
||||||
"Open modal": "פתיחת חלונית",
|
"Open modal": "פתיחת חלונית",
|
||||||
"Open one of the publicly available styles to start from.": "פתיחת אחד הסטייליםפ הציבוריים על מנת להתחיל מהם.",
|
"Open one of the publicly available styles to start from.": "פתיחת אחד הסטייליםפ הציבוריים על מנת להתחיל מהם.",
|
||||||
|
|||||||
@@ -54,6 +54,8 @@
|
|||||||
"Delete expression": "Elimina espressione",
|
"Delete expression": "Elimina espressione",
|
||||||
"Delete filter block": "Elimina blocco filtro",
|
"Delete filter block": "Elimina blocco filtro",
|
||||||
"Deuteranopia filter": "Filtro deuteranopia",
|
"Deuteranopia filter": "Filtro deuteranopia",
|
||||||
|
"Drag to resize the layer list": "Trascina per ridimensionare l'elenco dei livelli",
|
||||||
|
"Drag to resize the sidebar": "Trascina per ridimensionare la barra laterale",
|
||||||
"Duplicate": "Duplica",
|
"Duplicate": "Duplica",
|
||||||
"Encoding": "Codifica",
|
"Encoding": "Codifica",
|
||||||
"Enter URL...": "Inserisci URL...",
|
"Enter URL...": "Inserisci URL...",
|
||||||
@@ -121,7 +123,7 @@
|
|||||||
"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.",
|
"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.",
|
||||||
"Open": "Apri",
|
"Open": "Apri",
|
||||||
"Open a local JSON style from your computer.": "Apri uno stile JSON dal tuo computer.",
|
"Open a local JSON style from your computer.": "Apri uno stile JSON dal tuo computer.",
|
||||||
"Open Layers (experimental)": "Apri livelli (sperimentale)",
|
"OpenLayers (experimental)": "OpenLayers (sperimentale)",
|
||||||
"Open local Style": "Apri stile locale",
|
"Open local Style": "Apri stile locale",
|
||||||
"Open modal": "Apri finestra modale",
|
"Open modal": "Apri finestra modale",
|
||||||
"Open one of the publicly available styles to start from.": "Apro uno degli stili pubblici a partire da.",
|
"Open one of the publicly available styles to start from.": "Apro uno degli stili pubblici a partire da.",
|
||||||
|
|||||||
@@ -54,6 +54,8 @@
|
|||||||
"Delete expression": "式を削除",
|
"Delete expression": "式を削除",
|
||||||
"Delete filter block": "フィルタブロックを削除",
|
"Delete filter block": "フィルタブロックを削除",
|
||||||
"Deuteranopia filter": "緑色盲フィルタ",
|
"Deuteranopia filter": "緑色盲フィルタ",
|
||||||
|
"Drag to resize the layer list": "ドラッグしてレイヤーリストのサイズを変更",
|
||||||
|
"Drag to resize the sidebar": "ドラッグしてサイドバーのサイズを変更",
|
||||||
"Duplicate": "複製",
|
"Duplicate": "複製",
|
||||||
"Encoding": "エンコーディング",
|
"Encoding": "エンコーディング",
|
||||||
"Enter URL...": "URLを入力",
|
"Enter URL...": "URLを入力",
|
||||||
@@ -121,7 +123,7 @@
|
|||||||
"No global state variables defined. Add variables to create reusable values in your style.": "グローバルステート変数が定義されていません。スタイルで再利用可能な値を作成するには、変数を追加してください。",
|
"No global state variables defined. Add variables to create reusable values in your style.": "グローバルステート変数が定義されていません。スタイルで再利用可能な値を作成するには、変数を追加してください。",
|
||||||
"Open": "開く",
|
"Open": "開く",
|
||||||
"Open a local JSON style from your computer.": "コンピュータからローカルJSONスタイルを開きます。",
|
"Open a local JSON style from your computer.": "コンピュータからローカルJSONスタイルを開きます。",
|
||||||
"Open Layers (experimental)": "Open Layers (実験的)",
|
"OpenLayers (experimental)": "OpenLayers (実験的)",
|
||||||
"Open local Style": "ローカルスタイルを開く",
|
"Open local Style": "ローカルスタイルを開く",
|
||||||
"Open modal": "モーダルを開く",
|
"Open modal": "モーダルを開く",
|
||||||
"Open one of the publicly available styles to start from.": "公開スタイルを選んで開始しましょう。",
|
"Open one of the publicly available styles to start from.": "公開スタイルを選んで開始しましょう。",
|
||||||
|
|||||||
@@ -54,6 +54,8 @@
|
|||||||
"Delete expression": "표현식 삭제",
|
"Delete expression": "표현식 삭제",
|
||||||
"Delete filter block": "필터 블록 삭제",
|
"Delete filter block": "필터 블록 삭제",
|
||||||
"Deuteranopia filter": "녹색맹 필터",
|
"Deuteranopia filter": "녹색맹 필터",
|
||||||
|
"Drag to resize the layer list": "드래그하여 레이어 목록 크기 조정",
|
||||||
|
"Drag to resize the sidebar": "드래그하여 사이드바 크기 조정",
|
||||||
"Duplicate": "복제",
|
"Duplicate": "복제",
|
||||||
"Encoding": "인코딩",
|
"Encoding": "인코딩",
|
||||||
"Enter URL...": "URL 입력...",
|
"Enter URL...": "URL 입력...",
|
||||||
@@ -121,7 +123,7 @@
|
|||||||
"No global state variables defined. Add variables to create reusable values in your style.": "전역 상태 변수가 정의되지 않았습니다. 스타일에서 재사용 가능한 값을 생성하려면 변수를 추가하세요.",
|
"No global state variables defined. Add variables to create reusable values in your style.": "전역 상태 변수가 정의되지 않았습니다. 스타일에서 재사용 가능한 값을 생성하려면 변수를 추가하세요.",
|
||||||
"Open": "열기",
|
"Open": "열기",
|
||||||
"Open a local JSON style from your computer.": "컴퓨터에서 로컬 JSON 스타일을 엽니다.",
|
"Open a local JSON style from your computer.": "컴퓨터에서 로컬 JSON 스타일을 엽니다.",
|
||||||
"Open Layers (experimental)": "레이어 열기 (실험적)",
|
"OpenLayers (experimental)": "OpenLayers (실험적)",
|
||||||
"Open local Style": "로컬 스타일 열기",
|
"Open local Style": "로컬 스타일 열기",
|
||||||
"Open modal": "모달 열기",
|
"Open modal": "모달 열기",
|
||||||
"Open one of the publicly available styles to start from.": "공개 스타일 중 하나를 선택하여 시작하세요.",
|
"Open one of the publicly available styles to start from.": "공개 스타일 중 하나를 선택하여 시작하세요.",
|
||||||
|
|||||||
@@ -55,6 +55,8 @@
|
|||||||
"Delete filter block": "Filtre bloğunu sil",
|
"Delete filter block": "Filtre bloğunu sil",
|
||||||
"Deuteranopia filter": "Döteranopi filtresi",
|
"Deuteranopia filter": "Döteranopi filtresi",
|
||||||
"Drag and drop a style JSON file here or click to browse": "Bir stil JSON dosyasını buraya sürükleyip bırakın veya göz atmak için tıklayın",
|
"Drag and drop a style JSON file here or click to browse": "Bir stil JSON dosyasını buraya sürükleyip bırakın veya göz atmak için tıklayın",
|
||||||
|
"Drag to resize the layer list": "Katman listesini yeniden boyutlandırmak için sürükleyin",
|
||||||
|
"Drag to resize the sidebar": "Kenar çubuğunu yeniden boyutlandırmak için sürükleyin",
|
||||||
"Duplicate": "Kopyala",
|
"Duplicate": "Kopyala",
|
||||||
"Encoding": "Kodlama",
|
"Encoding": "Kodlama",
|
||||||
"Enter URL...": "URL girin...",
|
"Enter URL...": "URL girin...",
|
||||||
@@ -122,7 +124,7 @@
|
|||||||
"No global state variables defined. Add variables to create reusable values in your style.": "Genel durum değişkeni tanımlanmadı. Stilinizde yeniden kullanılabilir değerler oluşturmak için değişken ekleyin.",
|
"No global state variables defined. Add variables to create reusable values in your style.": "Genel durum değişkeni tanımlanmadı. Stilinizde yeniden kullanılabilir değerler oluşturmak için değişken ekleyin.",
|
||||||
"Open": "Aç",
|
"Open": "Aç",
|
||||||
"Open a local JSON style from your computer.": "Bilgisayarınızdan yerel bir JSON stili açın.",
|
"Open a local JSON style from your computer.": "Bilgisayarınızdan yerel bir JSON stili açın.",
|
||||||
"Open Layers (experimental)": "Open Layers (deneysel)",
|
"OpenLayers (experimental)": "OpenLayers (deneysel)",
|
||||||
"Open local Style": "Yerel Stili Aç",
|
"Open local Style": "Yerel Stili Aç",
|
||||||
"Open modal": "Modalı aç",
|
"Open modal": "Modalı aç",
|
||||||
"Open one of the publicly available styles to start from.": "Başlamak için herkese açık stillerden birini açın.",
|
"Open one of the publicly available styles to start from.": "Başlamak için herkese açık stillerden birini açın.",
|
||||||
|
|||||||
@@ -54,6 +54,8 @@
|
|||||||
"Delete expression": "删除表达式",
|
"Delete expression": "删除表达式",
|
||||||
"Delete filter block": "删除过滤器块",
|
"Delete filter block": "删除过滤器块",
|
||||||
"Deuteranopia filter": "绿色盲滤镜",
|
"Deuteranopia filter": "绿色盲滤镜",
|
||||||
|
"Drag to resize the layer list": "拖动以调整图层列表大小",
|
||||||
|
"Drag to resize the sidebar": "拖动以调整侧边栏大小",
|
||||||
"Duplicate": "复制",
|
"Duplicate": "复制",
|
||||||
"Encoding": "编码",
|
"Encoding": "编码",
|
||||||
"Enter URL...": "输入URL...",
|
"Enter URL...": "输入URL...",
|
||||||
@@ -121,7 +123,7 @@
|
|||||||
"No global state variables defined. Add variables to create reusable values in your style.": "未定义全局状态变量。添加变量以在样式中创建可重用的值。",
|
"No global state variables defined. Add variables to create reusable values in your style.": "未定义全局状态变量。添加变量以在样式中创建可重用的值。",
|
||||||
"Open": "打开",
|
"Open": "打开",
|
||||||
"Open a local JSON style from your computer.": "从您的计算机打开本地JSON样式。",
|
"Open a local JSON style from your computer.": "从您的计算机打开本地JSON样式。",
|
||||||
"Open Layers (experimental)": "开放图层(实验性)",
|
"OpenLayers (experimental)": "OpenLayers(实验性)",
|
||||||
"Open local Style": "打开本地样式",
|
"Open local Style": "打开本地样式",
|
||||||
"Open modal": "打开模态框",
|
"Open modal": "打开模态框",
|
||||||
"Open one of the publicly available styles to start from.": "打开一个公开可用的样式开始。",
|
"Open one of the publicly available styles to start from.": "打开一个公开可用的样式开始。",
|
||||||
|
|||||||
@@ -6,7 +6,8 @@
|
|||||||
.maputnik-map__container {
|
.maputnik-map__container {
|
||||||
background: white;
|
background: white;
|
||||||
display: flex;
|
display: flex;
|
||||||
width: vars.$layout-map-width;
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
|
||||||
&--error {
|
&--error {
|
||||||
align-items: center;
|
align-items: center;
|
||||||
|
|||||||
+60
-12
@@ -13,6 +13,9 @@
|
|||||||
|
|
||||||
//APP LAYOUT
|
//APP LAYOUT
|
||||||
.maputnik-layout {
|
.maputnik-layout {
|
||||||
|
// Kept up to date by AppLayout as the sidebar is resized.
|
||||||
|
--sidebar-width: #{vars.$layout-list-width + vars.$layout-editor-width};
|
||||||
|
|
||||||
font-family: vars.$font-family;
|
font-family: vars.$font-family;
|
||||||
color: vars.$color-white;
|
color: vars.$color-white;
|
||||||
|
|
||||||
@@ -28,31 +31,76 @@
|
|||||||
display: flex;
|
display: flex;
|
||||||
}
|
}
|
||||||
|
|
||||||
&-list {
|
// The resizable panel group filling the main area.
|
||||||
width: 200px;
|
&-panels {
|
||||||
background-color: vars.$color-black;
|
flex: 1;
|
||||||
|
min-width: 0;
|
||||||
|
height: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
&-sidebar-panels,
|
||||||
|
&-sidebar,
|
||||||
|
&-code-editor,
|
||||||
|
&-list,
|
||||||
|
&-drawer,
|
||||||
|
&-map {
|
||||||
|
height: 100%;
|
||||||
|
// scroll-container is position: absolute
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
|
||||||
|
&-sidebar,
|
||||||
|
&-code-editor,
|
||||||
|
&-list,
|
||||||
&-drawer {
|
&-drawer {
|
||||||
width: 370px;
|
|
||||||
background-color: vars.$color-black;
|
background-color: vars.$color-black;
|
||||||
// scroll-container is position: absolute
|
|
||||||
position: relative;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
&-code-editor {
|
&-resize-handle {
|
||||||
width: 570px;
|
width: 5px;
|
||||||
background-color: vars.$color-black;
|
background-color: transparent;
|
||||||
// scroll-container is position: absolute
|
|
||||||
position: relative;
|
position: relative;
|
||||||
|
z-index: 5;
|
||||||
|
transition: background-color 0.15s ease;
|
||||||
|
|
||||||
|
&:hover,
|
||||||
|
&:active,
|
||||||
|
&:focus-visible {
|
||||||
|
background-color: rgba(vars.$color-lowgray, 0.5);
|
||||||
|
}
|
||||||
|
|
||||||
|
// The separator is keyboard resizable, so it needs a visible focus ring.
|
||||||
|
&:focus-visible {
|
||||||
|
outline: #8e8e8e auto 1px;
|
||||||
|
}
|
||||||
|
|
||||||
|
&::after {
|
||||||
|
content: "";
|
||||||
|
position: absolute;
|
||||||
|
top: 50%;
|
||||||
|
left: 50%;
|
||||||
|
transform: translate(-50%, -50%);
|
||||||
|
width: 3px;
|
||||||
|
height: 30px;
|
||||||
|
border-radius: 2px;
|
||||||
|
background-color: vars.$color-lowgray;
|
||||||
|
opacity: 0;
|
||||||
|
transition: opacity 0.15s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
&:hover::after,
|
||||||
|
&:active::after,
|
||||||
|
&:focus-visible::after {
|
||||||
|
opacity: 0.7;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
&-bottom {
|
&-bottom {
|
||||||
position: fixed;
|
position: fixed;
|
||||||
bottom: 0;
|
bottom: 0;
|
||||||
right: 0;
|
|
||||||
z-index: 10;
|
z-index: 10;
|
||||||
width: vars.$layout-map-width;
|
inset-inline-start: var(--sidebar-width);
|
||||||
|
inset-inline-end: 0;
|
||||||
background-color: vars.$color-black;
|
background-color: vars.$color-black;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -23,7 +23,6 @@ $toolbar-offset: 0;
|
|||||||
|
|
||||||
$layout-list-width: 200px;
|
$layout-list-width: 200px;
|
||||||
$layout-editor-width: 370px;
|
$layout-editor-width: 370px;
|
||||||
$layout-map-width: calc(100% - #{$layout-list-width + $layout-editor-width});
|
|
||||||
|
|
||||||
// 'menu-down' from 'https://materialdesignicons.com/'
|
// 'menu-down' from 'https://materialdesignicons.com/'
|
||||||
// See <https://github.com/Templarian/MaterialDesign/blob/master/LICENSE>
|
// See <https://github.com/Templarian/MaterialDesign/blob/master/LICENSE>
|
||||||
|
|||||||
Reference in New Issue
Block a user