/
uzer_007
/
Rixea
Обзор
Документация
Войти
/
uzer_007
/
Rixea
Код
Запросы
0
Задачи
Вики
Пакеты
5
Релизы
2
CI/CD
Аналитика
Безопасность
master
web_src/js/components/ViewFileTree.vue
134 строки
4 KB
uzer_007
feat: первая версия Rixea
02 авг 2026, 22:16
02 авг 2026, 22:16
d4f7504
Код
Авторство
О чём код?
<script lang="ts" setup> import ViewFileTreeItem from './ViewFileTreeItem.vue'; import {nextTick, onMounted, onUnmounted, shallowRef, useTemplateRef, type ShallowRef} from 'vue'; import {createViewFileTreeStore} from './ViewFileTreeStore.ts'; const elRoot = useTemplateRef('elRoot') as Readonly<ShallowRef<HTMLDivElement>>; const rootRetryButton = useTemplateRef('rootRetryButton') as Readonly<ShallowRef<HTMLButtonElement | null>>; const navigationRetryButton = useTemplateRef('navigationRetryButton') as Readonly<ShallowRef<HTMLButtonElement | null>>; const props = defineProps({ repoLink: {type: String, required: true}, treePath: {type: String, required: true}, currentRefNameSubURL: {type: String, required: true}, loadErrorText: {type: String, required: true}, contentLoadErrorText: {type: String, required: true}, loadingText: {type: String, required: true}, retryText: {type: String, required: true}, }); const store = createViewFileTreeStore(props); const isLoadingRoot = shallowRef(false); const hasRootLoadError = shallowRef(false); const isRetryingNavigation = shallowRef(false); const loadRoot = async (restoreFocus = false) => { if (isLoadingRoot.value) return; isLoadingRoot.value = true; hasRootLoadError.value = false; try { store.rootFiles = await store.loadChildren('', props.treePath); } catch { hasRootLoadError.value = true; } finally { isLoadingRoot.value = false; } if (restoreFocus) { await nextTick(); if (hasRootLoadError.value) { rootRetryButton.value?.focus(); } else { elRoot.value.querySelector<HTMLElement>('.tree-item')?.focus(); } } }; const retryNavigation = async () => { if (isRetryingNavigation.value) return; isRetryingNavigation.value = true; const loaded = await store.retryNavigation(); isRetryingNavigation.value = false; await nextTick(); if (loaded) { elRoot.value.querySelector<HTMLElement>('.tree-item.selected')?.focus(); } else { navigationRetryButton.value?.focus(); } }; const onPopState = (event: PopStateEvent) => { const treePath = event.state?.treePath || ''; if (event.state?.url) { void store.navigateTreeView(treePath, {url: event.state.url, pushHistory: false}); } else { store.selectedItem = treePath; } }; onMounted(() => { elRoot.value.closest('.is-loading')?.classList?.remove('is-loading'); window.addEventListener('popstate', onPopState); void loadRoot(); }); onUnmounted(() => window.removeEventListener('popstate', onPopState)); </script> <template> <div class="view-file-tree-items" ref="elRoot" :aria-busy="isLoadingRoot ? 'true' : 'false'"> <div v-if="isLoadingRoot" class="file-tree-state" role="status" aria-live="polite"> <div class="is-loading"/> <span>{{ props.loadingText }}</span> </div> <div v-else-if="hasRootLoadError" class="file-tree-state" role="alert"> <span>{{ props.loadErrorText }}</span> <button ref="rootRetryButton" type="button" class="ui compact basic button" @click="loadRoot(true)"> {{ props.retryText }} </button> </div> <template v-else> <div v-if="store.navigationLoadError" class="file-tree-state file-tree-navigation-error" role="alert"> <span>{{ props.contentLoadErrorText }}</span> <button ref="navigationRetryButton" type="button" class="ui compact basic button" :disabled="isRetryingNavigation" :aria-busy="isRetryingNavigation ? 'true' : 'false'" @click="retryNavigation" > {{ props.retryText }} </button> </div> <ViewFileTreeItem v-for="item in store.rootFiles" :key="item.entryName" :item="item" :store="store"/> </template> </div> </template> <style scoped> .view-file-tree-items { display: flex; flex-direction: column; gap: 1px; margin-right: .5rem; } .file-tree-state { display: flex; min-height: 8rem; padding: 1rem; flex-direction: column; align-items: center; justify-content: center; gap: 0.75rem; text-align: center; } .file-tree-state .is-loading { width: 100%; height: 5rem; } .file-tree-navigation-error { min-height: 0; border-bottom: 1px solid var(--color-secondary); } </style>