UNPKG

@grafana/ui

Version:
1 lines 12.6 kB
{"version":3,"file":"useSidebar.cjs","sources":["../../../../src/components/Sidebar/useSidebar.tsx"],"sourcesContent":["import React, { useCallback, useContext, useEffect } from 'react';\nimport { useMedia } from 'react-use';\n\nimport { store } from '@grafana/data';\n\nimport { useTheme2 } from '../../themes/ThemeContext';\nimport { clamp } from '../../utils/clamp';\n\nexport type SidebarPosition = 'left' | 'right';\n\nexport interface SidebarContextValue {\n isDocked: boolean;\n position: SidebarPosition;\n compact: boolean;\n hasOpenPane?: boolean;\n tabsMode?: boolean;\n outerWrapperProps: React.HTMLAttributes<HTMLDivElement>;\n paneWidth: number;\n bottomMargin: number;\n edgeMargin: number;\n contentMargin: number;\n isHidden: boolean;\n isHiddenPreference: boolean;\n canGoBack?: boolean;\n onToggleDock?: () => void;\n onResize: (diff: number) => void;\n /** Called when pane is closed or clicked outside of (in undocked mode) */\n onClosePane?: () => void;\n /** Open previous pane */\n onGoBack?: () => void;\n onToggleIsHidden: () => void;\n setIsHidden: (value: boolean) => void;\n}\n\nexport const SidebarContext: React.Context<SidebarContextValue | undefined> = React.createContext<\n SidebarContextValue | undefined\n>(undefined);\n\nexport const useSidebarContext = () => useContext(SidebarContext);\n\nexport interface UseSideBarOptions {\n hasOpenPane?: boolean;\n position?: SidebarPosition;\n tabsMode?: boolean;\n /** Initial state for compact mode */\n defaultToCompact?: boolean;\n /** Initial state for docked mode */\n defaultToDocked?: boolean;\n /** defaults to 2 grid units (16px) */\n bottomMargin?: number;\n /** defaults to 2 grid units (16px) */\n edgeMargin?: number;\n /** defaults to 2 grid units (16px) */\n contentMargin?: number;\n /** Called when pane is closed or clicked outside of (in undocked mode) */\n onClosePane?: () => void;\n /** Disables go back button */\n canGoBack?: boolean;\n /** Open previous pane */\n onGoBack?: () => void;\n /**\n * Optional key to use for persisting sidebar state (docked / compact / size)\n * Can only be app name as the final local storag key will be `grafana.ui.sidebar.{persistenceKey}.{docked|compact|size}`\n */\n persistenceKey?: string;\n /** Whether the sidebar is hidden by default */\n defaultIsHidden?: boolean;\n /**\n * Optional override for the persistance key used to store the hidden state.\n * Allows the hidden preference to be shared across consumers that otherwise use different\n * persistenceKeys (e.g. dashboard view vs edit mode share a single hide preference).\n */\n hiddenPersistenceKey?: string;\n}\n\nexport const SIDE_BAR_WIDTH_ICON_ONLY = 5;\nexport const SIDE_BAR_WIDTH_WITH_TEXT = 8;\n\nexport function useSidebar({\n hasOpenPane,\n position = 'right',\n tabsMode,\n defaultToCompact = true,\n defaultToDocked = false,\n bottomMargin = 2,\n edgeMargin = 2,\n contentMargin = 2,\n persistenceKey,\n onClosePane,\n onGoBack,\n canGoBack,\n defaultIsHidden = false,\n hiddenPersistenceKey,\n}: UseSideBarOptions): SidebarContextValue {\n const theme = useTheme2();\n const [isDocked, setIsDocked] = useSidebarSavedState(persistenceKey, 'docked', defaultToDocked);\n const [compact, setCompact] = useSidebarSavedState(persistenceKey, 'compact', defaultToCompact);\n const [paneWidth, setPaneWidth] = useSidebarSavedState(persistenceKey, 'size', 240);\n const [isHidden, setIsHidden] = useSidebarSavedState(\n hiddenPersistenceKey ?? persistenceKey,\n 'hidden',\n defaultIsHidden\n );\n const isMobile = useMedia(`(max-width: ${theme.breakpoints.values.sm}px)`);\n const isTemporarilyShown = isHidden && Boolean(hasOpenPane);\n const effectiveIsHidden = isHidden && !isTemporarilyShown;\n // On small viewports the sidebar is always rendered as an undocked overlay so it doesn't\n // permanently steal horizontal space — docking only applies on larger viewports.\n const effectiveIsDocked = !isTemporarilyShown && !isMobile && isDocked;\n\n // Used to accumulate drag distance to know when to change compact mode\n const [_, setCompactDrag] = React.useState(0);\n\n const onToggleDock = useCallback(() => {\n setIsDocked((prev) => !prev);\n }, [setIsDocked]);\n\n // Calculate how much space the outer wrapper needs to reserve for the sidebar toolbar + pane (if docked)\n const prop = position === 'right' ? 'paddingRight' : 'paddingLeft';\n const toolbarWidth =\n ((compact ? SIDE_BAR_WIDTH_ICON_ONLY : SIDE_BAR_WIDTH_WITH_TEXT) + edgeMargin + contentMargin) *\n theme.spacing.gridSize;\n\n const outerWrapperProps =\n effectiveIsHidden || isTemporarilyShown\n ? {}\n : {\n style: {\n [prop]: effectiveIsDocked && hasOpenPane ? paneWidth + toolbarWidth : toolbarWidth,\n },\n };\n\n const onResize = useCallback(\n (diff: number) => {\n setPaneWidth((prevWidth) => {\n // If no pane is open we use the resize action to toggle compact mode (button text visibility)\n if (!hasOpenPane) {\n setCompactDrag((prevDrag) => {\n const newDrag = prevDrag + diff;\n if (newDrag < -20 && !compact) {\n setCompact(() => true);\n return 0;\n } else if (newDrag > 20 && compact) {\n setCompact(() => false);\n return 0;\n }\n\n return newDrag;\n });\n\n return prevWidth;\n }\n\n const maxWidth = Math.max(window.innerWidth * 0.5, 500);\n return clamp(prevWidth + diff, 100, maxWidth);\n });\n },\n [hasOpenPane, setCompact, setPaneWidth, compact]\n );\n\n const onToggleIsHidden = useCallback(() => setIsHidden((prev) => !prev), [setIsHidden]);\n const setIsHiddenValue = useCallback((value: boolean) => setIsHidden(() => value), [setIsHidden]);\n\n return {\n isDocked: effectiveIsDocked,\n onToggleDock: isMobile || isTemporarilyShown ? undefined : onToggleDock,\n onResize,\n outerWrapperProps,\n position,\n compact,\n hasOpenPane,\n tabsMode,\n paneWidth,\n edgeMargin,\n bottomMargin,\n contentMargin,\n isHidden: effectiveIsHidden,\n isHiddenPreference: isHidden,\n onClosePane,\n onGoBack,\n canGoBack,\n onToggleIsHidden,\n setIsHidden: setIsHiddenValue,\n };\n}\n\nfunction readFromStore<T>(persistenceKey: string | undefined, subKey: string, defaultValue: T): T {\n if (!persistenceKey) {\n return defaultValue;\n }\n\n if (typeof defaultValue === 'boolean') {\n // eslint-disable-next-line @typescript-eslint/consistent-type-assertions\n return store.getBool(`grafana.ui.sidebar.${persistenceKey}.${subKey}`, defaultValue) as T;\n }\n\n if (typeof defaultValue === 'number') {\n const value = Number.parseInt(store.get(`grafana.ui.sidebar.${persistenceKey}.${subKey}`), 10);\n // eslint-disable-next-line @typescript-eslint/consistent-type-assertions\n return Number.isNaN(value) ? defaultValue : (value as T);\n }\n\n return defaultValue;\n}\n\nexport function useSidebarSavedState<T = number | boolean>(\n persistenceKey: string | undefined,\n subKey: string,\n defaultValue: T\n) {\n const [state, setState] = React.useState<T>(() => readFromStore(persistenceKey, subKey, defaultValue));\n\n useEffect(() => {\n setState(readFromStore(persistenceKey, subKey, defaultValue));\n // Re-read from storage when the persistence key changes, but not when defaultValue changes\n // to avoid overriding a user-persisted value.\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [persistenceKey, subKey]);\n\n const setPersisted = useCallback(\n (cb: (prevState: T) => T) => {\n setState((prevState) => {\n const newState = cb(prevState);\n\n if (!persistenceKey) {\n return newState;\n }\n\n if (persistenceKey) {\n store.set(`grafana.ui.sidebar.${persistenceKey}.${subKey}`, String(newState));\n }\n\n return newState;\n });\n },\n [persistenceKey, subKey]\n );\n\n return [state, setPersisted] as const;\n}\n"],"names":["React","useContext","useTheme2","useMedia","useCallback","clamp","store","useEffect"],"mappings":"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"}