Section, SectionHeader and PublicPageShell move onto wire-* classes with local style blocks, variants as data attributes. SectionHeader loses 38 utility lines, and Section stops spending one line per variant and colour pairing: tinted and solid now select on two attributes. PageHeader was already on the convention and only needed the audit. examples/basic-app/app/pages/layout.wrn composes the whole set into one page. Building it surfaced a library-wide bug. Ten custom properties were referenced by components and defined by nothing: --wire-color-focus, --wire-color-surface-soft, --wire-color-on-danger, --wire-color-surface-subtle and the input-* family, plus hover and contrast for every semantic colour except primary and secondary. An undefined custom property does not warn, it resolves to nothing, so focus rings drew with no colour and every soft surface rendered transparent -- 27 components referenced surface-soft alone. They are derived in the theme now, and a test checks every token a component references against the rendered theme CSS rather than the source, since most are generated. The semantic spread also had to move ahead of the primary and secondary entries so the palette keeps winning for those two. Known and unresolved: LayoutSplitter emits its sizeChange output and the component does fire it, but a parent binding on the tag is not invoked. The tour page therefore points at the handle aria-valuenow rather than wiring a handler that would never update. Outputs work elsewhere, so this is narrower than an outputs-are-broken problem and needs its own investigation. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
147 lines
4.8 KiB
TypeScript
147 lines
4.8 KiB
TypeScript
// AUTO-GENERATED by `wrnexus dev` - do not edit.
|
|
// Typed routes support required, optional, and catch-all parameters.
|
|
|
|
export interface Routes {
|
|
"/": Record<string, never>;
|
|
"/about": Record<string, never>;
|
|
"/async-data": Record<string, never>;
|
|
"/chat": Record<string, never>;
|
|
"/client-only": Record<string, never>;
|
|
"/dashboard": Record<string, never>;
|
|
"/hello": Record<string, never>;
|
|
"/language-tools": Record<string, never>;
|
|
"/layout": Record<string, never>;
|
|
"/login": Record<string, never>;
|
|
"/modal": Record<string, never>;
|
|
"/navigation": Record<string, never>;
|
|
"/partial-static": Record<string, never>;
|
|
"/platform-showcase": Record<string, never>;
|
|
"/reactive": Record<string, never>;
|
|
"/server-actions": Record<string, never>;
|
|
"/table": Record<string, never>;
|
|
"/test": Record<string, never>;
|
|
"/ui": Record<string, never>;
|
|
}
|
|
|
|
export interface RouteNames {
|
|
"index": "/";
|
|
"about": "/about";
|
|
"async.data": "/async-data";
|
|
"chat": "/chat";
|
|
"client.only": "/client-only";
|
|
"dashboard": "/dashboard";
|
|
"hello": "/hello";
|
|
"language.tools": "/language-tools";
|
|
"layout": "/layout";
|
|
"login": "/login";
|
|
"modal": "/modal";
|
|
"navigation": "/navigation";
|
|
"partial.static": "/partial-static";
|
|
"platform.showcase": "/platform-showcase";
|
|
"reactive": "/reactive";
|
|
"server.actions": "/server-actions";
|
|
"table": "/table";
|
|
"test": "/test";
|
|
"ui": "/ui";
|
|
}
|
|
|
|
export interface RouteQueries {
|
|
[path: string]: Record<string, string | number | boolean | null | undefined>;
|
|
}
|
|
|
|
export type RoutePath = keyof Routes;
|
|
export type RouteName = keyof RouteNames;
|
|
export type RouteQuery<P extends RoutePath> = P extends keyof RouteQueries
|
|
? RouteQueries[P]
|
|
: Record<string, string | number | boolean | null | undefined>;
|
|
type RouteValue = string | readonly string[] | undefined;
|
|
|
|
function encodeRouteValue(value: RouteValue, catchAll: boolean): string {
|
|
if (value === undefined) return "";
|
|
const values = Array.isArray(value) ? value : catchAll ? String(value).split("/") : [String(value)];
|
|
return values.map((part) => encodeURIComponent(part)).join("/");
|
|
}
|
|
|
|
function buildHref(path: string, params: Record<string, RouteValue> = {}): string {
|
|
const output: string[] = [];
|
|
for (const segment of path.split("/").filter(Boolean)) {
|
|
let name: string | undefined;
|
|
let optional = false;
|
|
let catchAll = false;
|
|
if (segment.startsWith("[[") && segment.endsWith("]]")) {
|
|
optional = true;
|
|
name = segment.slice(2, -2);
|
|
} else if (segment.startsWith("[") && segment.endsWith("]")) {
|
|
name = segment.slice(1, -1);
|
|
if (name.endsWith("?")) {
|
|
optional = true;
|
|
name = name.slice(0, -1);
|
|
}
|
|
}
|
|
if (!name) {
|
|
output.push(segment);
|
|
continue;
|
|
}
|
|
if (name.startsWith("...")) {
|
|
catchAll = true;
|
|
name = name.slice(3);
|
|
}
|
|
const value = params[name];
|
|
if (value === undefined && optional) continue;
|
|
if (value === undefined) throw new Error(`WRN-ROUTE-MISSING-PARAM: Missing route parameter '${name}'.`);
|
|
output.push(encodeRouteValue(value, catchAll));
|
|
}
|
|
return "/" + output.filter(Boolean).join("/");
|
|
}
|
|
|
|
export function href<P extends RoutePath>(
|
|
path: P,
|
|
...args: keyof Routes[P] extends never
|
|
? []
|
|
: Record<string, never> extends Routes[P]
|
|
? [params?: Routes[P]]
|
|
: [params: Routes[P]]
|
|
): string {
|
|
const params = (args[0] ?? {}) as Record<string, RouteValue>;
|
|
return buildHref(String(path), params);
|
|
}
|
|
|
|
export function route<N extends RouteName>(
|
|
name: N,
|
|
...args: keyof Routes[RouteNames[N]] extends never
|
|
? [params?: Routes[RouteNames[N]], query?: RouteQuery<RouteNames[N]>]
|
|
: Record<string, never> extends Routes[RouteNames[N]]
|
|
? [params?: Routes[RouteNames[N]], query?: RouteQuery<RouteNames[N]>]
|
|
: [params: Routes[RouteNames[N]], query?: RouteQuery<RouteNames[N]>]
|
|
): string {
|
|
const paths: Record<RouteName, RoutePath> = {
|
|
"index": "/",
|
|
"about": "/about",
|
|
"async.data": "/async-data",
|
|
"chat": "/chat",
|
|
"client.only": "/client-only",
|
|
"dashboard": "/dashboard",
|
|
"hello": "/hello",
|
|
"language.tools": "/language-tools",
|
|
"layout": "/layout",
|
|
"login": "/login",
|
|
"modal": "/modal",
|
|
"navigation": "/navigation",
|
|
"partial.static": "/partial-static",
|
|
"platform.showcase": "/platform-showcase",
|
|
"reactive": "/reactive",
|
|
"server.actions": "/server-actions",
|
|
"table": "/table",
|
|
"test": "/test",
|
|
"ui": "/ui"
|
|
} as Record<RouteName, RoutePath>;
|
|
const output = buildHref(paths[name], (args[0] ?? {}) as Record<string, RouteValue>);
|
|
const query = args[1];
|
|
if (!query) return output;
|
|
const search = new URLSearchParams();
|
|
for (const [key, value] of Object.entries(query))
|
|
if (value !== undefined && value !== null) search.set(key, String(value));
|
|
const text = search.toString();
|
|
return text ? `${output}?${text}` : output;
|
|
}
|