Spaces:
Paused
Paused
| <script | |
| lang="ts" | |
| generics=" | |
| Context extends Exclude<keyof CobaltSettings, 'schemaVersion'>, | |
| Id extends keyof CobaltSettings[Context] | |
| " | |
| > | |
| import { get } from "svelte/store"; | |
| import { t } from "$lib/i18n/translations"; | |
| import type { CobaltSettings } from "$lib/types/settings"; | |
| import settings, { updateSetting } from "$lib/state/settings"; | |
| import { customInstanceWarning } from "$lib/api/safety-warning"; | |
| import IconX from "@tabler/icons-svelte/IconX.svelte"; | |
| import IconCheck from "@tabler/icons-svelte/IconCheck.svelte"; | |
| export let settingId: Id; | |
| export let settingContext: Context; | |
| export let placeholder: string; | |
| export let altText: string; | |
| export let type: "url" | "uuid" = "url"; | |
| export let showInstanceWarning = false; | |
| const regex = { | |
| url: "https?:\\/\\/[a-z0-9.\\-]+(:\\d+)?/?", | |
| uuid: "^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$", | |
| }; | |
| let input: HTMLInputElement; | |
| let inputValue: string = String(get(settings)[settingContext][settingId]); | |
| let inputFocused = false; | |
| let validInput = false; | |
| const writeToSettings = (value: string, type: "url" | "uuid" | "text") => { | |
| updateSetting({ | |
| [settingContext]: { | |
| [settingId]: | |
| type === "url" ? new URL(value).origin.toString() : value, | |
| }, | |
| }); | |
| inputValue = String(get(settings)[settingContext][settingId]); | |
| }; | |
| const save = async () => { | |
| if (showInstanceWarning) { | |
| await customInstanceWarning(); | |
| if ($settings.processing.seenCustomWarning && inputValue) { | |
| return writeToSettings(inputValue, type); | |
| } | |
| return; | |
| } | |
| return writeToSettings(inputValue, type); | |
| }; | |
| </script> | |
| <div id="settings-input-holder"> | |
| <div id="input-container" class:focused={inputFocused} aria-hidden="false"> | |
| <input | |
| id="input-box" | |
| bind:this={input} | |
| bind:value={inputValue} | |
| on:input={() => (validInput = input.checkValidity())} | |
| on:input={() => (inputFocused = true)} | |
| on:focus={() => (inputFocused = true)} | |
| on:blur={() => (inputFocused = false)} | |
| spellcheck="false" | |
| autocomplete="off" | |
| autocapitalize="off" | |
| maxlength="64" | |
| pattern={regex[type]} | |
| aria-label={altText} | |
| aria-hidden="false" | |
| /> | |
| {#if inputValue.length === 0} | |
| <span class="input-placeholder" aria-hidden="true"> | |
| {placeholder} | |
| </span> | |
| {/if} | |
| </div> | |
| <div id="settings-input-buttons"> | |
| <button | |
| class="settings-input-button" | |
| aria-label={$t("button.save")} | |
| disabled={inputValue == $settings[settingContext][settingId] || !validInput} | |
| on:click={save} | |
| > | |
| <IconCheck /> | |
| </button> | |
| <button | |
| class="settings-input-button" | |
| aria-label={$t("button.reset")} | |
| disabled={String($settings[settingContext][settingId]).length <= 0} | |
| on:click={() => writeToSettings("", "text")} | |
| > | |
| <IconX /> | |
| </button> | |
| </div> | |
| </div> | |
| <style> | |
| #settings-input-holder { | |
| display: flex; | |
| gap: 6px; | |
| } | |
| #input-container { | |
| padding: 0 18px; | |
| border-radius: var(--border-radius); | |
| color: var(--secondary); | |
| background-color: var(--button); | |
| box-shadow: var(--button-box-shadow); | |
| display: flex; | |
| align-items: center; | |
| width: 100%; | |
| position: relative; | |
| overflow: hidden; | |
| } | |
| #input-container, | |
| #input-box { | |
| font-size: 13.5px; | |
| font-weight: 500; | |
| min-width: 0; | |
| } | |
| #input-box { | |
| flex: 1; | |
| background-color: transparent; | |
| color: var(--secondary); | |
| border: none; | |
| padding-block: 0; | |
| padding-inline: 0; | |
| padding: 12px 0; | |
| } | |
| #input-box::placeholder { | |
| color: var(--gray); | |
| /* fix for firefox */ | |
| opacity: 1; | |
| } | |
| .input-placeholder { | |
| position: absolute; | |
| color: var(--gray); | |
| pointer-events: none; | |
| white-space: nowrap; | |
| } | |
| #input-box:focus-visible { | |
| box-shadow: unset ; | |
| } | |
| #input-container.focused { | |
| box-shadow: 0 0 0 2px var(--secondary) inset; | |
| } | |
| #settings-input-buttons { | |
| display: flex; | |
| flex-direction: row; | |
| gap: 6px; | |
| } | |
| .settings-input-button { | |
| height: 42px; | |
| width: 42px; | |
| padding: 0; | |
| } | |
| .settings-input-button :global(svg) { | |
| height: 21px; | |
| width: 21px; | |
| stroke-width: 1.5px; | |
| } | |
| .settings-input-button[disabled] { | |
| opacity: 0.5; | |
| pointer-events: none; | |
| } | |
| </style> | |