Files
WRNexusJS/packages/ui/src/registry.ts
T
ClintchizandClaude Opus 5 949cf78636
Quality / quality (ubuntu-latest) (push) Failing after 13m40s
Quality / quality (windows-latest) (push) Canceled after 0s
feat(ui): add DataTable and Toaster, drop the legacy Table, fix overlay dialogs
DataTable replaces the 20-line Table scaffold entirely: columns, sorting,
filtering, pagination, selection, bulk actions, comparison layout, sticky
first column, custom HTML cells, and a remote source driven by a `request`
output rather than a function prop (props travel as HTML attributes, so a
function arrives as its own source text).

Toaster replaces the hand-rolled status div: tone icons, actions, hover
pause/resume and a progress bar.

Overlays audit -- Modal and Drawer declared aria-modal="true" but nothing
ever moved focus into the panel, so the @keydown handler on their root
never ran and closeOnEscape did nothing. Focus, focus restore, a Tab trap
and a body scroll lock now live in the reactive runtime, shared by both.

ContextMenu placed pointer menus by subtracting a guessed 340x420 from the
viewport, which pushed every menu that was not that size away from the
pointer; it now positions at the pointer and lets the anchored clamp pull
it back once it can be measured.

The reactive runtime size budget moves 150k -> 175k to cover anchored
overlays, dialog behaviour, the toaster and the DataTable client half.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-07 14:59:58 +05:30

94 lines
3.3 KiB
TypeScript

/**
* @wrnexus/ui registry — SERVER-ONLY filesystem helpers for discovering the
* package's `.wrn` component sources and stylesheet on disk.
*
* This is the code that used to live in `index.ts`. It was moved here
* because it depends on `node:fs`/`node:path`/`node:url`, which don't exist
* in a browser build — and `index.ts` is the package's main entry, so any
* client-side (browser) bundle that reaches ANY `@wrnexus/ui` component
* transitively tried to bundle this file too, failing with:
* "Browser polyfill for module 'node:url' doesn't have a matching export
* named 'fileURLToPath'"
*
* Import from `@wrnexus/ui/registry` (server-side code only — CLI,
* dev-server, build). Never import this from a `.wrn` component or anything
* that can end up in a client runtime bundle.
*/
import { readdirSync, readFileSync } from "node:fs";
import { dirname, join } from "node:path";
import { fileURLToPath } from "node:url";
const here = dirname(fileURLToPath(import.meta.url));
const packageRoot = join(here, "..");
let cachedCss: string | undefined;
let cachedComponentFiles: Map<string, string> | undefined;
/** Absolute path to the directory of Wire UI component `.wrn` files. */
export function uiComponentsDir(): string {
return join(packageRoot, "components");
}
/** Absolute path to the Wire UI stylesheet. */
export function uiCssPath(): string {
return join(packageRoot, "ui.css");
}
/** The Wire UI stylesheet contents (all `.wire-*` classes, themed via tokens). */
export function uiCss(): string {
return (cachedCss ??= readFileSync(uiCssPath(), "utf8"));
}
function uiComponentFiles(): Map<string, string> {
if (cachedComponentFiles) return cachedComponentFiles;
const files = new Map<string, string>();
const componentFiles = readdirSync(uiComponentsDir()).filter((entry) => entry.endsWith(".wrn"));
for (const file of componentFiles) {
const path = join(uiComponentsDir(), file);
const source = readFileSync(path, "utf8");
const declaration = /^\s*component\s+([A-Za-z][A-Za-z0-9_]*)\b/m.exec(source);
if (!declaration) {
throw new Error(`UI component source does not declare a component: ${path}`);
}
const name = declaration[1]!;
const previous = files.get(name);
if (previous) {
throw new Error(`Duplicate UI component declaration '${name}' in ${previous} and ${path}`);
}
files.set(name, path);
}
cachedComponentFiles = files;
return files;
}
/** Names declared by the bundled components, independent of filename casing. */
export function uiComponentNames(): string[] {
return [...uiComponentFiles().keys()].sort((left, right) => left.localeCompare(right));
}
/** Absolute path to a bundled component by its declared component name. */
export function uiComponentPath(name: string): string {
const files = uiComponentFiles();
const exact = files.get(name);
if (exact) return exact;
const normalized = name.toLowerCase();
const matches = [...files.entries()].filter(
([componentName]) => componentName.toLowerCase() === normalized,
);
if (matches.length === 1) return matches[0]![1];
throw new Error(`Unknown WRNexus UI component '${name}'.`);
}
export { uiComponentReference, findUiComponent, auditUiComponents } from "./metadata.ts";
export type { UiComponentMetadata, UiComponentReference } from "./metadata.ts";