Ground-up rebuild of the GMW frontend as an Ambient Field console: - WebGL ambient background (Three.js shader, drifting motes, reduced-motion aware) - Glassmorphism dark cyber theme across all 8 routes - SSR page + client view split with SWR fallback; realtime via WebSocket - Command palette (Cmd+K), chatbot FAB, guild/channel pickers - Chart primitives: donut, radial-gauge, area-activity, sparkline, equalizer Cleanup (review pass): - Remove stray Puppeteer nav-test/nav-debug scripts - Replace non-null assertions with guards (dashboard/moderation) - Drop unused useGuilds fetches in messages/voice views - Type implicit-any `let` declarations across pages - Add a11y roles/labels to SVG charts and audio, tidy imports
32 lines
989 B
TypeScript
32 lines
989 B
TypeScript
"use client";
|
|
|
|
import * as React from "react";
|
|
|
|
/**
|
|
* Minimal Slot — merges its props onto its single child element (Radix-style
|
|
* `asChild`). Enough for wrapping <Link>/<a> in a Button.
|
|
*/
|
|
export const Slot = React.forwardRef<
|
|
HTMLElement,
|
|
React.HTMLAttributes<HTMLElement> & { children?: React.ReactNode }
|
|
>(({ children, ...slotProps }, ref) => {
|
|
if (!React.isValidElement(children)) return null;
|
|
const childProps = children.props as Record<string, unknown>;
|
|
const merged: Record<string, unknown> = { ...childProps, ...slotProps, ref };
|
|
// Merge className
|
|
if (slotProps.className || childProps.className) {
|
|
merged.className = [childProps.className, slotProps.className]
|
|
.filter(Boolean)
|
|
.join(" ");
|
|
}
|
|
// Merge style
|
|
if (slotProps.style || childProps.style) {
|
|
merged.style = {
|
|
...(childProps.style as object),
|
|
...(slotProps.style as object),
|
|
};
|
|
}
|
|
return React.cloneElement(children, merged);
|
|
});
|
|
Slot.displayName = "Slot";
|