forked from MapComplete/MapComplete
Merge pull request #1795 from pietervdvn/feature/json-editor
JSON editor in Studio
This commit is contained in:
commit
1048299b03
7 changed files with 208 additions and 39 deletions
45
package-lock.json
generated
45
package-lock.json
generated
|
@ -44,6 +44,7 @@
|
||||||
"lz-string": "^1.4.4",
|
"lz-string": "^1.4.4",
|
||||||
"mangrove-reviews-typescript": "^1.1.0",
|
"mangrove-reviews-typescript": "^1.1.0",
|
||||||
"maplibre-gl": "^3.5.0",
|
"maplibre-gl": "^3.5.0",
|
||||||
|
"monaco-editor": "^0.46.0",
|
||||||
"nano-markdown": "^1.2.2",
|
"nano-markdown": "^1.2.2",
|
||||||
"opening_hours": "^3.6.0",
|
"opening_hours": "^3.6.0",
|
||||||
"osm-auth": "^2.2.0",
|
"osm-auth": "^2.2.0",
|
||||||
|
@ -68,6 +69,7 @@
|
||||||
"@babeard/svelte-heroicons": "^2.0.0-rc.0",
|
"@babeard/svelte-heroicons": "^2.0.0-rc.0",
|
||||||
"@babel/polyfill": "^7.10.4",
|
"@babel/polyfill": "^7.10.4",
|
||||||
"@babel/preset-env": "7.13.8",
|
"@babel/preset-env": "7.13.8",
|
||||||
|
"@monaco-editor/loader": "^1.4.0",
|
||||||
"@parcel/service-worker": "^2.6.0",
|
"@parcel/service-worker": "^2.6.0",
|
||||||
"@rollup/plugin-json": "^6.0.0",
|
"@rollup/plugin-json": "^6.0.0",
|
||||||
"@sveltejs/vite-plugin-svelte": "^2.0.2",
|
"@sveltejs/vite-plugin-svelte": "^2.0.2",
|
||||||
|
@ -2355,6 +2357,18 @@
|
||||||
"gl-style-validate": "dist/gl-style-validate.mjs"
|
"gl-style-validate": "dist/gl-style-validate.mjs"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/@monaco-editor/loader": {
|
||||||
|
"version": "1.4.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@monaco-editor/loader/-/loader-1.4.0.tgz",
|
||||||
|
"integrity": "sha512-00ioBig0x642hytVspPl7DbQyaSWRaolYie/UFNjoTdvoKPzo6xrXLhTk9ixgIKcLH5b5vDOjVNiGyY+uDCUlg==",
|
||||||
|
"dev": true,
|
||||||
|
"dependencies": {
|
||||||
|
"state-local": "^1.0.6"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"monaco-editor": ">= 0.21.0 < 1"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/@nodelib/fs.scandir": {
|
"node_modules/@nodelib/fs.scandir": {
|
||||||
"version": "2.1.5",
|
"version": "2.1.5",
|
||||||
"resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz",
|
"resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz",
|
||||||
|
@ -9377,6 +9391,11 @@
|
||||||
"url": "https://github.com/chalk/supports-color?sponsor=1"
|
"url": "https://github.com/chalk/supports-color?sponsor=1"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/monaco-editor": {
|
||||||
|
"version": "0.46.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/monaco-editor/-/monaco-editor-0.46.0.tgz",
|
||||||
|
"integrity": "sha512-ADwtLIIww+9FKybWscd7OCfm9odsFYHImBRI1v9AviGce55QY8raT+9ihH8jX/E/e6QVSGM+pKj4jSUSRmALNQ=="
|
||||||
|
},
|
||||||
"node_modules/monotone-convex-hull-2d": {
|
"node_modules/monotone-convex-hull-2d": {
|
||||||
"version": "1.0.1",
|
"version": "1.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/monotone-convex-hull-2d/-/monotone-convex-hull-2d-1.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/monotone-convex-hull-2d/-/monotone-convex-hull-2d-1.0.1.tgz",
|
||||||
|
@ -11499,6 +11518,12 @@
|
||||||
"node": ">=0.1.14"
|
"node": ">=0.1.14"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/state-local": {
|
||||||
|
"version": "1.0.7",
|
||||||
|
"resolved": "https://registry.npmjs.org/state-local/-/state-local-1.0.7.tgz",
|
||||||
|
"integrity": "sha512-HTEHMNieakEnoe33shBYcZ7NX83ACUjCu8c40iOGEZsngj9zRnkqS9j1pqQPXwobB0ZcVTk27REb7COQ0UR59w==",
|
||||||
|
"dev": true
|
||||||
|
},
|
||||||
"node_modules/std-env": {
|
"node_modules/std-env": {
|
||||||
"version": "3.3.2",
|
"version": "3.3.2",
|
||||||
"resolved": "https://registry.npmjs.org/std-env/-/std-env-3.3.2.tgz",
|
"resolved": "https://registry.npmjs.org/std-env/-/std-env-3.3.2.tgz",
|
||||||
|
@ -15357,6 +15382,15 @@
|
||||||
"sort-object": "^3.0.3"
|
"sort-object": "^3.0.3"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"@monaco-editor/loader": {
|
||||||
|
"version": "1.4.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@monaco-editor/loader/-/loader-1.4.0.tgz",
|
||||||
|
"integrity": "sha512-00ioBig0x642hytVspPl7DbQyaSWRaolYie/UFNjoTdvoKPzo6xrXLhTk9ixgIKcLH5b5vDOjVNiGyY+uDCUlg==",
|
||||||
|
"dev": true,
|
||||||
|
"requires": {
|
||||||
|
"state-local": "^1.0.6"
|
||||||
|
}
|
||||||
|
},
|
||||||
"@nodelib/fs.scandir": {
|
"@nodelib/fs.scandir": {
|
||||||
"version": "2.1.5",
|
"version": "2.1.5",
|
||||||
"resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz",
|
"resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz",
|
||||||
|
@ -20688,6 +20722,11 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"monaco-editor": {
|
||||||
|
"version": "0.46.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/monaco-editor/-/monaco-editor-0.46.0.tgz",
|
||||||
|
"integrity": "sha512-ADwtLIIww+9FKybWscd7OCfm9odsFYHImBRI1v9AviGce55QY8raT+9ihH8jX/E/e6QVSGM+pKj4jSUSRmALNQ=="
|
||||||
|
},
|
||||||
"monotone-convex-hull-2d": {
|
"monotone-convex-hull-2d": {
|
||||||
"version": "1.0.1",
|
"version": "1.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/monotone-convex-hull-2d/-/monotone-convex-hull-2d-1.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/monotone-convex-hull-2d/-/monotone-convex-hull-2d-1.0.1.tgz",
|
||||||
|
@ -22222,6 +22261,12 @@
|
||||||
"integrity": "sha512-EeNzTVfj+1In7aSLPKDD03F/ly4RxEuF/EX0YcOG0cKoPXs+SLZxDawQbexQDBzwROs4VKLWTOaZQlZkGBFEIQ==",
|
"integrity": "sha512-EeNzTVfj+1In7aSLPKDD03F/ly4RxEuF/EX0YcOG0cKoPXs+SLZxDawQbexQDBzwROs4VKLWTOaZQlZkGBFEIQ==",
|
||||||
"optional": true
|
"optional": true
|
||||||
},
|
},
|
||||||
|
"state-local": {
|
||||||
|
"version": "1.0.7",
|
||||||
|
"resolved": "https://registry.npmjs.org/state-local/-/state-local-1.0.7.tgz",
|
||||||
|
"integrity": "sha512-HTEHMNieakEnoe33shBYcZ7NX83ACUjCu8c40iOGEZsngj9zRnkqS9j1pqQPXwobB0ZcVTk27REb7COQ0UR59w==",
|
||||||
|
"dev": true
|
||||||
|
},
|
||||||
"std-env": {
|
"std-env": {
|
||||||
"version": "3.3.2",
|
"version": "3.3.2",
|
||||||
"resolved": "https://registry.npmjs.org/std-env/-/std-env-3.3.2.tgz",
|
"resolved": "https://registry.npmjs.org/std-env/-/std-env-3.3.2.tgz",
|
||||||
|
|
|
@ -143,6 +143,7 @@
|
||||||
"lz-string": "^1.4.4",
|
"lz-string": "^1.4.4",
|
||||||
"mangrove-reviews-typescript": "^1.1.0",
|
"mangrove-reviews-typescript": "^1.1.0",
|
||||||
"maplibre-gl": "^3.5.0",
|
"maplibre-gl": "^3.5.0",
|
||||||
|
"monaco-editor": "^0.46.0",
|
||||||
"nano-markdown": "^1.2.2",
|
"nano-markdown": "^1.2.2",
|
||||||
"opening_hours": "^3.6.0",
|
"opening_hours": "^3.6.0",
|
||||||
"osm-auth": "^2.2.0",
|
"osm-auth": "^2.2.0",
|
||||||
|
@ -167,6 +168,7 @@
|
||||||
"@babeard/svelte-heroicons": "^2.0.0-rc.0",
|
"@babeard/svelte-heroicons": "^2.0.0-rc.0",
|
||||||
"@babel/polyfill": "^7.10.4",
|
"@babel/polyfill": "^7.10.4",
|
||||||
"@babel/preset-env": "7.13.8",
|
"@babel/preset-env": "7.13.8",
|
||||||
|
"@monaco-editor/loader": "^1.4.0",
|
||||||
"@parcel/service-worker": "^2.6.0",
|
"@parcel/service-worker": "^2.6.0",
|
||||||
"@rollup/plugin-json": "^6.0.0",
|
"@rollup/plugin-json": "^6.0.0",
|
||||||
"@sveltejs/vite-plugin-svelte": "^2.0.2",
|
"@sveltejs/vite-plugin-svelte": "^2.0.2",
|
||||||
|
|
|
@ -777,6 +777,10 @@ video {
|
||||||
float: left;
|
float: left;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.m-8 {
|
||||||
|
margin: 2rem;
|
||||||
|
}
|
||||||
|
|
||||||
.m-4 {
|
.m-4 {
|
||||||
margin: 1rem;
|
margin: 1rem;
|
||||||
}
|
}
|
||||||
|
@ -789,10 +793,6 @@ video {
|
||||||
margin: 0px;
|
margin: 0px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.m-8 {
|
|
||||||
margin: 2rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.m-2 {
|
.m-2 {
|
||||||
margin: 0.5rem;
|
margin: 0.5rem;
|
||||||
}
|
}
|
||||||
|
@ -896,6 +896,10 @@ video {
|
||||||
margin-right: 4rem;
|
margin-right: 4rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.mb-4 {
|
||||||
|
margin-bottom: 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
.mt-4 {
|
.mt-4 {
|
||||||
margin-top: 1rem;
|
margin-top: 1rem;
|
||||||
}
|
}
|
||||||
|
@ -928,10 +932,6 @@ video {
|
||||||
margin-right: 0.25rem;
|
margin-right: 0.25rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
.mb-4 {
|
|
||||||
margin-bottom: 1rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.ml-1 {
|
.ml-1 {
|
||||||
margin-left: 0.25rem;
|
margin-left: 0.25rem;
|
||||||
}
|
}
|
||||||
|
@ -1127,14 +1127,14 @@ video {
|
||||||
height: 50%;
|
height: 50%;
|
||||||
}
|
}
|
||||||
|
|
||||||
.h-7 {
|
|
||||||
height: 1.75rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.h-3 {
|
.h-3 {
|
||||||
height: 0.75rem;
|
height: 0.75rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.h-7 {
|
||||||
|
height: 1.75rem;
|
||||||
|
}
|
||||||
|
|
||||||
.h-11 {
|
.h-11 {
|
||||||
height: 2.75rem;
|
height: 2.75rem;
|
||||||
}
|
}
|
||||||
|
@ -1163,6 +1163,10 @@ video {
|
||||||
height: 20rem;
|
height: 20rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.h-5\/6 {
|
||||||
|
height: 83.333333%;
|
||||||
|
}
|
||||||
|
|
||||||
.h-56 {
|
.h-56 {
|
||||||
height: 14rem;
|
height: 14rem;
|
||||||
}
|
}
|
||||||
|
@ -1233,14 +1237,14 @@ video {
|
||||||
width: 1rem;
|
width: 1rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
.w-7 {
|
|
||||||
width: 1.75rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.w-3 {
|
.w-3 {
|
||||||
width: 0.75rem;
|
width: 0.75rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.w-7 {
|
||||||
|
width: 1.75rem;
|
||||||
|
}
|
||||||
|
|
||||||
.w-11 {
|
.w-11 {
|
||||||
width: 2.75rem;
|
width: 2.75rem;
|
||||||
}
|
}
|
||||||
|
@ -1266,6 +1270,14 @@ video {
|
||||||
width: 4rem;
|
width: 4rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.w-5\/6 {
|
||||||
|
width: 83.333333%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.w-1\/6 {
|
||||||
|
width: 16.666667%;
|
||||||
|
}
|
||||||
|
|
||||||
.w-min {
|
.w-min {
|
||||||
width: -webkit-min-content;
|
width: -webkit-min-content;
|
||||||
width: min-content;
|
width: min-content;
|
||||||
|
|
|
@ -28,6 +28,9 @@
|
||||||
window.setTimeout(() => tabElements[tab.data].click(), 50)
|
window.setTimeout(() => tabElements[tab.data].click(), 50)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
export function getTab() {
|
||||||
|
return tab
|
||||||
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<div class="tabbedgroup flex h-full w-full">
|
<div class="tabbedgroup flex h-full w-full">
|
||||||
|
|
|
@ -13,14 +13,15 @@
|
||||||
import SchemaBasedInput from "./SchemaBasedInput.svelte"
|
import SchemaBasedInput from "./SchemaBasedInput.svelte"
|
||||||
import FloatOver from "../Base/FloatOver.svelte"
|
import FloatOver from "../Base/FloatOver.svelte"
|
||||||
import TagRenderingInput from "./TagRenderingInput.svelte"
|
import TagRenderingInput from "./TagRenderingInput.svelte"
|
||||||
import FromHtml from "../Base/FromHtml.svelte"
|
|
||||||
import AllTagsPanel from "../Popup/AllTagsPanel.svelte"
|
import AllTagsPanel from "../Popup/AllTagsPanel.svelte"
|
||||||
import QuestionPreview from "./QuestionPreview.svelte"
|
import QuestionPreview from "./QuestionPreview.svelte"
|
||||||
import ShowConversionMessages from "./ShowConversionMessages.svelte"
|
import ShowConversionMessages from "./ShowConversionMessages.svelte"
|
||||||
|
import RawEditor from "./RawEditor.svelte"
|
||||||
|
|
||||||
const layerSchema: ConfigMeta[] = <any>layerSchemaRaw
|
const layerSchema: ConfigMeta[] = <any>layerSchemaRaw
|
||||||
|
|
||||||
export let state: EditLayerState
|
export let state: EditLayerState
|
||||||
|
|
||||||
export let backToStudio: () => void
|
export let backToStudio: () => void
|
||||||
let messages = state.messages
|
let messages = state.messages
|
||||||
let hasErrors = messages.mapD(
|
let hasErrors = messages.mapD(
|
||||||
|
@ -59,7 +60,7 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
let requiredFields = ["id", "name", "description", "source"]
|
let requiredFields = ["id", "name", "description", "source"]
|
||||||
let currentlyMissing = state.configuration.map((config) => {
|
let currentlyMissing = configuration.map((config) => {
|
||||||
if (!config) {
|
if (!config) {
|
||||||
return []
|
return []
|
||||||
}
|
}
|
||||||
|
@ -184,33 +185,34 @@
|
||||||
<Region configs={perRegion["expert"]} {state} />
|
<Region configs={perRegion["expert"]} {state} />
|
||||||
</div>
|
</div>
|
||||||
<div slot="title5">Configuration file</div>
|
<div slot="title5">Configuration file</div>
|
||||||
<div slot="content5">
|
<div slot="content5" class="flex h-full flex-col">
|
||||||
<div>
|
<div>
|
||||||
Below, you'll find the raw configuration file in `.json`-format. This is mostly for
|
Below, you'll find the raw configuration file in `.json`-format. This is mostly for
|
||||||
debugging purposes
|
debugging purposes, but you can also edit the file directly if you want.
|
||||||
</div>
|
</div>
|
||||||
<div class="literal-code">
|
|
||||||
<FromHtml src={JSON.stringify($configuration, null, " ").replaceAll("\n", "</br>")} />
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<ShowConversionMessages messages={$messages} />
|
<ShowConversionMessages messages={$messages} />
|
||||||
<div>
|
<div class="flex h-full w-full flex-row justify-between overflow-y-auto">
|
||||||
The testobject (which is used to render the questions in the 'information panel' item
|
<div class="literal-code h-full w-5/6 overflow-y-auto">
|
||||||
has the following tags:
|
<RawEditor {state} />
|
||||||
</div>
|
</div>
|
||||||
|
<div class="h-full w-1/6">
|
||||||
|
<div>
|
||||||
|
The testobject (which is used to render the questions in the 'information panel'
|
||||||
|
item has the following tags:
|
||||||
|
</div>
|
||||||
|
|
||||||
<AllTagsPanel tags={state.testTags} />
|
<AllTagsPanel tags={state.testTags} />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</TabbedGroup>
|
</TabbedGroup>
|
||||||
</div>
|
</div>
|
||||||
{#if $highlightedItem !== undefined}
|
{#if $highlightedItem !== undefined}
|
||||||
<FloatOver on:close={() => highlightedItem.setData(undefined)}>
|
<FloatOver on:close={() => highlightedItem.setData(undefined)}>
|
||||||
<div>
|
<div>
|
||||||
<TagRenderingInput
|
<TagRenderingInput path={$highlightedItem.path} {state} />
|
||||||
path={$highlightedItem.path}
|
<!--
|
||||||
{state}
|
schema={$highlightedItem.schema} -->
|
||||||
schema={$highlightedItem.schema}
|
|
||||||
/>
|
|
||||||
</div>
|
</div>
|
||||||
</FloatOver>
|
</FloatOver>
|
||||||
{/if}
|
{/if}
|
||||||
|
|
|
@ -6,6 +6,7 @@
|
||||||
import TabbedGroup from "../Base/TabbedGroup.svelte"
|
import TabbedGroup from "../Base/TabbedGroup.svelte"
|
||||||
import ShowConversionMessages from "./ShowConversionMessages.svelte"
|
import ShowConversionMessages from "./ShowConversionMessages.svelte"
|
||||||
import Region from "./Region.svelte"
|
import Region from "./Region.svelte"
|
||||||
|
import RawEditor from "./RawEditor.svelte"
|
||||||
|
|
||||||
export let state: EditThemeState
|
export let state: EditThemeState
|
||||||
let schema: ConfigMeta[] = state.schema.filter((schema) => schema.path.length > 0)
|
let schema: ConfigMeta[] = state.schema.filter((schema) => schema.path.length > 0)
|
||||||
|
@ -50,7 +51,7 @@
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="m4 h-full overflow-y-auto">
|
<div class="m4 h-full overflow-y-auto">
|
||||||
{Object.keys(perRegion).join(";")}
|
<!-- {Object.keys(perRegion).join(";")} -->
|
||||||
<TabbedGroup>
|
<TabbedGroup>
|
||||||
<div slot="title0">Basic properties</div>
|
<div slot="title0">Basic properties</div>
|
||||||
<div slot="content0">
|
<div slot="content0">
|
||||||
|
@ -73,12 +74,15 @@
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div slot="title4">Configuration file</div>
|
<div slot="title4">Configuration file</div>
|
||||||
<div slot="content4">
|
<div slot="content4" class="flex h-full flex-col">
|
||||||
<div class="literal-code">
|
<div>
|
||||||
{JSON.stringify($config)}
|
Below, you'll find the raw configuration file in `.json`-format. This is mostly for
|
||||||
|
debugging purposes, but you can also edit the file directly if you want.
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<ShowConversionMessages messages={$messages} />
|
<ShowConversionMessages messages={$messages} />
|
||||||
|
<div class="literal-code h-full w-full">
|
||||||
|
<RawEditor {state} />
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</TabbedGroup>
|
</TabbedGroup>
|
||||||
</div>
|
</div>
|
||||||
|
|
101
src/UI/Studio/RawEditor.svelte
Normal file
101
src/UI/Studio/RawEditor.svelte
Normal file
|
@ -0,0 +1,101 @@
|
||||||
|
<script lang="ts">
|
||||||
|
import { onDestroy, onMount } from "svelte"
|
||||||
|
import EditLayerState, { EditThemeState } from "./EditLayerState"
|
||||||
|
import loader from "@monaco-editor/loader"
|
||||||
|
import type * as Monaco from "monaco-editor/esm/vs/editor/editor.api"
|
||||||
|
import layerSchemaJSON from "../../../Docs/Schemas/LayerConfigJson.schema.json"
|
||||||
|
import layoutSchemaJSON from "../../../Docs/Schemas/LayoutConfigJson.schema.json"
|
||||||
|
|
||||||
|
export let state: EditLayerState | EditThemeState
|
||||||
|
|
||||||
|
let container: HTMLDivElement
|
||||||
|
let monaco: typeof Monaco
|
||||||
|
let editor: Monaco.editor.IStandaloneCodeEditor
|
||||||
|
let model: Monaco.editor.ITextModel
|
||||||
|
|
||||||
|
function save() {
|
||||||
|
try {
|
||||||
|
const newConfig = JSON.parse(editor.getValue())
|
||||||
|
state.configuration.setData(newConfig)
|
||||||
|
} catch (e) {
|
||||||
|
console.error(e)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Catch keyboard shortcuts
|
||||||
|
onMount(() => {
|
||||||
|
const handler = (e: KeyboardEvent) => {
|
||||||
|
if (e.key === "s" && (e.ctrlKey || e.metaKey)) {
|
||||||
|
e.preventDefault()
|
||||||
|
save()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
window.addEventListener("keydown", handler)
|
||||||
|
return () => window.removeEventListener("keydown", handler)
|
||||||
|
})
|
||||||
|
|
||||||
|
onMount(async () => {
|
||||||
|
const monacoEditor = await import("monaco-editor")
|
||||||
|
loader.config({
|
||||||
|
monaco: monacoEditor.default,
|
||||||
|
})
|
||||||
|
|
||||||
|
monaco = await loader.init()
|
||||||
|
|
||||||
|
// Determine schema based on the state
|
||||||
|
let schemaUri: string
|
||||||
|
if (state instanceof EditLayerState) {
|
||||||
|
schemaUri = "https://mapcomplete.org/schemas/layerconfig.json"
|
||||||
|
} else {
|
||||||
|
schemaUri = "https://mapcomplete.org/schemas/layoutconfig.json"
|
||||||
|
}
|
||||||
|
|
||||||
|
monaco.languages.json.jsonDefaults.setDiagnosticsOptions({
|
||||||
|
validate: true,
|
||||||
|
schemas: [
|
||||||
|
{
|
||||||
|
uri: schemaUri,
|
||||||
|
fileMatch: ["file.json"],
|
||||||
|
schema:
|
||||||
|
schemaUri === "https://mapcomplete.org/schemas/layerconfig.json"
|
||||||
|
? layerSchemaJSON
|
||||||
|
: layoutSchemaJSON,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
})
|
||||||
|
|
||||||
|
let modelUri = monaco.Uri.parse("inmemory://inmemory/file.json")
|
||||||
|
|
||||||
|
// Create a new model
|
||||||
|
model = monaco.editor.createModel(
|
||||||
|
JSON.stringify(state.configuration.data, null, " "),
|
||||||
|
"json",
|
||||||
|
modelUri
|
||||||
|
)
|
||||||
|
|
||||||
|
editor = monaco.editor.create(container, {
|
||||||
|
model,
|
||||||
|
automaticLayout: true,
|
||||||
|
})
|
||||||
|
|
||||||
|
// When the editor is changed, update the configuration, but only if the user hasn't typed for 500ms and the JSON is valid
|
||||||
|
let timeout: number
|
||||||
|
editor.onDidChangeModelContent(() => {
|
||||||
|
clearTimeout(timeout)
|
||||||
|
timeout = setTimeout(() => {
|
||||||
|
save()
|
||||||
|
}, 500)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
onDestroy(() => {
|
||||||
|
if (editor) {
|
||||||
|
editor.dispose()
|
||||||
|
}
|
||||||
|
if (model) {
|
||||||
|
model.dispose()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<div bind:this={container} class="h-full w-full" />
|
Loading…
Add table
Add a link
Reference in a new issue