mirror of
https://github.com/supabase/supabase.git
synced 2026-10-12 21:05:06 +03:00
fixes and refactoring
This commit is contained in:
1 parent
7914c4adf7
commit
cfd89fc984
11 files changed
+79
-148
No files matched your search
@@ -94,7 +94,7 @@ export const AdvisorSidebar = () => {
|
||||
const advisorSnap = useAdvisorCenterStateSnapshot()
|
||||
const { data: project } = useSelectedProjectQuery()
|
||||
|
||||
const isSidebarOpen = Boolean(advisorSnap.open)
|
||||
const isSidebarOpen = sidebarManagerState.isSidebarOpen(SIDEBAR_KEYS.ADVISOR_CENTER)
|
||||
|
||||
const {
|
||||
data: lintData,
|
||||
@@ -228,7 +228,7 @@ export const AdvisorSidebar = () => {
|
||||
const hiddenItemsCount = itemsFilteredByTabOnly.length - filteredItems.length
|
||||
|
||||
useEffect(() => {
|
||||
if (!advisorSnap.open) return
|
||||
if (!isSidebarOpen) return
|
||||
|
||||
if (advisorSnap.selectedItemId) {
|
||||
const selectedExists = filteredItems.some((item) => item.id === advisorSnap.selectedItemId)
|
||||
@@ -238,7 +238,7 @@ export const AdvisorSidebar = () => {
|
||||
} else if (filteredItems.length === 0) {
|
||||
advisorCenterState.selectItem(undefined)
|
||||
}
|
||||
}, [advisorSnap.open, filteredItems, advisorSnap.selectedItemId])
|
||||
}, [isSidebarOpen, filteredItems, advisorSnap.selectedItemId])
|
||||
|
||||
const selectedItem = filteredItems.find((item) => item.id === advisorSnap.selectedItemId)
|
||||
const isDetailView = !!selectedItem
|
||||
|
||||
@@ -1,19 +1,19 @@
|
||||
import { PropsWithChildren } from 'react'
|
||||
|
||||
import { useAiAssistantStateSnapshot } from 'state/ai-assistant-state'
|
||||
import { SIDEBAR_KEYS, sidebarManagerState } from 'state/sidebar-manager-state'
|
||||
import { cn } from 'ui'
|
||||
|
||||
/**
|
||||
* Standardized padding and width layout for non-custom reports
|
||||
*/
|
||||
const ReportPadding = ({ children }: PropsWithChildren<{}>) => {
|
||||
const snap = useAiAssistantStateSnapshot()
|
||||
const isAiAssistantOpen = sidebarManagerState.isSidebarOpen(SIDEBAR_KEYS.AI_ASSISTANT)
|
||||
|
||||
return (
|
||||
<div
|
||||
className={cn(
|
||||
'flex flex-col flex-grow gap-4 px-5 py-6 mx-auto 1xl:px-28 lg:px-16 2xl:px-32 w-full',
|
||||
snap.open ? 'xl:px-6' : 'xl:px-22'
|
||||
isAiAssistantOpen ? 'xl:px-6' : 'xl:px-22'
|
||||
)}
|
||||
>
|
||||
{children}
|
||||
|
||||
@@ -41,7 +41,7 @@ export const AIAssistantOption = ({
|
||||
}, [projectRef, organizationSlug, sendEvent])
|
||||
|
||||
// If no specific project selected, use the wildcard route
|
||||
const aiLink = `/project/${projectRef !== NO_PROJECT_MARKER ? projectRef : '_'}?aiAssistantPanelOpen=true&slug=${organizationSlug}`
|
||||
const aiLink = `/project/${projectRef !== NO_PROJECT_MARKER ? projectRef : '_'}?sidebar=ai-assistant&slug=${organizationSlug}`
|
||||
|
||||
if (!organizationSlug || organizationSlug === NO_ORG_MARKER) return null
|
||||
|
||||
|
||||
@@ -3,7 +3,6 @@ import { ShieldAlert } from 'lucide-react'
|
||||
import { useParams } from 'common'
|
||||
import { ButtonTooltip } from 'components/ui/ButtonTooltip'
|
||||
import { useProjectLintsQuery } from 'data/lint/lint-query'
|
||||
import { useAdvisorCenterStateSnapshot } from 'state/advisor-center-state'
|
||||
import {
|
||||
SIDEBAR_KEYS,
|
||||
sidebarManagerState,
|
||||
@@ -13,22 +12,17 @@ import { cn } from 'ui'
|
||||
|
||||
export const AdvisorButton = () => {
|
||||
const { ref: projectRef } = useParams()
|
||||
const advisorSnap = useAdvisorCenterStateSnapshot()
|
||||
const sidebarSnap = useSidebarManagerSnapshot()
|
||||
const { data: lints } = useProjectLintsQuery({ projectRef })
|
||||
|
||||
const hasCriticalIssues = Array.isArray(lints) && lints.some((lint) => lint.level === 'ERROR')
|
||||
|
||||
const handleClick = () => {
|
||||
if (advisorSnap.open) {
|
||||
sidebarManagerState.closeSidebar(SIDEBAR_KEYS.ADVISOR_CENTER)
|
||||
} else {
|
||||
sidebarManagerState.openSidebar(SIDEBAR_KEYS.ADVISOR_CENTER)
|
||||
}
|
||||
}
|
||||
|
||||
const isOpen = sidebarSnap.panels[SIDEBAR_KEYS.ADVISOR_CENTER]?.open
|
||||
|
||||
const handleClick = () => {
|
||||
sidebarManagerState.toggleSidebar(SIDEBAR_KEYS.ADVISOR_CENTER)
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="relative">
|
||||
<ButtonTooltip
|
||||
|
||||
@@ -1,24 +1,55 @@
|
||||
import { Fragment, ReactNode } from 'react'
|
||||
import { useRouter } from 'next/router'
|
||||
import { Fragment, ReactNode, useEffect } from 'react'
|
||||
|
||||
import { AdvisorSidebar } from 'components/interfaces/AdvisorCenter/AdvisorSidebar'
|
||||
import { AIAssistant } from 'components/ui/AIAssistantPanel/AIAssistant'
|
||||
import EditorPanel from 'components/ui/EditorPanel/EditorPanel'
|
||||
import { advisorCenterState } from 'state/advisor-center-state'
|
||||
import { editorPanelState } from 'state/editor-panel-state'
|
||||
import {
|
||||
SIDEBAR_KEYS,
|
||||
SidebarKey,
|
||||
sidebarManagerState,
|
||||
useSidebarManagerSnapshot,
|
||||
} from 'state/sidebar-manager-state'
|
||||
import { ResizableHandle, ResizablePanel, cn } from 'ui'
|
||||
|
||||
type LayoutSidebarProps = {
|
||||
isClient: boolean
|
||||
}
|
||||
// Register all sidebars at module level (runs once when module loads)
|
||||
sidebarManagerState.registerSidebar(SIDEBAR_KEYS.AI_ASSISTANT, {})
|
||||
|
||||
export const LayoutSidebar = ({ isClient }: LayoutSidebarProps) => {
|
||||
sidebarManagerState.registerSidebar(SIDEBAR_KEYS.ADVISOR_CENTER, {
|
||||
onClose: () => {
|
||||
advisorCenterState.selectedItemId = undefined
|
||||
},
|
||||
})
|
||||
|
||||
sidebarManagerState.registerSidebar(SIDEBAR_KEYS.EDITOR_PANEL, {
|
||||
onClose: () => {
|
||||
editorPanelState.reset()
|
||||
},
|
||||
})
|
||||
|
||||
export const LayoutSidebar = () => {
|
||||
const router = useRouter()
|
||||
const sidebarSnap = useSidebarManagerSnapshot()
|
||||
const activeSidebar = isClient ? sidebarSnap.activeSidebar : undefined
|
||||
const activeSidebar = sidebarSnap.activeSidebar
|
||||
|
||||
if (!isClient || !activeSidebar) return null
|
||||
// Handle URL params for deep linking
|
||||
useEffect(() => {
|
||||
if (!router.isReady) return
|
||||
|
||||
const sidebarParamRaw = router.query.sidebar
|
||||
const validSidebarKeys = Object.values(SIDEBAR_KEYS) as string[]
|
||||
const sidebarParam = typeof sidebarParamRaw === 'string' ? sidebarParamRaw : null
|
||||
|
||||
if (!sidebarParam || !validSidebarKeys.includes(sidebarParam)) return
|
||||
|
||||
if (sidebarSnap.activeSidebar !== sidebarParam) {
|
||||
sidebarManagerState.openSidebar(sidebarParam as SidebarKey)
|
||||
}
|
||||
}, [router.isReady, router.query.sidebar, sidebarSnap.activeSidebar])
|
||||
|
||||
if (!activeSidebar) return null
|
||||
|
||||
let sidebarContent: ReactNode = null
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { AnimatePresence, motion } from 'framer-motion'
|
||||
import Head from 'next/head'
|
||||
import { useRouter } from 'next/router'
|
||||
import { forwardRef, Fragment, PropsWithChildren, ReactNode, useEffect, useState } from 'react'
|
||||
import { forwardRef, Fragment, PropsWithChildren, ReactNode, useEffect } from 'react'
|
||||
|
||||
import { LOCAL_STORAGE_KEYS, useParams } from 'common'
|
||||
import { CreateBranchModal } from 'components/interfaces/BranchManagement/CreateBranchModal'
|
||||
@@ -91,7 +91,6 @@ export const ProjectLayout = forwardRef<HTMLDivElement, PropsWithChildren<Projec
|
||||
ref
|
||||
) => {
|
||||
const router = useRouter()
|
||||
const [isClient, setIsClient] = useState(false)
|
||||
const { data: selectedOrganization } = useSelectedOrganizationQuery()
|
||||
const { data: selectedProject } = useSelectedProjectQuery()
|
||||
const { mobileMenuOpen, showSidebar, setMobileMenuOpen } = useAppStateSnapshot()
|
||||
@@ -136,10 +135,6 @@ export const ProjectLayout = forwardRef<HTMLDivElement, PropsWithChildren<Projec
|
||||
router.pathname === '/project/[ref]' || router.pathname.includes('/project/[ref]/settings')
|
||||
const showPausedState = isPaused && !ignorePausedState
|
||||
|
||||
useEffect(() => {
|
||||
setIsClient(true)
|
||||
}, [])
|
||||
|
||||
return (
|
||||
<>
|
||||
<Head>
|
||||
@@ -238,7 +233,7 @@ export const ProjectLayout = forwardRef<HTMLDivElement, PropsWithChildren<Projec
|
||||
)}
|
||||
</main>
|
||||
</ResizablePanel>
|
||||
<LayoutSidebar isClient={isClient} />
|
||||
<LayoutSidebar />
|
||||
</ResizablePanelGroup>
|
||||
</ResizablePanel>
|
||||
</ResizablePanelGroup>
|
||||
|
||||
@@ -432,12 +432,14 @@ export const AIAssistant = ({ className }: AIAssistantProps) => {
|
||||
}
|
||||
}, [snap.initialInput])
|
||||
|
||||
const isSidebarOpen = sidebarManagerState.isSidebarOpen(SIDEBAR_KEYS.AI_ASSISTANT)
|
||||
|
||||
useEffect(() => {
|
||||
if (snap.open && isInSQLEditor && !!snippetContent) {
|
||||
if (isSidebarOpen && isInSQLEditor && !!snippetContent) {
|
||||
snap.setSqlSnippets([{ label: 'Current Query', content: snippetContent }])
|
||||
}
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [snap.open, isInSQLEditor, snippetContent])
|
||||
}, [isSidebarOpen, isInSQLEditor, snippetContent])
|
||||
|
||||
return (
|
||||
<ErrorBoundary
|
||||
|
||||
@@ -6,7 +6,6 @@ export type AdvisorCenterTab = 'all' | 'security' | 'performance' | 'messages'
|
||||
export type AdvisorSeverity = 'critical' | 'warning' | 'info'
|
||||
|
||||
const initialState = {
|
||||
open: false,
|
||||
activeTab: 'all' as AdvisorCenterTab,
|
||||
severityFilters: ['critical'] as AdvisorSeverity[],
|
||||
selectedItemId: undefined as string | undefined,
|
||||
@@ -44,16 +43,6 @@ export const advisorCenterState = proxy({
|
||||
},
|
||||
})
|
||||
|
||||
sidebarManagerState.registerSidebar(SIDEBAR_KEYS.ADVISOR_CENTER, {
|
||||
onOpen: () => {
|
||||
advisorCenterState.open = true
|
||||
},
|
||||
onClose: () => {
|
||||
advisorCenterState.open = false
|
||||
advisorCenterState.selectedItemId = undefined
|
||||
},
|
||||
})
|
||||
|
||||
export const getAdvisorCenterStateSnapshot = () => snapshot(advisorCenterState)
|
||||
|
||||
export const useAdvisorCenterStateSnapshot = (
|
||||
|
||||
@@ -1,13 +1,12 @@
|
||||
import type { UIMessage as MessageType } from '@ai-sdk/react'
|
||||
import { DBSchema, IDBPDatabase, openDB } from 'idb'
|
||||
import { debounce } from 'lodash'
|
||||
import { createContext, PropsWithChildren, useContext, useEffect, useState } from 'react'
|
||||
import { createContext, PropsWithChildren, useContext, useEffect, useRef, useState } from 'react'
|
||||
import { v4 as uuidv4 } from 'uuid'
|
||||
import { proxy, snapshot, subscribe, useSnapshot } from 'valtio'
|
||||
|
||||
import { LOCAL_STORAGE_KEYS } from 'common'
|
||||
import { useSelectedProjectQuery } from 'hooks/misc/useSelectedProject'
|
||||
import { SIDEBAR_KEYS, sidebarManagerState } from './sidebar-manager-state'
|
||||
|
||||
type SuggestionsType = {
|
||||
title: string
|
||||
@@ -29,7 +28,6 @@ type ChatSession = {
|
||||
}
|
||||
|
||||
type AiAssistantData = {
|
||||
open: boolean
|
||||
initialInput: string
|
||||
sqlSnippets?: SqlSnippet[]
|
||||
suggestions?: SuggestionsType
|
||||
@@ -42,14 +40,12 @@ type AiAssistantData = {
|
||||
// Data structure stored in IndexedDB
|
||||
type StoredAiAssistantState = {
|
||||
projectRef: string
|
||||
open: boolean
|
||||
activeChatId?: string
|
||||
chats: Record<string, ChatSession>
|
||||
model?: AssistantModel
|
||||
}
|
||||
|
||||
const INITIAL_AI_ASSISTANT: AiAssistantData = {
|
||||
open: false,
|
||||
initialInput: '',
|
||||
sqlSnippets: undefined,
|
||||
suggestions: undefined,
|
||||
@@ -168,7 +164,6 @@ async function tryMigrateFromLocalStorage(
|
||||
if (parsedFromLocalStorage && typeof parsedFromLocalStorage.chats === 'object') {
|
||||
migratedState = {
|
||||
projectRef: projectRef,
|
||||
open: parsedFromLocalStorage.open ?? false,
|
||||
activeChatId: parsedFromLocalStorage.activeChatId,
|
||||
chats: parsedFromLocalStorage.chats,
|
||||
model: parsedFromLocalStorage.model ?? INITIAL_AI_ASSISTANT.model,
|
||||
@@ -200,20 +195,6 @@ async function tryMigrateFromLocalStorage(
|
||||
|
||||
// Helper function to ensure an active chat exists or initialize a new one
|
||||
function ensureActiveChatOrInitialize(state: AiAssistantState) {
|
||||
// Check URL param again to override loaded 'open' state if present
|
||||
if (typeof window !== 'undefined') {
|
||||
const urlParams = new URLSearchParams(window.location.search)
|
||||
const aiAssistantPanelOpenParam = urlParams.get('aiAssistantPanelOpen')
|
||||
if (aiAssistantPanelOpenParam !== null) {
|
||||
const shouldOpen = aiAssistantPanelOpenParam === 'true'
|
||||
if (shouldOpen) {
|
||||
sidebarManagerState.openSidebar(SIDEBAR_KEYS.AI_ASSISTANT)
|
||||
} else {
|
||||
sidebarManagerState.closeSidebar(SIDEBAR_KEYS.AI_ASSISTANT)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Ensure an active chat exists after loading/migration
|
||||
if (!state.activeChatId || !state.chats[state.activeChatId]) {
|
||||
const chatIds = Object.keys(state.chats)
|
||||
@@ -234,20 +215,10 @@ export const createAiAssistantState = (): AiAssistantState => {
|
||||
// Initialize with defaults, loading happens asynchronously in the provider
|
||||
const initialState = { ...INITIAL_AI_ASSISTANT }
|
||||
|
||||
// Check URL params for initial 'open' state, overriding any loaded state later if present
|
||||
if (typeof window !== 'undefined') {
|
||||
const urlParams = new URLSearchParams(window.location.search)
|
||||
const aiAssistantPanelOpenParam = urlParams.get('aiAssistantPanelOpen')
|
||||
if (aiAssistantPanelOpenParam !== null) {
|
||||
initialState.open = aiAssistantPanelOpenParam === 'true'
|
||||
}
|
||||
}
|
||||
|
||||
const state: AiAssistantState = proxy({
|
||||
...initialState, // Spread initial values directly
|
||||
|
||||
resetAiAssistantPanel: () => {
|
||||
sidebarManagerState.closeSidebar(SIDEBAR_KEYS.AI_ASSISTANT)
|
||||
Object.assign(state, INITIAL_AI_ASSISTANT)
|
||||
},
|
||||
|
||||
@@ -262,14 +233,9 @@ export const createAiAssistantState = (): AiAssistantState => {
|
||||
|
||||
newChat: (
|
||||
options?: { name?: string } & Partial<
|
||||
Pick<AiAssistantData, 'open' | 'initialInput' | 'sqlSnippets' | 'suggestions' | 'tables'>
|
||||
Pick<AiAssistantData, 'initialInput' | 'sqlSnippets' | 'suggestions' | 'tables'>
|
||||
>
|
||||
) => {
|
||||
if (options?.open === true) {
|
||||
sidebarManagerState.openSidebar(SIDEBAR_KEYS.AI_ASSISTANT)
|
||||
} else if (options?.open === false) {
|
||||
sidebarManagerState.closeSidebar(SIDEBAR_KEYS.AI_ASSISTANT)
|
||||
}
|
||||
const chatId = uuidv4()
|
||||
const newChat: ChatSession = {
|
||||
id: chatId,
|
||||
@@ -401,33 +367,11 @@ export const createAiAssistantState = (): AiAssistantState => {
|
||||
state.activeChatId = persistedState.activeChatId
|
||||
state.model = persistedState.model ?? INITIAL_AI_ASSISTANT.model
|
||||
|
||||
// Check URL param again to override loaded 'open' state if present
|
||||
if (typeof window !== 'undefined') {
|
||||
const urlParams = new URLSearchParams(window.location.search)
|
||||
const aiAssistantPanelOpenParam = urlParams.get('aiAssistantPanelOpen')
|
||||
if (aiAssistantPanelOpenParam !== null) {
|
||||
const shouldOpen = aiAssistantPanelOpenParam === 'true'
|
||||
if (shouldOpen) {
|
||||
sidebarManagerState.openSidebar(SIDEBAR_KEYS.AI_ASSISTANT)
|
||||
} else {
|
||||
sidebarManagerState.closeSidebar(SIDEBAR_KEYS.AI_ASSISTANT)
|
||||
}
|
||||
} else if (persistedState.open) {
|
||||
sidebarManagerState.openSidebar(SIDEBAR_KEYS.AI_ASSISTANT)
|
||||
} else {
|
||||
sidebarManagerState.closeSidebar(SIDEBAR_KEYS.AI_ASSISTANT)
|
||||
}
|
||||
} else if (persistedState.open) {
|
||||
sidebarManagerState.openSidebar(SIDEBAR_KEYS.AI_ASSISTANT)
|
||||
} else {
|
||||
sidebarManagerState.closeSidebar(SIDEBAR_KEYS.AI_ASSISTANT)
|
||||
}
|
||||
|
||||
// Ensure an active chat exists after loading
|
||||
if (!state.activeChat) {
|
||||
const chatIds = Object.keys(state.chats)
|
||||
if (chatIds.length > 0) {
|
||||
// Maybe select the most recently updated? For now, first.
|
||||
// Select the most recently updated chat
|
||||
state.activeChatId = chatIds.sort(
|
||||
(a, b) =>
|
||||
(state.chats[b].updatedAt?.getTime() || 0) -
|
||||
@@ -454,7 +398,7 @@ export type AiAssistantState = AiAssistantData & {
|
||||
setModel: (model: AssistantModel) => void
|
||||
newChat: (
|
||||
options?: { name?: string } & Partial<
|
||||
Pick<AiAssistantData, 'open' | 'initialInput' | 'sqlSnippets' | 'suggestions' | 'tables'>
|
||||
Pick<AiAssistantData, 'initialInput' | 'sqlSnippets' | 'suggestions' | 'tables'>
|
||||
>
|
||||
) => string
|
||||
selectChat: (id: string) => void
|
||||
@@ -477,42 +421,28 @@ export const AiAssistantStateContextProvider = ({ children }: PropsWithChildren)
|
||||
const { data: project } = useSelectedProjectQuery()
|
||||
// Initialize state. createAiAssistantState now just sets defaults.
|
||||
const [state] = useState(() => createAiAssistantState())
|
||||
|
||||
useEffect(() => {
|
||||
const shouldRestoreOpen = state.open
|
||||
|
||||
sidebarManagerState.registerSidebar(SIDEBAR_KEYS.AI_ASSISTANT, {
|
||||
onOpen: () => {
|
||||
state.open = true
|
||||
},
|
||||
onClose: () => {
|
||||
state.open = false
|
||||
},
|
||||
})
|
||||
|
||||
if (shouldRestoreOpen) {
|
||||
sidebarManagerState.openSidebar(SIDEBAR_KEYS.AI_ASSISTANT)
|
||||
} else {
|
||||
sidebarManagerState.closeSidebar(SIDEBAR_KEYS.AI_ASSISTANT)
|
||||
}
|
||||
|
||||
return () => {
|
||||
sidebarManagerState.unregisterSidebar(SIDEBAR_KEYS.AI_ASSISTANT)
|
||||
}
|
||||
}, [state])
|
||||
const previousProjectRef = useRef<string | undefined>(project?.ref)
|
||||
|
||||
// Effect to load state from IndexedDB on mount or projectRef change
|
||||
useEffect(() => {
|
||||
let isMounted = true
|
||||
|
||||
async function loadAndInitializeState() {
|
||||
if (!project?.ref || typeof window === 'undefined') {
|
||||
if (project?.ref === undefined) {
|
||||
state.resetAiAssistantPanel()
|
||||
}
|
||||
return // Don't load if no projectRef or not in browser
|
||||
}
|
||||
if (typeof window === 'undefined') {
|
||||
return
|
||||
}
|
||||
|
||||
if (!project?.ref) {
|
||||
// Only reset if we previously had a project ref (e.g., navigating away)
|
||||
if (previousProjectRef.current) {
|
||||
state.resetAiAssistantPanel()
|
||||
}
|
||||
previousProjectRef.current = project?.ref
|
||||
return
|
||||
}
|
||||
|
||||
previousProjectRef.current = project.ref
|
||||
|
||||
async function loadAndInitializeState() {
|
||||
let loadedState: StoredAiAssistantState | null = null
|
||||
|
||||
// 1. Try loading from IndexedDB
|
||||
@@ -552,7 +482,6 @@ export const AiAssistantStateContextProvider = ({ children }: PropsWithChildren)
|
||||
// Prepare state for IndexedDB
|
||||
const stateToSave: StoredAiAssistantState = {
|
||||
projectRef: project?.ref,
|
||||
open: snap.open,
|
||||
activeChatId: snap.activeChatId,
|
||||
model: snap.model,
|
||||
chats: snap.chats
|
||||
|
||||
@@ -79,14 +79,6 @@ export const editorPanelState = proxy({
|
||||
},
|
||||
})
|
||||
|
||||
// Register to reset state when sidebar closes (but don't control sidebar open/close)
|
||||
sidebarManagerState.registerSidebar(SIDEBAR_KEYS.EDITOR_PANEL, {
|
||||
onClose: () => {
|
||||
// Auto-reset state when sidebar closes
|
||||
editorPanelState.reset()
|
||||
},
|
||||
})
|
||||
|
||||
export const getEditorPanelStateSnapshot = () => snapshot(editorPanelState)
|
||||
|
||||
export const useEditorPanelStateSnapshot = (
|
||||
|
||||
@@ -56,12 +56,11 @@ const createSidebarManagerState = () => {
|
||||
},
|
||||
|
||||
openSidebar(id: SidebarKey) {
|
||||
if (!state.panels[id]) {
|
||||
state.registerSidebar(id)
|
||||
}
|
||||
|
||||
const panel = state.panels[id]
|
||||
if (!panel) return
|
||||
if (!panel) {
|
||||
console.warn(`Sidebar "${id}" is not registered. Register it before opening.`)
|
||||
return
|
||||
}
|
||||
|
||||
if (panel.open) {
|
||||
state.activeSidebar = id
|
||||
|
||||
Reference in new issue
Block a user