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>
This commit is contained in:
@@ -110,6 +110,7 @@ export interface Routes {
|
||||
"/components/timeline": Record<string, never>;
|
||||
"/components/toast": Record<string, never>;
|
||||
"/components/toast-notifications": Record<string, never>;
|
||||
"/components/toaster": Record<string, never>;
|
||||
"/components/toggle-count": Record<string, never>;
|
||||
"/components/toggle-password": Record<string, never>;
|
||||
"/components/tooltip": Record<string, never>;
|
||||
@@ -134,9 +135,169 @@ export interface Routes {
|
||||
"/tables": Record<string, never>;
|
||||
"/templates": Record<string, never>;
|
||||
"/themes": Record<string, never>;
|
||||
"/v06-functions": Record<string, never>;
|
||||
"/v06-imports": Record<string, never>;
|
||||
"/v06-migration": Record<string, never>;
|
||||
"/v06-outputs": Record<string, never>;
|
||||
"/v06-runtime": Record<string, never>;
|
||||
"/v06-state": Record<string, never>;
|
||||
"/v06-stores": Record<string, never>;
|
||||
"/v06-types": Record<string, never>;
|
||||
}
|
||||
|
||||
export interface RouteNames {
|
||||
"index": "/";
|
||||
"accessibility": "/accessibility";
|
||||
"advanced.forms": "/advanced-forms";
|
||||
"base": "/base";
|
||||
"block.library": "/block-library";
|
||||
"colors": "/colors";
|
||||
"components.accordion": "/components/accordion";
|
||||
"components.advanced.date.picker": "/components/advanced-date-picker";
|
||||
"components.advanced.range.slider": "/components/advanced-range-slider";
|
||||
"components.advanced.select": "/components/advanced-select";
|
||||
"components.alert": "/components/alert";
|
||||
"components.announcement.bar": "/components/announcement-bar";
|
||||
"components.auth.form": "/components/auth-form";
|
||||
"components.auth.split.layout": "/components/auth-split-layout";
|
||||
"components.avatar": "/components/avatar";
|
||||
"components.avatar.group": "/components/avatar-group";
|
||||
"components.back.to.top": "/components/back-to-top";
|
||||
"components.badge": "/components/badge";
|
||||
"components.blockquote": "/components/blockquote";
|
||||
"components.breadcrumb": "/components/breadcrumb";
|
||||
"components.button": "/components/button";
|
||||
"components.button.group": "/components/button-group";
|
||||
"components.card": "/components/card";
|
||||
"components.carousel": "/components/carousel";
|
||||
"components.chart": "/components/chart";
|
||||
"components.chat.bubble": "/components/chat-bubble";
|
||||
"components.checkbox": "/components/checkbox";
|
||||
"components.clipboard": "/components/clipboard";
|
||||
"components.collapse": "/components/collapse";
|
||||
"components.color.picker": "/components/color-picker";
|
||||
"components.columns": "/components/columns";
|
||||
"components.combo.box": "/components/combo-box";
|
||||
"components.confetti": "/components/confetti";
|
||||
"components.container": "/components/container";
|
||||
"components.context.menu": "/components/context-menu";
|
||||
"components.copy.markup": "/components/copy-markup";
|
||||
"components.ctasection": "/components/ctasection";
|
||||
"components.custom.scrollbar": "/components/custom-scrollbar";
|
||||
"components.data.map": "/components/data-map";
|
||||
"components.data.table": "/components/data-table";
|
||||
"components.date.picker": "/components/date-picker";
|
||||
"components.device.frame": "/components/device-frame";
|
||||
"components.divider": "/components/divider";
|
||||
"components.drag.and.drop": "/components/drag-and-drop";
|
||||
"components.drawer": "/components/drawer";
|
||||
"components.dropdown": "/components/dropdown";
|
||||
"components.feature.card": "/components/feature-card";
|
||||
"components.feature.grid": "/components/feature-grid";
|
||||
"components.feature.icon.card": "/components/feature-icon-card";
|
||||
"components.file.input": "/components/file-input";
|
||||
"components.file.upload": "/components/file-upload";
|
||||
"components.file.upload.progress": "/components/file-upload-progress";
|
||||
"components.footer": "/components/footer";
|
||||
"components.grid": "/components/grid";
|
||||
"components.hero": "/components/hero";
|
||||
"components.hero.actions": "/components/hero-actions";
|
||||
"components.image": "/components/image";
|
||||
"components.input": "/components/input";
|
||||
"components.input.group": "/components/input-group";
|
||||
"components.input.number": "/components/input-number";
|
||||
"components.kbd": "/components/kbd";
|
||||
"components.layout.splitter": "/components/layout-splitter";
|
||||
"components.legend.indicator": "/components/legend-indicator";
|
||||
"components.link": "/components/link";
|
||||
"components.list": "/components/list";
|
||||
"components.list.group": "/components/list-group";
|
||||
"components.map": "/components/map";
|
||||
"components.marketing.section.header": "/components/marketing-section-header";
|
||||
"components.marquee": "/components/marquee";
|
||||
"components.mega.menu": "/components/mega-menu";
|
||||
"components.metric.card": "/components/metric-card";
|
||||
"components.metric.grid": "/components/metric-grid";
|
||||
"components.modal": "/components/modal";
|
||||
"components.nav": "/components/nav";
|
||||
"components.navbar": "/components/navbar";
|
||||
"components.page.header": "/components/page-header";
|
||||
"components.pagination": "/components/pagination";
|
||||
"components.pin.input": "/components/pin-input";
|
||||
"components.popover": "/components/popover";
|
||||
"components.portal.dashboard": "/components/portal-dashboard";
|
||||
"components.preference.switcher": "/components/preference-switcher";
|
||||
"components.progress": "/components/progress";
|
||||
"components.public.page.shell": "/components/public-page-shell";
|
||||
"components.radio": "/components/radio";
|
||||
"components.range.slider": "/components/range-slider";
|
||||
"components.rating": "/components/rating";
|
||||
"components.scrollspy": "/components/scrollspy";
|
||||
"components.search.box": "/components/search-box";
|
||||
"components.section": "/components/section";
|
||||
"components.section.header": "/components/section-header";
|
||||
"components.select": "/components/select";
|
||||
"components.sidebar": "/components/sidebar";
|
||||
"components.skeleton": "/components/skeleton";
|
||||
"components.spinner": "/components/spinner";
|
||||
"components.split.hero": "/components/split-hero";
|
||||
"components.stats.bar": "/components/stats-bar";
|
||||
"components.stepper": "/components/stepper";
|
||||
"components.strong.password": "/components/strong-password";
|
||||
"components.styled.icon": "/components/styled-icon";
|
||||
"components.switch": "/components/switch";
|
||||
"components.table": "/components/table";
|
||||
"components.tabs": "/components/tabs";
|
||||
"components.text.link": "/components/text-link";
|
||||
"components.textarea": "/components/textarea";
|
||||
"components.time.picker": "/components/time-picker";
|
||||
"components.timeline": "/components/timeline";
|
||||
"components.toast": "/components/toast";
|
||||
"components.toast.notifications": "/components/toast-notifications";
|
||||
"components.toaster": "/components/toaster";
|
||||
"components.toggle.count": "/components/toggle-count";
|
||||
"components.toggle.password": "/components/toggle-password";
|
||||
"components.tooltip": "/components/tooltip";
|
||||
"components.tree.view": "/components/tree-view";
|
||||
"components.typography": "/components/typography";
|
||||
"components.wysiwyg.editor": "/components/wysiwyg-editor";
|
||||
"core": "/core";
|
||||
"dark.mode": "/dark-mode";
|
||||
"data": "/data";
|
||||
"docs": "/docs";
|
||||
"fonts": "/fonts";
|
||||
"forms": "/forms";
|
||||
"framework.guides": "/framework-guides";
|
||||
"installation": "/installation";
|
||||
"integrations": "/integrations";
|
||||
"layout": "/layout";
|
||||
"marketing": "/marketing";
|
||||
"navigation": "/navigation";
|
||||
"overlays": "/overlays";
|
||||
"resources": "/resources";
|
||||
"sizing": "/sizing";
|
||||
"tables": "/tables";
|
||||
"templates": "/templates";
|
||||
"themes": "/themes";
|
||||
"v06.functions": "/v06-functions";
|
||||
"v06.imports": "/v06-imports";
|
||||
"v06.migration": "/v06-migration";
|
||||
"v06.outputs": "/v06-outputs";
|
||||
"v06.runtime": "/v06-runtime";
|
||||
"v06.state": "/v06-state";
|
||||
"v06.stores": "/v06-stores";
|
||||
"v06.types": "/v06-types";
|
||||
}
|
||||
|
||||
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 {
|
||||
@@ -145,17 +306,9 @@ function encodeRouteValue(value: RouteValue, catchAll: boolean): string {
|
||||
return values.map((part) => encodeURIComponent(part)).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>;
|
||||
function buildHref(path: string, params: Record<string, RouteValue> = {}): string {
|
||||
const output: string[] = [];
|
||||
for (const segment of String(path).split("/").filter(Boolean)) {
|
||||
for (const segment of path.split("/").filter(Boolean)) {
|
||||
let name: string | undefined;
|
||||
let optional = false;
|
||||
let catchAll = false;
|
||||
@@ -184,3 +337,176 @@ export function href<P extends RoutePath>(
|
||||
}
|
||||
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": "/",
|
||||
"accessibility": "/accessibility",
|
||||
"advanced.forms": "/advanced-forms",
|
||||
"base": "/base",
|
||||
"block.library": "/block-library",
|
||||
"colors": "/colors",
|
||||
"components.accordion": "/components/accordion",
|
||||
"components.advanced.date.picker": "/components/advanced-date-picker",
|
||||
"components.advanced.range.slider": "/components/advanced-range-slider",
|
||||
"components.advanced.select": "/components/advanced-select",
|
||||
"components.alert": "/components/alert",
|
||||
"components.announcement.bar": "/components/announcement-bar",
|
||||
"components.auth.form": "/components/auth-form",
|
||||
"components.auth.split.layout": "/components/auth-split-layout",
|
||||
"components.avatar": "/components/avatar",
|
||||
"components.avatar.group": "/components/avatar-group",
|
||||
"components.back.to.top": "/components/back-to-top",
|
||||
"components.badge": "/components/badge",
|
||||
"components.blockquote": "/components/blockquote",
|
||||
"components.breadcrumb": "/components/breadcrumb",
|
||||
"components.button": "/components/button",
|
||||
"components.button.group": "/components/button-group",
|
||||
"components.card": "/components/card",
|
||||
"components.carousel": "/components/carousel",
|
||||
"components.chart": "/components/chart",
|
||||
"components.chat.bubble": "/components/chat-bubble",
|
||||
"components.checkbox": "/components/checkbox",
|
||||
"components.clipboard": "/components/clipboard",
|
||||
"components.collapse": "/components/collapse",
|
||||
"components.color.picker": "/components/color-picker",
|
||||
"components.columns": "/components/columns",
|
||||
"components.combo.box": "/components/combo-box",
|
||||
"components.confetti": "/components/confetti",
|
||||
"components.container": "/components/container",
|
||||
"components.context.menu": "/components/context-menu",
|
||||
"components.copy.markup": "/components/copy-markup",
|
||||
"components.ctasection": "/components/ctasection",
|
||||
"components.custom.scrollbar": "/components/custom-scrollbar",
|
||||
"components.data.map": "/components/data-map",
|
||||
"components.data.table": "/components/data-table",
|
||||
"components.date.picker": "/components/date-picker",
|
||||
"components.device.frame": "/components/device-frame",
|
||||
"components.divider": "/components/divider",
|
||||
"components.drag.and.drop": "/components/drag-and-drop",
|
||||
"components.drawer": "/components/drawer",
|
||||
"components.dropdown": "/components/dropdown",
|
||||
"components.feature.card": "/components/feature-card",
|
||||
"components.feature.grid": "/components/feature-grid",
|
||||
"components.feature.icon.card": "/components/feature-icon-card",
|
||||
"components.file.input": "/components/file-input",
|
||||
"components.file.upload": "/components/file-upload",
|
||||
"components.file.upload.progress": "/components/file-upload-progress",
|
||||
"components.footer": "/components/footer",
|
||||
"components.grid": "/components/grid",
|
||||
"components.hero": "/components/hero",
|
||||
"components.hero.actions": "/components/hero-actions",
|
||||
"components.image": "/components/image",
|
||||
"components.input": "/components/input",
|
||||
"components.input.group": "/components/input-group",
|
||||
"components.input.number": "/components/input-number",
|
||||
"components.kbd": "/components/kbd",
|
||||
"components.layout.splitter": "/components/layout-splitter",
|
||||
"components.legend.indicator": "/components/legend-indicator",
|
||||
"components.link": "/components/link",
|
||||
"components.list": "/components/list",
|
||||
"components.list.group": "/components/list-group",
|
||||
"components.map": "/components/map",
|
||||
"components.marketing.section.header": "/components/marketing-section-header",
|
||||
"components.marquee": "/components/marquee",
|
||||
"components.mega.menu": "/components/mega-menu",
|
||||
"components.metric.card": "/components/metric-card",
|
||||
"components.metric.grid": "/components/metric-grid",
|
||||
"components.modal": "/components/modal",
|
||||
"components.nav": "/components/nav",
|
||||
"components.navbar": "/components/navbar",
|
||||
"components.page.header": "/components/page-header",
|
||||
"components.pagination": "/components/pagination",
|
||||
"components.pin.input": "/components/pin-input",
|
||||
"components.popover": "/components/popover",
|
||||
"components.portal.dashboard": "/components/portal-dashboard",
|
||||
"components.preference.switcher": "/components/preference-switcher",
|
||||
"components.progress": "/components/progress",
|
||||
"components.public.page.shell": "/components/public-page-shell",
|
||||
"components.radio": "/components/radio",
|
||||
"components.range.slider": "/components/range-slider",
|
||||
"components.rating": "/components/rating",
|
||||
"components.scrollspy": "/components/scrollspy",
|
||||
"components.search.box": "/components/search-box",
|
||||
"components.section": "/components/section",
|
||||
"components.section.header": "/components/section-header",
|
||||
"components.select": "/components/select",
|
||||
"components.sidebar": "/components/sidebar",
|
||||
"components.skeleton": "/components/skeleton",
|
||||
"components.spinner": "/components/spinner",
|
||||
"components.split.hero": "/components/split-hero",
|
||||
"components.stats.bar": "/components/stats-bar",
|
||||
"components.stepper": "/components/stepper",
|
||||
"components.strong.password": "/components/strong-password",
|
||||
"components.styled.icon": "/components/styled-icon",
|
||||
"components.switch": "/components/switch",
|
||||
"components.table": "/components/table",
|
||||
"components.tabs": "/components/tabs",
|
||||
"components.text.link": "/components/text-link",
|
||||
"components.textarea": "/components/textarea",
|
||||
"components.time.picker": "/components/time-picker",
|
||||
"components.timeline": "/components/timeline",
|
||||
"components.toast": "/components/toast",
|
||||
"components.toast.notifications": "/components/toast-notifications",
|
||||
"components.toaster": "/components/toaster",
|
||||
"components.toggle.count": "/components/toggle-count",
|
||||
"components.toggle.password": "/components/toggle-password",
|
||||
"components.tooltip": "/components/tooltip",
|
||||
"components.tree.view": "/components/tree-view",
|
||||
"components.typography": "/components/typography",
|
||||
"components.wysiwyg.editor": "/components/wysiwyg-editor",
|
||||
"core": "/core",
|
||||
"dark.mode": "/dark-mode",
|
||||
"data": "/data",
|
||||
"docs": "/docs",
|
||||
"fonts": "/fonts",
|
||||
"forms": "/forms",
|
||||
"framework.guides": "/framework-guides",
|
||||
"installation": "/installation",
|
||||
"integrations": "/integrations",
|
||||
"layout": "/layout",
|
||||
"marketing": "/marketing",
|
||||
"navigation": "/navigation",
|
||||
"overlays": "/overlays",
|
||||
"resources": "/resources",
|
||||
"sizing": "/sizing",
|
||||
"tables": "/tables",
|
||||
"templates": "/templates",
|
||||
"themes": "/themes",
|
||||
"v06.functions": "/v06-functions",
|
||||
"v06.imports": "/v06-imports",
|
||||
"v06.migration": "/v06-migration",
|
||||
"v06.outputs": "/v06-outputs",
|
||||
"v06.runtime": "/v06-runtime",
|
||||
"v06.state": "/v06-state",
|
||||
"v06.stores": "/v06-stores",
|
||||
"v06.types": "/v06-types"
|
||||
} 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;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user