mirror of
https://github.com/supabase/supabase.git
synced 2026-10-05 09:25:06 +03:00
* sidebar-manager * storage keys * tests * more ai spots * test fix * revert to default * remove ref * Update apps/studio/state/sidebar-manager-state.tsx Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> * Update apps/studio/components/ui/AIAssistantPanel/AIAssistant.tsx Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> * fix ts * fix * fux * fux query param * clean * fix * more * mock local storage * simplify * remove provider test * remve useopensidebar * fix(new homepage): open ai assistant on advisor card button clicks * Update apps/studio/components/layouts/ProjectLayout/LayoutSidebar/index.tsx Co-authored-by: Charis <26616127+charislam@users.noreply.github.com> * Update apps/studio/state/sidebar-manager-state.tsx Co-authored-by: Charis <26616127+charislam@users.noreply.github.com> * refine * editor sidebar manager * reset results * advisor sidebar manager * empty state and notice * event tracking * remove variable * remove use effect * open in sidebar * use sidebar old home * Update apps/studio/components/ui/EditorPanel/EditorPanel.tsx Co-authored-by: Charis <26616127+charislam@users.noreply.github.com> * connect hotkey * Update apps/studio/components/layouts/AppLayout/AssistantButton.tsx Co-authored-by: Charis <26616127+charislam@users.noreply.github.com> * Update apps/studio/state/advisor-state.ts Co-authored-by: Charis <26616127+charislam@users.noreply.github.com> * Update apps/studio/state/advisor-state.ts Co-authored-by: Charis <26616127+charislam@users.noreply.github.com> * fix * initial prompt * fix(inline editor button): only show keyboard shortcut if hotkey active * cleanup(advisor panel): minor code cleanup * fix(advisor panel): misplaced key on list * fix(advisor panel): add error state * fix(advisor panel): improve a11y * fix(advisor panel): cannot find selected item * fix * fix * tooltip * link * sidebar move up * merge inbox * project/org sidebars * panels * clean * fix use effect * layoutheader export * fix * ts * prettier * tests * remove markdown * remove org and project filters from state * text link * Update apps/studio/state/sidebar-manager-state.tsx Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> * fix * prettier * remove files * bump limit * noop * format * remove notifications on self hosted --------- Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> Co-authored-by: Charis Lam <26616127+charislam@users.noreply.github.com> Co-authored-by: Alaister Young <alaister@users.noreply.github.com>
188 lines
4.5 KiB
TypeScript
188 lines
4.5 KiB
TypeScript
import { LOCAL_STORAGE_KEYS } from 'common/constants'
|
|
import { useLocalStorageQuery } from 'hooks/misc/useLocalStorage'
|
|
import { ReactNode, useEffect } from 'react'
|
|
import { proxy, snapshot, useSnapshot } from 'valtio'
|
|
|
|
type SidebarHandlers = {
|
|
onOpen?: () => void
|
|
onClose?: () => void
|
|
}
|
|
|
|
type ManagedSidebar = SidebarHandlers & {
|
|
id: string
|
|
component: () => ReactNode | undefined
|
|
}
|
|
|
|
type SidebarManagerData = {
|
|
sidebars: Partial<Record<string, ManagedSidebar>>
|
|
activeSidebar: ManagedSidebar | undefined
|
|
}
|
|
|
|
type SidebarManagerState = SidebarManagerData & {
|
|
registerSidebar: (
|
|
id: string,
|
|
component: () => ReactNode | undefined,
|
|
handlers?: SidebarHandlers
|
|
) => void
|
|
unregisterSidebar: (id: string) => void
|
|
openSidebar: (id: string) => void
|
|
toggleSidebar: (id: string) => void
|
|
closeSidebar: (id: string) => void
|
|
isSidebarOpen: (id: string) => boolean
|
|
closeActive: () => void
|
|
}
|
|
|
|
const INITIAL_SIDEBAR_MANAGER_DATA: SidebarManagerData = {
|
|
sidebars: {},
|
|
activeSidebar: undefined,
|
|
}
|
|
|
|
const createSidebarManagerState = () => {
|
|
const state: SidebarManagerState = proxy({
|
|
...INITIAL_SIDEBAR_MANAGER_DATA,
|
|
|
|
registerSidebar(
|
|
id: string,
|
|
component: () => ReactNode | undefined,
|
|
handlers: SidebarHandlers = {}
|
|
) {
|
|
state.sidebars[id] = {
|
|
id,
|
|
component,
|
|
...handlers,
|
|
}
|
|
},
|
|
|
|
unregisterSidebar(id: string) {
|
|
const panel = state.sidebars[id]
|
|
if (!panel) return
|
|
|
|
delete state.sidebars[id]
|
|
|
|
if (state.activeSidebar?.id === id) {
|
|
state.activeSidebar = undefined
|
|
panel.onClose?.()
|
|
}
|
|
},
|
|
|
|
openSidebar(id: string) {
|
|
const panel = state.sidebars[id]
|
|
if (!panel) {
|
|
console.warn(`Sidebar "${id}" is not registered. Register it before opening.`)
|
|
return
|
|
}
|
|
|
|
if (state.activeSidebar?.id === id) {
|
|
return
|
|
}
|
|
|
|
if (state.activeSidebar && state.activeSidebar.id !== id) {
|
|
const previousPanel = state.activeSidebar
|
|
previousPanel?.onClose?.()
|
|
}
|
|
|
|
state.activeSidebar = panel
|
|
panel.onOpen?.()
|
|
},
|
|
|
|
toggleSidebar(id: string) {
|
|
const panel = state.sidebars[id]
|
|
if (!panel) {
|
|
console.warn(`Sidebar "${id}" is not registered. Register it before toggling.`)
|
|
return
|
|
}
|
|
|
|
if (state.activeSidebar?.id === id) {
|
|
panel.onClose?.()
|
|
state.activeSidebar = undefined
|
|
return
|
|
}
|
|
|
|
if (state.activeSidebar && state.activeSidebar.id !== id) {
|
|
const previousPanel = state.activeSidebar
|
|
previousPanel?.onClose?.()
|
|
}
|
|
|
|
state.activeSidebar = panel
|
|
panel.onOpen?.()
|
|
},
|
|
|
|
closeSidebar(id: string) {
|
|
if (state.activeSidebar?.id !== id) return
|
|
|
|
const panel = state.sidebars[id]
|
|
if (!panel) return
|
|
|
|
panel.onClose?.()
|
|
state.activeSidebar = undefined
|
|
return
|
|
},
|
|
|
|
isSidebarOpen(id: string) {
|
|
return state.activeSidebar?.id === id
|
|
},
|
|
|
|
closeActive() {
|
|
if (!state.activeSidebar) return
|
|
state.activeSidebar?.onClose?.()
|
|
state.activeSidebar = undefined
|
|
},
|
|
})
|
|
|
|
return state
|
|
}
|
|
|
|
export const sidebarManagerState = createSidebarManagerState()
|
|
|
|
export const getSidebarManagerSnapshot = () => snapshot(sidebarManagerState)
|
|
|
|
export const useSidebarManagerSnapshot = (options?: Parameters<typeof useSnapshot>[1]) =>
|
|
useSnapshot(sidebarManagerState, options)
|
|
|
|
export const useRegisterSidebar = (
|
|
id: string,
|
|
component: () => ReactNode,
|
|
handlers: SidebarHandlers = {},
|
|
hotKey?: string,
|
|
enabled?: boolean
|
|
) => {
|
|
const [isSidebarHotkeyEnabled] = useLocalStorageQuery<boolean>(
|
|
LOCAL_STORAGE_KEYS.HOTKEY_SIDEBAR(id),
|
|
true
|
|
)
|
|
|
|
const { sidebars, registerSidebar, unregisterSidebar } = useSidebarManagerSnapshot()
|
|
|
|
useEffect(() => {
|
|
const isEnabled = enabled !== false
|
|
if (!sidebars[id] && isEnabled) {
|
|
registerSidebar(id, component, handlers)
|
|
} else if (sidebars[id] && !isEnabled) {
|
|
unregisterSidebar(id)
|
|
}
|
|
|
|
return () => {
|
|
if (sidebars[id]) {
|
|
unregisterSidebar(id)
|
|
}
|
|
}
|
|
}, [id, enabled])
|
|
|
|
useEffect(() => {
|
|
if (!hotKey) return
|
|
|
|
function hotKeyHandler(e: KeyboardEvent) {
|
|
if ((e.metaKey || e.ctrlKey) && e.key === hotKey && !e.altKey && !e.shiftKey) {
|
|
sidebarManagerState.toggleSidebar(id)
|
|
}
|
|
}
|
|
|
|
if (isSidebarHotkeyEnabled) {
|
|
window.addEventListener('keydown', hotKeyHandler)
|
|
return () => {
|
|
window.removeEventListener('keydown', hotKeyHandler)
|
|
}
|
|
}
|
|
}, [id, hotKey, isSidebarHotkeyEnabled])
|
|
}
|