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 class="flex h-full w-full flex-row justify-between overflow-y-auto"> | ||||||
|  |             <div class="literal-code h-full w-5/6 overflow-y-auto"> | ||||||
|  |               <RawEditor {state} /> | ||||||
|  |             </div> | ||||||
|  |             <div class="h-full w-1/6"> | ||||||
|               <div> |               <div> | ||||||
|             The testobject (which is used to render the questions in the 'information panel' item |                 The testobject (which is used to render the questions in the 'information panel' | ||||||
|             has the following tags: |                 item has the following tags: | ||||||
|               </div> |               </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