mirror of
https://github.com/hunvreus/basecoat.git
synced 2026-11-11 04:38:18 +00:00
Working on charts and sidebar
This commit is contained in:
+1
-1
@@ -16,4 +16,4 @@
|
||||
|
||||
}
|
||||
|
||||
@import "./themes/claude.css";
|
||||
/* @import "./themes/claude.css"; */
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
<!DOCTYPE html>
|
||||
<html class="dark"><head>
|
||||
<html>
|
||||
<head>
|
||||
<!-- Theme -->
|
||||
<script>
|
||||
function isDark() {
|
||||
@@ -13,10 +14,6 @@
|
||||
document.addEventListener('alpine:init', () => {
|
||||
Alpine.store('theme', {
|
||||
isDark: isDark(),
|
||||
set(value) {
|
||||
this.isDark = value;
|
||||
localStorage.setItem('theme', value ? 'dark' : 'light');
|
||||
},
|
||||
toggle() {
|
||||
this.isDark = document.documentElement.classList.toggle('dark');
|
||||
localStorage.setItem('theme', this.isDark ? 'dark' : 'light');
|
||||
@@ -82,154 +79,14 @@
|
||||
</head>
|
||||
<body>
|
||||
|
||||
{% from "sidebar.njk" import sidebar %}
|
||||
{% from "popover.njk" import popover %}
|
||||
|
||||
{% set footer %}
|
||||
{% set trigger %}
|
||||
<img src="https://github.com/hunvreus.png" class="rounded-lg shrink-0 size-8">
|
||||
<div class="grid flex-1 text-left text-sm leading-tight">
|
||||
<span class="truncate font-medium">Ronan Berder</span>
|
||||
<span class="truncate text-xs">@hunvreus</span>
|
||||
</div>
|
||||
{% lucide "chevrons-up-down" %}
|
||||
{% endset %}
|
||||
{% call popover(
|
||||
trigger=trigger,
|
||||
trigger_attrs={"class": "btn-ghost p-2 h-12 w-full flex items-center justify-start"},
|
||||
content_attrs={"data-side": "right", "data-align": "end", "class": "w-80"}
|
||||
) %}
|
||||
<div class="grid gap-4 p-4">
|
||||
<header class="grid gap-1.5">
|
||||
<h2 class="font-semibold">I hope you like Basecoat...</h2>
|
||||
<p class="text-muted-foreground text-sm">I made this (and <a class="underline underline-offset-4" href="https://pagescms.org" target="_target">other things</a>) because I thought it'd be helpful to others. If you find it useful, please consider sponsoring me on GitHub or following me on X.</p>
|
||||
</header>
|
||||
<footer class="grid gap-2">
|
||||
<a href="https://github.com/sponsors/hunvreus" class="btn-sm" target="_blank">Sponsor me on GitHub</a>
|
||||
<a href="https://x.com/hunvreus" class="btn-sm-outline" target="_blank">Follow me on X</a>
|
||||
</footer>
|
||||
</div>
|
||||
{% endcall %}
|
||||
{% endset %}
|
||||
|
||||
{% set header %}
|
||||
<a href="/" class="btn-ghost p-2 h-12 w-full justify-start">
|
||||
<div class="bg-sidebar-primary text-sidebar-primary-foreground flex aspect-square size-8 items-center justify-center rounded-lg">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 256 256" class="h-4 w-4"><rect width="256" height="256" fill="none"></rect><line x1="208" y1="128" x2="128" y2="208" fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="32"></line><line x1="192" y1="40" x2="40" y2="192" fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="32"></line></svg>
|
||||
</div>
|
||||
<div class="grid flex-1 text-left text-sm leading-tight">
|
||||
<span class="truncate font-medium">Basecoat</span>
|
||||
<span class="truncate text-xs">v{{ pkg.version }}</span>
|
||||
</div>
|
||||
</a>
|
||||
<div class="flex flex-col gap-2 px-2 text-sm">
|
||||
<form class="form relative">
|
||||
<label for="search" class="sr-only">Search</label>
|
||||
<input type="text" placeholder="Search the docs..." class="h-8 w-full shadow-none pl-8">
|
||||
{% lucide "search", { "class": "pointer-events-none absolute top-1/2 left-2 size-4 -translate-y-1/2 opacity-50 select-none" } %}
|
||||
</form>
|
||||
</div>
|
||||
{% endset %}
|
||||
|
||||
{% set link_attrs = {
|
||||
"hx-boost": "true",
|
||||
"hx-select": "#content",
|
||||
"hx-target": "#content",
|
||||
"hx-swap": "outerHTML"
|
||||
} %}
|
||||
|
||||
{% set icon_info %}{% lucide "info" %}{% endset %}
|
||||
{% set icon_square_terminal %}{% lucide "square-terminal" %}{% endset %}
|
||||
{% set icon_layout_dashboard %}{% lucide "layout-dashboard" %}{% endset %}
|
||||
{% set icon_github %}{% lucide "github" %}{% endset %}
|
||||
|
||||
{% set menu = [
|
||||
{ type: "group", label: "Getting started", items: [
|
||||
{ type: "item", label: "Introduction", url: "/", icon: icon_info, current: true, attrs: link_attrs },
|
||||
{ type: "item", label: "Installation", url: "/installation", icon: icon_square_terminal, attrs: link_attrs },
|
||||
{ type: "item", label: "Kitchen sink", url: "/kitchen-sink", icon: icon_layout_dashboard, attrs: link_attrs },
|
||||
{ type: "item", label: "GitHub", url: "https://github.com/hunvreus/basecoat", icon: icon_github, attrs: link_attrs }
|
||||
]},
|
||||
{ type: 'group', label: 'Components', items: [
|
||||
{ type: "item", url: "/components/accordion", label: "Accordion", attrs: link_attrs },
|
||||
{ type: "item", url: "/components/alert", label: "Alert", attrs: link_attrs },
|
||||
{ type: "item", url: "/components/alert-dialog", label: "Alert Dialog", attrs: link_attrs },
|
||||
{ type: "item", url: "/components/avatar", label: "Avatar", attrs: link_attrs },
|
||||
{ type: "item", url: "/components/badge", label: "Badge", attrs: link_attrs },
|
||||
{ type: "item", url: "/components/breadcrumb", label: "Breadcrumb", attrs: link_attrs },
|
||||
{ type: "item", url: "/components/button", label: "Button", attrs: link_attrs },
|
||||
{ type: "item", url: "/components/card", label: "Card", attrs: link_attrs },
|
||||
{ type: "item", url: "/components/checkbox", label: "Checkbox", attrs: link_attrs },
|
||||
{ type: "item", url: "/components/combobox", label: "Combobox", attrs: link_attrs },
|
||||
{ type: "item", url: "/components/dialog", label: "Dialog", attrs: link_attrs },
|
||||
{ type: "item", url: "/components/dropdown-menu", label: "Dropdown Menu", attrs: link_attrs },
|
||||
{ type: "item", url: "/components/form", label: "Form", attrs: link_attrs },
|
||||
{ type: "item", url: "/components/input", label: "Input", attrs: link_attrs },
|
||||
{ type: "item", url: "/components/label", label: "Label", attrs: link_attrs },
|
||||
{ type: "item", url: "/components/pagination", label: "Pagination", attrs: link_attrs },
|
||||
{ type: "item", url: "/components/popover", label: "Popover", attrs: link_attrs },
|
||||
{ type: "item", url: "/components/radio-group", label: "Radio Group", attrs: link_attrs },
|
||||
{ type: "item", url: "/components/select", label: "Select", attrs: link_attrs },
|
||||
{ type: "item", url: "/components/skeleton", label: "Skeleton", attrs: link_attrs },
|
||||
{ type: "item", url: "/components/sidebar", label: "Sidebar", attrs: link_attrs },
|
||||
{ type: "item", url: "/components/slider", label: "Slider", attrs: link_attrs },
|
||||
{ type: "item", url: "/components/switch", label: "Switch", attrs: link_attrs },
|
||||
{ type: "item", url: "/components/table", label: "Table", attrs: link_attrs },
|
||||
{ type: "item", url: "/components/tabs", label: "Tabs", attrs: link_attrs },
|
||||
{ type: "item", url: "/components/textarea", label: "Textarea", attrs: link_attrs },
|
||||
{ type: "item", url: "/components/toast", label: "Toast", attrs: link_attrs },
|
||||
{ type: "item", url: "/components/tooltip", label: "Tooltip", attrs: link_attrs }
|
||||
]}
|
||||
] %}
|
||||
|
||||
{{ sidebar(
|
||||
label="Sidebar navigation",
|
||||
header=header,
|
||||
footer=footer,
|
||||
menu=menu
|
||||
) }}
|
||||
{% include "partials/sidebar.njk" %}
|
||||
|
||||
<main id="content">
|
||||
<header class="bg-background sticky inset-x-0 top-0 isolate z-20 flex shrink-0 items-center gap-2 border-b z-10">
|
||||
<div class="flex h-14 w-full items-center gap-2 px-4">
|
||||
<button
|
||||
type="button"
|
||||
aria-label="Hide sidebar"
|
||||
data-tooltip="Hide sidebar"
|
||||
:data-tooltip="sidebarOpen ? 'Hide sidebar' : 'Show sidebar'"
|
||||
data-side="bottom"
|
||||
data-align="start"
|
||||
class="btn-sm-icon-ghost mr-auto"
|
||||
x-init
|
||||
x-data="{ sidebarOpen: true }"
|
||||
@click="
|
||||
sidebarOpen = !sidebarOpen;
|
||||
$dispatch('sidebar:toggle');
|
||||
"
|
||||
>
|
||||
{% lucide "panel-left" %}
|
||||
</button>
|
||||
<select class="select h-8 leading-none">
|
||||
<option value="light">Default</option>
|
||||
<option value="light">Other</option>
|
||||
</select>
|
||||
<button
|
||||
type="button"
|
||||
aria-label="Toggle theme"
|
||||
data-tooltip="Toggle theme"
|
||||
data-side="bottom"
|
||||
data-align="end"
|
||||
class="btn-icon-outline size-8"
|
||||
x-init
|
||||
@click="$store.theme.toggle()"
|
||||
>
|
||||
<span x-show="$store.theme.isDark">{% lucide "sun" %}</span>
|
||||
<span x-show="!$store.theme.isDark">{% lucide "moon" %}</span>
|
||||
</button>
|
||||
</div>
|
||||
{% include "partials/header.njk" %}
|
||||
</header>
|
||||
|
||||
<div class="grid gap-4 p-4">
|
||||
<div class="p-4">
|
||||
{{ content | safe }}
|
||||
</div>
|
||||
</main>
|
||||
|
||||
@@ -5,7 +5,7 @@ layout: layouts/base.njk
|
||||
<main class="mx-auto relative py-6 lg:gap-10 lg:py-8 flex w-full max-w-screen-lg">
|
||||
<div class="mx-auto w-full max-w-screen-md">
|
||||
<header class="space-y-2">
|
||||
<h1 class="text-3xl font-medium tracking-tight">{{ title }}</h1>
|
||||
<h1 class="text-3xl font-semibold tracking-tight">{{ title }}</h1>
|
||||
<p class="text-muted-foreground">{{ description }}</p>
|
||||
</header>
|
||||
<article class="pb-12 mt-8 content">
|
||||
|
||||
@@ -1,149 +1,128 @@
|
||||
{% set components = [
|
||||
{ "id": "accordion", "name": "Accordion" },
|
||||
{ "id": "alert", "name": "Alert" },
|
||||
{ "id": "alert-dialog", "name": "Alert Dialog" },
|
||||
{ "id": "avatar", "name": "Avatar" },
|
||||
{ "id": "badge", "name": "Badge" },
|
||||
{ "id": "breadcrumb", "name": "Breadcrumb" },
|
||||
{ "id": "button", "name": "Button" },
|
||||
{ "id": "card", "name": "Card" },
|
||||
{ "id": "checkbox", "name": "Checkbox" },
|
||||
{ "id": "combobox", "name": "Combobox" },
|
||||
{ "id": "dialog", "name": "Dialog" },
|
||||
{ "id": "dropdown-menu", "name": "Dropdown Menu" },
|
||||
{ "id": "form", "name": "Form" },
|
||||
{ "id": "input", "name": "Input" },
|
||||
{ "id": "label", "name": "Label" },
|
||||
{ "id": "pagination", "name": "Pagination" },
|
||||
{ "id": "popover", "name": "Popover" },
|
||||
{ "id": "radio-group", "name": "Radio Group" },
|
||||
{ "id": "select", "name": "Select" },
|
||||
{ "id": "skeleton", "name": "Skeleton" },
|
||||
{ "id": "sidebar", "name": "Sidebar" },
|
||||
{ "id": "slider", "name": "Slider" },
|
||||
{ "id": "switch", "name": "Switch" },
|
||||
{ "id": "table", "name": "Table" },
|
||||
{ "id": "tabs", "name": "Tabs" },
|
||||
{ "id": "textarea", "name": "Textarea" },
|
||||
{ "id": "toast", "name": "Toast" },
|
||||
{ "id": "tooltip", "name": "Tooltip" }
|
||||
{% from "sidebar.njk" import sidebar %}
|
||||
{% from "popover.njk" import popover %}
|
||||
|
||||
{% set footer %}
|
||||
{% set trigger %}
|
||||
<img src="https://github.com/hunvreus.png" class="rounded-lg shrink-0 size-8">
|
||||
<div class="grid flex-1 text-left text-sm leading-tight">
|
||||
<span class="truncate font-medium">Ronan Berder</span>
|
||||
<span class="truncate text-xs">@hunvreus</span>
|
||||
</div>
|
||||
{% lucide "chevrons-up-down" %}
|
||||
{% endset %}
|
||||
{% call popover(
|
||||
trigger=trigger,
|
||||
trigger_attrs={"class": "btn-ghost p-2 h-12 w-full flex items-center justify-start"},
|
||||
content_attrs={"data-side": "right", "data-align": "end", "class": "w-80"}
|
||||
) %}
|
||||
<div class="grid gap-4 p-4">
|
||||
<header class="grid gap-1.5">
|
||||
<h2 class="font-semibold">I hope you like Basecoat...</h2>
|
||||
<p class="text-muted-foreground text-sm">I made this (and <a class="underline underline-offset-4" href="https://pagescms.org" target="_target">other things</a>) because I thought it'd be helpful to others. If you find it useful, please consider sponsoring me on GitHub or following me on X.</p>
|
||||
</header>
|
||||
<footer class="grid gap-2">
|
||||
<a href="https://github.com/sponsors/hunvreus" class="btn-sm" target="_blank">Sponsor me on GitHub</a>
|
||||
<a href="https://x.com/hunvreus" class="btn-sm-outline" target="_blank">Follow me on X</a>
|
||||
</footer>
|
||||
</div>
|
||||
{% endcall %}
|
||||
{% endset %}
|
||||
|
||||
{% set header %}
|
||||
<a href="/" class="btn-ghost p-2 h-12 w-full justify-start">
|
||||
<div class="bg-sidebar-primary text-sidebar-primary-foreground flex aspect-square size-8 items-center justify-center rounded-lg">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 256 256" class="h-4 w-4"><rect width="256" height="256" fill="none"></rect><line x1="208" y1="128" x2="128" y2="208" fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="32"></line><line x1="192" y1="40" x2="40" y2="192" fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="32"></line></svg>
|
||||
</div>
|
||||
<div class="grid flex-1 text-left text-sm leading-tight">
|
||||
<span class="truncate font-medium">Basecoat</span>
|
||||
<span class="truncate text-xs">v{{ pkg.version }}</span>
|
||||
</div>
|
||||
</a>
|
||||
<div class="flex flex-col gap-2 px-2 text-sm">
|
||||
<form class="form relative">
|
||||
<label for="search" class="sr-only">Search</label>
|
||||
<input type="text" placeholder="Search the docs..." class="h-8 w-full shadow-none pl-8">
|
||||
{% lucide "search", { "class": "pointer-events-none absolute top-1/2 left-2 size-4 -translate-y-1/2 opacity-50 select-none" } %}
|
||||
</form>
|
||||
</div>
|
||||
{% endset %}
|
||||
|
||||
{% set link_attrs = {
|
||||
"hx-boost": "true",
|
||||
"hx-select": "#content",
|
||||
"hx-target": "#content",
|
||||
"hx-swap": "outerHTML"
|
||||
} %}
|
||||
|
||||
{% set icon_info %}{% lucide "info" %}{% endset %}
|
||||
{% set icon_square_terminal %}{% lucide "square-terminal" %}{% endset %}
|
||||
{% set icon_layout_dashboard %}{% lucide "layout-dashboard" %}{% endset %}
|
||||
{% set icon_github %}{% lucide "github" %}{% endset %}
|
||||
|
||||
{% set menu = [
|
||||
{ type: "group", label: "Getting started", items: [
|
||||
{ label: "Introduction", url: "/", icon: icon_info, current: true, attrs: link_attrs },
|
||||
{ label: "Installation", url: "/installation", icon: icon_square_terminal, attrs: link_attrs },
|
||||
{ label: "Kitchen sink", url: "/kitchen-sink", icon: icon_layout_dashboard, attrs: link_attrs },
|
||||
{ label: "GitHub", url: "https://github.com/hunvreus/basecoat", icon: icon_github, attrs: link_attrs }
|
||||
]},
|
||||
{ type: 'group', label: 'Components', items: [
|
||||
{ label: "Accordion", url: "/components/accordion", attrs: link_attrs },
|
||||
{ label: "Alert", url: "/components/alert", attrs: link_attrs },
|
||||
{ label: "Alert Dialog", url: "/components/alert-dialog", attrs: link_attrs },
|
||||
{ label: "Avatar", url: "/components/avatar", attrs: link_attrs },
|
||||
{ label: "Badge", url: "/components/badge", attrs: link_attrs },
|
||||
{ label: "Breadcrumb", url: "/components/breadcrumb", attrs: link_attrs },
|
||||
{ label: "Button", url: "/components/button", attrs: link_attrs },
|
||||
{ label: "Card", url: "/components/card", attrs: link_attrs },
|
||||
{ label: "Checkbox", url: "/components/checkbox", attrs: link_attrs },
|
||||
{ label: "Combobox", url: "/components/combobox", attrs: link_attrs },
|
||||
{ label: "Dialog", url: "/components/dialog", attrs: link_attrs },
|
||||
{ label: "Dropdown Menu", url: "/components/dropdown-menu", attrs: link_attrs },
|
||||
{ label: "Form", url: "/components/form", attrs: link_attrs },
|
||||
{ label: "Input", url: "/components/input", attrs: link_attrs },
|
||||
{ label: "Label", url: "/components/label", attrs: link_attrs },
|
||||
{ label: "Pagination", url: "/components/pagination", attrs: link_attrs },
|
||||
{ label: "Popover", url: "/components/popover", attrs: link_attrs },
|
||||
{ label: "Radio Group", url: "/components/radio-group", attrs: link_attrs },
|
||||
{ label: "Select", url: "/components/select", attrs: link_attrs },
|
||||
{ label: "Skeleton", url: "/components/skeleton", attrs: link_attrs },
|
||||
{ label: "Sidebar", url: "/components/sidebar", attrs: link_attrs },
|
||||
{ label: "Slider", url: "/components/slider", attrs: link_attrs },
|
||||
{ label: "Switch", url: "/components/switch", attrs: link_attrs },
|
||||
{ label: "Table", url: "/components/table", attrs: link_attrs },
|
||||
{ label: "Tabs", url: "/components/tabs", attrs: link_attrs },
|
||||
{ label: "Textarea", url: "/components/textarea", attrs: link_attrs },
|
||||
{ label: "Toast", url: "/components/toast", attrs: link_attrs },
|
||||
{ label: "Tooltip", url: "/components/tooltip", attrs: link_attrs }
|
||||
]}
|
||||
] %}
|
||||
|
||||
<header class="flex flex-col gap-2 p-2">
|
||||
<a href="/" class="btn-ghost p-2 h-12 w-full justify-start">
|
||||
<div class="bg-sidebar-primary text-sidebar-primary-foreground flex aspect-square size-8 items-center justify-center rounded-lg">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 256 256" class="h-4 w-4"><rect width="256" height="256" fill="none"></rect><line x1="208" y1="128" x2="128" y2="208" fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="32"></line><line x1="192" y1="40" x2="40" y2="192" fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="32"></line></svg>
|
||||
</div>
|
||||
<div class="grid flex-1 text-left text-sm leading-tight">
|
||||
<span class="truncate font-medium">Basecoat</span>
|
||||
<span class="truncate text-xs">v0.1.0</span>
|
||||
</div>
|
||||
</a>
|
||||
<div class="flex flex-col gap-2 px-2 text-sm">
|
||||
<form class="form relative">
|
||||
<label for="search" class="sr-only">Search</label>
|
||||
<input type="text" placeholder="Search the docs..." class="h-8 w-full shadow-none pl-8">
|
||||
{% lucide "search", { "class": "pointer-events-none absolute top-1/2 left-2 size-4 -translate-y-1/2 opacity-50 select-none" } %}
|
||||
</form>
|
||||
</div>
|
||||
</header>
|
||||
<section class="flex flex-1 flex-col gap-2 overflow-y-auto scrollbar">
|
||||
<nav aria-label="Pages navigation" class="flex flex-col p-2">
|
||||
<h2 class="text-sidebar-foreground/70 flex h-8 items-center px-2 text-xs font-medium">Getting Started</h2>
|
||||
<ul class="flex flex-col gap-1">
|
||||
<li>
|
||||
<a
|
||||
href="/"
|
||||
class="h-8 flex items-center gap-2 rounded-md p-2 text-left text-sm outline-hidden ring-sidebar-ring transition-[width,height,padding] hover:bg-sidebar-accent hover:text-sidebar-accent-foreground focus-visible:ring-2 [&>svg]:size-4 [&>svg]:shrink-0"
|
||||
>
|
||||
{% lucide "info" %}
|
||||
Introduction
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a
|
||||
href="/installation"
|
||||
class="h-8 flex items-center gap-2 rounded-md p-2 text-left text-sm outline-hidden ring-sidebar-ring transition-[width,height,padding] hover:bg-sidebar-accent hover:text-sidebar-accent-foreground focus-visible:ring-2 [&>svg]:size-4 [&>svg]:shrink-0"
|
||||
>
|
||||
{% lucide "terminal" %}
|
||||
Installation
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a
|
||||
href="/kitchen-sink"
|
||||
class="h-8 flex items-center gap-2 rounded-md p-2 text-left text-sm outline-hidden ring-sidebar-ring transition-[width,height,padding] hover:bg-sidebar-accent hover:text-sidebar-accent-foreground focus-visible:ring-2 [&>svg]:size-4 [&>svg]:shrink-0"
|
||||
>
|
||||
{% lucide "layout-dashboard" %}
|
||||
Kitchen sink
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a
|
||||
href="https://github.com/hunvreus/basecoat"
|
||||
target="_blank"
|
||||
class="h-8 flex items-center gap-2 rounded-md p-2 text-left text-sm outline-hidden ring-sidebar-ring transition-[width,height,padding] hover:bg-sidebar-accent hover:text-sidebar-accent-foreground focus-visible:ring-2 [&>svg]:size-4 [&>svg]:shrink-0"
|
||||
>
|
||||
{% lucide "github" %}
|
||||
GitHub
|
||||
{% lucide "arrow-up-right", { "class": "text-muted-foreground size-3.5 ml-auto" } %}
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
</nav>
|
||||
<nav aria-label="Components navigation" class="flex flex-col p-2">
|
||||
<h2 class="text-sidebar-foreground/70 flex h-8 items-center px-2 text-xs font-medium">Components</h2>
|
||||
<ul class="flex flex-col gap-1">
|
||||
{% for component in components %}
|
||||
<li>
|
||||
<a
|
||||
href="/components/{{ component.id }}"
|
||||
class="h-8 flex items-center gap-2 rounded-md p-2 text-left text-sm outline-hidden ring-sidebar-ring transition-[width,height,padding] hover:bg-sidebar-accent hover:text-sidebar-accent-foreground focus-visible:ring-2 [&>svg]:size-4 [&>svg]:shrink-0"
|
||||
hx-boost="true"
|
||||
hx-select="#content"
|
||||
hx-target="#content"
|
||||
hx-swap="outerHTML"
|
||||
>
|
||||
{{ component.name }}
|
||||
</a>
|
||||
</li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
</nav>
|
||||
</section>
|
||||
<footer class="flex flex-col gap-2 p-2">
|
||||
{% set profile_menu %}
|
||||
<div class="flex items-center gap-2 px-1 py-1.5 text-left text-sm">
|
||||
<img src="https://avatar.vercel.sh/ronan_berder" class="size-8 shrink-0 rounded-full" alt="Ronan Berder"/>
|
||||
<div class="grid flex-1 text-left text-sm leading-tight">
|
||||
<span class="truncate font-medium">hunvreus</span>
|
||||
<span class="text-muted-foreground truncate text-xs">hunvreus@example.com</span>
|
||||
</div>
|
||||
</div>
|
||||
<hr role="separator">
|
||||
<nav role="menu">
|
||||
<button type="button" role="menuitem">
|
||||
{% lucide "star" %}
|
||||
Upgrade to Pro
|
||||
</button>
|
||||
<hr role="separator">
|
||||
<button type="button" role="menuitem">
|
||||
{% lucide "user" %}
|
||||
Account
|
||||
</button>
|
||||
<button type="button" role="menuitem">
|
||||
{% lucide "credit-card" %}
|
||||
Billing
|
||||
</button>
|
||||
<button type="button" role="menuitem">
|
||||
{% lucide "bell" %}
|
||||
Notifications
|
||||
</button>
|
||||
<hr role="separator">
|
||||
<button type="button" role="menuitem">
|
||||
{% lucide "log-out" %}
|
||||
Signout
|
||||
</button>
|
||||
</nav>
|
||||
{% endset %}
|
||||
</footer>
|
||||
|
||||
{{ sidebar(
|
||||
label="Sidebar navigation",
|
||||
header=header,
|
||||
footer=footer,
|
||||
content_attrs={
|
||||
"x-data": "{
|
||||
updateCurrentPage() {
|
||||
$el.querySelectorAll('a').forEach(link => {
|
||||
normalizedCurrentPathname = window.location.pathname.endsWith('/') ?
|
||||
window.location.pathname.slice(0, -1) :
|
||||
window.location.pathname;
|
||||
normalizedLinkPathname = link.pathname.endsWith('/') ?
|
||||
link.pathname.slice(0, -1) :
|
||||
link.pathname;
|
||||
if (normalizedCurrentPathname === normalizedLinkPathname) {
|
||||
link.setAttribute('aria-current', 'page');
|
||||
} else {
|
||||
link.setAttribute('aria-current', 'false');
|
||||
}
|
||||
});
|
||||
}
|
||||
}",
|
||||
"x-init": "updateCurrentPage()",
|
||||
"@htmx:pushed-into-history.window": "updateCurrentPage()"
|
||||
},
|
||||
menu=menu
|
||||
) }}
|
||||
+388
-490
File diff suppressed because it is too large
Load Diff
@@ -744,6 +744,9 @@
|
||||
|
||||
/* Sidebar */
|
||||
.sidebar {
|
||||
&[data-uninitialized] {
|
||||
@apply max-md:hidden;
|
||||
}
|
||||
&:not([aria-hidden]),
|
||||
&[aria-hidden=false] {
|
||||
@apply max-md:bg-black/50 max-md:fixed max-md:inset-0 max-md:z-40;
|
||||
@@ -809,7 +812,7 @@
|
||||
|
||||
> a,
|
||||
> details > summary {
|
||||
@apply flex w-full items-center gap-2 overflow-hidden rounded-md p-2 text-left text-sm outline-hidden ring-sidebar-ring transition-[width,height,padding] hover:bg-sidebar-accent hover:text-sidebar-accent-foreground focus-visible:ring-2 active:bg-sidebar-accent active:text-sidebar-accent-foreground disabled:pointer-events-none disabled:opacity-50 group-has-data-[sidebar=menu-action]/menu-item:pr-8 aria-disabled:pointer-events-none aria-disabled:opacity-50 data-[active=true]:bg-sidebar-accent data-[active=true]:font-medium data-[active=true]:text-sidebar-accent-foreground data-[state=open]:hover:bg-sidebar-accent data-[state=open]:hover:text-sidebar-accent-foreground group-data-[collapsible=icon]:size-8! group-data-[collapsible=icon]:p-2! [&>span:last-child]:truncate [&>svg]:size-4 [&>svg]:shrink-0;
|
||||
@apply flex w-full items-center gap-2 overflow-hidden rounded-md p-2 text-left text-sm outline-hidden ring-sidebar-ring transition-[width,height,padding] hover:bg-sidebar-accent hover:text-sidebar-accent-foreground focus-visible:ring-2 active:bg-sidebar-accent active:text-sidebar-accent-foreground disabled:pointer-events-none disabled:opacity-50 aria-disabled:pointer-events-none aria-disabled:opacity-50 [&[aria-current=page]]:bg-sidebar-accent [&[aria-current=page]]:font-medium [&[aria-current=page]]:text-sidebar-accent-foreground [&>span:last-child]:truncate [&>svg]:size-4 [&>svg]:shrink-0;
|
||||
|
||||
&:not([data-variant]),
|
||||
&[data-variant=default] {
|
||||
|
||||
+11
-9
@@ -2,22 +2,24 @@ window.basecoat = window.basecoat || {};
|
||||
window.basecoat.registerSidebar= function(Alpine) {
|
||||
if (Alpine.components && Alpine.components.sidebar) return;
|
||||
|
||||
Alpine.data('sidebar', (initialOpen = true) => ({
|
||||
id: null,
|
||||
open: true,
|
||||
Alpine.data('sidebar', (initialOpen = true, initialMobileOpen = false) => ({
|
||||
open: window.innerWidth >= 768
|
||||
? initialOpen
|
||||
: initialMobileOpen,
|
||||
|
||||
init() {
|
||||
this.id = this.$el.id;
|
||||
this.open = initialOpen;
|
||||
this.$nextTick(() => {
|
||||
this.$el.removeAttribute('data-uninitialized');
|
||||
});
|
||||
},
|
||||
|
||||
$main: {
|
||||
'@sidebar:open.window'(e) { if (!this.id || (e.detail && e.detail.id === this.id)) this.open = true },
|
||||
'@sidebar:close.window'(e) { if (!this.id || (e.detail && e.detail.id === this.id)) this.open = false },
|
||||
'@sidebar:toggle.window'(e) { if (!this.id || (e.detail && e.detail.id === this.id)) this.open = !this.open },
|
||||
'@sidebar:open.window'(e) { this.open = true },
|
||||
'@sidebar:close.window'(e) { this.open = false },
|
||||
'@sidebar:toggle.window'(e) { this.open = !this.open },
|
||||
'@click'(e) { if (e.target === this.$el) this.open = false },
|
||||
':aria-hidden'() { return !this.open },
|
||||
':inert'() { return !this.open },
|
||||
':inert'() { return !this.open }
|
||||
},
|
||||
}));
|
||||
};
|
||||
@@ -3,6 +3,7 @@
|
||||
|
||||
@param id {string} [optional] - Unique identifier for the dropdown component.
|
||||
@param trigger {string} [optional] - HTML content for the button that triggers the dropdown.
|
||||
@param menu {array} [optional] - Array of menu items for the dropdown.
|
||||
@param main_attrs {object} [optional] - Additional HTML attributes for the main container div.
|
||||
@param trigger_attrs {object} [optional] - Additional HTML attributes for the trigger button.
|
||||
@param content_attrs {object} [optional] - Additional HTML attributes for the dropdown content div.
|
||||
@@ -10,6 +11,7 @@
|
||||
{% macro dropdown_menu(
|
||||
id=None,
|
||||
trigger=None,
|
||||
menu=None,
|
||||
main_attrs={},
|
||||
trigger_attrs={},
|
||||
content_attrs={}
|
||||
@@ -20,7 +22,7 @@
|
||||
@click.away="open = false"
|
||||
{% if id %}id="{{ id }}"{% endif %}
|
||||
{% for key, value in main_attrs %}
|
||||
{% if key != 'class' %}{{ key }}="{{ value }}"{% endif %}
|
||||
{% if key != "class" %}{{ key }}="{{ value }}"{% endif %}
|
||||
{% endfor %}
|
||||
>
|
||||
{% if trigger %}
|
||||
@@ -50,8 +52,62 @@
|
||||
{% endfor %}
|
||||
>
|
||||
<nav role="menu">
|
||||
{{ caller() if caller }}
|
||||
{% if menu %}
|
||||
{{ render_dropdown_items(menu, id ~ "-items" if id else "items") }}
|
||||
{% else %}
|
||||
{{ caller() if caller }}
|
||||
{% endif %}
|
||||
</nav>
|
||||
</div>
|
||||
</div>
|
||||
{% endmacro %}
|
||||
|
||||
{#
|
||||
Renders dropdown menu items recursively.
|
||||
|
||||
@param items {array} - The array of items to render.
|
||||
@param parent_id_prefix {string} [optional] - Prefix for generating element IDs.
|
||||
#}
|
||||
{% macro render_dropdown_items(items, parent_id_prefix="items") %}
|
||||
{% for item in items %}
|
||||
{% set item_id = parent_id_prefix ~ "-" ~ loop.index %}
|
||||
|
||||
{% if item.type == "group" %}
|
||||
{% set group_label_id = item.id if item.id else "group-label-" + item_id %}
|
||||
<div
|
||||
role="group"
|
||||
aria-labelledby="{{ group_label_id }}"
|
||||
{% for key, value in item.attrs %}
|
||||
{{ key }}="{{ value }}"
|
||||
{% endfor %}
|
||||
>
|
||||
<div role="heading" id="{{ group_label_id }}">{{ item.label }}</div>
|
||||
{{ render_dropdown_items(item.items, item_id) if item.items }}
|
||||
</div>
|
||||
{% elif item.type == "separator" %}
|
||||
<hr role="separator" />
|
||||
{% elif item.type == "item" or not item.type %}
|
||||
{% if item.url %}
|
||||
<a
|
||||
role="menuitem"
|
||||
href="{{ item.url }}"
|
||||
{% for key, value in item.attrs %}
|
||||
{% if key != "url" %} {{ key }}="{{ value }}" {% endif %}
|
||||
{% endfor %}
|
||||
>
|
||||
{{ item.label | safe }}
|
||||
</a>
|
||||
{% else %}
|
||||
<button
|
||||
role="menuitem"
|
||||
type="button"
|
||||
{% for key, value in item.attrs %}
|
||||
{{ key }}="{{ value }}"
|
||||
{% endfor %}
|
||||
>
|
||||
{{ item.label | safe }}
|
||||
</button>
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
{% endmacro %}
|
||||
+44
-1
@@ -16,6 +16,7 @@
|
||||
id=None,
|
||||
selected=None,
|
||||
name=None,
|
||||
items=None,
|
||||
main_attrs={},
|
||||
trigger_attrs={},
|
||||
content_attrs={},
|
||||
@@ -91,7 +92,11 @@
|
||||
{{ key }}="{{ value }}"
|
||||
{% endfor %}
|
||||
>
|
||||
{{ caller() if caller }}
|
||||
{% if items %}
|
||||
{{ render_select_items(items, id ~ "-items" if id else "items") }}
|
||||
{% else %}
|
||||
{{ caller() if caller }}
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
{% if name is defined %}
|
||||
@@ -105,4 +110,42 @@
|
||||
>
|
||||
{% endif %}
|
||||
</div>
|
||||
{% endmacro %}
|
||||
|
||||
{#
|
||||
Renders a list of items for the select component.
|
||||
|
||||
@param items {array} - The array of items to render.
|
||||
@param parent_id_prefix {string} [optional] - The prefix for the item id.
|
||||
#}
|
||||
{% macro render_select_items(items, parent_id_prefix="items") %}
|
||||
{% for item in items %}
|
||||
{% set item_id = parent_id_prefix ~ "-" ~ loop.index %}
|
||||
|
||||
{% if item.type == "group" %}
|
||||
{% set group_label_id = item.id if item.id else "group-label-" + item_id %}
|
||||
<div
|
||||
role="group"
|
||||
aria-labelledby="{{ group_label_id }}"
|
||||
{% for key, value in item.attrs %}
|
||||
{{ key }}="{{ value }}"
|
||||
{% endfor %}
|
||||
>
|
||||
<div role="heading" id="{{ group_label_id }}">{{ item.label }}</div>
|
||||
{{ render_select_items(item.items, item_id) if item.items }}
|
||||
</div>
|
||||
{% elif item.type == "separator" %}
|
||||
<hr role="separator" />
|
||||
{% elif item.type == "item" or not item.type %}
|
||||
<div
|
||||
role="option"
|
||||
data-value="{{ item.value }}"
|
||||
{% for key, value in item.attrs %}
|
||||
{{ key }}="{{ value }}"
|
||||
{% endfor %}
|
||||
>
|
||||
{{ item.label | safe }}
|
||||
</div>
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
{% endmacro %}
|
||||
+79
-55
@@ -1,42 +1,14 @@
|
||||
{% macro render_menu_items(items, level=0, parent_id="") %}
|
||||
<ul>
|
||||
{% for item in items %}
|
||||
{% set item_id = parent_id ~ loop.index %}
|
||||
{% if item.type == 'item' %}
|
||||
<li>
|
||||
<a
|
||||
href="{{ item.url }}"
|
||||
{{ 'aria-current="page"' if item.current }}
|
||||
{% for key, value in item.attrs %}
|
||||
{% if key != 'href' and key != 'aria-current' %}{{ key }}="{{ value }}"{% endif %}
|
||||
{% endfor %}
|
||||
>
|
||||
{% if item.icon %}{{ item.icon | safe }}{% endif %}
|
||||
<span>{{ item.label }}</span>
|
||||
</a>
|
||||
</li>
|
||||
{% elif item.type == 'submenu' %}
|
||||
<li>
|
||||
<details {{ 'open' if item.open }} id="submenu-{{ item_id }}">
|
||||
<summary
|
||||
aria-controls="submenu-{{ item_id }}-content"
|
||||
{% for key, value in item.attrs %}
|
||||
{% if key != 'aria-controls' %}{{ key }}="{{ value }}"{% endif %}
|
||||
{% endfor %}
|
||||
>
|
||||
{% if item.icon %}{{ item.icon | safe }}{% endif %}
|
||||
{{ item.label }}
|
||||
</summary>
|
||||
<div id="submenu-{{ item_id }}-content">
|
||||
{{ render_menu_items(item.items, level + 1, item_id + '-') }}
|
||||
</div>
|
||||
</details>
|
||||
</li>
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
</ul>
|
||||
{% endmacro %}
|
||||
{#
|
||||
Renders a sidebar component.
|
||||
|
||||
@param id {string} [optional] - Unique identifier for the sidebar component.
|
||||
@param label {string} [optional] - Label for the sidebar navigation.
|
||||
@param open {boolean} [optional] - Whether the sidebar is open.
|
||||
@param side {string} [optional] - Side of the sidebar to display.
|
||||
@param header {string} [optional] - Header content for the sidebar.
|
||||
@param footer {string} [optional] - Footer content for the sidebar.
|
||||
@param menu {array} [optional] - Array of menu items for the sidebar.
|
||||
#}
|
||||
{% macro sidebar(
|
||||
id=None,
|
||||
label="Sidebar navigation",
|
||||
@@ -54,13 +26,14 @@
|
||||
<div
|
||||
{% if id %}id="{{ id }}"{% endif %}
|
||||
class="sidebar {{ main_attrs.class }}"
|
||||
aria-hidden="{{ 'false' if open else 'true' }}"
|
||||
data-side="{{ side if side else 'left' }}"
|
||||
{{ 'inert' if not open }}
|
||||
data-uninitialized
|
||||
data-side="{{ side if side else "left" }}"
|
||||
aria-hidden="{{ "true" if not open else "false" }}"
|
||||
{{ "inert" if not open }}
|
||||
{% for key, value in main_attrs %}
|
||||
{% if key != "class" %}{{ key }}="{{ value }}"{% endif %}
|
||||
{% endfor %}
|
||||
x-data="sidebar({{ 'true' if open else 'false' }})"
|
||||
x-data="sidebar({{ "true" if open else "false" }})"
|
||||
x-bind="$main"
|
||||
>
|
||||
<nav
|
||||
@@ -82,19 +55,7 @@
|
||||
{% endfor %}
|
||||
>
|
||||
{% if menu %}
|
||||
{% for group_or_item in menu %}
|
||||
{% set group_id = loop.index %}
|
||||
{% if group_or_item.type == 'group' %}
|
||||
<div role="group" aria-labelledby="nav-group-{{ group_id }}">
|
||||
<h3 id="nav-group-{{ group_id }}">{{ group_or_item.label }}</h3>
|
||||
{{ render_menu_items(group_or_item.items, 0, group_id + '-') }}
|
||||
</div>
|
||||
{% elif group_or_item.type == 'separator' %}
|
||||
<hr role="separator"/>
|
||||
{% elif group_or_item.type == 'item' or group_or_item.type == 'submenu' %}
|
||||
{{ render_menu_items([group_or_item], 0, 'top-' + group_id + '-') }}
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
{{ render_sidebar_content(menu, id ~ "-content" if id else "content") }}
|
||||
{% else %}
|
||||
{{ caller() if caller }}
|
||||
{% endif %}
|
||||
@@ -111,4 +72,67 @@
|
||||
{% endif %}
|
||||
</nav>
|
||||
</div>
|
||||
{% endmacro %}
|
||||
|
||||
{#
|
||||
Renders sidebar content recursively (groups, items, submenus, separators).
|
||||
|
||||
@param items {array} - The array of items to render.
|
||||
@param parent_id_prefix {string} [optional] - Prefix for generating element IDs.
|
||||
#}
|
||||
{% macro render_sidebar_content(items, parent_id_prefix="content") %}
|
||||
{% for item in items %}
|
||||
{% set item_id = parent_id_prefix ~ "-" ~ loop.index %}
|
||||
|
||||
{% if item.type == "group" %}
|
||||
{% set group_label_id = item.id if item.id else "group-label-" + item_id %}
|
||||
<div
|
||||
role="group"
|
||||
{% if item.label %}aria-labelledby="{{ group_label_id }}"{% endif %}
|
||||
{% for key, value in item.attrs %}
|
||||
{{ key }}="{{ value }}"
|
||||
{% endfor %}
|
||||
>
|
||||
{% if item.label %}
|
||||
<h3 id="{{ group_label_id }}">{{ item.label }}</h3>
|
||||
{% endif %}
|
||||
<ul>
|
||||
{{ render_sidebar_content(item.items, item_id) if item.items }}
|
||||
</ul>
|
||||
</div>
|
||||
{% elif item.type == "separator" %}
|
||||
<hr role="separator"/>
|
||||
{% elif item.type == "submenu" %}
|
||||
<li>
|
||||
<details
|
||||
id="submenu-{{ item_id }}"
|
||||
{{ "open" if item.open }}
|
||||
{% for key, value in item.attrs %}
|
||||
{% if key != "open" %}{{ key }}="{{ value }}"{% endif %}
|
||||
{% endfor %}
|
||||
>
|
||||
<summary aria-controls="submenu-{{ item_id }}-content">
|
||||
{% if item.icon %}{{ item.icon | safe }}{% endif %}
|
||||
{{ item.label }}
|
||||
</summary>
|
||||
<ul id="submenu-{{ item_id }}-content">
|
||||
{{ render_sidebar_content(item.items, item_id) if item.items }}
|
||||
</ul>
|
||||
</details>
|
||||
</li>
|
||||
{% elif item.type == "item" or not item.type %}
|
||||
<li>
|
||||
<a
|
||||
href="{{ item.url }}"
|
||||
{{ 'aria-current="page"' if item.current }}
|
||||
{% for key, value in item.attrs %}
|
||||
{% if key != "href" and key != "aria-current" %}{{ key }}="{{ value }}"{% endif %}
|
||||
{% endfor %}
|
||||
>
|
||||
{% if item.icon %}{{ item.icon | safe }}{% endif %}
|
||||
<span>{{ item.label }}</span>
|
||||
</a>
|
||||
</li>
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
{% endmacro %}
|
||||
Reference in New Issue
Block a user