forked from MapComplete/MapComplete
		
	Add tutorial for tagRenderings
This commit is contained in:
		
							parent
							
								
									0da93d6067
								
							
						
					
					
						commit
						7d43bb5983
					
				
					 14 changed files with 99 additions and 18 deletions
				
			
		
							
								
								
									
										59
									
								
								Docs/Studio/TagRenderingIntro.md
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										59
									
								
								Docs/Studio/TagRenderingIntro.md
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,59 @@ | |||
| # How to work with TagRenderings | ||||
| 
 | ||||
| The information box shows various attributes of the selected feature in a human friendly way. | ||||
| 
 | ||||
| This is done by a **tagRendering** which converts attributes into text. | ||||
| 
 | ||||
| This can be done by using **predefined options** (mappings) or with a **render**-string | ||||
| 
 | ||||
| # Predefined options | ||||
| 
 | ||||
| A predefined option states that, `if` a certain tag is present, `then` a certain text should be shown. | ||||
| 
 | ||||
| For example, a playground may be lit or not. | ||||
| In OpenStreetMap, this is encoded with the tag `lit=yes` or `lit=no`. We might want to show `This playground is lit at night` and `This playground is not lit at night` to users of MapComplete. | ||||
| 
 | ||||
| This is what this will look like in the interface: | ||||
| 
 | ||||
| <img class="w-1/2" src="../../public/assets/docs/PredefinedOption.png"/> | ||||
| 
 | ||||
| # Substituting attributes | ||||
| 
 | ||||
| If none of the predefined options match, the string given in the `render`-field is used (under the question _"What text should be rendered?"_). | ||||
| 
 | ||||
| A special property about all shown texts is that, **if the name of a key appears between braces, this will be replaced by the corresponding value**. | ||||
| 
 | ||||
| For example, if the object has tags `min_age=3` and the text to display is `Accessible to kids older than {min_age} years`, then this will be displayed to the user as **Accessible to kids older than 3 years** | ||||
| 
 | ||||
| Note that this also works withing predifined options | ||||
| 
 | ||||
| # Special values | ||||
| 
 | ||||
| Special components can be summoned by calling them. For example, the relevant wikipedia will be displayed by entering the text `{wikipedia()}`. A table with opening hours is displayed with `{opening_hours()}`. For a full reference, [see the documentation](../SpecialRenderings.md). | ||||
| 
 | ||||
| # Requesting data with predefined options | ||||
| 
 | ||||
| These renderings can be turned into a way to contribute data easily. If a **question** is provided, then these renderings will be asked if unknown or gain the pencil to make changes.  | ||||
| 
 | ||||
| A predefined option will show up as an option that can be picked. | ||||
| <img class="w-1/2" src="../../public/assets/docs/QuestionPredefinedOptions.png"/> | ||||
| 
 | ||||
| # Requesting data with an input field | ||||
| 
 | ||||
| It is also possible to have a text field. For this, the **key** to write into must be given (_What is the name of the attribute that should be written to?_), in this case `max_age`. | ||||
| <img class="w-1/2" src="../../public/assets/docs/QuestionTextField.png"/> | ||||
| 
 | ||||
| # Combining predefined options and freeform text | ||||
| 
 | ||||
| A text field and predefined options can be combined. The contributor can then choose between a predefined option or filling out something. | ||||
| <img class="w-1/2"  src="../../public/assets/docs/QuestionCombined.png"/> | ||||
| 
 | ||||
| # Selecting multiple values | ||||
| 
 | ||||
| One can set a question to allow multiple answers. This works with predefined options or a freeform text field. | ||||
| 
 | ||||
| <img  class="w-1/2" src="../../public/assets/docs/QuestionMulti.png"/> | ||||
| 
 | ||||
| Note that these will be rendered as a list: | ||||
| 
 | ||||
| <img class="w-1/2" src="../../public/assets/docs/RenderMulti.png"/> | ||||
							
								
								
									
										
											BIN
										
									
								
								public/assets/docs/PredefinedOption.png
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								public/assets/docs/PredefinedOption.png
									
										
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							| After Width: | Height: | Size: 43 KiB | 
							
								
								
									
										
											BIN
										
									
								
								public/assets/docs/QuestionCombined.png
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								public/assets/docs/QuestionCombined.png
									
										
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							| After Width: | Height: | Size: 39 KiB | 
							
								
								
									
										
											BIN
										
									
								
								public/assets/docs/QuestionMulti.png
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								public/assets/docs/QuestionMulti.png
									
										
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							| After Width: | Height: | Size: 47 KiB | 
							
								
								
									
										
											BIN
										
									
								
								public/assets/docs/QuestionPredefinedOptions.png
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								public/assets/docs/QuestionPredefinedOptions.png
									
										
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							| After Width: | Height: | Size: 25 KiB | 
							
								
								
									
										
											BIN
										
									
								
								public/assets/docs/QuestionTextField.png
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								public/assets/docs/QuestionTextField.png
									
										
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							| After Width: | Height: | Size: 24 KiB | 
							
								
								
									
										
											BIN
										
									
								
								public/assets/docs/RenderMulti.png
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								public/assets/docs/RenderMulti.png
									
										
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							| After Width: | Height: | Size: 16 KiB | 
|  | @ -946,10 +946,6 @@ video { | |||
|   margin-right: 0.75rem; | ||||
| } | ||||
| 
 | ||||
| .mt-16 { | ||||
|   margin-top: 4rem; | ||||
| } | ||||
| 
 | ||||
| .mr-12 { | ||||
|   margin-right: 3rem; | ||||
| } | ||||
|  |  | |||
|  | @ -309,8 +309,8 @@ function generateWikipage() { | |||
|     }) | ||||
| } | ||||
| 
 | ||||
| function studioDocs() { | ||||
|     const lines = readFileSync("./Docs/Studio/Introduction.md", "utf8").split("\n") | ||||
| function studioDocsFor(source: string, target: string) { | ||||
|     const lines = readFileSync(source, "utf8").split("\n") | ||||
| 
 | ||||
|     const sections: string[][] = [] | ||||
|     let currentSection: string[] = [] | ||||
|  | @ -325,13 +325,21 @@ function studioDocs() { | |||
|     } | ||||
|     sections.push(currentSection) | ||||
|     writeFileSync( | ||||
|         "./src/assets/studio_introduction.json", | ||||
|         target, | ||||
|         JSON.stringify({ | ||||
|             sections: sections.map((s) => s.join("\n")).filter((s) => s.length > 0), | ||||
|         }) | ||||
|     ) | ||||
| } | ||||
| 
 | ||||
| function studioDocs() { | ||||
|     studioDocsFor("./Docs/Studio/Introduction.md", "./src/assets/studio_introduction.json") | ||||
|     studioDocsFor( | ||||
|         "./Docs/Studio/TagRenderingIntro.md", | ||||
|         "./src/assets/studio_tagrenderings_intro.json" | ||||
|     ) | ||||
| } | ||||
| 
 | ||||
| console.log("Starting documentation generation...") | ||||
| ScriptUtils.fixUtils() | ||||
| studioDocs() | ||||
|  |  | |||
|  | @ -168,7 +168,7 @@ | |||
|     </div> | ||||
|     {#if $highlightedItem !== undefined} | ||||
|       <FloatOver on:close={() => highlightedItem.setData(undefined)}> | ||||
|         <div class="mt-16"> | ||||
|         <div> | ||||
|           <TagRenderingInput path={$highlightedItem.path} {state} schema={$highlightedItem.schema} /> | ||||
|         </div> | ||||
|       </FloatOver> | ||||
|  |  | |||
|  | @ -21,6 +21,7 @@ import LayerConfig from "../../Models/ThemeConfig/LayerConfig" | |||
| import { LayoutConfigJson } from "../../Models/ThemeConfig/Json/LayoutConfigJson" | ||||
| import { PrepareTheme } from "../../Models/ThemeConfig/Conversion/PrepareTheme" | ||||
| import { ConversionContext } from "../../Models/ThemeConfig/Conversion/ConversionContext" | ||||
| import { LocalStorageSource } from "../../Logic/Web/LocalStorageSource" | ||||
| 
 | ||||
| export interface HighlightedTagRendering { | ||||
|     path: ReadonlyArray<string | number> | ||||
|  | @ -31,6 +32,9 @@ export abstract class EditJsonState<T> { | |||
|     public readonly schema: ConfigMeta[] | ||||
|     public readonly category: "layers" | "themes" | ||||
|     public readonly server: StudioServer | ||||
|     public readonly showIntro: UIEventSource<"no" | "intro" | "tagrenderings"> = <any>( | ||||
|         LocalStorageSource.Get("studio-show-intro", "intro") | ||||
|     ) | ||||
| 
 | ||||
|     public readonly expertMode: UIEventSource<boolean> | ||||
| 
 | ||||
|  |  | |||
|  | @ -17,7 +17,10 @@ import { TrashIcon } from "@rgossiaux/svelte-heroicons/outline"; | |||
| import questionableTagRenderingSchemaRaw from "../../assets/schemas/questionabletagrenderingconfigmeta.json"; | ||||
| import SchemaBasedField from "./SchemaBasedField.svelte"; | ||||
| import Region from "./Region.svelte"; | ||||
| import exp from "constants"; | ||||
| import NextButton from "../Base/NextButton.svelte"; | ||||
| import { QuestionMarkCircleIcon } from "@rgossiaux/svelte-heroicons/solid"; | ||||
| import { LocalStorageSource } from "../../Logic/Web/LocalStorageSource"; | ||||
| import { onMount } from "svelte"; | ||||
| 
 | ||||
| export let state: EditLayerState; | ||||
| export let schema: ConfigMeta; | ||||
|  | @ -25,7 +28,13 @@ export let path: (string | number)[]; | |||
| let expertMode = state.expertMode; | ||||
| const store = state.getStoreFor(path); | ||||
| let value = store.data; | ||||
| 
 | ||||
| let hasSeenIntro = UIEventSource.asBoolean(LocalStorageSource.Get("studio-seen-tagrendering-tutorial", "false")) | ||||
| onMount(() => { | ||||
| if(!hasSeenIntro.data){ | ||||
|   state.showIntro.setData("tagrenderings") | ||||
|   hasSeenIntro.setData(true) | ||||
| } | ||||
| }) | ||||
| /** | ||||
|  * Allows the theme builder to create 'writable' themes. | ||||
|  * Should only be enabled for 'tagrenderings' in the theme, if the source is OSM | ||||
|  | @ -99,7 +108,7 @@ const ignored = new Set(["labels", "description", "classes"]); | |||
| 
 | ||||
| const freeformSchemaAll = <ConfigMeta[]>questionableTagRenderingSchemaRaw | ||||
|   .filter(schema => schema.path.length == 2 && schema.path[0] === "freeform" && ($allowQuestions || schema.path[1] === "key")); | ||||
| let freeformSchema = $expertMode ? freeformSchemaAll : freeformSchemaAll.filter(schema => schema.hints?.group !== "expert") | ||||
| let freeformSchema = $expertMode ? freeformSchemaAll : freeformSchemaAll.filter(schema => schema.hints?.group !== "expert"); | ||||
| const missing: string[] = questionableTagRenderingSchemaRaw.filter(schema => schema.path.length >= 1 && !items.has(schema.path[0]) && !ignored.has(schema.path[0])).map(schema => schema.path.join(".")); | ||||
| console.log({ state }); | ||||
| 
 | ||||
|  | @ -112,7 +121,7 @@ console.log({ state }); | |||
|     <slot name="upper-right" /> | ||||
|   </div> | ||||
| {:else} | ||||
|   <div class="flex flex-col w-full p-1 gap-y-1"> | ||||
|   <div class="flex flex-col w-full p-1 gap-y-1 pr-12"> | ||||
|     <div class="flex justify-end"> | ||||
|       <slot name="upper-right" /> | ||||
|     </div> | ||||
|  | @ -157,5 +166,8 @@ console.log({ state }); | |||
|     {#each missing as field} | ||||
|       <SchemaBasedField {state} path={[...path,field]} schema={topLevelItems[field]} /> | ||||
|     {/each} | ||||
| 
 | ||||
|     <NextButton clss="small mt-8" on:click={() => state.showIntro.setData("tagrenderings")}><QuestionMarkCircleIcon class="h-6 w-6"/> Show the introduction again</NextButton> | ||||
|      | ||||
|   </div> | ||||
| {/if} | ||||
|  |  | |||
|  | @ -21,12 +21,13 @@ | |||
|   import FloatOver from "./Base/FloatOver.svelte"; | ||||
|   import Walkthrough from "./Walkthrough/Walkthrough.svelte"; | ||||
|   import * as intro from "../assets/studio_introduction.json"; | ||||
|   import * as intro_tagrenderings from "../assets/studio_tagrenderings_intro.json"; | ||||
|    | ||||
|   import { QuestionMarkCircleIcon } from "@babeard/svelte-heroicons/mini"; | ||||
|   import type { ConfigMeta } from "./Studio/configMeta"; | ||||
|   import EditTheme from "./Studio/EditTheme.svelte"; | ||||
|   import * as meta from "../../package.json"; | ||||
|   import Checkbox from "./Base/Checkbox.svelte"; | ||||
|   import exp from "constants"; | ||||
| 
 | ||||
|   export let studioUrl = window.location.hostname === "127.0.0.2" ? "http://127.0.0.1:1235" : "https://studio.mapcomplete.org"; | ||||
| 
 | ||||
|  | @ -61,13 +62,13 @@ | |||
| 
 | ||||
|   const layerSchema: ConfigMeta[] = <any>layerSchemaRaw; | ||||
|   let editLayerState = new EditLayerState(layerSchema, studio, osmConnection, { expertMode }); | ||||
|   let showIntro = editLayerState.showIntro | ||||
| 
 | ||||
|   const layoutSchema: ConfigMeta[] = <any>layoutSchemaRaw; | ||||
|   let editThemeState = new EditThemeState(layoutSchema, studio, { expertMode }); | ||||
| 
 | ||||
|   let layerId = editLayerState.configuration.map(layerConfig => layerConfig.id); | ||||
| 
 | ||||
|   let showIntro = UIEventSource.asBoolean(LocalStorageSource.Get("studio-show-intro", "true")); | ||||
|   const version = meta.version; | ||||
| 
 | ||||
|   async function editLayer(event: Event) { | ||||
|  | @ -162,7 +163,7 @@ | |||
|           <NextButton on:click={() => {editThemeState.configuration.setData({}); state = "editing_theme"}}> | ||||
|             Create a new theme | ||||
|           </NextButton> | ||||
|           <NextButton clss="small" on:click={() => {showIntro.setData(true)} }> | ||||
|           <NextButton clss="small" on:click={() => {showIntro.setData("intro")} }> | ||||
|             <QuestionMarkCircleIcon class="w-6 h-6" /> | ||||
|             Show the introduction again | ||||
|           </NextButton> | ||||
|  | @ -222,10 +223,10 @@ | |||
| </If> | ||||
| 
 | ||||
| 
 | ||||
| {#if $showIntro} | ||||
|   <FloatOver on:close={() => {showIntro.setData(false)}}> | ||||
| {#if {intro, tagrenderings: intro_tagrenderings}[$showIntro]?.sections} | ||||
|   <FloatOver on:close={() => {showIntro.setData("no")}}> | ||||
|     <div class="flex p-4 h-full pr-12"> | ||||
|       <Walkthrough pages={intro.sections} on:done={() => {showIntro.setData(false)}} /> | ||||
|       <Walkthrough pages={{intro, tagrenderings: intro_tagrenderings}[$showIntro]?.sections} on:done={() => {showIntro.setData("no")}} /> | ||||
|     </div> | ||||
|   </FloatOver> | ||||
| 
 | ||||
|  |  | |||
							
								
								
									
										1
									
								
								src/assets/studio_tagrenderings_intro.json
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								src/assets/studio_tagrenderings_intro.json
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1 @@ | |||
| {"sections":["# How to work with TagRenderings\n\nThe information box shows various attributes of the selected feature in a human friendly way.\n\nThis is done by a **tagRendering** which converts attributes into text.\n\nThis can be done by using **predefined options** (mappings) or with a **render**-string\n","# Predefined options\n\nA predefined option states that, `if` a certain tag is present, `then` a certain text should be shown.\n\nFor example, a playground may be lit or not.\nIn OpenStreetMap, this is encoded with the tag `lit=yes` or `lit=no`. We might want to show `This playground is lit at night` and `This playground is not lit at night` to users of MapComplete.\n\nThis is what this will look like in the interface:\n\n<img src=\"./assets/docs/PredefinedOption.png\"/>\n","# Substituting attributes\n\nIf none of the predefined options match, the string given in the `render`-field is used (under the question _\"What text should be rendered?\"_).\n\nA special property about all shown texts is that, **if the name of a key appears between braces, this will be replaced by the corresponding value**.\n\nFor example, if the object has tags `min_age=3` and the text to display is `Accessible to kids older than {min_age} years`, then this will be displayed to the user as **Accessible to kids older than 3 years**\n\nNote that this also works withing predifined options\n","# Special values\n\nSpecial components can be summoned by calling them. For example, the relevant wikipedia will be displayed by entering the text `{wikipedia()}`. A table with opening hours is displayed with `{opening_hours()}`. For a full reference, [see the documentation](../SpecialRenderings.md).\n","# Requesting data with predefined options\n\nThese renderings can be turned into a way to contribute data easily. If a **question** is provided, then these renderings will be asked if unknown or gain the pencil to make changes. \n\nA predefined option will show up as an option that can be picked.\n<img src=\"./assets/docs/QuestionPredefinedOptions.png\"/>\n","# Requesting data with an input field\n\nIt is also possible to have a text field. For this, the **key** to write into must be given (_What is the name of the attribute that should be written to?_), in this case `max_age`.\n<img src=\"./assets/docs/QuestionTextField.png\"/>\n","# Combining predefined options and freeform text\n\nA text field and predefined options can be combined. The contributor can then choose between a predefined option or filling out something.\n<img src=\"./assets/docs/QuestionCombined.png\"/>\n","# Selecting multiple values\n\nOne can set a question to allow multiple answers. This works with predefined options or a freeform text field.\n\n<img src=\"./assets/docs/QuestionMulti.png\"/>\n\nNote that these will be rendered as a list:\n\n<img src=\"./assets/docs/RenderMulti.png\"/>\n"]} | ||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue