import { type JSX, Accessor, createContext, createMemo, createSignal, untrack, useContext, } from "solid-js"; import { Motion, Presence } from "solid-motionone"; import { Rerun } from "@solid-primitives/keyed"; import { SettingsConfiguration, SettingsEntry, SettingsList } from "."; import { SettingsContent } from "./_layout/Content"; import { SettingsSidebar } from "./_layout/Sidebar"; export interface SettingsProps { /** * Close settings */ onClose?: () => void; /** * Settings context */ context: never; } /** * Transition animation */ export type SettingsTransition = "normal" | "to-child" | "to-parent"; /** * Provide navigation to child components */ const SettingsNavigationContext = createContext<{ page: Accessor; navigate: (path: string | SettingsEntry) => void; }>(); /** * Generic Settings component */ export function Settings(props: SettingsProps & SettingsConfiguration) { const [page, setPage] = createSignal( // eslint-disable-next-line (props.context as any)?.page, ); const [transition, setTransition] = createSignal("normal"); /** * Navigate to a certain page */ function navigate(entry: string | SettingsEntry) { let id; if (typeof entry === "object") { if (entry.onClick) { entry.onClick(); } else if (entry.href) { window.open(entry.href, "_blank"); } else if (entry.id) { id = entry.id; } } else { id = entry; } if (!id) return; const current = page(); if (current?.startsWith(id)) { setTransition("to-parent"); } else if (current && id.startsWith(current)) { setTransition("to-child"); } else { setTransition("normal"); } setPage(id); } return ( {(list) => ( <> untrack(transition) !== "normal" && setTimeout(() => (el.style.visibility = "visible"), 250) } initial={ transition() === "normal" ? { opacity: 0, y: 50 } : transition() === "to-child" ? { x: "100vw", } : { x: "-100vw" } } animate={{ opacity: 1, x: 0, y: 0, }} exit={ transition() === "normal" ? undefined : transition() === "to-child" ? { x: "-100vw", } : { x: "100vw" } } transition={{ duration: 0.2, easing: [0.17, 0.67, 0.58, 0.98], }} > {props.render({ page }, props.context)} )} ); } /** * Memoise the list but generate it within context */ function MemoisedList(props: { context: never; list: (context: never) => SettingsList; children: (list: Accessor>) => JSX.Element; }) { /** * Generate list of categories / links */ const list = createMemo(() => props.list(props.context)); return <>{props.children(list)}; } /** * Use settings navigation context */ export const useSettingsNavigation = () => useContext(SettingsNavigationContext)!;