openpencil/src/components/TabBar.vue
Danila Poyarkov f22d2c9bad fix(app): serialize document recovery lifecycle
- Await IndexedDB writes and tab recovery cleanup

- Serialize recovery backend fallback and snapshot adoption

- Preserve queued snapshots when IndexedDB falls back to memory
2026-08-13 17:22:59 +03:00

80 lines
2.2 KiB
Vue

<script setup lang="ts">
import { computed } from 'vue'
import { TabsList, TabsRoot, TabsTrigger } from 'reka-ui'
import { tv } from 'tailwind-variants'
import Tip from '@/components/ui/Tip.vue'
import tabBarTheme from '@/theme/tab-bar'
import { useTabsStore, createTab } from '@/app/tabs'
import { useI18n } from '@open-pencil/vue'
const { dialogs } = useI18n()
const { tabs, activeTabId, switchTab, closeTab } = useTabsStore()
const tabBarStyles = tv(tabBarTheme)
const baseStyles = tabBarStyles()
const modelValue = computed({
get: () => activeTabId.value,
set: (id: string) => switchTab(id)
})
function onMiddleClick(e: MouseEvent, tabId: string) {
if (e.button === 1) {
e.preventDefault()
void closeTab(tabId)
}
}
function onClose(e: MouseEvent, tabId: string) {
e.stopPropagation()
void closeTab(tabId)
}
</script>
<template>
<TabsRoot
v-if="tabs.length > 1"
v-model="modelValue"
activation-mode="automatic"
:class="baseStyles.root()"
>
<TabsList :class="baseStyles.list()">
<TabsTrigger
v-for="tab in tabs"
:key="tab.id"
:value="tab.id"
data-test-id="tabbar-tab"
:class="tabBarStyles({ active: tab.isActive }).trigger()"
:data-active="tab.isActive || undefined"
@mousedown="onMiddleClick($event, tab.id)"
>
<icon-lucide-file :class="baseStyles.icon()" />
<span :class="baseStyles.label()">{{ tab.name }}</span>
<Tip :label="dialogs.closeTab({ name: tab.name })">
<button
data-test-id="tabbar-close"
:class="tabBarStyles({ active: tab.isActive }).close()"
:data-active="tab.isActive || undefined"
:aria-label="dialogs.closeTab({ name: tab.name })"
tabindex="-1"
@click="onClose($event, tab.id)"
>
<icon-lucide-x :class="baseStyles.closeIcon()" />
</button>
</Tip>
</TabsTrigger>
</TabsList>
<Tip :label="dialogs.newTab">
<button
data-test-id="tabbar-new"
:class="baseStyles.newAction()"
:aria-label="dialogs.newTab"
@click="createTab()"
>
<icon-lucide-plus :class="baseStyles.newIcon()" />
</button>
</Tip>
</TabsRoot>
</template>