diff --git a/UnicadoGUI/Frontend/src/lib/BaseButton.svelte b/UnicadoGUI/Frontend/src/lib/components/Buttons/BaseButton.svelte similarity index 100% rename from UnicadoGUI/Frontend/src/lib/BaseButton.svelte rename to UnicadoGUI/Frontend/src/lib/components/Buttons/BaseButton.svelte diff --git a/UnicadoGUI/Frontend/src/lib/DeleteProjectByIdButton.svelte b/UnicadoGUI/Frontend/src/lib/components/Buttons/DeleteProjectByIdButton.svelte similarity index 93% rename from UnicadoGUI/Frontend/src/lib/DeleteProjectByIdButton.svelte rename to UnicadoGUI/Frontend/src/lib/components/Buttons/DeleteProjectByIdButton.svelte index 9bca9e86e45379fd9966c85d4ca49bfc832b199e..7ff8e9d75a8c2f622af69e4481f971a28fcd9083 100644 --- a/UnicadoGUI/Frontend/src/lib/DeleteProjectByIdButton.svelte +++ b/UnicadoGUI/Frontend/src/lib/components/Buttons/DeleteProjectByIdButton.svelte @@ -1,6 +1,6 @@ <script lang="ts"> import { Modal, ModalHeader, ModalBody, ModalFooter, Button, Icon } from "@sveltestrap/sveltestrap"; - import { removeProjectById, getProjectNameById } from '../stores/projects'; + import { removeProjectById, getProjectNameById } from '../../../stores/projects'; import { createEventDispatcher } from 'svelte'; export let projectId : number; diff --git a/UnicadoGUI/Frontend/src/lib/DeleteProjectByNameButton.svelte b/UnicadoGUI/Frontend/src/lib/components/Buttons/DeleteProjectByNameButton.svelte similarity index 96% rename from UnicadoGUI/Frontend/src/lib/DeleteProjectByNameButton.svelte rename to UnicadoGUI/Frontend/src/lib/components/Buttons/DeleteProjectByNameButton.svelte index b40085867692f07160f1a6e1435ee8d5ecb2738a..77a0e943098ec7dbef489eef6749e57879ae12e9 100644 --- a/UnicadoGUI/Frontend/src/lib/DeleteProjectByNameButton.svelte +++ b/UnicadoGUI/Frontend/src/lib/components/Buttons/DeleteProjectByNameButton.svelte @@ -7,7 +7,7 @@ Button, Icon, } from "@sveltestrap/sveltestrap"; - import { userProjects, removeProject } from "../stores/projects"; + import { userProjects, removeProject } from "../../../stores/projects"; import { onDestroy, createEventDispatcher } from "svelte"; let projects: any = []; export let projectToRemove = ""; diff --git a/UnicadoGUI/Frontend/src/lib/LoadProjectsFromServerButton.svelte b/UnicadoGUI/Frontend/src/lib/components/Buttons/LoadProjectsFromServerButton.svelte similarity index 90% rename from UnicadoGUI/Frontend/src/lib/LoadProjectsFromServerButton.svelte rename to UnicadoGUI/Frontend/src/lib/components/Buttons/LoadProjectsFromServerButton.svelte index 65d60bac61901938fa2e09f243eb987fb55d3eb3..6c894919b757525582e7f16d57a0af6a8d5af1f0 100644 --- a/UnicadoGUI/Frontend/src/lib/LoadProjectsFromServerButton.svelte +++ b/UnicadoGUI/Frontend/src/lib/components/Buttons/LoadProjectsFromServerButton.svelte @@ -1,6 +1,6 @@ <script> import { Button, Icon } from "@sveltestrap/sveltestrap"; - import { loadProjectsFromServer } from "../stores/projects"; + import { loadProjectsFromServer } from "../../../stores/projects.js"; const triggerFileDownload = async () => { try { diff --git a/UnicadoGUI/Frontend/src/lib/LoadProjectsFromZipButton.svelte b/UnicadoGUI/Frontend/src/lib/components/Buttons/LoadProjectsFromZipButton.svelte similarity index 93% rename from UnicadoGUI/Frontend/src/lib/LoadProjectsFromZipButton.svelte rename to UnicadoGUI/Frontend/src/lib/components/Buttons/LoadProjectsFromZipButton.svelte index 8baa6b8427afda732073758ff9997baa6bdcecbe..ef71200874aab6ab06e93a46e427f06edf22e81f 100644 --- a/UnicadoGUI/Frontend/src/lib/LoadProjectsFromZipButton.svelte +++ b/UnicadoGUI/Frontend/src/lib/components/Buttons/LoadProjectsFromZipButton.svelte @@ -1,6 +1,6 @@ <script lang="ts"> import { Button, Icon } from "@sveltestrap/sveltestrap"; - import { loadProjectsFromZip } from "../stores/projects"; + import { loadProjectsFromZip } from "../../../stores/projects"; function handleFileUpload(event: Event) { const target = event.target as HTMLInputElement; diff --git a/UnicadoGUI/Frontend/src/lib/NewProjectButton.svelte b/UnicadoGUI/Frontend/src/lib/components/Buttons/NewProjectButton.svelte similarity index 96% rename from UnicadoGUI/Frontend/src/lib/NewProjectButton.svelte rename to UnicadoGUI/Frontend/src/lib/components/Buttons/NewProjectButton.svelte index 06636fe9ef086e07fab7d4aa2e5f10052a1ff17e..7bf9299777d6ab4b426ce354eb570f9ac0556626 100644 --- a/UnicadoGUI/Frontend/src/lib/NewProjectButton.svelte +++ b/UnicadoGUI/Frontend/src/lib/components/Buttons/NewProjectButton.svelte @@ -7,7 +7,7 @@ Button, Icon, } from "@sveltestrap/sveltestrap"; - import { addProject } from "../stores/projects"; + import { addProject } from "../../../stores/projects.js"; import { createEventDispatcher } from "svelte"; export let projectName = ""; diff --git a/UnicadoGUI/Frontend/src/lib/NotSavedWarningButton.svelte b/UnicadoGUI/Frontend/src/lib/components/Buttons/NotSavedWarningButton.svelte similarity index 78% rename from UnicadoGUI/Frontend/src/lib/NotSavedWarningButton.svelte rename to UnicadoGUI/Frontend/src/lib/components/Buttons/NotSavedWarningButton.svelte index d56eb58c4c9faf0e3d689c24b45caba4c16f1fd3..9e8874e8924e3c34d3822659ab41ff001362b372 100644 --- a/UnicadoGUI/Frontend/src/lib/NotSavedWarningButton.svelte +++ b/UnicadoGUI/Frontend/src/lib/components/Buttons/NotSavedWarningButton.svelte @@ -7,9 +7,9 @@ Button, Icon, } from "@sveltestrap/sveltestrap"; - import SaveProjectsToZipButton from "$lib/SaveProjectsToZipButton.svelte"; - import SaveProjectsToServerButton from "$lib/SaveProjectsToServerButton.svelte"; - import { isSavePending } from "../stores/projects"; + import SaveProjectsToZipButton from "$lib/components/Buttons/SaveProjectsToZipButton.svelte"; + import SaveProjectsToServerButton from "$lib/components/Buttons/SaveProjectsToServerButton.svelte"; + import { isSavePending } from "../../../stores/projects"; let isModalOpen = false; $: buttonColor = $isSavePending ? "danger" : "secondary"; diff --git a/UnicadoGUI/Frontend/src/lib/SaveProjectsToServerButton.svelte b/UnicadoGUI/Frontend/src/lib/components/Buttons/SaveProjectsToServerButton.svelte similarity index 90% rename from UnicadoGUI/Frontend/src/lib/SaveProjectsToServerButton.svelte rename to UnicadoGUI/Frontend/src/lib/components/Buttons/SaveProjectsToServerButton.svelte index ef4e1c5bf0359a700843efcc3695b01994f370c7..6f4fca94f3a4fbfdf38fcee1e3cb4613b6146f31 100644 --- a/UnicadoGUI/Frontend/src/lib/SaveProjectsToServerButton.svelte +++ b/UnicadoGUI/Frontend/src/lib/components/Buttons/SaveProjectsToServerButton.svelte @@ -1,6 +1,6 @@ <script> import { Button, Icon } from "@sveltestrap/sveltestrap"; - import { saveProjectsOnServer } from "../stores/projects"; + import { saveProjectsOnServer } from "../../../stores/projects.js"; const triggerFileUpload = async () => { try { diff --git a/UnicadoGUI/Frontend/src/lib/SaveProjectsToZipButton.svelte b/UnicadoGUI/Frontend/src/lib/components/Buttons/SaveProjectsToZipButton.svelte similarity index 90% rename from UnicadoGUI/Frontend/src/lib/SaveProjectsToZipButton.svelte rename to UnicadoGUI/Frontend/src/lib/components/Buttons/SaveProjectsToZipButton.svelte index 9b240933773c1fb54aa3d5693d1b76998e84a28e..982e32cc13d88840fc63b365cbf20120fc9e44e4 100644 --- a/UnicadoGUI/Frontend/src/lib/SaveProjectsToZipButton.svelte +++ b/UnicadoGUI/Frontend/src/lib/components/Buttons/SaveProjectsToZipButton.svelte @@ -1,6 +1,6 @@ <script lang="ts"> import { Button, Icon } from "@sveltestrap/sveltestrap"; - import { saveProjectsAsZip } from '../stores/projects'; + import { saveProjectsAsZip } from '../../../stores/projects'; const handleSave = async () => { try { diff --git a/UnicadoGUI/Frontend/src/lib/components/ConfigView.svelte b/UnicadoGUI/Frontend/src/lib/components/ConfigView.svelte new file mode 100644 index 0000000000000000000000000000000000000000..11d4ed7d3af5efe0f7c7dc2610bd52ed02ae84a4 --- /dev/null +++ b/UnicadoGUI/Frontend/src/lib/components/ConfigView.svelte @@ -0,0 +1,114 @@ +<script lang="ts"> + import { Input, Styles, Tooltip } from "@sveltestrap/sveltestrap"; + import { + calcStepSize, + formatLabel, + } from "$lib/utilities/utilities"; + import { createEventDispatcher } from 'svelte'; + + export let data:any; + const dispatch = createEventDispatcher(); + + function checkModalChanged() { + data.changed = true; + dispatch('update', {data}) + } + + function validateNumber(index: number) { + console.log("validateNumber"); + if (data.html[index].bind.value < data.html[index].min) { + data.html[index].bind.value = data.html[index].min; + } else if ( + data.html[index].bind.value > data.html[index].max + ) { + data.html[index].bind.value = data.html[index].max; + } + } +</script> + +{#each data.html as element, index} + {#if element.type == "switch"} + <Input + id="uiElement{index}" + type="switch" + label={formatLabel(element.label)} + bind:checked={element.bind.value} + on:change={() => checkModalChanged()} + /> + <Tooltip target="uiElement{index}" placement="left"> + {element.description} + </Tooltip> + {:else if element.type == "label"} + <h3>{formatLabel(element.label)}</h3> + {:else if element.type == "selector"} + <h6>{formatLabel(element.label)}</h6> + <Input + id="uiElement{index}" + type="select" + bind:value={element.bind.value} + on:change={() => checkModalChanged()} + > + {#each element.options as option} + <option>{option}</option> + {/each} + </Input> + <Tooltip target="uiElement{index}" placement="left"> + {element.description} + </Tooltip> + {:else if element.type == "textfield"} + <h7>{formatLabel(element.label)}</h7> + <Input + id="uiElement{index}" + type="text" + bind:value={element.bind.value} + on:change={() => checkModalChanged()} + /> + <Tooltip target="uiElement{index}" placement="left"> + {element.description} + </Tooltip> + {:else if element.type == "numberfield"} + {#if element.unit != "1" && element.unit != "-" && element.unit != "count"} + <h7>{formatLabel(element.label)} (in {element.unit})</h7> + {:else} + <h7>{formatLabel(element.label)}</h7> + {/if} + {#if element.max != Number.POSITIVE_INFINITY && element.min != Number.NEGATIVE_INFINITY} + <Input + type="range" + min={element.min} + max={element.max} + step="0.01" + bind:value={element.bind.value} + placeholder="range placeholder" + on:change={() => checkModalChanged()} + /> + <Input + id="uiElement{index}" + type="number" + min={element.min} + max={element.max} + step="0.01" + bind:value={element.bind.value} + on:change={() => checkModalChanged()} + on:blur={() => validateNumber(index)} + /> + {:else} + <Input + id="uiElement{index}" + type="number" + min={element.min} + max={element.max} + step={calcStepSize(element.bind.value)} + bind:value={element.bind.value} + on:change={() => checkModalChanged()} + on:blur={() => validateNumber(index)} + /> + {/if} + + <Tooltip target="uiElement{index}" placement="left"> + {element.description} + </Tooltip> + {/if} +{/each} + +<Styles /> diff --git a/UnicadoGUI/Frontend/src/routes/model/InPlaceEdit.svelte b/UnicadoGUI/Frontend/src/lib/components/InPlaceEdit.svelte similarity index 100% rename from UnicadoGUI/Frontend/src/routes/model/InPlaceEdit.svelte rename to UnicadoGUI/Frontend/src/lib/components/InPlaceEdit.svelte diff --git a/UnicadoGUI/Frontend/src/lib/components/TabComponent.svelte b/UnicadoGUI/Frontend/src/lib/components/TabComponent.svelte index 6d5b8d95596314295df4dcefd37eb5b042cfad64..e013c6327386ff1ce536eb0aa4582aaf948930b5 100644 --- a/UnicadoGUI/Frontend/src/lib/components/TabComponent.svelte +++ b/UnicadoGUI/Frontend/src/lib/components/TabComponent.svelte @@ -1,18 +1,10 @@ <script lang="ts"> - import { Input, Styles, Tooltip } from "@sveltestrap/sveltestrap"; + import { Styles} from "@sveltestrap/sveltestrap"; import { onMount } from "svelte"; import { - SettingsSwitch, - SettingsSelector, - SettingsTextfield, - SettingsNumberfield, - SettingsLabel, - } from "$lib/settings/SettingsUi"; - import { - isNumber, - calcStepSize, - formatLabel, + generateHTML } from "$lib/utilities/utilities"; + import ConfigView from "$lib/components/ConfigView.svelte"; export let data; @@ -24,198 +16,20 @@ name: "", response: {}, html: [], + changed: false, }; onMount(async () => { console.log(data); - generateHTML(data); + generateHTML(data,modalModule.html); modalModule.html = [...modalModule.html]; }); - - function generateHTML(json: dictionary) { - Object.entries(json).forEach(([key, value]) => { - if (value == null) { - return; - } - if (value.hasOwnProperty("value")) { - if ( - value["@description"] && - value["@description"].toLowerCase().includes("switch") - ) { - value.value = value.value === "true" || value.value === "1"; //transform string input to boolean for easier binding - modalModule.html.push( - new SettingsSwitch(key, value["@description"], value), - ); - } else if ( - value["@description"] && - value["@description"].toLowerCase().includes("selector") - ) { - let options = []; - for (let i = 0; i < 5; i++) { - if (value["@description"].includes("mode_" + i)) { - options.push("mode_" + i); - } - } - modalModule.html.push( - new SettingsSelector( - key, - value["@description"], - value, - options, - ), - ); - } else if (isNumber(value.value)) { - value.value = parseFloat(value.value); - let numberField = new SettingsNumberfield( - key, - value["@description"], - value, - ); - if ( - value.hasOwnProperty("lower_boundary") && - isNumber(value["lower_boundary"]) - ) { - numberField.setMin(value["lower_boundary"]); - } - if ( - value.hasOwnProperty("upper_boundary") && - isNumber(value["upper_boundary"]) - ) { - numberField.setMax(value["upper_boundary"]); - } - if (value.hasOwnProperty("@unit")) { - numberField.setUnit(value["@unit"]); - } //spaeter entweder unit oder Unit in XML - if (value.hasOwnProperty("@Unit")) { - numberField.setUnit(value["@Unit"]); - } - if (value.hasOwnProperty("unit")) { - numberField.setUnit(value["unit"]); - } - if (value.hasOwnProperty("Unit")) { - numberField.setUnit(value["Unit"]); - } - modalModule.html.push(numberField); - } else { - modalModule.html.push( - new SettingsTextfield( - key, - value["@description"], - value, - key, - ), - ); - } - } else if (typeof value === "object") { - modalModule.html.push(new SettingsLabel(key)); - generateHTML(json[key]); - } else { - return; - } - }); - } - - let modalChanged: boolean = false; - function checkModalChanged() { - modalChanged = true; - modalChanged = modalChanged; - } - - function validateNumber(index: number) { - console.log("validateNumber"); - if (modalModule.html[index].bind.value < modalModule.html[index].min) { - modalModule.html[index].bind.value = modalModule.html[index].min; - } else if ( - modalModule.html[index].bind.value > modalModule.html[index].max - ) { - modalModule.html[index].bind.value = modalModule.html[index].max; - } + function handleUpdate(event:any){ + modalModule = event.detail.data; + modalModule = modalModule; } </script> -{#each modalModule.html as element, index} - {#if element.type == "switch"} - <Input - id="uiElement{index}" - type="switch" - label={formatLabel(element.label)} - bind:checked={element.bind.value} - on:change={() => checkModalChanged()} - /> - <Tooltip target="uiElement{index}" placement="left"> - {element.description} - </Tooltip> - {:else if element.type == "label"} - <h3>{formatLabel(element.label)}</h3> - {:else if element.type == "selector"} - <h6>{formatLabel(element.label)}</h6> - <Input - id="uiElement{index}" - type="select" - bind:value={element.bind.value} - on:change={() => checkModalChanged()} - > - {#each element.options as option} - <option>{option}</option> - {/each} - </Input> - <Tooltip target="uiElement{index}" placement="left"> - {element.description} - </Tooltip> - {:else if element.type == "textfield"} - <h7>{formatLabel(element.label)}</h7> - <Input - id="uiElement{index}" - type="text" - bind:value={element.bind.value} - on:change={() => checkModalChanged()} - /> - <Tooltip target="uiElement{index}" placement="left"> - {element.description} - </Tooltip> - {:else if element.type == "numberfield"} - {#if element.unit != "1" && element.unit != "-" && element.unit != "count"} - <h7>{formatLabel(element.label)} (in {element.unit})</h7> - {:else} - <h7>{formatLabel(element.label)}</h7> - {/if} - {#if element.max != Number.POSITIVE_INFINITY && element.min != Number.NEGATIVE_INFINITY} - <Input - type="range" - min={element.min} - max={element.max} - step="0.01" - bind:value={element.bind.value} - placeholder="range placeholder" - on:change={() => checkModalChanged()} - /> - <Input - id="uiElement{index}" - type="number" - min={element.min} - max={element.max} - step="0.01" - bind:value={element.bind.value} - on:change={() => checkModalChanged()} - on:blur={() => validateNumber(index)} - /> - {:else} - <Input - id="uiElement{index}" - type="number" - min={element.min} - max={element.max} - step={calcStepSize(element.bind.value)} - bind:value={element.bind.value} - on:change={() => checkModalChanged()} - on:blur={() => validateNumber(index)} - /> - {/if} - - <Tooltip target="uiElement{index}" placement="left"> - {element.description} - </Tooltip> - {/if} -{/each} +<ConfigView data={modalModule} on:update={handleUpdate}/> <Styles /> diff --git a/UnicadoGUI/Frontend/src/lib/index.ts b/UnicadoGUI/Frontend/src/lib/index.ts index 856f2b6c38aec1085db88189bcf492dbb49a1c45..0d7c663c49ab824aef78132f6d082f072f6bec79 100644 --- a/UnicadoGUI/Frontend/src/lib/index.ts +++ b/UnicadoGUI/Frontend/src/lib/index.ts @@ -1 +1,2 @@ // place files you want to import through the `$lib` alias in this folder. + diff --git a/UnicadoGUI/Frontend/src/lib/settings/SettingsUi.ts b/UnicadoGUI/Frontend/src/lib/settings/SettingsUi.ts index e2d87279780ef9bb8c7d2991c3974b62f3950fe3..a75057970d440a395fb1ab949e3620216894bcf2 100644 --- a/UnicadoGUI/Frontend/src/lib/settings/SettingsUi.ts +++ b/UnicadoGUI/Frontend/src/lib/settings/SettingsUi.ts @@ -84,11 +84,17 @@ export class SettingsNumberfield implements SettingsUi { } } -export class SettingsLabel { +export class SettingsLabel implements SettingsUi{ readonly type = "label"; label: string; + bind: string; + description: string; - constructor(label: string) { + constructor(label: string,description: string, bind: string) { this.label = label; + this.description = description; + this.bind = bind; } + + } diff --git a/UnicadoGUI/Frontend/src/lib/utilities/utilities.ts b/UnicadoGUI/Frontend/src/lib/utilities/utilities.ts index ba33364b778c88b35f60ce831b4100de61695c88..6012b8dacd7811b805edb37d174a0da57e01ffc0 100644 --- a/UnicadoGUI/Frontend/src/lib/utilities/utilities.ts +++ b/UnicadoGUI/Frontend/src/lib/utilities/utilities.ts @@ -1,3 +1,17 @@ +import { + SettingsLabel, + SettingsNumberfield, + SettingsSelector, + SettingsSwitch, + SettingsTextfield, type SettingsUi +} from "$lib/settings/SettingsUi"; + +//template for datatype of dictionaries, only to avoid errors in Typescript +export type dictionary = { + [key: string]: any +} + +//Test if the string includes a number export function isNumber(n: string) { return !isNaN(parseFloat(n)) && isFinite(parseFloat(n)); } @@ -16,4 +30,48 @@ export function calcStepSize(value: number) { //Replacing underscores to space export function formatLabel(label: string) { return label.replaceAll("_", " "); -} \ No newline at end of file +} + +/*Parse json response to matching SettingsUi-Elements + * to portray it later in the DOM + * recursive Function + * */ +export function generateHTML(json: dictionary, html : SettingsUi[]) { + Object.entries(json).forEach(([key, value]) => { + if (value == null) { + return + } + if (value.hasOwnProperty("value")) { + if (value["@description"] && value["@description"].toLowerCase().includes("switch")) { + value.value = (value.value === 'true' || value.value === "1") //transform string input to boolean for easier binding + html.push(new SettingsSwitch(key, value["@description"], value)) + }else if(value["@description"] && (value["@description"].toLowerCase().includes("selector:")||value["@description"].toLowerCase().startsWith("selector"))){ + let options = [] + if(value["@description"].includes("mode_" )){ + options = value["@description"].match(/mode_\d+/g) + }else{ + let selectorString = value["@description"].split("Selector:")[1] + options=selectorString.split(" / ") + } + html.push(new SettingsSelector(key, value["@description"], value, options)) + }else if(isNumber(value.value)){ + value.value=parseFloat(value.value); + let numberField = new SettingsNumberfield(key, value["@description"], value) + if(value.hasOwnProperty('lower_boundary')&&isNumber(value['lower_boundary'])){numberField.setMin(value['lower_boundary'])} + if(value.hasOwnProperty('upper_boundary')&&isNumber(value['upper_boundary'])){numberField.setMax(value['upper_boundary'])} + if(value.hasOwnProperty("@unit")){numberField.setUnit(value["@unit"])}//spaeter entweder unit oder Unit in XML + if(value.hasOwnProperty("@Unit")){numberField.setUnit(value["@Unit"])} + if(value.hasOwnProperty("unit")){numberField.setUnit(value["unit"])} + if(value.hasOwnProperty("Unit")){numberField.setUnit(value["Unit"])} + html.push(numberField); + } else { + html.push(new SettingsTextfield(key, value["@description"], value, key)) + } + } else if (typeof value === "object") { + html.push(new SettingsLabel(key,'','')) + generateHTML(json[key],html) + } else { + return; + } + }) +} diff --git a/UnicadoGUI/Frontend/src/routes/+layout.svelte b/UnicadoGUI/Frontend/src/routes/+layout.svelte index 161d8417aed4dc237542ab4abb6165abb1e3daa9..e70addb2cc2e5357ada17480ba3132daeff4eb77 100644 --- a/UnicadoGUI/Frontend/src/routes/+layout.svelte +++ b/UnicadoGUI/Frontend/src/routes/+layout.svelte @@ -12,8 +12,8 @@ } from "@sveltestrap/sveltestrap"; import { userProjects } from "../stores/projects"; import { onMount } from "svelte"; - import DeleteProjectByIdButton from "$lib/DeleteProjectByIdButton.svelte"; - import NotSavedWarningButton from "$lib/NotSavedWarningButton.svelte"; + import DeleteProjectByIdButton from "$lib/components/Buttons/DeleteProjectByIdButton.svelte"; + import NotSavedWarningButton from "$lib/components/Buttons/NotSavedWarningButton.svelte"; let projects: any[] = []; onMount(() => { diff --git a/UnicadoGUI/Frontend/src/routes/+page.svelte b/UnicadoGUI/Frontend/src/routes/+page.svelte index f6317e014443521b1f1f7223e451a66fd8106869..8b852b9c5d560d48b3a106ff40c35acd228384db 100644 --- a/UnicadoGUI/Frontend/src/routes/+page.svelte +++ b/UnicadoGUI/Frontend/src/routes/+page.svelte @@ -4,12 +4,12 @@ let projectName = ""; let projectToRemove = ""; - import NewProjectButton from "$lib/NewProjectButton.svelte"; - import DeleteProjectButton from "$lib/DeleteProjectByNameButton.svelte"; - import SaveProjectsToZipButton from "$lib/SaveProjectsToZipButton.svelte"; - import LoadProjectsFromZipButton from "$lib/LoadProjectsFromZipButton.svelte"; - import LoadProjectFromServerButton from "$lib/LoadProjectsFromServerButton.svelte"; - import SaveProjectsToServerButton from "$lib/SaveProjectsToServerButton.svelte"; + import NewProjectButton from "$lib/components/Buttons/NewProjectButton.svelte"; + import DeleteProjectButton from "$lib/components/Buttons/DeleteProjectByNameButton.svelte"; + import SaveProjectsToZipButton from "$lib/components/Buttons/SaveProjectsToZipButton.svelte"; + import LoadProjectsFromZipButton from "$lib/components/Buttons/LoadProjectsFromZipButton.svelte"; + import LoadProjectFromServerButton from "$lib/components/Buttons/LoadProjectsFromServerButton.svelte"; + import SaveProjectsToServerButton from "$lib/components/Buttons/SaveProjectsToServerButton.svelte"; </script> <h1>Welcome to the UNICADO WebApp</h1> diff --git a/UnicadoGUI/Frontend/src/routes/model/+page.svelte b/UnicadoGUI/Frontend/src/routes/model/+page.svelte index bf0d19b1f5f60162d7f74ba4a3b88d85fb31a5dd..93ec7737b3f9902a526c77ef6786f7c3d0686c2b 100644 --- a/UnicadoGUI/Frontend/src/routes/model/+page.svelte +++ b/UnicadoGUI/Frontend/src/routes/model/+page.svelte @@ -14,97 +14,13 @@ Styles, Tooltip } from "@sveltestrap/sveltestrap"; import {onMount} from "svelte"; - import InPlaceEdit from './InPlaceEdit.svelte'; - - //Interface for Ui-elements in module settings and it's implementations - interface SettingsUi { - type: string; - label: string; - description: string; - bind: string; - } - - class SettingsSwitch implements SettingsUi { - readonly type = "switch"; - label: string; - description: string; - bind: string; - - constructor(label: string, description: string, bind: string) { - this.bind = bind; - this.label = label; - this.description = description; - } - } - - class SettingsSelector implements SettingsUi { - readonly type = "selector"; - label: string; - description: string; - bind: string; - options: string[]; - - constructor(label: string, description: string, bind: string, options: string[]) { - this.bind = bind; - this.label = label; - this.description = description; - this.options = options; - } - } - - class SettingsTextfield implements SettingsUi { - readonly type = "textfield"; - label: string; - description: string; - bind: string; - input: string; - - constructor(label: string, description: string, bind: string, input: string) { - this.bind = bind; - this.label = label; - this.description = description; - this.input = input; - } - } - - class SettingsNumberfield implements SettingsUi { - readonly type = "numberfield"; - label: string; - description: string; - bind: string; - unit: string = '-'; - max: number = Number.POSITIVE_INFINITY; - min: number = Number.NEGATIVE_INFINITY; - - constructor(label: string, description: string, bind: string) { - this.bind = bind; - this.label = label; - this.description = description; - } - setMax(max:number){ - this.max=max; - } - setMin(min:number){ - this.min=min; - } - setUnit(unit:string){ - this.unit=unit; - } - } - - class SettingsLabel { - readonly type = "label"; - label: string; - - constructor(label: string) { - this.label = label; - } - } - - //template for datatype of dictionaries, only to avoid errors in Typescript - type dictionary = { - [key: string]: any - } + import InPlaceEdit from '$lib/components/InPlaceEdit.svelte'; + import { + generateHTML, + formatLabel, + } from "$lib/utilities/utilities.js"; + import type {dictionary} from "$lib/utilities/utilities.js"; + import ConfigView from "$lib/components/ConfigView.svelte"; /* Dictionary with information of the module * including the json response and the settings elements it needs to implement in the dom @@ -112,7 +28,8 @@ let modalModule: dictionary = { name: '', response: {}, - html: [] + html: [], + changed: false } //Class for a group of modules @@ -137,9 +54,6 @@ //Flag for Alarm which is invoked by changes let saveAlert = false; - //Flag for changes in settings modal by the user - let modalChanged: boolean = false; - //Array of all the groups of the model let groups: group[] = []; @@ -162,9 +76,7 @@ //Function for loading list of available modules from server async function loadAvailableModules() { - //transitionally reading from convergenceloop_conf.xml - //TO-DO: replace with something more recent - const res = await fetch("http://127.0.0.1:8000//modules/selection"); + const res = await fetch("http://127.0.0.1:8000/modules/selection"); const data = await res.json(); modules = data['availableModules']; modules = Array.from(new Set(modules)); @@ -212,19 +124,19 @@ modalModule.response = JSON.parse(data); modalModule.html = []; modalModule = modalModule; - generateHTML(modalModule.response); - modalChanged = false; - modalChanged = modalChanged; + generateHTML(modalModule.response,modalModule.html); + modalModule.changed = false; + modalModule.changed = modalModule.changed; modalModule = modalModule; + } catch (error) { console.error(error); modalModule.name = "no"; modalModule.response = {}; - modalModule.html = []; - modalModule.html.push(new SettingsLabel("No config found!")); } } + //Wrapper to give Click-Event handler a synchronous Output function getModuleConfClickWrapper(item: string) { getModuleConf(item).catch(console.error); @@ -235,49 +147,6 @@ }; } - /*Parse json response to matching SettingsUi-Elements - * to portray it later in the DOM - * recursive Function - * */ - function generateHTML(json: dictionary) { - Object.entries(json).forEach(([key, value]) => { - if (value == null) { - return - } - if (value.hasOwnProperty("value")) { - if (value["@description"] && value["@description"].toLowerCase().includes("switch")) { - value.value = (value.value === 'true' || value.value === "1") //transform string input to boolean for easier binding - modalModule.html.push(new SettingsSwitch(key, value["@description"], value)) - } else if (value["@description"] && value["@description"].toLowerCase().includes("selector")) { - let options = []; - for (let i = 0; i < 5; i++) { - if (value["@description"].includes("mode_" + i)) { - options.push("mode_" + i) - } - } - modalModule.html.push(new SettingsSelector(key, value["@description"], value, options)) - }else if(isNumber(value.value)){ - value.value=parseFloat(value.value); - let numberField = new SettingsNumberfield(key, value["@description"], value) - if(value.hasOwnProperty('lower_boundary')&&isNumber(value['lower_boundary'])){numberField.setMin(value['lower_boundary'])} - if(value.hasOwnProperty('upper_boundary')&&isNumber(value['upper_boundary'])){numberField.setMax(value['upper_boundary'])} - if(value.hasOwnProperty("@unit")){numberField.setUnit(value["@unit"])}//spaeter entweder unit oder Unit in XML - if(value.hasOwnProperty("@Unit")){numberField.setUnit(value["@Unit"])} - if(value.hasOwnProperty("unit")){numberField.setUnit(value["unit"])} - if(value.hasOwnProperty("Unit")){numberField.setUnit(value["Unit"])} - modalModule.html.push(numberField); - } else { - modalModule.html.push(new SettingsTextfield(key, value["@description"], value, key)) - } - } else if (typeof value === "object") { - modalModule.html.push(new SettingsLabel(key)) - generateHTML(json[key]) - } else { - return; - } - }) - } - //Save Settings of Module to Server async function uploadModuleConf() { const response = await fetch('http://127.0.0.1:8000/modules/config/update', { @@ -288,19 +157,8 @@ } }) .then(response => response.json()) - modalChanged = false; - modalChanged = modalChanged; - } - - //Replacing underscores to space - function formatLabel(label: string) { - return label.replaceAll("_", " ") - } - - //check if the settings modal of a module changed - function checkModalChanged() { - modalChanged = true; - modalChanged = modalChanged; + modalModule.changed = false; + modalModule.changed = modalModule.changed; } //reset settings of an module @@ -345,6 +203,11 @@ changed = changed; } + function handleUpdate(event:any){ + modalModule = event.detail.data; + modalModule = modalModule; + } + //Makes new group and adding it to groups function addGroup() { groups.push(new group("group " + groups.length, 1)) @@ -390,33 +253,9 @@ updateAll() } - //Calculate Stepsize of number inputs from current value - function calcStepSize(value:number){ - const numberString = value.toString() - if (!numberString.includes('.')) { - return 1; - } else { - const decimalPart = numberString.split('.')[1]; - return Math.pow(10, -decimalPart.length); - } - } - //check if string is a number - function isNumber(n:string){ - return!isNaN(parseFloat(n))&&isFinite(parseFloat(n)) - } - //check if number is in boundry - function validateNumber(index:number){ - console.log("validateNumber") - if(modalModule.html[index].bind.value<modalModule.html[index].min){ - modalModule.html[index].bind.value=modalModule.html[index].min; - }else if(modalModule.html[index].bind.value>modalModule.html[index].max){ - modalModule.html[index].bind.value=modalModule.html[index].max; - } - } - </script> <Container> - <h2>Simulationsmodel</h2> + <h2>Simulationsmodel12</h2> <Alert color="primary" isOpen={saveAlert} toggle={() => (saveAlert = false)}> Changes in Model please save or reload before starting!. </Alert> @@ -486,7 +325,7 @@ draggable="true" on:dragstart={event=>dragStart(event,groupIndex,groupitem.modules.indexOf(item))} on:click={getModuleConfClickWrapper(item)}> - {item} + {formatLabel(item)} </li> </div> {/each} @@ -495,61 +334,14 @@ {/each} </Row> <Modal isOpen={open} toggle={toggle} scrollable> - <ModalHeader toggle={toggle}>{modalModule.name} settings</ModalHeader> + <ModalHeader toggle={toggle}>{formatLabel(modalModule.name)} settings</ModalHeader> <ModalBody> - {#each modalModule.html as element, index} - {#if element.type == "switch"} - <Input id="uiElement{index}" type="switch" label="{formatLabel(element.label)}" - bind:checked={element.bind.value} on:change={()=>checkModalChanged()}/> - <Tooltip target="uiElement{index}" placement="left"> - {element.description} - </Tooltip> - {:else if element.type == "label"} - <h3>{formatLabel(element.label)}</h3> - {:else if element.type == "selector"} - <h6>{formatLabel(element.label)}</h6> - <Input id="uiElement{index}" type="select" bind:value={element.bind.value} - on:change={()=>checkModalChanged()}> - {#each element.options as option} - <option>{option}</option> - {/each} - </Input> - <Tooltip target="uiElement{index}" placement="left"> - {element.description} - </Tooltip> - {:else if element.type == "textfield"} - <h7>{formatLabel(element.label)}</h7> - <Input id="uiElement{index}" type="text" bind:value={element.bind.value} - on:change={()=>checkModalChanged()}/> - <Tooltip target="uiElement{index}" placement="left"> - {element.description} - </Tooltip> - {:else if element.type == "numberfield"} - {#if element.unit != "1" && element.unit != "-" && element.unit != "count"} - <h7>{formatLabel(element.label)} (in {element.unit})</h7> - {:else} - <h7>{formatLabel(element.label)}</h7> - {/if} - {#if element.max != Number.POSITIVE_INFINITY && element.min != Number.NEGATIVE_INFINITY} - <Input type="range" min={element.min} max={element.max} step=0.01 bind:value={element.bind.value} - placeholder="range placeholder" on:change={()=>checkModalChanged()}/> - <Input id="uiElement{index}" type="number" min={element.min} max={element.max} step=0.01 - bind:value={element.bind.value} on:change={()=>checkModalChanged()} on:blur={()=>validateNumber(index)}/> - {:else} - <Input id="uiElement{index}" type="number" min={element.min} max={element.max} step={calcStepSize(element.bind.value)} - bind:value={element.bind.value} on:change={()=>checkModalChanged()} on:blur={()=>validateNumber(index)}/> - {/if} - - <Tooltip target="uiElement{index}" placement="left"> - {element.description} - </Tooltip> - {/if} - {/each} + <ConfigView data={modalModule} on:update={handleUpdate}/> </ModalBody> - {#if modalModule.name != "no"} + {#if modalModule.name !== "no"} <ModalFooter> <ButtonGroup> - {#if modalChanged} + {#if modalModule.changed} <Button color="primary" on:click={uploadModuleConf}> <Icon name="floppy"/> Save