diff --git a/Docs/Studio/TagRenderingIntro.md b/Docs/Studio/TagRenderingIntro.md new file mode 100644 index 000000000..231689497 --- /dev/null +++ b/Docs/Studio/TagRenderingIntro.md @@ -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: + + + +# 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. + + +# 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`. + + +# 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. + + +# Selecting multiple values + +One can set a question to allow multiple answers. This works with predefined options or a freeform text field. + + + +Note that these will be rendered as a list: + + diff --git a/public/assets/docs/PredefinedOption.png b/public/assets/docs/PredefinedOption.png new file mode 100644 index 000000000..5e50afa51 Binary files /dev/null and b/public/assets/docs/PredefinedOption.png differ diff --git a/public/assets/docs/QuestionCombined.png b/public/assets/docs/QuestionCombined.png new file mode 100644 index 000000000..49f9bb604 Binary files /dev/null and b/public/assets/docs/QuestionCombined.png differ diff --git a/public/assets/docs/QuestionMulti.png b/public/assets/docs/QuestionMulti.png new file mode 100644 index 000000000..cb23a4a48 Binary files /dev/null and b/public/assets/docs/QuestionMulti.png differ diff --git a/public/assets/docs/QuestionPredefinedOptions.png b/public/assets/docs/QuestionPredefinedOptions.png new file mode 100644 index 000000000..a2c9e8914 Binary files /dev/null and b/public/assets/docs/QuestionPredefinedOptions.png differ diff --git a/public/assets/docs/QuestionTextField.png b/public/assets/docs/QuestionTextField.png new file mode 100644 index 000000000..cdf28b776 Binary files /dev/null and b/public/assets/docs/QuestionTextField.png differ diff --git a/public/assets/docs/RenderMulti.png b/public/assets/docs/RenderMulti.png new file mode 100644 index 000000000..e1821104e Binary files /dev/null and b/public/assets/docs/RenderMulti.png differ diff --git a/public/css/index-tailwind-output.css b/public/css/index-tailwind-output.css index f569519d5..cfa520ffc 100644 --- a/public/css/index-tailwind-output.css +++ b/public/css/index-tailwind-output.css @@ -946,10 +946,6 @@ video { margin-right: 0.75rem; } -.mt-16 { - margin-top: 4rem; -} - .mr-12 { margin-right: 3rem; } diff --git a/scripts/generateDocs.ts b/scripts/generateDocs.ts index c98938456..d95a9a1a8 100644 --- a/scripts/generateDocs.ts +++ b/scripts/generateDocs.ts @@ -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() diff --git a/src/UI/Studio/EditLayer.svelte b/src/UI/Studio/EditLayer.svelte index a20b342a7..01e3105ff 100644 --- a/src/UI/Studio/EditLayer.svelte +++ b/src/UI/Studio/EditLayer.svelte @@ -168,7 +168,7 @@ {#if $highlightedItem !== undefined} highlightedItem.setData(undefined)}> -
+
diff --git a/src/UI/Studio/EditLayerState.ts b/src/UI/Studio/EditLayerState.ts index 705e1e0f0..5183ea724 100644 --- a/src/UI/Studio/EditLayerState.ts +++ b/src/UI/Studio/EditLayerState.ts @@ -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 @@ -31,6 +32,9 @@ export abstract class EditJsonState { public readonly schema: ConfigMeta[] public readonly category: "layers" | "themes" public readonly server: StudioServer + public readonly showIntro: UIEventSource<"no" | "intro" | "tagrenderings"> = ( + LocalStorageSource.Get("studio-show-intro", "intro") + ) public readonly expertMode: UIEventSource diff --git a/src/UI/Studio/TagRenderingInput.svelte b/src/UI/Studio/TagRenderingInput.svelte index d74e69ca5..9fc1caf41 100644 --- a/src/UI/Studio/TagRenderingInput.svelte +++ b/src/UI/Studio/TagRenderingInput.svelte @@ -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 = 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 });
{:else} -
+
@@ -157,5 +166,8 @@ console.log({ state }); {#each missing as field} {/each} + + state.showIntro.setData("tagrenderings")}> Show the introduction again +
{/if} diff --git a/src/UI/StudioGUI.svelte b/src/UI/StudioGUI.svelte index 62f8a2051..4a19d812c 100644 --- a/src/UI/StudioGUI.svelte +++ b/src/UI/StudioGUI.svelte @@ -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[] = layerSchemaRaw; let editLayerState = new EditLayerState(layerSchema, studio, osmConnection, { expertMode }); + let showIntro = editLayerState.showIntro const layoutSchema: ConfigMeta[] = 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 @@ {editThemeState.configuration.setData({}); state = "editing_theme"}}> Create a new theme - {showIntro.setData(true)} }> + {showIntro.setData("intro")} }> Show the introduction again @@ -222,10 +223,10 @@ -{#if $showIntro} - {showIntro.setData(false)}}> +{#if {intro, tagrenderings: intro_tagrenderings}[$showIntro]?.sections} + {showIntro.setData("no")}}>
- {showIntro.setData(false)}} /> + {showIntro.setData("no")}} />
diff --git a/src/assets/studio_tagrenderings_intro.json b/src/assets/studio_tagrenderings_intro.json new file mode 100644 index 000000000..843b57db5 --- /dev/null +++ b/src/assets/studio_tagrenderings_intro.json @@ -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\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\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\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\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\n\nNote that these will be rendered as a list:\n\n\n"]} \ No newline at end of file