docs: redesign sidebar (#8408)
* initial changes * redesign the sidebar + nav drawer * changes to sidebar items * finish up sidebar redesign * support new sidebar in resources * general fixes * integrate in ui * support api reference * refactor * integrate in user guide * docs: fix build errors * fix user guide build * more refactoring * added banner * added bottom logo + icon * fix up sidebar * fix up paddings * fix shadow bottom * docs: add table of content (#8445) * add toc types * implement toc functionality * finished toc redesign * redesigned table of content * mobile fixes * truncate text in toc * mobile fixes * merge fixes * implement redesign * add hide sidebar * add menu action item * finish up hide sidebar design * implement redesign in resources * integrate in api reference * integrate changes in ui * fixes to api reference scrolling * fix build error * fix build errors * fixes * fixes to sidebar * general fixes * fix active category not closing * fix long titles
This commit is contained in:
@@ -0,0 +1,51 @@
|
||||
"use client"
|
||||
|
||||
import React, { createContext, useContext, useMemo, useState } from "react"
|
||||
import { NavigationDropdownItem } from "types"
|
||||
|
||||
export type MainNavContext = {
|
||||
navItems: NavigationDropdownItem[]
|
||||
activeItem?: NavigationDropdownItem
|
||||
reportIssueLink: string
|
||||
}
|
||||
|
||||
const MainNavContext = createContext<MainNavContext | null>(null)
|
||||
|
||||
export type MainNavProviderProps = {
|
||||
navItems: NavigationDropdownItem[]
|
||||
reportIssueLink: string
|
||||
children?: React.ReactNode
|
||||
}
|
||||
|
||||
export const MainNavProvider = ({
|
||||
navItems,
|
||||
reportIssueLink,
|
||||
children,
|
||||
}: MainNavProviderProps) => {
|
||||
const activeItem = useMemo(
|
||||
() => navItems.find((item) => item.type === "link" && item.isActive),
|
||||
[navItems]
|
||||
)
|
||||
|
||||
return (
|
||||
<MainNavContext.Provider
|
||||
value={{
|
||||
navItems,
|
||||
activeItem,
|
||||
reportIssueLink,
|
||||
}}
|
||||
>
|
||||
{children}
|
||||
</MainNavContext.Provider>
|
||||
)
|
||||
}
|
||||
|
||||
export const useMainNav = () => {
|
||||
const context = useContext(MainNavContext)
|
||||
|
||||
if (!context) {
|
||||
throw new Error("useMainNav must be used within a MainNavProvider")
|
||||
}
|
||||
|
||||
return context
|
||||
}
|
||||
@@ -22,9 +22,9 @@ export const MobileProvider = ({ children }: MobileProviderProps) => {
|
||||
const [isMobile, setIsMobile] = useState(false)
|
||||
|
||||
const handleResize = useCallback(() => {
|
||||
if (window.innerWidth < 1025 && !isMobile) {
|
||||
if (window.innerWidth < 1024 && !isMobile) {
|
||||
setIsMobile(true)
|
||||
} else if (window.innerWidth >= 1025 && isMobile) {
|
||||
} else if (window.innerWidth >= 1024 && isMobile) {
|
||||
setIsMobile(false)
|
||||
}
|
||||
}, [isMobile])
|
||||
|
||||
@@ -1,55 +0,0 @@
|
||||
"use client"
|
||||
|
||||
import React, { createContext, useContext, useState, useEffect } from "react"
|
||||
import { usePathname } from "next/navigation"
|
||||
|
||||
export type NavbarContextType = {
|
||||
activeItem: string | null
|
||||
setActiveItem: (value: string) => void
|
||||
}
|
||||
|
||||
const NavbarContext = createContext<NavbarContextType | null>(null)
|
||||
|
||||
export type NavbarProviderProps = {
|
||||
children: React.ReactNode
|
||||
basePath?: string
|
||||
}
|
||||
|
||||
export const NavbarProvider = ({
|
||||
children,
|
||||
basePath = "",
|
||||
}: NavbarProviderProps) => {
|
||||
const [activeItem, setActiveItem] = useState<string | null>(null)
|
||||
const pathname = usePathname()
|
||||
|
||||
const assemblePathName = (path: string) =>
|
||||
`${basePath}/${path.charAt(0) === "/" ? path.substring(1) : path}`
|
||||
|
||||
useEffect(() => {
|
||||
const newPath = assemblePathName(pathname)
|
||||
if (activeItem !== newPath) {
|
||||
setActiveItem(newPath)
|
||||
}
|
||||
}, [pathname, activeItem])
|
||||
|
||||
return (
|
||||
<NavbarContext.Provider
|
||||
value={{
|
||||
activeItem,
|
||||
setActiveItem,
|
||||
}}
|
||||
>
|
||||
{children}
|
||||
</NavbarContext.Provider>
|
||||
)
|
||||
}
|
||||
|
||||
export const useNavbar = (): NavbarContextType => {
|
||||
const context = useContext(NavbarContext)
|
||||
|
||||
if (!context) {
|
||||
throw new Error("useNavbar must be used inside a NavbarProvider")
|
||||
}
|
||||
|
||||
return context
|
||||
}
|
||||
@@ -9,7 +9,7 @@ import React, {
|
||||
} from "react"
|
||||
import { useSidebar } from "../Sidebar"
|
||||
import { usePrevious } from "@uidotdev/usehooks"
|
||||
import { SidebarItemType } from "types"
|
||||
import { InteractiveSidebarItem, SidebarItem } from "types"
|
||||
|
||||
export type Page = {
|
||||
title: string
|
||||
@@ -27,8 +27,8 @@ export const PaginationContext = createContext<PaginationContextType | null>(
|
||||
null
|
||||
)
|
||||
|
||||
type SidebarItemWithParent = SidebarItemType & {
|
||||
parent?: SidebarItemType
|
||||
type SidebarItemWithParent = InteractiveSidebarItem & {
|
||||
parent?: SidebarItem
|
||||
}
|
||||
|
||||
type SearchItemsResult = {
|
||||
@@ -43,20 +43,20 @@ export type PaginationProviderProps = {
|
||||
|
||||
export const PaginationProvider = ({ children }: PaginationProviderProps) => {
|
||||
const { items, activePath } = useSidebar()
|
||||
const combinedItems = useMemo(() => [...items.top, ...items.bottom], [items])
|
||||
const combinedItems = useMemo(() => [...items.default], [items])
|
||||
const previousActivePath = usePrevious(activePath)
|
||||
const [nextPage, setNextPage] = useState<Page | undefined>()
|
||||
const [prevPage, setPrevPage] = useState<Page | undefined>()
|
||||
|
||||
const getFirstChild = (
|
||||
item: SidebarItemType
|
||||
item: InteractiveSidebarItem
|
||||
): SidebarItemWithParent | undefined => {
|
||||
const children = getChildrenWithPages(item)
|
||||
if (!children?.length) {
|
||||
return undefined
|
||||
}
|
||||
|
||||
return children[0].path
|
||||
return children[0].type === "link"
|
||||
? {
|
||||
...children[0],
|
||||
parent: item,
|
||||
@@ -65,16 +65,18 @@ export const PaginationProvider = ({ children }: PaginationProviderProps) => {
|
||||
}
|
||||
|
||||
const getChildrenWithPages = (
|
||||
item: SidebarItemType
|
||||
item: InteractiveSidebarItem
|
||||
): SidebarItemWithParent[] | undefined => {
|
||||
return item.children?.filter(
|
||||
(childItem) =>
|
||||
childItem.path !== undefined || getChildrenWithPages(childItem)?.length
|
||||
)
|
||||
childItem.type === "link" ||
|
||||
(childItem.type !== "separator" &&
|
||||
getChildrenWithPages(childItem)?.length)
|
||||
) as SidebarItemWithParent[]
|
||||
}
|
||||
|
||||
const getPrevItem = (
|
||||
items: SidebarItemType[],
|
||||
items: SidebarItem[],
|
||||
index: number
|
||||
): SidebarItemWithParent | undefined => {
|
||||
let foundItem: SidebarItemWithParent | undefined
|
||||
@@ -82,6 +84,9 @@ export const PaginationProvider = ({ children }: PaginationProviderProps) => {
|
||||
.slice(0, index)
|
||||
.reverse()
|
||||
.some((item) => {
|
||||
if (item.type === "separator") {
|
||||
return false
|
||||
}
|
||||
if (item.children?.length) {
|
||||
const childItem = getPrevItem(item.children, item.children.length)
|
||||
if (childItem) {
|
||||
@@ -90,7 +95,7 @@ export const PaginationProvider = ({ children }: PaginationProviderProps) => {
|
||||
parent: item,
|
||||
}
|
||||
}
|
||||
} else if (item.path) {
|
||||
} else if (item.type === "link") {
|
||||
foundItem = item
|
||||
}
|
||||
|
||||
@@ -101,12 +106,16 @@ export const PaginationProvider = ({ children }: PaginationProviderProps) => {
|
||||
}
|
||||
|
||||
const getNextItem = (
|
||||
items: SidebarItemType[],
|
||||
items: SidebarItem[],
|
||||
index: number
|
||||
): SidebarItemWithParent | undefined => {
|
||||
let foundItem: SidebarItemWithParent | undefined
|
||||
items.slice(index + 1).some((item) => {
|
||||
if (item.path) {
|
||||
if (item.type === "separator") {
|
||||
return false
|
||||
}
|
||||
|
||||
if (item.type === "link") {
|
||||
foundItem = item
|
||||
} else if (item.children?.length) {
|
||||
const childItem = getNextItem(item.children, -1)
|
||||
@@ -124,13 +133,13 @@ export const PaginationProvider = ({ children }: PaginationProviderProps) => {
|
||||
return foundItem
|
||||
}
|
||||
|
||||
const searchItems = (currentItems: SidebarItemType[]): SearchItemsResult => {
|
||||
const searchItems = (currentItems: SidebarItem[]): SearchItemsResult => {
|
||||
const result: SearchItemsResult = {
|
||||
foundActive: false,
|
||||
}
|
||||
|
||||
result.foundActive = currentItems.some((item, index) => {
|
||||
if (item.path === activePath) {
|
||||
if (item.type === "link" && item.path === activePath) {
|
||||
if (index !== 0) {
|
||||
result.prevItem = getPrevItem(currentItems, index)
|
||||
}
|
||||
@@ -145,16 +154,15 @@ export const PaginationProvider = ({ children }: PaginationProviderProps) => {
|
||||
return true
|
||||
}
|
||||
|
||||
if (item.children?.length) {
|
||||
if (item.type !== "separator" && item.children?.length) {
|
||||
const childrenResult = searchItems(item.children)
|
||||
|
||||
if (childrenResult.foundActive) {
|
||||
result.prevItem = childrenResult.prevItem
|
||||
result.nextItem = childrenResult.nextItem
|
||||
if (!result.prevItem) {
|
||||
result.prevItem = item.path
|
||||
? item
|
||||
: getPrevItem(currentItems, index)
|
||||
result.prevItem =
|
||||
item.type === "link" ? item : getPrevItem(currentItems, index)
|
||||
}
|
||||
|
||||
if (!result.nextItem && index !== currentItems.length - 1) {
|
||||
@@ -178,8 +186,11 @@ export const PaginationProvider = ({ children }: PaginationProviderProps) => {
|
||||
result.prevItem
|
||||
? {
|
||||
title: result.prevItem.title,
|
||||
link: result.prevItem.path || "",
|
||||
parentTitle: result.prevItem.parent?.title,
|
||||
link: result.prevItem.type === "link" ? result.prevItem.path : "",
|
||||
parentTitle:
|
||||
result.prevItem.parent?.type !== "separator"
|
||||
? result.prevItem.parent?.title
|
||||
: undefined,
|
||||
}
|
||||
: undefined
|
||||
)
|
||||
@@ -187,8 +198,11 @@ export const PaginationProvider = ({ children }: PaginationProviderProps) => {
|
||||
result.nextItem
|
||||
? {
|
||||
title: result.nextItem.title,
|
||||
link: result.nextItem.path || "",
|
||||
parentTitle: result.nextItem.parent?.title,
|
||||
link: result.nextItem.type === "link" ? result.nextItem.path : "",
|
||||
parentTitle:
|
||||
result.nextItem.parent?.type !== "separator"
|
||||
? result.nextItem.parent?.title
|
||||
: undefined,
|
||||
}
|
||||
: undefined
|
||||
)
|
||||
|
||||
@@ -15,53 +15,47 @@ import { getScrolledTop } from "@/utils"
|
||||
import { useIsBrowser } from "@/hooks"
|
||||
import {
|
||||
SidebarItemSections,
|
||||
SidebarItemType,
|
||||
SidebarSectionItemsType,
|
||||
SidebarItem,
|
||||
SidebarSectionItems,
|
||||
SidebarItemLink,
|
||||
InteractiveSidebarItem,
|
||||
SidebarItemCategory,
|
||||
} from "types"
|
||||
|
||||
export type CurrentItemsState = SidebarSectionItemsType & {
|
||||
export type CurrentItemsState = SidebarSectionItems & {
|
||||
previousSidebar?: CurrentItemsState
|
||||
}
|
||||
|
||||
export type SidebarStyleOptions = {
|
||||
disableActiveTransition?: boolean
|
||||
noTitleStyling?: boolean
|
||||
}
|
||||
|
||||
export type SidebarContextType = {
|
||||
items: SidebarSectionItemsType
|
||||
items: SidebarSectionItems
|
||||
currentItems: CurrentItemsState | undefined
|
||||
activePath: string | null
|
||||
getActiveItem: () => SidebarItemType | undefined
|
||||
getActiveItem: () => SidebarItemLink | undefined
|
||||
setActivePath: (path: string | null) => void
|
||||
isItemActive: (item: SidebarItemType, checkChildren?: boolean) => boolean
|
||||
addItems: (
|
||||
item: SidebarItemType[],
|
||||
options?: {
|
||||
section?: SidebarItemSections
|
||||
parent?: {
|
||||
path: string
|
||||
changeLoaded?: boolean
|
||||
}
|
||||
indexPosition?: number
|
||||
ignoreExisting?: boolean
|
||||
}
|
||||
) => void
|
||||
isLinkActive: (item: SidebarItem, checkChildren?: boolean) => boolean
|
||||
isChildrenActive: (item: SidebarItemCategory) => boolean
|
||||
addItems: (item: SidebarItem[], options?: ActionOptionsType) => void
|
||||
findItemInSection: (
|
||||
section: SidebarItemType[],
|
||||
item: Partial<SidebarItemType>,
|
||||
section: SidebarItem[],
|
||||
item: Partial<SidebarItem>,
|
||||
checkChildren?: boolean
|
||||
) => SidebarItemType | undefined
|
||||
) => SidebarItem | undefined
|
||||
mobileSidebarOpen: boolean
|
||||
setMobileSidebarOpen: React.Dispatch<React.SetStateAction<boolean>>
|
||||
isSidebarEmpty: () => boolean
|
||||
desktopSidebarOpen: boolean
|
||||
setDesktopSidebarOpen: React.Dispatch<React.SetStateAction<boolean>>
|
||||
staticSidebarItems?: boolean
|
||||
shouldHandleHashChange: boolean
|
||||
sidebarRef: React.RefObject<HTMLDivElement>
|
||||
goBack: () => void
|
||||
sidebarTopHeight: number
|
||||
setSidebarTopHeight: React.Dispatch<React.SetStateAction<number>>
|
||||
resetItems: () => void
|
||||
isItemLoaded: (path: string) => boolean
|
||||
} & SidebarStyleOptions
|
||||
|
||||
export const SidebarContext = createContext<SidebarContextType | null>(null)
|
||||
@@ -70,6 +64,7 @@ export type ActionOptionsType = {
|
||||
section?: SidebarItemSections
|
||||
parent?: {
|
||||
path: string
|
||||
title: string
|
||||
changeLoaded?: boolean
|
||||
}
|
||||
indexPosition?: number
|
||||
@@ -79,25 +74,36 @@ export type ActionOptionsType = {
|
||||
export type ActionType =
|
||||
| {
|
||||
type: "add" | "update"
|
||||
items: SidebarItemType[]
|
||||
items: SidebarItem[]
|
||||
options?: ActionOptionsType
|
||||
}
|
||||
| {
|
||||
type: "replace"
|
||||
replacementItems: SidebarSectionItemsType
|
||||
replacementItems: SidebarSectionItems
|
||||
}
|
||||
|
||||
const areItemsEqual = (itemA: SidebarItem, itemB: SidebarItem): boolean => {
|
||||
if (itemA.type === "separator" || itemB.type === "separator") {
|
||||
return false
|
||||
}
|
||||
const hasSameTitle = itemA.title === itemB.title
|
||||
const hasSamePath =
|
||||
itemA.type === "link" && itemB.type === "link" && itemA.path === itemB.path
|
||||
|
||||
return hasSameTitle || hasSamePath
|
||||
}
|
||||
|
||||
const findItem = (
|
||||
section: SidebarItemType[],
|
||||
item: Partial<SidebarItemType>,
|
||||
section: SidebarItem[],
|
||||
item: Partial<SidebarItem>,
|
||||
checkChildren = true
|
||||
): SidebarItemType | undefined => {
|
||||
let foundItem: SidebarItemType | undefined
|
||||
): SidebarItemLink | undefined => {
|
||||
let foundItem: SidebarItemLink | undefined
|
||||
section.some((i) => {
|
||||
if (
|
||||
(!item.path && !i.path && i.title === item.title) ||
|
||||
i.path === item.path
|
||||
) {
|
||||
if (i.type === "separator") {
|
||||
return false
|
||||
}
|
||||
if (areItemsEqual(item as SidebarItem, i) && i.type === "link") {
|
||||
foundItem = i
|
||||
} else if (checkChildren && i.children) {
|
||||
foundItem = findItem(i.children, item)
|
||||
@@ -109,28 +115,19 @@ const findItem = (
|
||||
return foundItem
|
||||
}
|
||||
|
||||
export const reducer = (
|
||||
state: SidebarSectionItemsType,
|
||||
actionData: ActionType
|
||||
) => {
|
||||
export const reducer = (state: SidebarSectionItems, actionData: ActionType) => {
|
||||
if (actionData.type === "replace") {
|
||||
return actionData.replacementItems
|
||||
}
|
||||
|
||||
const { type, options } = actionData
|
||||
let { items } = actionData
|
||||
|
||||
const {
|
||||
section = SidebarItemSections.TOP,
|
||||
parent,
|
||||
ignoreExisting = false,
|
||||
indexPosition,
|
||||
} = options || {}
|
||||
const { parent, ignoreExisting = false, indexPosition } = options || {}
|
||||
const sectionName = SidebarItemSections.DEFAULT
|
||||
const sectionItems = state[sectionName]
|
||||
|
||||
if (!ignoreExisting) {
|
||||
const selectedSection =
|
||||
section === SidebarItemSections.BOTTOM ? state.bottom : state.top
|
||||
items = items.filter((item) => !findItem(selectedSection, item))
|
||||
items = items.filter((item) => !findItem(sectionItems, item))
|
||||
}
|
||||
|
||||
if (!items.length) {
|
||||
@@ -141,21 +138,24 @@ export const reducer = (
|
||||
case "add":
|
||||
return {
|
||||
...state,
|
||||
[section]:
|
||||
[sectionName]:
|
||||
indexPosition !== undefined
|
||||
? [
|
||||
...state[section].slice(0, indexPosition),
|
||||
...sectionItems.slice(0, indexPosition),
|
||||
...items,
|
||||
...state[section].slice(indexPosition),
|
||||
...sectionItems.slice(indexPosition),
|
||||
]
|
||||
: [...state[section], ...items],
|
||||
: [...sectionItems, ...items],
|
||||
}
|
||||
case "update":
|
||||
// find item index
|
||||
return {
|
||||
...state,
|
||||
[section]: state[section].map((i) => {
|
||||
if (i.path && parent?.path && i.path === parent?.path) {
|
||||
[sectionName]: sectionItems.map((i) => {
|
||||
if (i.type === "separator") {
|
||||
return i
|
||||
}
|
||||
if (parent && areItemsEqual(i, parent as SidebarItem)) {
|
||||
return {
|
||||
...i,
|
||||
children:
|
||||
@@ -166,7 +166,11 @@ export const reducer = (
|
||||
...(i.children?.slice(indexPosition) || []),
|
||||
]
|
||||
: [...(i.children || []), ...items],
|
||||
loaded: parent.changeLoaded ? true : i.loaded,
|
||||
loaded: parent.changeLoaded
|
||||
? true
|
||||
: i.type === "link"
|
||||
? i.loaded
|
||||
: true,
|
||||
}
|
||||
}
|
||||
return i
|
||||
@@ -181,7 +185,7 @@ export type SidebarProviderProps = {
|
||||
children?: ReactNode
|
||||
isLoading?: boolean
|
||||
setIsLoading?: React.Dispatch<React.SetStateAction<boolean>>
|
||||
initialItems?: SidebarSectionItemsType
|
||||
initialItems?: SidebarSectionItems
|
||||
shouldHandleHashChange?: boolean
|
||||
shouldHandlePathChange?: boolean
|
||||
scrollableElement?: Element | Window
|
||||
@@ -199,12 +203,10 @@ export const SidebarProvider = ({
|
||||
scrollableElement,
|
||||
staticSidebarItems = false,
|
||||
disableActiveTransition = false,
|
||||
noTitleStyling = false,
|
||||
resetOnCondition,
|
||||
}: SidebarProviderProps) => {
|
||||
const [items, dispatch] = useReducer(reducer, {
|
||||
top: initialItems?.top || [],
|
||||
bottom: initialItems?.bottom || [],
|
||||
default: initialItems?.default || [],
|
||||
mobile: initialItems?.mobile || [],
|
||||
})
|
||||
const [currentItems, setCurrentItems] = useState<
|
||||
@@ -212,6 +214,7 @@ export const SidebarProvider = ({
|
||||
>()
|
||||
const [activePath, setActivePath] = useState<string | null>("")
|
||||
const [mobileSidebarOpen, setMobileSidebarOpen] = useState<boolean>(false)
|
||||
const [sidebarTopHeight, setSidebarTopHeight] = useState(0)
|
||||
const [desktopSidebarOpen, setDesktopSidebarOpen] = useState(true)
|
||||
const sidebarRef = useRef<HTMLDivElement>(null)
|
||||
|
||||
@@ -224,30 +227,29 @@ export const SidebarProvider = ({
|
||||
|
||||
const findItemInSection = useCallback(findItem, [])
|
||||
|
||||
const isItemLoaded = useCallback(
|
||||
(path: string) => {
|
||||
const item =
|
||||
findItemInSection(items.mobile, { path, type: "link" }) ||
|
||||
findItemInSection(items.default, { path, type: "link" })
|
||||
|
||||
return item?.loaded || false
|
||||
},
|
||||
[items]
|
||||
)
|
||||
|
||||
const getActiveItem = useCallback(() => {
|
||||
if (activePath === null) {
|
||||
return undefined
|
||||
}
|
||||
|
||||
return (
|
||||
findItemInSection(items.mobile, { path: activePath }) ||
|
||||
findItemInSection(items.top, { path: activePath }) ||
|
||||
findItemInSection(items.bottom, { path: activePath })
|
||||
findItemInSection(items.mobile, { path: activePath, type: "link" }) ||
|
||||
findItemInSection(items.default, { path: activePath, type: "link" })
|
||||
)
|
||||
}, [activePath, items, findItemInSection])
|
||||
|
||||
const addItems = (
|
||||
newItems: SidebarItemType[],
|
||||
options?: {
|
||||
section?: SidebarItemSections
|
||||
parent?: {
|
||||
path: string
|
||||
changeLoaded?: boolean
|
||||
}
|
||||
indexPosition?: number
|
||||
ignoreExisting?: boolean
|
||||
}
|
||||
) => {
|
||||
const addItems = (newItems: SidebarItem[], options?: ActionOptionsType) => {
|
||||
dispatch({
|
||||
type: options?.parent ? "update" : "add",
|
||||
items: newItems,
|
||||
@@ -255,8 +257,12 @@ export const SidebarProvider = ({
|
||||
})
|
||||
}
|
||||
|
||||
const isItemActive = useCallback(
|
||||
(item: SidebarItemType, checkChildren = false): boolean => {
|
||||
const isLinkActive = useCallback(
|
||||
(item: SidebarItem, checkChildren = false): boolean => {
|
||||
if (item.type !== "link") {
|
||||
return false
|
||||
}
|
||||
|
||||
return (
|
||||
item.path === activePath ||
|
||||
(checkChildren && activePath?.split("_")[0] === item.path)
|
||||
@@ -265,15 +271,22 @@ export const SidebarProvider = ({
|
||||
[activePath]
|
||||
)
|
||||
|
||||
const isSidebarEmpty = useCallback((): boolean => {
|
||||
return Object.values(items).every((sectionItems) => {
|
||||
if (!Array.isArray(sectionItems)) {
|
||||
return true
|
||||
}
|
||||
const isChildrenActive = useCallback(
|
||||
(item: InteractiveSidebarItem): boolean => {
|
||||
return (
|
||||
item.children?.some((child) => {
|
||||
if (isLinkActive(child, true)) {
|
||||
return true
|
||||
}
|
||||
|
||||
return sectionItems.length === 0
|
||||
})
|
||||
}, [items])
|
||||
return child.type !== "separator" && child.children
|
||||
? isChildrenActive(child)
|
||||
: false
|
||||
}) || false
|
||||
)
|
||||
},
|
||||
[isLinkActive]
|
||||
)
|
||||
|
||||
const init = () => {
|
||||
const currentPath = location.hash.replace("#", "")
|
||||
@@ -283,23 +296,27 @@ export const SidebarProvider = ({
|
||||
}
|
||||
|
||||
const getCurrentSidebar = useCallback(
|
||||
(searchItems: SidebarItemType[]): SidebarItemType | undefined => {
|
||||
let currentSidebar: SidebarItemType | undefined
|
||||
(searchItems: SidebarItem[]): InteractiveSidebarItem | undefined => {
|
||||
let currentSidebar: InteractiveSidebarItem | undefined
|
||||
searchItems.some((item) => {
|
||||
if (item.isChildSidebar) {
|
||||
if (isItemActive(item)) {
|
||||
currentSidebar = item
|
||||
} else if (item.children?.length) {
|
||||
const childSidebar =
|
||||
getCurrentSidebar(item.children) ||
|
||||
findItem(item.children, { path: activePath || undefined })
|
||||
if (item.type === "separator") {
|
||||
return false
|
||||
}
|
||||
if (item.isChildSidebar && isLinkActive(item)) {
|
||||
currentSidebar = item
|
||||
}
|
||||
|
||||
if (childSidebar) {
|
||||
currentSidebar = childSidebar.isChildSidebar ? childSidebar : item
|
||||
}
|
||||
if (!currentSidebar && item.children?.length) {
|
||||
const childSidebar =
|
||||
getCurrentSidebar(item.children) ||
|
||||
findItem(item.children, {
|
||||
path: activePath || undefined,
|
||||
type: "link",
|
||||
})
|
||||
|
||||
if (childSidebar) {
|
||||
currentSidebar = childSidebar.isChildSidebar ? childSidebar : item
|
||||
}
|
||||
} else if (item.children?.length) {
|
||||
currentSidebar = getCurrentSidebar(item.children)
|
||||
}
|
||||
|
||||
return currentSidebar !== undefined
|
||||
@@ -307,7 +324,7 @@ export const SidebarProvider = ({
|
||||
|
||||
return currentSidebar
|
||||
},
|
||||
[isItemActive, activePath]
|
||||
[isLinkActive, activePath]
|
||||
)
|
||||
|
||||
const goBack = () => {
|
||||
@@ -317,9 +334,9 @@ export const SidebarProvider = ({
|
||||
|
||||
const previousSidebar = currentItems.previousSidebar || items
|
||||
|
||||
const backItem =
|
||||
previousSidebar.top.find((item) => item.path && !item.isChildSidebar) ||
|
||||
previousSidebar.bottom.find((item) => item.path && !item.isChildSidebar)
|
||||
const backItem = previousSidebar.default.find(
|
||||
(item) => item.type === "link" && !item.isChildSidebar
|
||||
) as SidebarItemLink
|
||||
|
||||
if (!backItem) {
|
||||
return
|
||||
@@ -334,8 +351,7 @@ export const SidebarProvider = ({
|
||||
dispatch({
|
||||
type: "replace",
|
||||
replacementItems: {
|
||||
top: initialItems?.top || [],
|
||||
bottom: initialItems?.bottom || [],
|
||||
default: initialItems?.default || [],
|
||||
mobile: initialItems?.mobile || [],
|
||||
},
|
||||
})
|
||||
@@ -391,7 +407,7 @@ export const SidebarProvider = ({
|
||||
}, [shouldHandleHashChange, isBrowser])
|
||||
|
||||
useEffect(() => {
|
||||
if (isLoading && items.top.length && items.bottom.length) {
|
||||
if (isLoading && items.default.length) {
|
||||
setIsLoading?.(false)
|
||||
}
|
||||
}, [items, isLoading, setIsLoading])
|
||||
@@ -412,8 +428,7 @@ export const SidebarProvider = ({
|
||||
return
|
||||
}
|
||||
|
||||
const currentSidebar =
|
||||
getCurrentSidebar(items.top) || getCurrentSidebar(items.bottom)
|
||||
const currentSidebar = getCurrentSidebar(items.default)
|
||||
|
||||
if (!currentSidebar) {
|
||||
setCurrentItems(undefined)
|
||||
@@ -423,18 +438,20 @@ export const SidebarProvider = ({
|
||||
if (
|
||||
currentSidebar.isChildSidebar &&
|
||||
currentSidebar.children &&
|
||||
currentItems?.parentItem?.path !== currentSidebar.path
|
||||
(!currentItems?.parentItem ||
|
||||
!areItemsEqual(currentItems?.parentItem, currentSidebar))
|
||||
) {
|
||||
const { children, ...parentItem } = currentSidebar
|
||||
const hasPreviousSidebar =
|
||||
currentItems?.previousSidebar?.parentItem?.type === "link" &&
|
||||
parentItem.type === "link" &&
|
||||
currentItems.previousSidebar.parentItem.path !== parentItem.path
|
||||
|
||||
setCurrentItems({
|
||||
top: children,
|
||||
bottom: [],
|
||||
default: children,
|
||||
mobile: items.mobile,
|
||||
parentItem: parentItem,
|
||||
previousSidebar:
|
||||
currentItems?.previousSidebar?.parentItem?.path !== parentItem.path
|
||||
? currentItems
|
||||
: undefined,
|
||||
previousSidebar: hasPreviousSidebar ? currentItems : undefined,
|
||||
})
|
||||
}
|
||||
}, [getCurrentSidebar, activePath])
|
||||
@@ -443,7 +460,7 @@ export const SidebarProvider = ({
|
||||
if (resetOnCondition?.()) {
|
||||
resetItems()
|
||||
}
|
||||
}, [resetOnCondition])
|
||||
}, [resetOnCondition, resetItems])
|
||||
|
||||
return (
|
||||
<SidebarContext.Provider
|
||||
@@ -453,21 +470,23 @@ export const SidebarProvider = ({
|
||||
addItems,
|
||||
activePath,
|
||||
setActivePath,
|
||||
isItemActive,
|
||||
isLinkActive: isLinkActive,
|
||||
isChildrenActive: isChildrenActive,
|
||||
findItemInSection,
|
||||
mobileSidebarOpen,
|
||||
setMobileSidebarOpen,
|
||||
isSidebarEmpty,
|
||||
getActiveItem,
|
||||
desktopSidebarOpen,
|
||||
setDesktopSidebarOpen,
|
||||
getActiveItem,
|
||||
staticSidebarItems,
|
||||
disableActiveTransition,
|
||||
noTitleStyling,
|
||||
shouldHandleHashChange,
|
||||
sidebarRef,
|
||||
goBack,
|
||||
sidebarTopHeight,
|
||||
setSidebarTopHeight,
|
||||
resetItems,
|
||||
isItemLoaded,
|
||||
}}
|
||||
>
|
||||
{children}
|
||||
|
||||
@@ -23,8 +23,7 @@ export const SiteConfigProvider = ({
|
||||
initConfig || {
|
||||
baseUrl: "",
|
||||
sidebar: {
|
||||
top: [],
|
||||
bottom: [],
|
||||
default: [],
|
||||
mobile: [],
|
||||
},
|
||||
}
|
||||
|
||||
@@ -2,9 +2,9 @@ export * from "./AiAssistant"
|
||||
export * from "./Analytics"
|
||||
export * from "./ColorMode"
|
||||
export * from "./LearningPath"
|
||||
export * from "./MainNav"
|
||||
export * from "./Mobile"
|
||||
export * from "./Modal"
|
||||
export * from "./Navbar"
|
||||
export * from "./Notification"
|
||||
export * from "./PageLoading"
|
||||
export * from "./Pagination"
|
||||
|
||||
Reference in New Issue
Block a user