import type { AdminLayoutMenuItem } from '@/components/layouts/main-layout/types' import { computed, onBeforeUnmount, onMounted, ref, type Ref, watch } from 'vue' type ToggleSidebarPayload = { drawer: boolean rail: boolean } type UseAdminLayoutStateOptions = { appBarRef: Ref breadcrumbBarVisible: Ref emitUpdateBreadcrumbBarVisible: (value: boolean) => void emitUpdateFavoritesBarVisible: (value: boolean) => void emitUpdateIsRail: (value: boolean) => void favoritesBarVisible: Ref isMobile: Ref isRail: Ref // 必須為 Ref,確保父層 prop 更新時 getter 能即時反映 menuItems: AdminLayoutMenuItem[] onToggleSidebar: (payload: ToggleSidebarPayload) => void } export function useAdminLayoutState(options: UseAdminLayoutStateOptions) { const drawer = ref(true) const mobileFavoritesPanel = ref(false) const mobileMenuPath = ref([]) const localBreadcrumbBarVisible = ref(true) const localFavoritesBarVisible = ref(true) const localIsRail = ref(false) const opened = ref([]) const appBarHeight = ref(0) const isRail = computed({ get: () => options.isRail.value ?? localIsRail.value, set: (value: boolean) => { if (options.isRail.value === null) { localIsRail.value = value return } options.emitUpdateIsRail(value) }, }) const showFavoritesBar = computed({ get: () => options.favoritesBarVisible.value ?? localFavoritesBarVisible.value, set: (value: boolean) => { if (options.favoritesBarVisible.value === null) { localFavoritesBarVisible.value = value return } options.emitUpdateFavoritesBarVisible(value) }, }) const breadcrumbBarVisible = computed({ get: () => options.breadcrumbBarVisible.value ?? localBreadcrumbBarVisible.value, set: (value: boolean) => { if (options.breadcrumbBarVisible.value === null) { localBreadcrumbBarVisible.value = value return } options.emitUpdateBreadcrumbBarVisible(value) }, }) const mobileCurrentItems = computed(() => mobileMenuPath.value.reduce( (items, currentItem) => currentItem?.subItems ?? [], options.menuItems || [] ) ) const mobileCurrentLevel = computed(() => mobileMenuPath.value.length + 1) const mobileMenuLevels = computed(() => Array.from({ length: mobileCurrentLevel.value }, (_, index) => ({ level: index + 1, title: index === 0 ? '主選單' : (mobileMenuPath.value[index - 1]?.title ?? `第${index + 1}層`), })) ) function resetMobilePanels() { mobileFavoritesPanel.value = false mobileMenuPath.value = [] } function toggleSidebar() { if (options.isMobile.value) { drawer.value = !drawer.value } else { isRail.value = !isRail.value } options.onToggleSidebar({ drawer: drawer.value, rail: isRail.value, }) } function goToMobileLevel(level: number) { mobileFavoritesPanel.value = false mobileMenuPath.value = mobileMenuPath.value.slice(0, Math.max(0, level - 1)) } function openMobileFavoritesPanel() { mobileMenuPath.value = [] mobileFavoritesPanel.value = true } function handleMobileMenuClick( item: AdminLayoutMenuItem, onSelect: (selectedItem: AdminLayoutMenuItem) => void ) { if (item?.subItems?.length) { mobileMenuPath.value = [...mobileMenuPath.value, item] return } onSelect(item) } function handleSelectFavorite( item: AdminLayoutMenuItem, onSelect: (selectedItem: AdminLayoutMenuItem) => void ) { onSelect(item) mobileFavoritesPanel.value = false } function toggleFavoritesBar(nextValue?: boolean) { showFavoritesBar.value = typeof nextValue === 'boolean' ? nextValue : !showFavoritesBar.value } function handleUnshrink() { isRail.value = false } let appBarObserver: ResizeObserver | null = null function resolveObservedElement() { const target = options.appBarRef.value as HTMLElement | { $el?: HTMLElement } | null if (!target) return null if (target instanceof HTMLElement) return target return target.$el ?? null } function updateAppBarHeight() { const el = resolveObservedElement() if (!el) return appBarHeight.value = Math.round(el.getBoundingClientRect().height || 0) } onMounted(() => { updateAppBarHeight() if (typeof ResizeObserver === 'undefined') return const el = resolveObservedElement() if (!el) return appBarObserver = new ResizeObserver(() => updateAppBarHeight()) appBarObserver.observe(el) }) onBeforeUnmount(() => { if (!appBarObserver) return appBarObserver.disconnect() appBarObserver = null }) watch(options.isMobile, (value) => { if (!value) { resetMobilePanels() } }) watch(drawer, (value) => { if (!value) { resetMobilePanels() } }) const mainStyle = computed(() => ({ paddingTop: appBarHeight.value ? `${appBarHeight.value}px` : undefined, height: '100vh', minHeight: 0, flex: '1 1 0', })) return { drawer, goToMobileLevel, handleMobileMenuClick, handleSelectFavorite, handleUnshrink, isRail, mainStyle, mobileCurrentItems, mobileCurrentLevel, mobileFavoritesPanel, mobileMenuLevels, openMobileFavoritesPanel, opened, breadcrumbBarVisible, showFavoritesBar, toggleFavoritesBar, toggleSidebar, } }