Compare commits

...

121 Commits

Author SHA1 Message Date
HarelM 73c6f97d57 Update other deps 2024-03-21 16:45:41 +02:00
HarelM 22e33a45c6 Fix package lock 2024-03-21 16:37:20 +02:00
HarelM 37ad297b16 Fore vite downgrade 2024-03-21 16:33:57 +02:00
HarelM 22f063aebb Revert some packages due to firefox test failure 2024-03-21 16:20:13 +02:00
HarelM 055eb13c51 Updated more packages to latest version 2024-03-21 15:39:39 +02:00
HarelM 6bf26ad8e1 Fix docker file build 2024-03-20 12:57:52 +02:00
HarelM ce1e6fc509 legacy-peer-deps, the proper way. 2024-03-20 12:51:03 +02:00
HarelM e353aa5890 Added --legacy-peer-deps, need a better solution 2024-03-20 12:47:19 +02:00
HarelM b9dc10085d Update react to version 18 2024-03-20 12:32:20 +02:00
Harel M 355b663e7e Another attempt to fix the inspect issues (#889)
Fixes #871

- #871 

In case of same sources, calling the render method when the map style
changes, but not right away to let the map load the sources should fix
the issue.
2024-03-14 21:02:49 +02:00
Harel M 3c043fd5e0 Replace mapbox-gl-inspect with maplibre-gl-inspect (#888)
This hopefully fixes #871 

- #871 

I still need to update maplibre-gl-inspect to allow this to be fixed.
2024-03-13 22:48:01 +02:00
Harel M 5f54dd0ccf Temp fix using patch-package for maplibre-inspect issue (#885)
This is a temporary fix for:
 - #871

The fix is using `patch-package` to fix the part of the code that is
causing the issue, I believe.
Initial tests shows that this is fixing the issue, I hope it is not
introducing other issues.
A proper fix and also using the maplibre package will be done once a
repo is migrated as can be expected after the following PR is resolved:
- https://github.com/maplibre/maplibre/issues/359
2024-03-09 23:03:23 +02:00
Harel M 3727f5da48 Improve inspect hover UI (#879)
Fixes #868
- #868

It solves all the block within blocks and all kind of controls that are
not required which creates gaps.
I use a simple table, so the width is dynamic but it is always aligned
for all the properties and the features.

![image](https://github.com/maplibre/maputnik/assets/3269297/75138b00-ec7b-4e8d-b51b-f8ff6abcd5cb)

Vary basic stuff.
There's still the layer popup, which I'm not sure I know what it is and
might need to be fixed as well.
CC: @zstadler
2024-02-21 07:17:28 +02:00
Yuri Astrakhan 079c5f67cc Merge entire history from desktop repo to /desktop
Preserve history of the maputnik/desktop repository,
but move related files to `/desktop` directory,
and adjust the ci script to use it.
2024-02-12 13:52:35 -05:00
Yuri Astrakhan a304d4e060 Renamed ci to ci-desktop 2024-02-12 13:48:37 -05:00
Kevin Schaul 7ac1b03b5a Move into /desktop dir
For eventual merge into maplibre/maputnik repo
2024-02-12 11:06:45 -06:00
glebpinigin b9e32894b3 Readme.md container port changed to 80 (#881)
Activating maputnik with `docker run` requires to bind port 80 from
container and not 8888, because nginx is running with default config and
listening port 80

For `npm run` port 8888 is explicitly set up [in
config](https://github.com/maplibre/maputnik/blob/bc5ecfade6801e16df339a8caba766d28b88de56/vite.config.ts#L8).
2024-02-08 23:35:46 +02:00
Harel M bc5ecfade6 Removes number conversion for dirty input (#878)
Fixes #870

- #870

This regression bug was introduced as part of the refactoring of
typescript.
Here:
- #848

I've added tests to cover the scenario so that it won't happen again,
hopefully.
2024-02-07 10:32:19 +02:00
HarelM c84c7a7b96 Fix redo issue 2024-02-04 16:37:47 +02:00
Harel M cb77c6b4e2 Add nominatim search to maputnik (#873)
This replaces PR:
- #785

Before:

![image](https://github.com/maplibre/maputnik/assets/3269297/95297211-4108-43d8-8a43-42f87e2fbe16)
After:

![image](https://github.com/maplibre/maputnik/assets/3269297/86c3ae58-1bb7-4d2c-8ad9-6b84a21c96f7)

This is based on the geocoder example in maplibre docs:
https://maplibre.org/maplibre-gl-js/docs/examples/geocoder/
2024-02-04 11:37:23 +02:00
Harel M ea42f434eb Update MapLibre to version 4 (#872)
This updates maplibre from a pre-release to an official release.
It also removed the pre.2 from maputnik as maputnik is now published and
there's no reason to keep it in pre-release version name.
This doesn't mean maputnik has an npm package at this point though.
2024-02-04 10:01:22 +02:00
dependabot[bot] 6f82c12861 Bump vite from 5.0.10 to 5.0.12 (#865)
Bumps [vite](https://github.com/vitejs/vite/tree/HEAD/packages/vite)
from 5.0.10 to 5.0.12.
<details>
<summary>Changelog</summary>
<p><em>Sourced from <a
href="https://github.com/vitejs/vite/blob/v5.0.12/packages/vite/CHANGELOG.md">vite's
changelog</a>.</em></p>
<blockquote>
<h2><!-- raw HTML omitted -->5.0.12 (2024-01-19)<!-- raw HTML omitted
--></h2>
<ul>
<li>fix: await <code>configResolved</code> hooks of worker plugins (<a
href="https://github.com/vitejs/vite/tree/HEAD/packages/vite/issues/15597">#15597</a>)
(<a
href="https://github.com/vitejs/vite/tree/HEAD/packages/vite/issues/15605">#15605</a>)
(<a href="https://github.com/vitejs/vite/commit/ef89f80">ef89f80</a>),
closes <a
href="https://redirect.github.com/vitejs/vite/issues/15597">#15597</a>
<a
href="https://redirect.github.com/vitejs/vite/issues/15605">#15605</a></li>
<li>fix: fs deny for case insensitive systems (<a
href="https://github.com/vitejs/vite/tree/HEAD/packages/vite/issues/15653">#15653</a>)
(<a href="https://github.com/vitejs/vite/commit/91641c4">91641c4</a>),
closes <a
href="https://redirect.github.com/vitejs/vite/issues/15653">#15653</a></li>
</ul>
<h2><!-- raw HTML omitted -->5.0.11 (2024-01-05)<!-- raw HTML omitted
--></h2>
<ul>
<li>fix: don't pretransform classic script links (<a
href="https://github.com/vitejs/vite/tree/HEAD/packages/vite/issues/15361">#15361</a>)
(<a href="https://github.com/vitejs/vite/commit/19e3c9a">19e3c9a</a>),
closes <a
href="https://redirect.github.com/vitejs/vite/issues/15361">#15361</a></li>
<li>fix: inject <code>__vite__mapDeps</code> code before sourcemap file
comment (<a
href="https://github.com/vitejs/vite/tree/HEAD/packages/vite/issues/15483">#15483</a>)
(<a href="https://github.com/vitejs/vite/commit/d2aa096">d2aa096</a>),
closes <a
href="https://redirect.github.com/vitejs/vite/issues/15483">#15483</a></li>
<li>fix(assets): avoid splitting <code>,</code> inside base64 value of
<code>srcset</code> attribute (<a
href="https://github.com/vitejs/vite/tree/HEAD/packages/vite/issues/15422">#15422</a>)
(<a href="https://github.com/vitejs/vite/commit/8de7bd2">8de7bd2</a>),
closes <a
href="https://redirect.github.com/vitejs/vite/issues/15422">#15422</a></li>
<li>fix(html): handle offset magic-string slice error (<a
href="https://github.com/vitejs/vite/tree/HEAD/packages/vite/issues/15435">#15435</a>)
(<a href="https://github.com/vitejs/vite/commit/5ea9edb">5ea9edb</a>),
closes <a
href="https://redirect.github.com/vitejs/vite/issues/15435">#15435</a></li>
<li>chore(deps): update dependency strip-literal to v2 (<a
href="https://github.com/vitejs/vite/tree/HEAD/packages/vite/issues/15475">#15475</a>)
(<a href="https://github.com/vitejs/vite/commit/49d21fe">49d21fe</a>),
closes <a
href="https://redirect.github.com/vitejs/vite/issues/15475">#15475</a></li>
<li>chore(deps): update tj-actions/changed-files action to v41 (<a
href="https://github.com/vitejs/vite/tree/HEAD/packages/vite/issues/15476">#15476</a>)
(<a href="https://github.com/vitejs/vite/commit/2a540ee">2a540ee</a>),
closes <a
href="https://redirect.github.com/vitejs/vite/issues/15476">#15476</a></li>
</ul>
</blockquote>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="https://github.com/vitejs/vite/commit/ee81e196769c102a6b1bf30f8444ccde236e71d5"><code>ee81e19</code></a>
release: v5.0.12</li>
<li><a
href="https://github.com/vitejs/vite/commit/91641c4da0a011d4c5352e88fc68389d4e1289a5"><code>91641c4</code></a>
fix: fs deny for case insensitive systems (<a
href="https://github.com/vitejs/vite/tree/HEAD/packages/vite/issues/15653">#15653</a>)</li>
<li><a
href="https://github.com/vitejs/vite/commit/ef89f8092f0eb1d8fd7d21256e6af8c4e64fe9b2"><code>ef89f80</code></a>
fix: await <code>configResolved</code> hooks of worker plugins (<a
href="https://github.com/vitejs/vite/tree/HEAD/packages/vite/issues/15597">#15597</a>)
(<a
href="https://github.com/vitejs/vite/tree/HEAD/packages/vite/issues/15605">#15605</a>)</li>
<li><a
href="https://github.com/vitejs/vite/commit/b44c49302ffbf0c82f984f6219ed6376d1e4552a"><code>b44c493</code></a>
release: v5.0.11</li>
<li><a
href="https://github.com/vitejs/vite/commit/d2aa0969ee316000d3b957d7e879f001e85e369e"><code>d2aa096</code></a>
fix: inject <code>__vite__mapDeps</code> code before sourcemap file
comment (<a
href="https://github.com/vitejs/vite/tree/HEAD/packages/vite/issues/15483">#15483</a>)</li>
<li><a
href="https://github.com/vitejs/vite/commit/2a540eee82f9a31deff8215bdbdccfa46d494a06"><code>2a540ee</code></a>
chore(deps): update tj-actions/changed-files action to v41 (<a
href="https://github.com/vitejs/vite/tree/HEAD/packages/vite/issues/15476">#15476</a>)</li>
<li><a
href="https://github.com/vitejs/vite/commit/5ea9edbc9ceb991e85f893fe62d68ed028677451"><code>5ea9edb</code></a>
fix(html): handle offset magic-string slice error (<a
href="https://github.com/vitejs/vite/tree/HEAD/packages/vite/issues/15435">#15435</a>)</li>
<li><a
href="https://github.com/vitejs/vite/commit/49d21fe1feaac30dee0196bd484480a8000a4363"><code>49d21fe</code></a>
chore(deps): update dependency strip-literal to v2 (<a
href="https://github.com/vitejs/vite/tree/HEAD/packages/vite/issues/15475">#15475</a>)</li>
<li><a
href="https://github.com/vitejs/vite/commit/8de7bd2b68db27b83d9484cc8d4e26436615168e"><code>8de7bd2</code></a>
fix(assets): avoid splitting <code>,</code> inside base64 value of
<code>srcset</code> attribute (#...</li>
<li><a
href="https://github.com/vitejs/vite/commit/19e3c9a8a16847486fbad8a8cd48fc771b1538bb"><code>19e3c9a</code></a>
fix: don't pretransform classic script links (<a
href="https://github.com/vitejs/vite/tree/HEAD/packages/vite/issues/15361">#15361</a>)</li>
<li>See full diff in <a
href="https://github.com/vitejs/vite/commits/v5.0.12/packages/vite">compare
view</a></li>
</ul>
</details>
<br />


[![Dependabot compatibility
score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=vite&package-manager=npm_and_yarn&previous-version=5.0.10&new-version=5.0.12)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores)

Dependabot will resolve any conflicts with this PR as long as you don't
alter it yourself. You can also trigger a rebase manually by commenting
`@dependabot rebase`.

[//]: # (dependabot-automerge-start)
[//]: # (dependabot-automerge-end)

---

<details>
<summary>Dependabot commands and options</summary>
<br />

You can trigger Dependabot actions by commenting on this PR:
- `@dependabot rebase` will rebase this PR
- `@dependabot recreate` will recreate this PR, overwriting any edits
that have been made to it
- `@dependabot merge` will merge this PR after your CI passes on it
- `@dependabot squash and merge` will squash and merge this PR after
your CI passes on it
- `@dependabot cancel merge` will cancel a previously requested merge
and block automerging
- `@dependabot reopen` will reopen this PR if it is closed
- `@dependabot close` will close this PR and stop Dependabot recreating
it. You can achieve the same result by closing it manually
- `@dependabot show <dependency name> ignore conditions` will show all
of the ignore conditions of the specified dependency
- `@dependabot ignore this major version` will close this PR and stop
Dependabot creating any more for this major version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this minor version` will close this PR and stop
Dependabot creating any more for this minor version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this dependency` will close this PR and stop
Dependabot creating any more for this dependency (unless you reopen the
PR or upgrade to it yourself)
You can disable automated security fix PRs for this repo from the
[Security Alerts
page](https://github.com/maplibre/maputnik/network/alerts).

</details>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-02-04 09:18:19 +02:00
HarelM 3b95b25777 Remove unneed console.log 2024-01-13 14:38:56 +02:00
Harel M 1da65f2116 Fix missing logo (#863)
Fixes #861
This should hopefully address the last item in the below issue of the
missing logo.
I tested it locally and it seems to be working as expected, I hope this
will be the case once deployed.
- #861
2024-01-12 18:46:10 +02:00
Harel M a62db148cd Remove Storybook, cleanups (#860)
1. Changed references to point to this new repo
2. Fixed docker image publishing to point to ghcr.io.
3. Remove survey link - the survey is closed and there's no point in
keeping it.
4. Remove storybook - Basically a storybook is the ability to look at
components and see how they look and interact with them.
It's a powerful tool for developing component library with "live"
documentation.
But it's an overkill for this project and I would like to reduce
maintenance costs.
Currently all the "stories" are in javascript and not in typescript and
it feels like a waste of time to try and maintain it, along with
updating the storybook library itself and everything around it.
2024-01-12 10:59:57 +02:00
HarelM 6ed10a862f Update pages deploy on every commit to main 2024-01-11 22:57:07 +02:00
HarelM 123e84f19b login to ghcr.io 2024-01-11 22:29:46 +02:00
HarelM d9b1b6f3ae Fix docker deploy, remove unneeded files 2024-01-11 22:24:47 +02:00
Harel M e0cef99c07 Update LICENSE 2024-01-11 22:11:18 +02:00
Harel M eb48bed32a Update MapLibre dependencies, add terrain editing (#859)
This PR aims at updating MapLibre dependencies.

The main goal of this update is to allow adding terrain specification to
the editor.
This requires version 4 of maplibre so currently it will use the
pre-release.
2024-01-11 22:05:47 +02:00
Kevin Schaul 7265bf0aa4 Refactor into a go module, update dependencies (#18)
* Refactor into a go module

`go get` is no longer supported outside of a go module. This commit
converts the project into a go module, moving dependencies into a go.mod
file.

* Use go.rice instead of go-bindata-assetfs

go.rice appears to be maintained and more popular

* Edit dependencies

* Pin to specific versions

* Updating workflows

* Fix syntax, I hope

* Update go

* Increment version number

* Version number

* version number
2023-07-18 16:44:57 +02:00
pathmapper c9504fcaed Merge pull request #16 from pathmapper/static
Add option to serve directory under /static/ (supersedes #2)
2020-07-24 18:31:30 +02:00
pathmapper b7ef0943f4 Fix formatting 2020-07-23 20:25:24 +02:00
pathmapper 4661677387 Add docs 2020-07-23 20:19:33 +02:00
pathmapper 77ed14a340 Update desktop version number 2020-07-23 19:52:38 +02:00
pathmapper e24d390f7c Fix build error 2020-07-23 19:33:23 +02:00
pathmapper 698fdfc958 Fix syntax error 2020-07-23 19:28:34 +02:00
pathmapper 77b3655c3c Add option to serve directory under /static/ (from #2) 2020-07-23 19:22:43 +02:00
pathmapper c264cd1771 Merge pull request #15 from pathmapper/update_desktop_version
Update desktop version number
2020-06-03 15:08:54 +02:00
pathmapper 1495d11462 Update desktop version number
The makefile was updated in https://github.com/maputnik/desktop/pull/14
2020-06-03 15:05:47 +02:00
pathmapper fda11e52e7 Merge pull request #14 from pathmapper/makefile_editor
Update for use in the editor ci workflow
2020-05-31 21:43:57 +02:00
pathmapper d9e3aa6ac4 Update for use in the editor ci workflow 2020-05-31 21:41:02 +02:00
pathmapper aeca95a27f Merge pull request #13 from pathmapper/rename_ci
Rename ci
2020-05-31 21:30:22 +02:00
pathmapper 7dfcdac202 Rename ci 2020-05-31 21:23:52 +02:00
pathmapper 4f156ee3fd Merge pull request #12 from pathmapper/gh-actions
Replace Travis with Github Actions
2020-05-31 21:14:39 +02:00
pathmapper 6d00214f55 Update README.md 2020-05-31 21:09:02 +02:00
pathmapper 1e7b6e809c Remove Travis 2020-05-31 21:05:00 +02:00
pathmapper cdcc61e234 Create go.yml 2020-05-31 21:04:11 +02:00
pathmapper 9c63172d36 Merge pull request #11 from pathmapper/travis
Travis: Update go version and use OSM Liberty
2020-05-28 23:00:06 +02:00
pathmapper 6f21fd8dff Use OSM Liberty style 2020-05-28 22:56:08 +02:00
pathmapper 0e788c5841 Update go version 2020-05-28 22:55:39 +02:00
pathmapper 7229df704a Merge pull request #10 from pathmapper/1.7.0
Update Editor to v1.7.0
2020-05-28 22:08:56 +02:00
pathmapper 686fd27b35 Update Editor to v1.7.0 2020-05-28 22:05:48 +02:00
pathmapper 293342e4fb Update Desktop version number 2020-05-28 22:04:40 +02:00
pathmapper 03d9b946e7 Merge pull request #9 from miya0001/patch
Fixed the error `cannot use cli.StringFlag literal ...`
2020-05-28 21:54:55 +02:00
Takayuki Miyauchi 4a3825fa89 fixed the error cannot use cli.StringFlag literal ... 2020-02-21 07:24:00 +09:00
pathmapper 5371b0f9fb Merge pull request #8 from pathmapper/editor_1.6.1
Update editor to v1.6.1
2019-10-13 17:50:22 +02:00
pathmapper 538cea7f45 Update editor to v1.6.1 2019-10-13 14:28:42 +02:00
pathmapper fdfc470ccc Merge pull request #6 from pathmapper/update_versions
Update version numbers
2019-10-10 14:30:14 +02:00
pathmapper 3153eea1da Update version numbers 2019-10-08 22:29:45 +02:00
pathmapper 8471d0af3d Merge pull request #5 from JesseCrocker/choose-listen-port
Add option to choose a listening port
2019-10-08 22:20:05 +02:00
Jesse Crocker 1ce2d59b9b Add --port option to readme 2019-09-04 09:21:56 -06:00
Jesse Crocker d951256b1c Add option to choose a listening port 2019-04-15 12:29:15 -06:00
pathmapper ec753869d5 Merge pull request #3 from maputnik/update-release-to-v1.5.0
Update release to v1.5.0
2019-02-09 14:07:52 +01:00
Lukas Welte 2e58be1c90 Switch around editor and desktop in version field so that it prints nicer 2019-02-09 13:47:58 +01:00
Lukas Welte 562a4f7322 Remove appveyor status from README 2019-02-09 13:45:45 +01:00
Lukas Welte e52a63e1dd Remove the deprecation warning from the readme 2019-02-09 12:47:47 +01:00
Lukas Welte 4533fd06ed Have Desktop version (changes in Desktop code) and editor version in version field 2019-02-09 12:39:32 +01:00
pathmapper b4fc62632c Merge pull request #4 from pathmapper/pathmapper-update-readme
Update README.md
2019-02-09 10:28:15 +01:00
pathmapper e3a9a8a38c Update README.md 2019-02-09 10:09:05 +01:00
pathmapper 7c4e982fb3 Merge pull request #1 from fawick/master
Fix README.md
2019-02-09 09:51:13 +01:00
Lukas Welte 85dd22b09a Use recent go version 2019-02-07 17:58:11 +01:00
Lukas Welte 18e15eeb5c remove complete bin folder in make clean 2019-02-07 17:35:23 +01:00
Lukas Welte 3a45b8dd41 Move dependencies into Makefile 2019-02-07 17:33:38 +01:00
Lukas Welte 5b8412765b Linux should be enough to build go for linux, windows and mac 2019-02-07 17:29:49 +01:00
Lukas Welte 69519df82f Use folders for different maputnik executables 2019-02-07 17:25:27 +01:00
Lukas Welte 8052701021 Use go cross compile to build once with travis for all platforms 2019-02-07 17:18:48 +01:00
Lukas Welte 35c0150522 Download release instead of building editor from source 2019-02-07 16:05:02 +01:00
Lukas Welte c55278e7da Remove editor submodule 2019-02-07 15:56:29 +01:00
Lukas Martinelli d3ecef3de6 Update README.md 2018-04-15 16:14:22 +05:30
Fabian Wickborn 3ef0a90de4 Fix README.md 2017-10-25 20:33:45 +02:00
Lukas Martinelli 87290889fd Upgrade to v1.0.2 2017-01-25 13:57:27 +01:00
Lukas Martinelli 1997e31b6b Build latest 2017-01-18 13:06:43 +01:00
Lukas Martinelli 5b21a2fa4f Build latest 2017-01-18 13:03:50 +01:00
Lukas Martinelli d314add6a9 Release v1.0.1 of CLI 2017-01-16 10:18:27 +01:00
Lukas Martinelli 50d61cdb0e Move to v1.0.0 2017-01-13 21:56:06 +01:00
Lukas Martinelli 4ffea21c5f Upgrade to latest Maputnik 2017-01-10 14:57:13 +01:00
Lukas Martinelli d29a79e79f Update editor 2017-01-08 17:15:57 +01:00
Lukas Martinelli 004177a3c8 Kill watch ocmmand after 5s 2017-01-08 13:40:38 +01:00
Lukas Martinelli b9e70a943f Update x/sys 2017-01-08 13:29:20 +01:00
Lukas Martinelli de853eb2d7 Use Go 1.7 explicitely in Travis 2017-01-08 13:29:20 +01:00
Lukas Martinelli f242c2c015 Update README.md 2017-01-08 10:45:01 +01:00
Lukas Martinelli d895cf079c Execute maputnik in travis 2017-01-08 10:39:12 +01:00
Lukas Martinelli 147cbc1580 Include subdirectories in embedded assets 2017-01-01 21:12:24 +01:00
Lukas Martinelli 3e46c0d3ba Set version, badge and download instruction 2017-01-01 16:49:18 +01:00
Lukas Martinelli d233e0a14d Create public dir 2017-01-01 16:33:29 +01:00
Lukas Martinelli a73b2fd7e1 Use nvm for travis 2017-01-01 16:24:36 +01:00
Lukas Martinelli 69f63f2844 Use latest node in desktop build 2017-01-01 16:22:01 +01:00
Lukas Martinelli 10136c07db Publish artifacts 2017-01-01 16:16:18 +01:00
Lukas Martinelli 5e3156ab21 Fetch submodule in appveyor 2017-01-01 16:00:59 +01:00
Lukas Martinelli 6be8959951 Update to latest master 2017-01-01 15:52:10 +01:00
Lukas Martinelli d2cd84de2b Switch filewatch implementation to fsnotify 2017-01-01 15:50:58 +01:00
Lukas Martinelli 60bea1777a Ugrade to v0.3.1 2016-12-31 16:12:07 +01:00
Lukas Martinelli ce9216b2d5 Write and read file directly 2016-12-03 23:04:32 +01:00
Lukas Martinelli 35ed202cd0 Support CORS 2016-12-03 23:04:12 +01:00
Lukas Martinelli 0d77518a02 Support saving file 2016-12-03 16:51:47 +01:00
lukasmartinelli 11375008fa Extend README with endpoint desription 2016-11-05 17:40:19 +01:00
lukasmartinelli 009f4e105d Specify path to mingw make 2016-11-03 13:29:14 +01:00
lukasmartinelli b1af4917e5 Add missing bindata deps 2016-11-03 13:26:14 +01:00
lukasmartinelli 5b712d74ae Use MinGW make in AppVeyor 2016-11-03 13:24:02 +01:00
lukasmartinelli ca2df37c79 Fix go get in AppVeyor 2016-11-03 13:19:58 +01:00
lukasmartinelli 7d1890156d Update Travis go get 2016-11-03 13:18:08 +01:00
lukasmartinelli ecab640a9a Add AppVeyor integration 2016-11-02 17:10:40 +01:00
lukasmartinelli f8cb0619f3 Use HTTPS endpoint for submodule 2016-11-02 17:08:48 +01:00
lukasmartinelli d874b2503b Add Travis file 2016-11-02 17:01:46 +01:00
lukasmartinelli 3d09f2a0f3 Add server usage example 2016-09-27 17:31:14 +02:00
lukasmartinelli 1f1580276d Integrate file access into API 2016-09-27 14:28:40 +02:00
lukasmartinelli 0421a7f099 Add websocket filewatcher 2016-09-27 11:45:27 +02:00
lukasmartinelli 8b722fc967 Add Makefile 2016-09-27 10:59:56 +02:00
lukasmartinelli 66e3ce8743 Add server and bindata packaging 2016-09-27 10:27:47 +02:00
Lukas Martinelli 45eb3a01e6 Initial commit 2016-09-27 10:08:08 +02:00
109 changed files with 3208 additions and 12900 deletions
-4
View File
@@ -1,4 +0,0 @@
{
"packages": [],
"sandboxes": ["/"]
}
+52
View File
@@ -0,0 +1,52 @@
name: ci-desktop
on: [ push ]
jobs:
build:
runs-on: ubuntu-latest
defaults:
run:
working-directory: ./desktop
steps:
- uses: actions/checkout@v3
- name: Set up Go
uses: actions/setup-go@v3
with:
go-version: 1.18
- name: Make
run: make
- name: Test --help
run: ./bin/linux/maputnik --help
- name: Test --version
run: ./bin/linux/maputnik --version
- name: Get style
run: wget https://maputnik.github.io/osm-liberty/style.json
- name: Test --watch
run: ./bin/linux/maputnik --watch --file style.json & sleep 5; kill $!
- name: Artifacts/linux
uses: actions/upload-artifact@v3
with:
name: maputnik-linux
path: bin/linux/
- name: Artifacts/darwin
uses: actions/upload-artifact@v3
with:
name: maputnik-darwin
path: bin/darwin/
- name: Artifacts/windows
uses: actions/upload-artifact@v3
with:
name: maputnik-windows
path: bin/windows/
+3 -9
View File
@@ -15,7 +15,7 @@ jobs:
steps:
- uses: actions/checkout@v4
- run: docker build -t docker.pkg.github.com/maputnik/editor/editor:main .
- run: docker build -t test-docker-image-build .
# build the editor
build-node:
@@ -53,17 +53,11 @@ jobs:
node-version-file: '.nvmrc'
- run: npm ci
- run: npm run build
- run: npm run build-storybook
- name: artifacts/editor
- name: artifacts/maputnik
uses: actions/upload-artifact@v1
with:
name: editor
name: maputnik
path: dist
- name: artifacts/storybook
uses: actions/upload-artifact@v1
with:
name: storybook
path: build/storybook
# Build and upload desktop CLI artifacts
- name: Set up Go
+33 -8
View File
@@ -3,24 +3,49 @@ name: deploy
on:
push:
branches: [ main ]
tags:
- 'v*'
jobs:
deploy-pages:
name: deploy/pages
runs-on: ubuntu-latest
if: ${{ github.event_name == 'push' }}
steps:
- uses: actions/checkout@v4
- name: Use Node.js from nvmrc
uses: actions/setup-node@v4
with:
node-version-file: '.nvmrc'
- name: Install
run: npm ci
- name: Build
run: npm run build
- name: Upload to GitHub Pages
uses: peaceiris/actions-gh-pages@v3
with:
github_token: ${{ secrets.GITHUB_TOKEN }}
publish_dir: dist
# publish docker to GitHub registry
deploy-docker:
name: deploy/docker
runs-on: ${{ matrix.os }}
runs-on: ubuntu-latest
if: ${{ github.event_name == 'push' }}
strategy:
fail-fast: false
matrix:
os: [ubuntu-latest]
steps:
- name: Login to GitHub Container Registry
uses: docker/login-action@v3
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- uses: actions/checkout@v4
- 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:main .
- run: docker push docker.pkg.github.com/maputnik/editor/editor:main
- run: docker build -t ghcr.io/maplibre/maputnik:main .
- run: docker push ghcr.io/maplibre/maputnik:main
+1
View File
@@ -0,0 +1 @@
legacy-peer-deps = true
-9
View File
@@ -1,9 +0,0 @@
const config = {
stories: ['../stories/**/*.stories.jsx'],
addons: ['@storybook/addon-actions', '@storybook/addon-links', '@storybook/addon-a11y/register', '@storybook/addon-storysource'],
framework: {
name: '@storybook/react-vite',
options: {}
}
};
export default config;
-7
View File
@@ -1,7 +0,0 @@
import { addons } from '@storybook/addons';
import { themes } from '@storybook/theming';
import theme from './maputnik.theme';
addons.setConfig({
theme: theme,
});
-8
View File
@@ -1,8 +0,0 @@
import { create } from '@storybook/theming/create';
export default create({
base: 'light',
brandTitle: 'Maputnik',
brandUrl: 'https://github.com/maputnik/editor',
});
+3 -3
View File
@@ -2,12 +2,12 @@ FROM node:18 as builder
WORKDIR /maputnik
# Only copy package.json to prevent npm install from running on every build
COPY package.json package-lock.json ./
RUN npm install
COPY package.json package-lock.json .npmrc ./
RUN npm ci
# Build maputnik
COPY . .
RUN npm run build
RUN npx vite build
#---------------------------------------------------------------------------
# Create a clean nginx-alpine slim image with just the build results
+1
View File
@@ -1,5 +1,6 @@
The MIT License (MIT)
Copyright (c) 2015 Lukas Martinelli
Copyright (c) 2024 MapLibre contributors
Permission is hereby granted, free of charge, to any person obtaining a copy
+6 -11
View File
@@ -1,10 +1,10 @@
<img width="200" alt="Maputnik logo" src="https://cdn.jsdelivr.net/gh/maputnik/design/logos/logo-color.png" />
# Maputnik
[![GitHub CI status](https://github.com/maputnik/editor/workflows/ci/badge.svg)][github-action-ci]
[![GitHub CI status](https://github.com/maplibre/maputnik/workflows/ci/badge.svg)][github-action-ci]
[![License](https://img.shields.io/badge/license-MIT-blue.svg)][license]
[github-action-ci]: https://github.com/maputnik/editor/actions?query=workflow%3Aci
[github-action-ci]: https://github.com/maplibre/maputnik/actions?query=workflow%3Aci
[license]: https://tldrlegal.com/license/mit-license
A free and open visual editor for the [MapLibre GL styles](https://maplibre.org/maplibre-style-spec/)
@@ -14,18 +14,18 @@ targeted at developers and map designers.
## Usage
- :link: Design your maps online at **<https://www.maplibre.org/maputnik/>** (all in local storage)
- :link: Use the [Maputnik CLI](https://github.com/maputnik/editor/wiki/Maputnik-CLI) for local style development
- :link: Use the [Maputnik CLI](https://github.com/maplibre/maputnik/wiki/Maputnik-CLI) for local style development
- In a Docker, run this command and browse to http://localhost:8888, Ctrl+C to stop the server.
```bash
docker run -it --rm -p 8888:8888 maputnik/editor
docker run -it --rm -p 8888:80 ghcr.io/maplibre/maputnik:main
```
## Documentation
The documentation can be found in the [Wiki](https://github.com/maputnik/editor/wiki). You are welcome to collaborate!
The documentation can be found in the [Wiki](https://github.com/maplibre/maputnik/wiki). You are welcome to collaborate!
- :link: **Study the [Maputnik Wiki](https://github.com/maputnik/editor/wiki)**
- :link: **Study the [Maputnik Wiki](https://github.com/maplibre/maputnik/wiki)**
- :video_camera: Design a map from Scratch https://youtu.be/XoDh0gEnBQo
[![Design Map from Scratch](https://j.gifs.com/g5XMgl.gif)](https://youtu.be/XoDh0gEnBQo)
@@ -93,11 +93,6 @@ You can also see the tests as they run or select which suites to run by executin
npm run cy:open
```
## Related Projects
- [maputnik-dev-server](https://github.com/nycplanning/labs-maputnik-dev-server) - An express.js server that allows for quickly loading the style from any mapboxGL map into mapuntnik.
## Sponsors
Thanks to the supporters of the **[Kickstarter campaign](https://www.kickstarter.com/projects/174808720/maputnik-visual-map-editor-for-mapbox-gl)**. This project would not be possible without these commercial and individual sponsors.
+35
View File
@@ -87,4 +87,39 @@ describe("history", () => {
],
});
});
it("should not redo after undo and value change", () => {
when.setStyle("geojson");
when.modal.open();
when.modal.fillLayers({
id: "step 1",
type: "background",
});
when.modal.open();
when.modal.fillLayers({
id: "step 2",
type: "background",
});
when.typeKeys(undoKeyCombo);
when.typeKeys(undoKeyCombo);
then(get.styleFromLocalStorage()).shouldDeepNestedInclude({ layers: [] });
when.modal.open();
when.modal.fillLayers({
id: "step 3",
type: "background",
});
when.typeKeys(redoKeyCombo);
then(get.styleFromLocalStorage()).shouldDeepNestedInclude({
layers: [
{
id: "step 3",
type: "background",
},
],
});
});
});
+19
View File
@@ -280,6 +280,25 @@ describe("layers", () => {
});
});
});
describe("opacity", () => {
let bgId: string;
beforeEach(() => {
bgId = createBackground();
when.click("layer-list-item:background:" + bgId);
when.type("spec-field-input:background-opacity", "0.");
});
it("should keep '.' in the input field", () => {
then(get.elementByTestId("spec-field-input:background-opacity")).shouldHaveValue("0.");
});
it("should revert to a valid value when focus out", () => {
when.click("layer-list-item:background:" + bgId);
then(get.elementByTestId("spec-field-input:background-opacity")).shouldHaveValue('0');
});
});
});
describe("filter", () => {
+6
View File
@@ -23,4 +23,10 @@ describe("map", () => {
);
});
});
describe("search", () => {
it('should exist', () => {
then(get.searchControl()).shouldBeVisible();
});
});
});
+4 -1
View File
@@ -1,3 +1,5 @@
/// <reference types="cypress-plugin-tab" />
import { CypressHelper } from "@shellygo/cypress-test-utils";
import { Assertable, then } from "@shellygo/cypress-test-utils/assertable";
import MaputnikCypressHelper from "./maputnik-cypress-helper";
@@ -14,7 +16,7 @@ const styleFromWindow = (win: Window) => {
export class MaputnikAssertable<T> extends Assertable<T> {
shouldEqualToStoredStyle = () =>
then(
new CypressHelper().get.window().then((win) => {
new CypressHelper().get.window().then((win: Window) => {
const style = styleFromWindow(win);
then(this.chainable).shouldDeepNestedInclude(style);
})
@@ -177,5 +179,6 @@ export class MaputnikDriver {
skipTargetLayerEditor: () =>
this.helper.get.elementByTestId("skip-target-layer-editor"),
canvas: () => this.helper.get.element("canvas"),
searchControl: () => this.helper.get.element('.maplibregl-ctrl-geocoder')
};
}
+4 -4
View File
@@ -79,12 +79,12 @@ describe("modals", () => {
when.click("nav:settings");
});
describe("when click name", () => {
describe("when click name filed spec information", () => {
beforeEach(() => {
when.click("field-doc-button-Name");
});
it("name", () => {
it("should show the spec information", () => {
then(get.elementsText("spec-field-doc")).shouldInclude(
"name for the style"
);
@@ -142,7 +142,7 @@ describe("modals", () => {
);
when.click("modal:settings.name");
then(
get.styleFromLocalStorage().pipe((style) => style.metadata)
get.styleFromLocalStorage().then((style) => style.metadata)
).shouldInclude({
"maputnik:openmaptiles_access_token": apiKey,
});
@@ -156,7 +156,7 @@ describe("modals", () => {
);
when.click("modal:settings.name");
then(
get.styleFromLocalStorage().pipe((style) => style.metadata)
get.styleFromLocalStorage().then((style) => style.metadata)
).shouldInclude({ "maputnik:thunderforest_access_token": apiKey });
});
+31
View File
@@ -0,0 +1,31 @@
# Compiled Object files, Static and Dynamic libs (Shared Objects)
*.o
*.a
*.so
# Folders
_obj
_test
editor
# Architecture specific extensions/prefixes
*.[568vq]
[568vq].out
*.cgo1.go
*.cgo2.c
_cgo_defun.c
_cgo_gotypes.go
_cgo_export.*
_testmain.go
*.exe
*.test
*.prof
# Binary version of pubilic/editor
rice-box.go
# Built binary
maputnik
+21
View File
@@ -0,0 +1,21 @@
MIT License
Copyright (c) 2016 Maputnik
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
+31
View File
@@ -0,0 +1,31 @@
SOURCEDIR=.
SOURCES := $(shell find $(SOURCEDIR) -name '*.go')
BINARY=maputnik
EDITOR_VERSION ?= v1.7.0
GOPATH := $(if $(GOPATH),$(GOPATH),$(HOME)/go)
GOBIN := $(if $(GOBIN),$(GOBIN),$(HOME)/go/bin)
all: $(BINARY)
$(BINARY): $(GOBIN)/gox $(SOURCES) rice-box.go
$(GOBIN)/gox -osarch "windows/amd64 linux/amd64 darwin/amd64" -output "bin/{{.OS}}/${BINARY}"
editor/create_folder:
mkdir -p editor
editor/pull_release: editor/create_folder
# if the directory /home/runner/work/editor/editor/build/build exists, we assume that we are are running the makefile within the editor ci workflow
test -d /home/runner/work/editor/editor/build/build && echo "exists" && cd editor && cp -R /home/runner/work/editor/editor/build/build public/ || (echo "does not exist" && cd editor && rm -rf public && curl -L https://github.com/maputnik/editor/releases/download/$(EDITOR_VERSION)/public.zip --output public.zip && unzip public.zip && rm public.zip)
$(GOBIN)/gox:
go install github.com/mitchellh/gox@v1.0.1
$(GOBIN)/rice:
go install github.com/GeertJohan/go.rice/rice@v1.0.3
rice-box.go: $(GOBIN)/rice editor/pull_release
$(GOBIN)/rice embed-go
.PHONY: clean
clean:
rm -rf editor/public && rm -f rice-box.go && rm -rf bin
+78
View File
@@ -0,0 +1,78 @@
# Maputnik Desktop [![GitHub CI status](https://github.com/maputnik/desktop/workflows/ci/badge.svg)](https://github.com/maputnik/desktop/actions?query=workflow%3Aci)
---
A Golang based cross platform executable for integrating Maputnik locally.
This binary packages up the JavaScript and CSS bundle produced by [maputnik/editor](https://github.com/maputnik/desktop)
and embeds it in the program for easy distribution. It also allows
exposing a local style file and work on it both in Maputnik and with your favorite
editor.
Report issues on [maputnik/editor](https://github.com/maputnik/editor).
## Install
You can download a single binary for Linux, OSX or Windows from [the latest releases of **maputnik/editor**](https://github.com/maputnik/editor/releases/latest).
### Usage
Simply start up a web server and access the Maputnik editor GUI at `localhost:8000`.
```bash
maputnik
```
Expose a local style file to Maputnik allowing the web based editor
to save to the local filesystem.
```bash
maputnik --file basic-v9.json
```
Watch the local style for changes and inform the editor via web socket.
This makes it possible to edit the style with a local text editor and still
use Maputnik.
```bash
maputnik --watch --file basic-v9.json
```
Choose a local port to listen on, instead of using the default port 8000.
```bash
maputnik --port 8001
```
Specify a path to a directory which, if it exists, will be served under http://localhost:8000/static/ .
Could be used to serve sprites and glyphs.
```bash
maputnik --static ./localFolder
```
### API
`maputnik` exposes the configured styles via a HTTP API.
| Method | Description
|---------------------------------|---------------------------------------
| `GET /styles` | List the ID of all configured style files
| `GET /styles/{filename}` | Get contents of a single style file
| `PUT /styles/{filename}` | Update contents of a style file
| `WEBSOCKET /ws` | Listen to change events for the configured style files
### Build
Clone the repository. Make sure you clone it into the correct directory `$GOPATH/src/github.com/maputnik`.
```
git clone git@github.com:maputnik/desktop.git
```
Run `make` to install the 3rd party dependencies and build the `maputnik` binary embedding the editor.
```
make
```
You should now find the `maputnik` binary in your `bin` directory.
+81
View File
@@ -0,0 +1,81 @@
package main
import (
"bytes"
"encoding/json"
"fmt"
"io/ioutil"
"log"
"net/http"
"github.com/gorilla/mux"
)
func StyleFileAccessor(filename string) styleFileAccessor {
return styleFileAccessor{filename, styleId(filename)}
}
func styleId(filename string) string {
raw, err := ioutil.ReadFile(filename)
if err != nil {
log.Panicln(err)
}
var spec styleSpec
err = json.Unmarshal(raw, &spec)
if err != nil {
log.Panicln(err)
}
if spec.Id == "" {
fmt.Println("No id in style")
}
return spec.Id
}
type styleSpec struct {
Id string `json:"id"`
}
// Allows access to a single style file
type styleFileAccessor struct {
filename string
id string
}
func (fa styleFileAccessor) ListFiles(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json")
encoder := json.NewEncoder(w)
encoder.Encode([]string{fa.id})
}
func (fa styleFileAccessor) ReadFile(w http.ResponseWriter, r *http.Request) {
vars := mux.Vars(r)
_ = vars["styleId"]
//TODO: Choose right file
// right now we just return the single file we know of
w.Header().Set("Content-Type", "application/json")
raw, err := ioutil.ReadFile(fa.filename)
if err != nil {
log.Panicln(err)
}
w.Write(raw)
}
func (fa styleFileAccessor) SaveFile(w http.ResponseWriter, r *http.Request) {
vars := mux.Vars(r)
_ = vars["styleId"]
//TODO: Save to right file
w.Header().Set("Content-Type", "application/json")
body, _ := ioutil.ReadAll(r.Body)
var out bytes.Buffer
json.Indent(&out, body, "", " ")
if err := ioutil.WriteFile(fa.filename, out.Bytes(), 0666); err != nil {
log.Fatalf("Can not copy from request to file: %s", err.Error())
}
}
+69
View File
@@ -0,0 +1,69 @@
package filewatch
import (
"io/ioutil"
"log"
"net/http"
"github.com/fsnotify/fsnotify"
"github.com/gorilla/websocket"
)
var upgrader = websocket.Upgrader{
ReadBufferSize: 1024,
WriteBufferSize: 1024,
CheckOrigin: func(r *http.Request) bool { return true },
}
func writer(ws *websocket.Conn, filename string) {
watcher, err := fsnotify.NewWatcher()
if err != nil {
log.Fatal(err)
}
defer watcher.Close()
done := make(chan bool)
go func() {
for {
select {
case event := <-watcher.Events:
if event.Op&fsnotify.Write == fsnotify.Write {
log.Println("Modified file:", event.Name)
var p []byte
var err error
p, err = ioutil.ReadFile(filename)
if err != nil {
log.Fatal(err)
}
if p != nil {
if err := ws.WriteMessage(websocket.TextMessage, p); err != nil {
return
}
}
}
case err := <-watcher.Errors:
log.Println("Watch error:", err)
}
}
}()
if err = watcher.Add(filename); err != nil {
log.Fatal(err)
}
<-done
}
func ServeWebsocketFileWatcher(filename string, w http.ResponseWriter, r *http.Request) {
ws, err := upgrader.Upgrade(w, r, nil)
if err != nil {
if _, ok := err.(websocket.HandshakeError); !ok {
log.Println(err)
}
return
}
writer(ws, filename)
defer ws.Close()
}
+27
View File
@@ -0,0 +1,27 @@
module maputnik/desktop
go 1.19
require (
github.com/GeertJohan/go.rice v1.0.3
github.com/fsnotify/fsnotify v1.6.0
github.com/gorilla/handlers v1.5.1
github.com/gorilla/mux v1.8.0
github.com/gorilla/websocket v1.5.0
github.com/maputnik/desktop v1.0.7
github.com/urfave/cli v1.22.12
)
require (
github.com/GeertJohan/go.incremental v1.0.0 // indirect
github.com/akavel/rsrc v0.8.0 // indirect
github.com/cpuguy83/go-md2man/v2 v2.0.2 // indirect
github.com/daaku/go.zipexe v1.0.2 // indirect
github.com/felixge/httpsnoop v1.0.1 // indirect
github.com/jessevdk/go-flags v1.4.0 // indirect
github.com/nkovacs/streamquote v1.0.0 // indirect
github.com/russross/blackfriday/v2 v2.1.0 // indirect
github.com/valyala/bytebufferpool v1.0.0 // indirect
github.com/valyala/fasttemplate v1.0.1 // indirect
golang.org/x/sys v0.0.0-20220908164124-27713097b956 // indirect
)
+54
View File
@@ -0,0 +1,54 @@
github.com/BurntSushi/toml v1.2.1/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ=
github.com/GeertJohan/go.incremental v1.0.0 h1:7AH+pY1XUgQE4Y1HcXYaMqAI0m9yrFqo/jt0CW30vsg=
github.com/GeertJohan/go.incremental v1.0.0/go.mod h1:6fAjUhbVuX1KcMD3c8TEgVUqmo4seqhv0i0kdATSkM0=
github.com/GeertJohan/go.rice v1.0.3 h1:k5viR+xGtIhF61125vCE1cmJ5957RQGXG6dmbaWZSmI=
github.com/GeertJohan/go.rice v1.0.3/go.mod h1:XVdrU4pW00M4ikZed5q56tPf1v2KwnIKeIdc9CBYNt4=
github.com/akavel/rsrc v0.8.0 h1:zjWn7ukO9Kc5Q62DOJCcxGpXC18RawVtYAGdz2aLlfw=
github.com/akavel/rsrc v0.8.0/go.mod h1:uLoCtb9J+EyAqh+26kdrTgmzRBFPGOolLWKpdxkKq+c=
github.com/cpuguy83/go-md2man/v2 v2.0.2 h1:p1EgwI/C7NhT0JmVkwCD2ZBK8j4aeHQX2pMHHBfMQ6w=
github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
github.com/daaku/go.zipexe v1.0.2 h1:Zg55YLYTr7M9wjKn8SY/WcpuuEi+kR2u4E8RhvpyXmk=
github.com/daaku/go.zipexe v1.0.2/go.mod h1:5xWogtqlYnfBXkSB1o9xysukNP9GTvaNkqzUZbt3Bw8=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/felixge/httpsnoop v1.0.1 h1:lvB5Jl89CsZtGIWuTcDM1E/vkVs49/Ml7JJe07l8SPQ=
github.com/felixge/httpsnoop v1.0.1/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U=
github.com/fsnotify/fsnotify v1.6.0 h1:n+5WquG0fcWoWp6xPWfHdbskMCQaFnG6PfBrh1Ky4HY=
github.com/fsnotify/fsnotify v1.6.0/go.mod h1:sl3t1tCWJFWoRz9R8WJCbQihKKwmorjAbSClcnxKAGw=
github.com/gorilla/handlers v1.5.1 h1:9lRY6j8DEeeBT10CvO9hGW0gmky0BprnvDI5vfhUHH4=
github.com/gorilla/handlers v1.5.1/go.mod h1:t8XrUpc4KVXb7HGyJ4/cEnwQiaxrX/hz1Zv/4g96P1Q=
github.com/gorilla/mux v1.8.0 h1:i40aqfkR1h2SlN9hojwV5ZA91wcXFOvkdNIeFDP5koI=
github.com/gorilla/mux v1.8.0/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So=
github.com/gorilla/websocket v1.5.0 h1:PPwGk2jz7EePpoHN/+ClbZu8SPxiqlu12wZP/3sWmnc=
github.com/gorilla/websocket v1.5.0/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
github.com/jessevdk/go-flags v1.4.0 h1:4IU2WS7AumrZ/40jfhf4QVDMsQwqA7VEHozFRrGARJA=
github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI=
github.com/maputnik/desktop v1.0.7 h1:rdFg7emIJOT3YsZpwqSChmWtMOvu+T4h6WwVQAZP9n4=
github.com/maputnik/desktop v1.0.7/go.mod h1:wmDjHUztx9jOBz0I22589yWguAGdV/sEM57YANpN8oQ=
github.com/nkovacs/streamquote v1.0.0 h1:PmVIV08Zlx2lZK5fFZlMZ04eHcDTIFJCv/5/0twVUow=
github.com/nkovacs/streamquote v1.0.0/go.mod h1:BN+NaZ2CmdKqUuTUXUEm9j95B2TRbpOWpxbJYzzgUsc=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk=
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
github.com/stretchr/testify v1.8.1 h1:w7B6lhMri9wdJUVmEZPGGhZzrYTPvgJArz7wNPgYKsk=
github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
github.com/urfave/cli v1.22.12 h1:igJgVw1JdKH+trcLWLeLwZjU9fEfPesQ+9/e4MQ44S8=
github.com/urfave/cli v1.22.12/go.mod h1:sSBEIC79qR6OvcmsD4U3KABeOTxDqQtdDnaFuUN30b8=
github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw=
github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc=
github.com/valyala/fasttemplate v1.0.1 h1:tY9CJiPnMXf1ERmG2EyK7gNUd+c6RKGD0IfU8WdUSz8=
github.com/valyala/fasttemplate v1.0.1/go.mod h1:UQGH1tvbgY+Nz5t2n7tXsz52dQxojPUpymEIMZ47gx8=
golang.org/x/sys v0.0.0-20220908164124-27713097b956 h1:XeJjHH1KiLpKGb6lvMiksZ9l0fVUh+AmGcm0nOMEBOY=
golang.org/x/sys v0.0.0-20220908164124-27713097b956/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
+80
View File
@@ -0,0 +1,80 @@
package main
import (
"fmt"
"net/http"
"os"
"path/filepath"
"github.com/GeertJohan/go.rice"
"github.com/gorilla/handlers"
"github.com/gorilla/mux"
"github.com/maputnik/desktop/filewatch"
"github.com/urfave/cli"
)
func main() {
app := cli.NewApp()
app.Name = "maputnik"
app.Usage = "Server for integrating Maputnik locally"
app.Version = "Editor: 1.7.0; Desktop: 1.1.0"
app.Flags = []cli.Flag{
&cli.StringFlag{
Name: "file, f",
Usage: "Allow access to JSON style from web client",
},
&cli.BoolFlag{
Name: "watch",
Usage: "Notify web client about JSON style file changes",
},
&cli.IntFlag{
Name: "port",
Value: 8000,
Usage: "TCP port to listen on",
},
&cli.StringFlag{
Name: "static",
Usage: "Serve directory under /static/",
},
}
app.Action = func(c *cli.Context) error {
gui := http.FileServer(rice.MustFindBox("editor/public").HTTPBox())
router := mux.NewRouter().StrictSlash(true)
filename := c.String("file")
if filename != "" {
fmt.Printf("%s is accessible via Maputnik\n", filename)
// Allow access to reading and writing file on the local system
path, _ := filepath.Abs(filename)
accessor := StyleFileAccessor(path)
router.Path("/styles").Methods("GET").HandlerFunc(accessor.ListFiles)
router.Path("/styles/{styleId}").Methods("GET").HandlerFunc(accessor.ReadFile)
router.Path("/styles/{styleId}").Methods("PUT").HandlerFunc(accessor.SaveFile)
// Register websocket to notify we clients about file changes
if c.Bool("watch") {
router.Path("/ws").HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
filewatch.ServeWebsocketFileWatcher(filename, w, r)
})
}
}
staticDir := c.String("static")
if staticDir != "" {
h := http.StripPrefix("/static/", http.FileServer(http.Dir(staticDir)))
router.PathPrefix("/static/").Handler(h)
}
router.PathPrefix("/").Handler(http.StripPrefix("/", gui))
loggedRouter := handlers.LoggingHandler(os.Stdout, router)
corsRouter := handlers.CORS(handlers.AllowedHeaders([]string{"Content-Type"}), handlers.AllowedMethods([]string{"GET", "PUT"}), handlers.AllowedOrigins([]string{"*"}), handlers.AllowCredentials())(loggedRouter)
fmt.Printf("Exposing Maputnik on http://localhost:%d\n", c.Int("port"))
return http.ListenAndServe(fmt.Sprintf(":%d", c.Int("port")), corsRouter)
}
app.Run(os.Args)
}
Binary file not shown.

Before

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 8.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 24 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 65 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 37 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 14 KiB

+2290 -10750
View File
File diff suppressed because it is too large Load Diff
+53 -61
View File
@@ -1,41 +1,43 @@
{
"name": "maputnik",
"version": "2.0.0-pre.2",
"version": "2.0.0",
"description": "A MapLibre GL visual style editor",
"type": "module",
"main": "''",
"scripts": {
"start": "vite",
"build": "tsc && vite build",
"build": "tsc && vite build --base=/maputnik/",
"lint": "eslint ./src ./cypress --ext ts,tsx,js,jsx --report-unused-disable-directives --max-warnings 0",
"test": "cypress run",
"cy:open": "cypress open",
"lint-css": "stylelint \"src/styles/*.scss\"",
"storybook": "storybook dev -h 0.0.0.0 -p 6006",
"build-storybook": "storybook build -o build/storybook"
"lint-css": "stylelint \"src/styles/*.scss\""
},
"repository": {
"type": "git",
"url": "https://github.com/maputnik/editor"
"url": "https://github.com/maplibre/maputnik"
},
"author": "Lukas Martinelli",
"license": "MIT",
"homepage": "https://github.com/maputnik/editor#readme",
"homepage": "https://github.com/maplibre/maputnik#readme",
"dependencies": {
"@mapbox/mapbox-gl-rtl-text": "^0.2.3",
"@maplibre/maplibre-gl-style-spec": "^17.0.1",
"@mdi/js": "^6.6.96",
"@mdi/react": "^1.5.0",
"@typescript-eslint/eslint-plugin": "^6.16.0",
"@typescript-eslint/parser": "^6.16.0",
"@maplibre/maplibre-gl-geocoder": "^1.5.0",
"@maplibre/maplibre-gl-inspect": "^1.6.3",
"@maplibre/maplibre-gl-style-spec": "^20.1.1",
"@mdi/js": "^7.4.47",
"@mdi/react": "^1.6.1",
"@typescript-eslint/eslint-plugin": "^7.3.1",
"@typescript-eslint/parser": "^7.3.1",
"array-move": "^4.0.0",
"buffer": "^6.0.3",
"classnames": "^2.3.1",
"classnames": "^2.5.1",
"codemirror": "^5.65.2",
"color": "^4.2.3",
"cypress-plugin-tab": "^1.0.5",
"detect-browser": "^5.3.0",
"events": "^3.3.0",
"file-saver": "^2.0.5",
"json-stringify-pretty-compact": "^3.0.0",
"json-stringify-pretty-compact": "^4.0.0",
"json-to-ast": "^2.1.0",
"jsonlint": "github:josdejong/jsonlint#85a19d7",
"lodash": "^4.17.21",
@@ -45,30 +47,29 @@
"lodash.get": "^4.4.2",
"lodash.isequal": "^4.5.0",
"lodash.throttle": "^4.1.1",
"mapbox-gl-inspect": "^1.3.1",
"maplibre-gl": "^2.4.0",
"maplibre-gl": "^4.1.1",
"maputnik-design": "github:maputnik/design#172b06c",
"ol": "^6.14.1",
"ol-mapbox-style": "^7.1.1",
"prop-types": "^15.8.1",
"react": "^16.0.0",
"react-accessible-accordion": "^4.0.0",
"react": "^18.2.0",
"react-accessible-accordion": "^5.0.0",
"react-aria-menubutton": "^7.0.3",
"react-aria-modal": "^4.0.1",
"react-aria-modal": "^5.0.2",
"react-autobind": "^1.0.6",
"react-autocomplete": "^1.8.1",
"react-collapse": "^5.1.1",
"react-color": "^2.19.3",
"react-dom": "^16.0.0",
"react-dom": "^18.2.0",
"react-file-reader-input": "^2.0.0",
"react-icon-base": "^2.1.2",
"react-icons": "^4.3.1",
"react-icons": "^5.0.1",
"react-sortable-hoc": "^2.0.0",
"reconnecting-websocket": "^4.4.0",
"sass": "^1.50.0",
"slugify": "^1.6.5",
"sass": "^1.72.0",
"slugify": "^1.6.6",
"string-hash": "^1.1.3",
"url": "^0.11.0"
"url": "^0.11.3"
},
"jshintConfig": {
"esversion": 6
@@ -88,24 +89,15 @@
}
},
"devDependencies": {
"@cypress/code-coverage": "^3.12.15",
"@cypress/code-coverage": "^3.12.30",
"@istanbuljs/nyc-config-typescript": "^1.0.2",
"@rollup/plugin-replace": "^5.0.5",
"@shellygo/cypress-test-utils": "^2.0.17",
"@storybook/addon-a11y": "^7.6.5",
"@storybook/addon-actions": "^7.6.5",
"@storybook/addon-links": "^7.6.5",
"@storybook/addon-storysource": "^7.6.5",
"@storybook/addons": "^7.6.5",
"@storybook/builder-vite": "^7.6.5",
"@storybook/react": "^7.6.5",
"@storybook/react-vite": "^7.6.5",
"@storybook/theming": "^7.6.5",
"@shellygo/cypress-test-utils": "^2.0.52",
"@types/codemirror": "^5.60.15",
"@types/color": "^3.0.6",
"@types/cors": "^2.8.17",
"@types/file-saver": "^2.0.7",
"@types/geojson": "^7946.0.13",
"@types/geojson": "^7946.0.14",
"@types/json-to-ast": "^2.1.4",
"@types/lodash.capitalize": "^4.2.9",
"@types/lodash.clamp": "^4.0.9",
@@ -113,37 +105,37 @@
"@types/lodash.get": "^4.4.9",
"@types/lodash.isequal": "^4.5.8",
"@types/lodash.throttle": "^4.1.9",
"@types/react": "^16.14.52",
"@types/react-aria-menubutton": "^6.2.13",
"@types/react-aria-modal": "^4.0.9",
"@types/react-autocomplete": "^1.8.9",
"@types/mocha": "^10.0.6",
"@types/randomcolor": "^0.5.9",
"@types/react": "^18.2.67",
"@types/react-aria-menubutton": "^6.2.14",
"@types/react-aria-modal": "^4.0.10",
"@types/react-autocomplete": "^1.8.10",
"@types/react-collapse": "^5.0.4",
"@types/react-color": "^3.0.10",
"@types/react-dom": "^16.9.24",
"@types/react-color": "^3.0.12",
"@types/react-dom": "^18.2.22",
"@types/react-file-reader-input": "^2.0.4",
"@types/react-icon-base": "^2.1.6",
"@types/string-hash": "^1.1.3",
"@types/uuid": "^9.0.7",
"@vitejs/plugin-react": "^4.2.0",
"@types/uuid": "^9.0.8",
"@vitejs/plugin-react": "^4.2.1",
"cors": "^2.8.5",
"cypress": "^13.6.1",
"eslint": "^8.56.0",
"eslint-plugin-react": "^7.33.2",
"cypress": "^13.7.0",
"eslint": "^8.57.0",
"eslint-plugin-react": "^7.34.1",
"eslint-plugin-react-hooks": "^4.6.0",
"eslint-plugin-react-refresh": "^0.4.5",
"express": "^4.17.3",
"eslint-plugin-react-refresh": "^0.4.6",
"istanbul": "^0.4.5",
"istanbul-lib-coverage": "^3.2.0",
"mocha": "^9.2.2",
"postcss": "^8.4.12",
"react-hot-loader": "^4.13.0",
"storybook": "^7.6.5",
"stylelint": "^14.6.1",
"stylelint-config-recommended-scss": "^6.0.0",
"stylelint-scss": "^4.2.0",
"typescript": "^5.3.3",
"uuid": "^8.3.2",
"vite": "^5.0.0",
"vite-plugin-istanbul": "^5.0.0"
"istanbul-lib-coverage": "^3.2.2",
"mocha": "^10.3.0",
"postcss": "^8.4.38",
"react-hot-loader": "^4.13.1",
"stylelint": "^16.2.1",
"stylelint-config-recommended-scss": "^14.0.0",
"stylelint-scss": "^6.2.1",
"typescript": "^5.4.3",
"uuid": "^9.0.1",
"vite": "^5.2.2",
"vite-plugin-istanbul": "^6.0.0"
}
}
+2 -3
View File
@@ -9,7 +9,7 @@ import {unset} from 'lodash'
import {arrayMoveMutable} from 'array-move'
import hash from "string-hash";
import {Map, LayerSpecification, StyleSpecification, ValidationError, SourceSpecification} from 'maplibre-gl'
import {latest, validate} from '@maplibre/maplibre-gl-style-spec'
import {latest, validateStyleMin} from '@maplibre/maplibre-gl-style-spec'
import MapMaplibreGl from './MapMaplibreGl'
import MapOpenLayers from './MapOpenLayers'
@@ -379,8 +379,7 @@ export default class App extends React.Component<any, AppState> {
this.getInitialStateFromUrl(newStyle);
}
// This "any" can be removed in latest version of maplibre where maplibre re-exported types from style-spec
const errors = validate(newStyle as any, latest) || [];
const errors: ValidationError[] = validateStyleMin(newStyle) || [];
// The validate function doesn't give us errors for duplicate error with
// empty string for layer.id, manually deal with that here.
+1 -1
View File
@@ -4,7 +4,7 @@ import {LayerSpecification, StyleSpecification} from 'maplibre-gl';
type AppMessagePanelProps = {
errors?: unknown[]
infos?: unknown[]
infos?: string[]
mapStyle?: StyleSpecification
onLayerSelect?(...args: unknown[]): unknown
currentLayer?: LayerSpecification
+6 -32
View File
@@ -2,9 +2,10 @@ import React from 'react'
import classnames from 'classnames'
import {detect} from 'detect-browser';
import {MdFileDownload, MdOpenInBrowser, MdSettings, MdLayers, MdHelpOutline, MdFindInPage, MdAssignmentTurnedIn} from 'react-icons/md'
import {MdFileDownload, MdOpenInBrowser, MdSettings, MdLayers, MdHelpOutline, MdFindInPage} from 'react-icons/md'
import pkgJson from '../../package.json'
//@ts-ignore
import maputnikLogo from 'maputnik-design/logos/logo-color.svg?inline'
// This is required because of <https://stackoverflow.com/a/49846426>, there isn't another way to detect support that I'm aware of.
const browser = detect();
@@ -43,29 +44,6 @@ class ToolbarLink extends React.Component<ToolbarLinkProps> {
}
}
type ToolbarLinkHighlightedProps = {
className?: string
children?: React.ReactNode
href?: string
onToggleModal?(...args: unknown[]): unknown
};
class ToolbarLinkHighlighted extends React.Component<ToolbarLinkHighlightedProps> {
render() {
return <a
className={classnames('maputnik-toolbar-link', "maputnik-toolbar-link--highlighted", this.props.className)}
href={this.props.href}
rel="noopener noreferrer"
target="_blank"
data-wd-key="toolbar:link-highlighted"
>
<span className="maputnik-toolbar-link-wrapper">
{this.props.children}
</span>
</a>
}
}
type ToolbarSelectProps = {
children?: React.ReactNode
wdKey?: string
@@ -216,9 +194,9 @@ export default class AppToolbar extends React.Component<AppToolbarProps> {
className="maputnik-toolbar-logo"
target="blank"
rel="noreferrer noopener"
href="https://github.com/maputnik/editor"
href="https://github.com/maplibre/maputnik"
>
<img src="node_modules/maputnik-design/logos/logo-color.svg" />
<img src={maputnikLogo} alt="Maputnik on GitHub" />
<h1>
<span className="maputnik-toolbar-name">{pkgJson.name}</span>
<span className="maputnik-toolbar-version">v{pkgJson.version}</span>
@@ -272,14 +250,10 @@ export default class AppToolbar extends React.Component<AppToolbarProps> {
</label>
</ToolbarSelect>
<ToolbarLink href={"https://github.com/maputnik/editor/wiki"}>
<ToolbarLink href={"https://github.com/maplibre/maputnik/wiki"}>
<MdHelpOutline />
<IconText>Help</IconText>
</ToolbarLink>
<ToolbarLinkHighlighted href={"https://gregorywolanski.typeform.com/to/cPgaSY"}>
<MdAssignmentTurnedIn />
<IconText>Take the Maputnik Survey</IconText>
</ToolbarLinkHighlighted>
</div>
</div>
</nav>
+2 -2
View File
@@ -1,10 +1,10 @@
import React, {SyntheticEvent} from 'react'
import React, {PropsWithChildren, SyntheticEvent} from 'react'
import classnames from 'classnames'
import FieldDocLabel from './FieldDocLabel'
import Doc from './Doc'
type BlockProps = {
type BlockProps = PropsWithChildren & {
"data-wd-key"?: string
label?: string
action?: React.ReactElement
+1 -1
View File
@@ -31,7 +31,7 @@ export default class Doc extends React.Component<DocProps> {
const renderValues = (
!!values &&
// HACK: Currently we merge additional values into the style spec, so this is required
// See <https://github.com/maputnik/editor/blob/main/src/components/PropertyGroup.jsx#L16>
// See <https://github.com/maplibre/maputnik/blob/main/src/components/PropertyGroup.jsx#L16>
!Array.isArray(values)
);
+1 -1
View File
@@ -2,7 +2,7 @@ import React from 'react'
import {MdInfoOutline, MdHighlightOff} from 'react-icons/md'
type FieldDocLabelProps = {
label: object | string | undefined
label: JSX.Element | string | undefined
fieldSpec?: {
doc?: string
}
+1 -1
View File
@@ -1,6 +1,6 @@
import React from 'react'
import {latest} from '@maplibre/maplibre-gl-style-spec'
import latest from '@maplibre/maplibre-gl-style-spec/dist/latest.json'
import Block from './Block'
import InputString from './InputString'
+1 -1
View File
@@ -1,6 +1,6 @@
import React from 'react'
import {latest} from '@maplibre/maplibre-gl-style-spec'
import latest from '@maplibre/maplibre-gl-style-spec/dist/latest.json'
import Block from './Block'
import InputNumber from './InputNumber'
+1 -1
View File
@@ -1,6 +1,6 @@
import React from 'react'
import {latest} from '@maplibre/maplibre-gl-style-spec'
import latest from '@maplibre/maplibre-gl-style-spec/dist/latest.json'
import Block from './Block'
import InputNumber from './InputNumber'
+1 -1
View File
@@ -1,6 +1,6 @@
import React from 'react'
import {latest} from '@maplibre/maplibre-gl-style-spec'
import latest from '@maplibre/maplibre-gl-style-spec/dist/latest.json'
import Block from './Block'
import InputAutocomplete from './InputAutocomplete'
+1 -1
View File
@@ -1,6 +1,6 @@
import React from 'react'
import {latest} from '@maplibre/maplibre-gl-style-spec'
import latest from '@maplibre/maplibre-gl-style-spec/dist/latest.json'
import Block from './Block'
import InputSelect from './InputSelect'
import InputString from './InputString'
+2 -2
View File
@@ -1,9 +1,9 @@
import React, { ReactElement } from 'react'
import React, { PropsWithChildren, ReactElement } from 'react'
import FieldDocLabel from './FieldDocLabel'
import Doc from './Doc'
import generateUniqueId from '../libs/document-uid';
type FieldsetProps = {
type FieldsetProps = PropsWithChildren & {
label?: string,
fieldSpec?: { doc?: string },
action?: ReactElement,
+1 -1
View File
@@ -47,7 +47,7 @@ function createStyleFromFilter(filter: LegacyFilterSpecification | ExpressionSpe
"sources": {
"tmp": {
"type": "geojson",
"data": {}
"data": ''
}
},
"sprite": "",
+2 -2
View File
@@ -1,8 +1,8 @@
import React from 'react'
import React, { PropsWithChildren } from 'react'
import InputButton from './InputButton'
import {MdDelete} from 'react-icons/md'
type FilterEditorBlockProps = {
type FilterEditorBlockProps = PropsWithChildren & {
onDelete(...args: unknown[]): unknown
};
+6 -3
View File
@@ -19,7 +19,10 @@ type InputNumberState = {
editing: boolean
editingRange?: boolean
value?: number
dirtyValue?: number
/**
* This is the value that is currently being edited. It can be an invalid value.
*/
dirtyValue?: number | string | undefined
}
export default class InputNumber extends React.Component<InputNumberProps, InputNumberState> {
@@ -66,7 +69,7 @@ export default class InputNumber extends React.Component<InputNumberProps, Input
}
this.setState({
dirtyValue: newValue === "" ? undefined : value,
dirtyValue: newValue === "" ? undefined : newValue,
})
}
@@ -125,7 +128,7 @@ export default class InputNumber extends React.Component<InputNumberProps, Input
// for example we might go from 13 to 13.23, however because we know
// that came from a keyboard event we always want to increase by a
// single step value.
if (value < this.state.dirtyValue!) {
if (value < +this.state.dirtyValue!) {
value = this.state.value! - step;
}
else {
+1
View File
@@ -49,6 +49,7 @@ export default class SpecField extends React.Component<SpecFieldProps> {
value: this.props.value,
default: this.props.fieldSpec?.default,
name: this.props.fieldName,
"data-wd-key": "spec-field-input:" + this.props.fieldName,
onChange: (newValue: number | undefined | (string | number | undefined)[]) => this.props.onChange!(this.props.fieldName, newValue),
'aria-label': this.props['aria-label'],
}
+1 -1
View File
@@ -139,6 +139,6 @@ class LayerListItem extends React.Component<LayerListItemProps> {
}
}
const LayerListItemSortable = SortableElement((props: LayerListItemProps) => <LayerListItem {...props} />);
const LayerListItemSortable = SortableElement<LayerListItemProps>((props: LayerListItemProps) => <LayerListItem {...props} />);
export default LayerListItemSortable;
+76 -64
View File
@@ -1,10 +1,8 @@
import React, {type JSX} from 'react'
import ReactDOM from 'react-dom'
import MapLibreGl, {LayerSpecification, LngLat, Map, MapOptions, SourceSpecification, StyleSpecification} from 'maplibre-gl'
// @ts-ignore
import MapboxInspect from 'mapbox-gl-inspect'
// @ts-ignore
import colors from 'mapbox-gl-inspect/lib/colors'
import MaplibreInspect from '@maplibre/maplibre-gl-inspect'
import colors from '@maplibre/maplibre-gl-inspect/lib/colors'
import MapMaplibreGlLayerPopup from './MapMaplibreGlLayerPopup'
import MapMaplibreGlFeaturePropertyPopup, { InspectFeature } from './MapMaplibreGlFeaturePropertyPopup'
import Color from 'color'
@@ -13,13 +11,13 @@ import { HighlightedLayer, colorHighlightedLayer } from '../libs/highlight'
import 'maplibre-gl/dist/maplibre-gl.css'
import '../maplibregl.css'
import '../libs/maplibre-rtl'
//@ts-ignore
import MaplibreGeocoder from '@maplibre/maplibre-gl-geocoder';
import '@maplibre/maplibre-gl-geocoder/dist/maplibre-gl-geocoder.css';
const IS_SUPPORTED = MapLibreGl.supported();
function renderPopup(popup: JSX.Element, mountNode: ReactDOM.Container) {
function renderPopup(popup: JSX.Element, mountNode: ReactDOM.Container): HTMLElement {
ReactDOM.render(popup, mountNode);
return mountNode;
return mountNode as HTMLElement;
}
function buildInspectStyle(originalMapStyle: StyleSpecification, coloredLayers: HighlightedLayer[], highlightedLayer?: HighlightedLayer) {
@@ -37,6 +35,7 @@ function buildInspectStyle(originalMapStyle: StyleSpecification, coloredLayers:
}
const sources: {[key:string]: SourceSpecification} = {}
Object.keys(originalMapStyle.sources).forEach(sourceId => {
const source = originalMapStyle.sources[sourceId]
if(source.type !== 'raster' && source.type !== 'raster-dem') {
@@ -69,7 +68,7 @@ type MapMaplibreGlProps = {
type MapMaplibreGlState = {
map: Map | null
inspect: MapboxInspect | null
inspect: MaplibreInspect | null
zoom?: number
};
@@ -91,18 +90,6 @@ export default class MapMaplibreGl extends React.Component<MapMaplibreGlProps, M
}
}
updateMapFromProps(props: MapMaplibreGlProps) {
if(!IS_SUPPORTED) return;
if(!this.state.map) return
//Maplibre GL now does diffing natively so we don't need to calculate
//the necessary operations ourselves!
this.state.map.setStyle(
this.props.replaceAccessTokens(props.mapStyle),
{diff: true}
)
}
shouldComponentUpdate(nextProps: MapMaplibreGlProps, nextState: MapMaplibreGlState) {
let should = false;
@@ -115,39 +102,32 @@ export default class MapMaplibreGl extends React.Component<MapMaplibreGlProps, M
}
componentDidUpdate() {
if(!IS_SUPPORTED) return;
const map = this.state.map;
this.updateMapFromProps(this.props);
if(this.state.inspect && this.props.inspectModeEnabled !== this.state.inspect._showInspectMap) {
// HACK: Fix for <https://github.com/maputnik/editor/issues/576>, while we wait for a proper fix.
// eslint-disable-next-line
this.state.inspect._popupBlocked = false;
this.state.inspect.toggleInspector()
}
const styleWithTokens = this.props.replaceAccessTokens(this.props.mapStyle);
if (map) {
if (this.props.inspectModeEnabled) {
// HACK: We need to work out why we need to do this and what's causing
// this error. I'm assuming an issue with maplibre-gl update and
// mapbox-gl-inspect.
try {
this.state.inspect.render();
} catch(err) {
console.error("FIXME: Caught error", err);
}
}
// Maplibre GL now does diffing natively so we don't need to calculate
// the necessary operations ourselves!
// We also need to update the style for inspect to work properly
map.setStyle(styleWithTokens, {diff: true});
map.showTileBoundaries = this.props.options?.showTileBoundaries!;
map.showCollisionBoxes = this.props.options?.showCollisionBoxes!;
map.showOverdrawInspector = this.props.options?.showOverdrawInspector!;
}
if(this.state.inspect && this.props.inspectModeEnabled !== this.state.inspect._showInspectMap) {
this.state.inspect.toggleInspector()
}
if (this.state.inspect && this.props.inspectModeEnabled) {
this.state.inspect.setOriginalStyle(styleWithTokens);
// In case the sources are the same, there's a need to refresh the style
setTimeout(() => {
this.state.inspect!.render();
}, 500);
}
}
componentDidMount() {
if(!IS_SUPPORTED) return;
const mapOpts = {
...this.props.options,
container: this.container!,
@@ -169,6 +149,8 @@ export default class MapMaplibreGl extends React.Component<MapMaplibreGlProps, M
map.showCollisionBoxes = mapOpts.showCollisionBoxes!;
map.showOverdrawInspector = mapOpts.showOverdrawInspector!;
this.initGeocoder(map);
const zoomControl = new ZoomControl;
map.addControl(zoomControl, 'top-right');
@@ -177,7 +159,7 @@ export default class MapMaplibreGl extends React.Component<MapMaplibreGlProps, M
const tmpNode = document.createElement('div');
const inspect = new MapboxInspect({
const inspect = new MaplibreInspect({
popup: new MapLibreGl.Popup({
closeOnClick: false
}),
@@ -234,25 +216,55 @@ export default class MapMaplibreGl extends React.Component<MapMaplibreGlProps, M
this.props.onLayerSelect(index);
}
initGeocoder(map: Map) {
const geocoderConfig = {
forwardGeocode: async (config:{query: string, limit: number, language: string[]}) => {
const features = [];
try {
const request = `https://nominatim.openstreetmap.org/search?q=${config.query}&format=geojson&polygon_geojson=1&addressdetails=1`;
const response = await fetch(request);
const geojson = await response.json();
for (const feature of geojson.features) {
const center = [
feature.bbox[0] +
(feature.bbox[2] - feature.bbox[0]) / 2,
feature.bbox[1] +
(feature.bbox[3] - feature.bbox[1]) / 2
];
const point = {
type: 'Feature',
geometry: {
type: 'Point',
coordinates: center
},
place_name: feature.properties.display_name,
properties: feature.properties,
text: feature.properties.display_name,
place_type: ['place'],
center
};
features.push(point);
}
} catch (e) {
console.error(`Failed to forwardGeocode with error: ${e}`);
}
return {
features
};
}
};
const geocoder = new MaplibreGeocoder(geocoderConfig, {maplibregl: MapLibreGl});
map.addControl(geocoder, 'top-left');
}
render() {
if(IS_SUPPORTED) {
return <div
className="maputnik-map__map"
role="region"
aria-label="Map view"
ref={x => this.container = x}
data-wd-key="maplibre:map"
></div>
}
else {
return <div
className="maputnik-map maputnik-map--error"
>
<div className="maputnik-map__error-message">
Error: Cannot load MaplibreGL, WebGL is either unsupported or disabled
</div>
</div>
}
return <div
className="maputnik-map__map"
role="region"
aria-label="Map view"
ref={x => this.container = x}
data-wd-key="maplibre:map"
></div>
}
}
@@ -1,18 +1,12 @@
import React from 'react'
import Block from './Block'
import FieldString from './FieldString'
import type { GeoJSONFeatureWithSourceLayer } from '@maplibre/maplibre-gl-inspect'
export type InspectFeature = {
id: string
properties: {[key:string]: any}
layer: {[key:string]: any}
geometry: GeoJSON.Geometry
sourceLayer: string
export type InspectFeature = GeoJSONFeatureWithSourceLayer & {
inspectModeCounter?: number
counter?: number
}
function displayValue(value: string | number | Date | object) {
function displayValue(value: string | number | Date | object | undefined) {
if (typeof value === 'undefined' || value === null) return value;
if (value instanceof Date) return value.toLocaleString();
if (typeof value === 'object' ||
@@ -21,30 +15,25 @@ function displayValue(value: string | number | Date | object) {
return value;
}
function renderProperties(feature: InspectFeature) {
return Object.keys(feature.properties).map(propertyName => {
const property = feature.properties[propertyName]
return <Block key={propertyName} label={propertyName}>
<FieldString value={displayValue(property)} style={{backgroundColor: 'transparent'}}/>
</Block>
})
}
function renderFeatureId(feature: InspectFeature) {
return <Block key={"feature-id"} label={"feature_id"}>
<FieldString value={displayValue(feature.id)} style={{backgroundColor: 'transparent'}} />
</Block>
function renderKeyValueTableRow(key: string, value: string | undefined) {
return <tr key={key}>
<td className="maputnik-popup-table-cell">{key}</td>
<td className="maputnik-popup-table-cell">{value}</td>
</tr>
}
function renderFeature(feature: InspectFeature, idx: number) {
return <div key={`${feature.sourceLayer}-${idx}`}>
<div className="maputnik-popup-layer-id">{feature.layer['source']}: {feature.layer['source-layer']}{feature.inspectModeCounter && <span> × {feature.inspectModeCounter}</span>}</div>
<Block key={"property-type"} label={"$type"}>
<FieldString value={feature.geometry.type} style={{backgroundColor: 'transparent'}} />
</Block>
{renderFeatureId(feature)}
{renderProperties(feature)}
</div>
return <React.Fragment key={idx}>
<tr>
<td colSpan={2} className="maputnik-popup-layer-id">{feature.layer['source']}: {feature.layer['source-layer']}{feature.inspectModeCounter && <span> × {feature.inspectModeCounter}</span>}</td>
</tr>
{renderKeyValueTableRow("$type", feature.geometry.type)}
{renderKeyValueTableRow("Feature ID", displayValue(feature.id))}
{Object.keys(feature.properties).map(propertyName => {
const property = feature.properties[propertyName];
return renderKeyValueTableRow(propertyName, displayValue(property))
})}
</React.Fragment>
}
function removeDuplicatedFeatures(features: InspectFeature[]) {
@@ -78,7 +67,11 @@ class FeaturePropertyPopup extends React.Component<FeaturePropertyPopupProps> {
render() {
const features = removeDuplicatedFeatures(this.props.features)
return <div className="maputnik-feature-property-popup">
{features.map(renderFeature)}
<table className="maputnik-popup-table">
<tbody>
{features.map(renderFeature)}
</tbody>
</table>
</div>
}
}
+14 -21
View File
@@ -8,15 +8,16 @@ function groupFeaturesBySourceLayer(features: InspectFeature[]) {
const returnedFeatures: {[key: string]: number} = {}
features.forEach(feature => {
const sourceKey = feature.layer['source-layer'] as string;
if(Object.prototype.hasOwnProperty.call(returnedFeatures, feature.layer.id)) {
returnedFeatures[feature.layer.id]++
const featureObject = sources[feature.layer['source-layer']].find((f: InspectFeature) => f.layer.id === feature.layer.id)
const featureObject = sources[sourceKey].find((f: InspectFeature) => f.layer.id === feature.layer.id)
featureObject!.counter = returnedFeatures[feature.layer.id]
} else {
sources[feature.layer['source-layer']] = sources[feature.layer['source-layer']] || []
sources[feature.layer['source-layer']].push(feature)
sources[sourceKey] = sources[sourceKey] || []
sources[sourceKey].push(feature)
returnedFeatures[feature.layer.id] = 1
}
@@ -40,29 +41,21 @@ class FeatureLayerPopup extends React.Component<FeatureLayerPopupProps> {
try {
const paintProps = feature.layer.paint;
let propName;
if(Object.prototype.hasOwnProperty.call(paintProps, "text-color") && paintProps["text-color"]) {
propName = "text-color";
if("text-color" in paintProps && paintProps["text-color"]) {
return String(paintProps["text-color"]);
}
else if (Object.prototype.hasOwnProperty.call(paintProps, "fill-color") && paintProps["fill-color"]) {
propName = "fill-color";
if ("fill-color" in paintProps && paintProps["fill-color"]) {
return String(paintProps["fill-color"]);
}
else if (Object.prototype.hasOwnProperty.call(paintProps, "line-color") && paintProps["line-color"]) {
propName = "line-color";
if ("line-color" in paintProps && paintProps["line-color"]) {
return String(paintProps["line-color"]);
}
else if (Object.prototype.hasOwnProperty.call(paintProps, "fill-extrusion-color") && paintProps["fill-extrusion-color"]) {
propName = "fill-extrusion-color";
}
if(propName) {
const color = feature.layer.paint[propName];
return String(color);
}
else {
// Default color
return "black";
if ("fill-extrusion-color" in paintProps && paintProps["fill-extrusion-color"]) {
return String(paintProps["fill-extrusion-color"]);
}
// Default color
return "black";
}
// This is quite complex, just incase there's an edgecase we're missing
// always return black if we get an unexpected error.
+3 -3
View File
@@ -1,10 +1,10 @@
import React from 'react'
import React, { PropsWithChildren } from 'react'
import {MdClose} from 'react-icons/md'
import AriaModal from 'react-aria-modal'
import classnames from 'classnames';
type ModalProps = {
type ModalProps = PropsWithChildren & {
"data-wd-key"?: string
isOpen: boolean
title: string
@@ -20,7 +20,7 @@ export default class Modal extends React.Component<ModalProps> {
underlayClickExits: true
}
// See <https://github.com/maputnik/editor/issues/416>
// See <https://github.com/maplibre/maputnik/issues/416>
onClose = () => {
if (document.activeElement) {
(document.activeElement as HTMLElement).blur();
+47 -21
View File
@@ -1,6 +1,6 @@
import React from 'react'
import {latest} from '@maplibre/maplibre-gl-style-spec'
import type {LightSpecification, StyleSpecification, TransitionSpecification} from 'maplibre-gl'
import latest from '@maplibre/maplibre-gl-style-spec/dist/latest.json'
import type {LightSpecification, StyleSpecification, TerrainSpecification, TransitionSpecification} from 'maplibre-gl'
import FieldArray from './FieldArray'
import FieldNumber from './FieldNumber'
@@ -58,6 +58,25 @@ export default class ModalSettings extends React.Component<ModalSettingsProps> {
});
}
changeTerrainProperty(property: keyof TerrainSpecification, value: any) {
const terrain = {
...this.props.mapStyle.terrain,
}
if (value === undefined) {
delete terrain[property];
}
else {
// @ts-ignore
terrain[property] = value;
}
this.props.onStyleChanged({
...this.props.mapStyle,
terrain,
});
}
changeStyleProperty(property: keyof StyleSpecification | "owner", value: any) {
const changedStyle = {
...this.props.mapStyle,
@@ -77,10 +96,10 @@ export default class ModalSettings extends React.Component<ModalSettingsProps> {
render() {
const metadata = this.props.mapStyle.metadata || {} as any;
const {onChangeMetadataProperty, mapStyle} = this.props;
const inputProps = { }
const light = this.props.mapStyle.light || {};
const transition = this.props.mapStyle.transition || {};
const terrain = this.props.mapStyle.terrain || {} as TerrainSpecification;
return <Modal
data-wd-key="modal:settings"
@@ -89,21 +108,21 @@ export default class ModalSettings extends React.Component<ModalSettingsProps> {
title={'Style Settings'}
>
<div className="modal:settings">
<FieldString {...inputProps}
<FieldString
label={"Name"}
fieldSpec={latest.$root.name}
data-wd-key="modal:settings.name"
value={this.props.mapStyle.name}
onChange={this.changeStyleProperty.bind(this, "name")}
/>
<FieldString {...inputProps}
<FieldString
label={"Owner"}
fieldSpec={{doc: "Owner ID of the style. Used by Mapbox or future style APIs."}}
data-wd-key="modal:settings.owner"
value={(this.props.mapStyle as any).owner}
onChange={this.changeStyleProperty.bind(this, "owner")}
/>
<FieldUrl {...inputProps}
<FieldUrl
fieldSpec={latest.$root.sprite}
label="Sprite URL"
data-wd-key="modal:settings.sprite"
@@ -111,7 +130,7 @@ export default class ModalSettings extends React.Component<ModalSettingsProps> {
onChange={this.changeStyleProperty.bind(this, "sprite")}
/>
<FieldUrl {...inputProps}
<FieldUrl
label="Glyphs URL"
fieldSpec={latest.$root.glyphs}
data-wd-key="modal:settings.glyphs"
@@ -119,7 +138,7 @@ export default class ModalSettings extends React.Component<ModalSettingsProps> {
onChange={this.changeStyleProperty.bind(this, "glyphs")}
/>
<FieldString {...inputProps}
<FieldString
label={fieldSpecAdditional.maputnik.maptiler_access_token.label}
fieldSpec={fieldSpecAdditional.maputnik.maptiler_access_token}
data-wd-key="modal:settings.maputnik:openmaptiles_access_token"
@@ -127,7 +146,7 @@ export default class ModalSettings extends React.Component<ModalSettingsProps> {
onChange={onChangeMetadataProperty.bind(this, "maputnik:openmaptiles_access_token")}
/>
<FieldString {...inputProps}
<FieldString
label={fieldSpecAdditional.maputnik.thunderforest_access_token.label}
fieldSpec={fieldSpecAdditional.maputnik.thunderforest_access_token}
data-wd-key="modal:settings.maputnik:thunderforest_access_token"
@@ -141,21 +160,19 @@ export default class ModalSettings extends React.Component<ModalSettingsProps> {
length={2}
type="number"
value={mapStyle.center || []}
default={latest.$root.center.default || [0, 0]}
default={[0, 0]}
onChange={this.changeStyleProperty.bind(this, "center")}
/>
<FieldNumber
{...inputProps}
label={"Zoom"}
fieldSpec={latest.$root.zoom}
value={mapStyle.zoom}
default={latest.$root.zoom.default || 0}
default={0}
onChange={this.changeStyleProperty.bind(this, "zoom")}
/>
<FieldNumber
{...inputProps}
label={"Bearing"}
fieldSpec={latest.$root.bearing}
value={mapStyle.bearing}
@@ -164,7 +181,6 @@ export default class ModalSettings extends React.Component<ModalSettingsProps> {
/>
<FieldNumber
{...inputProps}
label={"Pitch"}
fieldSpec={latest.$root.pitch}
value={mapStyle.pitch}
@@ -173,7 +189,6 @@ export default class ModalSettings extends React.Component<ModalSettingsProps> {
/>
<FieldEnum
{...inputProps}
label={"Light anchor"}
fieldSpec={latest.light.anchor}
name="light-anchor"
@@ -184,7 +199,6 @@ export default class ModalSettings extends React.Component<ModalSettingsProps> {
/>
<FieldColor
{...inputProps}
label={"Light color"}
fieldSpec={latest.light.color}
value={light.color as string}
@@ -193,7 +207,6 @@ export default class ModalSettings extends React.Component<ModalSettingsProps> {
/>
<FieldNumber
{...inputProps}
label={"Light intensity"}
fieldSpec={latest.light.intensity}
value={light.intensity as number}
@@ -202,7 +215,6 @@ export default class ModalSettings extends React.Component<ModalSettingsProps> {
/>
<FieldArray
{...inputProps}
label={"Light position"}
fieldSpec={latest.light.position}
type="number"
@@ -212,8 +224,23 @@ export default class ModalSettings extends React.Component<ModalSettingsProps> {
onChange={this.changeLightProperty.bind(this, "position")}
/>
<FieldString
label={"Terrain source"}
fieldSpec={latest.terrain.source}
data-wd-key="modal:settings.maputnik:terrain_source"
value={terrain.source}
onChange={this.changeTerrainProperty.bind(this, "source")}
/>
<FieldNumber
label={"Terrain exaggeration"}
fieldSpec={latest.terrain.exaggeration}
value={terrain.exaggeration}
default={latest.terrain.exaggeration.default}
onChange={this.changeTerrainProperty.bind(this, "exaggeration")}
/>
<FieldNumber
{...inputProps}
label={"Transition delay"}
fieldSpec={latest.transition.delay}
value={transition.delay}
@@ -222,7 +249,6 @@ export default class ModalSettings extends React.Component<ModalSettingsProps> {
/>
<FieldNumber
{...inputProps}
label={"Transition duration"}
fieldSpec={latest.transition.duration}
value={transition.duration}
@@ -230,7 +256,7 @@ export default class ModalSettings extends React.Component<ModalSettingsProps> {
onChange={this.changeTransitionProperty.bind(this, "duration")}
/>
<FieldSelect {...inputProps}
<FieldSelect
label={fieldSpecAdditional.maputnik.style_renderer.label}
fieldSpec={fieldSpecAdditional.maputnik.style_renderer}
data-wd-key="modal:settings.maputnik:renderer"
+2 -2
View File
@@ -1,6 +1,6 @@
import React from 'react'
import {MdAddCircleOutline, MdDelete} from 'react-icons/md'
import {latest} from '@maplibre/maplibre-gl-style-spec'
import latest from '@maplibre/maplibre-gl-style-spec/dist/latest.json'
import type {GeoJSONSourceSpecification, RasterDEMSourceSpecification, RasterSourceSpecification, SourceSpecification, StyleSpecification, VectorSourceSpecification} from 'maplibre-gl'
import Modal from './Modal'
@@ -134,7 +134,7 @@ class AddSource extends React.Component<AddSourceProps, AddSourceState> {
case 'geojson_json': return {
type: 'geojson',
cluster: (source as GeoJSONSourceSpecification).cluster || false,
data: {}
data: ''
}
case 'tilejson_vector': return {
type: 'vector',
+2 -2
View File
@@ -1,6 +1,6 @@
import React from 'react'
import {mdiFunctionVariant, mdiTableRowPlusAfter} from '@mdi/js';
import {latest} from '@maplibre/maplibre-gl-style-spec'
import latest from '@maplibre/maplibre-gl-style-spec/dist/latest.json'
import InputButton from './InputButton'
import InputSpec from './InputSpec'
@@ -302,7 +302,7 @@ export default class DataProperty extends React.Component<DataPropertyProps, Dat
<div className="maputnik-data-spec-property-input">
<InputSpec
fieldName={"base"}
fieldSpec={latest.function.base}
fieldSpec={latest.function.base as typeof latest.function.base & { type: "number" }}
value={this.props.value?.base}
onChange={(_, newValue) => this.changeBase(newValue as number)}
/>
+2 -2
View File
@@ -1,6 +1,6 @@
import React from 'react'
import {mdiFunctionVariant, mdiTableRowPlusAfter} from '@mdi/js';
import {latest} from '@maplibre/maplibre-gl-style-spec'
import latest from '@maplibre/maplibre-gl-style-spec/dist/latest.json'
import InputButton from './InputButton'
import InputSpec from './InputSpec'
@@ -207,7 +207,7 @@ export default class ZoomProperty extends React.Component<ZoomPropertyProps, Zoo
<div className="maputnik-data-spec-property-input">
<InputSpec
fieldName={"base"}
fieldSpec={latest.function.base}
fieldSpec={latest.function.base as typeof latest.function.base & { type: "number" }}
value={this.props.value?.base}
onChange={(_, newValue) => this.changeBase(newValue as number | undefined)}
/>
+1 -1
View File
@@ -1,4 +1,4 @@
{
"openmaptiles": "KDhMfHvorAFkFe64wlZb",
"openmaptiles": "get_your_own_OpIi9ZULNHzrESv6T2vL",
"thunderforest": "b71f7f0ba4064f5eb9e903859a9cf5c6"
}
+4 -4
View File
@@ -1,15 +1,15 @@
import { IconContext } from "react-icons";
import ReactDOM from 'react-dom';
import { createRoot } from 'react-dom/client';
import './favicon.ico'
import './styles/index.scss'
import App from './components/App';
ReactDOM.render(
const root = createRoot(document.querySelector("#app"));
root.render(
<IconContext.Provider value={{className: 'react-icons'}}>
<App/>
</IconContext.Provider>,
document.querySelector("#app")
</IconContext.Provider>
);
// Hide the loader.
+2 -2
View File
@@ -2,7 +2,7 @@
import jsonlint from 'jsonlint';
import CodeMirror, { MarkerRange } from 'codemirror';
import jsonToAst from 'json-to-ast';
import {expression, validate} from '@maplibre/maplibre-gl-style-spec';
import {expression, validateStyleMin} from '@maplibre/maplibre-gl-style-spec';
type MarkerRangeWithMessage = MarkerRange & {message: string};
@@ -102,7 +102,7 @@ CodeMirror.registerHelper("lint", "mgl", (text: string, opts: any, doc: any) =>
let out: ReturnType<typeof expression.createExpression> | null = null;
if (context === "layer") {
// Just an empty style so we can validate a layer.
const errors = validate({
const errors = validateStyleMin({
"version": 8,
"name": "Empty Style",
"metadata": {},
+1 -1
View File
@@ -1,4 +1,4 @@
import {latest} from '@maplibre/maplibre-gl-style-spec'
import latest from '@maplibre/maplibre-gl-style-spec/dist/latest.json'
export const combiningFilterOps = ['all', 'any', 'none'];
export const setFilterOps = ['in', '!in'];
+2 -4
View File
@@ -1,7 +1,5 @@
// @ts-ignore
import stylegen from 'mapbox-gl-inspect/lib/stylegen'
// @ts-ignore
import colors from 'mapbox-gl-inspect/lib/colors'
import stylegen from '@maplibre/maplibre-gl-inspect/lib/stylegen'
import colors from '@maplibre/maplibre-gl-inspect/lib/colors'
import type {FilterSpecification,LayerSpecification } from 'maplibre-gl'
export type HighlightedLayer = LayerSpecification & {filter?: FilterSpecification};
+1 -1
View File
@@ -1,3 +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', () => {});
MapLibreGl.setRTLTextPlugin('https://unpkg.com/@mapbox/mapbox-gl-rtl-text@0.2.3/mapbox-gl-rtl-text.min.js', false);
+5 -2
View File
@@ -19,10 +19,13 @@ export class RevisionStore {
}
addRevision(revision: StyleSpecification & {id: string}) {
//TODO: compare new revision style id with old ones
//and ensure that it is always the same id
// clear any "redo" revisions once a change is made
// and ensure current index is at end of list
this.revisions = this.revisions.slice(0, this.currentIdx + 1);
this.revisions.push(revision)
this.currentIdx++
//}
}
undo() {
+9
View File
@@ -36,3 +36,12 @@
margin-top: $margin-2;
}
}
.maputnik-popup-table {
width: 100%;
}
.maputnik-popup-table-cell {
color: $color-lowgray;
padding-left: $margin-2;
}
-26
View File
@@ -1,26 +0,0 @@
import '../src/styles/index.scss';
import React from 'react';
import {Describe} from './ui';
export default {
title: 'Welcome',
};
export const ToStorybook = () => {
return (
<Describe>
<h1>Maputnik component library</h1>
<p>
This is the Maputnik component library, which shows the uses of some commonly used components from the Maputnik editor. This is a stand alone place where we can better refine them and improve their API separate from their use inside the editor.
</p>
<p>
This should also help us better refine our CSS and make it more modular as currently we rely on the cascade quite a bit in a number of places.
</p>
</Describe>
);
}
ToStorybook.story = {
name: 'Intro',
};
-44
View File
@@ -1,44 +0,0 @@
import React from 'react';
import {useActionState} from './helper';
import FieldArray from '../src/components/FieldArray';
import {Wrapper} from './ui';
export default {
title: 'FieldArray',
component: FieldArray,
};
export const NumberType = () => {
const [value, setValue] = useActionState("onChange", [1,2,3]);
return (
<Wrapper>
<FieldArray
label="Foobar"
type="number"
value={value}
length={3}
onChange={setValue}
/>
</Wrapper>
);
};
export const StringType = () => {
const [value, setValue] = useActionState("onChange", ["a", "b", "c"]);
return (
<Wrapper>
<FieldArray
label="Foobar"
type="string"
value={value}
length={3}
onChange={setValue}
/>
</Wrapper>
);
};
-27
View File
@@ -1,27 +0,0 @@
import React from 'react';
import {useActionState} from './helper';
import FieldAutocomplete from '../src/components/FieldAutocomplete';
import {Wrapper} from './ui';
export default {
title: 'FieldAutocomplete',
component: FieldAutocomplete,
};
export const Basic = () => {
const options = [["FOO", "foo"], ["BAR", "bar"], ["BAZ", "baz"]];
const [value, setValue] = useActionState("onChange", "bar");
return (
<Wrapper>
<FieldAutocomplete
label="Foobar"
options={options}
value={value}
onChange={setValue}
/>
</Wrapper>
);
};
-39
View File
@@ -1,39 +0,0 @@
import React from 'react';
import {useActionState} from './helper';
import FieldCheckbox from '../src/components/FieldCheckbox';
import {Wrapper} from './ui';
export default {
title: 'FieldCheckbox',
component: FieldCheckbox,
};
export const BasicUnchecked = () => {
const [value, setValue] = useActionState("onChange", false);
return (
<Wrapper>
<FieldCheckbox
label="Foobar"
value={value}
onChange={setValue}
/>
</Wrapper>
);
};
export const BasicChecked = () => {
const [value, setValue] = useActionState("onChange", true);
return (
<Wrapper>
<FieldCheckbox
label="Foobar"
value={value}
onChange={setValue}
/>
</Wrapper>
);
};
-25
View File
@@ -1,25 +0,0 @@
import React from 'react';
import {useActionState} from './helper';
import FieldColor from '../src/components/FieldColor';
import {Wrapper} from './ui';
export default {
title: 'FieldColor',
component: FieldColor,
};
export const Basic = () => {
const [color, setColor] = useActionState("onChange", "#ff0000");
return (
<Wrapper>
<FieldColor
label="Foobar"
value={color}
onChange={setColor}
/>
</Wrapper>
);
};
-56
View File
@@ -1,56 +0,0 @@
import React from 'react';
import {useActionState} from './helper';
import FieldDynamicArray from '../src/components/FieldDynamicArray';
import {Wrapper} from './ui';
export default {
title: 'FieldDynamicArray',
component: FieldDynamicArray,
};
export const NumberType = () => {
const [value, setValue] = useActionState("onChange", [1,2,3]);
return (
<Wrapper>
<FieldDynamicArray
label="Foobar"
type="number"
value={value}
onChange={setValue}
/>
</Wrapper>
);
};
export const UrlType = () => {
const [value, setValue] = useActionState("onChange", ["http://example.com"]);
return (
<Wrapper>
<FieldDynamicArray
label="Foobar"
type="url"
value={value}
onChange={setValue}
/>
</Wrapper>
);
};
export const EnumType = () => {
const [value, setValue] = useActionState("onChange", ["foo"]);
return (
<Wrapper>
<FieldDynamicArray
label="Foobar"
fieldSpec={{values: {"foo": null, "bar": null, "baz": null}}}
type="enum"
value={value}
onChange={setValue}
/>
</Wrapper>
);
};
-78
View File
@@ -1,78 +0,0 @@
import React from 'react';
import {useActionState} from './helper';
import FieldEnum from '../src/components/FieldEnum';
import {Wrapper} from './ui';
export default {
title: 'FieldEnum',
component: FieldEnum,
};
export const BasicFew = () => {
const options = ["Foo", "Bar", "Baz"];
const [value, setValue] = useActionState("onChange", "Foo");
return (
<Wrapper>
<FieldEnum
label="Foobar"
options={options}
value={value}
onChange={setValue}
/>
</Wrapper>
);
};
export const BasicFewWithDefault = () => {
const options = ["Foo", "Bar", "Baz"];
const [value, setValue] = useActionState("onChange", null);
return (
<Wrapper>
<FieldEnum
label="Foobar"
options={options}
default={"Baz"}
value={value}
onChange={setValue}
/>
</Wrapper>
);
};
export const BasicMany = () => {
const options = ["a", "b", "c", "d", "e", "f", "g", "h", "i", "j"];
const [value, setValue] = useActionState("onChange", "a");
return (
<Wrapper>
<FieldEnum
label="Foobar"
options={options}
value={value}
onChange={setValue}
/>
</Wrapper>
);
};
export const BasicManyWithDefault = () => {
const options = ["a", "b", "c", "d", "e", "f", "g", "h", "i", "j"];
const [value, setValue] = useActionState("onChange", "a");
return (
<Wrapper>
<FieldEnum
label="Foobar"
options={options}
default={"h"}
value={value}
onChange={setValue}
/>
</Wrapper>
);
};
-39
View File
@@ -1,39 +0,0 @@
import React from 'react';
import FieldFunction from '../src/components/FieldFunction';
import {latest} from '@maplibre/maplibre-gl-style-spec'
export default {
title: 'FieldFunction',
component: FieldFunction,
};
export const Basic = () => {
const value = {
"property": "rank",
"type": "categorical",
"default": "#222",
"stops": [
[
{"zoom": 6, "value": ""},
["#777"]
],
[
{"zoom": 10, "value": ""},
["#444"]
]
]
};
return <div style={{width: "360px"}}>
<FieldFunction
onChange={() => {}}
value={value}
errors={[]}
fieldName={"Color"}
fieldType={"color"}
fieldSpec={latest['paint_fill']['fill-color']}
/>
</div>
};
-27
View File
@@ -1,27 +0,0 @@
import React from 'react';
import {useActionState} from './helper';
import FieldMultiInput from '../src/components/FieldMultiInput';
import {Wrapper} from './ui';
export default {
title: 'FieldMultiInput',
component: FieldMultiInput,
};
export const Basic = () => {
const options = [["FOO", "foo"], ["BAR", "bar"], ["BAZ", "baz"]];
const [value, setValue] = useActionState("onChange", "FOO");
return (
<Wrapper>
<FieldMultiInput
label="Foobar"
options={options}
value={value}
onChange={setValue}
/>
</Wrapper>
);
};
-41
View File
@@ -1,41 +0,0 @@
import React from 'react';
import {useActionState} from './helper';
import FieldNumber from '../src/components/FieldNumber';
import {Wrapper} from './ui';
export default {
title: 'FieldNumber',
component: FieldNumber,
};
export const Basic = () => {
const [value, setValue] = useActionState("onChange", 1);
return (
<Wrapper>
<FieldNumber
label="number"
value={value}
onChange={setValue}
/>
</Wrapper>
);
};
export const Range = () => {
const [value, setValue] = useActionState("onChange", 1);
return (
<Wrapper>
<FieldNumber
label="number"
value={value}
onChange={setValue}
min={1}
max={24}
allowRange={true}
rangeStep={1}
/>
</Wrapper>
);
};
-28
View File
@@ -1,28 +0,0 @@
import React from 'react';
import {useActionState} from './helper';
import FieldSelect from '../src/components/FieldSelect';
import {Wrapper} from './ui';
export default {
title: 'FieldSelect',
component: FieldSelect,
};
export const Basic = () => {
const options = [["FOO", "Foo"], ["BAR", "Bar"], ["BAZ", "Baz"]];
const [value, setValue] = useActionState("onChange", "FOO");
return (
<Wrapper>
<FieldSelect
label="Foobar"
options={options}
value={value}
onChange={setValue}
/>
</Wrapper>
);
};
-25
View File
@@ -1,25 +0,0 @@
import React from 'react';
import {useActionState} from './helper';
import FieldString from '../src/components/FieldString';
import {Wrapper} from './ui';
export default {
title: 'FieldString',
component: FieldString,
};
export const Basic = () => {
const [value, setValue] = useActionState("onChange", "Hello world");
return (
<Wrapper>
<FieldString
label="Foobar"
value={value}
onChange={setValue}
/>
</Wrapper>
);
};
-42
View File
@@ -1,42 +0,0 @@
import React from 'react';
import {useActionState} from './helper';
import FieldUrl from '../src/components/FieldUrl';
import {Wrapper} from './ui';
export default {
title: 'FieldUrl',
component: FieldUrl,
};
export const Valid = () => {
const [value, setValue] = useActionState("onChange", "http://example.com");
return (
<Wrapper>
<FieldUrl
label="Foobar"
value={value}
onChange={setValue}
onInput={setValue}
/>
</Wrapper>
);
};
export const Invalid = () => {
const [value, setValue] = useActionState("onChange", "foo");
return (
<Wrapper>
<FieldUrl
label="Foobar"
value={value}
onChange={setValue}
onInput={setValue}
/>
</Wrapper>
);
};
-53
View File
@@ -1,53 +0,0 @@
import React from 'react';
import IconLayer from '../src/components/IconLayer';
import {Wrapper} from './ui';
export default {
title: 'IconLayer',
component: IconLayer,
};
export const IconList = () => {
const types = [
'fill-extrusion',
'raster',
'hillshade',
'heatmap',
'fill',
'background',
'line',
'symbol',
'circle',
'INVALID',
]
return <Wrapper>
<table style={{textAlign: "left"}}>
<thead style={{borderBottom: "solid 1px white"}}>
<tr>
<td>ID</td>
<td>Preview</td>
</tr>
</thead>
<tbody>
{types.map(type => (
<tr>
<td style={{paddingRight: "1em"}}>
<code>{type}</code>
</td>
<td>
<IconLayer type={type} />
</td>
</tr>
))}
</tbody>
</table>
</Wrapper>
};
-42
View File
@@ -1,42 +0,0 @@
import React from 'react';
import {useActionState} from './helper';
import InputArray from '../src/components/InputArray';
import {Wrapper} from './ui';
export default {
title: 'InputArray',
component: InputArray,
};
export const NumberType = () => {
const [value, setValue] = useActionState("onChange", [1,2,3]);
return (
<Wrapper>
<InputArray
type="number"
value={value}
length={3}
onChange={setValue}
/>
</Wrapper>
);
};
export const StringType = () => {
const [value, setValue] = useActionState("onChange", ["a", "b", "c"]);
return (
<Wrapper>
<InputArray
type="string"
value={value}
length={3}
onChange={setValue}
/>
</Wrapper>
);
};
-27
View File
@@ -1,27 +0,0 @@
import React from 'react';
import {useActionState} from './helper';
import InputAutocomplete from '../src/components/InputAutocomplete';
import {InputContainer} from './ui';
export default {
title: 'InputAutocomplete',
component: InputAutocomplete,
};
export const Basic = () => {
const options = [["FOO", "foo"], ["BAR", "bar"], ["BAZ", "baz"]];
const [value, setValue] = useActionState("onChange", "bar");
return (
<InputContainer>
<InputAutocomplete
label="Foobar"
options={options}
value={value}
onChange={setValue}
/>
</InputContainer>
);
};
-18
View File
@@ -1,18 +0,0 @@
import React from 'react';
import InputButton from '../src/components/InputButton';
import {action} from '@storybook/addon-actions';
import {InputContainer} from './ui';
export default {
title: 'InputButton',
component: InputButton,
};
export const Basic = () => (
<InputContainer>
<InputButton onClick={action('onClick')}>
Hello InputButton
</InputButton>
</InputContainer>
);
-39
View File
@@ -1,39 +0,0 @@
import React from 'react';
import {useActionState} from './helper';
import InputCheckbox from '../src/components/InputCheckbox';
import {InputContainer} from './ui';
export default {
title: 'InputCheckbox',
component: InputCheckbox,
};
export const BasicUnchecked = () => {
const [value, setValue] = useActionState("onChange", false);
return (
<InputContainer>
<InputCheckbox
label="Foobar"
value={value}
onChange={setValue}
/>
</InputContainer>
);
};
export const BasicChecked = () => {
const [value, setValue] = useActionState("onChange", true);
return (
<InputContainer>
<InputCheckbox
label="Foobar"
value={value}
onChange={setValue}
/>
</InputContainer>
);
};
-25
View File
@@ -1,25 +0,0 @@
import React from 'react';
import {useActionState} from './helper';
import InputColor from '../src/components/InputColor';
import {InputContainer} from './ui';
export default {
title: 'InputColor',
component: InputColor,
};
export const Basic = () => {
const [color, setColor] = useActionState("onChange", "#ff0000");
return (
<InputContainer>
<InputColor
label="Foobar"
value={color}
onChange={setColor}
/>
</InputContainer>
);
};
-53
View File
@@ -1,53 +0,0 @@
import React from 'react';
import {useActionState} from './helper';
import InputDynamicArray from '../src/components/InputDynamicArray';
import {Wrapper} from './ui';
export default {
title: 'InputDynamicArray',
component: InputDynamicArray,
};
export const NumberType = () => {
const [value, setValue] = useActionState("onChange", [1,2,3]);
return (
<Wrapper>
<InputDynamicArray
type="number"
value={value}
onChange={setValue}
/>
</Wrapper>
);
};
export const UrlType = () => {
const [value, setValue] = useActionState("onChange", ["http://example.com"]);
return (
<Wrapper>
<InputDynamicArray
type="url"
value={value}
onChange={setValue}
/>
</Wrapper>
);
};
export const EnumType = () => {
const [value, setValue] = useActionState("onChange", ["foo"]);
return (
<Wrapper>
<InputDynamicArray
fieldSpec={{values: {"foo": null, "bar": null, "baz": null}}}
type="enum"
value={value}
onChange={setValue}
/>
</Wrapper>
);
};
-75
View File
@@ -1,75 +0,0 @@
import React from 'react';
import {useActionState} from './helper';
import InputEnum from '../src/components/InputEnum';
import {InputContainer} from './ui';
export default {
title: 'InputEnum',
component: InputEnum,
};
export const BasicFew = () => {
const options = ["Foo", "Bar", "Baz"];
const [value, setValue] = useActionState("onChange", "Foo");
return (
<InputContainer>
<InputEnum
options={options}
value={value}
onChange={setValue}
/>
</InputContainer>
);
};
export const BasicFewWithDefault = () => {
const options = ["Foo", "Bar", "Baz"];
const [value, setValue] = useActionState("onChange", null);
return (
<InputContainer>
<InputEnum
options={options}
default={"Baz"}
value={value}
onChange={setValue}
/>
</InputContainer>
);
};
export const BasicMany = () => {
const options = ["a", "b", "c", "d", "e", "f", "g", "h", "i", "j"];
const [value, setValue] = useActionState("onChange", "a");
return (
<InputContainer>
<InputEnum
options={options}
value={value}
onChange={setValue}
/>
</InputContainer>
);
};
export const BasicManyWithDefault = () => {
const options = ["a", "b", "c", "d", "e", "f", "g", "h", "i", "j"];
const [value, setValue] = useActionState("onChange", "a");
return (
<InputContainer>
<InputEnum
options={options}A
default={"h"}
value={value}
onChange={setValue}
/>
</InputContainer>
);
};
-24
View File
@@ -1,24 +0,0 @@
import React from 'react';
import InputJson from '../src/components/InputJson';
import {action} from '@storybook/addon-actions';
import {Wrapper} from './ui';
export default {
title: 'InputJson',
component: InputJson,
};
export const Basic = () => {
const layer = {
id: "background",
type: "background",
};
return <Wrapper>
<InputJson
layer={layer}
onClick={action('onClick')}
/>
</Wrapper>
};
-27
View File
@@ -1,27 +0,0 @@
import React from 'react';
import {useActionState} from './helper';
import InputMultiInput from '../src/components/InputMultiInput';
import {InputContainer} from './ui';
export default {
title: 'InputMultiInput',
component: InputMultiInput,
};
export const Basic = () => {
const options = [["FOO", "foo"], ["BAR", "bar"], ["BAZ", "baz"]];
const [value, setValue] = useActionState("onChange", "FOO");
return (
<InputContainer>
<InputMultiInput
options={options}
value={value}
onChange={setValue}
/>
</InputContainer>
);
};
-42
View File
@@ -1,42 +0,0 @@
import React from 'react';
import {useActionState} from './helper';
import InputNumber from '../src/components/InputNumber';
import {InputContainer} from './ui';
export default {
title: 'InputNumber',
component: InputNumber,
};
export const Basic = () => {
const [value, setValue] = useActionState("onChange", 1);
return (
<InputContainer>
<InputNumber
name="number"
value={value}
onChange={setValue}
/>
</InputContainer>
);
};
export const Range = () => {
const [value, setValue] = useActionState("onChange", 1);
return (
<InputContainer>
<InputNumber
name="number"
value={value}
onChange={setValue}
min={1}
max={24}
allowRange={true}
rangeStep={1}
/>
</InputContainer>
);
};
-28
View File
@@ -1,28 +0,0 @@
import React from 'react';
import {useActionState} from './helper';
import InputSelect from '../src/components/InputSelect';
import {InputContainer} from './ui';
export default {
title: 'InputSelect',
component: InputSelect,
};
export const Basic = () => {
const options = [["FOO", "Foo"], ["BAR", "Bar"], ["BAZ", "Baz"]];
const [value, setValue] = useActionState("onChange", "FOO");
return (
<InputContainer>
<InputSelect
options={options}
value={value}
onChange={setValue}
/>
</InputContainer>
);
};
-67
View File
@@ -1,67 +0,0 @@
import React from 'react';
import {useActionState} from './helper';
import InputString from '../src/components/InputString';
import {InputContainer} from './ui';
export default {
title: 'InputString',
component: InputString,
};
export const Basic = () => {
const [value, setValue] = useActionState("onChange", "Hello world");
return (
<InputContainer>
<InputString
value={value}
onChange={setValue}
/>
</InputContainer>
);
};
export const WithDefault = () => {
const [value, setValue] = useActionState("onChange", null);
return (
<InputContainer>
<InputString
value={value}
default={"Edit me..."}
onChange={setValue}
/>
</InputContainer>
);
};
export const Multiline = () => {
const [value, setValue] = useActionState("onChange", "Hello\nworld");
return (
<InputContainer>
<InputString
multi={true}
value={value}
onChange={setValue}
/>
</InputContainer>
);
};
export const MultilineWithDefault = () => {
const [value, setValue] = useActionState("onChange", null);
return (
<InputContainer>
<InputString
multi={true}
default={"Edit\nme.."}
value={value}
onChange={setValue}
/>
</InputContainer>
);
};
-40
View File
@@ -1,40 +0,0 @@
import React from 'react';
import {useActionState} from './helper';
import InputUrl from '../src/components/InputUrl';
import {InputContainer} from './ui';
export default {
title: 'InputUrl',
component: InputUrl,
};
export const Valid = () => {
const [value, setValue] = useActionState("onChange", "http://example.com");
return (
<InputContainer>
<InputUrl
value={value}
onChange={setValue}
onInput={setValue}
/>
</InputContainer>
);
};
export const Invalid = () => {
const [value, setValue] = useActionState("onChange", "foo");
return (
<InputContainer>
<InputUrl
value={value}
onChange={setValue}
onInput={setValue}
/>
</InputContainer>
);
};
-198
View File
@@ -1,198 +0,0 @@
import React from 'react';
import LayerEditor from '../src/components/LayerEditor';
export default {
title: 'LayerEditor',
component: LayerEditor,
};
export const Background = () => (
<div>
<LayerEditor
layer={{id: "background", type: "background"}}
sources={{}}
vectorLayers={{}}
spec={latest}
errors={[]}
onLayerChanged={() => {}}
onLayerIdChange={() => {}}
onMoveLayer={() => {}}
onLayerDestroy={() => {}}
onLayerCopy={() => {}}
onLayerVisibilityToggle={() => {}}
isFirstLayer={true}
isLastLayer={false}
layerIndex={0}
/>
</div>
);
export const Fill = () => (
<div>
<LayerEditor
layer={{id: "fill", type: "fill", source: "openmaptiles"}}
sources={{}}
vectorLayers={{}}
spec={latest}
errors={[]}
onLayerChanged={() => {}}
onLayerIdChange={() => {}}
onMoveLayer={() => {}}
onLayerDestroy={() => {}}
onLayerCopy={() => {}}
onLayerVisibilityToggle={() => {}}
isFirstLayer={true}
isLastLayer={false}
layerIndex={0}
/>
</div>
);
export const Line = () => (
<div>
<LayerEditor
layer={{id: "line", type: "line", source: "openmaptiles"}}
sources={{}}
vectorLayers={{}}
spec={latest}
errors={[]}
onLayerChanged={() => {}}
onLayerIdChange={() => {}}
onMoveLayer={() => {}}
onLayerDestroy={() => {}}
onLayerCopy={() => {}}
onLayerVisibilityToggle={() => {}}
isFirstLayer={true}
isLastLayer={false}
layerIndex={0}
/>
</div>
);
export const Symbol = () => (
<div>
<LayerEditor
layer={{id: "symbol", type: "symbol", source: "openmaptiles"}}
sources={{}}
vectorLayers={{}}
spec={latest}
errors={[]}
onLayerChanged={() => {}}
onLayerIdChange={() => {}}
onMoveLayer={() => {}}
onLayerDestroy={() => {}}
onLayerCopy={() => {}}
onLayerVisibilityToggle={() => {}}
isFirstLayer={true}
isLastLayer={false}
layerIndex={0}
/>
</div>
);
export const Raster = () => (
<div>
<LayerEditor
layer={{id: "raster", type: "raster", source: "openmaptiles"}}
sources={{}}
vectorLayers={{}}
spec={latest}
errors={[]}
onLayerChanged={() => {}}
onLayerIdChange={() => {}}
onMoveLayer={() => {}}
onLayerDestroy={() => {}}
onLayerCopy={() => {}}
onLayerVisibilityToggle={() => {}}
isFirstLayer={true}
isLastLayer={false}
layerIndex={0}
/>
</div>
);
export const Cirlce = () => (
<div>
<LayerEditor
layer={{id: "circle", type: "circle", source: "openmaptiles"}}
sources={{}}
vectorLayers={{}}
spec={latest}
errors={[]}
onLayerChanged={() => {}}
onLayerIdChange={() => {}}
onMoveLayer={() => {}}
onLayerDestroy={() => {}}
onLayerCopy={() => {}}
onLayerVisibilityToggle={() => {}}
isFirstLayer={true}
isLastLayer={false}
layerIndex={0}
/>
</div>
);
export const FillExtrusion = () => (
<div>
<LayerEditor
layer={{id: "fill-extrusion", type: "fill-extrusion", source: "openmaptiles"}}
sources={{}}
vectorLayers={{}}
spec={latest}
errors={[]}
onLayerChanged={() => {}}
onLayerIdChange={() => {}}
onMoveLayer={() => {}}
onLayerDestroy={() => {}}
onLayerCopy={() => {}}
onLayerVisibilityToggle={() => {}}
isFirstLayer={true}
isLastLayer={false}
layerIndex={0}
/>
</div>
);
export const Heatmap = () => (
<div>
<LayerEditor
layer={{id: "heatmap", type: "heatmap", source: "openmaptiles"}}
sources={{}}
vectorLayers={{}}
spec={latest}
errors={[]}
onLayerChanged={() => {}}
onLayerIdChange={() => {}}
onMoveLayer={() => {}}
onLayerDestroy={() => {}}
onLayerCopy={() => {}}
onLayerVisibilityToggle={() => {}}
isFirstLayer={true}
isLastLayer={false}
layerIndex={0}
/>
</div>
);
export const Hillshade = () => (
<div>
<LayerEditor
layer={{id: "hillshade", type: "hillshade", source: "openmaptiles"}}
sources={{}}
vectorLayers={{}}
spec={latest}
errors={[]}
onLayerChanged={() => {}}
onLayerIdChange={() => {}}
onMoveLayer={() => {}}
onLayerDestroy={() => {}}
onLayerCopy={() => {}}
onLayerVisibilityToggle={() => {}}
isFirstLayer={true}
isLastLayer={false}
layerIndex={0}
/>
</div>
);
-55
View File
@@ -1,55 +0,0 @@
import React from 'react';
import LayerList from '../src/components/LayerList';
import {Wrapper} from './ui';
export default {
title: 'LayerList',
component: LayerList,
};
export const Basic = () => (
<Wrapper>
<div style={{width: "200px"}}>
<LayerList
layers={[
{
id: "background",
type: "background",
},
{
id: "water",
type: "fill",
source: "openmaptiles"
},
{
id: "road_trunk",
type: "line",
source: "openmaptiles"
},
{
id: "road_minor",
type: "line",
source: "openmaptiles"
},
{
id: "building",
type: "fill",
source: "openmaptiles"
},
]}
selectedLayerIndex={0}
onLayersChange={() => {}}
onLayerSelect={() => {}}
onLayerDestroy={() => {}}
onLayerCopy={() => {}}
onLayerVisibilityToggle={() => {}}
onMoveLayer={() => {}}
sources={{}}
errors={[]}
/>
</div>
</Wrapper>
);
-104
View File
@@ -1,104 +0,0 @@
import React from 'react';
import MapMaplibreGl from '../src/components/MapMaplibreGl';
export default {
title: 'MapMaplibreGl',
component: MapMaplibreGl,
};
const mapStyle = {
"version": 8,
"sources": {
"test1": {
"type": "geojson",
"data": {
"type": "FeatureCollection",
"features": [
{
"type": "Feature",
"geometry": {
"type": "Point",
"coordinates": [0, -10]
},
"properties": {}
}
]
}
},
"test2": {
"type": "geojson",
"data": {
"type": "FeatureCollection",
"features": [
{
"type": "Feature",
"geometry": {
"type": "Point",
"coordinates": [15, 10]
},
"properties": {}
}
]
}
},
"test3": {
"type": "geojson",
"data": {
"type": "FeatureCollection",
"features": [
{
"type": "Feature",
"geometry": {
"type": "Point",
"coordinates": [-15, 10]
},
"properties": {}
}
]
}
}
},
"sprite": "",
"glyphs": "https://orangemug.github.io/font-glyphs/glyphs/{fontstack}/{range}.pbf",
"layers": [
{
"id": "test1",
"type": "circle",
"source": "test1",
"paint": {
"circle-radius": 40,
"circle-color": "red"
}
},
{
"id": "test2",
"type": "circle",
"source": "test2",
"paint": {
"circle-radius": 40,
"circle-color": "green"
}
},
{
"id": "test3",
"type": "circle",
"source": "test3",
"paint": {
"circle-radius": 40,
"circle-color": "blue"
}
}
]
}
export const Basic = () => {
return <div style={{height: "100vh", width: "100vw"}}>
<MapMaplibreGl
mapStyle={mapStyle}
inspectModeEnabled={false}
replaceAccessTokens={(s) => s}
/>
</div>
};
-108
View File
@@ -1,108 +0,0 @@
import React from 'react';
import MapOpenLayers from '../src/components/MapOpenLayers';
export default {
title: 'MapOpenLayers',
component: MapOpenLayers,
};
const mapStyle = {
"version": 8,
"sources": {
"test1": {
"type": "geojson",
"data": {
"type": "FeatureCollection",
"features": [
{
"type": "Feature",
"geometry": {
"type": "Point",
"coordinates": [0, -10]
},
"properties": {}
}
]
}
},
"test2": {
"type": "geojson",
"data": {
"type": "FeatureCollection",
"features": [
{
"type": "Feature",
"geometry": {
"type": "Point",
"coordinates": [15, 10]
},
"properties": {}
}
]
}
},
"test3": {
"type": "geojson",
"data": {
"type": "FeatureCollection",
"features": [
{
"type": "Feature",
"geometry": {
"type": "Point",
"coordinates": [-15, 10]
},
"properties": {}
}
]
}
}
},
"sprite": "",
"glyphs": "https://orangemug.github.io/font-glyphs/glyphs/{fontstack}/{range}.pbf",
"layers": [
{
"id": "test1",
"type": "circle",
"source": "test1",
"paint": {
"circle-radius": 40,
"circle-color": "red"
}
},
{
"id": "test2",
"type": "circle",
"source": "test2",
"paint": {
"circle-radius": 40,
"circle-color": "green"
}
},
{
"id": "test3",
"type": "circle",
"source": "test3",
"paint": {
"circle-radius": 40,
"circle-color": "blue"
}
}
]
}
export const Basic = () => {
return <div style={{height: "100vh", width: "100vw", display: "flex"}}>
<MapOpenLayers
mapStyle={mapStyle}
inspectModeEnabled={false}
replaceAccessTokens={(s) => s}
onChange={() => {}}
debugToolbox={true}
/>
</div>
};
-23
View File
@@ -1,23 +0,0 @@
import React from 'react';
import Modal from '../src/components/Modal';
import {Wrapper} from './ui';
export default {
title: 'Modal',
component: Modal,
};
export const Basic = () => (
<Wrapper>
<div style={{maxHeight: "200px"}}>
<Modal title="hello" isOpen={true}>
{[...Array(50).keys()].map(() => {
return <p>Some text</p>
})}
</Modal>
</div>
</Wrapper>
);
-22
View File
@@ -1,22 +0,0 @@
import React from 'react';
import ModalAdd from '../src/components/ModalAdd';
import {Wrapper} from './ui';
export default {
title: 'ModalAdd',
component: ModalAdd,
};
export const Basic = () => (
<Wrapper>
<div style={{maxHeight: "200px"}}>
<ModalAdd
layers={[]}
sources={{}}
isOpen={true}
/>
</div>
</Wrapper>
);
-25
View File
@@ -1,25 +0,0 @@
import React from 'react';
import ModalDebug from '../src/components/ModalDebug';
import {Wrapper} from './ui';
export default {
title: 'ModalDebug',
component: ModalDebug,
};
export const Basic = () => (
<Wrapper>
<div style={{maxHeight: "200px"}}>
<ModalDebug
isOpen={true}
renderer="mlgljs"
maplibreGlDebugOptions={{}}
mapView={{zoom: 1, center: {lat: 0, lng: 0}}}
/>
</div>
</Wrapper>
);

Some files were not shown because too many files have changed in this diff Show More