<script setup lang="ts"> import { Status } from "@/client/resource"; import { computed, onMounted, reactive, watch } from "vue"; import { useResourceStore } from "@/stores/resources"; import type { ExtendedColors, SizeModifierType } from "@/types/PropTypes"; const model = defineModel<string | undefined>({ required: true, }); const resourceRegex = /CLDB-([\da-f]{32})\/(latest|[\da-f]{32})([/\S]*)/g; const props = defineProps<{ // eslint-disable-next-line @typescript-eslint/no-explicit-any parameter: Record<string, any>; required?: boolean; sizeModifier?: SizeModifierType; border?: ExtendedColors; allowRaw?: boolean; }>(); const emit = defineEmits<{ (e: "switch-to-raw"): void; }>(); const resourceRepository = useResourceStore(); const randomIDSuffix = Math.random().toString(16).substring(2, 8); type ResourcePath = { resource_id: string; resource_version_id: string; suffix?: string; }; const resource = reactive<ResourcePath>({ resource_id: "", resource_version_id: "", suffix: undefined, }); const helpTextPresent = computed<boolean>(() => props.parameter["help_text"]); const baseDynamicClass = computed<string[]>(() => props.border ? ["border", `border-${props.border}`] : [], ); const selectDynamicClass = computed<string[]>(() => { const cssClasses = [...baseDynamicClass.value]; if (props.sizeModifier) { cssClasses.push(`form-select-${props.sizeModifier}`); } return cssClasses; }); const inputDynamicClass = computed<string[]>(() => { const cssClasses = [...baseDynamicClass.value]; if (props.sizeModifier) { cssClasses.push(`form-control-${props.sizeModifier}`); } if (!helpTextPresent.value && !props.allowRaw) { cssClasses.push("rounded-end"); } return cssClasses; }); function updateResourceId(rid: string) { resource.resource_id = rid; resource.resource_version_id = ""; model.value = translateToModel(); } function updateResourceVersionId(rvid: string) { resource.resource_version_id = rvid; model.value = translateToModel(); resourceRepository.fetchResourceTree(resource.resource_id, rvid); } watch(model, (newVal, oldVal) => { if (newVal != oldVal && newVal !== translateToModel()) { parseModel(newVal); } }); function parseModel(val?: string) { if (val == undefined || val.length === 0) { Object.assign(resource, { resource_id: "", resource_version_id: "", suffix: undefined, }); } else { const match = resourceRegex.exec(val); if (match) { const tempResource: ResourcePath = { resource_id: "", resource_version_id: "", }; tempResource.resource_id = hexToUUID(match[1]); tempResource.suffix = match[3]; tempResource.resource_version_id = match[2].length === 32 ? hexToUUID(match[2]) : resourceRepository.getLatestVersion(resource.resource_id); if ( resourceRepository.resourceMapping[tempResource.resource_id] == undefined || resourceRepository.versionMapping[tempResource.resource_version_id] == undefined ) { // Missing resource emit("switch-to-raw"); return; } Object.assign(resource, tempResource); } else { // Not resource path emit("switch-to-raw"); } } } function hexToUUID(hex?: string): string { if (hex) { return `${hex.slice(0, 8)}-${hex.slice(8, 12)}-${hex.slice(12, 16)}-${hex.slice(16, 20)}-${hex.slice(20, 32)}`; } return ""; } watch( () => resource.suffix, (newVal, oldVal) => { if (newVal !== oldVal) { model.value = translateToModel(); } }, ); function translateToModel(): string { if (resource.resource_version_id.length === 0) { return ""; } let val = resourceRepository.versionMapping[resource.resource_version_id] ?.cluster_path ?? ""; if (resource.suffix != undefined && val.length > 0) { val = val + resource.suffix; } return val; } onMounted(() => { parseModel(model.value); }); </script> <template> <select class="form-select" :class="selectDynamicClass" :required="props.required" :value="resource.resource_id" @change=" (event) => updateResourceId((event.target as HTMLSelectElement)?.value) " > > <option selected disabled value="">Please select a resource</option> <option v-for="resource in resourceRepository.resources" :key="resource.resource_id" :value="resource.resource_id" > {{ resource.name }} </option> </select> <select class="form-select" :class="selectDynamicClass" :required="resource.resource_id.length > 0" :value="resource.resource_version_id" @change=" (event) => updateResourceVersionId((event.target as HTMLSelectElement)?.value) " :disabled="resource.resource_id.length === 0" > <option disabled selected value="">Please select a version</option> <option v-for="version in resourceRepository.resourceMapping[resource.resource_id] ?.versions ?? []" :key="version.resource_version_id" :value="version.resource_version_id" > {{ version.release }} {{ version.status === Status.LATEST ? "- Latest" : "" }} </option> </select> <input type="text" class="form-control" :class="inputDynamicClass" placeholder="/optional/path/in/resource/..." minlength="2" maxlength="256" pattern="\/\S*" v-model="resource.suffix" :list="'resource-tree-options-' + randomIDSuffix" /> <datalist :id="'resource-tree-options-' + randomIDSuffix"> <option v-for="file in resourceRepository.resourceTreeList[ resource.resource_version_id ] ?? []" :value="file" :key="file" /> </datalist> <button v-if="allowRaw" type="button" class="btn btn-outline-secondary" @click="emit('switch-to-raw')" > Advanced </button> </template> <style scoped></style>