mirror of
https://github.com/maputnik/editor.git
synced 2026-09-01 09:57:27 +00:00
Compare commits
23 Commits
v2.0.0
...
3bf0e510e6
| Author | SHA1 | Date | |
|---|---|---|---|
| 3bf0e510e6 | |||
| e8d07fa694 | |||
| 4d1e2e6893 | |||
| f219ff1e17 | |||
| 8eabfa5519 | |||
| ad69cbdb20 | |||
| 17eaa3f204 | |||
| 1df2e36dbb | |||
| 0c46affda9 | |||
| 18f45e932b | |||
| 31d56c9fae | |||
| b7838ad6e1 | |||
| c92fd12854 | |||
| eb55796461 | |||
| 4b97f82980 | |||
| 5d0b6e3201 | |||
| 5ab9be9fdb | |||
| 9659d41b83 | |||
| 52f949e152 | |||
| e4d559f953 | |||
| 577663f706 | |||
| 393f4a38b9 | |||
| 3727c9ad5e |
@@ -1,21 +0,0 @@
|
|||||||
{
|
|
||||||
"presets": [
|
|
||||||
"@babel/preset-env",
|
|
||||||
"@babel/preset-react"
|
|
||||||
],
|
|
||||||
"plugins": [
|
|
||||||
"static-fs",
|
|
||||||
"react-hot-loader/babel",
|
|
||||||
"@babel/plugin-proposal-class-properties",
|
|
||||||
"@babel/transform-runtime"
|
|
||||||
],
|
|
||||||
"env": {
|
|
||||||
"test": {
|
|
||||||
"plugins": [
|
|
||||||
["istanbul", {
|
|
||||||
"exclude": ["node_modules/**", "test/**"]
|
|
||||||
}]
|
|
||||||
]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,20 @@
|
|||||||
|
module.exports = {
|
||||||
|
root: true,
|
||||||
|
env: { browser: true, es2020: true },
|
||||||
|
extends: [
|
||||||
|
'eslint:recommended',
|
||||||
|
'plugin:react/recommended',
|
||||||
|
'plugin:react/jsx-runtime',
|
||||||
|
'plugin:react-hooks/recommended',
|
||||||
|
],
|
||||||
|
ignorePatterns: ['dist', '.eslintrc.cjs'],
|
||||||
|
parserOptions: { ecmaVersion: 'latest', sourceType: 'module' },
|
||||||
|
settings: { react: { version: '18.2' } },
|
||||||
|
plugins: ['react-refresh'],
|
||||||
|
rules: {
|
||||||
|
'react-refresh/only-export-components': [
|
||||||
|
'warn',
|
||||||
|
{ allowConstantExport: true },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
}
|
||||||
@@ -9,7 +9,7 @@ assignees: ''
|
|||||||
|
|
||||||
<!-- Thanks for your feedback! Please complete the following information: -->
|
<!-- Thanks for your feedback! Please complete the following information: -->
|
||||||
|
|
||||||
**Maputnik version**:<!-- e.g v1.7.0, master -->
|
**Maputnik version**:<!-- e.g v1.7.0, main -->
|
||||||
**Browser**:
|
**Browser**:
|
||||||
**OS**:<!-- (Windows, macOS, Linux) -->
|
**OS**:<!-- (Windows, macOS, Linux) -->
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,14 @@
|
|||||||
|
version: 2
|
||||||
|
updates:
|
||||||
|
- package-ecosystem: "npm"
|
||||||
|
directory: "/"
|
||||||
|
schedule:
|
||||||
|
interval: "daily"
|
||||||
|
open-pull-requests-limit: 2
|
||||||
|
versioning-strategy: increase
|
||||||
|
- package-ecosystem: "github-actions"
|
||||||
|
directory: "/"
|
||||||
|
schedule:
|
||||||
|
interval: "daily"
|
||||||
|
open-pull-requests-limit: 2
|
||||||
|
versioning-strategy: increase
|
||||||
+19
-72
@@ -2,26 +2,11 @@ name: ci
|
|||||||
|
|
||||||
on:
|
on:
|
||||||
pull_request:
|
pull_request:
|
||||||
branches: [ master ]
|
branches: [ main ]
|
||||||
push:
|
push:
|
||||||
branches: [ master ]
|
branches: [ main ]
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
|
|
||||||
# post a comment linking to codesandbox with the current branch
|
|
||||||
# meta-demo-comment:
|
|
||||||
# name: meta/demo-comment
|
|
||||||
# runs-on: ubuntu-latest
|
|
||||||
|
|
||||||
# if: ${{ github.event_name == 'pull_request' }}
|
|
||||||
|
|
||||||
# steps:
|
|
||||||
# - uses: unsplash/comment-on-pr@v1.2.0
|
|
||||||
# env:
|
|
||||||
# GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
|
||||||
# with:
|
|
||||||
# msg: "Demo: <https://codesandbox.io/embed/github/${{ github.repository }}/tree/${{ github.head_ref }}?view=preview>"
|
|
||||||
|
|
||||||
build-docker:
|
build-docker:
|
||||||
name: build/docker
|
name: build/docker
|
||||||
runs-on: ${{ matrix.os }}
|
runs-on: ${{ matrix.os }}
|
||||||
@@ -34,8 +19,8 @@ jobs:
|
|||||||
os: [ubuntu-latest]
|
os: [ubuntu-latest]
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v2
|
- uses: actions/checkout@v4
|
||||||
- run: docker build -t docker.pkg.github.com/maputnik/editor/editor:master .
|
- run: docker build -t docker.pkg.github.com/maputnik/editor/editor:main .
|
||||||
|
|
||||||
# build the editor
|
# build the editor
|
||||||
build-node:
|
build-node:
|
||||||
@@ -48,10 +33,10 @@ jobs:
|
|||||||
fail-fast: false
|
fail-fast: false
|
||||||
matrix:
|
matrix:
|
||||||
os: [ubuntu-latest, windows-latest, macos-latest]
|
os: [ubuntu-latest, windows-latest, macos-latest]
|
||||||
node-version: [16.x]
|
node-version: [18.x]
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v2
|
- uses: actions/checkout@v4
|
||||||
- uses: actions/setup-node@v1
|
- uses: actions/setup-node@v1
|
||||||
with:
|
with:
|
||||||
node-version: ${{ matrix.node-version }}
|
node-version: ${{ matrix.node-version }}
|
||||||
@@ -74,10 +59,10 @@ jobs:
|
|||||||
fail-fast: false
|
fail-fast: false
|
||||||
matrix:
|
matrix:
|
||||||
os: [ubuntu-latest]
|
os: [ubuntu-latest]
|
||||||
node-version: [16.x]
|
node-version: [18.x]
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v2
|
- uses: actions/checkout@v4
|
||||||
- uses: actions/setup-node@v1
|
- uses: actions/setup-node@v1
|
||||||
with:
|
with:
|
||||||
node-version: ${{ matrix.node-version }}
|
node-version: ${{ matrix.node-version }}
|
||||||
@@ -94,13 +79,7 @@ jobs:
|
|||||||
uses: actions/upload-artifact@v1
|
uses: actions/upload-artifact@v1
|
||||||
with:
|
with:
|
||||||
name: editor
|
name: editor
|
||||||
path: build/build
|
path: dist
|
||||||
- run: npm run profiling-build
|
|
||||||
- name: artifacts/editor-profiling
|
|
||||||
uses: actions/upload-artifact@v1
|
|
||||||
with:
|
|
||||||
name: editor-profiling
|
|
||||||
path: build/profiling
|
|
||||||
- name: artifacts/storybook
|
- name: artifacts/storybook
|
||||||
uses: actions/upload-artifact@v1
|
uses: actions/upload-artifact@v1
|
||||||
with:
|
with:
|
||||||
@@ -115,7 +94,7 @@ jobs:
|
|||||||
id: go
|
id: go
|
||||||
|
|
||||||
- name: Check out code into the Go module directory
|
- name: Check out code into the Go module directory
|
||||||
uses: actions/checkout@v2
|
uses: actions/checkout@v4
|
||||||
with:
|
with:
|
||||||
repository: maputnik/desktop
|
repository: maputnik/desktop
|
||||||
ref: master
|
ref: master
|
||||||
@@ -142,52 +121,20 @@ jobs:
|
|||||||
name: maputnik-windows
|
name: maputnik-windows
|
||||||
path: ./src/github.com/maputnik/desktop/bin/windows/
|
path: ./src/github.com/maputnik/desktop/bin/windows/
|
||||||
|
|
||||||
# build and test the editor
|
cypress-run:
|
||||||
test_selenium_standalone:
|
|
||||||
name: "test/standalone-${{ matrix.browser }} (${{ matrix.os }})"
|
|
||||||
runs-on: ${{ matrix.os }}
|
|
||||||
|
|
||||||
if: ${{ github.event_name == 'push' || github.event_name == 'pull_request' }}
|
|
||||||
|
|
||||||
strategy:
|
strategy:
|
||||||
fail-fast: false
|
fail-fast: false
|
||||||
matrix:
|
matrix:
|
||||||
os: [ubuntu-latest]
|
|
||||||
node-version: [16]
|
|
||||||
browser: [chrome, firefox]
|
browser: [chrome, firefox]
|
||||||
|
|
||||||
container:
|
runs-on: ubuntu-22.04
|
||||||
image: node:${{ matrix.node-version }}
|
|
||||||
options: --network-alias testhost
|
|
||||||
|
|
||||||
services:
|
|
||||||
selenium:
|
|
||||||
# geckodriver-0.31 seems to have problems as of 2022 May 1
|
|
||||||
image: selenium/standalone-${{ matrix.browser == 'firefox' && 'firefox:99.0-geckodriver-0.30-20220427' || matrix.browser }}
|
|
||||||
ports:
|
|
||||||
- 4444:4444
|
|
||||||
options: --shm-size=2gb
|
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v2
|
- name: Checkout
|
||||||
- uses: actions/cache@v1
|
uses: actions/checkout@v4
|
||||||
with:
|
|
||||||
path: ~/.npm
|
|
||||||
key: ${{ runner.os }}-node-${{ hashFiles('**/package-lock.json') }}
|
|
||||||
restore-keys: |
|
|
||||||
${{ runner.os }}-node-
|
|
||||||
- run: npm ci
|
- run: npm ci
|
||||||
- run: BROWSER=${{ matrix.browser }} TEST_NETWORK=testhost DOCKER_HOST=selenium npm run test
|
- name: Cypress run
|
||||||
- if: ${{ matrix.browser == 'chrome' }}
|
uses: cypress-io/github-action@v6
|
||||||
run: ./node_modules/.bin/istanbul report --include build/coverage/coverage.json --dir build/coverage html lcov
|
|
||||||
- if: ${{ matrix.browser == 'chrome' }}
|
|
||||||
name: artifacts/coverage
|
|
||||||
uses: actions/upload-artifact@v1
|
|
||||||
with:
|
with:
|
||||||
name: coverage
|
build: npm run build
|
||||||
path: build/coverage
|
start: npm run start
|
||||||
- name: artifacts/screenshots
|
browser: ${{ matrix.browser }}
|
||||||
uses: actions/upload-artifact@v1
|
|
||||||
with:
|
|
||||||
name: screenshots-${{ matrix.browser }}
|
|
||||||
path: build/screenshots
|
|
||||||
|
|||||||
@@ -2,13 +2,12 @@ name: deploy
|
|||||||
|
|
||||||
on:
|
on:
|
||||||
push:
|
push:
|
||||||
branches: [ master ]
|
branches: [ main ]
|
||||||
push:
|
|
||||||
tags:
|
tags:
|
||||||
- 'v*'
|
- 'v*'
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
# publish docker to github registry
|
# publish docker to GitHub registry
|
||||||
deploy-docker:
|
deploy-docker:
|
||||||
name: deploy/docker
|
name: deploy/docker
|
||||||
runs-on: ${{ matrix.os }}
|
runs-on: ${{ matrix.os }}
|
||||||
@@ -21,8 +20,7 @@ jobs:
|
|||||||
os: [ubuntu-latest]
|
os: [ubuntu-latest]
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v2
|
- uses: actions/checkout@v4
|
||||||
- run: echo "${{ secrets.GITHUB_TOKEN }}" | docker login docker.pkg.github.com -u orangemug --password-stdin
|
- run: echo "${{ secrets.GITHUB_TOKEN }}" | docker login docker.pkg.github.com -u orangemug --password-stdin
|
||||||
- run: docker build -t docker.pkg.github.com/maputnik/editor/editor:master .
|
- run: docker build -t docker.pkg.github.com/maputnik/editor/editor:main .
|
||||||
- run: docker push docker.pkg.github.com/maputnik/editor/editor:master
|
- run: docker push docker.pkg.github.com/maputnik/editor/editor:main
|
||||||
|
|
||||||
|
|||||||
@@ -33,3 +33,5 @@ public
|
|||||||
/errorShots
|
/errorShots
|
||||||
/old
|
/old
|
||||||
/build
|
/build
|
||||||
|
/cypress/screenshots
|
||||||
|
/dist/
|
||||||
|
|||||||
+7
-22
@@ -1,24 +1,9 @@
|
|||||||
const rules = require('../config/webpack.rules');
|
const config = {
|
||||||
|
stories: ['../stories/**/*.stories.jsx'],
|
||||||
module.exports = {
|
addons: ['@storybook/addon-actions', '@storybook/addon-links', '@storybook/addon-a11y/register', '@storybook/addon-storysource'],
|
||||||
stories: ['../stories/**/*.stories.js'],
|
framework: {
|
||||||
addons: [
|
name: '@storybook/react-vite',
|
||||||
'@storybook/addon-actions',
|
options: {}
|
||||||
'@storybook/addon-links',
|
|
||||||
'@storybook/addon-a11y/register',
|
|
||||||
'@storybook/addon-storysource',
|
|
||||||
],
|
|
||||||
webpackFinal: async config => {
|
|
||||||
// do mutation to the config
|
|
||||||
console.log("config.module", config.module);
|
|
||||||
|
|
||||||
return {
|
|
||||||
...config,
|
|
||||||
module: {
|
|
||||||
rules: [
|
|
||||||
...rules,
|
|
||||||
]
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
},
|
export default config;
|
||||||
};
|
|
||||||
+2
-2
@@ -1,4 +1,4 @@
|
|||||||
FROM node:10 as builder
|
FROM node:18 as builder
|
||||||
WORKDIR /maputnik
|
WORKDIR /maputnik
|
||||||
|
|
||||||
# Only copy package.json to prevent npm install from running on every build
|
# Only copy package.json to prevent npm install from running on every build
|
||||||
@@ -16,7 +16,7 @@ RUN npm run build
|
|||||||
FROM python:3-slim
|
FROM python:3-slim
|
||||||
WORKDIR /maputnik
|
WORKDIR /maputnik
|
||||||
|
|
||||||
COPY --from=builder /maputnik/build/build .
|
COPY --from=builder /maputnik/dist .
|
||||||
|
|
||||||
EXPOSE 8888
|
EXPOSE 8888
|
||||||
CMD python -m http.server 8888
|
CMD python -m http.server 8888
|
||||||
|
|||||||
@@ -7,7 +7,7 @@
|
|||||||
[github-action-ci]: https://github.com/maputnik/editor/actions?query=workflow%3Aci
|
[github-action-ci]: https://github.com/maputnik/editor/actions?query=workflow%3Aci
|
||||||
[license]: https://tldrlegal.com/license/mit-license
|
[license]: https://tldrlegal.com/license/mit-license
|
||||||
|
|
||||||
A free and open visual editor for the [Mapbox GL styles](https://www.mapbox.com/mapbox-gl-style-spec/)
|
A free and open visual editor for the [MapLibre GL styles](https://maplibre.org/maplibre-style-spec/)
|
||||||
targeted at developers and map designers.
|
targeted at developers and map designers.
|
||||||
|
|
||||||
|
|
||||||
@@ -36,7 +36,7 @@ The documentation can be found in the [Wiki](https://github.com/maputnik/editor/
|
|||||||
|
|
||||||
## Develop
|
## Develop
|
||||||
|
|
||||||
Maputnik is written in ES6 and is using [React](https://github.com/facebook/react) and [Mapbox GL JS](https://www.mapbox.com/mapbox-gl-js/api/).
|
Maputnik is written in ES6 and is using [React](https://github.com/facebook/react) and [MapLibre GL JS](https://maplibre.org/projects/maplibre-gl-js/).
|
||||||
|
|
||||||
We ensure building and developing Maputnik works with the [current active LTS Node.js version and above](https://github.com/nodejs/Release#release-schedule).
|
We ensure building and developing Maputnik works with the [current active LTS Node.js version and above](https://github.com/nodejs/Release#release-schedule).
|
||||||
|
|
||||||
@@ -49,18 +49,14 @@ npm install
|
|||||||
npm run start
|
npm run start
|
||||||
```
|
```
|
||||||
|
|
||||||
If you want Maputnik to be accessible externally use the [`--host` option](https://webpack.js.org/configuration/dev-server/#devserverhost):
|
If you want Maputnik to be accessible externally use the [`--host` option](https://vitejs.dev/config/server-options.html#server-host):
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
# start externally accessible dev server
|
# start externally accessible dev server
|
||||||
npm run start -- --host 0.0.0.0
|
npm run start -- --host 0.0.0.0
|
||||||
```
|
```
|
||||||
|
|
||||||
The build process will watch for changes to the filesystem, rebuild and autoreload the editor. However note this from the [webpack-dev-server docs](https://webpack.js.org/configuration/dev-server/):
|
The build process will watch for changes to the filesystem, rebuild and autoreload the editor.
|
||||||
|
|
||||||
> webpack uses the file system to get notified of file changes. In some cases this does not work. For example, when using Network File System (NFS). Vagrant also has a lot of problems with this. ([snippet source](https://webpack.js.org/configuration/dev-server/#devserverwatchoptions-))
|
|
||||||
|
|
||||||
To enable polling add `export WEBPACK_DEV_SERVER_POLLING=1` to your environment.
|
|
||||||
|
|
||||||
```
|
```
|
||||||
npm run build
|
npm run build
|
||||||
@@ -76,9 +72,9 @@ npm run lint-styles
|
|||||||
|
|
||||||
|
|
||||||
## Tests
|
## Tests
|
||||||
For testing we use [webdriverio](https://webdriver.io) and [selenium-standalone](https://github.com/webdriverio/selenium-standalone).
|
For E2E testing we use [Cypress](https://www.cypress.io/)
|
||||||
|
|
||||||
[selenium-standalone](https://github.com/webdriverio/selenium-standalone) starts a server that will launch browsers on your local machine. You need to have Java installed on your machine as well as *chrome* or *firefox*.
|
[Cypress](https://www.cypress.io/) doesn't starts a server so you'll need to start one manually by running `npm run start`.
|
||||||
|
|
||||||
Now open a terminal and run the following using *chrome*:
|
Now open a terminal and run the following using *chrome*:
|
||||||
|
|
||||||
@@ -87,10 +83,16 @@ npm run test
|
|||||||
```
|
```
|
||||||
or *firefox*:
|
or *firefox*:
|
||||||
```
|
```
|
||||||
BROWSER=firefox npm run test
|
npm run test -- --browser firefox
|
||||||
```
|
```
|
||||||
|
|
||||||
After some time you should see a browser launch which will be automated by the test runner.
|
See the following docs for more info: (Launching Browsers)[https://docs.cypress.io/guides/guides/launching-browsers]
|
||||||
|
|
||||||
|
You can also see the tests as they run or select which suites to run by executing:
|
||||||
|
|
||||||
|
```
|
||||||
|
npm run cy:open
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
## Related Projects
|
## Related Projects
|
||||||
@@ -155,6 +157,5 @@ Sina Martinelli, Nicholas Doiron, Neil Cawse, Urs42, Benedikt Groß, Manuel Roth
|
|||||||
|
|
||||||
Maputnik is [licensed under MIT](LICENSE) and is Copyright (c) Lukas Martinelli and contributors.
|
Maputnik is [licensed under MIT](LICENSE) and is Copyright (c) Lukas Martinelli and contributors.
|
||||||
|
|
||||||
**Disclaimer** This project is not affiliated with Mapbox or Mapbox Studio. It is an independent style editor for the
|
**Disclaimer** This is an independent style editor.
|
||||||
open source technology in the Mapbox GL ecosystem.
|
|
||||||
As contributor please take extra care of not violating any Mapbox trademarks. Do not get inspired by Mapbox Studio and make your own decisions for a good style editor.
|
As contributor please take extra care of not violating any Mapbox trademarks. Do not get inspired by Mapbox Studio and make your own decisions for a good style editor.
|
||||||
|
|||||||
@@ -1,47 +0,0 @@
|
|||||||
var webpack = require("webpack");
|
|
||||||
var WebpackDevServer = require("webpack-dev-server");
|
|
||||||
var webpackConfig = require("./webpack.config");
|
|
||||||
var testConfig = require("../test/config/specs");
|
|
||||||
var artifacts = require("../test/artifacts");
|
|
||||||
|
|
||||||
|
|
||||||
var server;
|
|
||||||
var SCREENSHOT_PATH = artifacts.pathSync("screenshots");
|
|
||||||
|
|
||||||
exports.config = {
|
|
||||||
runner: 'local',
|
|
||||||
path: '/wd/hub',
|
|
||||||
specs: [
|
|
||||||
'./test/functional/index.js'
|
|
||||||
],
|
|
||||||
maxInstances: 10,
|
|
||||||
capabilities: [
|
|
||||||
{
|
|
||||||
maxInstances: 5,
|
|
||||||
browserName: (process.env.BROWSER || 'chrome'),
|
|
||||||
}
|
|
||||||
],
|
|
||||||
// geckodriver-0.31 seems to have problems as of 2022 May 1
|
|
||||||
services: process.env.DOCKER_HOST ? [] : [ ['selenium-standalone', { drivers: { firefox: '0.30.0', chrome: 'latest' } } ] ],
|
|
||||||
logLevel: 'info',
|
|
||||||
bail: 0,
|
|
||||||
screenshotPath: SCREENSHOT_PATH,
|
|
||||||
hostname: process.env.DOCKER_HOST || "0.0.0.0",
|
|
||||||
framework: 'mocha',
|
|
||||||
reporters: ['spec'],
|
|
||||||
mochaOpts: {
|
|
||||||
ui: 'bdd',
|
|
||||||
// Because we don't know how long the initial build will take...
|
|
||||||
timeout: 4*60*1000,
|
|
||||||
},
|
|
||||||
onPrepare: async function (config, capabilities) {
|
|
||||||
webpackConfig.devServer.host = testConfig.testNetwork;
|
|
||||||
webpackConfig.devServer.port = testConfig.port;
|
|
||||||
const compiler = webpack(webpackConfig);
|
|
||||||
server = new WebpackDevServer(webpackConfig.devServer, compiler);
|
|
||||||
await server.start();
|
|
||||||
},
|
|
||||||
onComplete: async function (exitCode, config, capabilities) {
|
|
||||||
await server.stop();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,74 +0,0 @@
|
|||||||
"use strict";
|
|
||||||
var path = require('path');
|
|
||||||
var rules = require('./webpack.rules');
|
|
||||||
var HtmlWebpackPlugin = require('html-webpack-plugin');
|
|
||||||
var HtmlWebpackInlineSVGPlugin = require('html-webpack-inline-svg-plugin');
|
|
||||||
var CopyWebpackPlugin = require('copy-webpack-plugin');
|
|
||||||
|
|
||||||
const HOST = process.env.HOST || "127.0.0.1";
|
|
||||||
const PORT = process.env.PORT || "8888";
|
|
||||||
|
|
||||||
module.exports = {
|
|
||||||
target: 'web',
|
|
||||||
mode: 'development',
|
|
||||||
entry: [
|
|
||||||
`webpack-dev-server/client?http://${HOST}:${PORT}`,
|
|
||||||
`webpack/hot/only-dev-server`,
|
|
||||||
`./src/index.jsx` // Your appʼs entry point
|
|
||||||
],
|
|
||||||
devtool: process.env.WEBPACK_DEVTOOL || 'cheap-module-source-map',
|
|
||||||
output: {
|
|
||||||
path: path.join(__dirname, '..', 'public'),
|
|
||||||
filename: 'bundle.js'
|
|
||||||
},
|
|
||||||
resolve: {
|
|
||||||
extensions: ['.js', '.jsx']
|
|
||||||
},
|
|
||||||
module: {
|
|
||||||
noParse: [
|
|
||||||
/mapbox-gl\/dist\/mapbox-gl.js/
|
|
||||||
],
|
|
||||||
rules: rules
|
|
||||||
},
|
|
||||||
node: {
|
|
||||||
fs: "empty",
|
|
||||||
net: 'empty',
|
|
||||||
tls: 'empty'
|
|
||||||
},
|
|
||||||
devServer: {
|
|
||||||
// enable HMR
|
|
||||||
hot: true,
|
|
||||||
// serve index.html in place of 404 responses to allow HTML5 history
|
|
||||||
historyApiFallback: true,
|
|
||||||
port: PORT,
|
|
||||||
host: HOST,
|
|
||||||
watchFiles: {
|
|
||||||
options: {
|
|
||||||
// Disabled polling by default as it causes lots of CPU usage and hence drains laptop batteries. To enable polling add WEBPACK_DEV_SERVER_POLLING to your environment
|
|
||||||
// See <https://webpack.js.org/configuration/watch/#watchoptions-poll> for details
|
|
||||||
usePolling: (!!process.env.WEBPACK_DEV_SERVER_POLLING ? true : false),
|
|
||||||
watch: false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
optimization: {
|
|
||||||
noEmitOnErrors: true,
|
|
||||||
},
|
|
||||||
plugins: [
|
|
||||||
new HtmlWebpackPlugin({
|
|
||||||
title: 'Maputnik',
|
|
||||||
template: './src/template.html'
|
|
||||||
}),
|
|
||||||
new HtmlWebpackInlineSVGPlugin({
|
|
||||||
runPreEmit: true,
|
|
||||||
}),
|
|
||||||
new CopyWebpackPlugin({
|
|
||||||
patterns: [
|
|
||||||
{
|
|
||||||
from: './src/manifest.json',
|
|
||||||
to: 'manifest.json'
|
|
||||||
}
|
|
||||||
]
|
|
||||||
})
|
|
||||||
]
|
|
||||||
};
|
|
||||||
@@ -1,68 +0,0 @@
|
|||||||
var webpack = require('webpack');
|
|
||||||
var path = require('path');
|
|
||||||
var rules = require('./webpack.rules');
|
|
||||||
var HtmlWebpackPlugin = require('html-webpack-plugin');
|
|
||||||
var HtmlWebpackInlineSVGPlugin = require('html-webpack-inline-svg-plugin');
|
|
||||||
var WebpackCleanupPlugin = require('webpack-cleanup-plugin');
|
|
||||||
var BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin;
|
|
||||||
var CopyWebpackPlugin = require('copy-webpack-plugin');
|
|
||||||
var artifacts = require("../test/artifacts");
|
|
||||||
|
|
||||||
var OUTPATH = artifacts.pathSync("/build");
|
|
||||||
|
|
||||||
module.exports = {
|
|
||||||
entry: {
|
|
||||||
app: './src/index.jsx',
|
|
||||||
},
|
|
||||||
output: {
|
|
||||||
path: OUTPATH,
|
|
||||||
filename: '[name].[contenthash].js',
|
|
||||||
chunkFilename: '[contenthash].js'
|
|
||||||
},
|
|
||||||
resolve: {
|
|
||||||
extensions: ['.js', '.jsx']
|
|
||||||
},
|
|
||||||
module: {
|
|
||||||
noParse: [
|
|
||||||
/mapbox-gl\/dist\/mapbox-gl.js/
|
|
||||||
],
|
|
||||||
rules: rules
|
|
||||||
},
|
|
||||||
node: {
|
|
||||||
fs: "empty",
|
|
||||||
net: 'empty',
|
|
||||||
tls: 'empty'
|
|
||||||
},
|
|
||||||
plugins: [
|
|
||||||
new webpack.NoEmitOnErrorsPlugin(),
|
|
||||||
new WebpackCleanupPlugin(),
|
|
||||||
new webpack.DefinePlugin({
|
|
||||||
'process.env': {
|
|
||||||
NODE_ENV: '"production"'
|
|
||||||
}
|
|
||||||
}),
|
|
||||||
new HtmlWebpackPlugin({
|
|
||||||
template: './src/template.html',
|
|
||||||
title: 'Maputnik'
|
|
||||||
}),
|
|
||||||
new HtmlWebpackInlineSVGPlugin({
|
|
||||||
runPreEmit: true,
|
|
||||||
}),
|
|
||||||
new CopyWebpackPlugin({
|
|
||||||
patterns: [
|
|
||||||
{
|
|
||||||
from: './src/manifest.json',
|
|
||||||
to: 'manifest.json'
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}),
|
|
||||||
new BundleAnalyzerPlugin({
|
|
||||||
analyzerMode: 'static',
|
|
||||||
defaultSizes: 'gzip',
|
|
||||||
openAnalyzer: false,
|
|
||||||
generateStatsFile: true,
|
|
||||||
reportFilename: 'bundle-stats.html',
|
|
||||||
statsFilename: 'bundle-stats.json',
|
|
||||||
})
|
|
||||||
]
|
|
||||||
};
|
|
||||||
@@ -1,20 +0,0 @@
|
|||||||
const webpackProdConfig = require('./webpack.production.config');
|
|
||||||
const artifacts = require("../test/artifacts");
|
|
||||||
|
|
||||||
const OUTPATH = artifacts.pathSync("/profiling");
|
|
||||||
|
|
||||||
module.exports = {
|
|
||||||
...webpackProdConfig,
|
|
||||||
output: {
|
|
||||||
...webpackProdConfig.output,
|
|
||||||
path: OUTPATH,
|
|
||||||
},
|
|
||||||
resolve: {
|
|
||||||
...webpackProdConfig.resolve,
|
|
||||||
alias: {
|
|
||||||
...webpackProdConfig.resolve.alias,
|
|
||||||
'react-dom$': 'react-dom/profiling',
|
|
||||||
'scheduler/tracing': 'scheduler/tracing-profiling',
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
@@ -1,44 +0,0 @@
|
|||||||
const path = require("path");
|
|
||||||
|
|
||||||
module.exports = [
|
|
||||||
{
|
|
||||||
test: /\.jsx?$/,
|
|
||||||
exclude: [
|
|
||||||
path.resolve(__dirname, '../node_modules')
|
|
||||||
],
|
|
||||||
use: 'babel-loader'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
test: /\.(eot|ttf|woff|woff2)$/,
|
|
||||||
use: 'file-loader?name=fonts/[name].[ext]'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
test: /\.ico$/,
|
|
||||||
use: 'file-loader?name=[name].[ext]'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
test: /\.(gif|jpg|png)$/,
|
|
||||||
use: 'file-loader?name=img/[name].[ext]'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
test: /\.svg$/,
|
|
||||||
use: [
|
|
||||||
'svg-inline-loader'
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
test: /[\/\\](node_modules|global|src)[\/\\].*\.scss$/,
|
|
||||||
use: [
|
|
||||||
'style-loader',
|
|
||||||
"css-loader",
|
|
||||||
"sass-loader"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
test: /[\/\\](node_modules|global|src)[\/\\].*\.css$/,
|
|
||||||
use: [
|
|
||||||
'style-loader',
|
|
||||||
'css-loader'
|
|
||||||
]
|
|
||||||
}
|
|
||||||
];
|
|
||||||
@@ -0,0 +1,13 @@
|
|||||||
|
import { defineConfig } from "cypress";
|
||||||
|
|
||||||
|
export default defineConfig({
|
||||||
|
e2e: {
|
||||||
|
setupNodeEvents(on, config) {
|
||||||
|
// implement node event listeners here
|
||||||
|
},
|
||||||
|
retries: {
|
||||||
|
runMode: 2,
|
||||||
|
openMode: 0,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
@@ -0,0 +1,40 @@
|
|||||||
|
import MaputnikDriver from "./driver";
|
||||||
|
|
||||||
|
describe("accessibility", () => {
|
||||||
|
let { beforeAndAfter, given, when, get, should } = new MaputnikDriver();
|
||||||
|
beforeAndAfter();
|
||||||
|
|
||||||
|
describe("skip links", () => {
|
||||||
|
beforeEach(() => {
|
||||||
|
when.setStyle("layer");
|
||||||
|
});
|
||||||
|
|
||||||
|
it("skip link to layer list", () => {
|
||||||
|
const selector = "root:skip:layer-list";
|
||||||
|
should.exist(selector);
|
||||||
|
when.tab();
|
||||||
|
should.beFocused(selector);
|
||||||
|
when.click(selector);
|
||||||
|
should.beFocused("skip-target-layer-list");
|
||||||
|
});
|
||||||
|
|
||||||
|
// 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", () => {
|
||||||
|
const selector = "root:skip:layer-editor";
|
||||||
|
should.exist(selector);
|
||||||
|
when.tab().tab();
|
||||||
|
should.beFocused(selector);
|
||||||
|
when.click(selector);
|
||||||
|
should.beFocused("skip-target-layer-editor");
|
||||||
|
});
|
||||||
|
|
||||||
|
it("skip link to map view", () => {
|
||||||
|
const selector = "root:skip:map-view";
|
||||||
|
should.exist(selector);
|
||||||
|
when.tab().tab().tab();
|
||||||
|
should.beFocused(selector);
|
||||||
|
when.click(selector);
|
||||||
|
should.canvasBeFocused();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
@@ -0,0 +1,210 @@
|
|||||||
|
import { CypressHelper } from "@shellygo/cypress-test-utils";
|
||||||
|
import { v1 as uuid } from "uuid";
|
||||||
|
export default class MaputnikDriver {
|
||||||
|
private helper = new CypressHelper({ defaultDataAttribute: "data-wd-key" });
|
||||||
|
public beforeAndAfter = () => {
|
||||||
|
beforeEach(() => {
|
||||||
|
this.given.setupInterception();
|
||||||
|
this.when.setStyle("both");
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
public given = {
|
||||||
|
setupInterception: () => {
|
||||||
|
cy.intercept("GET", "http://localhost:8888/example-style.json", {
|
||||||
|
fixture: "example-style.json",
|
||||||
|
}).as("example-style.json");
|
||||||
|
cy.intercept("GET", "http://localhost:8888/example-layer-style.json", {
|
||||||
|
fixture: "example-layer-style.json",
|
||||||
|
});
|
||||||
|
cy.intercept("GET", "http://localhost:8888/geojson-style.json", {
|
||||||
|
fixture: "geojson-style.json",
|
||||||
|
});
|
||||||
|
cy.intercept("GET", "http://localhost:8888/raster-style.json", {
|
||||||
|
fixture: "raster-style.json",
|
||||||
|
});
|
||||||
|
cy.intercept("GET", "http://localhost:8888/geojson-raster-style.json", {
|
||||||
|
fixture: "geojson-raster-style.json",
|
||||||
|
});
|
||||||
|
cy.intercept({ method: "GET", url: "*example.local/*" }, []);
|
||||||
|
cy.intercept({ method: "GET", url: "*example.com/*" }, []);
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
public when = {
|
||||||
|
within: (selector: string, fn: () => void) => {
|
||||||
|
this.helper.when.within(fn, selector);
|
||||||
|
},
|
||||||
|
tab: () => cy.get("body").tab(),
|
||||||
|
waitForExampleFileRequset: () => {
|
||||||
|
this.helper.when.waitForResponse("example-style.json");
|
||||||
|
},
|
||||||
|
chooseExampleFile: () => {
|
||||||
|
cy.get("input[type='file']").selectFile(
|
||||||
|
"cypress/fixtures/example-style.json",
|
||||||
|
{ force: true }
|
||||||
|
);
|
||||||
|
},
|
||||||
|
setStyle: (
|
||||||
|
styleProperties: "geojson" | "raster" | "both" | "layer" | "",
|
||||||
|
zoom?: number
|
||||||
|
) => {
|
||||||
|
let url = "?debug";
|
||||||
|
switch (styleProperties) {
|
||||||
|
case "geojson":
|
||||||
|
url += "&style=http://localhost:8888/geojson-style.json";
|
||||||
|
break;
|
||||||
|
case "raster":
|
||||||
|
url += "&style=http://localhost:8888/raster-style.json";
|
||||||
|
break;
|
||||||
|
case "both":
|
||||||
|
url += "&style=http://localhost:8888/geojson-raster-style.json";
|
||||||
|
break;
|
||||||
|
case "layer":
|
||||||
|
url += "&style=http://localhost:8888/example-layer-style.json";
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (zoom) {
|
||||||
|
url += "#" + zoom + "/41.3805/2.1635";
|
||||||
|
}
|
||||||
|
cy.visit("http://localhost:8888/" + url);
|
||||||
|
if (styleProperties) {
|
||||||
|
cy.on("window:confirm", () => true);
|
||||||
|
}
|
||||||
|
cy.get(".maputnik-toolbar-link").should("be.visible");
|
||||||
|
},
|
||||||
|
fillLayersModal: (opts: {type: string, layer?: string, id?: string}) => {
|
||||||
|
var type = opts.type;
|
||||||
|
var layer = opts.layer;
|
||||||
|
var id;
|
||||||
|
if (opts.id) {
|
||||||
|
id = opts.id;
|
||||||
|
} else {
|
||||||
|
id = `${type}:${uuid()}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
cy.get(
|
||||||
|
this.get.dataAttribute("add-layer.layer-type", "select")
|
||||||
|
).select(type);
|
||||||
|
cy.get(this.get.dataAttribute("add-layer.layer-id", "input")).type(id);
|
||||||
|
if (layer) {
|
||||||
|
cy.get(
|
||||||
|
this.get.dataAttribute("add-layer.layer-source-block", "input")
|
||||||
|
).type(layer);
|
||||||
|
}
|
||||||
|
this.when.click("add-layer");
|
||||||
|
|
||||||
|
return id;
|
||||||
|
},
|
||||||
|
|
||||||
|
typeKeys: (keys: string) => {
|
||||||
|
cy.get("body").type(keys);
|
||||||
|
},
|
||||||
|
|
||||||
|
click: (selector: string) => {
|
||||||
|
this.helper.when.click(selector);
|
||||||
|
},
|
||||||
|
|
||||||
|
clickZoomin: () => {
|
||||||
|
cy.get(".maplibregl-ctrl-zoom-in").click();
|
||||||
|
},
|
||||||
|
|
||||||
|
selectWithin: (selector: string, value: string) => {
|
||||||
|
this.when.within(selector, () => {
|
||||||
|
cy.get("select").select(value);
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
select: (selector: string, value: string) => {
|
||||||
|
this.helper.get.element(selector).select(value);
|
||||||
|
},
|
||||||
|
|
||||||
|
focus: (selector: string) => {
|
||||||
|
this.helper.when.focus(selector);
|
||||||
|
},
|
||||||
|
|
||||||
|
setValue: (selector: string, text: string) => {
|
||||||
|
cy.get(selector).clear().type(text, { parseSpecialCharSequences: false });
|
||||||
|
},
|
||||||
|
|
||||||
|
closeModal: (key: string) => {
|
||||||
|
this.helper.when.waitUntil(() => this.helper.get.element(key));
|
||||||
|
this.when.click(key + ".close-modal");
|
||||||
|
},
|
||||||
|
|
||||||
|
openLayersModal: () => {
|
||||||
|
this.helper.when.click("layer-list:add-layer");
|
||||||
|
|
||||||
|
cy.get(this.get.dataAttribute("modal:add-layer")).should("exist");
|
||||||
|
cy.get(this.get.dataAttribute("modal:add-layer")).should("be.visible");
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
public get = {
|
||||||
|
isMac: () => {
|
||||||
|
return Cypress.platform === "darwin";
|
||||||
|
},
|
||||||
|
styleFromWindow: (win: Window) => {
|
||||||
|
const styleId = win.localStorage.getItem("maputnik:latest_style");
|
||||||
|
const styleItem = win.localStorage.getItem(`maputnik:style:${styleId}`);
|
||||||
|
const obj = JSON.parse(styleItem || "");
|
||||||
|
return obj;
|
||||||
|
},
|
||||||
|
exampleFileUrl: () => {
|
||||||
|
return "http://localhost:8888/example-style.json";
|
||||||
|
},
|
||||||
|
dataAttribute: (key: string, selector?: string): string => {
|
||||||
|
return `*[data-wd-key='${key}'] ${selector || ""}`;
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
public should = {
|
||||||
|
canvasBeFocused: () => {
|
||||||
|
this.when.within("maplibre:map", () => {
|
||||||
|
cy.get("canvas").should("be.focused");
|
||||||
|
});
|
||||||
|
},
|
||||||
|
notExist: (selector: string) => {
|
||||||
|
cy.get(selector).should("not.exist");
|
||||||
|
},
|
||||||
|
beFocused: (selector: string) => {
|
||||||
|
this.helper.get.element(selector).should("have.focus");
|
||||||
|
},
|
||||||
|
|
||||||
|
notBeFocused: (selector: string) => {
|
||||||
|
this.helper.get.element(selector).should("not.have.focus");
|
||||||
|
},
|
||||||
|
|
||||||
|
beVisible: (selector: string) => {
|
||||||
|
this.helper.get.element(selector).should("be.visible");
|
||||||
|
},
|
||||||
|
|
||||||
|
notBeVisible: (selector: string) => {
|
||||||
|
this.helper.get.element(selector).should("not.be.visible");
|
||||||
|
},
|
||||||
|
|
||||||
|
equalStyleStore: (getter: (obj: any) => any, styleObj: any) => {
|
||||||
|
cy.window().then((win: any) => {
|
||||||
|
const obj = this.get.styleFromWindow(win);
|
||||||
|
assert.deepEqual(getter(obj), styleObj);
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
styleStoreEqualToExampleFileData: () => {
|
||||||
|
cy.window().then((win: any) => {
|
||||||
|
const obj = this.get.styleFromWindow(win);
|
||||||
|
cy.fixture("example-style.json").should("deep.equal", obj);
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
exist: (selector: string) => {
|
||||||
|
this.helper.get.element(selector).should("exist");
|
||||||
|
},
|
||||||
|
beSelected: (selector: string, value: string) => {
|
||||||
|
this.helper.get.element(selector).find(`option[value="${value}"]`).should("be.selected");
|
||||||
|
},
|
||||||
|
containText: (selector: string, text: string) => {
|
||||||
|
this.helper.get.element(selector).should("contain.text", text);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
@@ -0,0 +1,97 @@
|
|||||||
|
import MaputnikDriver from "./driver";
|
||||||
|
|
||||||
|
describe("history", () => {
|
||||||
|
let { beforeAndAfter, given, when, get, should } = new MaputnikDriver();
|
||||||
|
beforeAndAfter();
|
||||||
|
|
||||||
|
let undoKeyCombo: string;
|
||||||
|
let redoKeyCombo: string;
|
||||||
|
|
||||||
|
before(() => {
|
||||||
|
const isMac = get.isMac();
|
||||||
|
undoKeyCombo = isMac ? "{meta}z" : "{ctrl}z";
|
||||||
|
redoKeyCombo = isMac ? "{meta}{shift}z" : "{ctrl}y";
|
||||||
|
});
|
||||||
|
|
||||||
|
it("undo/redo", () => {
|
||||||
|
when.setStyle("geojson");
|
||||||
|
when.openLayersModal();
|
||||||
|
|
||||||
|
should.equalStyleStore((a: any) => a.layers, []);
|
||||||
|
|
||||||
|
when.fillLayersModal({
|
||||||
|
id: "step 1",
|
||||||
|
type: "background",
|
||||||
|
});
|
||||||
|
|
||||||
|
should.equalStyleStore(
|
||||||
|
(a: any) => a.layers,
|
||||||
|
[
|
||||||
|
{
|
||||||
|
id: "step 1",
|
||||||
|
type: "background",
|
||||||
|
},
|
||||||
|
]
|
||||||
|
);
|
||||||
|
|
||||||
|
when.openLayersModal();
|
||||||
|
when.fillLayersModal({
|
||||||
|
id: "step 2",
|
||||||
|
type: "background",
|
||||||
|
});
|
||||||
|
|
||||||
|
should.equalStyleStore(
|
||||||
|
(a: any) => a.layers,
|
||||||
|
[
|
||||||
|
{
|
||||||
|
id: "step 1",
|
||||||
|
type: "background",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: "step 2",
|
||||||
|
type: "background",
|
||||||
|
},
|
||||||
|
]
|
||||||
|
);
|
||||||
|
|
||||||
|
when.typeKeys(undoKeyCombo);
|
||||||
|
should.equalStyleStore(
|
||||||
|
(a: any) => a.layers,
|
||||||
|
[
|
||||||
|
{
|
||||||
|
id: "step 1",
|
||||||
|
type: "background",
|
||||||
|
},
|
||||||
|
]
|
||||||
|
);
|
||||||
|
|
||||||
|
when.typeKeys(undoKeyCombo);
|
||||||
|
should.equalStyleStore((a: any) => a.layers, []);
|
||||||
|
|
||||||
|
when.typeKeys(redoKeyCombo);
|
||||||
|
should.equalStyleStore(
|
||||||
|
(a: any) => a.layers,
|
||||||
|
[
|
||||||
|
{
|
||||||
|
id: "step 1",
|
||||||
|
type: "background",
|
||||||
|
},
|
||||||
|
]
|
||||||
|
);
|
||||||
|
|
||||||
|
when.typeKeys(redoKeyCombo);
|
||||||
|
should.equalStyleStore(
|
||||||
|
(a: any) => a.layers,
|
||||||
|
[
|
||||||
|
{
|
||||||
|
id: "step 1",
|
||||||
|
type: "background",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: "step 2",
|
||||||
|
type: "background",
|
||||||
|
},
|
||||||
|
]
|
||||||
|
);
|
||||||
|
});
|
||||||
|
});
|
||||||
@@ -0,0 +1,61 @@
|
|||||||
|
import MaputnikDriver from "./driver";
|
||||||
|
|
||||||
|
describe("keyboard", () => {
|
||||||
|
let { beforeAndAfter, given, when, get, should } = new MaputnikDriver();
|
||||||
|
beforeAndAfter();
|
||||||
|
describe("shortcuts", () => {
|
||||||
|
beforeEach(() => {
|
||||||
|
given.setupInterception();
|
||||||
|
when.setStyle("");
|
||||||
|
});
|
||||||
|
|
||||||
|
it("ESC should unfocus", () => {
|
||||||
|
const targetSelector = "maputnik-select";
|
||||||
|
when.focus(targetSelector);
|
||||||
|
should.beFocused(targetSelector);
|
||||||
|
|
||||||
|
when.typeKeys("{esc}");
|
||||||
|
expect(should.notBeFocused(targetSelector));
|
||||||
|
});
|
||||||
|
|
||||||
|
it("'?' should show shortcuts modal", () => {
|
||||||
|
when.typeKeys("?");
|
||||||
|
should.beVisible("modal:shortcuts");
|
||||||
|
});
|
||||||
|
|
||||||
|
it("'o' should show open modal", () => {
|
||||||
|
when.typeKeys("o");
|
||||||
|
should.beVisible("modal:open");
|
||||||
|
});
|
||||||
|
|
||||||
|
it("'e' should show export modal", () => {
|
||||||
|
when.typeKeys("e");
|
||||||
|
should.beVisible("modal:export");
|
||||||
|
});
|
||||||
|
|
||||||
|
it("'d' should show sources modal", () => {
|
||||||
|
when.typeKeys("d");
|
||||||
|
should.beVisible("modal:sources");
|
||||||
|
});
|
||||||
|
|
||||||
|
it("'s' should show settings modal", () => {
|
||||||
|
when.typeKeys("s");
|
||||||
|
should.beVisible("modal:settings");
|
||||||
|
});
|
||||||
|
|
||||||
|
it("'i' should change map to inspect mode", () => {
|
||||||
|
when.typeKeys("i");
|
||||||
|
should.beSelected("nav:inspect", "inspect");
|
||||||
|
});
|
||||||
|
|
||||||
|
it("'m' should focus map", () => {
|
||||||
|
when.typeKeys("m");
|
||||||
|
should.canvasBeFocused();
|
||||||
|
});
|
||||||
|
|
||||||
|
it("'!' should show debug modal", () => {
|
||||||
|
when.typeKeys("!");
|
||||||
|
should.beVisible("modal:debug");
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
@@ -0,0 +1,491 @@
|
|||||||
|
import { v1 as uuid } from "uuid";
|
||||||
|
import MaputnikDriver from "./driver";
|
||||||
|
|
||||||
|
describe("layers", () => {
|
||||||
|
let { beforeAndAfter, given, when, get, should } = new MaputnikDriver();
|
||||||
|
beforeAndAfter();
|
||||||
|
beforeEach(() => {
|
||||||
|
when.setStyle("both");
|
||||||
|
when.openLayersModal();
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("ops", () => {
|
||||||
|
it("delete", () => {
|
||||||
|
var id = when.fillLayersModal({
|
||||||
|
type: "background",
|
||||||
|
});
|
||||||
|
|
||||||
|
should.equalStyleStore(
|
||||||
|
(a: any) => a.layers,
|
||||||
|
[
|
||||||
|
{
|
||||||
|
id: id,
|
||||||
|
type: "background",
|
||||||
|
},
|
||||||
|
]
|
||||||
|
);
|
||||||
|
|
||||||
|
when.click("layer-list-item:" + id + ":delete");
|
||||||
|
|
||||||
|
should.equalStyleStore((a: any) => a.layers, []);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("duplicate", () => {
|
||||||
|
var styleObj;
|
||||||
|
var id = when.fillLayersModal({
|
||||||
|
type: "background",
|
||||||
|
});
|
||||||
|
|
||||||
|
should.equalStyleStore(
|
||||||
|
(a: any) => a.layers,
|
||||||
|
[
|
||||||
|
{
|
||||||
|
id: id,
|
||||||
|
type: "background",
|
||||||
|
},
|
||||||
|
]
|
||||||
|
);
|
||||||
|
|
||||||
|
when.click("layer-list-item:" + id + ":copy");
|
||||||
|
|
||||||
|
should.equalStyleStore(
|
||||||
|
(a: any) => a.layers,
|
||||||
|
[
|
||||||
|
{
|
||||||
|
id: id + "-copy",
|
||||||
|
type: "background",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: id,
|
||||||
|
type: "background",
|
||||||
|
},
|
||||||
|
]
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("hide", () => {
|
||||||
|
var styleObj;
|
||||||
|
var id = when.fillLayersModal({
|
||||||
|
type: "background",
|
||||||
|
});
|
||||||
|
|
||||||
|
should.equalStyleStore(
|
||||||
|
(a: any) => a.layers,
|
||||||
|
[
|
||||||
|
{
|
||||||
|
id: id,
|
||||||
|
type: "background",
|
||||||
|
},
|
||||||
|
]
|
||||||
|
);
|
||||||
|
|
||||||
|
when.click("layer-list-item:" + id + ":toggle-visibility");
|
||||||
|
|
||||||
|
should.equalStyleStore(
|
||||||
|
(a: any) => a.layers,
|
||||||
|
[
|
||||||
|
{
|
||||||
|
id: id,
|
||||||
|
type: "background",
|
||||||
|
layout: {
|
||||||
|
visibility: "none",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
]
|
||||||
|
);
|
||||||
|
|
||||||
|
when.click("layer-list-item:" + id + ":toggle-visibility");
|
||||||
|
|
||||||
|
should.equalStyleStore(
|
||||||
|
(a: any) => a.layers,
|
||||||
|
[
|
||||||
|
{
|
||||||
|
id: id,
|
||||||
|
type: "background",
|
||||||
|
layout: {
|
||||||
|
visibility: "visible",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
]
|
||||||
|
);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("background", () => {
|
||||||
|
it("add", () => {
|
||||||
|
var id = when.fillLayersModal({
|
||||||
|
type: "background",
|
||||||
|
});
|
||||||
|
|
||||||
|
should.equalStyleStore(
|
||||||
|
(a: any) => a.layers,
|
||||||
|
[
|
||||||
|
{
|
||||||
|
id: id,
|
||||||
|
type: "background",
|
||||||
|
},
|
||||||
|
]
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("modify", () => {
|
||||||
|
function createBackground() {
|
||||||
|
// Setup
|
||||||
|
var id = uuid();
|
||||||
|
|
||||||
|
when.selectWithin("add-layer.layer-type", "background");
|
||||||
|
when.setValue(
|
||||||
|
get.dataAttribute("add-layer.layer-id", "input"),
|
||||||
|
"background:" + id
|
||||||
|
);
|
||||||
|
|
||||||
|
when.click("add-layer");
|
||||||
|
|
||||||
|
should.equalStyleStore(
|
||||||
|
(a: any) => a.layers,
|
||||||
|
[
|
||||||
|
{
|
||||||
|
id: "background:" + id,
|
||||||
|
type: "background",
|
||||||
|
},
|
||||||
|
]
|
||||||
|
);
|
||||||
|
return id;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ====> THESE SHOULD BE FROM THE SPEC
|
||||||
|
describe("layer", () => {
|
||||||
|
it("expand/collapse");
|
||||||
|
it("id", () => {
|
||||||
|
var bgId = createBackground();
|
||||||
|
|
||||||
|
when.click("layer-list-item:background:" + bgId);
|
||||||
|
|
||||||
|
var id = uuid();
|
||||||
|
when.setValue(
|
||||||
|
get.dataAttribute("layer-editor.layer-id", "input"),
|
||||||
|
"foobar:" + id
|
||||||
|
);
|
||||||
|
when.click("min-zoom");
|
||||||
|
|
||||||
|
should.equalStyleStore(
|
||||||
|
(a: any) => a.layers,
|
||||||
|
[
|
||||||
|
{
|
||||||
|
id: "foobar:" + id,
|
||||||
|
type: "background",
|
||||||
|
},
|
||||||
|
]
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("min-zoom", () => {
|
||||||
|
var bgId = createBackground();
|
||||||
|
|
||||||
|
when.click("layer-list-item:background:" + bgId);
|
||||||
|
when.setValue(
|
||||||
|
get.dataAttribute("min-zoom", 'input[type="text"]'),
|
||||||
|
"1"
|
||||||
|
);
|
||||||
|
|
||||||
|
when.click("layer-editor.layer-id");
|
||||||
|
|
||||||
|
should.equalStyleStore(
|
||||||
|
(a: any) => a.layers,
|
||||||
|
[
|
||||||
|
{
|
||||||
|
id: "background:" + bgId,
|
||||||
|
type: "background",
|
||||||
|
minzoom: 1,
|
||||||
|
},
|
||||||
|
]
|
||||||
|
);
|
||||||
|
|
||||||
|
// AND RESET!
|
||||||
|
// driver.setValue(driver.get.dataAttribute("min-zoom", "input"), "")
|
||||||
|
// driver.click(driver.get.dataAttribute("max-zoom", "input"));
|
||||||
|
|
||||||
|
// driver.isStyleStoreEqual((a: any) => a.layers, [
|
||||||
|
// {
|
||||||
|
// "id": 'background:'+bgId,
|
||||||
|
// "type": 'background'
|
||||||
|
// }
|
||||||
|
// ]);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("max-zoom", () => {
|
||||||
|
var bgId = createBackground();
|
||||||
|
|
||||||
|
when.click("layer-list-item:background:" + bgId);
|
||||||
|
when.setValue(
|
||||||
|
get.dataAttribute("max-zoom", 'input[type="text"]'),
|
||||||
|
"1"
|
||||||
|
);
|
||||||
|
|
||||||
|
when.click("layer-editor.layer-id");
|
||||||
|
|
||||||
|
should.equalStyleStore(
|
||||||
|
(a: any) => a.layers,
|
||||||
|
[
|
||||||
|
{
|
||||||
|
id: "background:" + bgId,
|
||||||
|
type: "background",
|
||||||
|
maxzoom: 1,
|
||||||
|
},
|
||||||
|
]
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("comments", () => {
|
||||||
|
var bgId = createBackground();
|
||||||
|
var id = uuid();
|
||||||
|
|
||||||
|
when.click("layer-list-item:background:" + bgId);
|
||||||
|
when.setValue(get.dataAttribute("layer-comment", "textarea"), id);
|
||||||
|
|
||||||
|
when.click("layer-editor.layer-id");
|
||||||
|
|
||||||
|
should.equalStyleStore(
|
||||||
|
(a: any) => a.layers,
|
||||||
|
[
|
||||||
|
{
|
||||||
|
id: "background:" + bgId,
|
||||||
|
type: "background",
|
||||||
|
metadata: {
|
||||||
|
"maputnik:comment": id,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
]
|
||||||
|
);
|
||||||
|
|
||||||
|
// Unset it again.
|
||||||
|
// TODO: This fails
|
||||||
|
// driver.setValue(driver.getDataAttribute("layer-comment", "textarea"), "");
|
||||||
|
// driver.click(driver.getDataAttribute("min-zoom", "input"));
|
||||||
|
|
||||||
|
// driver.isStyleStoreEqual((a: any) => a.layers, [
|
||||||
|
// {
|
||||||
|
// "id": 'background:'+bgId,
|
||||||
|
// "type": 'background'
|
||||||
|
// }
|
||||||
|
// ]);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("color", () => {
|
||||||
|
var bgId = createBackground();
|
||||||
|
|
||||||
|
when.click("layer-list-item:background:" + bgId);
|
||||||
|
|
||||||
|
when.click("spec-field:background-color");
|
||||||
|
|
||||||
|
should.equalStyleStore(
|
||||||
|
(a: any) => a.layers,
|
||||||
|
[
|
||||||
|
{
|
||||||
|
id: "background:" + bgId,
|
||||||
|
type: "background",
|
||||||
|
},
|
||||||
|
]
|
||||||
|
);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("filter", () => {
|
||||||
|
it("expand/collapse");
|
||||||
|
it("compound filter");
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("paint", () => {
|
||||||
|
it("expand/collapse");
|
||||||
|
it("color");
|
||||||
|
it("pattern");
|
||||||
|
it("opacity");
|
||||||
|
});
|
||||||
|
// <=====
|
||||||
|
|
||||||
|
describe("json-editor", () => {
|
||||||
|
it("expand/collapse");
|
||||||
|
it("modify");
|
||||||
|
|
||||||
|
// TODO
|
||||||
|
it.skip("parse error", () => {
|
||||||
|
var bgId = createBackground();
|
||||||
|
|
||||||
|
when.click("layer-list-item:background:" + bgId);
|
||||||
|
|
||||||
|
var errorSelector = ".CodeMirror-lint-marker-error";
|
||||||
|
should.notExist(errorSelector);
|
||||||
|
|
||||||
|
when.click(".CodeMirror");
|
||||||
|
when.typeKeys(
|
||||||
|
"\uE013\uE013\uE013\uE013\uE013\uE013\uE013\uE013\uE013\uE013\uE013\uE013 {"
|
||||||
|
);
|
||||||
|
should.exist(errorSelector);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("fill", () => {
|
||||||
|
it("add", () => {
|
||||||
|
var id = when.fillLayersModal({
|
||||||
|
type: "fill",
|
||||||
|
layer: "example",
|
||||||
|
});
|
||||||
|
|
||||||
|
should.equalStyleStore(
|
||||||
|
(a: any) => a.layers,
|
||||||
|
[
|
||||||
|
{
|
||||||
|
id: id,
|
||||||
|
type: "fill",
|
||||||
|
source: "example",
|
||||||
|
},
|
||||||
|
]
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
// TODO: Change source
|
||||||
|
it("change source");
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("line", () => {
|
||||||
|
it("add", () => {
|
||||||
|
var id = when.fillLayersModal({
|
||||||
|
type: "line",
|
||||||
|
layer: "example",
|
||||||
|
});
|
||||||
|
|
||||||
|
should.equalStyleStore(
|
||||||
|
(a: any) => a.layers,
|
||||||
|
[
|
||||||
|
{
|
||||||
|
id: id,
|
||||||
|
type: "line",
|
||||||
|
source: "example",
|
||||||
|
},
|
||||||
|
]
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("groups", () => {
|
||||||
|
// TODO
|
||||||
|
// Click each of the layer groups.
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("symbol", () => {
|
||||||
|
it("add", () => {
|
||||||
|
var id = when.fillLayersModal({
|
||||||
|
type: "symbol",
|
||||||
|
layer: "example",
|
||||||
|
});
|
||||||
|
|
||||||
|
should.equalStyleStore(
|
||||||
|
(a: any) => a.layers,
|
||||||
|
[
|
||||||
|
{
|
||||||
|
id: id,
|
||||||
|
type: "symbol",
|
||||||
|
source: "example",
|
||||||
|
},
|
||||||
|
]
|
||||||
|
);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("raster", () => {
|
||||||
|
it("add", () => {
|
||||||
|
var id = when.fillLayersModal({
|
||||||
|
type: "raster",
|
||||||
|
layer: "raster",
|
||||||
|
});
|
||||||
|
|
||||||
|
should.equalStyleStore(
|
||||||
|
(a: any) => a.layers,
|
||||||
|
[
|
||||||
|
{
|
||||||
|
id: id,
|
||||||
|
type: "raster",
|
||||||
|
source: "raster",
|
||||||
|
},
|
||||||
|
]
|
||||||
|
);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("circle", () => {
|
||||||
|
it("add", () => {
|
||||||
|
var id = when.fillLayersModal({
|
||||||
|
type: "circle",
|
||||||
|
layer: "example",
|
||||||
|
});
|
||||||
|
|
||||||
|
should.equalStyleStore(
|
||||||
|
(a: any) => a.layers,
|
||||||
|
[
|
||||||
|
{
|
||||||
|
id: id,
|
||||||
|
type: "circle",
|
||||||
|
source: "example",
|
||||||
|
},
|
||||||
|
]
|
||||||
|
);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("fill extrusion", () => {
|
||||||
|
it("add", () => {
|
||||||
|
var id = when.fillLayersModal({
|
||||||
|
type: "fill-extrusion",
|
||||||
|
layer: "example",
|
||||||
|
});
|
||||||
|
|
||||||
|
should.equalStyleStore(
|
||||||
|
(a: any) => a.layers,
|
||||||
|
[
|
||||||
|
{
|
||||||
|
id: id,
|
||||||
|
type: "fill-extrusion",
|
||||||
|
source: "example",
|
||||||
|
},
|
||||||
|
]
|
||||||
|
);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("groups", () => {
|
||||||
|
it("simple", () => {
|
||||||
|
when.setStyle("geojson");
|
||||||
|
|
||||||
|
when.openLayersModal();
|
||||||
|
when.fillLayersModal({
|
||||||
|
id: "foo",
|
||||||
|
type: "background",
|
||||||
|
});
|
||||||
|
|
||||||
|
when.openLayersModal();
|
||||||
|
when.fillLayersModal({
|
||||||
|
id: "foo_bar",
|
||||||
|
type: "background",
|
||||||
|
});
|
||||||
|
|
||||||
|
when.openLayersModal();
|
||||||
|
when.fillLayersModal({
|
||||||
|
id: "foo_bar_baz",
|
||||||
|
type: "background",
|
||||||
|
});
|
||||||
|
|
||||||
|
should.beVisible("layer-list-item:foo");
|
||||||
|
|
||||||
|
should.notBeVisible("layer-list-item:foo_bar");
|
||||||
|
should.notBeVisible("layer-list-item:foo_bar_baz");
|
||||||
|
|
||||||
|
when.click("layer-list-group:foo-0");
|
||||||
|
|
||||||
|
should.beVisible("layer-list-item:foo");
|
||||||
|
should.beVisible("layer-list-item:foo_bar");
|
||||||
|
should.beVisible("layer-list-item:foo_bar_baz");
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
@@ -0,0 +1,23 @@
|
|||||||
|
import MaputnikDriver from "./driver";
|
||||||
|
|
||||||
|
describe("map", () => {
|
||||||
|
let { beforeAndAfter, given, when, get, should } = new MaputnikDriver();
|
||||||
|
beforeAndAfter();
|
||||||
|
describe("zoom level", () => {
|
||||||
|
it("via url", () => {
|
||||||
|
var zoomLevel = 12.37;
|
||||||
|
when.setStyle("geojson", zoomLevel);
|
||||||
|
should.beVisible("maplibre:ctrl-zoom");
|
||||||
|
should.containText("maplibre:ctrl-zoom", "Zoom: " + zoomLevel);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("via map controls", () => {
|
||||||
|
var zoomLevel = 12.37;
|
||||||
|
when.setStyle("geojson", zoomLevel);
|
||||||
|
|
||||||
|
should.beVisible("maplibre:ctrl-zoom");
|
||||||
|
when.clickZoomin();
|
||||||
|
should.containText("maplibre:ctrl-zoom", "Zoom: "+(zoomLevel + 1));
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
@@ -0,0 +1,154 @@
|
|||||||
|
import MaputnikDriver from "./driver";
|
||||||
|
|
||||||
|
describe("modals", () => {
|
||||||
|
let { beforeAndAfter, given, when, get, should } = new MaputnikDriver();
|
||||||
|
beforeAndAfter();
|
||||||
|
beforeEach(() => {
|
||||||
|
when.setStyle("");
|
||||||
|
});
|
||||||
|
describe("open", () => {
|
||||||
|
beforeEach(() => {
|
||||||
|
when.click("nav:open");
|
||||||
|
});
|
||||||
|
|
||||||
|
it("close", () => {
|
||||||
|
when.closeModal("modal:open");
|
||||||
|
should.notExist("modal:open");
|
||||||
|
});
|
||||||
|
|
||||||
|
it.skip("upload", () => {
|
||||||
|
// HM: I was not able to make the following choose file actually to select a file and close the modal...
|
||||||
|
when.chooseExampleFile();
|
||||||
|
|
||||||
|
should.styleStoreEqualToExampleFileData();
|
||||||
|
});
|
||||||
|
|
||||||
|
it("load from url", () => {
|
||||||
|
var styleFileUrl = get.exampleFileUrl();
|
||||||
|
|
||||||
|
when.setValue(get.dataAttribute("modal:open.url.input"), styleFileUrl);
|
||||||
|
when.click("modal:open.url.button");
|
||||||
|
when.waitForExampleFileRequset();
|
||||||
|
|
||||||
|
should.styleStoreEqualToExampleFileData();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("shortcuts", () => {
|
||||||
|
it("open/close", () => {
|
||||||
|
when.setStyle("");
|
||||||
|
when.typeKeys("?");
|
||||||
|
when.closeModal("modal:shortcuts");
|
||||||
|
should.notExist("modal:shortcuts");
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("export", () => {
|
||||||
|
beforeEach(() => {
|
||||||
|
when.click("nav:export");
|
||||||
|
});
|
||||||
|
|
||||||
|
it("close", () => {
|
||||||
|
when.closeModal("modal:export");
|
||||||
|
should.notExist("modal:export");
|
||||||
|
});
|
||||||
|
|
||||||
|
// TODO: Work out how to download a file and check the contents
|
||||||
|
it("download");
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("sources", () => {
|
||||||
|
it("active sources");
|
||||||
|
it("public source");
|
||||||
|
it("add new source");
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("inspect", () => {
|
||||||
|
it("toggle", () => {
|
||||||
|
when.setStyle("geojson");
|
||||||
|
|
||||||
|
when.selectWithin("nav:inspect", "inspect");
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("style settings", () => {
|
||||||
|
beforeEach(() => {
|
||||||
|
when.click("nav:settings");
|
||||||
|
});
|
||||||
|
|
||||||
|
it("name", () => {
|
||||||
|
when.setValue(get.dataAttribute("modal:settings.name"), "foobar");
|
||||||
|
when.click("modal:settings.owner");
|
||||||
|
|
||||||
|
should.equalStyleStore((obj) => obj.name, "foobar");
|
||||||
|
});
|
||||||
|
it("owner", () => {
|
||||||
|
when.setValue(get.dataAttribute("modal:settings.owner"), "foobar");
|
||||||
|
when.click("modal:settings.name");
|
||||||
|
|
||||||
|
should.equalStyleStore((obj) => obj.owner, "foobar");
|
||||||
|
});
|
||||||
|
it("sprite url", () => {
|
||||||
|
when.setValue(
|
||||||
|
get.dataAttribute("modal:settings.sprite"),
|
||||||
|
"http://example.com"
|
||||||
|
);
|
||||||
|
when.click("modal:settings.name");
|
||||||
|
|
||||||
|
should.equalStyleStore((obj) => obj.sprite, "http://example.com");
|
||||||
|
});
|
||||||
|
it("glyphs url", () => {
|
||||||
|
var glyphsUrl = "http://example.com/{fontstack}/{range}.pbf";
|
||||||
|
when.setValue(get.dataAttribute("modal:settings.glyphs"), glyphsUrl);
|
||||||
|
when.click("modal:settings.name");
|
||||||
|
|
||||||
|
should.equalStyleStore((obj) => obj.glyphs, glyphsUrl);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("maptiler access token", () => {
|
||||||
|
var apiKey = "testing123";
|
||||||
|
when.setValue(
|
||||||
|
get.dataAttribute(
|
||||||
|
"modal:settings.maputnik:openmaptiles_access_token"
|
||||||
|
),
|
||||||
|
apiKey
|
||||||
|
);
|
||||||
|
when.click("modal:settings.name");
|
||||||
|
|
||||||
|
should.equalStyleStore(
|
||||||
|
(obj) => obj.metadata["maputnik:openmaptiles_access_token"],
|
||||||
|
apiKey
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("thunderforest access token", () => {
|
||||||
|
var apiKey = "testing123";
|
||||||
|
when.setValue(
|
||||||
|
get.dataAttribute(
|
||||||
|
"modal:settings.maputnik:thunderforest_access_token"
|
||||||
|
),
|
||||||
|
apiKey
|
||||||
|
);
|
||||||
|
when.click("modal:settings.name");
|
||||||
|
|
||||||
|
should.equalStyleStore(
|
||||||
|
(obj) => obj.metadata["maputnik:thunderforest_access_token"],
|
||||||
|
apiKey
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("style renderer", () => {
|
||||||
|
cy.on("uncaught:exception", () => false); // this is due to the fact that this is an invalid style for openlayers
|
||||||
|
when.select("modal:settings.maputnik:renderer", "ol");
|
||||||
|
should.beSelected("modal:settings.maputnik:renderer", "ol");
|
||||||
|
|
||||||
|
when.click("modal:settings.name");
|
||||||
|
|
||||||
|
should.equalStyleStore((obj) => obj.metadata["maputnik:renderer"], "ol");
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("sources", () => {
|
||||||
|
it("toggle");
|
||||||
|
});
|
||||||
|
});
|
||||||
@@ -3,7 +3,7 @@
|
|||||||
"version": 8,
|
"version": 8,
|
||||||
"name": "Test Style",
|
"name": "Test Style",
|
||||||
"metadata": {
|
"metadata": {
|
||||||
"maputnik:renderer": "mbgljs"
|
"maputnik:renderer": "mlgljs"
|
||||||
},
|
},
|
||||||
"sources": {},
|
"sources": {},
|
||||||
"glyphs": "https://example.local/fonts/{fontstack}/{range}.pbf",
|
"glyphs": "https://example.local/fonts/{fontstack}/{range}.pbf",
|
||||||
@@ -3,7 +3,7 @@
|
|||||||
"version": 8,
|
"version": 8,
|
||||||
"name": "Test Style",
|
"name": "Test Style",
|
||||||
"metadata": {
|
"metadata": {
|
||||||
"maputnik:renderer": "mbgljs"
|
"maputnik:renderer": "mlgljs"
|
||||||
},
|
},
|
||||||
"sources": {},
|
"sources": {},
|
||||||
"glyphs": "https://example.local/fonts/{fontstack}/{range}.pbf",
|
"glyphs": "https://example.local/fonts/{fontstack}/{range}.pbf",
|
||||||
@@ -0,0 +1,34 @@
|
|||||||
|
{
|
||||||
|
"id": "test-style",
|
||||||
|
"version": 8,
|
||||||
|
"name": "Test Style",
|
||||||
|
"metadata": {
|
||||||
|
"maputnik:renderer": "mlgljs"
|
||||||
|
},
|
||||||
|
"sources": {
|
||||||
|
"example": {
|
||||||
|
"type": "vector",
|
||||||
|
"data": {
|
||||||
|
"type": "FeatureCollection",
|
||||||
|
"features":[{
|
||||||
|
"type": "Feature",
|
||||||
|
"properties": {
|
||||||
|
"name": "Dinagat Islands"
|
||||||
|
},
|
||||||
|
"geometry":{
|
||||||
|
"type": "Point",
|
||||||
|
"coordinates": [125.6, 10.1]
|
||||||
|
}
|
||||||
|
}]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"raster": {
|
||||||
|
"tileSize": 256,
|
||||||
|
"tiles": ["http://localhost/example/{x}/{y}/{z}"],
|
||||||
|
"type": "raster"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"glyphs": "https://example.local/fonts/{fontstack}/{range}.pbf",
|
||||||
|
"sprites": "https://example.local/fonts/{fontstack}/{range}.pbf",
|
||||||
|
"layers": []
|
||||||
|
}
|
||||||
@@ -0,0 +1,29 @@
|
|||||||
|
{
|
||||||
|
"id": "test-style",
|
||||||
|
"version": 8,
|
||||||
|
"name": "Test Style",
|
||||||
|
"metadata": {
|
||||||
|
"maputnik:renderer": "mlgljs"
|
||||||
|
},
|
||||||
|
"sources": {
|
||||||
|
"example": {
|
||||||
|
"type": "vector",
|
||||||
|
"data": {
|
||||||
|
"type": "FeatureCollection",
|
||||||
|
"features":[{
|
||||||
|
"type": "Feature",
|
||||||
|
"properties": {
|
||||||
|
"name": "Dinagat Islands"
|
||||||
|
},
|
||||||
|
"geometry":{
|
||||||
|
"type": "Point",
|
||||||
|
"coordinates": [125.6, 10.1]
|
||||||
|
}
|
||||||
|
}]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"glyphs": "https://example.local/fonts/{fontstack}/{range}.pbf",
|
||||||
|
"sprites": "https://example.local/fonts/{fontstack}/{range}.pbf",
|
||||||
|
"layers": []
|
||||||
|
}
|
||||||
@@ -0,0 +1,18 @@
|
|||||||
|
{
|
||||||
|
"id": "test-style",
|
||||||
|
"version": 8,
|
||||||
|
"name": "Test Style",
|
||||||
|
"metadata": {
|
||||||
|
"maputnik:renderer": "mlgljs"
|
||||||
|
},
|
||||||
|
"sources": {
|
||||||
|
"raster": {
|
||||||
|
"tileSize": 256,
|
||||||
|
"tiles": ["http://localhost/example/{x}/{y}/{z}"],
|
||||||
|
"type": "raster"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"glyphs": "https://example.local/fonts/{fontstack}/{range}.pbf",
|
||||||
|
"sprites": "https://example.local/fonts/{fontstack}/{range}.pbf",
|
||||||
|
"layers": []
|
||||||
|
}
|
||||||
@@ -0,0 +1,37 @@
|
|||||||
|
/// <reference types="cypress" />
|
||||||
|
// ***********************************************
|
||||||
|
// This example commands.ts shows you how to
|
||||||
|
// create various custom commands and overwrite
|
||||||
|
// existing commands.
|
||||||
|
//
|
||||||
|
// For more comprehensive examples of custom
|
||||||
|
// commands please read more here:
|
||||||
|
// https://on.cypress.io/custom-commands
|
||||||
|
// ***********************************************
|
||||||
|
//
|
||||||
|
//
|
||||||
|
// -- This is a parent command --
|
||||||
|
// Cypress.Commands.add('login', (email, password) => { ... })
|
||||||
|
//
|
||||||
|
//
|
||||||
|
// -- This is a child command --
|
||||||
|
// Cypress.Commands.add('drag', { prevSubject: 'element'}, (subject, options) => { ... })
|
||||||
|
//
|
||||||
|
//
|
||||||
|
// -- This is a dual command --
|
||||||
|
// Cypress.Commands.add('dismiss', { prevSubject: 'optional'}, (subject, options) => { ... })
|
||||||
|
//
|
||||||
|
//
|
||||||
|
// -- This will overwrite an existing command --
|
||||||
|
// Cypress.Commands.overwrite('visit', (originalFn, url, options) => { ... })
|
||||||
|
//
|
||||||
|
// declare global {
|
||||||
|
// namespace Cypress {
|
||||||
|
// interface Chainable {
|
||||||
|
// login(email: string, password: string): Chainable<void>
|
||||||
|
// drag(subject: string, options?: Partial<TypeOptions>): Chainable<Element>
|
||||||
|
// dismiss(subject: string, options?: Partial<TypeOptions>): Chainable<Element>
|
||||||
|
// visit(originalFn: CommandOriginalFn, url: string, options: Partial<VisitOptions>): Chainable<Element>
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// }
|
||||||
@@ -0,0 +1,21 @@
|
|||||||
|
// ***********************************************************
|
||||||
|
// This example support/e2e.ts is processed and
|
||||||
|
// loaded automatically before your test files.
|
||||||
|
//
|
||||||
|
// This is a great place to put global configuration and
|
||||||
|
// behavior that modifies Cypress.
|
||||||
|
//
|
||||||
|
// You can change the location of this file or turn off
|
||||||
|
// automatically serving support files with the
|
||||||
|
// 'supportFile' configuration option.
|
||||||
|
//
|
||||||
|
// You can read more here:
|
||||||
|
// https://on.cypress.io/configuration
|
||||||
|
// ***********************************************************
|
||||||
|
|
||||||
|
// Import commands.js using ES2015 syntax:
|
||||||
|
import "cypress-plugin-tab";
|
||||||
|
import "./commands";
|
||||||
|
|
||||||
|
// Alternatively you can use CommonJS syntax:
|
||||||
|
// require('./commands')
|
||||||
@@ -2,9 +2,10 @@
|
|||||||
<html lang="en">
|
<html lang="en">
|
||||||
<head>
|
<head>
|
||||||
<meta charset="utf-8">
|
<meta charset="utf-8">
|
||||||
<title><%= htmlWebpackPlugin.options.title %></title>
|
<title>Maputnik</title>
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||||
<link rel="manifest" href="manifest.json">
|
<link rel="manifest" href="src/manifest.json">
|
||||||
|
<link rel="icon" href="src/favicon.ico" type="image/x-icon" />
|
||||||
<style>
|
<style>
|
||||||
html {
|
html {
|
||||||
background-color: rgb(28, 31, 36);
|
background-color: rgb(28, 31, 36);
|
||||||
@@ -23,7 +24,7 @@
|
|||||||
align-items: center;
|
align-items: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
.loading__logo svg {
|
.loading__logo img {
|
||||||
width: 200px;
|
width: 200px;
|
||||||
height: 200px;
|
height: 200px;
|
||||||
}
|
}
|
||||||
@@ -38,7 +39,6 @@
|
|||||||
</style>
|
</style>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<!-- TODO: Import dynamically -->
|
|
||||||
<!-- From <https://github.com/hail2u/color-blindness-emulation> -->
|
<!-- From <https://github.com/hail2u/color-blindness-emulation> -->
|
||||||
<svg
|
<svg
|
||||||
aria-hidden="true"
|
aria-hidden="true"
|
||||||
@@ -123,10 +123,10 @@
|
|||||||
<div id="app"></div>
|
<div id="app"></div>
|
||||||
<div class="loading">
|
<div class="loading">
|
||||||
<div class="loading__logo">
|
<div class="loading__logo">
|
||||||
<!-- replaced by 'html-webpack-inline-svg-plugin' -->
|
|
||||||
<img inline src="node_modules/maputnik-design/logos/logo-loading.svg" />
|
<img inline src="node_modules/maputnik-design/logos/logo-loading.svg" />
|
||||||
</div>
|
</div>
|
||||||
<div class="loading__text">Loading…</div>
|
<div class="loading__text">Loading…</div>
|
||||||
</div>
|
</div>
|
||||||
|
<script type="module" src="/src/index.jsx"></script>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
Generated
+6578
-38098
File diff suppressed because it is too large
Load Diff
+35
-84
@@ -1,22 +1,17 @@
|
|||||||
{
|
{
|
||||||
"name": "maputnik",
|
"name": "maputnik",
|
||||||
"version": "2.0.0-pre.1",
|
"version": "2.0.0-pre.2",
|
||||||
"description": "A MapLibre GL visual style editor",
|
"description": "A MapLibre GL visual style editor",
|
||||||
"main": "''",
|
"main": "''",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"stats": "webpack --config config/webpack.production.config.js --progress=profile --json > stats.json",
|
"start": "vite",
|
||||||
"build": "webpack --config config/webpack.production.config.js --progress=profile --color",
|
"build": "tsc && vite build",
|
||||||
"profiling-build": "webpack --config config/webpack.profiling.config.js --progress=profile --color",
|
"lint": "eslint ./src --ext ts,tsx,js,jsx --report-unused-disable-directives --max-warnings 0 && npm run lint-css",
|
||||||
"test": "cross-env NODE_ENV=test wdio config/wdio.conf.js",
|
"test": "cypress run",
|
||||||
"test-watch": "cross-env NODE_ENV=test wdio config/wdio.conf.js --watch",
|
"cy:open": "cypress open",
|
||||||
"start": "webpack-dev-server --progress=profile --color --config config/webpack.config.js",
|
|
||||||
"start-prod": "webpack-dev-server --progress=profile --color --config config/webpack.production.config.js",
|
|
||||||
"start-sandbox": "webpack-dev-server --disable-host-check --host 0.0.0.0 --progress=profile --color --config config/webpack.production.config.js",
|
|
||||||
"lint-js": "eslint --ext js --ext jsx src test",
|
|
||||||
"lint-css": "stylelint \"src/styles/*.scss\"",
|
"lint-css": "stylelint \"src/styles/*.scss\"",
|
||||||
"lint": "npm run lint-js && npm run lint-css",
|
"storybook": "storybook dev -h 0.0.0.0 -p 6006",
|
||||||
"storybook": "start-storybook -h 0.0.0.0 -p 6006",
|
"build-storybook": "storybook build -o build/storybook"
|
||||||
"build-storybook": "build-storybook -o build/storybook"
|
|
||||||
},
|
},
|
||||||
"repository": {
|
"repository": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
@@ -26,9 +21,9 @@
|
|||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"homepage": "https://github.com/maputnik/editor#readme",
|
"homepage": "https://github.com/maputnik/editor#readme",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@babel/runtime": "^7.17.9",
|
|
||||||
"@mapbox/mapbox-gl-rtl-text": "^0.2.3",
|
"@mapbox/mapbox-gl-rtl-text": "^0.2.3",
|
||||||
"@maplibre/maplibre-gl-style-spec": "^17.0.1",
|
"@maplibre/maplibre-gl-style-spec": "^17.0.1",
|
||||||
|
"@mdi/js": "^6.6.96",
|
||||||
"@mdi/react": "^1.5.0",
|
"@mdi/react": "^1.5.0",
|
||||||
"array-move": "^4.0.0",
|
"array-move": "^4.0.0",
|
||||||
"buffer": "^6.0.3",
|
"buffer": "^6.0.3",
|
||||||
@@ -89,87 +84,43 @@
|
|||||||
]
|
]
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"eslintConfig": {
|
|
||||||
"plugins": [
|
|
||||||
"react"
|
|
||||||
],
|
|
||||||
"extends": [
|
|
||||||
"plugin:react/recommended"
|
|
||||||
],
|
|
||||||
"env": {
|
|
||||||
"browser": true,
|
|
||||||
"node": true,
|
|
||||||
"es6": true
|
|
||||||
},
|
|
||||||
"parser": "@babel/eslint-parser",
|
|
||||||
"parserOptions": {
|
|
||||||
"ecmaVersion": 6,
|
|
||||||
"sourceType": "module",
|
|
||||||
"ecmaFeatures": {
|
|
||||||
"impliedStrict": true,
|
|
||||||
"experimentalObjectRestSpread": true,
|
|
||||||
"jsx": true
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"settings": {
|
|
||||||
"react": {
|
|
||||||
"version": "detect"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@babel/core": "^7.17.9",
|
"@rollup/plugin-replace": "^5.0.5",
|
||||||
"@babel/eslint-parser": "^7.19.1",
|
"@shellygo/cypress-test-utils": "^2.0.9",
|
||||||
"@babel/plugin-proposal-class-properties": "^7.16.7",
|
"@storybook/addon-a11y": "^7.6.5",
|
||||||
"@babel/plugin-transform-runtime": "^7.17.0",
|
"@storybook/addon-actions": "^7.6.5",
|
||||||
"@babel/preset-env": "^7.16.11",
|
"@storybook/addon-links": "^7.6.5",
|
||||||
"@babel/preset-flow": "^7.16.7",
|
"@storybook/addon-storysource": "^7.6.5",
|
||||||
"@babel/preset-react": "^7.16.7",
|
"@storybook/addons": "^7.6.5",
|
||||||
"@mdi/js": "^6.6.96",
|
"@storybook/builder-vite": "^7.6.5",
|
||||||
"@storybook/addon-a11y": "^6.4.20",
|
"@storybook/react": "^7.6.5",
|
||||||
"@storybook/addon-actions": "^6.4.20",
|
"@storybook/react-vite": "^7.6.5",
|
||||||
"@storybook/addon-links": "^6.4.20",
|
"@storybook/theming": "^7.6.5",
|
||||||
"@storybook/addon-storysource": "^6.4.20",
|
"@types/cors": "^2.8.17",
|
||||||
"@storybook/addons": "^6.4.20",
|
"@types/lodash.isequal": "^4.5.8",
|
||||||
"@storybook/react": "^6.4.20",
|
"@types/lodash.throttle": "^4.1.9",
|
||||||
"@storybook/theming": "^6.4.20",
|
"@types/react": "^16.14.52",
|
||||||
"@wdio/cli": "^7.19.3",
|
"@types/react-dom": "^16.9.24",
|
||||||
"@wdio/local-runner": "^7.19.3",
|
"@types/uuid": "^9.0.7",
|
||||||
"@wdio/mocha-framework": "^7.19.3",
|
"@vitejs/plugin-react": "^4.2.0",
|
||||||
"@wdio/selenium-standalone-service": "^7.19.1",
|
|
||||||
"@wdio/spec-reporter": "^7.19.1",
|
|
||||||
"babel-loader": "^8.2.4",
|
|
||||||
"babel-plugin-istanbul": "^6.1.1",
|
|
||||||
"babel-plugin-static-fs": "^3.0.0",
|
|
||||||
"copy-webpack-plugin": "^6.4.1",
|
|
||||||
"cors": "^2.8.5",
|
"cors": "^2.8.5",
|
||||||
"cross-env": "^7.0.3",
|
"cypress": "^13.6.1",
|
||||||
"css-loader": "^5.2.7",
|
"eslint": "^8.53.0",
|
||||||
"eslint": "^8.12.0",
|
"eslint-plugin-react": "^7.33.2",
|
||||||
"eslint-plugin-react": "^7.29.4",
|
"eslint-plugin-react-hooks": "^4.6.0",
|
||||||
|
"eslint-plugin-react-refresh": "^0.4.4",
|
||||||
"express": "^4.17.3",
|
"express": "^4.17.3",
|
||||||
"html-webpack-inline-svg-plugin": "^2.3.0",
|
|
||||||
"html-webpack-plugin": "^4.5.2",
|
|
||||||
"istanbul": "^0.4.5",
|
"istanbul": "^0.4.5",
|
||||||
"istanbul-lib-coverage": "^3.2.0",
|
"istanbul-lib-coverage": "^3.2.0",
|
||||||
"mkdirp": "^1.0.4",
|
|
||||||
"mocha": "^9.2.2",
|
"mocha": "^9.2.2",
|
||||||
"postcss": "^8.4.12",
|
"postcss": "^8.4.12",
|
||||||
"react-hot-loader": "^4.13.0",
|
"react-hot-loader": "^4.13.0",
|
||||||
"sass-loader": "^10.2.1",
|
"storybook": "^7.6.5",
|
||||||
"style-loader": "^2.0.0",
|
|
||||||
"stylelint": "^14.6.1",
|
"stylelint": "^14.6.1",
|
||||||
"stylelint-config-recommended-scss": "^6.0.0",
|
"stylelint-config-recommended-scss": "^6.0.0",
|
||||||
"stylelint-scss": "^4.2.0",
|
"stylelint-scss": "^4.2.0",
|
||||||
"svg-inline-loader": "^0.8.2",
|
"typescript": "^5.3.3",
|
||||||
"transform-loader": "^0.2.4",
|
|
||||||
"typescript": "^4.6.3",
|
|
||||||
"uuid": "^8.3.2",
|
"uuid": "^8.3.2",
|
||||||
"webdriverio": "^7.19.3",
|
"vite": "^5.0.0"
|
||||||
"webpack": "^4.46.0",
|
|
||||||
"webpack-bundle-analyzer": "^4.5.0",
|
|
||||||
"webpack-cleanup-plugin": "^0.5.1",
|
|
||||||
"webpack-cli": "^4.9.2",
|
|
||||||
"webpack-dev-server": "^4.8.1"
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +0,0 @@
|
|||||||
{
|
|
||||||
"container": {
|
|
||||||
"startScript": "start-sandbox"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
+14
-14
@@ -9,7 +9,7 @@ import {arrayMoveMutable} from 'array-move'
|
|||||||
import url from 'url'
|
import url from 'url'
|
||||||
import hash from "string-hash";
|
import hash from "string-hash";
|
||||||
|
|
||||||
import MapMapboxGl from './MapMapboxGl'
|
import MapMaplibreGl from './MapMaplibreGl'
|
||||||
import MapOpenLayers from './MapOpenLayers'
|
import MapOpenLayers from './MapOpenLayers'
|
||||||
import LayerList from './LayerList'
|
import LayerList from './LayerList'
|
||||||
import LayerEditor from './LayerEditor'
|
import LayerEditor from './LayerEditor'
|
||||||
@@ -136,7 +136,7 @@ export default class App extends React.Component {
|
|||||||
{
|
{
|
||||||
key: "m",
|
key: "m",
|
||||||
handler: () => {
|
handler: () => {
|
||||||
document.querySelector(".mapboxgl-canvas").focus();
|
document.querySelector(".maplibregl-canvas").focus();
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -220,7 +220,7 @@ export default class App extends React.Component {
|
|||||||
survey: false,
|
survey: false,
|
||||||
debug: false,
|
debug: false,
|
||||||
},
|
},
|
||||||
mapboxGlDebugOptions: {
|
maplibreGlDebugOptions: {
|
||||||
showTileBoundaries: false,
|
showTileBoundaries: false,
|
||||||
showCollisionBoxes: false,
|
showCollisionBoxes: false,
|
||||||
showOverdrawInspector: false,
|
showOverdrawInspector: false,
|
||||||
@@ -290,7 +290,7 @@ export default class App extends React.Component {
|
|||||||
// If we're changing renderer reset the map state.
|
// If we're changing renderer reset the map state.
|
||||||
if (
|
if (
|
||||||
property === 'maputnik:renderer' &&
|
property === 'maputnik:renderer' &&
|
||||||
value !== get(this.state.mapStyle, ['metadata', 'maputnik:renderer'], 'mbgljs')
|
value !== get(this.state.mapStyle, ['metadata', 'maputnik:renderer'], 'mlgljs')
|
||||||
) {
|
) {
|
||||||
this.setState({
|
this.setState({
|
||||||
mapState: 'map'
|
mapState: 'map'
|
||||||
@@ -547,7 +547,7 @@ export default class App extends React.Component {
|
|||||||
...styleObj,
|
...styleObj,
|
||||||
metadata: {
|
metadata: {
|
||||||
...styleObj.metadata,
|
...styleObj.metadata,
|
||||||
'maputnik:renderer': 'mbgljs'
|
'maputnik:renderer': 'mlgljs'
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return changedStyle
|
return changedStyle
|
||||||
@@ -626,7 +626,7 @@ export default class App extends React.Component {
|
|||||||
|
|
||||||
_getRenderer () {
|
_getRenderer () {
|
||||||
const metadata = this.state.mapStyle.metadata || {};
|
const metadata = this.state.mapStyle.metadata || {};
|
||||||
return metadata['maputnik:renderer'] || 'mbgljs';
|
return metadata['maputnik:renderer'] || 'mlgljs';
|
||||||
}
|
}
|
||||||
|
|
||||||
onMapChange = (mapView) => {
|
onMapChange = (mapView) => {
|
||||||
@@ -665,9 +665,9 @@ export default class App extends React.Component {
|
|||||||
onLayerSelect={this.onLayerSelect}
|
onLayerSelect={this.onLayerSelect}
|
||||||
/>
|
/>
|
||||||
} else {
|
} else {
|
||||||
mapElement = <MapMapboxGl {...mapProps}
|
mapElement = <MapMaplibreGl {...mapProps}
|
||||||
onChange={this.onMapChange}
|
onChange={this.onMapChange}
|
||||||
options={this.state.mapboxGlDebugOptions}
|
options={this.state.maplibreGlDebugOptions}
|
||||||
inspectModeEnabled={this.state.mapState === "inspect"}
|
inspectModeEnabled={this.state.mapState === "inspect"}
|
||||||
highlightedLayer={this.state.mapStyle.layers[this.state.selectedLayerIndex]}
|
highlightedLayer={this.state.mapStyle.layers[this.state.selectedLayerIndex]}
|
||||||
onLayerSelect={this.onLayerSelect} />
|
onLayerSelect={this.onLayerSelect} />
|
||||||
@@ -682,7 +682,7 @@ export default class App extends React.Component {
|
|||||||
elementStyle.filter = `url('#${filterName}')`;
|
elementStyle.filter = `url('#${filterName}')`;
|
||||||
}
|
}
|
||||||
|
|
||||||
return <div style={elementStyle} className="maputnik-map__container">
|
return <div style={elementStyle} className="maputnik-map__container" data-wd-key="maplibre:container">
|
||||||
{mapElement}
|
{mapElement}
|
||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
@@ -800,10 +800,10 @@ export default class App extends React.Component {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
onChangeMaboxGlDebug = (key, value) => {
|
onChangeMaplibreGlDebug = (key, value) => {
|
||||||
this.setState({
|
this.setState({
|
||||||
mapboxGlDebugOptions: {
|
maplibreGlDebugOptions: {
|
||||||
...this.state.mapboxGlDebugOptions,
|
...this.state.maplibreGlDebugOptions,
|
||||||
[key]: value,
|
[key]: value,
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@@ -870,9 +870,9 @@ export default class App extends React.Component {
|
|||||||
const modals = <div>
|
const modals = <div>
|
||||||
<ModalDebug
|
<ModalDebug
|
||||||
renderer={this._getRenderer()}
|
renderer={this._getRenderer()}
|
||||||
mapboxGlDebugOptions={this.state.mapboxGlDebugOptions}
|
maplibreGlDebugOptions={this.state.maplibreGlDebugOptions}
|
||||||
openlayersDebugOptions={this.state.openlayersDebugOptions}
|
openlayersDebugOptions={this.state.openlayersDebugOptions}
|
||||||
onChangeMaboxGlDebug={this.onChangeMaboxGlDebug}
|
onChangeMaplibreGlDebug={this.onChangeMaplibreGlDebug}
|
||||||
onChangeOpenlayersDebug={this.onChangeOpenlayersDebug}
|
onChangeOpenlayersDebug={this.onChangeOpenlayersDebug}
|
||||||
isOpen={this.state.isOpen.debug}
|
isOpen={this.state.isOpen.debug}
|
||||||
onOpenToggle={this.toggleModal.bind(this, 'debug')}
|
onOpenToggle={this.toggleModal.bind(this, 'debug')}
|
||||||
|
|||||||
@@ -4,9 +4,6 @@ import classnames from 'classnames'
|
|||||||
import {detect} from 'detect-browser';
|
import {detect} from 'detect-browser';
|
||||||
|
|
||||||
import {MdFileDownload, MdOpenInBrowser, MdSettings, MdLayers, MdHelpOutline, MdFindInPage, MdAssignmentTurnedIn} from 'react-icons/md'
|
import {MdFileDownload, MdOpenInBrowser, MdSettings, MdLayers, MdHelpOutline, MdFindInPage, MdAssignmentTurnedIn} from 'react-icons/md'
|
||||||
|
|
||||||
|
|
||||||
import logoImage from 'maputnik-design/logos/logo-color.svg'
|
|
||||||
import pkgJson from '../../package.json'
|
import pkgJson from '../../package.json'
|
||||||
|
|
||||||
|
|
||||||
@@ -133,7 +130,7 @@ export default class AppToolbar extends React.Component {
|
|||||||
|
|
||||||
onSkip = (target) => {
|
onSkip = (target) => {
|
||||||
if (target === "map") {
|
if (target === "map") {
|
||||||
document.querySelector(".mapboxgl-canvas").focus();
|
document.querySelector(".maplibregl-canvas").focus();
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
const el = document.querySelector("#skip-target-"+target);
|
const el = document.querySelector("#skip-target-"+target);
|
||||||
@@ -152,7 +149,7 @@ export default class AppToolbar extends React.Component {
|
|||||||
id: "inspect",
|
id: "inspect",
|
||||||
group: "general",
|
group: "general",
|
||||||
title: "Inspect",
|
title: "Inspect",
|
||||||
disabled: this.props.renderer !== 'mbgljs',
|
disabled: this.props.renderer === 'ol',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: "filter-deuteranopia",
|
id: "filter-deuteranopia",
|
||||||
@@ -217,7 +214,7 @@ export default class AppToolbar extends React.Component {
|
|||||||
rel="noreferrer noopener"
|
rel="noreferrer noopener"
|
||||||
href="https://github.com/maputnik/editor"
|
href="https://github.com/maputnik/editor"
|
||||||
>
|
>
|
||||||
<span dangerouslySetInnerHTML={{__html: logoImage}} />
|
<img src="node_modules/maputnik-design/logos/logo-color.svg" />
|
||||||
<h1>
|
<h1>
|
||||||
<span className="maputnik-toolbar-name">{pkgJson.name}</span>
|
<span className="maputnik-toolbar-name">{pkgJson.name}</span>
|
||||||
<span className="maputnik-toolbar-version">v{pkgJson.version}</span>
|
<span className="maputnik-toolbar-version">v{pkgJson.version}</span>
|
||||||
@@ -247,12 +244,13 @@ export default class AppToolbar extends React.Component {
|
|||||||
<label>View
|
<label>View
|
||||||
<select
|
<select
|
||||||
className="maputnik-select"
|
className="maputnik-select"
|
||||||
|
data-wd-key="maputnik-select"
|
||||||
onChange={(e) => this.handleSelection(e.target.value)}
|
onChange={(e) => this.handleSelection(e.target.value)}
|
||||||
value={currentView.id}
|
value={currentView.id}
|
||||||
>
|
>
|
||||||
{views.filter(v => v.group === "general").map((item) => {
|
{views.filter(v => v.group === "general").map((item) => {
|
||||||
return (
|
return (
|
||||||
<option key={item.id} value={item.id} disabled={item.disabled}>
|
<option key={item.id} value={item.id} disabled={item.disabled} data-wd-key={item.id}>
|
||||||
{item.title}
|
{item.title}
|
||||||
</option>
|
</option>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import React from 'react'
|
import React from 'react'
|
||||||
import PropTypes from 'prop-types'
|
import PropTypes from 'prop-types'
|
||||||
import { Collapse as ReactCollapse } from 'react-collapse'
|
import { Collapse as ReactCollapse } from 'react-collapse'
|
||||||
import accessibility from '../../libs/accessibility'
|
import {reducedMotionEnabled} from '../../libs/accessibility'
|
||||||
|
|
||||||
|
|
||||||
export default class Collapse extends React.Component {
|
export default class Collapse extends React.Component {
|
||||||
@@ -15,7 +15,7 @@ export default class Collapse extends React.Component {
|
|||||||
}
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
if (accessibility.reducedMotionEnabled()) {
|
if (reducedMotionEnabled()) {
|
||||||
return (
|
return (
|
||||||
<div style={{display: this.props.isActive ? "block" : "none"}}>
|
<div style={{display: this.props.isActive ? "block" : "none"}}>
|
||||||
{this.props.children}
|
{this.props.children}
|
||||||
|
|||||||
@@ -22,7 +22,7 @@ export default class Doc extends React.Component {
|
|||||||
const renderValues = (
|
const renderValues = (
|
||||||
!!values &&
|
!!values &&
|
||||||
// HACK: Currently we merge additional values into the style spec, so this is required
|
// HACK: Currently we merge additional values into the style spec, so this is required
|
||||||
// See <https://github.com/maputnik/editor/blob/master/src/components/fields/PropertyGroup.jsx#L16>
|
// See <https://github.com/maputnik/editor/blob/main/src/components/PropertyGroup.jsx#L16>
|
||||||
!Array.isArray(values)
|
!Array.isArray(values)
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import React from 'react'
|
import React from 'react'
|
||||||
import PropTypes from 'prop-types'
|
import PropTypes from 'prop-types'
|
||||||
import { combiningFilterOps } from '../libs/filterops.js'
|
import {combiningFilterOps} from '../libs/filterops'
|
||||||
import {mdiTableRowPlusAfter} from '@mdi/js';
|
import {mdiTableRowPlusAfter} from '@mdi/js';
|
||||||
import {isEqual} from 'lodash';
|
import {isEqual} from 'lodash';
|
||||||
|
|
||||||
@@ -42,7 +42,7 @@ function createStyleFromFilter (filter) {
|
|||||||
"id": "tmp",
|
"id": "tmp",
|
||||||
"version": 8,
|
"version": 8,
|
||||||
"name": "Empty Style",
|
"name": "Empty Style",
|
||||||
"metadata": {"maputnik:renderer": "mbgljs"},
|
"metadata": {"maputnik:renderer": "mlgljs"},
|
||||||
"sources": {
|
"sources": {
|
||||||
"tmp": {
|
"tmp": {
|
||||||
"type": "geojson",
|
"type": "geojson",
|
||||||
|
|||||||
@@ -32,7 +32,7 @@ function optionsLabelLength(options) {
|
|||||||
return sum
|
return sum
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Display any field from the Mapbox GL style spec and
|
/** Display any field from the Maplibre GL style spec and
|
||||||
* choose the correct field component based on the @{fieldSpec}
|
* choose the correct field component based on the @{fieldSpec}
|
||||||
* to display @{value}. */
|
* to display @{value}. */
|
||||||
export default class SpecField extends React.Component {
|
export default class SpecField extends React.Component {
|
||||||
|
|||||||
@@ -305,7 +305,7 @@ export default class LayerEditor extends React.Component {
|
|||||||
onSelection={handleSelection}
|
onSelection={handleSelection}
|
||||||
closeOnSelection={false}
|
closeOnSelection={false}
|
||||||
>
|
>
|
||||||
<Button id="skip-target-layer-editor" className='more-menu__button' title="Layer options">
|
<Button id="skip-target-layer-editor" data-wd-key="skip-target-layer-editor" className='more-menu__button' title="Layer options">
|
||||||
<MdMoreVert className="more-menu__button__svg" />
|
<MdMoreVert className="more-menu__button__svg" />
|
||||||
</Button>
|
</Button>
|
||||||
<Menu>
|
<Menu>
|
||||||
|
|||||||
@@ -286,6 +286,7 @@ class LayerListContainer extends React.Component {
|
|||||||
<div className="maputnik-multibutton">
|
<div className="maputnik-multibutton">
|
||||||
<button
|
<button
|
||||||
id="skip-target-layer-list"
|
id="skip-target-layer-list"
|
||||||
|
data-wd-key="skip-target-layer-list"
|
||||||
onClick={this.toggleLayers}
|
onClick={this.toggleLayers}
|
||||||
className="maputnik-button">
|
className="maputnik-button">
|
||||||
{this.state.areAllGroupsExpanded === true ? "Collapse" : "Expand"}
|
{this.state.areAllGroupsExpanded === true ? "Collapse" : "Expand"}
|
||||||
|
|||||||
@@ -3,16 +3,16 @@ import PropTypes from 'prop-types'
|
|||||||
import ReactDOM from 'react-dom'
|
import ReactDOM from 'react-dom'
|
||||||
import MapLibreGl from 'maplibre-gl'
|
import MapLibreGl from 'maplibre-gl'
|
||||||
import MapboxInspect from 'mapbox-gl-inspect'
|
import MapboxInspect from 'mapbox-gl-inspect'
|
||||||
import MapMapboxGlLayerPopup from './MapMapboxGlLayerPopup'
|
import MapMaplibreGlLayerPopup from './MapMaplibreGlLayerPopup'
|
||||||
import MapMapboxGlFeaturePropertyPopup from './MapMapboxGlFeaturePropertyPopup'
|
import MapMaplibreGlFeaturePropertyPopup from './MapMaplibreGlFeaturePropertyPopup'
|
||||||
import tokens from '../config/tokens.json'
|
import tokens from '../config/tokens.json'
|
||||||
import colors from 'mapbox-gl-inspect/lib/colors'
|
import colors from 'mapbox-gl-inspect/lib/colors'
|
||||||
import Color from 'color'
|
import Color from 'color'
|
||||||
import ZoomControl from '../libs/zoomcontrol'
|
import ZoomControl from '../libs/zoomcontrol'
|
||||||
import { colorHighlightedLayer } from '../libs/highlight'
|
import { colorHighlightedLayer } from '../libs/highlight'
|
||||||
import 'maplibre-gl/dist/maplibre-gl.css'
|
import 'maplibre-gl/dist/maplibre-gl.css'
|
||||||
import '../mapboxgl.css'
|
import '../maplibregl.css'
|
||||||
import '../libs/mapbox-rtl'
|
import '../libs/maplibre-rtl'
|
||||||
|
|
||||||
|
|
||||||
const IS_SUPPORTED = MapLibreGl.supported();
|
const IS_SUPPORTED = MapLibreGl.supported();
|
||||||
@@ -52,7 +52,7 @@ function buildInspectStyle(originalMapStyle, coloredLayers, highlightedLayer) {
|
|||||||
return inspectStyle
|
return inspectStyle
|
||||||
}
|
}
|
||||||
|
|
||||||
export default class MapMapboxGl extends React.Component {
|
export default class MapMaplibreGl extends React.Component {
|
||||||
static propTypes = {
|
static propTypes = {
|
||||||
onDataChange: PropTypes.func,
|
onDataChange: PropTypes.func,
|
||||||
onLayerSelect: PropTypes.func.isRequired,
|
onLayerSelect: PropTypes.func.isRequired,
|
||||||
@@ -69,7 +69,6 @@ export default class MapMapboxGl extends React.Component {
|
|||||||
onDataChange: () => {},
|
onDataChange: () => {},
|
||||||
onLayerSelect: () => {},
|
onLayerSelect: () => {},
|
||||||
onChange: () => {},
|
onChange: () => {},
|
||||||
mapboxAccessToken: tokens.mapbox,
|
|
||||||
options: {},
|
options: {},
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -86,7 +85,7 @@ export default class MapMapboxGl extends React.Component {
|
|||||||
|
|
||||||
if(!this.state.map) return
|
if(!this.state.map) return
|
||||||
|
|
||||||
//Mapbox GL now does diffing natively so we don't need to calculate
|
//Maplibre GL now does diffing natively so we don't need to calculate
|
||||||
//the necessary operations ourselves!
|
//the necessary operations ourselves!
|
||||||
this.state.map.setStyle(
|
this.state.map.setStyle(
|
||||||
this.props.replaceAccessTokens(props.mapStyle),
|
this.props.replaceAccessTokens(props.mapStyle),
|
||||||
@@ -120,7 +119,7 @@ export default class MapMapboxGl extends React.Component {
|
|||||||
if (map) {
|
if (map) {
|
||||||
if (this.props.inspectModeEnabled) {
|
if (this.props.inspectModeEnabled) {
|
||||||
// HACK: We need to work out why we need to do this and what's causing
|
// HACK: We need to work out why we need to do this and what's causing
|
||||||
// this error. I'm assuming an issue with mapbox-gl update and
|
// this error. I'm assuming an issue with maplibre-gl update and
|
||||||
// mapbox-gl-inspect.
|
// mapbox-gl-inspect.
|
||||||
try {
|
try {
|
||||||
this.state.inspect.render();
|
this.state.inspect.render();
|
||||||
@@ -182,9 +181,9 @@ export default class MapMapboxGl extends React.Component {
|
|||||||
buildInspectStyle: (originalMapStyle, coloredLayers) => buildInspectStyle(originalMapStyle, coloredLayers, this.props.highlightedLayer),
|
buildInspectStyle: (originalMapStyle, coloredLayers) => buildInspectStyle(originalMapStyle, coloredLayers, this.props.highlightedLayer),
|
||||||
renderPopup: features => {
|
renderPopup: features => {
|
||||||
if(this.props.inspectModeEnabled) {
|
if(this.props.inspectModeEnabled) {
|
||||||
return renderPopup(<MapMapboxGlFeaturePropertyPopup features={features} />, tmpNode);
|
return renderPopup(<MapMaplibreGlFeaturePropertyPopup features={features} />, tmpNode);
|
||||||
} else {
|
} else {
|
||||||
return renderPopup(<MapMapboxGlLayerPopup features={features} onLayerSelect={this.onLayerSelectById} zoom={this.state.zoom} />, tmpNode);
|
return renderPopup(<MapMaplibreGlLayerPopup features={features} onLayerSelect={this.onLayerSelectById} zoom={this.state.zoom} />, tmpNode);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
@@ -231,6 +230,7 @@ export default class MapMapboxGl extends React.Component {
|
|||||||
role="region"
|
role="region"
|
||||||
aria-label="Map view"
|
aria-label="Map view"
|
||||||
ref={x => this.container = x}
|
ref={x => this.container = x}
|
||||||
|
data-wd-key="maplibre:map"
|
||||||
></div>
|
></div>
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
@@ -238,7 +238,7 @@ export default class MapMapboxGl extends React.Component {
|
|||||||
className="maputnik-map maputnik-map--error"
|
className="maputnik-map maputnik-map--error"
|
||||||
>
|
>
|
||||||
<div className="maputnik-map__error-message">
|
<div className="maputnik-map__error-message">
|
||||||
Error: Cannot load MapboxGL, WebGL is either unsupported or disabled
|
Error: Cannot load MaplibreGL, WebGL is either unsupported or disabled
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
@@ -1,13 +1,12 @@
|
|||||||
import React from 'react'
|
import React from 'react'
|
||||||
import {throttle} from 'lodash';
|
import {throttle} from 'lodash';
|
||||||
import PropTypes from 'prop-types'
|
import PropTypes from 'prop-types'
|
||||||
import { loadJSON } from '../libs/urlopen'
|
|
||||||
|
|
||||||
import MapMapboxGlLayerPopup from './MapMapboxGlLayerPopup';
|
import MapMaplibreGlLayerPopup from './MapMaplibreGlLayerPopup';
|
||||||
|
|
||||||
import 'ol/ol.css'
|
import 'ol/ol.css'
|
||||||
import {apply} from 'ol-mapbox-style';
|
import {apply} from 'ol-mapbox-style';
|
||||||
import {Map, View, Proj, Overlay} from 'ol';
|
import {Map, View, Overlay} from 'ol';
|
||||||
|
|
||||||
import {toLonLat} from 'ol/proj';
|
import {toLonLat} from 'ol/proj';
|
||||||
import {toStringHDMS} from 'ol/coordinate';
|
import {toStringHDMS} from 'ol/coordinate';
|
||||||
@@ -146,13 +145,13 @@ export default class MapOpenLayers extends React.Component {
|
|||||||
className="maputnik-popup"
|
className="maputnik-popup"
|
||||||
>
|
>
|
||||||
<button
|
<button
|
||||||
className="mapboxgl-popup-close-button"
|
className="maplibregl-popup-close-button"
|
||||||
onClick={this.closeOverlay}
|
onClick={this.closeOverlay}
|
||||||
aria-label="Close popup"
|
aria-label="Close popup"
|
||||||
>
|
>
|
||||||
×
|
×
|
||||||
</button>
|
</button>
|
||||||
<MapMapboxGlLayerPopup
|
<MapMaplibreGlLayerPopup
|
||||||
features={this.state.selectedFeatures || []}
|
features={this.state.selectedFeatures || []}
|
||||||
onLayerSelect={this.props.onLayerSelect}
|
onLayerSelect={this.props.onLayerSelect}
|
||||||
/>
|
/>
|
||||||
|
|||||||
@@ -27,9 +27,9 @@ export default class Modal extends React.Component {
|
|||||||
document.activeElement.blur();
|
document.activeElement.blur();
|
||||||
}
|
}
|
||||||
|
|
||||||
setImmediate(() => {
|
setTimeout(() => {
|
||||||
this.props.onOpenToggle(false);
|
this.props.onOpenToggle(false);
|
||||||
});
|
}, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ export default class ModalDebug extends React.Component {
|
|||||||
onChangeMaboxGlDebug: PropTypes.func.isRequired,
|
onChangeMaboxGlDebug: PropTypes.func.isRequired,
|
||||||
onChangeOpenlayersDebug: PropTypes.func.isRequired,
|
onChangeOpenlayersDebug: PropTypes.func.isRequired,
|
||||||
onOpenToggle: PropTypes.func.isRequired,
|
onOpenToggle: PropTypes.func.isRequired,
|
||||||
mapboxGlDebugOptions: PropTypes.object,
|
maplibreGlDebugOptions: PropTypes.object,
|
||||||
openlayersDebugOptions: PropTypes.object,
|
openlayersDebugOptions: PropTypes.object,
|
||||||
mapView: PropTypes.object,
|
mapView: PropTypes.object,
|
||||||
}
|
}
|
||||||
@@ -31,9 +31,9 @@ export default class ModalDebug extends React.Component {
|
|||||||
>
|
>
|
||||||
<section className="maputnik-modal-section maputnik-modal-shortcuts">
|
<section className="maputnik-modal-section maputnik-modal-shortcuts">
|
||||||
<h1>Options</h1>
|
<h1>Options</h1>
|
||||||
{this.props.renderer === 'mbgljs' &&
|
{this.props.renderer === 'mlgljs' &&
|
||||||
<ul>
|
<ul>
|
||||||
{Object.entries(this.props.mapboxGlDebugOptions).map(([key, val]) => {
|
{Object.entries(this.props.maplibreGlDebugOptions).map(([key, val]) => {
|
||||||
return <li key={key}>
|
return <li key={key}>
|
||||||
<label>
|
<label>
|
||||||
<input type="checkbox" checked={val} onClick={(e) => this.props.onChangeMaboxGlDebug(key, e.target.checked)} /> {key}
|
<input type="checkbox" checked={val} onClick={(e) => this.props.onChangeMaboxGlDebug(key, e.target.checked)} /> {key}
|
||||||
|
|||||||
@@ -2,11 +2,9 @@ import React from 'react'
|
|||||||
import PropTypes from 'prop-types'
|
import PropTypes from 'prop-types'
|
||||||
import Slugify from 'slugify'
|
import Slugify from 'slugify'
|
||||||
import {saveAs} from 'file-saver'
|
import {saveAs} from 'file-saver'
|
||||||
import pkgLockJson from '../../package-lock.json'
|
import {version} from 'maplibre-gl'
|
||||||
|
|
||||||
import {format} from '@maplibre/maplibre-gl-style-spec'
|
import {format} from '@maplibre/maplibre-gl-style-spec'
|
||||||
import FieldString from './FieldString'
|
import FieldString from './FieldString'
|
||||||
import FieldCheckbox from './FieldCheckbox'
|
|
||||||
import InputButton from './InputButton'
|
import InputButton from './InputButton'
|
||||||
import Modal from './Modal'
|
import Modal from './Modal'
|
||||||
import {MdFileDownload} from 'react-icons/md'
|
import {MdFileDownload} from 'react-icons/md'
|
||||||
@@ -14,7 +12,7 @@ import style from '../libs/style'
|
|||||||
import fieldSpecAdditional from '../libs/field-spec-additional'
|
import fieldSpecAdditional from '../libs/field-spec-additional'
|
||||||
|
|
||||||
|
|
||||||
const MAPBOX_GL_VERSION = pkgLockJson.dependencies["mapbox-gl"].version;
|
const MAPLIBRE_GL_VERSION = version;
|
||||||
|
|
||||||
|
|
||||||
export default class ModalExport extends React.Component {
|
export default class ModalExport extends React.Component {
|
||||||
@@ -58,8 +56,8 @@ export default class ModalExport extends React.Component {
|
|||||||
<meta charset="utf-8" />
|
<meta charset="utf-8" />
|
||||||
<title>${htmlTitle}</title>
|
<title>${htmlTitle}</title>
|
||||||
<meta name="viewport" content="initial-scale=1,maximum-scale=1,user-scalable=no" />
|
<meta name="viewport" content="initial-scale=1,maximum-scale=1,user-scalable=no" />
|
||||||
<script src="https://api.mapbox.com/mapbox-gl-js/v${MAPBOX_GL_VERSION}/mapbox-gl.js"></script>
|
<script src="https://unpkg.com/maplibre-gl@${MAPLIBRE_GL_VERSION}/dist/maplibre-gl.js"></script>
|
||||||
<link href="https://api.mapbox.com/mapbox-gl-js/v${MAPBOX_GL_VERSION}/mapbox-gl.css" rel="stylesheet" />
|
<link href="https://unpkg.com/maplibre-gl@${MAPLIBRE_GL_VERSION}/dist/maplibre-gl.css" rel="stylesheet" />
|
||||||
<style>
|
<style>
|
||||||
body { margin: 0; padding: 0; }
|
body { margin: 0; padding: 0; }
|
||||||
#map { position: absolute; top: 0; bottom: 0; width: 100%; }
|
#map { position: absolute; top: 0; bottom: 0; width: 100%; }
|
||||||
@@ -68,12 +66,11 @@ export default class ModalExport extends React.Component {
|
|||||||
<body>
|
<body>
|
||||||
<div id="map"></div>
|
<div id="map"></div>
|
||||||
<script>
|
<script>
|
||||||
mapboxgl.accessToken = 'access_token';
|
const map = new maplibregl.Map({
|
||||||
const map = new mapboxgl.Map({
|
|
||||||
container: 'map',
|
container: 'map',
|
||||||
style: ${tokenStyle},
|
style: ${tokenStyle},
|
||||||
});
|
});
|
||||||
map.addControl(new mapboxgl.NavigationControl());
|
map.addControl(new maplibregl.NavigationControl());
|
||||||
</script>
|
</script>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
@@ -119,12 +116,6 @@ export default class ModalExport extends React.Component {
|
|||||||
</p>
|
</p>
|
||||||
|
|
||||||
<div>
|
<div>
|
||||||
<FieldString
|
|
||||||
label={fieldSpecAdditional.maputnik.mapbox_access_token.label}
|
|
||||||
fieldSpec={fieldSpecAdditional.maputnik.mapbox_access_token}
|
|
||||||
value={(this.props.mapStyle.metadata || {})['maputnik:mapbox_access_token']}
|
|
||||||
onChange={this.changeMetadataProperty.bind(this, "maputnik:mapbox_access_token")}
|
|
||||||
/>
|
|
||||||
<FieldString
|
<FieldString
|
||||||
label={fieldSpecAdditional.maputnik.maptiler_access_token.label}
|
label={fieldSpecAdditional.maputnik.maptiler_access_token.label}
|
||||||
fieldSpec={fieldSpecAdditional.maputnik.maptiler_access_token}
|
fieldSpec={fieldSpecAdditional.maputnik.maptiler_access_token}
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ import InputUrl from './InputUrl'
|
|||||||
import {MdFileUpload} from 'react-icons/md'
|
import {MdFileUpload} from 'react-icons/md'
|
||||||
import {MdAddCircleOutline} from 'react-icons/md'
|
import {MdAddCircleOutline} from 'react-icons/md'
|
||||||
|
|
||||||
import style from '../libs/style.js'
|
import style from '../libs/style'
|
||||||
import publicStyles from '../config/styles.json'
|
import publicStyles from '../config/styles.json'
|
||||||
|
|
||||||
class PublicStyle extends React.Component {
|
class PublicStyle extends React.Component {
|
||||||
|
|||||||
@@ -117,14 +117,6 @@ export default class ModalSettings extends React.Component {
|
|||||||
onChange={this.changeStyleProperty.bind(this, "glyphs")}
|
onChange={this.changeStyleProperty.bind(this, "glyphs")}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<FieldString {...inputProps}
|
|
||||||
label={fieldSpecAdditional.maputnik.mapbox_access_token.label}
|
|
||||||
fieldSpec={fieldSpecAdditional.maputnik.mapbox_access_token}
|
|
||||||
data-wd-key="modal:settings.maputnik:mapbox_access_token"
|
|
||||||
value={metadata['maputnik:mapbox_access_token']}
|
|
||||||
onChange={onChangeMetadataProperty.bind(this, "maputnik:mapbox_access_token")}
|
|
||||||
/>
|
|
||||||
|
|
||||||
<FieldString {...inputProps}
|
<FieldString {...inputProps}
|
||||||
label={fieldSpecAdditional.maputnik.maptiler_access_token.label}
|
label={fieldSpecAdditional.maputnik.maptiler_access_token.label}
|
||||||
fieldSpec={fieldSpecAdditional.maputnik.maptiler_access_token}
|
fieldSpec={fieldSpecAdditional.maputnik.maptiler_access_token}
|
||||||
@@ -241,10 +233,10 @@ export default class ModalSettings extends React.Component {
|
|||||||
fieldSpec={fieldSpecAdditional.maputnik.style_renderer}
|
fieldSpec={fieldSpecAdditional.maputnik.style_renderer}
|
||||||
data-wd-key="modal:settings.maputnik:renderer"
|
data-wd-key="modal:settings.maputnik:renderer"
|
||||||
options={[
|
options={[
|
||||||
['mbgljs', 'MapboxGL JS'],
|
['mlgljs', 'MapLibreGL JS'],
|
||||||
['ol', 'Open Layers (experimental)'],
|
['ol', 'Open Layers (experimental)'],
|
||||||
]}
|
]}
|
||||||
value={metadata['maputnik:renderer'] || 'mbgljs'}
|
value={metadata['maputnik:renderer'] || 'mlgljs'}
|
||||||
onChange={onChangeMetadataProperty.bind(this, 'maputnik:renderer')}
|
onChange={onChangeMetadataProperty.bind(this, 'maputnik:renderer')}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -128,6 +128,7 @@ class AddSource extends React.Component {
|
|||||||
}
|
}
|
||||||
case 'geojson_json': return {
|
case 'geojson_json': return {
|
||||||
type: 'geojson',
|
type: 'geojson',
|
||||||
|
cluster: source.cluster || false,
|
||||||
data: {}
|
data: {}
|
||||||
}
|
}
|
||||||
case 'tilejson_vector': return {
|
case 'tilejson_vector': return {
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ import FieldSelect from './FieldSelect'
|
|||||||
import FieldDynamicArray from './FieldDynamicArray'
|
import FieldDynamicArray from './FieldDynamicArray'
|
||||||
import FieldArray from './FieldArray'
|
import FieldArray from './FieldArray'
|
||||||
import FieldJson from './FieldJson'
|
import FieldJson from './FieldJson'
|
||||||
|
import FieldCheckbox from './FieldCheckbox'
|
||||||
|
|
||||||
|
|
||||||
class TileJSONSourceEditor extends React.Component {
|
class TileJSONSourceEditor extends React.Component {
|
||||||
@@ -205,7 +206,8 @@ class GeoJSONSourceFieldJsonEditor extends React.Component {
|
|||||||
}
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
return <Block label={"GeoJSON"} fieldSpec={latest.source_geojson.data}>
|
return <div>
|
||||||
|
<Block label={"GeoJSON"} fieldSpec={latest.source_geojson.data}>
|
||||||
<FieldJson
|
<FieldJson
|
||||||
layer={this.props.source.data}
|
layer={this.props.source.data}
|
||||||
maxHeight={200}
|
maxHeight={200}
|
||||||
@@ -222,6 +224,17 @@ class GeoJSONSourceFieldJsonEditor extends React.Component {
|
|||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
</Block>
|
</Block>
|
||||||
|
<FieldCheckbox
|
||||||
|
label={'Cluster'}
|
||||||
|
value={this.props.source.cluster}
|
||||||
|
onChange={cluster => {
|
||||||
|
this.props.onChange({
|
||||||
|
...this.props.source,
|
||||||
|
cluster: cluster,
|
||||||
|
})
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import React from 'react'
|
import React from 'react'
|
||||||
import PropTypes from 'prop-types'
|
import PropTypes from 'prop-types'
|
||||||
|
|
||||||
import { otherFilterOps } from '../libs/filterops.js'
|
import {otherFilterOps} from '../libs/filterops'
|
||||||
import InputString from './InputString'
|
import InputString from './InputString'
|
||||||
import InputAutocomplete from './InputAutocomplete'
|
import InputAutocomplete from './InputAutocomplete'
|
||||||
import InputSelect from './InputSelect'
|
import InputSelect from './InputSelect'
|
||||||
|
|||||||
@@ -8,7 +8,6 @@ import Fieldset from './Fieldset'
|
|||||||
const typeMap = {
|
const typeMap = {
|
||||||
color: () => Block,
|
color: () => Block,
|
||||||
enum: ({fieldSpec}) => (Object.keys(fieldSpec.values).length <= 3 ? Fieldset : Block),
|
enum: ({fieldSpec}) => (Object.keys(fieldSpec.values).length <= 3 ? Fieldset : Block),
|
||||||
number: () => Block,
|
|
||||||
boolean: () => Block,
|
boolean: () => Block,
|
||||||
array: () => Fieldset,
|
array: () => Fieldset,
|
||||||
resolvedImage: () => Block,
|
resolvedImage: () => Block,
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
{
|
{
|
||||||
"mapbox": "pk.eyJ1IjoibW9yZ2Vua2FmZmVlIiwiYSI6ImNpeHJmNXNmZTAwNHIycXBid2NqdTJibjMifQ.Dv1-GDpTWi0NP6xW9Fct1w",
|
|
||||||
"openmaptiles": "KDhMfHvorAFkFe64wlZb",
|
"openmaptiles": "KDhMfHvorAFkFe64wlZb",
|
||||||
"thunderforest": "b71f7f0ba4064f5eb9e903859a9cf5c6"
|
"thunderforest": "b71f7f0ba4064f5eb9e903859a9cf5c6"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,12 +0,0 @@
|
|||||||
import throttle from 'lodash.throttle'
|
|
||||||
|
|
||||||
|
|
||||||
// Throttle for 3 seconds so when a user enables it they don't have to refresh the page.
|
|
||||||
const reducedMotionEnabled = throttle(() => {
|
|
||||||
return window.matchMedia("(prefers-reduced-motion: reduce)").matches
|
|
||||||
}, 3000);
|
|
||||||
|
|
||||||
|
|
||||||
export default {
|
|
||||||
reducedMotionEnabled
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
import throttle from 'lodash.throttle'
|
||||||
|
|
||||||
|
export default {
|
||||||
|
// Throttle for 3 seconds so when a user enables it they don't have to refresh the page.
|
||||||
|
reducedMotionEnabled: throttle(() => {
|
||||||
|
return window.matchMedia("(prefers-reduced-motion: reduce)").matches
|
||||||
|
}, 3000)
|
||||||
|
}
|
||||||
@@ -1,10 +1,21 @@
|
|||||||
import style from './style.js'
|
import style from './style.js'
|
||||||
import {format} from '@maplibre/maplibre-gl-style-spec'
|
import {StyleSpecification, format} from '@maplibre/maplibre-gl-style-spec'
|
||||||
import ReconnectingWebSocket from 'reconnecting-websocket'
|
import ReconnectingWebSocket from 'reconnecting-websocket'
|
||||||
|
|
||||||
|
export type ApiStyleStoreOptions = {
|
||||||
|
port?: string
|
||||||
|
host?: string
|
||||||
|
onLocalStyleChange?: (style: any) => void
|
||||||
|
}
|
||||||
|
|
||||||
export class ApiStyleStore {
|
export class ApiStyleStore {
|
||||||
|
|
||||||
constructor(opts) {
|
localUrl: string;
|
||||||
|
websocketUrl: string;
|
||||||
|
latestStyleId: string | undefined = undefined;
|
||||||
|
onLocalStyleChange: (style: any) => void;
|
||||||
|
|
||||||
|
constructor(opts: ApiStyleStoreOptions) {
|
||||||
this.onLocalStyleChange = opts.onLocalStyleChange || (() => {})
|
this.onLocalStyleChange = opts.onLocalStyleChange || (() => {})
|
||||||
const port = opts.port || '8000'
|
const port = opts.port || '8000'
|
||||||
const host = opts.host || 'localhost'
|
const host = opts.host || 'localhost'
|
||||||
@@ -13,7 +24,7 @@ export class ApiStyleStore {
|
|||||||
this.init = this.init.bind(this)
|
this.init = this.init.bind(this)
|
||||||
}
|
}
|
||||||
|
|
||||||
init(cb) {
|
init(cb: (...args: any[]) => void) {
|
||||||
fetch(this.localUrl + '/styles', {
|
fetch(this.localUrl + '/styles', {
|
||||||
mode: 'cors',
|
mode: 'cors',
|
||||||
})
|
})
|
||||||
@@ -26,7 +37,7 @@ export class ApiStyleStore {
|
|||||||
this.notifyLocalChanges()
|
this.notifyLocalChanges()
|
||||||
cb(null)
|
cb(null)
|
||||||
})
|
})
|
||||||
.catch(function(e) {
|
.catch(() => {
|
||||||
cb(new Error('Can not connect to style API'))
|
cb(new Error('Can not connect to style API'))
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@@ -47,7 +58,7 @@ export class ApiStyleStore {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
latestStyle(cb) {
|
latestStyle(cb: (...args: any[]) => void) {
|
||||||
if(this.latestStyleId) {
|
if(this.latestStyleId) {
|
||||||
fetch(this.localUrl + '/styles/' + this.latestStyleId, {
|
fetch(this.localUrl + '/styles/' + this.latestStyleId, {
|
||||||
mode: 'cors',
|
mode: 'cors',
|
||||||
@@ -64,7 +75,7 @@ export class ApiStyleStore {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Save current style replacing previous version
|
// Save current style replacing previous version
|
||||||
save(mapStyle) {
|
save(mapStyle: StyleSpecification & { id: string }) {
|
||||||
const styleJSON = format(
|
const styleJSON = format(
|
||||||
style.stripAccessTokens(
|
style.stripAccessTokens(
|
||||||
style.replaceAccessTokens(mapStyle)
|
style.replaceAccessTokens(mapStyle)
|
||||||
@@ -1,12 +1,16 @@
|
|||||||
import querystring from 'querystring'
|
interface DebugStore {
|
||||||
|
[namespace: string]: {
|
||||||
|
[key: string]: any
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const debugStore: DebugStore = {};
|
||||||
const debugStore = {};
|
|
||||||
|
|
||||||
function enabled() {
|
function enabled() {
|
||||||
const qs = querystring.parse(window.location.search.slice(1));
|
const qs = new URL(window.location.href).searchParams;
|
||||||
if(qs.hasOwnProperty("debug")) {
|
const debugQs = qs.get("debug");
|
||||||
return !!qs.debug.match(/^(|1|true)$/);
|
if(debugQs) {
|
||||||
|
return !!debugQs.match(/^(|1|true)$/);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
return false;
|
return false;
|
||||||
@@ -17,7 +21,7 @@ function genErr() {
|
|||||||
return new Error("Debug not enabled, enable by appending '?debug' to your query string");
|
return new Error("Debug not enabled, enable by appending '?debug' to your query string");
|
||||||
}
|
}
|
||||||
|
|
||||||
function set(namespace, key, value) {
|
function set(namespace: keyof DebugStore, key: string, value: any) {
|
||||||
if(!enabled()) {
|
if(!enabled()) {
|
||||||
throw genErr();
|
throw genErr();
|
||||||
}
|
}
|
||||||
@@ -25,7 +29,7 @@ function set(namespace, key, value) {
|
|||||||
debugStore[namespace][key] = value;
|
debugStore[namespace][key] = value;
|
||||||
}
|
}
|
||||||
|
|
||||||
function get(namespace, key) {
|
function get(namespace: keyof DebugStore, key: string) {
|
||||||
if(!enabled()) {
|
if(!enabled()) {
|
||||||
throw genErr();
|
throw genErr();
|
||||||
}
|
}
|
||||||
@@ -38,7 +42,7 @@ const mod = {
|
|||||||
enabled,
|
enabled,
|
||||||
get,
|
get,
|
||||||
set
|
set
|
||||||
}
|
};
|
||||||
|
|
||||||
window.debug = mod;
|
(window as any).debug = mod;
|
||||||
export default mod;
|
export default mod;
|
||||||
@@ -1,13 +0,0 @@
|
|||||||
import {diff} from '@maplibre/maplibre-gl-style-spec'
|
|
||||||
|
|
||||||
export function diffMessages(beforeStyle, afterStyle) {
|
|
||||||
const changes = diff(beforeStyle, afterStyle)
|
|
||||||
return changes.map(cmd => cmd.command + ' ' + cmd.args.join(' '))
|
|
||||||
}
|
|
||||||
|
|
||||||
export function undoMessages(beforeStyle, afterStyle) {
|
|
||||||
return diffMessages(beforeStyle, afterStyle).map(m => 'Undo ' + m)
|
|
||||||
}
|
|
||||||
export function redoMessages(beforeStyle, afterStyle) {
|
|
||||||
return diffMessages(beforeStyle, afterStyle).map(m => 'Redo ' + m)
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,13 @@
|
|||||||
|
import {StyleSpecification, diff} from '@maplibre/maplibre-gl-style-spec'
|
||||||
|
|
||||||
|
function diffMessages(beforeStyle: StyleSpecification, afterStyle: StyleSpecification) {
|
||||||
|
const changes = diff(beforeStyle, afterStyle)
|
||||||
|
return changes.map(cmd => cmd.command + ' ' + cmd.args.join(' '))
|
||||||
|
}
|
||||||
|
|
||||||
|
export function undoMessages(beforeStyle: StyleSpecification, afterStyle: StyleSpecification) {
|
||||||
|
return diffMessages(beforeStyle, afterStyle).map(m => 'Undo ' + m)
|
||||||
|
}
|
||||||
|
export function redoMessages(beforeStyle: StyleSpecification, afterStyle: StyleSpecification) {
|
||||||
|
return diffMessages(beforeStyle, afterStyle).map(m => 'Redo ' + m)
|
||||||
|
}
|
||||||
@@ -3,7 +3,7 @@
|
|||||||
*/
|
*/
|
||||||
let REF = 0;
|
let REF = 0;
|
||||||
|
|
||||||
export default function(prefix="") {
|
export default function generateUniqueId(prefix="") {
|
||||||
REF++;
|
REF++;
|
||||||
return prefix+REF;
|
return prefix+REF;
|
||||||
}
|
}
|
||||||
@@ -1,9 +1,5 @@
|
|||||||
const spec = {
|
const spec = {
|
||||||
maputnik: {
|
maputnik: {
|
||||||
mapbox_access_token: {
|
|
||||||
label: "Mapbox Access Token",
|
|
||||||
doc: "Public access token for Mapbox services."
|
|
||||||
},
|
|
||||||
maptiler_access_token: {
|
maptiler_access_token: {
|
||||||
label: "MapTiler Access Token",
|
label: "MapTiler Access Token",
|
||||||
doc: "Public access token for MapTiler Cloud."
|
doc: "Public access token for MapTiler Cloud."
|
||||||
@@ -1,6 +0,0 @@
|
|||||||
import {latest} from '@maplibre/maplibre-gl-style-spec'
|
|
||||||
export const combiningFilterOps = ['all', 'any', 'none']
|
|
||||||
export const setFilterOps = ['in', '!in']
|
|
||||||
export const otherFilterOps = Object
|
|
||||||
.keys(latest.filter_operator.values)
|
|
||||||
.filter(op => combiningFilterOps.indexOf(op) < 0)
|
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
import {latest} from '@maplibre/maplibre-gl-style-spec'
|
||||||
|
|
||||||
|
export const combiningFilterOps = ['all', 'any', 'none'];
|
||||||
|
export const setFilterOps = ['in', '!in'];
|
||||||
|
export const otherFilterOps = Object
|
||||||
|
.keys(latest.filter_operator.values)
|
||||||
|
.filter(op => combiningFilterOps.indexOf(op) < 0);
|
||||||
@@ -1,17 +1,20 @@
|
|||||||
|
// @ts-ignore
|
||||||
import stylegen from 'mapbox-gl-inspect/lib/stylegen'
|
import stylegen from 'mapbox-gl-inspect/lib/stylegen'
|
||||||
|
// @ts-ignore
|
||||||
import colors from 'mapbox-gl-inspect/lib/colors'
|
import colors from 'mapbox-gl-inspect/lib/colors'
|
||||||
|
import {FilterSpecification,LayerSpecification } from '@maplibre/maplibre-gl-style-spec'
|
||||||
|
|
||||||
export function colorHighlightedLayer(layer) {
|
export function colorHighlightedLayer(layer: LayerSpecification) {
|
||||||
if(!layer || layer.type === 'background' || layer.type === 'raster') return null
|
if(!layer || layer.type === 'background' || layer.type === 'raster') return null
|
||||||
|
|
||||||
function changeLayer(l) {
|
function changeLayer(l: LayerSpecification & {filter?: FilterSpecification}) {
|
||||||
if(l.type === 'circle') {
|
if(l.type === 'circle') {
|
||||||
l.paint['circle-radius'] = 3
|
l.paint!['circle-radius'] = 3
|
||||||
} else if(l.type === 'line') {
|
} else if(l.type === 'line') {
|
||||||
l.paint['line-width'] = 2
|
l.paint!['line-width'] = 2
|
||||||
}
|
}
|
||||||
|
|
||||||
if(layer.filter) {
|
if("filter" in layer) {
|
||||||
l.filter = layer.filter
|
l.filter = layer.filter
|
||||||
} else {
|
} else {
|
||||||
delete l['filter']
|
delete l['filter']
|
||||||
@@ -21,8 +24,7 @@ export function colorHighlightedLayer(layer) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const sourceLayerId = layer['source-layer'] || ''
|
const sourceLayerId = layer['source-layer'] || ''
|
||||||
const color = colors.brightColor(sourceLayerId, 1)
|
const color = colors.brightColor(sourceLayerId, 1);
|
||||||
const layers = []
|
|
||||||
|
|
||||||
if(layer.type === "fill" || layer.type === 'fill-extrusion') {
|
if(layer.type === "fill" || layer.type === 'fill-extrusion') {
|
||||||
return changeLayer(stylegen.polygonLayer(color, color, layer.source, layer['source-layer']))
|
return changeLayer(stylegen.polygonLayer(color, color, layer.source, layer['source-layer']))
|
||||||
@@ -1,17 +1,18 @@
|
|||||||
import {latest} from '@maplibre/maplibre-gl-style-spec'
|
import {latest} from '@maplibre/maplibre-gl-style-spec'
|
||||||
|
import { LayerSpecification } from 'maplibre-gl'
|
||||||
|
|
||||||
export function changeType(layer, newType) {
|
export function changeType(layer: LayerSpecification, newType: string) {
|
||||||
const changedPaintProps = { ...layer.paint }
|
const changedPaintProps: LayerSpecification["paint"] = { ...layer.paint }
|
||||||
Object.keys(changedPaintProps).forEach(propertyName => {
|
Object.keys(changedPaintProps).forEach(propertyName => {
|
||||||
if(!(propertyName in latest['paint_' + newType])) {
|
if(!(propertyName in latest['paint_' + newType])) {
|
||||||
delete changedPaintProps[propertyName]
|
delete changedPaintProps[propertyName as keyof LayerSpecification["paint"]]
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
const changedLayoutProps = { ...layer.layout }
|
const changedLayoutProps: LayerSpecification["layout"] = { ...layer.layout }
|
||||||
Object.keys(changedLayoutProps).forEach(propertyName => {
|
Object.keys(changedLayoutProps).forEach(propertyName => {
|
||||||
if(!(propertyName in latest['layout_' + newType])) {
|
if(!(propertyName in latest['layout_' + newType])) {
|
||||||
delete changedLayoutProps[propertyName]
|
delete changedLayoutProps[propertyName as keyof LayerSpecification["layout"]]
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
@@ -26,15 +27,15 @@ export function changeType(layer, newType) {
|
|||||||
/** A {@property} in either the paint our layout {@group} has changed
|
/** A {@property} in either the paint our layout {@group} has changed
|
||||||
* to a {@newValue}.
|
* to a {@newValue}.
|
||||||
*/
|
*/
|
||||||
export function changeProperty(layer, group, property, newValue) {
|
export function changeProperty(layer: LayerSpecification, group: keyof LayerSpecification, property: string, newValue: any) {
|
||||||
// Remove the property if undefined
|
// Remove the property if undefined
|
||||||
if(newValue === undefined) {
|
if(newValue === undefined) {
|
||||||
if(group) {
|
if(group) {
|
||||||
const newLayer = {
|
const newLayer: any = {
|
||||||
...layer,
|
...layer,
|
||||||
// Change object so the diff works in ./src/components/map/MapboxGlMap.jsx
|
// Change object so the diff works in ./src/components/map/MaplibreGlMap.jsx
|
||||||
[group]: {
|
[group]: {
|
||||||
...layer[group]
|
...layer[group] as any
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
delete newLayer[group][property];
|
delete newLayer[group][property];
|
||||||
@@ -45,7 +46,7 @@ export function changeProperty(layer, group, property, newValue) {
|
|||||||
}
|
}
|
||||||
return newLayer;
|
return newLayer;
|
||||||
} else {
|
} else {
|
||||||
const newLayer = {
|
const newLayer: any = {
|
||||||
...layer
|
...layer
|
||||||
};
|
};
|
||||||
delete newLayer[property];
|
delete newLayer[property];
|
||||||
@@ -57,7 +58,7 @@ export function changeProperty(layer, group, property, newValue) {
|
|||||||
return {
|
return {
|
||||||
...layer,
|
...layer,
|
||||||
[group]: {
|
[group]: {
|
||||||
...layer[group],
|
...layer[group] as any,
|
||||||
[property]: newValue
|
[property]: newValue
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,10 +1,22 @@
|
|||||||
import throttle from 'lodash.throttle'
|
import throttle from 'lodash.throttle'
|
||||||
import isEqual from 'lodash.isequal'
|
import isEqual from 'lodash.isequal'
|
||||||
|
import { Map } from 'maplibre-gl';
|
||||||
|
|
||||||
|
export type LayerWatcherOptions = {
|
||||||
|
onSourcesChange?: (sources: { [sourceId: string]: string[] }) => void;
|
||||||
|
onVectorLayersChange?: (vectorLayers: { [vectorLayerId: string]: { [propertyName: string]: { [propertyValue: string]: {} } } }) => void;
|
||||||
|
}
|
||||||
|
|
||||||
/** Listens to map events to build up a store of available vector
|
/** Listens to map events to build up a store of available vector
|
||||||
* layers contained in the tiles */
|
* layers contained in the tiles */
|
||||||
export default class LayerWatcher {
|
export default class LayerWatcher {
|
||||||
constructor(opts = {}) {
|
onSourcesChange: (sources: { [sourceId: string]: string[] }) => void;
|
||||||
|
onVectorLayersChange: (vectorLayers: { [vectorLayerId: string]: { [propertyName: string]: { [propertyValue: string]: {} } } }) => void;
|
||||||
|
throttledAnalyzeVectorLayerFields: (map: any) => void;
|
||||||
|
_sources: { [sourceId: string]: string[] };
|
||||||
|
_vectorLayers: { [vectorLayerId: string]: { [propertyName: string]: { [propertyValue: string]: {} } } };
|
||||||
|
|
||||||
|
constructor(opts: LayerWatcherOptions = {}) {
|
||||||
this.onSourcesChange = opts.onSourcesChange || (() => {})
|
this.onSourcesChange = opts.onSourcesChange || (() => {})
|
||||||
this.onVectorLayersChange = opts.onVectorLayersChange || (() => {})
|
this.onVectorLayersChange = opts.onVectorLayersChange || (() => {})
|
||||||
|
|
||||||
@@ -17,13 +29,13 @@ export default class LayerWatcher {
|
|||||||
this.throttledAnalyzeVectorLayerFields = throttle(this.analyzeVectorLayerFields, 5000)
|
this.throttledAnalyzeVectorLayerFields = throttle(this.analyzeVectorLayerFields, 5000)
|
||||||
}
|
}
|
||||||
|
|
||||||
analyzeMap(map) {
|
analyzeMap(map: Map) {
|
||||||
const previousSources = { ...this._sources }
|
const previousSources = { ...this._sources }
|
||||||
|
|
||||||
Object.keys(map.style.sourceCaches).forEach(sourceId => {
|
Object.keys(map.style.sourceCaches).forEach(sourceId => {
|
||||||
//NOTE: This heavily depends on the internal API of Mapbox GL
|
//NOTE: This heavily depends on the internal API of Maplibre GL
|
||||||
//so this breaks between Mapbox GL JS releases
|
//so this breaks between Maplibre GL JS releases
|
||||||
this._sources[sourceId] = map.style.sourceCaches[sourceId]._source.vectorLayerIds
|
this._sources[sourceId] = map.style.sourceCaches[sourceId]._source.vectorLayerIds as string[];
|
||||||
})
|
})
|
||||||
|
|
||||||
if(!isEqual(previousSources, this._sources)) {
|
if(!isEqual(previousSources, this._sources)) {
|
||||||
@@ -33,14 +45,14 @@ export default class LayerWatcher {
|
|||||||
this.throttledAnalyzeVectorLayerFields(map)
|
this.throttledAnalyzeVectorLayerFields(map)
|
||||||
}
|
}
|
||||||
|
|
||||||
analyzeVectorLayerFields(map) {
|
analyzeVectorLayerFields(map: Map) {
|
||||||
const previousVectorLayers = { ...this._vectorLayers }
|
const previousVectorLayers = { ...this._vectorLayers }
|
||||||
|
|
||||||
Object.keys(this._sources).forEach(sourceId => {
|
Object.keys(this._sources).forEach(sourceId => {
|
||||||
(this._sources[sourceId] || []).forEach(vectorLayerId => {
|
(this._sources[sourceId] || []).forEach(vectorLayerId => {
|
||||||
const knownProperties = this._vectorLayers[vectorLayerId] || {}
|
const knownProperties = this._vectorLayers[vectorLayerId] || {}
|
||||||
const params = { sourceLayer: vectorLayerId }
|
const params = { sourceLayer: vectorLayerId }
|
||||||
map.querySourceFeatures(sourceId, params).forEach(feature => {
|
map.querySourceFeatures(sourceId, params as any).forEach(feature => {
|
||||||
Object.keys(feature.properties).forEach(propertyName => {
|
Object.keys(feature.properties).forEach(propertyName => {
|
||||||
const knownPropertyValues = knownProperties[propertyName] || {}
|
const knownPropertyValues = knownProperties[propertyName] || {}
|
||||||
knownPropertyValues[feature.properties[propertyName]] = {}
|
knownPropertyValues[feature.properties[propertyName]] = {}
|
||||||
@@ -1,11 +0,0 @@
|
|||||||
import MapLibreGl from "maplibre-gl"
|
|
||||||
import {readFileSync} from 'fs'
|
|
||||||
|
|
||||||
const data = readFileSync(__dirname+"/../../node_modules/@mapbox/mapbox-gl-rtl-text/mapbox-gl-rtl-text.js", "utf8");
|
|
||||||
|
|
||||||
const blob = new window.Blob([data], {
|
|
||||||
type: "text/javascript"
|
|
||||||
});
|
|
||||||
const objectUrl = window.URL.createObjectURL(blob);
|
|
||||||
|
|
||||||
MapLibreGl.setRTLTextPlugin(objectUrl, () => {});
|
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
import MapLibreGl from "maplibre-gl"
|
||||||
|
|
||||||
|
MapLibreGl.setRTLTextPlugin('https://unpkg.com/@mapbox/mapbox-gl-rtl-text@0.2.3/mapbox-gl-rtl-text.min.js', () => {});
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
import npmurl from 'url'
|
import npmurl from 'url'
|
||||||
|
|
||||||
function loadJSON(url, defaultValue, cb) {
|
function loadJSON(url: string, defaultValue: any, cb: (...args: any[]) => void) {
|
||||||
fetch(url, {
|
fetch(url, {
|
||||||
mode: 'cors',
|
mode: 'cors',
|
||||||
credentials: "same-origin"
|
credentials: "same-origin"
|
||||||
@@ -17,7 +17,7 @@ function loadJSON(url, defaultValue, cb) {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
export function downloadGlyphsMetadata(urlTemplate, cb) {
|
export function downloadGlyphsMetadata(urlTemplate: string, cb: (...args: any[]) => void) {
|
||||||
if(!urlTemplate) return cb([])
|
if(!urlTemplate) return cb([])
|
||||||
|
|
||||||
// Special handling because Tileserver GL serves the fontstacks metadata differently
|
// Special handling because Tileserver GL serves the fontstacks metadata differently
|
||||||
@@ -27,14 +27,14 @@ export function downloadGlyphsMetadata(urlTemplate, cb) {
|
|||||||
if(urlObj.pathname === normPathPart) {
|
if(urlObj.pathname === normPathPart) {
|
||||||
urlObj.pathname = '/fontstacks.json';
|
urlObj.pathname = '/fontstacks.json';
|
||||||
} else {
|
} else {
|
||||||
urlObj.pathname = urlObj.pathname.replace(normPathPart, '.json');
|
urlObj.pathname = urlObj.pathname!.replace(normPathPart, '.json');
|
||||||
}
|
}
|
||||||
let url = npmurl.format(urlObj);
|
let url = npmurl.format(urlObj);
|
||||||
|
|
||||||
loadJSON(url, [], cb)
|
loadJSON(url, [], cb)
|
||||||
}
|
}
|
||||||
|
|
||||||
export function downloadSpriteMetadata(baseUrl, cb) {
|
export function downloadSpriteMetadata(baseUrl: string, cb: (...args: any[]) => void) {
|
||||||
if(!baseUrl) return cb([])
|
if(!baseUrl) return cb([])
|
||||||
const url = baseUrl + '.json'
|
const url = baseUrl + '.json'
|
||||||
loadJSON(url, {}, glyphs => cb(Object.keys(glyphs)))
|
loadJSON(url, {}, glyphs => cb(Object.keys(glyphs)))
|
||||||
@@ -1,12 +0,0 @@
|
|||||||
function asBool(queryObj, key) {
|
|
||||||
if(queryObj.hasOwnProperty(key)) {
|
|
||||||
return !queryObj[key].match(/^false|0$/);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
module.exports = {
|
|
||||||
asBool
|
|
||||||
}
|
|
||||||
@@ -1,4 +1,10 @@
|
|||||||
|
import type {StyleSpecification} from "@maplibre/maplibre-gl-style-spec";
|
||||||
|
|
||||||
export class RevisionStore {
|
export class RevisionStore {
|
||||||
|
revisions: StyleSpecification[];
|
||||||
|
currentIdx: number;
|
||||||
|
|
||||||
|
|
||||||
constructor(initialRevisions=[]) {
|
constructor(initialRevisions=[]) {
|
||||||
this.revisions = initialRevisions
|
this.revisions = initialRevisions
|
||||||
this.currentIdx = initialRevisions.length - 1
|
this.currentIdx = initialRevisions.length - 1
|
||||||
@@ -12,7 +18,7 @@ export class RevisionStore {
|
|||||||
return this.revisions[this.currentIdx]
|
return this.revisions[this.currentIdx]
|
||||||
}
|
}
|
||||||
|
|
||||||
addRevision(revision) {
|
addRevision(revision: StyleSpecification) {
|
||||||
//TODO: compare new revision style id with old ones
|
//TODO: compare new revision style id with old ones
|
||||||
//and ensure that it is always the same id
|
//and ensure that it is always the same id
|
||||||
this.revisions.push(revision)
|
this.revisions.push(revision)
|
||||||
@@ -21,15 +27,15 @@ export class RevisionStore {
|
|||||||
|
|
||||||
undo() {
|
undo() {
|
||||||
if(this.currentIdx > 0) {
|
if(this.currentIdx > 0) {
|
||||||
this.currentIdx--
|
this.currentIdx--;
|
||||||
}
|
}
|
||||||
return this.current
|
return this.current;
|
||||||
}
|
}
|
||||||
|
|
||||||
redo() {
|
redo() {
|
||||||
if(this.currentIdx < this.revisions.length - 1) {
|
if(this.currentIdx < this.revisions.length - 1) {
|
||||||
this.currentIdx++
|
this.currentIdx++
|
||||||
}
|
}
|
||||||
return this.current
|
return this.current;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,14 +0,0 @@
|
|||||||
export default function(a, b) {
|
|
||||||
a = parseFloat(a);
|
|
||||||
b = parseFloat(b);
|
|
||||||
|
|
||||||
if(a < b) {
|
|
||||||
return -1
|
|
||||||
}
|
|
||||||
else if(a > b) {
|
|
||||||
return 1
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,14 @@
|
|||||||
|
export default function(num1: string | number, num2: string| number) {
|
||||||
|
const a = +num1;
|
||||||
|
const b = +num2;
|
||||||
|
|
||||||
|
if(a < b) {
|
||||||
|
return -1
|
||||||
|
}
|
||||||
|
else if(a > b) {
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,25 +0,0 @@
|
|||||||
export function deleteSource(mapStyle, sourceId) {
|
|
||||||
const remainingSources = { ...mapStyle.sources}
|
|
||||||
delete remainingSources[sourceId]
|
|
||||||
return {
|
|
||||||
...mapStyle,
|
|
||||||
sources: remainingSources
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
export function addSource(mapStyle, sourceId, source) {
|
|
||||||
return changeSource(mapStyle, sourceId, source)
|
|
||||||
}
|
|
||||||
|
|
||||||
export function changeSource(mapStyle, sourceId, source) {
|
|
||||||
const changedSources = {
|
|
||||||
...mapStyle.sources,
|
|
||||||
[sourceId]: source
|
|
||||||
}
|
|
||||||
return {
|
|
||||||
...mapStyle,
|
|
||||||
sources: changedSources
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@@ -0,0 +1,27 @@
|
|||||||
|
import type {StyleSpecification, SourceSpecification} from "@maplibre/maplibre-gl-style-spec";
|
||||||
|
|
||||||
|
export function deleteSource(mapStyle: StyleSpecification, sourceId: string) {
|
||||||
|
const remainingSources = { ...mapStyle.sources}
|
||||||
|
delete remainingSources[sourceId]
|
||||||
|
return {
|
||||||
|
...mapStyle,
|
||||||
|
sources: remainingSources
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
export function addSource(mapStyle: StyleSpecification, sourceId: string, source: SourceSpecification) {
|
||||||
|
return changeSource(mapStyle, sourceId, source)
|
||||||
|
}
|
||||||
|
|
||||||
|
export function changeSource(mapStyle: StyleSpecification, sourceId: string, source: SourceSpecification) {
|
||||||
|
const changedSources = {
|
||||||
|
...mapStyle.sources,
|
||||||
|
[sourceId]: source
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
...mapStyle,
|
||||||
|
sources: changedSources
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
import {derefLayers} from '@maplibre/maplibre-gl-style-spec'
|
import {derefLayers, StyleSpecification, LayerSpecification} from '@maplibre/maplibre-gl-style-spec'
|
||||||
import tokens from '../config/tokens.json'
|
import tokens from '../config/tokens.json'
|
||||||
|
|
||||||
// Empty style is always used if no style could be restored or fetched
|
// Empty style is always used if no style could be restored or fetched
|
||||||
@@ -9,18 +9,20 @@ const emptyStyle = ensureStyleValidity({
|
|||||||
})
|
})
|
||||||
|
|
||||||
function generateId() {
|
function generateId() {
|
||||||
return Math.random().toString(36).substr(2, 9)
|
return Math.random().toString(36).substring(2, 9)
|
||||||
}
|
}
|
||||||
|
|
||||||
function ensureHasId(style) {
|
function ensureHasId(style: StyleSpecification & { id?: string }): StyleSpecification & { id: string } {
|
||||||
if('id' in style) return style
|
if(!('id' in style) || !style.id) {
|
||||||
style.id = generateId()
|
style.id = generateId();
|
||||||
return style
|
return style as StyleSpecification & { id: string };
|
||||||
|
}
|
||||||
|
return style as StyleSpecification & { id: string };
|
||||||
}
|
}
|
||||||
|
|
||||||
function ensureHasNoInteractive(style) {
|
function ensureHasNoInteractive(style: StyleSpecification & {id: string}) {
|
||||||
const changedLayers = style.layers.map(layer => {
|
const changedLayers = style.layers.map(layer => {
|
||||||
const changedLayer = { ...layer }
|
const changedLayer: LayerSpecification & { interactive?: any } = { ...layer }
|
||||||
delete changedLayer.interactive
|
delete changedLayer.interactive
|
||||||
return changedLayer
|
return changedLayer
|
||||||
})
|
})
|
||||||
@@ -31,18 +33,18 @@ function ensureHasNoInteractive(style) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function ensureHasNoRefs(style) {
|
function ensureHasNoRefs(style: StyleSpecification & {id: string}) {
|
||||||
return {
|
return {
|
||||||
...style,
|
...style,
|
||||||
layers: derefLayers(style.layers)
|
layers: derefLayers(style.layers)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function ensureStyleValidity(style) {
|
function ensureStyleValidity(style: StyleSpecification): StyleSpecification & { id: string } {
|
||||||
return ensureHasNoInteractive(ensureHasNoRefs(ensureHasId(style)))
|
return ensureHasNoInteractive(ensureHasNoRefs(ensureHasId(style)))
|
||||||
}
|
}
|
||||||
|
|
||||||
function indexOfLayer(layers, layerId) {
|
function indexOfLayer(layers: LayerSpecification[], layerId: string) {
|
||||||
for (let i = 0; i < layers.length; i++) {
|
for (let i = 0; i < layers.length; i++) {
|
||||||
if(layers[i].id === layerId) {
|
if(layers[i].id === layerId) {
|
||||||
return i
|
return i
|
||||||
@@ -51,25 +53,25 @@ function indexOfLayer(layers, layerId) {
|
|||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
|
|
||||||
function getAccessToken(sourceName, mapStyle, opts) {
|
function getAccessToken(sourceName: string, mapStyle: StyleSpecification, opts: {allowFallback?: boolean}) {
|
||||||
if(sourceName === "thunderforest_transport" || sourceName === "thunderforest_outdoors") {
|
if(sourceName === "thunderforest_transport" || sourceName === "thunderforest_outdoors") {
|
||||||
sourceName = "thunderforest"
|
sourceName = "thunderforest"
|
||||||
}
|
}
|
||||||
|
|
||||||
const metadata = mapStyle.metadata || {}
|
const metadata = mapStyle.metadata || {} as any;
|
||||||
let accessToken = metadata[`maputnik:${sourceName}_access_token`]
|
let accessToken = metadata[`maputnik:${sourceName}_access_token`]
|
||||||
|
|
||||||
if(opts.allowFallback && !accessToken) {
|
if(opts.allowFallback && !accessToken) {
|
||||||
accessToken = tokens[sourceName]
|
accessToken = tokens[sourceName as keyof typeof tokens]
|
||||||
}
|
}
|
||||||
|
|
||||||
return accessToken;
|
return accessToken;
|
||||||
}
|
}
|
||||||
|
|
||||||
function replaceSourceAccessToken(mapStyle, sourceName, opts={}) {
|
function replaceSourceAccessToken(mapStyle: StyleSpecification, sourceName: string, opts={}) {
|
||||||
const source = mapStyle.sources[sourceName]
|
const source = mapStyle.sources[sourceName]
|
||||||
if(!source) return mapStyle
|
if(!source) return mapStyle
|
||||||
if(!source.hasOwnProperty("url")) return mapStyle
|
if(!("url" in source) || !source.url) return mapStyle
|
||||||
|
|
||||||
const accessToken = getAccessToken(sourceName, mapStyle, opts)
|
const accessToken = getAccessToken(sourceName, mapStyle, opts)
|
||||||
|
|
||||||
@@ -92,7 +94,7 @@ function replaceSourceAccessToken(mapStyle, sourceName, opts={}) {
|
|||||||
return changedStyle
|
return changedStyle
|
||||||
}
|
}
|
||||||
|
|
||||||
function replaceAccessTokens(mapStyle, opts={}) {
|
function replaceAccessTokens(mapStyle: StyleSpecification, opts={}) {
|
||||||
let changedStyle = mapStyle
|
let changedStyle = mapStyle
|
||||||
|
|
||||||
Object.keys(mapStyle.sources).forEach((sourceName) => {
|
Object.keys(mapStyle.sources).forEach((sourceName) => {
|
||||||
@@ -112,11 +114,10 @@ function replaceAccessTokens(mapStyle, opts={}) {
|
|||||||
return changedStyle
|
return changedStyle
|
||||||
}
|
}
|
||||||
|
|
||||||
function stripAccessTokens(mapStyle) {
|
function stripAccessTokens(mapStyle: StyleSpecification) {
|
||||||
const changedMetadata = {
|
const changedMetadata = {
|
||||||
...mapStyle.metadata
|
...mapStyle.metadata as any
|
||||||
};
|
};
|
||||||
delete changedMetadata['maputnik:mapbox_access_token'];
|
|
||||||
delete changedMetadata['maputnik:openmaptiles_access_token'];
|
delete changedMetadata['maputnik:openmaptiles_access_token'];
|
||||||
return {
|
return {
|
||||||
...mapStyle,
|
...mapStyle,
|
||||||
@@ -1,6 +1,7 @@
|
|||||||
import style from './style.js'
|
import style from './style'
|
||||||
import {loadStyleUrl} from './urlopen'
|
import {loadStyleUrl} from './urlopen'
|
||||||
import publicSources from '../config/styles.json'
|
import publicSources from '../config/styles.json'
|
||||||
|
import { StyleSpecification } from '@maplibre/maplibre-gl-style-spec'
|
||||||
|
|
||||||
const storagePrefix = "maputnik"
|
const storagePrefix = "maputnik"
|
||||||
const stylePrefix = 'style'
|
const stylePrefix = 'style'
|
||||||
@@ -12,7 +13,7 @@ const storageKeys = {
|
|||||||
const defaultStyleUrl = publicSources[0].url
|
const defaultStyleUrl = publicSources[0].url
|
||||||
|
|
||||||
// Fetch a default style via URL and return it or a fallback style via callback
|
// Fetch a default style via URL and return it or a fallback style via callback
|
||||||
export function loadDefaultStyle(cb) {
|
export function loadDefaultStyle(cb: (...args: any[]) => void) {
|
||||||
loadStyleUrl(defaultStyleUrl, cb)
|
loadStyleUrl(defaultStyleUrl, cb)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -21,20 +22,20 @@ function loadStoredStyles() {
|
|||||||
const styles = []
|
const styles = []
|
||||||
for (let i = 0; i < window.localStorage.length; i++) {
|
for (let i = 0; i < window.localStorage.length; i++) {
|
||||||
const key = window.localStorage.key(i)
|
const key = window.localStorage.key(i)
|
||||||
if(isStyleKey(key)) {
|
if(isStyleKey(key!)) {
|
||||||
styles.push(fromKey(key))
|
styles.push(fromKey(key!))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return styles
|
return styles
|
||||||
}
|
}
|
||||||
|
|
||||||
function isStyleKey(key) {
|
function isStyleKey(key: string) {
|
||||||
const parts = key.split(":")
|
const parts = key.split(":")
|
||||||
return parts.length === 3 && parts[0] === storagePrefix && parts[1] === stylePrefix
|
return parts.length === 3 && parts[0] === storagePrefix && parts[1] === stylePrefix
|
||||||
}
|
}
|
||||||
|
|
||||||
// Load style id from key
|
// Load style id from key
|
||||||
function fromKey(key) {
|
function fromKey(key: string) {
|
||||||
if(!isStyleKey(key)) {
|
if(!isStyleKey(key)) {
|
||||||
throw "Key is not a valid style key"
|
throw "Key is not a valid style key"
|
||||||
}
|
}
|
||||||
@@ -45,26 +46,31 @@ function fromKey(key) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Calculate key that identifies the style with a version
|
// Calculate key that identifies the style with a version
|
||||||
function styleKey(styleId) {
|
function styleKey(styleId: string) {
|
||||||
return [storagePrefix, stylePrefix, styleId].join(":")
|
return [storagePrefix, stylePrefix, styleId].join(":")
|
||||||
}
|
}
|
||||||
|
|
||||||
// Manages many possible styles that are stored in the local storage
|
// Manages many possible styles that are stored in the local storage
|
||||||
export class StyleStore {
|
export class StyleStore {
|
||||||
|
/**
|
||||||
|
* List of style ids
|
||||||
|
*/
|
||||||
|
mapStyles: string[];
|
||||||
|
|
||||||
// Tile store will load all items from local storage and
|
// Tile store will load all items from local storage and
|
||||||
// assume they do not change will working on it
|
// assume they do not change will working on it
|
||||||
constructor() {
|
constructor() {
|
||||||
this.mapStyles = loadStoredStyles()
|
this.mapStyles = loadStoredStyles();
|
||||||
}
|
}
|
||||||
|
|
||||||
init(cb) {
|
init(cb: (...args: any[]) => void) {
|
||||||
cb(null)
|
cb(null)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Delete entire style history
|
// Delete entire style history
|
||||||
purge() {
|
purge() {
|
||||||
for (let i = 0; i < window.localStorage.length; i++) {
|
for (let i = 0; i < window.localStorage.length; i++) {
|
||||||
const key = window.localStorage.key(i)
|
const key = window.localStorage.key(i) as string;
|
||||||
if(key.startsWith(storagePrefix)) {
|
if(key.startsWith(storagePrefix)) {
|
||||||
window.localStorage.removeItem(key)
|
window.localStorage.removeItem(key)
|
||||||
}
|
}
|
||||||
@@ -72,9 +78,9 @@ export class StyleStore {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Find the last edited style
|
// Find the last edited style
|
||||||
latestStyle(cb) {
|
latestStyle(cb: (...args: any[]) => void) {
|
||||||
if(this.mapStyles.length === 0) return loadDefaultStyle(cb)
|
if(this.mapStyles.length === 0) return loadDefaultStyle(cb)
|
||||||
const styleId = window.localStorage.getItem(storageKeys.latest)
|
const styleId = window.localStorage.getItem(storageKeys.latest) as string;
|
||||||
const styleItem = window.localStorage.getItem(styleKey(styleId))
|
const styleItem = window.localStorage.getItem(styleKey(styleId))
|
||||||
|
|
||||||
if(styleItem) return cb(JSON.parse(styleItem))
|
if(styleItem) return cb(JSON.parse(styleItem))
|
||||||
@@ -82,7 +88,7 @@ export class StyleStore {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Save current style replacing previous version
|
// Save current style replacing previous version
|
||||||
save(mapStyle) {
|
save(mapStyle: StyleSpecification & { id: string }) {
|
||||||
mapStyle = style.ensureStyleValidity(mapStyle)
|
mapStyle = style.ensureStyleValidity(mapStyle)
|
||||||
const key = styleKey(mapStyle.id)
|
const key = styleKey(mapStyle.id)
|
||||||
window.localStorage.setItem(key, JSON.stringify(mapStyle))
|
window.localStorage.setItem(key, JSON.stringify(mapStyle))
|
||||||
@@ -1,65 +0,0 @@
|
|||||||
import url from 'url'
|
|
||||||
import querystring from 'querystring'
|
|
||||||
import style from './style.js'
|
|
||||||
|
|
||||||
export function initialStyleUrl() {
|
|
||||||
const initialUrl = url.parse(window.location.href, true)
|
|
||||||
return (initialUrl.query || {}).style
|
|
||||||
}
|
|
||||||
|
|
||||||
export function loadStyleUrl(styleUrl, cb) {
|
|
||||||
console.log('Loading style', styleUrl)
|
|
||||||
fetch(styleUrl, {
|
|
||||||
mode: 'cors',
|
|
||||||
credentials: "same-origin"
|
|
||||||
})
|
|
||||||
.then(function(response) {
|
|
||||||
return response.json();
|
|
||||||
})
|
|
||||||
.then(function(body) {
|
|
||||||
cb(style.ensureStyleValidity(body))
|
|
||||||
})
|
|
||||||
.catch(function() {
|
|
||||||
console.warn('Could not fetch default style', styleUrl)
|
|
||||||
cb(style.emptyStyle)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
export function removeStyleQuerystring() {
|
|
||||||
const initialUrl = url.parse(window.location.href, true)
|
|
||||||
let qs = querystring.parse(window.location.search.slice(1))
|
|
||||||
delete qs["style"]
|
|
||||||
if(Object.getOwnPropertyNames(qs).length === 0) {
|
|
||||||
qs = ""
|
|
||||||
} else {
|
|
||||||
qs = "?" + querystring.stringify(qs)
|
|
||||||
}
|
|
||||||
let newUrlHash = initialUrl.hash
|
|
||||||
if(newUrlHash === null) {
|
|
||||||
newUrlHash = ""
|
|
||||||
}
|
|
||||||
const newUrl = initialUrl.protocol + "//" + initialUrl.host + initialUrl.pathname + qs + newUrlHash
|
|
||||||
window.history.replaceState({}, document.title, newUrl)
|
|
||||||
}
|
|
||||||
|
|
||||||
export function loadJSON(url, defaultValue, cb) {
|
|
||||||
fetch(url, {
|
|
||||||
mode: 'cors',
|
|
||||||
credentials: "same-origin"
|
|
||||||
})
|
|
||||||
.then(function(response) {
|
|
||||||
return response.json();
|
|
||||||
})
|
|
||||||
.then(function(body) {
|
|
||||||
try {
|
|
||||||
cb(body)
|
|
||||||
} catch(err) {
|
|
||||||
console.error(err)
|
|
||||||
cb(defaultValue)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.catch(function() {
|
|
||||||
console.error('Can not load JSON from ' + url)
|
|
||||||
cb(defaultValue)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,31 @@
|
|||||||
|
// @ts-ignore
|
||||||
|
import style from './style'
|
||||||
|
|
||||||
|
export function initialStyleUrl() {
|
||||||
|
const initialUrl = new URL(window.location.href);
|
||||||
|
return initialUrl.searchParams.get('style');
|
||||||
|
}
|
||||||
|
|
||||||
|
export function loadStyleUrl(styleUrl: string, cb: (...args: any[]) => void) {
|
||||||
|
console.log('Loading style', styleUrl)
|
||||||
|
fetch(styleUrl, {
|
||||||
|
mode: 'cors',
|
||||||
|
credentials: "same-origin"
|
||||||
|
})
|
||||||
|
.then(function(response) {
|
||||||
|
return response.json();
|
||||||
|
})
|
||||||
|
.then(function(body) {
|
||||||
|
cb(style.ensureStyleValidity(body))
|
||||||
|
})
|
||||||
|
.catch(function() {
|
||||||
|
console.warn('Could not fetch default style', styleUrl)
|
||||||
|
cb(style.emptyStyle)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
export function removeStyleQuerystring() {
|
||||||
|
const initialUrl = new URL(window.location.href);
|
||||||
|
initialUrl.searchParams.delete('style');
|
||||||
|
window.history.replaceState({}, document.title, initialUrl.toString())
|
||||||
|
}
|
||||||
@@ -1,30 +0,0 @@
|
|||||||
export default class ZoomControl {
|
|
||||||
onAdd(map) {
|
|
||||||
this._map = map;
|
|
||||||
this._container = document.createElement('div');
|
|
||||||
this._container.className = 'mapboxgl-ctrl mapboxgl-ctrl-group mapboxgl-ctrl-zoom';
|
|
||||||
this._container.innerHTML = `
|
|
||||||
Zoom: <span></span>
|
|
||||||
`;
|
|
||||||
this._textEl = this._container.querySelector("span");
|
|
||||||
|
|
||||||
this.addEventListeners();
|
|
||||||
|
|
||||||
return this._container;
|
|
||||||
}
|
|
||||||
|
|
||||||
updateZoomLevel() {
|
|
||||||
this._textEl.innerHTML = this._map.getZoom().toFixed(2);
|
|
||||||
}
|
|
||||||
|
|
||||||
addEventListeners (){
|
|
||||||
this._map.on('render', this.updateZoomLevel.bind(this) );
|
|
||||||
this._map.on('zoomIn', this.updateZoomLevel.bind(this) );
|
|
||||||
this._map.on('zoomOut', this.updateZoomLevel.bind(this) );
|
|
||||||
}
|
|
||||||
|
|
||||||
onRemove() {
|
|
||||||
this._container.parentNode.removeChild(this._container);
|
|
||||||
this._map = undefined;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,37 @@
|
|||||||
|
import {Map} from 'maplibre-gl';
|
||||||
|
|
||||||
|
export default class ZoomControl {
|
||||||
|
_map: Map| undefined = undefined;
|
||||||
|
_container: HTMLDivElement | undefined = undefined;
|
||||||
|
_textEl: HTMLSpanElement | null = null;
|
||||||
|
|
||||||
|
onAdd(map: Map) {
|
||||||
|
this._map = map;
|
||||||
|
this._container = document.createElement('div');
|
||||||
|
this._container.className = 'maplibregl-ctrl maplibregl-ctrl-group maplibregl-ctrl-zoom';
|
||||||
|
this._container.setAttribute("data-wd-key", "maplibre:ctrl-zoom");
|
||||||
|
this._container.innerHTML = `
|
||||||
|
Zoom: <span></span>
|
||||||
|
`;
|
||||||
|
this._textEl = this._container.querySelector("span");
|
||||||
|
|
||||||
|
this.addEventListeners();
|
||||||
|
|
||||||
|
return this._container;
|
||||||
|
}
|
||||||
|
|
||||||
|
updateZoomLevel() {
|
||||||
|
this._textEl!.innerHTML = this._map!.getZoom().toFixed(2);
|
||||||
|
}
|
||||||
|
|
||||||
|
addEventListeners (){
|
||||||
|
this._map!.on('render', () => this.updateZoomLevel());
|
||||||
|
this._map!.on('zoomIn', () => this.updateZoomLevel());
|
||||||
|
this._map!.on('zoomOut', () => this.updateZoomLevel());
|
||||||
|
}
|
||||||
|
|
||||||
|
onRemove() {
|
||||||
|
this._container!.parentNode!.removeChild(this._container!);
|
||||||
|
this._map = undefined;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,93 +1,91 @@
|
|||||||
/* TODO */
|
.maplibregl-popup-anchor-top .maplibregl-popup-tip {
|
||||||
|
|
||||||
.mapboxgl-popup-anchor-top .mapboxgl-popup-tip {
|
|
||||||
border-bottom-color: rgb(28, 31, 36);
|
border-bottom-color: rgb(28, 31, 36);
|
||||||
}
|
}
|
||||||
|
|
||||||
.mapboxgl-popup-anchor-left .mapboxgl-popup-tip {
|
.maplibregl-popup-anchor-left .maplibregl-popup-tip {
|
||||||
border-right-color: rgb(28, 31, 36);
|
border-right-color: rgb(28, 31, 36);
|
||||||
}
|
}
|
||||||
|
|
||||||
.mapboxgl-popup-anchor-right .mapboxgl-popup-tip {
|
.maplibregl-popup-anchor-right .maplibregl-popup-tip {
|
||||||
border-left-color: rgb(28, 31, 36);
|
border-left-color: rgb(28, 31, 36);
|
||||||
}
|
}
|
||||||
|
|
||||||
.mapboxgl-popup-anchor-bottom .mapboxgl-popup-tip {
|
.maplibregl-popup-anchor-bottom .maplibregl-popup-tip {
|
||||||
border-top-color: rgb(28, 31, 36);
|
border-top-color: rgb(28, 31, 36);
|
||||||
}
|
}
|
||||||
|
|
||||||
.mapboxgl-popup-anchor-top-left .mapboxgl-popup-tip {
|
.maplibregl-popup-anchor-top-left .maplibregl-popup-tip {
|
||||||
border-bottom-color: rgb(28, 31, 36);
|
border-bottom-color: rgb(28, 31, 36);
|
||||||
}
|
}
|
||||||
|
|
||||||
.mapboxgl-popup-anchor-top-right .mapboxgl-popup-tip {
|
.maplibregl-popup-anchor-top-right .maplibregl-popup-tip {
|
||||||
border-bottom-color: rgb(28, 31, 36);
|
border-bottom-color: rgb(28, 31, 36);
|
||||||
}
|
}
|
||||||
|
|
||||||
.mapboxgl-popup-anchor-bottom-left .mapboxgl-popup-tip {
|
.maplibregl-popup-anchor-bottom-left .maplibregl-popup-tip {
|
||||||
border-top-color: rgb(28, 31, 36);
|
border-top-color: rgb(28, 31, 36);
|
||||||
}
|
}
|
||||||
|
|
||||||
.mapboxgl-popup-anchor-bottom-right .mapboxgl-popup-tip {
|
.maplibregl-popup-anchor-bottom-right .maplibregl-popup-tip {
|
||||||
border-top-color: rgb(28, 31, 36);
|
border-top-color: rgb(28, 31, 36);
|
||||||
}
|
}
|
||||||
|
|
||||||
.mapboxgl-popup-content {
|
.maplibregl-popup-content {
|
||||||
background-color: rgb(28, 31, 36);
|
background-color: rgb(28, 31, 36);
|
||||||
box-shadow: rgba(0, 0, 0, 0.298039) 0px 0px 5px 0px;
|
box-shadow: rgba(0, 0, 0, 0.298039) 0px 0px 5px 0px;
|
||||||
padding: 0px;
|
padding: 0px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.mapboxgl-popup-close-button {
|
.maplibregl-popup-close-button {
|
||||||
color: white;
|
color: white;
|
||||||
}
|
}
|
||||||
|
|
||||||
.mapboxgl-ctrl-zoom {
|
.maplibregl-ctrl-zoom {
|
||||||
color: #a4a4a4;
|
color: #a4a4a4;
|
||||||
font-weight: bold;
|
font-weight: bold;
|
||||||
padding: 4px 8px;
|
padding: 4px 8px;
|
||||||
user-select: none;
|
user-select: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
.mapboxgl-ctrl-group {
|
.maplibregl-ctrl-group {
|
||||||
background: rgb(28, 31, 36);
|
background: rgb(28, 31, 36);
|
||||||
}
|
}
|
||||||
|
|
||||||
.mapboxgl-ctrl-group > button {
|
.maplibregl-ctrl-group > button {
|
||||||
background-color: rgb(28, 31, 36);
|
background-color: rgb(28, 31, 36);
|
||||||
border-color: rgb(28, 31, 36);
|
border-color: rgb(28, 31, 36);
|
||||||
}
|
}
|
||||||
|
|
||||||
.mapboxgl-ctrl button:not(:disabled):hover {
|
.maplibregl-ctrl button:not(:disabled):hover {
|
||||||
background-color: rgb(86, 83, 83);
|
background-color: rgb(86, 83, 83);
|
||||||
}
|
}
|
||||||
|
|
||||||
.mapboxgl-ctrl-zoom-in {
|
.maplibregl-ctrl-zoom-in {
|
||||||
border-radius: 4px 4px 0 0;
|
border-radius: 4px 4px 0 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.mapboxgl-ctrl-compass {
|
.maplibregl-ctrl-compass {
|
||||||
border-radius: 0 0 4px 4px;
|
border-radius: 0 0 4px 4px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.mapboxgl-ctrl button.mapboxgl-ctrl-zoom-in .mapboxgl-ctrl-icon {
|
.maplibregl-ctrl button.maplibregl-ctrl-zoom-in .maplibregl-ctrl-icon {
|
||||||
background-image: url("data:image/svg+xml;charset=utf8,%3Csvg%20viewBox%3D%270%200%2020%2020%27%20xmlns%3D%27http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%27%3E%0A%20%20%3Cpath%20style%3D%27fill%3A%23a4a4a4%3B%27%20d%3D%27M%2010%206%20C%209.446%206%209%206.4459904%209%207%20L%209%209%20L%207%209%20C%206.446%209%206%209.446%206%2010%20C%206%2010.554%206.446%2011%207%2011%20L%209%2011%20L%209%2013%20C%209%2013.55401%209.446%2014%2010%2014%20C%2010.554%2014%2011%2013.55401%2011%2013%20L%2011%2011%20L%2013%2011%20C%2013.554%2011%2014%2010.554%2014%2010%20C%2014%209.446%2013.554%209%2013%209%20L%2011%209%20L%2011%207%20C%2011%206.4459904%2010.554%206%2010%206%20z%27%20%2F%3E%0A%3C%2Fsvg%3E%0A")
|
background-image: url("data:image/svg+xml;charset=utf8,%3Csvg%20viewBox%3D%270%200%2020%2020%27%20xmlns%3D%27http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%27%3E%0A%20%20%3Cpath%20style%3D%27fill%3A%23a4a4a4%3B%27%20d%3D%27M%2010%206%20C%209.446%206%209%206.4459904%209%207%20L%209%209%20L%207%209%20C%206.446%209%206%209.446%206%2010%20C%206%2010.554%206.446%2011%207%2011%20L%209%2011%20L%209%2013%20C%209%2013.55401%209.446%2014%2010%2014%20C%2010.554%2014%2011%2013.55401%2011%2013%20L%2011%2011%20L%2013%2011%20C%2013.554%2011%2014%2010.554%2014%2010%20C%2014%209.446%2013.554%209%2013%209%20L%2011%209%20L%2011%207%20C%2011%206.4459904%2010.554%206%2010%206%20z%27%20%2F%3E%0A%3C%2Fsvg%3E%0A")
|
||||||
}
|
}
|
||||||
|
|
||||||
.mapboxgl-ctrl button.mapboxgl-ctrl-zoom-out .mapboxgl-ctrl-icon {
|
.maplibregl-ctrl button.maplibregl-ctrl-zoom-out .maplibregl-ctrl-icon {
|
||||||
background-image: url("data:image/svg+xml;charset=utf8,%3Csvg%20viewBox%3D%270%200%2020%2020%27%20xmlns%3D%27http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%27%3E%0A%20%20%3Cpath%20style%3D%27fill%3A%23a4a4a4%3B%27%20d%3D%27m%207%2C9%20c%20-0.554%2C0%20-1%2C0.446%20-1%2C1%200%2C0.554%200.446%2C1%201%2C1%20l%206%2C0%20c%200.554%2C0%201%2C-0.446%201%2C-1%200%2C-0.554%20-0.446%2C-1%20-1%2C-1%20z%27%20%2F%3E%0A%3C%2Fsvg%3E%0A")
|
background-image: url("data:image/svg+xml;charset=utf8,%3Csvg%20viewBox%3D%270%200%2020%2020%27%20xmlns%3D%27http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%27%3E%0A%20%20%3Cpath%20style%3D%27fill%3A%23a4a4a4%3B%27%20d%3D%27m%207%2C9%20c%20-0.554%2C0%20-1%2C0.446%20-1%2C1%200%2C0.554%200.446%2C1%201%2C1%20l%206%2C0%20c%200.554%2C0%201%2C-0.446%201%2C-1%200%2C-0.554%20-0.446%2C-1%20-1%2C-1%20z%27%20%2F%3E%0A%3C%2Fsvg%3E%0A")
|
||||||
}
|
}
|
||||||
|
|
||||||
.mapboxgl-ctrl button.mapboxgl-ctrl-compass .mapboxgl-ctrl-icon {
|
.maplibregl-ctrl button.maplibregl-ctrl-compass .maplibregl-ctrl-icon {
|
||||||
background-image: url("data:image/svg+xml;charset=utf8,%3Csvg%20xmlns%3D%27http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%27%20viewBox%3D%270%200%2020%2020%27%3E%0A%09%3Cpolygon%20fill%3D%27%23a4a4a4%27%20points%3D%276%2C9%2010%2C1%2014%2C9%27%2F%3E%0A%09%3Cpolygon%20fill%3D%27%23545454%27%20points%3D%276%2C11%2010%2C19%2014%2C11%20%27%2F%3E%0A%3C%2Fsvg%3E");
|
background-image: url("data:image/svg+xml;charset=utf8,%3Csvg%20xmlns%3D%27http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%27%20viewBox%3D%270%200%2020%2020%27%3E%0A%09%3Cpolygon%20fill%3D%27%23a4a4a4%27%20points%3D%276%2C9%2010%2C1%2014%2C9%27%2F%3E%0A%09%3Cpolygon%20fill%3D%27%23545454%27%20points%3D%276%2C11%2010%2C19%2014%2C11%20%27%2F%3E%0A%3C%2Fsvg%3E");
|
||||||
background-size: 18px;
|
background-size: 18px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.mapboxgl-ctrl-inspect {
|
.maplibregl-ctrl-inspect {
|
||||||
background-image: url('data:image/svg+xml;charset=utf8,<svg%20xmlns="http://www.w3.org/2000/svg"%20fill="#8e8e8e%22%20preserveAspectRatio=%22xMidYMid%20meet%22%20viewBox=%22-10%20-10%2060%2060%22%3E%3Cg%3E%3Cpath%20d=%22m15%2021.6q0-2%201.5-3.5t3.5-1.5%203.5%201.5%201.5%203.5-1.5%203.6-3.5%201.4-3.5-1.4-1.5-3.6z%20m18.4%2011.1l-6.4-6.5q1.4-2.1%201.4-4.6%200-3.4-2.5-5.8t-5.9-2.4-5.9%202.4-2.5%205.8%202.5%205.9%205.9%202.5q2.4%200%204.6-1.4l7.4%207.4q-0.9%200.6-2%200.6h-20q-1.3%200-2.3-0.9t-1.1-2.3l0.1-26.8q0-1.3%201-2.3t2.3-0.9h13.4l10%2010v19.3z%22%3E%3C/path%3E%3C/g%3E%3C/svg%3E');
|
background-image: url('data:image/svg+xml;charset=utf8,<svg%20xmlns="http://www.w3.org/2000/svg"%20fill="#8e8e8e%22%20preserveAspectRatio=%22xMidYMid%20meet%22%20viewBox=%22-10%20-10%2060%2060%22%3E%3Cg%3E%3Cpath%20d=%22m15%2021.6q0-2%201.5-3.5t3.5-1.5%203.5%201.5%201.5%203.5-1.5%203.6-3.5%201.4-3.5-1.4-1.5-3.6z%20m18.4%2011.1l-6.4-6.5q1.4-2.1%201.4-4.6%200-3.4-2.5-5.8t-5.9-2.4-5.9%202.4-2.5%205.8%202.5%205.9%205.9%202.5q2.4%200%204.6-1.4l7.4%207.4q-0.9%200.6-2%200.6h-20q-1.3%200-2.3-0.9t-1.1-2.3l0.1-26.8q0-1.3%201-2.3t2.3-0.9h13.4l10%2010v19.3z%22%3E%3C/path%3E%3C/g%3E%3C/svg%3E');
|
||||||
}
|
}
|
||||||
|
|
||||||
.mapboxgl-ctrl-map {
|
.maplibregl-ctrl-map {
|
||||||
background-image: url('data:image/svg+xml;charset=utf8,<svg%20xmlns="http://www.w3.org/2000/svg"%20fill="#8e8e8e%22%20viewBox=%22-10%20-10%2060%2060%22%20preserveAspectRatio=%22xMidYMid%20meet%22%3E%3Cg%3E%3Cpath%20d=%22m25%2031.640000000000004v-19.766666666666673l-10-3.511666666666663v19.766666666666666z%20m9.140000000000008-26.640000000000004q0.8599999999999923%200%200.8599999999999923%200.8600000000000003v25.156666666666666q0%200.625-0.625%200.783333333333335l-9.375%203.1999999999999993-10-3.5133333333333354-8.906666666666668%203.4383333333333326-0.2333333333333334%200.07833333333333314q-0.8616666666666664%200-0.8616666666666664-0.8599999999999994v-25.156666666666663q0-0.625%200.6233333333333331-0.7833333333333332l9.378333333333334-3.198333333333334%2010%203.5133333333333336%208.905000000000001-3.4383333333333344z%22%3E%3C/path%3E%3C/g%3E%3C/svg%3E');
|
background-image: url('data:image/svg+xml;charset=utf8,<svg%20xmlns="http://www.w3.org/2000/svg"%20fill="#8e8e8e%22%20viewBox=%22-10%20-10%2060%2060%22%20preserveAspectRatio=%22xMidYMid%20meet%22%3E%3Cg%3E%3Cpath%20d=%22m25%2031.640000000000004v-19.766666666666673l-10-3.511666666666663v19.766666666666666z%20m9.140000000000008-26.640000000000004q0.8599999999999923%200%200.8599999999999923%200.8600000000000003v25.156666666666666q0%200.625-0.625%200.783333333333335l-9.375%203.1999999999999993-10-3.5133333333333354-8.906666666666668%203.4383333333333326-0.2333333333333334%200.07833333333333314q-0.8616666666666664%200-0.8616666666666664-0.8599999999999994v-25.156666666666663q0-0.625%200.6233333333333331-0.7833333333333332l9.378333333333334-3.198333333333334%2010%203.5133333333333336%208.905000000000001-3.4383333333333344z%22%3E%3C/path%3E%3C/g%3E%3C/svg%3E');
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -30,7 +30,7 @@
|
|||||||
line-height: 26px;
|
line-height: 26px;
|
||||||
}
|
}
|
||||||
|
|
||||||
svg {
|
img {
|
||||||
width: 30px;
|
width: 30px;
|
||||||
padding-right: $margin-2;
|
padding-right: $margin-2;
|
||||||
vertical-align: top;
|
vertical-align: top;
|
||||||
|
|||||||
@@ -18,13 +18,6 @@
|
|||||||
@import 'codemirror';
|
@import 'codemirror';
|
||||||
@import 'react-collapse';
|
@import 'react-collapse';
|
||||||
|
|
||||||
/**
|
|
||||||
* Hacks for webdriverio isVisibleWithinViewport
|
|
||||||
*/
|
|
||||||
#app {
|
|
||||||
height: 100vh;
|
|
||||||
}
|
|
||||||
|
|
||||||
.maputnik-layout {
|
.maputnik-layout {
|
||||||
height: 100vh;
|
height: 100vh;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,12 +2,10 @@ import React from 'react';
|
|||||||
import {useActionState} from './helper';
|
import {useActionState} from './helper';
|
||||||
import FieldArray from '../src/components/FieldArray';
|
import FieldArray from '../src/components/FieldArray';
|
||||||
import {Wrapper} from './ui';
|
import {Wrapper} from './ui';
|
||||||
import {withA11y} from '@storybook/addon-a11y';
|
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
title: 'FieldArray',
|
title: 'FieldArray',
|
||||||
component: FieldArray,
|
component: FieldArray,
|
||||||
decorators: [withA11y],
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
@@ -2,12 +2,10 @@ import React from 'react';
|
|||||||
import {useActionState} from './helper';
|
import {useActionState} from './helper';
|
||||||
import FieldAutocomplete from '../src/components/FieldAutocomplete';
|
import FieldAutocomplete from '../src/components/FieldAutocomplete';
|
||||||
import {Wrapper} from './ui';
|
import {Wrapper} from './ui';
|
||||||
import {withA11y} from '@storybook/addon-a11y';
|
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
title: 'FieldAutocomplete',
|
title: 'FieldAutocomplete',
|
||||||
component: FieldAutocomplete,
|
component: FieldAutocomplete,
|
||||||
decorators: [withA11y],
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
@@ -2,12 +2,10 @@ import React from 'react';
|
|||||||
import {useActionState} from './helper';
|
import {useActionState} from './helper';
|
||||||
import FieldCheckbox from '../src/components/FieldCheckbox';
|
import FieldCheckbox from '../src/components/FieldCheckbox';
|
||||||
import {Wrapper} from './ui';
|
import {Wrapper} from './ui';
|
||||||
import {withA11y} from '@storybook/addon-a11y';
|
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
title: 'FieldCheckbox',
|
title: 'FieldCheckbox',
|
||||||
component: FieldCheckbox,
|
component: FieldCheckbox,
|
||||||
decorators: [withA11y],
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
@@ -2,12 +2,10 @@ import React from 'react';
|
|||||||
import {useActionState} from './helper';
|
import {useActionState} from './helper';
|
||||||
import FieldColor from '../src/components/FieldColor';
|
import FieldColor from '../src/components/FieldColor';
|
||||||
import {Wrapper} from './ui';
|
import {Wrapper} from './ui';
|
||||||
import {withA11y} from '@storybook/addon-a11y';
|
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
title: 'FieldColor',
|
title: 'FieldColor',
|
||||||
component: FieldColor,
|
component: FieldColor,
|
||||||
decorators: [withA11y],
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
@@ -2,12 +2,10 @@ import React from 'react';
|
|||||||
import {useActionState} from './helper';
|
import {useActionState} from './helper';
|
||||||
import FieldDynamicArray from '../src/components/FieldDynamicArray';
|
import FieldDynamicArray from '../src/components/FieldDynamicArray';
|
||||||
import {Wrapper} from './ui';
|
import {Wrapper} from './ui';
|
||||||
import {withA11y} from '@storybook/addon-a11y';
|
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
title: 'FieldDynamicArray',
|
title: 'FieldDynamicArray',
|
||||||
component: FieldDynamicArray,
|
component: FieldDynamicArray,
|
||||||
decorators: [withA11y],
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
@@ -2,12 +2,10 @@ import React from 'react';
|
|||||||
import {useActionState} from './helper';
|
import {useActionState} from './helper';
|
||||||
import FieldEnum from '../src/components/FieldEnum';
|
import FieldEnum from '../src/components/FieldEnum';
|
||||||
import {Wrapper} from './ui';
|
import {Wrapper} from './ui';
|
||||||
import {withA11y} from '@storybook/addon-a11y';
|
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
title: 'FieldEnum',
|
title: 'FieldEnum',
|
||||||
component: FieldEnum,
|
component: FieldEnum,
|
||||||
decorators: [withA11y],
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
@@ -1,15 +1,11 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import FieldFunction from '../src/components/FieldFunction';
|
import FieldFunction from '../src/components/FieldFunction';
|
||||||
import {action} from '@storybook/addon-actions';
|
|
||||||
import {Wrapper} from './ui';
|
|
||||||
import {withA11y} from '@storybook/addon-a11y';
|
|
||||||
import {latest} from '@maplibre/maplibre-gl-style-spec'
|
import {latest} from '@maplibre/maplibre-gl-style-spec'
|
||||||
|
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
title: 'FieldFunction',
|
title: 'FieldFunction',
|
||||||
component: FieldFunction,
|
component: FieldFunction,
|
||||||
decorators: [withA11y],
|
|
||||||
};
|
};
|
||||||
|
|
||||||
export const Basic = () => {
|
export const Basic = () => {
|
||||||
@@ -2,12 +2,10 @@ import React from 'react';
|
|||||||
import {useActionState} from './helper';
|
import {useActionState} from './helper';
|
||||||
import FieldMultiInput from '../src/components/FieldMultiInput';
|
import FieldMultiInput from '../src/components/FieldMultiInput';
|
||||||
import {Wrapper} from './ui';
|
import {Wrapper} from './ui';
|
||||||
import {withA11y} from '@storybook/addon-a11y';
|
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
title: 'FieldMultiInput',
|
title: 'FieldMultiInput',
|
||||||
component: FieldMultiInput,
|
component: FieldMultiInput,
|
||||||
decorators: [withA11y],
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
@@ -2,12 +2,10 @@ import React from 'react';
|
|||||||
import {useActionState} from './helper';
|
import {useActionState} from './helper';
|
||||||
import FieldNumber from '../src/components/FieldNumber';
|
import FieldNumber from '../src/components/FieldNumber';
|
||||||
import {Wrapper} from './ui';
|
import {Wrapper} from './ui';
|
||||||
import {withA11y} from '@storybook/addon-a11y';
|
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
title: 'FieldNumber',
|
title: 'FieldNumber',
|
||||||
component: FieldNumber,
|
component: FieldNumber,
|
||||||
decorators: [withA11y],
|
|
||||||
};
|
};
|
||||||
|
|
||||||
export const Basic = () => {
|
export const Basic = () => {
|
||||||
@@ -2,12 +2,10 @@ import React from 'react';
|
|||||||
import {useActionState} from './helper';
|
import {useActionState} from './helper';
|
||||||
import FieldSelect from '../src/components/FieldSelect';
|
import FieldSelect from '../src/components/FieldSelect';
|
||||||
import {Wrapper} from './ui';
|
import {Wrapper} from './ui';
|
||||||
import {withA11y} from '@storybook/addon-a11y';
|
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
title: 'FieldSelect',
|
title: 'FieldSelect',
|
||||||
component: FieldSelect,
|
component: FieldSelect,
|
||||||
decorators: [withA11y],
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
@@ -2,12 +2,10 @@ import React from 'react';
|
|||||||
import {useActionState} from './helper';
|
import {useActionState} from './helper';
|
||||||
import FieldString from '../src/components/FieldString';
|
import FieldString from '../src/components/FieldString';
|
||||||
import {Wrapper} from './ui';
|
import {Wrapper} from './ui';
|
||||||
import {withA11y} from '@storybook/addon-a11y';
|
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
title: 'FieldString',
|
title: 'FieldString',
|
||||||
component: FieldString,
|
component: FieldString,
|
||||||
decorators: [withA11y],
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user