From 5341fe58d04eca00f44f863e65c930973afcdd1b Mon Sep 17 00:00:00 2001 From: outfoxxed Date: Thu, 24 Oct 2024 16:26:48 -0700 Subject: [PATCH] rewrite nav --- src/components/Accordion.astro | 13 +- src/components/Collapsible.astro | 6 +- src/components/NavCollapsible.astro | 20 ++ src/components/navigation/sidebars/Nav.astro | 31 ++- .../navigation/sidebars/nav/Link.astro | 10 + .../navigation/sidebars/nav/RootNav.astro | 51 +++++ .../nav/{index.tsx => SidebarWrapper.tsx} | 35 +--- .../navigation/sidebars/nav/Tree.astro | 23 +++ .../navigation/sidebars/nav/Tree.tsx | 176 ---------------- src/components/navigation/sidebars/types.d.ts | 6 - src/components/type/TypeDetails.astro | 4 +- src/icons/nav-marker.svg | 1 + src/styles/components/accordion.css | 1 + src/styles/css-config/base.css | 6 +- src/styles/css-config/code.css | 2 +- src/styles/docs/docs.css | 4 +- src/styles/docs/nav/nav-tree.css | 191 +++++++++--------- src/styles/docs/nav/nav.css | 89 ++------ 18 files changed, 254 insertions(+), 415 deletions(-) create mode 100644 src/components/NavCollapsible.astro create mode 100644 src/components/navigation/sidebars/nav/Link.astro create mode 100644 src/components/navigation/sidebars/nav/RootNav.astro rename src/components/navigation/sidebars/nav/{index.tsx => SidebarWrapper.tsx} (69%) create mode 100644 src/components/navigation/sidebars/nav/Tree.astro delete mode 100644 src/components/navigation/sidebars/nav/Tree.tsx create mode 100644 src/icons/nav-marker.svg diff --git a/src/components/Accordion.astro b/src/components/Accordion.astro index 613dd50..b55caee 100644 --- a/src/components/Accordion.astro +++ b/src/components/Accordion.astro @@ -1,5 +1,5 @@ --- -import "@styles/components/accordion.css" +import "@styles/components/accordion.css"; ---
@@ -19,12 +19,13 @@ import "@styles/components/accordion.css" const body = accordion.querySelector(".accordion-container") as HTMLDivElement; summary.addEventListener("click", event => { + if ((event.target as Element).tagName === "A") return; event.preventDefault(); - body.classList.toggle("animate", true); if (!accordion.open || accordion.classList.contains("closing")) { accordion.classList.toggle("closing", false); body.style.setProperty("--height", "0px"); + body.classList.toggle("animate", true); requestAnimationFrame(() => { accordion.open = true; @@ -34,8 +35,12 @@ import "@styles/components/accordion.css" body.style.setProperty("--height", body.scrollHeight + "px"); requestAnimationFrame(() => { - accordion.classList.toggle("closing", true); - body.style.setProperty("--height", "0px"); + body.classList.toggle("animate", true); + + requestAnimationFrame(() => { + accordion.classList.toggle("closing", true); + body.style.setProperty("--height", "0px"); + }); }); } }); diff --git a/src/components/Collapsible.astro b/src/components/Collapsible.astro index b4b9405..a6e1a74 100644 --- a/src/components/Collapsible.astro +++ b/src/components/Collapsible.astro @@ -1,9 +1,9 @@ --- -import Accordion from "./Accordion.astro" -import collapsibleMarker from "@icons/collapsible-marker.svg?raw" +import Accordion from "./Accordion.astro"; +import collapsibleMarker from "@icons/collapsible-marker.svg?raw"; interface Props { - title: string + title: string; } const { title } = Astro.props; --- diff --git a/src/components/NavCollapsible.astro b/src/components/NavCollapsible.astro new file mode 100644 index 0000000..cc71c89 --- /dev/null +++ b/src/components/NavCollapsible.astro @@ -0,0 +1,20 @@ +--- +import Accordion from "./Accordion.astro"; +import navMarker from "@icons/nav-marker.svg?raw"; + +interface Props { + title: string; + link: string; + current?: boolean; +} +const { title, link, current } = Astro.props; +--- + +
+ {title} + +
+ +
diff --git a/src/components/navigation/sidebars/Nav.astro b/src/components/navigation/sidebars/Nav.astro index 79af2b1..9001700 100644 --- a/src/components/navigation/sidebars/Nav.astro +++ b/src/components/navigation/sidebars/Nav.astro @@ -1,31 +1,24 @@ --- -import { generateTypeData } from "@config/io/generateTypeData"; -import { groupRoutes } from "@config/io/helpers"; -import NavComponent from "./nav"; - -const routes = await generateTypeData(); -const groupedRoutes = groupRoutes(routes); +import SidebarWrapper from "./nav/SidebarWrapper.tsx"; +import RootNav from "./nav/RootNav.astro"; const url = Astro.url.pathname.split("/"); const currentRoute = url[2]; const currentModule = url[3]; const currentClass = url[4]; -const treeProps = { - items: groupedRoutes, - currentRoute: currentRoute, - currentModule: currentModule, - currentClass: currentClass, -}; +export interface Props { + mobile: boolean; +} const { mobile } = Astro.props; --- - diff --git a/src/components/navigation/sidebars/nav/Link.astro b/src/components/navigation/sidebars/nav/Link.astro new file mode 100644 index 0000000..b439df8 --- /dev/null +++ b/src/components/navigation/sidebars/nav/Link.astro @@ -0,0 +1,10 @@ +--- +interface Props { + title: string; + link: string; + current?: boolean; +} + +const { title, link, current } = Astro.props; +--- +{title} diff --git a/src/components/navigation/sidebars/nav/RootNav.astro b/src/components/navigation/sidebars/nav/RootNav.astro new file mode 100644 index 0000000..2ef3771 --- /dev/null +++ b/src/components/navigation/sidebars/nav/RootNav.astro @@ -0,0 +1,51 @@ +--- +export interface Props { + currentRoute: string; + currentModule: string; + currentClass: string; +} + +const { currentRoute, currentModule, currentClass } = Astro.props; + +import { generateTypeData } from "@config/io/generateTypeData"; +import { groupRoutes } from "@config/io/helpers"; +import Tree from "./Tree.astro"; + +const routes = await generateTypeData(); +const groupedRoutes = groupRoutes(routes); + +const configuration = { + title: "Configuration", + link: "/docs/configuration", + current: currentRoute.startsWith("configuration"), + entries: groupedRoutes.tutorials.configuration.map( + ({ name, type }) => ({ + title: name, + link: `/docs/configuration/${type}`, + current: currentModule === type, + }) + ), +}; + +const types = { + title: "Quickshell Types", + link: "/docs/types", + current: currentRoute.startsWith("types"), + entries: Object.entries(groupedRoutes.types).map( + ([module, items]) => ({ + title: module, + link: `/docs/types/${module}`, + current: currentModule === module, + entries: items.map(type => ({ + title: type.name, + link: `/docs/types/${module}/${type.name}`, + current: currentClass === type.name, + })), + }) + ), +}; +--- + diff --git a/src/components/navigation/sidebars/nav/index.tsx b/src/components/navigation/sidebars/nav/SidebarWrapper.tsx similarity index 69% rename from src/components/navigation/sidebars/nav/index.tsx rename to src/components/navigation/sidebars/nav/SidebarWrapper.tsx index 8ba78de..2c81e2f 100644 --- a/src/components/navigation/sidebars/nav/index.tsx +++ b/src/components/navigation/sidebars/nav/SidebarWrapper.tsx @@ -4,37 +4,25 @@ import { onMount, onCleanup, type Component, + type JSXElement, } from "solid-js"; -import { LoadingSpinner, MenuToX, XToMenu } from "@icons"; -import { Tree } from "./Tree"; -import type { NavProps } from "../types"; +import { MenuToX, XToMenu } from "@icons"; -const NavComponent: Component = props => { +export interface SidebarContent { + children: JSXElement; +} + +const NavComponent: Component = props => { const [open, setOpen] = createSignal(false); - const { tree, mobile, routes } = props; + const { children } = props; let navRef: HTMLDivElement; - if (!tree) { - return ; - } - function toggle(e: MouseEvent) { e.preventDefault(); setOpen(!open()); } - if (!mobile) { - return ( - - ); - } - const handleClickOutside = (event: MouseEvent) => { const isLink = "href" in (event.target || {}); const isInBody = document.body.contains(event.target as Node); @@ -78,12 +66,7 @@ const NavComponent: Component = props => { id={open() ? "#qs_search" : ""} class={`nav-items ${open() ? "shown" : ""}`} > - + {children} ); diff --git a/src/components/navigation/sidebars/nav/Tree.astro b/src/components/navigation/sidebars/nav/Tree.astro new file mode 100644 index 0000000..d3cb288 --- /dev/null +++ b/src/components/navigation/sidebars/nav/Tree.astro @@ -0,0 +1,23 @@ +--- +import NavCollapsible from "@components/NavCollapsible.astro"; +import Self from "./Tree.astro"; +import Link from "./Link.astro"; + +interface TreeEntry { + title: string; + link: string; + current?: boolean; + entries?: TreeEntry[]; +} + +interface Props extends TreeEntry {} + +const { title, link, entries, current } = Astro.props; +--- + + {entries?.map(entry => entry.entries ? ( + + ) : ( + + ))} + diff --git a/src/components/navigation/sidebars/nav/Tree.tsx b/src/components/navigation/sidebars/nav/Tree.tsx deleted file mode 100644 index 70cacd2..0000000 --- a/src/components/navigation/sidebars/nav/Tree.tsx +++ /dev/null @@ -1,176 +0,0 @@ -import { type Component, Index, For } from "solid-js"; -import { Accordion } from "@ark-ui/solid"; - -import { LinkSimple, ShevronSmallDown } from "@icons"; -import type { TreeProps } from "../types"; - -export const Tree: Component = props => { - const { currentRoute, currentModule, currentClass, items } = - props; - - const typeKeys = items!.types && Object.keys(items!.types); - - const tutorials = - items!.tutorials && items!.tutorials - ? items!.tutorials.configuration - : null; - - return ( - - ); -}; diff --git a/src/components/navigation/sidebars/types.d.ts b/src/components/navigation/sidebars/types.d.ts index 3189119..8ad4b48 100644 --- a/src/components/navigation/sidebars/types.d.ts +++ b/src/components/navigation/sidebars/types.d.ts @@ -16,12 +16,6 @@ export interface TreeProps { currentClass: string | null; } -export interface NavProps { - routes: GroupedRoutes; - tree: TreeProps; - mobile: boolean; -} - // Right export interface TOCProps { config?: ConfigHeading[]; diff --git a/src/components/type/TypeDetails.astro b/src/components/type/TypeDetails.astro index cb7601e..4e66677 100644 --- a/src/components/type/TypeDetails.astro +++ b/src/components/type/TypeDetails.astro @@ -1,8 +1,8 @@ --- -import { processMarkdown } from "@config/io/markdown" +import { processMarkdown } from "@config/io/markdown"; export interface Props { - markdown?: string, + markdown?: string; } const { markdown } = Astro.props; diff --git a/src/icons/nav-marker.svg b/src/icons/nav-marker.svg new file mode 100644 index 0000000..2d77f0a --- /dev/null +++ b/src/icons/nav-marker.svg @@ -0,0 +1 @@ + diff --git a/src/styles/components/accordion.css b/src/styles/components/accordion.css index db316b0..4ba43bc 100644 --- a/src/styles/components/accordion.css +++ b/src/styles/components/accordion.css @@ -7,6 +7,7 @@ & .accordion-container { /* fixes jumps due to margins on inline items */ display: flex; + flex-direction: column; } & .accordion-container.animate { diff --git a/src/styles/css-config/base.css b/src/styles/css-config/base.css index c4a3d2e..6505db5 100644 --- a/src/styles/css-config/base.css +++ b/src/styles/css-config/base.css @@ -53,8 +53,10 @@ html { --var-link-color: 190 85 60; --inner-param-color: 215 80 27; --inner-param-border-color: 215 50 50; - --nav-hovered-bkg: var(--blue) 100 93; + --nav-hovered-bkg: var(--blue) 100 92; + --nav-hovered-weak-bkg: var(--blue) 100 93; --nav-selected-bkg: var(--blue) 100 90; + --nav-selected-hovered-bkg: var(--blue) 100 85; --nav-selected-text: var(--blue) 60 60; --nav-indicator-bkg: var(--blue) 45 80; --toc-hovered-bkg: 0 0 0 / 0.1; @@ -106,7 +108,9 @@ html.dark { --inner-param-border-color: 215 26 46; --inner-param-color: 215 60 70; --nav-hovered-bkg: var(--blue) 40 10; + --nav-hovered-weak-bkg: var(--blue) 35 8; --nav-selected-bkg: var(--blue) 40 13; + --nav-selected-hovered-bkg: var(--blue) 40 17; --nav-selected-text: var(--blue) 100 70; --nav-indicator-bkg: var(--blue) 30 30; --toc-hovered-bkg: 0 0 100 / 0.07; diff --git a/src/styles/css-config/code.css b/src/styles/css-config/code.css index f30fa94..10d4dd4 100644 --- a/src/styles/css-config/code.css +++ b/src/styles/css-config/code.css @@ -27,7 +27,7 @@ pre { overflow: auto; text-wrap: wrap; - &>button { + & > button { all: unset; width: 2rem; height: 2rem; diff --git a/src/styles/docs/docs.css b/src/styles/docs/docs.css index 398fa8a..e4d5b45 100644 --- a/src/styles/docs/docs.css +++ b/src/styles/docs/docs.css @@ -186,6 +186,8 @@ ul { cursor: pointer; } - & div > svg { transform: rotate(90deg) } + & div > svg { + transform: rotate(90deg); + } } } diff --git a/src/styles/docs/nav/nav-tree.css b/src/styles/docs/nav/nav-tree.css index e686fea..7c81ff3 100644 --- a/src/styles/docs/nav/nav-tree.css +++ b/src/styles/docs/nav/nav-tree.css @@ -1,117 +1,108 @@ -[data-scope="accordion"][data-part="root"] { - display: flex; - flex-direction: column; - gap: 0.15rem; - - & [data-part="item"] { - padding-top: 0.2rem; - padding-bottom: 0.2rem; - } +.nav-component { + margin: 0.35em 0; } -[data-scope="accordion"][data-part="item-trigger"] { - background-color: transparent; - position: relative; - border: unset; - border-radius: 6px; - cursor: pointer; - font-size: 1rem; - width: 100%; - min-height: 2.2rem; - display: flex; - justify-content: flex-start; - align-items: center; - gap: 0.25rem; +.nav-link { + text-decoration: none !important; +} +.nav-item { + display: block; + border-radius: 6px; transition: background-color 0.2s ease; + padding: 0.4em; &:hover { background-color: hsl(var(--nav-hovered-bkg)); } -} -[data-scope="accordion"][data-part="item-indicator"] { - position: relative; - display: flex; - flex-shrink: 0; - justify-content: center; - align-items: center; - width: 24px; - height: 24px; - border-radius: 50%; - margin-left: 3px; - transition: transform 250ms ease-in-out; - - &:hover { - background-color: hsl(var(--nav-indicator-bkg)); - } - - &[data-state="open"] { - transform: rotate(180deg); - } -} - -.nav-shevron { - color: black; -} - -html.dark .nav-shevron { - color: white; -} - -[data-scope="accordion"][data-part="item-content"] { - --height: 709; - margin-block: 0.175rem; - margin-left: 1.6rem; - - & .arktree-item, - [data-part="item-content"]>div { - display: flex; - flex-direction: column; - gap: 0.15em; - margin: 0.4rem 0; - border-radius: 6px; - - transition: background-color 0.2s ease; - - &:hover { - background-color: hsl(var(--nav-hovered-bkg)); - } - - &>a { - padding: 0.4rem; - padding-left: 0.6rem; - width: 100%; - text-decoration: none; - } - } - - & .arktree-item.__current-type-doc, - [data-part="item-content"]>div.__current-type-doc { + &.nav-current { + color: hsl(var(--nav-selected-text)); background-color: hsl(var(--nav-selected-bkg)); + } +} - & a { +.nav-collapsible { + overflow: hidden; + + & > summary { + user-select: none; + + & > div { + & > .nav-collapse-marker, + a { + transition: background-color 0.2s ease; + } + + & > .nav-collapse-marker { + border-radius: 0 6px 6px 0; + padding: 0.4em 0.8em; + display: flex; + } + + & > a { + border-radius: 6px 0 0 6px; + padding: 0.4em; + flex-grow: 1; + } + + &:hover { + & > .nav-collapse-marker, + a { + background-color: hsl(var(--nav-hovered-bkg)); + + &:not(:hover) { + background-color: hsl(var(--nav-hovered-weak-bkg)); + } + } + } + + display: flex; + flex-direction: row; + align-items: stretch; + gap: 0.1em; + + & svg { + transition: transform 0.3s ease; + font-size: 1.1em; + } + } + } + + & .accordion-container > div { + padding-left: 1.2em; + + & p:first-child { + padding-top: 0; + margin-top: 0; + } + } +} + +.nav-collapsible.nav-current { + & > summary > div { + & > a { color: hsl(var(--nav-selected-text)); } + + & > .nav-collapse-marker, + a { + background-color: hsl(var(--nav-selected-bkg)); + } + + &:hover { + & > .nav-collapse-marker, + a { + background-color: hsl(var(--nav-selected-hovered-bkg)); + + &:not(:hover) { + background-color: hsl(var(--nav-selected-bkg)); + } + } + } } } -[data-scope="accordion"][data-part="item-content"][data-state="open"] { - animation: slideDown 250ms ease; -} - -[data-scope="accordion"][data-part="item-content"][data-state="closed"] { - animation: slideUp 200ms ease; -} - -.__current-type-doc { - color: hsl(var(--nav-selected-text)); - - & [data-part="item-trigger"] a { - color: hsl(var(--nav-selected-text)); - } - - & [data-scope="accordion"][data-part="item-trigger"] { - background-color: hsl(var(--nav-selected-bkg)); - } +.nav-collapsible[open]:not(.closing) > summary > div svg { + transform: rotate(180deg); } diff --git a/src/styles/docs/nav/nav.css b/src/styles/docs/nav/nav.css index 78898d8..56ce422 100644 --- a/src/styles/docs/nav/nav.css +++ b/src/styles/docs/nav/nav.css @@ -37,11 +37,11 @@ position: absolute; z-index: 11; overflow: hidden; - top: 2.6rem; - left: calc(-3rem + -80svw); + top: 2.5rem; + left: -24rem; height: calc(100svh - 2.5rem); + font-size: 1rem; scrollbar-width: none; - font-size: 1.2rem; -ms-overflow-style: none; background: hsla(var(--overlay-bkg)); border-right: 1px solid hsl(var(--overlay-bkg-border)); @@ -50,8 +50,7 @@ display: none; } - transition: left 0.3s ease, - padding 0.3s ease; + transition: left 0.3s ease, padding 0.3s ease; &.shown { display: flex; @@ -61,47 +60,16 @@ & .navtree { /* lines up with non overlay nav */ - padding: 0.5rem 0.618rem; + padding: 0rem 0.618rem; height: 100%; - width: 80svw; + width: 21rem; box-sizing: content-box; overflow-y: scroll; scrollbar-width: none; -ms-overflow-style: none; margin-bottom: 1rem; text-wrap: nowrap; - - & [data-part="item-indicator"] { - margin-left: 1rem; - margin-right: 0.8rem; - height: 2rem; - width: 2rem; - - & svg { - height: max-content; - width: 100%; - } - } - - & [data-part="item-trigger"] { - min-height: 3em; - font-size: 1.2rem; - } - - & [data-part="item-content"] { - margin-left: 3.7rem; - } - - & [data-part="item"] { - & [data-part="item-content"]>div { - min-height: 3em; - - &>a { - margin: auto 0; - } - } - } } } } @@ -112,45 +80,14 @@ text-align: start; } -@media (min-width: 40rem) { - .nav-toggle { - .nav-items { - font-size: 1rem; - top: 2.5rem; - left: -24rem; +@media not (min-width: 40rem) { + .nav-toggle .nav-items { + top: 2.6rem; + left: calc(-3rem + -80svw); + font-size: 1.2rem; - & .navtree { - width: 21rem; - } - - & .navtree { - & [data-part="item-indicator"] { - margin-left: 0.2rem; - margin-right: 0rem; - height: 1.5rem; - width: 1.5rem; - - & svg { - height: unset; - width: unset; - } - } - - & [data-part="item-trigger"] { - min-height: 2.2rem; - font-size: 1rem; - } - - & [data-part="item-content"] { - margin-left: 1.6rem; - } - - & [data-part="item"] { - & [data-part="item-content"]>div { - min-height: 2.2rem; - } - } - } + & .navtree { + width: 80svw; } } }