mirror of
https://github.com/Studio-Saelix/sencho.git
synced 2026-08-11 19:26:56 +00:00
0bf061a745
* feat(settings): group sections, add ⌘K search, scope breadcrumb Restructures the Settings Hub sidebar into four labelled groups (Identity, System, Alerts, Advanced), adds a ⌘K command palette for section search, and surfaces the active scope (global vs node-scoped) in the content breadcrumb. - New `settings/registry.ts` centralises group/item metadata, tier gates, glyph assignments, visibility rules, and keyword hints consumed by both the sidebar and the command palette - Cyan 2px left rail + gradient on the active sidebar item; mono-uppercase group headers; tier chips inline for locked items - Scoped ⌘K handler via onKeyDownCapture on DialogContent so the hub no longer hijacks the global sidebar shortcut while open - ScrollArea gains an opt-in `block` prop so the Nodes management table can overflow horizontally without Radix's default `display: table` wrapper clipping action buttons - Docs reference updated with the grouped sidebar, scope breadcrumb, and ⌘K walkthrough plus refreshed screenshots * refactor(settings): drop duplicate section headers, redesign system limits, always-visible tier chips - Remove redundant section titles in every settings page; the dialog header now owns the title and description - Rework System Limits into a compact row panel with inline-edit chips (warn state, focus ring) and an ON/OFF toggle pill - Show tier chips on sidebar and command palette whether locked or unlocked, so Skipper/Admiral scope is always legible - Keep right-aligned action buttons on pages that had a title+button header (Users, Labels, Nodes, API Tokens, Registries) * fix(settings): seed NumberChip draft on edit instead of via effect ESLint rule react-hooks/set-state-in-effect flagged the sync effect that mirrored the external value into local draft state. Replace it with a startEdit handler that seeds draft from value at click time, so the button path always reads value directly and no cascading render is triggered on prop change. * fix(settings): restore heading role and clean sidebar accessible names - Wrap the settings dialog title in an h2 so screen readers and E2E locators see a heading again after the in-section headers were removed - Mark the sidebar glyph aria-hidden so the button's accessible name is just the item label (fixes anchored name matchers) - Align the MFA E2E helper with the renamed Account section heading
64 lines
2.4 KiB
TypeScript
64 lines
2.4 KiB
TypeScript
import * as React from "react"
|
|
import * as ScrollAreaPrimitive from "@radix-ui/react-scroll-area"
|
|
|
|
import { cn } from "@/lib/utils"
|
|
|
|
const ScrollArea = React.forwardRef<
|
|
React.ElementRef<typeof ScrollAreaPrimitive.Root>,
|
|
React.ComponentPropsWithoutRef<typeof ScrollAreaPrimitive.Root> & {
|
|
viewportRef?: React.Ref<HTMLDivElement>;
|
|
// Opt in when the viewport wraps content that manages its own horizontal
|
|
// overflow (e.g., a child `overflow-x-auto` table). Overrides Radix's
|
|
// default `display: table` wrapper so the child can be constrained to the
|
|
// viewport width and trigger its own scroll. Also renders a horizontal
|
|
// ScrollBar for viewports that overflow directly.
|
|
block?: boolean;
|
|
}
|
|
>(({ className, children, viewportRef, block, ...props }, ref) => (
|
|
<ScrollAreaPrimitive.Root
|
|
ref={ref}
|
|
className={cn("relative overflow-hidden", className)}
|
|
{...props}
|
|
>
|
|
<ScrollAreaPrimitive.Viewport
|
|
ref={viewportRef}
|
|
className={cn(
|
|
"h-full w-full rounded-[inherit]",
|
|
block && "[&>div]:!block [&>div]:!min-w-0"
|
|
)}
|
|
>
|
|
{children}
|
|
</ScrollAreaPrimitive.Viewport>
|
|
<ScrollBar orientation="vertical" />
|
|
{block && <ScrollBar orientation="horizontal" />}
|
|
<ScrollAreaPrimitive.Corner />
|
|
</ScrollAreaPrimitive.Root>
|
|
))
|
|
ScrollArea.displayName = ScrollAreaPrimitive.Root.displayName
|
|
|
|
// Thumb uses a translucent foreground alpha so it harmonizes with glass
|
|
// overlays (dropdowns, popovers, dialogs) instead of competing with content.
|
|
const ScrollBar = React.forwardRef<
|
|
React.ElementRef<typeof ScrollAreaPrimitive.ScrollAreaScrollbar>,
|
|
React.ComponentPropsWithoutRef<typeof ScrollAreaPrimitive.ScrollAreaScrollbar>
|
|
>(({ className, orientation = "vertical", ...props }, ref) => (
|
|
<ScrollAreaPrimitive.ScrollAreaScrollbar
|
|
ref={ref}
|
|
orientation={orientation}
|
|
className={cn(
|
|
"flex touch-none select-none transition-colors bg-transparent",
|
|
orientation === "vertical" &&
|
|
"h-full w-2 border-l border-l-transparent p-[1px]",
|
|
orientation === "horizontal" &&
|
|
"h-2 flex-col border-t border-t-transparent p-[1px]",
|
|
className
|
|
)}
|
|
{...props}
|
|
>
|
|
<ScrollAreaPrimitive.ScrollAreaThumb className="relative flex-1 rounded-full bg-foreground/20 transition-colors hover:bg-foreground/40" />
|
|
</ScrollAreaPrimitive.ScrollAreaScrollbar>
|
|
))
|
|
ScrollBar.displayName = ScrollAreaPrimitive.ScrollAreaScrollbar.displayName
|
|
|
|
export { ScrollArea, ScrollBar }
|