Spaces:
Running
Running
| import { IsMobile } from "$lib/hooks/is-mobile.svelte.js"; | |
| import { getContext, setContext } from "svelte"; | |
| import { SIDEBAR_KEYBOARD_SHORTCUT } from "./constants.js"; | |
| type Getter<T> = () => T; | |
| export type SidebarStateProps = { | |
| /** | |
| * A getter function that returns the current open state of the sidebar. | |
| * We use a getter function here to support `bind:open` on the `Sidebar.Provider` | |
| * component. | |
| */ | |
| open: Getter<boolean>; | |
| /** | |
| * A function that sets the open state of the sidebar. To support `bind:open`, we need | |
| * a source of truth for changing the open state to ensure it will be synced throughout | |
| * the sub-components and any `bind:` references. | |
| */ | |
| setOpen: (open: boolean) => void; | |
| }; | |
| class SidebarState { | |
| readonly props: SidebarStateProps; | |
| open = $derived.by(() => this.props.open()); | |
| openMobile = $state(false); | |
| setOpen: SidebarStateProps["setOpen"]; | |
| #isMobile: IsMobile; | |
| state = $derived.by(() => (this.open ? "expanded" : "collapsed")); | |
| constructor(props: SidebarStateProps) { | |
| this.setOpen = props.setOpen; | |
| this.#isMobile = new IsMobile(); | |
| this.props = props; | |
| } | |
| // Convenience getter for checking if the sidebar is mobile | |
| // without this, we would need to use `sidebar.isMobile.current` everywhere | |
| get isMobile() { | |
| return this.#isMobile.current; | |
| } | |
| // Event handler to apply to the `<svelte:window>` | |
| handleShortcutKeydown = (e: KeyboardEvent) => { | |
| if (e.key === SIDEBAR_KEYBOARD_SHORTCUT && (e.metaKey || e.ctrlKey)) { | |
| e.preventDefault(); | |
| this.toggle(); | |
| } | |
| }; | |
| setOpenMobile = (value: boolean) => { | |
| this.openMobile = value; | |
| }; | |
| toggle = () => { | |
| return this.#isMobile.current ? (this.openMobile = !this.openMobile) : this.setOpen(!this.open); | |
| }; | |
| } | |
| const SYMBOL_KEY = "scn-sidebar"; | |
| /** | |
| * Instantiates a new `SidebarState` instance and sets it in the context. | |
| * | |
| * @param props The constructor props for the `SidebarState` class. | |
| * @returns The `SidebarState` instance. | |
| */ | |
| export function setSidebar(props: SidebarStateProps): SidebarState { | |
| return setContext(Symbol.for(SYMBOL_KEY), new SidebarState(props)); | |
| } | |
| /** | |
| * Retrieves the `SidebarState` instance from the context. This is a class instance, | |
| * so you cannot destructure it. | |
| * @returns The `SidebarState` instance. | |
| */ | |
| export function useSidebar(): SidebarState { | |
| return getContext(Symbol.for(SYMBOL_KEY)); | |
| } | |