diff --git a/.eslintrc b/.eslintrc deleted file mode 100644 index d85160a0d..000000000 --- a/.eslintrc +++ /dev/null @@ -1,46 +0,0 @@ -{ - "root": true, - "env": { - "browser": true, - "es2020": true - }, - "extends": [ - "eslint:recommended", - "plugin:react/recommended", - "plugin:react/jsx-runtime", - "plugin:react-hooks/recommended", - ], - "ignorePatterns": [ - "dist" - ], - "parser": "@typescript-eslint/parser", - "parserOptions": { - "ecmaVersion": "latest", - "sourceType": "module" - }, - "settings": { - "react": { "version": "16.4" } - }, - "plugins": [ - "@typescript-eslint", - "react-refresh"], - "rules": { - "react-refresh/only-export-components": [ - "warn", - { "allowConstantExport": true } - ], - "@typescript-eslint/no-unused-vars": [ - "warn", - { "argsIgnorePattern": "^_" } - ], - "no-unused-vars": "off", - "react/prop-types": ["off"], - // Disable no-undef. It's covered by @typescript-eslint - "no-undef": "off", - "indent": ["error", 2], - "no-var": ["error"] - }, - "globals": { - "global": "readonly" - } -} diff --git a/.github/dependabot.yml b/.github/dependabot.yml index 2e98e0e14..8011837db 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -1,14 +1,17 @@ +# To get started with Dependabot version updates, you'll need to specify which +# package ecosystems to update and where the package manifests are located. +# Please see the documentation for all configuration options: +# https://help.github.com/github/administering-a-repository/configuration-options-for-dependency-updates + version: 2 updates: - - package-ecosystem: "npm" - directory: "/" + - package-ecosystem: "npm" # See documentation for possible values + directory: "/" # Location of package manifests schedule: interval: "daily" - open-pull-requests-limit: 2 + open-pull-requests-limit: 20 versioning-strategy: increase - package-ecosystem: "github-actions" directory: "/" schedule: interval: "daily" - open-pull-requests-limit: 2 - versioning-strategy: increase diff --git a/.github/workflows/auto-merge-dependabot.yml b/.github/workflows/auto-merge-dependabot.yml new file mode 100644 index 000000000..697721a9d --- /dev/null +++ b/.github/workflows/auto-merge-dependabot.yml @@ -0,0 +1,26 @@ +name: Automerge Dependabot + +on: pull_request + +permissions: write-all + +jobs: + dependabot: + runs-on: ubuntu-latest + if: ${{ github.actor == 'dependabot[bot]' }} + steps: + - name: Dependabot metadata + id: metadata + uses: dependabot/fetch-metadata@v2.2.0 + with: + github-token: "${{ secrets.GITHUB_TOKEN }}" + - name: Approve Dependabot PRs + run: gh pr review --approve "$PR_URL" + env: + PR_URL: ${{github.event.pull_request.html_url}} + GITHUB_TOKEN: ${{secrets.GITHUB_TOKEN}} + - name: Enable auto-merge for Dependabot PRs + run: gh pr merge --auto --squash "$PR_URL" + env: + PR_URL: ${{github.event.pull_request.html_url}} + GITHUB_TOKEN: ${{secrets.GITHUB_TOKEN}} diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 49e0e5009..c31789cfc 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -107,7 +107,7 @@ jobs: start: npm run start browser: ${{ matrix.browser }} - name: Upload coverage reports to Codecov - uses: codecov/codecov-action@v3 + uses: codecov/codecov-action@v5 with: files: ${{ github.workspace }}/.nyc_output/out.json verbose: true diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml new file mode 100644 index 000000000..5bd7ffe15 --- /dev/null +++ b/.github/workflows/codeql-analysis.yml @@ -0,0 +1,70 @@ +# For most projects, this workflow file will not need changing; you simply need +# to commit it to your repository. +# +# You may wish to alter this file to override the set of languages analyzed, +# or to provide custom queries or build logic. +# +# ******** NOTE ******** +# We have attempted to detect the languages in your repository. Please check +# the `language` matrix defined below to confirm you have the correct set of +# supported CodeQL languages. +# +name: "CodeQL" + +on: + push: + branches: [ main ] + pull_request: + # The branches below must be a subset of the branches above + branches: [ main ] + schedule: + - cron: '17 0 * * 6' + +jobs: + analyze: + name: Analyze + runs-on: ubuntu-latest + permissions: + actions: read + contents: read + security-events: write + + strategy: + fail-fast: false + matrix: + language: [ 'javascript' ] + # CodeQL supports [ 'cpp', 'csharp', 'go', 'java', 'javascript', 'python', 'ruby' ] + # Learn more about CodeQL language support at https://git.io/codeql-language-support + + steps: + - name: Checkout repository + uses: actions/checkout@v4 + + # Initializes the CodeQL tools for scanning. + - name: Initialize CodeQL + uses: github/codeql-action/init@v3 + with: + languages: ${{ matrix.language }} + # If you wish to specify custom queries, you can do so here or in a config file. + # By default, queries listed here will override any specified in a config file. + # Prefix the list here with "+" to use these queries and those in the config file. + # queries: ./path/to/local/query, your-org/your-repo/queries@main + + # Autobuild attempts to build any compiled languages (C/C++, C#, or Java). + # If this step fails, then you should remove it and run the build manually (see below) + - name: Autobuild + uses: github/codeql-action/autobuild@v3 + + # ℹī¸ Command-line programs to run using the OS shell. + # 📚 https://git.io/JvXDl + + # ✏ī¸ If the Autobuild fails above, remove it and uncomment the following three lines + # and modify them (or add more) to build your code if your project + # uses a compiled language + + #- run: | + # make bootstrap + # make release + + - name: Perform CodeQL Analysis + uses: github/codeql-action/analyze@v3 diff --git a/.github/workflows/create-bump-version-pr.yml b/.github/workflows/create-bump-version-pr.yml index 468b5b1ff..4b6797197 100644 --- a/.github/workflows/create-bump-version-pr.yml +++ b/.github/workflows/create-bump-version-pr.yml @@ -32,7 +32,7 @@ jobs: ./build/bump-version-changelog.js ${{ inputs.version }} - name: Create Pull Request - uses: peter-evans/create-pull-request@v6 + uses: peter-evans/create-pull-request@v7 with: commit-message: Bump version to ${{ inputs.version }} branch: bump-version-to-${{ inputs.version }} diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml index 4242b02a6..4a8b5c3cc 100644 --- a/.github/workflows/deploy.yml +++ b/.github/workflows/deploy.yml @@ -24,7 +24,7 @@ jobs: run: npm run build - name: Upload to GitHub Pages - uses: peaceiris/actions-gh-pages@v3 + uses: peaceiris/actions-gh-pages@v4 with: github_token: ${{ secrets.GITHUB_TOKEN }} publish_dir: dist diff --git a/.nvmrc b/.nvmrc index ca0a999a4..fb0a13554 100644 --- a/.nvmrc +++ b/.nvmrc @@ -1 +1 @@ -18.19 \ No newline at end of file +22.13 diff --git a/CHANGELOG.md b/CHANGELOG.md index 2421aab63..a7d154029 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,10 +6,15 @@ - Add scheme type options for vector/raster tile - Add `tileSize` field for raster and raster-dem tile sources - Update Protomaps Light gallery style to v4 -- Add support to edit local files on the file system +- Add support to edit local files on the file system if supported by the browser +- Upgrade to MapLibre LG JS v5 +- Upgrade Vite 6 and Cypress 14 ([#970](https://github.com/maplibre/maputnik/pull/970)) +- Upgrade OpenLayers from v6 to v10 - _...Add new stuff here..._ ### 🐞 Bug fixes + +- Fix incorrect handing of network error response (#944) - _...Add new stuff here..._ ## 2.1.1 diff --git a/build/release-notes.js b/build/release-notes.js index 8e2e372a9..0cf331a6a 100755 --- a/build/release-notes.js +++ b/build/release-notes.js @@ -20,14 +20,14 @@ const changelog = fs.readFileSync(changelogPath, 'utf8'); */ const regex = /^## (\d+\.\d+\.\d+.*?)\n(.+?)(?=\n^## \d+\.\d+\.\d+.*?\n)/gms; -let releaseNotes = []; +const releaseNotes = []; let match; // eslint-disable-next-line no-cond-assign while (match = regex.exec(changelog)) { - releaseNotes.push({ - 'version': match[1], - 'changelog': match[2].trim(), - }); + releaseNotes.push({ + 'version': match[1], + 'changelog': match[2].trim(), + }); } const latest = releaseNotes[0]; @@ -44,5 +44,4 @@ const templatedReleaseNotes = `${header} ${latest.changelog}`; -// eslint-disable-next-line eol-last process.stdout.write(templatedReleaseNotes.trimEnd()); diff --git a/cypress/e2e/accessibility.cy.ts b/cypress/e2e/accessibility.cy.ts index 6c2219ff8..d27b19397 100644 --- a/cypress/e2e/accessibility.cy.ts +++ b/cypress/e2e/accessibility.cy.ts @@ -1,7 +1,7 @@ import { MaputnikDriver } from "./maputnik-driver"; describe("accessibility", () => { - let { beforeAndAfter, get, when, then } = new MaputnikDriver(); + const { beforeAndAfter, get, when, then } = new MaputnikDriver(); beforeAndAfter(); describe("skip links", () => { diff --git a/cypress/e2e/history.cy.ts b/cypress/e2e/history.cy.ts index 4f865ce4f..3934ee616 100644 --- a/cypress/e2e/history.cy.ts +++ b/cypress/e2e/history.cy.ts @@ -1,7 +1,7 @@ import { MaputnikDriver } from "./maputnik-driver"; describe("history", () => { - let { beforeAndAfter, when, get, then } = new MaputnikDriver(); + const { beforeAndAfter, when, get, then } = new MaputnikDriver(); beforeAndAfter(); let undoKeyCombo: string; diff --git a/cypress/e2e/i18n.cy.ts b/cypress/e2e/i18n.cy.ts index 066ad1ed9..23d1a407f 100644 --- a/cypress/e2e/i18n.cy.ts +++ b/cypress/e2e/i18n.cy.ts @@ -1,7 +1,7 @@ import { MaputnikDriver } from "./maputnik-driver"; describe("i18n", () => { - let { beforeAndAfter, get, when, then } = new MaputnikDriver(); + const { beforeAndAfter, get, when, then } = new MaputnikDriver(); beforeAndAfter(); describe("language detector", () => { diff --git a/cypress/e2e/keyboard.cy.ts b/cypress/e2e/keyboard.cy.ts index 39ab1127c..2f52b1e89 100644 --- a/cypress/e2e/keyboard.cy.ts +++ b/cypress/e2e/keyboard.cy.ts @@ -1,7 +1,7 @@ import { MaputnikDriver } from "./maputnik-driver"; describe("keyboard", () => { - let { beforeAndAfter, given, when, get, then } = new MaputnikDriver(); + const { beforeAndAfter, given, when, get, then } = new MaputnikDriver(); beforeAndAfter(); describe("shortcuts", () => { beforeEach(() => { diff --git a/cypress/e2e/layers.cy.ts b/cypress/e2e/layers.cy.ts index 4be2949fe..e782a57ed 100644 --- a/cypress/e2e/layers.cy.ts +++ b/cypress/e2e/layers.cy.ts @@ -2,7 +2,7 @@ import { v1 as uuid } from "uuid"; import { MaputnikDriver } from "./maputnik-driver"; describe("layers", () => { - let { beforeAndAfter, get, when, then } = new MaputnikDriver(); + const { beforeAndAfter, get, when, then } = new MaputnikDriver(); beforeAndAfter(); beforeEach(() => { when.setStyle("both"); @@ -101,7 +101,7 @@ describe("layers", () => { }); describe("background", () => { it("add", () => { - let id = when.modal.fillLayers({ + const id = when.modal.fillLayers({ type: "background", }); then(get.styleFromLocalStorage()).shouldDeepNestedInclude({ @@ -117,7 +117,7 @@ describe("layers", () => { describe("modify", () => { function createBackground() { // Setup - let id = uuid(); + const id = uuid(); when.selectWithin("add-layer.layer-type", "background"); when.setValue("add-layer.layer-id.input", "background:" + id); @@ -139,11 +139,11 @@ describe("layers", () => { describe("layer", () => { it("expand/collapse"); it("id", () => { - let bgId = createBackground(); + const bgId = createBackground(); when.click("layer-list-item:background:" + bgId); - let id = uuid(); + const id = uuid(); when.setValue("layer-editor.layer-id.input", "foobar:" + id); when.click("min-zoom"); @@ -219,7 +219,7 @@ describe("layers", () => { describe("comments", () => { let bgId: string; - let comment = "42"; + const comment = "42"; beforeEach(() => { bgId = createBackground(); @@ -320,11 +320,11 @@ describe("layers", () => { // TODO it.skip("parse error", () => { - let bgId = createBackground(); + const bgId = createBackground(); when.click("layer-list-item:background:" + bgId); - let errorSelector = ".CodeMirror-lint-marker-error"; + const errorSelector = ".CodeMirror-lint-marker-error"; then(get.elementByTestId(errorSelector)).shouldNotExist(); when.click(".CodeMirror"); @@ -339,7 +339,7 @@ describe("layers", () => { describe("fill", () => { it("add", () => { - let id = when.modal.fillLayers({ + const id = when.modal.fillLayers({ type: "fill", layer: "example", }); @@ -361,7 +361,7 @@ describe("layers", () => { describe("line", () => { it("add", () => { - let id = when.modal.fillLayers({ + const id = when.modal.fillLayers({ type: "line", layer: "example", }); @@ -385,7 +385,7 @@ describe("layers", () => { describe("symbol", () => { it("add", () => { - let id = when.modal.fillLayers({ + const id = when.modal.fillLayers({ type: "symbol", layer: "example", }); @@ -404,7 +404,7 @@ describe("layers", () => { describe("raster", () => { it("add", () => { - let id = when.modal.fillLayers({ + const id = when.modal.fillLayers({ type: "raster", layer: "raster", }); @@ -423,7 +423,7 @@ describe("layers", () => { describe("circle", () => { it("add", () => { - let id = when.modal.fillLayers({ + const id = when.modal.fillLayers({ type: "circle", layer: "example", }); @@ -442,7 +442,7 @@ describe("layers", () => { describe("fill extrusion", () => { it("add", () => { - let id = when.modal.fillLayers({ + const id = when.modal.fillLayers({ type: "fill-extrusion", layer: "example", }); @@ -494,4 +494,33 @@ describe("layers", () => { ).shouldBeVisible(); }); }); + + + describe("layereditor jsonlint should error", ()=>{ + + it("add", () => { + const id = when.modal.fillLayers({ + type: "circle", + layer: "example", + }); + + then(get.styleFromLocalStorage()).shouldDeepNestedInclude({ + layers: [ + { + id: id, + type: "circle", + source: "example", + }, + ], + }); + + const sourceText = get.elementByText('"source"'); + + sourceText.click(); + sourceText.type("\""); + + const error = get.element('.CodeMirror-lint-marker-error'); + error.should('exist'); + }); + }); }); diff --git a/cypress/e2e/map.cy.ts b/cypress/e2e/map.cy.ts index c8c11cb34..45befb8a2 100644 --- a/cypress/e2e/map.cy.ts +++ b/cypress/e2e/map.cy.ts @@ -1,11 +1,11 @@ import { MaputnikDriver } from "./maputnik-driver"; describe("map", () => { - let { beforeAndAfter, get, when, then } = new MaputnikDriver(); + const { beforeAndAfter, get, when, then } = new MaputnikDriver(); beforeAndAfter(); describe("zoom level", () => { it("via url", () => { - let zoomLevel = 12.37; + const zoomLevel = 12.37; when.setStyle("geojson", zoomLevel); then(get.elementByTestId("maplibre:ctrl-zoom")).shouldBeVisible(); then(get.elementByTestId("maplibre:ctrl-zoom")).shouldContainText( @@ -14,7 +14,7 @@ describe("map", () => { }); it("via map controls", () => { - let zoomLevel = 12.37; + const zoomLevel = 12.37; when.setStyle("geojson", zoomLevel); then(get.elementByTestId("maplibre:ctrl-zoom")).shouldBeVisible(); when.clickZoomIn(); diff --git a/cypress/e2e/modal-driver.ts b/cypress/e2e/modal-driver.ts index 8a8b992cc..8d3b90a21 100644 --- a/cypress/e2e/modal-driver.ts +++ b/cypress/e2e/modal-driver.ts @@ -8,8 +8,8 @@ export default class ModalDriver { fillLayers: (opts: { type: string; layer?: string; id?: string }) => { // Having logic in test code is an anti pattern. // This should be splitted to multiple single responsibility functions - let type = opts.type; - let layer = opts.layer; + const type = opts.type; + const layer = opts.layer; let id; if (opts.id) { id = opts.id; diff --git a/cypress/e2e/modals.cy.ts b/cypress/e2e/modals.cy.ts index a9dcb9a58..4e3c6e6de 100644 --- a/cypress/e2e/modals.cy.ts +++ b/cypress/e2e/modals.cy.ts @@ -1,7 +1,7 @@ import { MaputnikDriver } from "./maputnik-driver"; describe("modals", () => { - let { beforeAndAfter, when, get, then } = new MaputnikDriver(); + const { beforeAndAfter, when, get, then } = new MaputnikDriver(); beforeAndAfter(); beforeEach(() => { @@ -25,7 +25,7 @@ describe("modals", () => { describe("when click open url", () => { beforeEach(() => { - let styleFileUrl = get.exampleFileUrl(); + const styleFileUrl = get.exampleFileUrl(); when.setValue("modal:open.url.input", styleFileUrl); when.click("modal:open.url.button"); @@ -70,7 +70,7 @@ describe("modals", () => { it("public source"); it("add new source", () => { - let sourceId = "n1z2v3r"; + const sourceId = "n1z2v3r"; when.setValue("modal:sources.add.source_id", sourceId); when.select("modal:sources.add.source_type", "tile_vector"); when.select("modal:sources.add.scheme_type", "tms"); @@ -102,7 +102,7 @@ describe("modals", () => { }); it("add new raster source", () => { - let sourceId = "rastertest"; + const sourceId = "rastertest"; when.setValue("modal:sources.add.source_id", sourceId); when.select("modal:sources.add.source_type", "tile_raster"); when.select("modal:sources.add.scheme_type", "xyz"); @@ -177,7 +177,7 @@ describe("modals", () => { }); }); it("glyphs url", () => { - let glyphsUrl = "http://example.com/{fontstack}/{range}.pbf"; + const glyphsUrl = "http://example.com/{fontstack}/{range}.pbf"; when.setValue("modal:settings.glyphs", glyphsUrl); when.click("modal:settings.name"); then(get.styleFromLocalStorage()).shouldDeepNestedInclude({ @@ -186,7 +186,7 @@ describe("modals", () => { }); it("maptiler access token", () => { - let apiKey = "testing123"; + const apiKey = "testing123"; when.setValue( "modal:settings.maputnik:openmaptiles_access_token", apiKey @@ -200,7 +200,7 @@ describe("modals", () => { }); it("thunderforest access token", () => { - let apiKey = "testing123"; + const apiKey = "testing123"; when.setValue( "modal:settings.maputnik:thunderforest_access_token", apiKey @@ -212,7 +212,7 @@ describe("modals", () => { }); it("stadia access token", () => { - let apiKey = "testing123"; + const apiKey = "testing123"; when.setValue( "modal:settings.maputnik:stadia_access_token", apiKey diff --git a/eslint.config.js b/eslint.config.js new file mode 100644 index 000000000..4800f10a1 --- /dev/null +++ b/eslint.config.js @@ -0,0 +1,61 @@ +import eslint from '@eslint/js'; +import tseslint from 'typescript-eslint'; +import reactPlugin from 'eslint-plugin-react'; +import reactHooksPlugin from 'eslint-plugin-react-hooks'; +import reactRefreshPlugin from 'eslint-plugin-react-refresh'; + +export default tseslint.config({ + extends: [ + eslint.configs.recommended, + tseslint.configs.recommended, + ], + files: ['**/*.{js,jsx,ts,tsx}'], + ignores: [ + "dist/**/*", + ], + languageOptions: { + ecmaVersion: 2024, + sourceType: 'module', + globals: { + global: 'readonly' + } + }, + settings: { + react: { version: '18.2' } + }, + plugins: { + 'react': reactPlugin, + 'react-hooks': reactHooksPlugin, + 'react-refresh': reactRefreshPlugin + }, + rules: { + 'react-refresh/only-export-components': [ + 'warn', + { allowConstantExport: true } + ], + "@typescript-eslint/no-explicit-any": "off", + '@typescript-eslint/no-unused-vars': [ + 'warn', + { + varsIgnorePattern: '^_', + caughtErrors: 'all', + caughtErrorsIgnorePattern: '^_', + argsIgnorePattern: '^_' + } + ], + 'no-unused-vars': 'off', + 'react/prop-types': 'off', + 'no-undef': 'off', + 'indent': ['error', 2], + 'no-var': 'error', + '@typescript-eslint/no-non-null-asserted-optional-chain': 'off', + '@typescript-eslint/ban-ts-comment': 'off', + '@typescript-eslint/no-empty-object-type': 'off', + + }, + linterOptions: { + reportUnusedDisableDirectives: true, + noInlineConfig: false + } +} +) diff --git a/i18next-parser.config.ts b/i18next-parser.config.ts index 7701b911b..8072755ae 100644 --- a/i18next-parser.config.ts +++ b/i18next-parser.config.ts @@ -10,7 +10,7 @@ export default { keySeparator: false, namespaceSeparator: false, - defaultValue: (locale, ns, key) => { + defaultValue: (_locale, _ns, _key) => { // The default value is a string that indicates that the string is not translated. return '__STRING_NOT_TRANSLATED__'; } diff --git a/src/components/InputUrl.tsx b/src/components/InputUrl.tsx index a11239dba..7353e2e39 100644 --- a/src/components/InputUrl.tsx +++ b/src/components/InputUrl.tsx @@ -15,7 +15,7 @@ function validate(url: string, t: TFunction): JSX.Element | undefined { const urlObj = new URL(url); return urlObj.protocol; } - catch (err) { + catch (_err) { return undefined; } }; diff --git a/src/components/LayerList.tsx b/src/components/LayerList.tsx index a066aa8df..9495cae70 100644 --- a/src/components/LayerList.tsx +++ b/src/components/LayerList.tsx @@ -318,14 +318,7 @@ class LayerListContainerInternal extends React.Component ) type LayerListProps = LayerListContainerProps & { diff --git a/src/components/MapMaplibreGl.tsx b/src/components/MapMaplibreGl.tsx index 833aace3c..e15bf5066 100644 --- a/src/components/MapMaplibreGl.tsx +++ b/src/components/MapMaplibreGl.tsx @@ -104,7 +104,7 @@ class MapMaplibreGlInternal extends React.Component { - tokenizedStyle () { + tokenizedStyle() { return format( style.stripAccessTokens( style.replaceAccessTokens(this.props.mapStyle) @@ -37,8 +38,8 @@ class ModalExportInternal extends React.Component { ); } - exportName () { - if(this.props.mapStyle.name) { + exportName() { + if (this.props.mapStyle.name) { return Slugify(this.props.mapStyle.name, { replacement: '_', remove: /[*\-+~.()'"!:]/g, @@ -86,6 +87,15 @@ class ModalExportInternal extends React.Component { async saveStyle() { const tokenStyle = this.tokenizedStyle(); + // it is not guaranteed that the File System Access API is available on all + // browsers. If the function is not available, a fallback behavior is used. + if (!showSaveFilePickerAvailable) { + const blob = new Blob([tokenStyle], {type: "application/json;charset=utf-8"}); + const exportName = this.exportName(); + saveAs(blob, exportName + ".json"); + return; + } + let fileHandle = this.props.fileHandle; if (fileHandle == null) { fileHandle = await this.createFileHandle(); @@ -112,12 +122,12 @@ class ModalExportInternal extends React.Component { this.props.onOpenToggle(); } - async createFileHandle() : Promise { + async createFileHandle(): Promise { const pickerOpts: SaveFilePickerOptions = { types: [ { description: "json", - accept: { "application/json": [".json"] }, + accept: {"application/json": [".json"]}, }, ], suggestedName: this.exportName(), @@ -179,23 +189,19 @@ class ModalExportInternal extends React.Component {
- - + + {t("Save")} - - - {t("Save as")} - - - - + {showSaveFilePickerAvailable && ( + + + {t("Save as")} + + )} + + + {t("Create HTML")}
diff --git a/src/components/ModalOpen.tsx b/src/components/ModalOpen.tsx index 2761d14ee..11c84e21f 100644 --- a/src/components/ModalOpen.tsx +++ b/src/components/ModalOpen.tsx @@ -1,6 +1,7 @@ import React, { FormEvent } from 'react' import {MdFileUpload} from 'react-icons/md' import {MdAddCircleOutline} from 'react-icons/md' +import FileReaderInput, { Result } from 'react-file-reader-input' import { Trans, WithTranslation, withTranslation } from 'react-i18next'; import ModalLoading from './ModalLoading' @@ -168,6 +169,32 @@ class ModalOpenInternal extends React.Component { + const [, file] = files[0]; + const reader = new FileReader(); + this.clearError(); + + reader.readAsText(file, "UTF-8"); + reader.onload = e => { + let mapStyle; + try { + mapStyle = JSON.parse(e.target?.result as string) + } + catch(err) { + this.setState({ + error: (err as Error).toString() + }); + return; + } + mapStyle = style.ensureStyleValidity(mapStyle) + this.props.onStyleOpen(mapStyle); + this.onOpenToggle(); + } + reader.onerror = e => console.log(e.target); + } + onOpenToggle() { this.setState({ styleUrl: "" @@ -217,10 +244,16 @@ class ModalOpenInternal extends React.Component{t("Open local Style")}

{t("Open a local JSON style from your computer.")}

- {t("Open Style")} - + {typeof window.showOpenFilePicker === "function" ? ( + {t("Open Style")} + + ) : ( + + {t("Open Style")} + + )}
diff --git a/src/components/SmallError.scss b/src/components/SmallError.scss index 9b8d4a705..0b99f113b 100644 --- a/src/components/SmallError.scss +++ b/src/components/SmallError.scss @@ -1,7 +1,7 @@ -@import '../styles/vars'; +@use '../styles/vars'; .SmallError { color: #E57373; - font-size: $font-size-6; - margin-top: $margin-2 + font-size: vars.$font-size-6; + margin-top: vars.$margin-2 } diff --git a/src/libs/codemirror-mgl.ts b/src/libs/codemirror-mgl.ts index 66e3d6bfd..dcff7e439 100644 --- a/src/libs/codemirror-mgl.ts +++ b/src/libs/codemirror-mgl.ts @@ -1,5 +1,4 @@ -// @ts-ignore - this is a fork of jsonlint -import jsonlint from 'jsonlint'; +import {parse} from '@prantlf/jsonlint'; import CodeMirror, { MarkerRange } from 'codemirror'; import jsonToAst from 'json-to-ast'; import {expression, validateStyleMin} from '@maplibre/maplibre-gl-style-spec'; @@ -15,49 +14,45 @@ CodeMirror.defineMode("mgl", (config, parserConfig) => { ); }); -CodeMirror.registerHelper("lint", "json", (text: string) => { - const found: MarkerRangeWithMessage[] = []; - // NOTE: This was modified from the original to remove the global, also the - // old jsonlint API was 'jsonlint.parseError' its now - // 'jsonlint.parser.parseError' - (jsonlint as any).parser.parseError = (str: string, hash: any) => { - const loc = hash.loc; - found.push({ - from: CodeMirror.Pos(loc.first_line - 1, loc.first_column), - to: CodeMirror.Pos(loc.last_line - 1, loc.last_column), - message: str - }); - }; +function tryToParse(text: string) { + const found: MarkerRangeWithMessage[] = []; try { - jsonlint.parse(text); + parse(text); } - catch(e) { - // Do nothing we catch the error above + catch(err: any) { + + const errorMatch = err.toString().match(/line (\d+), column (\d+)/); + if (errorMatch) { + const loc = { + first_line: parseInt(errorMatch[1], 10), + first_column: parseInt(errorMatch[2], 10), + last_line: parseInt(errorMatch[1], 10), + last_column: parseInt(errorMatch[2], 10) + }; + + // const loc = hash.loc; + found.push({ + from: CodeMirror.Pos(loc.first_line - 1, loc.first_column), + to: CodeMirror.Pos(loc.last_line - 1, loc.last_column), + message: err + }); + } } + return found; +} + +CodeMirror.registerHelper("lint", "json", (text: string) => { + return tryToParse(text); }); CodeMirror.registerHelper("lint", "mgl", (text: string, opts: any, doc: any) => { - const found: MarkerRangeWithMessage[] = []; - const {parser} = jsonlint as any; - const {context} = opts; + + const found: MarkerRangeWithMessage[] = tryToParse(text); - parser.parseError = (str: string, hash: any) => { - const loc = hash.loc; - found.push({ - from: CodeMirror.Pos(loc.first_line - 1, loc.first_column), - to: CodeMirror.Pos(loc.last_line - 1, loc.last_column), - message: str - }); - }; - try { - parser.parse(text); - } - catch (e) { - // ignore errors - } + const {context} = opts; if (found.length > 0) { // JSON invalid so don't go any further diff --git a/src/libs/metadata.ts b/src/libs/metadata.ts index f724661f4..dcc012431 100644 --- a/src/libs/metadata.ts +++ b/src/libs/metadata.ts @@ -5,14 +5,17 @@ function loadJSON(url: string, defaultValue: any, cb: (...args: any[]) => void) mode: 'cors', credentials: "same-origin" }) - .then(function(response) { + .then((response) => { + if (!response.ok) { + throw new Error('Failed to load metadata for ' + url); + } return response.json(); }) - .then(function(body) { + .then((body) => { cb(body) }) - .catch(function() { - console.warn('Can not metadata for ' + url) + .catch(() => { + console.warn('Can not load metadata for ' + url + ', using default value ' + defaultValue); cb(defaultValue) }) } diff --git a/src/libs/urlopen.ts b/src/libs/urlopen.ts index 8a8870396..dba2246ec 100644 --- a/src/libs/urlopen.ts +++ b/src/libs/urlopen.ts @@ -1,4 +1,3 @@ -// @ts-ignore import style from './style' export function initialStyleUrl() { diff --git a/src/styles/_base.scss b/src/styles/_base.scss index 0c97d23e8..477185e25 100644 --- a/src/styles/_base.scss +++ b/src/styles/_base.scss @@ -1,3 +1,5 @@ +@use "vars"; + @font-face { font-family: 'Roboto'; src: url('../fonts/Roboto-Regular.ttf') format('truetype'); @@ -15,8 +17,8 @@ } html { - color: $color-white; - font-size: $font-size-5; + color: vars.$color-white; + font-size: vars.$font-size-5; box-sizing: border-box; } @@ -32,34 +34,34 @@ body { } p { - font-size: $font-size-6; - margin-top: $margin-2; - margin-bottom: $margin-2; - color: $color-lowgray; + font-size: vars.$font-size-6; + margin-top: vars.$margin-2; + margin-bottom: vars.$margin-2; + color: vars.$color-lowgray; line-height: 1.3; } h1 { - font-size: $font-size-2; - margin-bottom: $margin-3; + font-size: vars.$font-size-2; + margin-bottom: vars.$margin-3; font-weight: bold; } h2 { - font-size: $font-size-3; - margin-bottom: $margin-3; + font-size: vars.$font-size-3; + margin-bottom: vars.$margin-3; font-weight: bold; } h3 { - font-size: $font-size-4; - margin-bottom: $margin-3; + font-size: vars.$font-size-4; + margin-bottom: vars.$margin-3; font-weight: bold; } h4 { - font-size: $font-size-5; - margin-bottom: $margin-3; + font-size: vars.$font-size-5; + margin-bottom: vars.$margin-3; } input:focus, @@ -68,12 +70,12 @@ textarea:focus, button:focus, .maputnik-toolbar-link:focus, select:focus { - color: $color-white; + color: vars.$color-white; outline: #8e8e8e auto 1px; } label:hover { - color: $color-white; + color: vars.$color-white; } .clearfix { diff --git a/src/styles/_codemirror.scss b/src/styles/_codemirror.scss index 7e2e20e20..b9ce50fe0 100644 --- a/src/styles/_codemirror.scss +++ b/src/styles/_codemirror.scss @@ -1,3 +1,5 @@ +@use "vars"; + .CodeMirror-lint-tooltip { z-index: 2000 !important; } @@ -55,7 +57,7 @@ .cm-s-maputnik .CodeMirror-matchingbracket { background: hsla(223, 12%, 35%, 1); - color: $color-white !important; + color: vars.$color-white !important; } .cm-s-maputnik .CodeMirror-nonmatchingbracket { @@ -79,7 +81,7 @@ z-index: 99999; padding: 0.3em 0.5em; background: hsla(0, 0%, 0%, 0.3); - color: $color-lowgray; + color: vars.$color-lowgray; border-bottom-left-radius: 2px; transition: opacity 320ms ease; opacity: 0; diff --git a/src/styles/_components.scss b/src/styles/_components.scss index e7d8f4a7a..8f61c3db0 100644 --- a/src/styles/_components.scss +++ b/src/styles/_components.scss @@ -1,9 +1,12 @@ +@use "mixins"; +@use "vars"; + @use 'sass:color'; // MAP .maputnik-map__container { background: white; display: flex; - width: $layout-map-width; + width: vars.$layout-map-width; &--error { align-items: center; @@ -36,7 +39,7 @@ &-wrapper { display: inline-block; box-sizing: border-box; - font-size: $font-size-6; + font-size: vars.$font-size-6; line-height: 2; user-select: none; position: relative; @@ -45,9 +48,9 @@ &-popup { display: none; - color: $color-lowgray; - background-color: $color-gray; - padding: $margin-2; + color: vars.$color-lowgray; + background-color: vars.$color-gray; + padding: vars.$margin-2; font-size: 10px; position: absolute; top: 20px; @@ -60,7 +63,7 @@ &-button { opacity: 0; pointer-events: none; - background: $color-black; + background: vars.$color-black; color: white; border: none; padding: 4px; @@ -77,11 +80,11 @@ } .maputnik-doc-inline { - color: $color-lowgray; - background-color: $color-gray; - padding: $margin-2; + color: vars.$color-lowgray; + background-color: vars.$color-gray; + padding: vars.$margin-2; font-size: 12px; - margin-top: $margin-3; + margin-top: vars.$margin-3; line-height: 1.5; flex: 1 0; } @@ -95,10 +98,10 @@ .maputnik-button { display: inline-block; cursor: pointer; - background-color: $color-midgray; - color: $color-lowgray; - font-size: $font-size-6; - padding: $margin-2; + background-color: vars.$color-midgray; + color: vars.$color-lowgray; + font-size: vars.$font-size-6; + padding: vars.$margin-2; user-select: none; border-width: 0; border-radius: 2px; @@ -106,36 +109,36 @@ text-decoration: none; &:hover { - background-color: color.adjust($color-midgray, $lightness: 12%); - color: $color-white; + background-color: color.adjust(vars.$color-midgray, $lightness: 12%); + color: vars.$color-white; } &:disabled { - background-color: color.adjust($color-midgray, $lightness: -5%); - color: $color-midgray; + background-color: color.adjust(vars.$color-midgray, $lightness: -5%); + color: vars.$color-midgray; cursor: not-allowed; } } .maputnik-big-button { - margin-top: $margin-3; + margin-top: vars.$margin-3; display: inline-block; - padding: $margin-3; - font-size: $font-size-5; + padding: vars.$margin-3; + font-size: vars.$font-size-5; } .maputnik-wide-button { - padding: $margin-2 $margin-3; + padding: vars.$margin-2 vars.$margin-3; } .maputnik-green-button { - background-color: $color-green; - color: $color-black; + background-color: vars.$color-green; + color: vars.$color-black; } .maputnik-white-button { - background-color: $color-white; - color: $color-black; + background-color: vars.$color-white; + color: vars.$color-black; } .maputnik-icon-button { @@ -150,19 +153,19 @@ } svg { - fill: $color-white; + fill: vars.$color-white; } } } // INPUT BLOCK .maputnik-input-block { - margin: $margin-3; + margin: vars.$margin-3; display: flex; flex-wrap: wrap; &-label { - color: $color-lowgray; + color: vars.$color-lowgray; user-select: none; width: 32%; vertical-align: top; @@ -170,7 +173,7 @@ } &-action { - color: $color-lowgray; + color: vars.$color-lowgray; user-select: none; width: 18%; vertical-align: top; @@ -187,7 +190,7 @@ .maputnik-input-block-label { display: inline-block; width: 32%; - margin-bottom: $margin-3; + margin-bottom: vars.$margin-3; } .maputnik-input-block-action { @@ -211,15 +214,15 @@ // SPACE HELPER .maputnik-space { - @include vendor-prefix(flex-grow, 1); + @include mixins.vendor-prefix(flex-grow, 1); } // MESSAGE PANEL .maputnik-message-panel { - padding: $margin-2; + padding: vars.$margin-2; &-error { - color: $color-red; + color: vars.$color-red; } &__switch-button { @@ -241,7 +244,7 @@ &__menu { position: absolute; z-index: 999999; - background: $color-black; + background: vars.$color-black; display: flex; flex-direction: column; align-content: stretch; @@ -262,13 +265,13 @@ color: #a4a4a4; padding: 0.4em 0.4em; font-size: 0.9em; - border: solid 1px $color-red; + border: solid 1px vars.$color-red; border-radius: 2px; - margin: $margin-2 0px; + margin: vars.$margin-2 0px; } .maputnik-expression-editor { - border: solid 1px $color-gray; + border: solid 1px vars.$color-gray; } .maputnik-input-block--wide { @@ -288,13 +291,13 @@ } .maputnik-expr-infobox { - font-size: $font-size-6; - background: $color-midgray; - padding: $margin-2; + font-size: vars.$font-size-6; + background: vars.$color-midgray; + padding: vars.$margin-2; border-radius: 2px; border-top-right-radius: 0px; border-top-left-radius: 0px; - color: $color-white; + color: vars.$color-white; } .maputnik-expr-infobox__button { diff --git a/src/styles/_filtereditor.scss b/src/styles/_filtereditor.scss index b8d4fd7d9..766eeac97 100644 --- a/src/styles/_filtereditor.scss +++ b/src/styles/_filtereditor.scss @@ -1,5 +1,7 @@ +@use "vars"; + .maputnik-filter-editor-wrapper { - padding: $margin-3; + padding: vars.$margin-3; overflow: hidden; .maputnik-input-block { @@ -8,8 +10,8 @@ } .maputnik-filter-editor { - @extend .clearfix; /* stylelint-disable-line */ - color: $color-lowgray; + @extend .clearfix !optional; /* stylelint-disable-line */ + color: vars.$color-lowgray; } .maputnik-filter-editor-property { @@ -39,7 +41,7 @@ } .maputnik-filter-editor-compound-select { - margin-bottom: $margin-2; + margin-bottom: vars.$margin-2; .maputnik-doc-wrapper { width: 50%; @@ -52,22 +54,22 @@ } .maputnik-filter-editor-unsupported { - color: $color-midgray; + color: vars.$color-midgray; } .maputnik-add-filter { display: inline-block; float: right; - margin-top: $margin-3; + margin-top: vars.$margin-3; } .maputnik-delete-filter { - @extend .maputnik-icon-button; /* stylelint-disable-line */ + @extend .maputnik-icon-button !optional; /* stylelint-disable-line */ } .maputnik-filter-editor-block-action { - margin-top: $margin-2; - margin-bottom: $margin-2; + margin-top: vars.$margin-2; + margin-bottom: vars.$margin-2; display: inline-block; width: 6%; margin-right: 1.5%; @@ -79,12 +81,12 @@ } .maputnik-radio-as-button { - @extend .maputnik-button; /* stylelint-disable-line */ + @extend .maputnik-button !optional; /* stylelint-disable-line */ border: solid 1px transparent; &:focus-within { - border: solid 1px $color-white; + border: solid 1px vars.$color-white; } input { diff --git a/src/styles/_input.scss b/src/styles/_input.scss index e6e5b8905..604d560e3 100644 --- a/src/styles/_input.scss +++ b/src/styles/_input.scss @@ -1,3 +1,6 @@ +@use "mixins"; +@use "vars"; + @use 'sass:color'; //INPUT .maputnik-input { @@ -5,13 +8,13 @@ width: 100%; display: block; box-sizing: border-box; - font-size: $font-size-6; + font-size: vars.$font-size-6; line-height: 2; - padding-left: $margin-2; - padding-right: $margin-2; + padding-left: vars.$margin-2; + padding-right: vars.$margin-2; border: none; - background-color: $color-gray; - color: color.adjust($color-lowgray, $lightness: 12%); + background-color: vars.$color-gray; + color: color.adjust(vars.$color-lowgray, $lightness: 12%); &:invalid { border: solid 1px #B71C1C; @@ -20,7 +23,7 @@ } .maputnik-string { - @extend .maputnik-input; /* stylelint-disable-line */ + @extend .maputnik-input !optional; /* stylelint-disable-line */ &--multi { resize: vertical; @@ -44,12 +47,12 @@ } .maputnik-number { - @extend .maputnik-input; /* stylelint-disable-line */ + @extend .maputnik-input !optional; /* stylelint-disable-line */ } //COLOR PICKER .maputnik-color { - @extend .maputnik-input; /* stylelint-disable-line */ + @extend .maputnik-input !optional; /* stylelint-disable-line */ height: 26px; } @@ -57,7 +60,7 @@ .maputnik-color-wrapper { position: relative; - @include flex-row; + @include mixins.flex-row; } .maputnik-color-swatch { @@ -71,7 +74,7 @@ .maputnik-array { > * { - margin-bottom: $margin-3; + margin-bottom: vars.$margin-3; } .maputnik-array-block { @@ -96,19 +99,19 @@ // SELECT .maputnik-select { - @extend .maputnik-input; /* stylelint-disable-line */ + @extend .maputnik-input !optional; /* stylelint-disable-line */ -moz-appearance: none; -webkit-appearance: none; - background: $color-gray url("#{$icon-down-arrow}") right center no-repeat; - color: $color-white; + background: vars.$color-gray url("#{vars.$icon-down-arrow}") right center no-repeat; + color: vars.$color-white; background-position: calc(100% - 2px) center; padding-right: 20px; height: 24px; } [dir="rtl"] .maputnik-select { - background: $color-gray url("#{$icon-down-arrow}") left center no-repeat; + background: vars.$color-gray url("#{vars.$icon-down-arrow}") left center no-repeat; } // MULTIBUTTON @@ -116,12 +119,12 @@ padding: 0; .maputnik-button { - margin-right: $margin-1; + margin-right: vars.$margin-1; } } .maputnik-button-selected { - background-color: color.adjust($color-midgray, $lightness: 12%); + background-color: color.adjust(vars.$color-midgray, $lightness: 12%); color: white; } @@ -137,7 +140,7 @@ outline: none; &-wrapper { - @extend .maputnik-input; /* stylelint-disable-line */ + @extend .maputnik-input !optional; /* stylelint-disable-line */ padding-left: 0; padding-right: 0; @@ -154,12 +157,12 @@ text-align: center; height: 24px; width: 24px; - margin-right: $margin-2; - background-color: $color-gray; + margin-right: vars.$margin-2; + background-color: vars.$color-gray; border-radius: 2px; border-style: solid; border-width: 2px; - border-color: $color-gray; + border-color: vars.$color-gray; transition: background-color 0.1s ease-out; position: absolute; top: 0; @@ -185,7 +188,7 @@ width: 50%; height: 50%; margin-top: 1px; - fill: $color-lowgray; + fill: vars.$color-lowgray; } } @@ -198,29 +201,29 @@ position: absolute; overflow: auto; max-height: 50%; - background: $color-gray; + background: vars.$color-gray; z-index: 3; } &-menu-item { user-select: none; - color: $color-lowgray; + color: vars.$color-lowgray; cursor: default; - padding: $margin-1; - font-size: $font-size-6; + padding: vars.$margin-1; + font-size: vars.$font-size-6; z-index: 3; - background: $color-gray; + background: vars.$color-gray; } &-menu-item-selected { - background: $color-midgray; + background: vars.$color-midgray; } } // FONT .maputnik-font { .maputnik-autocomplete:not(:last-child) { - margin-bottom: $margin-3; + margin-bottom: vars.$margin-3; } } @@ -236,21 +239,21 @@ .SpecDoc__sdk-support__table { width: 100%; - margin-top: $margin-3; + margin-top: vars.$margin-3; td, th { - border: solid 1px $color-midgray; + border: solid 1px vars.$color-midgray; padding: 4px 6px; white-space: nowrap; } } .SpecDoc__values li { - margin-top: $margin-3; + margin-top: vars.$margin-3; } .SpecDoc__values code { - background: $color-midgray; + background: vars.$color-midgray; padding: 0.1em 0.3em; border-radius: 2px; } diff --git a/src/styles/_layer.scss b/src/styles/_layer.scss index bba1b881c..e65fdae7b 100644 --- a/src/styles/_layer.scss +++ b/src/styles/_layer.scss @@ -1,3 +1,6 @@ +@use "mixins"; +@use "vars"; + @use 'sass:color'; // LAYER LIST .maputnik-layer-list { @@ -7,9 +10,9 @@ flex-direction: column; &-header { - padding: $margin-2 $margin-2 $margin-3; + padding: vars.$margin-2 vars.$margin-2 vars.$margin-3; - @include flex-row; + @include mixins.flex-row; flex: 0 0; > * { @@ -19,8 +22,8 @@ } &-header-title { - font-size: $font-size-5; - color: $color-white; + font-size: vars.$font-size-5; + color: vars.$color-white; font-weight: bold; line-height: 1.3; } @@ -28,7 +31,7 @@ &-container { padding: 0; margin: 0; - padding-bottom: $margin-5; + padding-bottom: vars.$margin-5; flex: 1; overflow-x: hidden; overflow-y: auto; @@ -47,9 +50,9 @@ &-item { border: solid 1px transparent; font-weight: 400; - color: $color-lowgray; - font-size: $font-size-6; - border-bottom-color: color.adjust($color-black, $lightness: 0.1%); + color: vars.$color-lowgray; + font-size: vars.$font-size-6; + border-bottom-color: color.adjust(vars.$color-black, $lightness: 0.1%); user-select: none; list-style: none; z-index: 2000; @@ -63,21 +66,22 @@ transition: opacity 600ms, visibility 600ms; &:focus-within { - border: solid 1px $color-lowgray; + border: solid 1px vars.$color-lowgray; } + @include mixins.flex-row; + @media screen and (prefers-reduced-motion: reduce) { transition-duration: 0; } - @include flex-row; } &-icon-action { display: none; svg { - fill: $color-black; + fill: vars.$color-black; } } @@ -88,10 +92,10 @@ height: 15px; svg { - fill: color.adjust($color-lowgray, $lightness: -20%); + fill: color.adjust(vars.$color-lowgray, $lightness: -20%); &:hover { - fill: $color-white; + fill: vars.$color-white; } } } @@ -102,24 +106,24 @@ .maputnik-layer-list-item:hover, .maputnik-layer-list-item-selected { - background-color: color.adjust($color-black, $lightness: 2%); + background-color: color.adjust(vars.$color-black, $lightness: 2%); .maputnik-layer-list-icon-action { display: block; svg { - fill: color.adjust($color-lowgray, $lightness: -0.5%); + fill: color.adjust(vars.$color-lowgray, $lightness: -0.5%); } } } .maputnik-layer-list-item--error { - color: $color-red; + color: vars.$color-red; } &-item-selected { - color: $color-white; + color: vars.$color-white; } &-item-collapsed { @@ -132,7 +136,7 @@ } &-item-group-last { - border-bottom: 2px solid $color-gray; + border-bottom: 2px solid vars.$color-gray; } &-item-id { @@ -150,14 +154,14 @@ &-group-header { border: solid 1px transparent; - font-size: $font-size-6; - color: $color-lowgray; - background-color: color.adjust($color-black, $lightness: 2%); + font-size: vars.$font-size-6; + color: vars.$color-lowgray; + background-color: color.adjust(vars.$color-black, $lightness: 2%); user-select: none; - padding: $margin-2; + padding: vars.$margin-2; &:focus-within { - border: solid 1px $color-lowgray; + border: solid 1px vars.$color-lowgray; } button { @@ -165,7 +169,7 @@ cursor: pointer; } - @include flex-row; + @include mixins.flex-row; svg { width: 14px; @@ -178,25 +182,27 @@ } &-group-content { - margin: 0 $margin-3; + margin: 0 vars.$margin-3; } } // FILTER EDITOR .maputnik-layer-editor-group { font-weight: bold; - font-size: $font-size-5; - background-color: color.adjust($color-black, $lightness: 2%); - color: $color-white; + font-size: vars.$font-size-5; + background-color: color.adjust(vars.$color-black, $lightness: 2%); + color: vars.$color-white; cursor: pointer; user-select: none; line-height: 20px; border-top: solid 1px #36383e; + @include mixins.flex-row; + &__button { flex: 1; display: flex; - padding: $margin-2; + padding: vars.$margin-2; &__icon { fill: white; @@ -223,17 +229,17 @@ } - @include flex-row; + &:hover { - background-color: $color-gray; + background-color: vars.$color-gray; } } // PROPERTY .maputnik-default-property { .maputnik-input-block-label { - color: color.adjust($color-lowgray, $lightness: -20%); + color: color.adjust(vars.$color-lowgray, $lightness: -20%); } .maputnik-string, @@ -241,8 +247,8 @@ .maputnik-color, .maputnik-select, .maputnik-checkbox-wrapper { - background-color: color.adjust($color-gray, $lightness: -2%); - color: color.adjust($color-lowgray, $lightness: -20%); + background-color: color.adjust(vars.$color-gray, $lightness: -2%); + color: color.adjust(vars.$color-lowgray, $lightness: -20%); } .maputnik-make-zoom-function svg { @@ -250,18 +256,18 @@ } .maputnik-multibutton .maputnik-button { - background-color: color.adjust($color-midgray, $lightness: -10%); - color: color.adjust($color-lowgray, $lightness: -20%); + background-color: color.adjust(vars.$color-midgray, $lightness: -10%); + color: color.adjust(vars.$color-lowgray, $lightness: -20%); &:hover { - background-color: color.adjust($color-midgray, $lightness: 12%); - color: $color-white; + background-color: color.adjust(vars.$color-midgray, $lightness: 12%); + color: vars.$color-white; } } .maputnik-multibutton .maputnik-button-selected { - background-color: color.adjust($color-midgray, $lightness: -2%); - color: $color-lowgray; + background-color: color.adjust(vars.$color-midgray, $lightness: -2%); + color: vars.$color-lowgray; } } @@ -276,8 +282,8 @@ &__menu { position: absolute; z-index: 9999; - background: $color-black; - border: solid 1px $color-midgray; + background: vars.$color-black; + border: solid 1px vars.$color-midgray; right: 0; min-width: 120px; } @@ -295,7 +301,7 @@ .layer-header { display: flex; padding: 6px; - background: $color-black; + background: vars.$color-black; &__title { flex: 1; @@ -310,7 +316,7 @@ // Clone of the element which is sorted .sortableHelper { - font-family: $font-family; + font-family: vars.$font-family; z-index: 9999; border: none; } diff --git a/src/styles/_layout.scss b/src/styles/_layout.scss index c6b7e0e53..28a9b2bae 100644 --- a/src/styles/_layout.scss +++ b/src/styles/_layout.scss @@ -1,3 +1,5 @@ +@use "vars"; + //SCROLLING .maputnik-scroll-container { overflow-x: hidden; @@ -11,14 +13,14 @@ //APP LAYOUT .maputnik-layout { - font-family: $font-family; - color: $color-white; + font-family: vars.$font-family; + color: vars.$color-white; &-main { position: fixed; bottom: 0; - height: calc(100% - #{$toolbar-height + $toolbar-offset}); - top: $toolbar-height + $toolbar-offset; + height: calc(100% - #{vars.$toolbar-height + vars.$toolbar-offset}); + top: vars.$toolbar-height + vars.$toolbar-offset; left: 0; right: 0; z-index: 3; @@ -28,12 +30,12 @@ &-list { width: 200px; - background-color: $color-black; + background-color: vars.$color-black; } &-drawer { width: 370px; - background-color: $color-black; + background-color: vars.$color-black; // scroll-container is position: absolute position: relative; } @@ -43,7 +45,7 @@ bottom: 0; right: 0; z-index: 1; - width: $layout-map-width; - background-color: $color-black; + width: vars.$layout-map-width; + background-color: vars.$color-black; } } diff --git a/src/styles/_map.scss b/src/styles/_map.scss index eb8c715ce..312f52471 100644 --- a/src/styles/_map.scss +++ b/src/styles/_map.scss @@ -1,3 +1,5 @@ +@use "vars"; + //OPENLAYERS .maputnik-layout { .ol-zoom { @@ -39,7 +41,7 @@ } .maputnik-ol-popup { - background: $color-black; + background: vars.$color-black; } @@ -74,7 +76,7 @@ background: #1c1f24; border-radius: 2px; padding: 6px 8px; - color: $color-lowgray; + color: vars.$color-lowgray; z-index: 9999; font-size: 12px; font-weight: bold; diff --git a/src/styles/_modal.scss b/src/styles/_modal.scss index 677791cca..b4960cef1 100644 --- a/src/styles/_modal.scss +++ b/src/styles/_modal.scss @@ -1,40 +1,43 @@ +@use "mixins"; +@use "vars"; + //MODAL .maputnik-modal { min-width: 350px; max-width: 600px; overflow: hidden; - background-color: $color-black; + background-color: vars.$color-black; box-shadow: 0 0 5px 0 rgba(0, 0, 0, 0.3); z-index: 3; position: relative; - font-family: $font-family; + font-family: vars.$font-family; display: flex; flex-direction: column; max-height: 100vh; } .maputnik-modal-section { - padding-top: $margin-3; - padding-bottom: $margin-3; + padding-top: vars.$margin-3; + padding-bottom: vars.$margin-3; h1 { - font-size: $font-size-4; + font-size: vars.$font-size-4; } h2 { - font-size: $font-size-5; + font-size: vars.$font-size-5; } /* Bug fix: */ min-height: 0; - @include flex-column; + @include mixins.flex-column; flex-shrink: 0; } .maputnik-modal-sub-section { - margin-top: $margin-1; + margin-top: vars.$margin-1; } .maputnik-modal-section--shrink { @@ -42,14 +45,14 @@ } .maputnik-modal-header { - background-color: $color-gray; - padding: $margin-3; + background-color: vars.$color-gray; + padding: vars.$margin-3; - @include flex-row; + @include mixins.flex-row; } .maputnik-modal-header-title { - font-size: $font-size-5; + font-size: vars.$font-size-5; margin: 0; } @@ -66,18 +69,18 @@ } .maputnik-modal-content { - padding: $margin-3; + padding: vars.$margin-3; - @include flex-column; + @include mixins.flex-column; } .maputnik-modal-header-space { - @extend .maputnik-space; /* stylelint-disable-line */ + @extend .maputnik-space !optional; /* stylelint-disable-line */ } //OPEN MODAL .maputnik-upload-button { - @extend .maputnik-big-button; /* stylelint-disable-line */ + @extend .maputnik-big-button !optional; /* stylelint-disable-line */ } .maputnik-style-gallery-container { @@ -88,35 +91,35 @@ vertical-align: top; margin-top: 10px; margin-right: 10px; - background-color: $color-gray; + background-color: vars.$color-gray; display: inline-block; width: 180px; - font-size: $font-size-2; - color: $color-lowgray; + font-size: vars.$font-size-2; + color: vars.$color-lowgray; } .maputnik-public-style-button { - background-color: $color-gray; - padding: $margin-3; + background-color: vars.$color-gray; + padding: vars.$margin-3; display: block; width: 100%; &:hover { - background-color: $color-midgray; + background-color: vars.$color-midgray; } } .maputnik-public-style-header { - @include flex-row; + @include mixins.flex-row; } .maputnik-public-style-thumbnail { display: block; - margin-top: $margin-2; + margin-top: vars.$margin-2; width: 100%; padding-top: calc(400 / 600 * 100%); background-size: cover; - background-color: $color-midgray; + background-color: vars.$color-midgray; } .maputnik-add-modal { @@ -139,18 +142,18 @@ } .maputnik-add-layer { - @extend .clearfix; /* stylelint-disable-line */ + @extend .clearfix !optional; /* stylelint-disable-line */ } //ADD MODAL .maputnik-add-layer-button { - @extend .maputnik-big-button; /* stylelint-disable-line */ + @extend .maputnik-big-button !optional; /* stylelint-disable-line */ - margin-right: $margin-3; + margin-right: vars.$margin-3; float: right; display: inline-block; margin-top: 3; - margin-bottom: $margin-3; + margin-bottom: vars.$margin-3; text-align: right; } @@ -163,19 +166,19 @@ vertical-align: top; margin-top: 1.5%; margin-right: 1.5%; - background-color: $color-gray; + background-color: vars.$color-gray; width: 48.5%; display: inline-block; } .maputnik-public-source-select { - padding: $margin-3; - font-size: $font-size-5; - color: $color-lowgray; + padding: vars.$margin-3; + font-size: vars.$font-size-5; + color: vars.$color-lowgray; background-color: transparent; width: 100%; - @include flex-row; + @include mixins.flex-row; } .maputnik-public-source-name { @@ -192,24 +195,24 @@ } .maputnik-active-source-type-editor-header { - background-color: $color-gray; - color: $color-lowgray; - padding: $margin-2; + background-color: vars.$color-gray; + color: vars.$color-lowgray; + padding: vars.$margin-2; - @include flex-row; + @include mixins.flex-row; } .maputnik-active-source-type-editor-header-id { font-weight: 700; line-height: 2; - font-size: $font-size-5; + font-size: vars.$font-size-5; } .maputnik-active-source-type-editor-content { - border-color: $color-gray; + border-color: vars.$color-gray; border-width: 2px; border-style: solid; - padding: $margin-2; + padding: vars.$margin-2; .maputnik-input-block-label { width: 30%; @@ -221,7 +224,7 @@ } .maputnik-add-source { - @extend .clearfix; /* stylelint-disable-line */ + @extend .clearfix !optional; /* stylelint-disable-line */ .maputnik-input-block-label { width: 30%; @@ -233,17 +236,17 @@ } .maputnik-add-source-button { - @extend .maputnik-big-button; /* stylelint-disable-line */ + @extend .maputnik-big-button !optional; /* stylelint-disable-line */ display: inline-block; margin-top: 0; - margin-right: $margin-3; + margin-right: vars.$margin-3; float: right; } //EXPORT MODAL .maputnik-export-gist { - font-size: $font-size-6; + font-size: vars.$font-size-6; .maputnik-input-block { margin-left: 0; @@ -255,7 +258,7 @@ } span { - color: $color-lowgray; + color: vars.$color-lowgray; } } @@ -291,12 +294,12 @@ } &__shortcut { - margin-bottom: $margin-2; + margin-bottom: vars.$margin-2; } dt { display: inline; - margin-right: $margin-2; + margin-right: vars.$margin-2; } dd { diff --git a/src/styles/_popup.scss b/src/styles/_popup.scss index 91f104fa0..ae1c091ea 100644 --- a/src/styles/_popup.scss +++ b/src/styles/_popup.scss @@ -1,3 +1,5 @@ +@use "vars"; + .maputnik-popup-layer { display: flex; flex-direction: row; @@ -11,20 +13,20 @@ .maputnik-popup-layer__label { display: block; - color: $color-lowgray; + color: vars.$color-lowgray; cursor: pointer; user-select: none; line-height: 1.2; - padding: $margin-2; - padding-top: $margin-1; - padding-bottom: $margin-1; + padding: vars.$margin-2; + padding-top: vars.$margin-1; + padding-bottom: vars.$margin-1; } .maputnik-popup-layer-id { - padding-left: $margin-2; - padding-right: $margin-2; - background-color: $color-midgray; - color: $color-white; + padding-left: vars.$margin-2; + padding-right: vars.$margin-2; + background-color: vars.$color-midgray; + color: vars.$color-white; } .maputnik-feature-property-popup { @@ -32,9 +34,9 @@ overflow-y: auto; .maputnik-input-block { margin: 0; - margin-left: $margin-2; - margin-right: $margin-2; - margin-top: $margin-2; + margin-left: vars.$margin-2; + margin-right: vars.$margin-2; + margin-top: vars.$margin-2; } } @@ -43,7 +45,7 @@ } .maputnik-popup-table-cell { - color: $color-lowgray; - padding-left: $margin-2; - padding-right: $margin-2; + color: vars.$color-lowgray; + padding-left: vars.$margin-2; + padding-right: vars.$margin-2; } diff --git a/src/styles/_toolbar.scss b/src/styles/_toolbar.scss index 7ca459149..022c52667 100644 --- a/src/styles/_toolbar.scss +++ b/src/styles/_toolbar.scss @@ -1,13 +1,15 @@ +@use "vars"; + @use 'sass:color'; // TOOLBAR .maputnik-toolbar { position: fixed; - height: $toolbar-height; + height: vars.$toolbar-height; width: 100%; z-index: 100; left: 0; - top: $toolbar-offset; - background-color: $color-black; + top: vars.$toolbar-offset; + background-color: vars.$color-black; } .maputnik-toolbar-logo-container { @@ -20,9 +22,9 @@ flex: 0 0 190px; width: 200px; text-align: left; - background-color: $color-black; - padding: $margin-2; - height: $toolbar-height; + background-color: vars.$color-black; + padding: vars.$margin-2; + height: vars.$toolbar-height; position: relative; overflow: hidden; @@ -33,19 +35,19 @@ img { width: 30px; - padding-right: $margin-2; + padding-right: vars.$margin-2; vertical-align: top; } } .maputnik-toolbar-link { vertical-align: top; - height: $toolbar-height; + height: vars.$toolbar-height; display: inline-block; - padding: $margin-3; - font-size: $font-size-5; + padding: vars.$margin-3; + font-size: vars.$font-size-5; cursor: pointer; - color: $color-white; + color: vars.$color-white; text-decoration: none; line-height: 20px; @@ -54,30 +56,30 @@ } &:hover { - background-color: $color-midgray; + background-color: vars.$color-midgray; } } .maputnik-toolbar-link--highlighted { line-height: 1; - padding: $margin-2 $margin-3; + padding: vars.$margin-2 vars.$margin-3; .maputnik-toolbar-link-wrapper { - background-color: $color-white; + background-color: vars.$color-white; border-radius: 2px; - padding: $margin-2; - margin-top: $margin-1; - color: $color-black; + padding: vars.$margin-2; + margin-top: vars.$margin-1; + color: vars.$color-black; display: block; } &:hover { - background-color: $color-black; + background-color: vars.$color-black; } &:hover .maputnik-toolbar-link-wrapper { - background-color: color.adjust($color-midgray, $lightness: 12%); - color: $color-white; + background-color: color.adjust(vars.$color-midgray, $lightness: 12%); + color: vars.$color-white; } } @@ -94,32 +96,31 @@ .maputnik-toolbar-action { background: inherit; border-width: 0; - @extend .maputnik-toolbar-link; /* stylelint-disable-line */ + @extend .maputnik-toolbar-link !optional; /* stylelint-disable-line */ } .maputnik-toolbar-select { background: inherit; border-width: 0; - @extend .maputnik-toolbar-link; /* stylelint-disable-line */ + @extend .maputnik-toolbar-link !optional; /* stylelint-disable-line */ select { margin: 0 6px; - border-width: 0; display: inline; width: auto; - border: solid 1px $color-midgray; + border: solid 1px vars.$color-midgray; vertical-align: inherit; margin-top: -2px; } } .maputnik-icon-text { - padding: 0 $margin-1; + padding: 0 vars.$margin-1; } .maputnik-icon-action { display: inline; - margin: 0 $margin-1; + margin: 0 vars.$margin-1; } .maputnik-toolbar__inner { @@ -141,7 +142,7 @@ height: 100%; text-align: center; display: block; - background-color: $color-black; + background-color: vars.$color-black; z-index: 999; line-height: 40px; left: 0; @@ -150,7 +151,7 @@ &:active, &:focus { width: 100%; - border-color: $color-lowgray; + border-color: vars.$color-lowgray; } } diff --git a/src/styles/_zoomproperty.scss b/src/styles/_zoomproperty.scss index 7a76e6b42..207f9b895 100644 --- a/src/styles/_zoomproperty.scss +++ b/src/styles/_zoomproperty.scss @@ -1,16 +1,18 @@ +@use "vars"; + // ZOOM FUNC .maputnik-make-zoom-function { background-color: transparent; display: inline-block; vertical-align: middle; - padding: 0 $margin-2 0 0; + padding: 0 vars.$margin-2 0 0; - @extend .maputnik-icon-button; /* stylelint-disable-line */ + @extend .maputnik-icon-button !optional; /* stylelint-disable-line */ } // ZOOM PROPERTY .maputnik-zoom-spec-property { - @extend .clearfix; /* stylelint-disable-line */ + @extend .clearfix !optional; /* stylelint-disable-line */ } .maputnik-zoom-spec-property-label { @@ -19,8 +21,8 @@ } .maputnik-zoom-spec-property-stop-item { - margin-bottom: $margin-2; - margin-top: $margin-2; + margin-bottom: vars.$margin-2; + margin-top: vars.$margin-2; } .maputnik-zoom-spec-property-stop-edit { @@ -49,13 +51,12 @@ padding-top: 0; vertical-align: middle; - @extend .maputnik-icon-button; /* stylelint-disable-line */ + @extend .maputnik-icon-button !optional; /* stylelint-disable-line */ } .maputnik-add-stop { - display: inline-block; float: right; - margin-right: $margin-3; + margin-right: vars.$margin-3; } // DATA FUNC @@ -63,9 +64,9 @@ background-color: transparent; display: inline-block; vertical-align: middle; - padding: 0 $margin-2 0 0; + padding: 0 vars.$margin-2 0 0; - @extend .maputnik-icon-button; /* stylelint-disable-line */ + @extend .maputnik-icon-button !optional; /* stylelint-disable-line */ } .maputnik-data-spec-property { @@ -86,7 +87,7 @@ .maputnik-doc-wrapper { width: 25%; - color: $color-lowgray; + color: vars.$color-lowgray; } .maputnik-doc-wrapper:hover { diff --git a/src/styles/index.scss b/src/styles/index.scss index 29dad0480..ad7bb1267 100644 --- a/src/styles/index.scss +++ b/src/styles/index.scss @@ -1,22 +1,22 @@ -@import 'vars'; -@import 'mixins'; -@import 'reset'; -@import 'base'; -@import 'components'; -@import 'scrollbar'; -@import 'picker'; -@import 'toolbar'; -@import 'modal'; -@import 'export'; -@import 'layout'; -@import 'layer'; -@import 'input'; -@import 'filtereditor'; -@import 'zoomproperty'; -@import 'popup'; -@import 'map'; -@import 'codemirror'; -@import 'react-collapse'; +@use 'vars'; +@use 'mixins'; +@use 'reset'; +@use 'base'; +@use 'components'; +@use 'scrollbar'; +@use 'picker'; +@use 'toolbar'; +@use 'modal'; +@use 'export'; +@use 'layout'; +@use 'layer'; +@use 'input'; +@use 'filtereditor'; +@use 'zoomproperty'; +@use 'popup'; +@use 'map'; +@use 'codemirror'; +@use 'react-collapse'; .maputnik-layout { height: 100vh; @@ -29,14 +29,14 @@ } .maputnik-data-fieldset-inner { - background: $color-black; - border: solid 1px $color-midgray; + background: vars.$color-black; + border: solid 1px vars.$color-midgray; border-radius: 2px; position: relative; // HACK: Overide .maputnik-input-block { - margin: $margin-2; + margin: vars.$margin-2; } .maputnik-add-stop { @@ -49,8 +49,8 @@ } .maputnik-toolbox { - margin: $margin-3; - margin-top: $margin-3; + margin: vars.$margin-3; + margin-top: vars.$margin-3; text-align: right; } @@ -58,40 +58,40 @@ .maputnik-data-spec-property { legend { - font-size: $font-size-6; - color: $color-lowgray; - margin-bottom: $margin-3; + font-size: vars.$font-size-6; + color: vars.$color-lowgray; + margin-bottom: vars.$margin-3; } .maputnik-data-spec-property-group { - margin-bottom: $margin-2; + margin-bottom: vars.$margin-2; } } .maputnik-data-spec-block { - margin: $margin-3; + margin: vars.$margin-3; } .maputnik-function-stop { - padding-left: $margin-2; - padding-right: $margin-2; + padding-left: vars.$margin-2; + padding-right: vars.$margin-2; } .maputnik-function-stop-table { text-align: left; - margin-bottom: $margin-2; + margin-bottom: vars.$margin-2; box-sizing: border-box; width: 100%; thead th { - padding: $margin-1 $margin-2; + padding: vars.$margin-1 vars.$margin-2; padding-left: 0; - color: $color-lowgray; + color: vars.$color-lowgray; } td, th { - font-size: $font-size-6; - color: $color-white; + font-size: vars.$font-size-6; + color: vars.$color-white; // HACK > * { @@ -102,7 +102,7 @@ &:not(:first-child) { - padding: $margin-1; + padding: vars.$margin-1; } &:nth-child(1) { @@ -147,10 +147,10 @@ } caption { - color: $color-lowgray; + color: vars.$color-lowgray; text-align: left; - border-top: solid 1px $color-black; - font-size: $font-size-6; + border-top: solid 1px vars.$color-black; + font-size: vars.$font-size-6; height: 0px; overflow: hidden; } diff --git a/tsconfig.json b/tsconfig.json index 46f4ef8f8..9d16a5530 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -22,6 +22,7 @@ "noFallthroughCasesInSwitch": true }, "include": ["src", "cypress/e2e"], + "exclude": ["dist"], "references": [{ "path": "./tsconfig.node.json" }], // TODO: Remove when issue is resolved https://github.com/cypress-io/cypress/issues/27448 "ts-node": { diff --git a/vite.config.ts b/vite.config.ts index e1ca3e83c..3d36e1801 100644 --- a/vite.config.ts +++ b/vite.config.ts @@ -18,7 +18,7 @@ export default defineConfig({ values: { "_token_stack:": "", }, - }) as any, + }), react(), istanbul({ cypress: true,