| | <script lang="ts"> |
| | import { getContext, onMount, tick } from 'svelte'; |
| | import { models, config } from '$lib/stores'; |
| | |
| | import { toast } from 'svelte-sonner'; |
| | import { copyToClipboard } from '$lib/utils'; |
| | |
| | import XMark from '$lib/components/icons/XMark.svelte'; |
| | import Modal from '$lib/components/common/Modal.svelte'; |
| | import Spinner from '$lib/components/common/Spinner.svelte'; |
| | import MapSelector from '$lib/components/common/Valves/MapSelector.svelte'; |
| | |
| | const i18n = getContext('i18n'); |
| | |
| | export let show = false; |
| | export let variables = {}; |
| | |
| | export let onSave = (e) => {}; |
| | |
| | let loading = false; |
| | let variableValues = {}; |
| | |
| | const submitHandler = async () => { |
| | onSave(variableValues); |
| | show = false; |
| | }; |
| | |
| | const init = async () => { |
| | loading = true; |
| | variableValues = {}; |
| | for (const variable of Object.keys(variables)) { |
| | if (variables[variable]?.default !== undefined) { |
| | variableValues[variable] = variables[variable].default; |
| | } else { |
| | variableValues[variable] = ''; |
| | } |
| | } |
| | loading = false; |
| | |
| | await tick(); |
| | |
| | const firstInputElement = document.getElementById('input-variable-0'); |
| | if (firstInputElement) { |
| | console.log('Focusing first input element:', firstInputElement); |
| | firstInputElement.focus(); |
| | } |
| | }; |
| | |
| | $: if (show) { |
| | init(); |
| | } |
| | </script> |
| |
|
| | <Modal bind:show size="md"> |
| | <div> |
| | <div class=" flex justify-between dark:text-gray-300 px-5 pt-4 pb-2"> |
| | <div class=" text-lg font-medium self-center">{$i18n.t('Input Variables')}</div> |
| | <button |
| | class="self-center" |
| | on:click={() => { |
| | show = false; |
| | }} |
| | > |
| | <XMark className={'size-5'} /> |
| | </button> |
| | </div> |
| |
|
| | <div class="flex flex-col md:flex-row w-full px-5 pb-4 md:space-x-4 dark:text-gray-200"> |
| | <div class=" flex flex-col w-full sm:flex-row sm:justify-center sm:space-x-6"> |
| | <form |
| | class="flex flex-col w-full" |
| | on:submit|preventDefault={() => { |
| | submitHandler(); |
| | }} |
| | > |
| | <div class="px-1"> |
| | {#if !loading} |
| | <div class="flex flex-col gap-1"> |
| | {#each Object.keys(variables) as variable, idx} |
| | {@const { type, ...variableAttributes } = variables[variable] ?? {}} |
| | |
| | <div class=" py-0.5 w-full justify-between"> |
| | <div class="flex w-full justify-between mb-1.5"> |
| | <div class=" self-center text-xs font-medium"> |
| | {variable} |
| | |
| | {#if variables[variable]?.required ?? false} |
| | <span class=" text-gray-500">*{$i18n.t('required')}</span> |
| | {/if} |
| | </div> |
| | </div> |
| | |
| | <div class="flex mt-0.5 mb-0.5 space-x-2"> |
| | <div class=" flex-1"> |
| | {#if variables[variable]?.type === 'select'} |
| | {@const options = variableAttributes?.options ?? []} |
| | {@const placeholder = variableAttributes?.placeholder ?? ''} |
| | |
| | <select |
| | class="w-full rounded-lg py-2 px-4 text-sm dark:text-gray-300 dark:bg-gray-850 outline-hidden border border-gray-100/30 dark:border-gray-850/30" |
| | bind:value={variableValues[variable]} |
| | id="input-variable-{idx}" |
| | > |
| | {#if placeholder} |
| | <option value="" disabled selected> |
| | {placeholder} |
| | </option> |
| | {/if} |
| | {#each options as option} |
| | <option value={option} selected={option === variableValues[variable]}> |
| | {option} |
| | </option> |
| | {/each} |
| | </select> |
| | {:else if variables[variable]?.type === 'checkbox'} |
| | <div class="flex items-center space-x-2"> |
| | <div class="relative flex justify-center items-center gap-2"> |
| | <input |
| | type="checkbox" |
| | bind:checked={variableValues[variable]} |
| | class="size-3.5 rounded cursor-pointer border border-gray-200 dark:border-gray-700" |
| | id="input-variable-{idx}" |
| | {...variableAttributes} |
| | /> |
| | |
| | <label for="input-variable-{idx}" class="text-sm" |
| | >{variables[variable]?.label ?? variable}</label |
| | > |
| | </div> |
| | |
| | <input |
| | type="text" |
| | class="flex-1 py-1 text-sm dark:text-gray-300 bg-transparent outline-hidden" |
| | placeholder={$i18n.t('Enter value (true/false)')} |
| | bind:value={variableValues[variable]} |
| | autocomplete="off" |
| | required={variables[variable]?.required ?? false} |
| | /> |
| | </div> |
| | {:else if variables[variable]?.type === 'color'} |
| | <div class="flex items-center space-x-2"> |
| | <div class="relative size-6"> |
| | <input |
| | type="color" |
| | class="size-6 rounded cursor-pointer border border-gray-200 dark:border-gray-700" |
| | value={variableValues[variable]} |
| | id="input-variable-{idx}" |
| | on:input={(e) => { |
| | // Convert the color value to uppercase immediately |
| | variableValues[variable] = e.target.value.toUpperCase(); |
| | }} |
| | {...variableAttributes} |
| | /> |
| | </div> |
| | |
| | <input |
| | type="text" |
| | class="flex-1 py-2 text-sm dark:text-gray-300 bg-transparent outline-hidden" |
| | placeholder={$i18n.t('Enter hex color (e.g. #FF0000)')} |
| | bind:value={variableValues[variable]} |
| | autocomplete="off" |
| | required={variables[variable]?.required ?? false} |
| | /> |
| | </div> |
| | {:else if variables[variable]?.type === 'date'} |
| | <input |
| | type="date" |
| | class="w-full rounded-lg py-2 px-4 text-sm dark:text-gray-300 dark:bg-gray-850 outline-hidden border border-gray-100/30 dark:border-gray-850/30" |
| | placeholder={variables[variable]?.placeholder ?? ''} |
| | bind:value={variableValues[variable]} |
| | autocomplete="off" |
| | id="input-variable-{idx}" |
| | required={variables[variable]?.required ?? false} |
| | {...variableAttributes} |
| | /> |
| | {:else if variables[variable]?.type === 'datetime-local'} |
| | <input |
| | type="datetime-local" |
| | class="w-full rounded-lg py-2 px-4 text-sm dark:text-gray-300 dark:bg-gray-850 outline-hidden border border-gray-100/30 dark:border-gray-850/30" |
| | placeholder={variables[variable]?.placeholder ?? ''} |
| | bind:value={variableValues[variable]} |
| | autocomplete="off" |
| | id="input-variable-{idx}" |
| | required={variables[variable]?.required ?? false} |
| | {...variableAttributes} |
| | /> |
| | {:else if variables[variable]?.type === 'email'} |
| | <input |
| | type="email" |
| | class="w-full rounded-lg py-2 px-4 text-sm dark:text-gray-300 dark:bg-gray-850 outline-hidden border border-gray-100/30 dark:border-gray-850/30" |
| | placeholder={variables[variable]?.placeholder ?? ''} |
| | bind:value={variableValues[variable]} |
| | autocomplete="off" |
| | id="input-variable-{idx}" |
| | required={variables[variable]?.required ?? false} |
| | {...variableAttributes} |
| | /> |
| | {:else if variables[variable]?.type === 'month'} |
| | <input |
| | type="month" |
| | class="w-full rounded-lg py-2 px-4 text-sm dark:text-gray-300 dark:bg-gray-850 outline-hidden border border-gray-100/30 dark:border-gray-850/30" |
| | placeholder={variables[variable]?.placeholder ?? ''} |
| | bind:value={variableValues[variable]} |
| | autocomplete="off" |
| | id="input-variable-{idx}" |
| | required={variables[variable]?.required ?? false} |
| | {...variableAttributes} |
| | /> |
| | {:else if variables[variable]?.type === 'number'} |
| | <input |
| | type="number" |
| | class="w-full rounded-lg py-2 px-4 text-sm dark:text-gray-300 dark:bg-gray-850 outline-hidden border border-gray-100/30 dark:border-gray-850/30" |
| | placeholder={variables[variable]?.placeholder ?? ''} |
| | bind:value={variableValues[variable]} |
| | autocomplete="off" |
| | id="input-variable-{idx}" |
| | required={variables[variable]?.required ?? false} |
| | {...variableAttributes} |
| | /> |
| | {:else if variables[variable]?.type === 'range'} |
| | <div class="flex items-center space-x-2"> |
| | <div class="relative flex justify-center items-center gap-2 flex-1"> |
| | <input |
| | type="range" |
| | bind:value={variableValues[variable]} |
| | class="w-full rounded-lg py-1 px-4 text-sm dark:text-gray-300 dark:bg-gray-850 outline-hidden border border-gray-100/30 dark:border-gray-850/30" |
| | id="input-variable-{idx}" |
| | {...variableAttributes} |
| | /> |
| | </div> |
| | |
| | <input |
| | type="text" |
| | class=" py-1 text-sm dark:text-gray-300 bg-transparent outline-hidden text-right" |
| | placeholder={$i18n.t('Enter value')} |
| | bind:value={variableValues[variable]} |
| | autocomplete="off" |
| | required={variables[variable]?.required ?? false} |
| | /> |
| | </div> |
| | |
| | |
| | |
| | |
| | {variables[variable]?.placeholder ?? ''} |
| | {variableValues[variable]} |
| | |
| | {idx} |
| | |
| | |
| | {:else if variables[variable]?.type === 'tel'} |
| | <input |
| | type="tel" |
| | class="w-full rounded-lg py-2 px-4 text-sm dark:text-gray-300 dark:bg-gray-850 outline-hidden border border-gray-100/30 dark:border-gray-850/30" |
| | placeholder={variables[variable]?.placeholder ?? ''} |
| | bind:value={variableValues[variable]} |
| | autocomplete="off" |
| | id="input-variable-{idx}" |
| | required={variables[variable]?.required ?? false} |
| | {...variableAttributes} |
| | /> |
| | {:else if variables[variable]?.type === 'text'} |
| | <input |
| | type="text" |
| | class="w-full rounded-lg py-2 px-4 text-sm dark:text-gray-300 dark:bg-gray-850 outline-hidden border border-gray-100/30 dark:border-gray-850/30" |
| | placeholder={variables[variable]?.placeholder ?? ''} |
| | bind:value={variableValues[variable]} |
| | autocomplete="off" |
| | id="input-variable-{idx}" |
| | required={variables[variable]?.required ?? false} |
| | {...variableAttributes} |
| | /> |
| | {:else if variables[variable]?.type === 'time'} |
| | <input |
| | type="time" |
| | class="w-full rounded-lg py-2 px-4 text-sm dark:text-gray-300 dark:bg-gray-850 outline-hidden border border-gray-100/30 dark:border-gray-850/30" |
| | placeholder={variables[variable]?.placeholder ?? ''} |
| | bind:value={variableValues[variable]} |
| | autocomplete="off" |
| | id="input-variable-{idx}" |
| | required={variables[variable]?.required ?? false} |
| | {...variableAttributes} |
| | /> |
| | {:else if variables[variable]?.type === 'url'} |
| | <input |
| | type="url" |
| | class="w-full rounded-lg py-2 px-4 text-sm dark:text-gray-300 dark:bg-gray-850 outline-hidden border border-gray-100/30 dark:border-gray-850/30" |
| | placeholder={variables[variable]?.placeholder ?? ''} |
| | bind:value={variableValues[variable]} |
| | autocomplete="off" |
| | id="input-variable-{idx}" |
| | required={variables[variable]?.required ?? false} |
| | {...variableAttributes} |
| | /> |
| | {:else if variables[variable]?.type === 'map'} |
| | |
| | <div class="flex flex-col items-center gap-1"> |
| | <MapSelector |
| | setViewLocation={((variableValues[variable] ?? '').includes(',') ?? |
| | false) |
| | ? variableValues[variable].split(',') |
| | : null} |
| | onClick={(value) => { |
| | variableValues[variable] = value; |
| | }} |
| | /> |
| | |
| | <input |
| | type="text" |
| | class=" w-full py-1 text-left text-sm dark:text-gray-300 bg-transparent outline-hidden" |
| | placeholder={$i18n.t('Enter coordinates (e.g. 51.505, -0.09)')} |
| | bind:value={variableValues[variable]} |
| | autocomplete="off" |
| | required={variables[variable]?.required ?? false} |
| | /> |
| | </div> |
| | {:else} |
| | <textarea |
| | class="w-full rounded-lg py-2 px-4 text-sm dark:text-gray-300 dark:bg-gray-850 outline-hidden border border-gray-100/30 dark:border-gray-850/30" |
| | placeholder={variables[variable]?.placeholder ?? ''} |
| | bind:value={variableValues[variable]} |
| | autocomplete="off" |
| | id="input-variable-{idx}" |
| | required={variables[variable]?.required ?? false} |
| | /> |
| | {/if} |
| | </div> |
| | </div> |
| | |
| | {#if (valvesSpec.properties[property]?.description ?? null) !== null} |
| | |
| | {valvesSpec.properties[property].description} |
| | |
| | {/if} |
| | </div> |
| | {/each} |
| | </div> |
| | {:else} |
| | <Spinner className="size-5" /> |
| | {/if} |
| | </div> |
| |
|
| | <div class="flex justify-end pt-3 text-sm font-medium"> |
| | <button |
| | class="px-3.5 py-1.5 text-sm font-medium bg-white hover:bg-gray-100 text-black dark:bg-black dark:text-white dark:hover:bg-gray-900 transition rounded-full" |
| | type="button" |
| | on:click={() => { |
| | show = false; |
| | }} |
| | > |
| | {$i18n.t('Cancel')} |
| | </button> |
| |
|
| | <button |
| | class="px-3.5 py-1.5 text-sm font-medium bg-black hover:bg-gray-900 text-white dark:bg-white dark:text-black dark:hover:bg-gray-100 transition rounded-full" |
| | type="submit" |
| | > |
| | {$i18n.t('Save')} |
| | </button> |
| | </div> |
| | </form> |
| | </div> |
| | </div> |
| | </div> |
| | </Modal> |
| |
|