@exxatdesignux/ui 0.5.1 → 0.5.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +16 -0
- package/consumer-extras/cursor-rules/exxat-data-tables.mdc +8 -6
- package/consumer-extras/cursor-rules/exxat-ds-agents.mdc +2 -1
- package/consumer-extras/cursor-rules/exxat-hub-supported-views.mdc +54 -0
- package/consumer-extras/cursor-rules/exxat-nav-single-active.mdc +31 -0
- package/consumer-extras/cursor-skills/exxat-ds-skill/SKILL.md +8 -3
- package/consumer-extras/cursor-skills/exxat-ds-skill/references/data-table-pattern.md +15 -5
- package/consumer-extras/cursor-skills/exxat-token-economy/SKILL.md +11 -4
- package/consumer-extras/handbook/HANDBOOK.md +1 -1
- package/consumer-extras/handbook/reference-implementations.md +2 -2
- package/consumer-extras/patterns/data-views-pattern.md +6 -0
- package/consumer-extras/patterns/hub-supported-views-pattern.md +53 -0
- package/dist/components/data-table/filter-text-value-input.js +1 -1
- package/dist/components/data-table/filter-text-value-input.js.map +1 -1
- package/dist/components/data-table/index.js +16 -12
- package/dist/components/data-table/index.js.map +1 -1
- package/dist/components/data-table/pagination.js +16 -12
- package/dist/components/data-table/pagination.js.map +1 -1
- package/dist/components/data-views/data-row-list.js +1 -1
- package/dist/components/data-views/data-row-list.js.map +1 -1
- package/dist/components/data-views/hub-table.d.ts +8 -4
- package/dist/components/data-views/hub-table.js +31 -16
- package/dist/components/data-views/hub-table.js.map +1 -1
- package/dist/components/data-views/index.d.ts +1 -1
- package/dist/components/data-views/index.js +31 -16
- package/dist/components/data-views/index.js.map +1 -1
- package/dist/components/data-views/list-page-connected-view-body.d.ts +1 -1
- package/dist/components/data-views/list-page-connected-view-body.js +1 -0
- package/dist/components/data-views/list-page-connected-view-body.js.map +1 -1
- package/dist/components/table-properties/column-row.js +1 -1
- package/dist/components/table-properties/column-row.js.map +1 -1
- package/dist/components/table-properties/drawer-button.js +6 -5
- package/dist/components/table-properties/drawer-button.js.map +1 -1
- package/dist/components/table-properties/drawer.js +6 -5
- package/dist/components/table-properties/drawer.js.map +1 -1
- package/dist/components/table-properties/filter-card.js +2 -2
- package/dist/components/table-properties/filter-card.js.map +1 -1
- package/dist/components/table-properties/index.d.ts +1 -1
- package/dist/components/table-properties/index.js +6 -5
- package/dist/components/table-properties/index.js.map +1 -1
- package/dist/components/table-properties/sort-card.js +1 -1
- package/dist/components/table-properties/sort-card.js.map +1 -1
- package/dist/components/templates/index.d.ts +1 -1
- package/dist/components/templates/index.js +16 -6
- package/dist/components/templates/index.js.map +1 -1
- package/dist/components/templates/list-page.d.ts +4 -2
- package/dist/components/templates/list-page.js +16 -6
- package/dist/components/templates/list-page.js.map +1 -1
- package/dist/components/ui/banner.d.ts +2 -2
- package/dist/components/ui/banner.js +1 -1
- package/dist/components/ui/banner.js.map +1 -1
- package/dist/components/ui/coach-mark.js +1 -1
- package/dist/components/ui/coach-mark.js.map +1 -1
- package/dist/components/ui/context-menu.js +1 -1
- package/dist/components/ui/context-menu.js.map +1 -1
- package/dist/components/ui/date-picker-field.js +1 -1
- package/dist/components/ui/date-picker-field.js.map +1 -1
- package/dist/components/ui/dropdown-menu.js +2 -2
- package/dist/components/ui/dropdown-menu.js.map +1 -1
- package/dist/components/ui/export-drawer.js +3 -3
- package/dist/components/ui/export-drawer.js.map +1 -1
- package/dist/components/ui/hover-card.js +1 -1
- package/dist/components/ui/hover-card.js.map +1 -1
- package/dist/components/ui/key-metrics.js +6 -6
- package/dist/components/ui/key-metrics.js.map +1 -1
- package/dist/components/ui/page-header.js +1 -1
- package/dist/components/ui/page-header.js.map +1 -1
- package/dist/components/ui/popover.js +1 -1
- package/dist/components/ui/popover.js.map +1 -1
- package/dist/components/ui/select.js +1 -1
- package/dist/components/ui/select.js.map +1 -1
- package/dist/components/ui/sheet.js +1 -1
- package/dist/components/ui/sheet.js.map +1 -1
- package/dist/components/ui/sidebar.d.ts +1 -1
- package/dist/components/ui/sidebar.js +3 -3
- package/dist/components/ui/sidebar.js.map +1 -1
- package/dist/components/ui/tip.js +1 -1
- package/dist/components/ui/tip.js.map +1 -1
- package/dist/components/ui/tooltip.js +1 -1
- package/dist/components/ui/tooltip.js.map +1 -1
- package/dist/components/ui/view-segmented-control.js +1 -1
- package/dist/components/ui/view-segmented-control.js.map +1 -1
- package/dist/{data-list-view-registry-CyBoBML4.d.ts → data-list-view-registry-BstmlfQ3.d.ts} +16 -1
- package/dist/index.d.ts +2 -1
- package/dist/index.js +151 -29
- package/dist/index.js.map +1 -1
- package/dist/lib/data-list-view-registry.d.ts +1 -1
- package/dist/lib/data-list-view-registry.js +17 -1
- package/dist/lib/data-list-view-registry.js.map +1 -1
- package/dist/lib/data-list-view-surface.d.ts +1 -1
- package/dist/lib/data-list-view-surface.js +1 -0
- package/dist/lib/data-list-view-surface.js.map +1 -1
- package/dist/lib/list-page-table-properties.d.ts +1 -1
- package/dist/lib/list-page-table-properties.js +1 -0
- package/dist/lib/list-page-table-properties.js.map +1 -1
- package/dist/lib/nav-active.d.ts +38 -0
- package/dist/lib/nav-active.js +104 -0
- package/dist/lib/nav-active.js.map +1 -0
- package/package.json +1 -1
- package/src/components/data-table/index.tsx +25 -17
- package/src/components/data-views/data-row-list.tsx +1 -1
- package/src/components/data-views/hub-table.tsx +9 -3
- package/src/components/templates/list-page.tsx +9 -3
- package/src/components/ui/banner.tsx +0 -2
- package/src/components/ui/coach-mark.tsx +1 -2
- package/src/components/ui/context-menu.tsx +1 -1
- package/src/components/ui/dropdown-menu.tsx +2 -2
- package/src/components/ui/hover-card.tsx +1 -1
- package/src/components/ui/key-metrics.tsx +4 -4
- package/src/components/ui/popover.tsx +1 -1
- package/src/components/ui/select.tsx +1 -1
- package/src/components/ui/sheet.tsx +1 -1
- package/src/components/ui/sidebar.tsx +3 -3
- package/src/components/ui/tooltip.tsx +1 -1
- package/src/index.ts +1 -0
- package/src/lib/data-list-view-registry.ts +31 -0
- package/src/lib/nav-active.ts +162 -0
- package/template/.claude/skills/exxat-ds-skill/SKILL.md +2 -1
- package/template/AGENTS.md +16 -1
- package/template/components/columns-client.tsx +3 -2
- package/template/components/columns-showcase.tsx +22 -18
- package/template/components/exxat-product-logo.tsx +1 -1
- package/template/components/library-table.tsx +62 -23
- package/template/components/new-library-item-form.tsx +0 -7
- package/template/components/product-wordmark.tsx +1 -1
- package/template/components/sidebar/app-sidebar.tsx +14 -106
- package/template/components/sidebar/secondary-nav.tsx +22 -4
- package/template/components/tokens-hub-auxiliary-views.tsx +301 -0
- package/template/components/tokens-themes-client.tsx +44 -16
- package/template/docs/HANDBOOK.md +1 -1
- package/template/docs/data-views-pattern.md +6 -0
- package/template/docs/glossary.md +2 -1
- package/template/docs/hub-supported-views-pattern.md +53 -0
- package/template/docs/reference-implementations.md +2 -2
- package/template/lib/full-hub-supported-views.ts +8 -0
- package/template/lib/library-supported-views.ts +5 -12
- package/template/package.json +11 -0
- package/tokens/hooks-index.json +2 -2
|
@@ -56,8 +56,6 @@ const VARIANT_CONFIG = {
|
|
|
56
56
|
},
|
|
57
57
|
}
|
|
58
58
|
|
|
59
|
-
type BannerVariant = keyof typeof VARIANT_CONFIG
|
|
60
|
-
|
|
61
59
|
// ─────────────────────────────────────────────────────────────────────────────
|
|
62
60
|
// SystemBanner — inline at the top of the main content area
|
|
63
61
|
// ─────────────────────────────────────────────────────────────────────────────
|
|
@@ -25,7 +25,6 @@ import * as React from "react"
|
|
|
25
25
|
import { createPortal } from "react-dom"
|
|
26
26
|
import { Popover as PopoverPrimitive } from "radix-ui"
|
|
27
27
|
import { cva, type VariantProps } from "class-variance-authority"
|
|
28
|
-
import { Button } from "./button"
|
|
29
28
|
import { cn } from "../../lib/utils"
|
|
30
29
|
import type { CoachMarkState } from "../../hooks/use-coach-mark"
|
|
31
30
|
|
|
@@ -258,7 +257,7 @@ export function CoachMark({
|
|
|
258
257
|
"data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0",
|
|
259
258
|
"data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95",
|
|
260
259
|
"data-[side=bottom]:slide-in-from-top-2 data-[side=top]:slide-in-from-bottom-2",
|
|
261
|
-
"data-[side=left]:slide-in-from-
|
|
260
|
+
"data-[side=left]:slide-in-from-end-2 data-[side=right]:slide-in-from-start-2",
|
|
262
261
|
className
|
|
263
262
|
)}
|
|
264
263
|
>
|
|
@@ -126,7 +126,7 @@ function ContextMenuContent({
|
|
|
126
126
|
<ContextMenuPrimitive.Content
|
|
127
127
|
data-slot="context-menu-content"
|
|
128
128
|
className={cn(
|
|
129
|
-
"z-50 max-h-(--radix-context-menu-content-available-height) origin-(--radix-context-menu-content-transform-origin) overflow-x-hidden overflow-y-auto rounded-lg bg-popover p-1 text-popover-foreground shadow-md ring-1 ring-foreground/10 duration-100 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-
|
|
129
|
+
"z-50 max-h-(--radix-context-menu-content-available-height) origin-(--radix-context-menu-content-transform-origin) overflow-x-hidden overflow-y-auto rounded-lg bg-popover p-1 text-popover-foreground shadow-md ring-1 ring-foreground/10 duration-100 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-end-2 data-[side=right]:slide-in-from-start-2 data-[side=top]:slide-in-from-bottom-2 data-open:animate-in data-open:fade-in-0 data-open:zoom-in-95 data-closed:animate-out data-closed:fade-out-0 data-closed:zoom-out-95",
|
|
130
130
|
DROPDOWN_MENU_CONTENT_SURFACE_CLASS,
|
|
131
131
|
className,
|
|
132
132
|
)}
|
|
@@ -47,7 +47,7 @@ function DropdownMenuContent({
|
|
|
47
47
|
sideOffset={sideOffset}
|
|
48
48
|
align={align}
|
|
49
49
|
className={cn(
|
|
50
|
-
"z-50 max-h-(--radix-dropdown-menu-content-available-height) origin-(--radix-dropdown-menu-content-transform-origin) overflow-x-hidden overflow-y-auto rounded-lg bg-popover p-1 text-popover-foreground shadow-md ring-1 ring-foreground/10 duration-100 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-
|
|
50
|
+
"z-50 max-h-(--radix-dropdown-menu-content-available-height) origin-(--radix-dropdown-menu-content-transform-origin) overflow-x-hidden overflow-y-auto rounded-lg bg-popover p-1 text-popover-foreground shadow-md ring-1 ring-foreground/10 duration-100 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-end-2 data-[side=right]:slide-in-from-start-2 data-[side=top]:slide-in-from-bottom-2 data-[state=closed]:overflow-hidden data-open:animate-in data-open:fade-in-0 data-open:zoom-in-95 data-closed:animate-out data-closed:fade-out-0 data-closed:zoom-out-95",
|
|
51
51
|
DROPDOWN_MENU_CONTENT_SURFACE_CLASS,
|
|
52
52
|
className,
|
|
53
53
|
)}
|
|
@@ -403,7 +403,7 @@ function DropdownMenuSubContent({
|
|
|
403
403
|
<DropdownMenuPrimitive.SubContent
|
|
404
404
|
data-slot="dropdown-menu-sub-content"
|
|
405
405
|
className={cn(
|
|
406
|
-
"z-50 origin-(--radix-dropdown-menu-content-transform-origin) overflow-hidden rounded-lg bg-popover p-1 text-popover-foreground shadow-lg ring-1 ring-foreground/10 duration-100 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-
|
|
406
|
+
"z-50 origin-(--radix-dropdown-menu-content-transform-origin) overflow-hidden rounded-lg bg-popover p-1 text-popover-foreground shadow-lg ring-1 ring-foreground/10 duration-100 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-end-2 data-[side=right]:slide-in-from-start-2 data-[side=top]:slide-in-from-bottom-2 data-open:animate-in data-open:fade-in-0 data-open:zoom-in-95 data-closed:animate-out data-closed:fade-out-0 data-closed:zoom-out-95",
|
|
407
407
|
DROPDOWN_MENU_CONTENT_SURFACE_CLASS,
|
|
408
408
|
className,
|
|
409
409
|
)}
|
|
@@ -54,7 +54,7 @@ function HoverCardContent({
|
|
|
54
54
|
align={align}
|
|
55
55
|
sideOffset={sideOffset}
|
|
56
56
|
className={cn(
|
|
57
|
-
"z-50 w-64 origin-(--radix-hover-card-content-transform-origin) rounded-lg bg-popover p-4 text-sm text-popover-foreground shadow-md ring-1 ring-foreground/10 outline-none data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-
|
|
57
|
+
"z-50 w-64 origin-(--radix-hover-card-content-transform-origin) rounded-lg bg-popover p-4 text-sm text-popover-foreground shadow-md ring-1 ring-foreground/10 outline-none data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-end-2 data-[side=right]:slide-in-from-start-2 data-[side=top]:slide-in-from-bottom-2 data-open:animate-in data-open:fade-in-0 data-open:zoom-in-95 data-closed:animate-out data-closed:fade-out-0 data-closed:zoom-out-95",
|
|
58
58
|
className,
|
|
59
59
|
)}
|
|
60
60
|
{...props}
|
|
@@ -261,7 +261,7 @@ function flatMetricsHairlineClass(
|
|
|
261
261
|
const childBorder = "[&>*]:border-[color:var(--key-metrics-flat-divider)]"
|
|
262
262
|
|
|
263
263
|
if (itemCount === 2) {
|
|
264
|
-
return cn("gap-0", childBorder, "[&>*:first-child]:border-
|
|
264
|
+
return cn("gap-0", childBorder, "[&>*:first-child]:border-e")
|
|
265
265
|
}
|
|
266
266
|
|
|
267
267
|
if (itemCount === 4) {
|
|
@@ -272,15 +272,15 @@ function flatMetricsHairlineClass(
|
|
|
272
272
|
"gap-0",
|
|
273
273
|
childBorder,
|
|
274
274
|
/* Wide strip (matches `@[30rem]:grid-cols-4`) — verticals between all tiles, no horizontal */
|
|
275
|
-
"[&>*:not(:last-child)]:border-
|
|
275
|
+
"[&>*:not(:last-child)]:border-e",
|
|
276
276
|
/* Narrow strip (`@[18rem]`–`@[30rem]` 2×2) */
|
|
277
277
|
`${narrow2x2}:[&>*:not(:last-child)]:border-e-0`,
|
|
278
|
-
`${narrow2x2}:[&>*:nth-child(odd)]:border-
|
|
278
|
+
`${narrow2x2}:[&>*:nth-child(odd)]:border-e`,
|
|
279
279
|
`${narrow2x2}:[&>*:not(:nth-last-child(-n+2))]:border-b`,
|
|
280
280
|
)
|
|
281
281
|
}
|
|
282
282
|
|
|
283
|
-
return cn("gap-0", childBorder, "[&>*:not(:last-child)]:border-
|
|
283
|
+
return cn("gap-0", childBorder, "[&>*:not(:last-child)]:border-e")
|
|
284
284
|
}
|
|
285
285
|
|
|
286
286
|
function metricsRowColumnsClass(rowLength: number, metricsHalfWidthLayout: boolean): string {
|
|
@@ -34,7 +34,7 @@ function PopoverContent({
|
|
|
34
34
|
"data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0",
|
|
35
35
|
"data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95",
|
|
36
36
|
"data-[side=bottom]:slide-in-from-top-2 data-[side=top]:slide-in-from-bottom-2",
|
|
37
|
-
"data-[side=left]:slide-in-from-
|
|
37
|
+
"data-[side=left]:slide-in-from-end-2 data-[side=right]:slide-in-from-start-2",
|
|
38
38
|
className
|
|
39
39
|
)}
|
|
40
40
|
{...props}
|
|
@@ -68,7 +68,7 @@ function SelectContent({
|
|
|
68
68
|
<SelectPrimitive.Content
|
|
69
69
|
data-slot="select-content"
|
|
70
70
|
data-align-trigger={position === "item-aligned"}
|
|
71
|
-
className={cn("relative z-50 max-h-(--radix-select-content-available-height) min-w-36 origin-(--radix-select-content-transform-origin) overflow-x-hidden overflow-y-auto rounded-lg bg-popover text-popover-foreground shadow-md ring-1 ring-foreground/10 duration-100 data-[align-trigger=true]:animate-none data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-
|
|
71
|
+
className={cn("relative z-50 max-h-(--radix-select-content-available-height) min-w-36 origin-(--radix-select-content-transform-origin) overflow-x-hidden overflow-y-auto rounded-lg bg-popover text-popover-foreground shadow-md ring-1 ring-foreground/10 duration-100 data-[align-trigger=true]:animate-none data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-end-2 data-[side=right]:slide-in-from-start-2 data-[side=top]:slide-in-from-bottom-2 data-open:animate-in data-open:fade-in-0 data-open:zoom-in-95 data-closed:animate-out data-closed:fade-out-0 data-closed:zoom-out-95", position ==="popper"&&"data-[side=bottom]:translate-y-1 data-[side=left]:-translate-x-1 rtl:data-[side=left]:translate-x-1 data-[side=right]:translate-x-1 rtl:data-[side=right]:-translate-x-1 data-[side=top]:-translate-y-1", className )}
|
|
72
72
|
position={position}
|
|
73
73
|
align={align}
|
|
74
74
|
{...props}
|
|
@@ -63,7 +63,7 @@ function SheetContent({
|
|
|
63
63
|
data-slot="sheet-content"
|
|
64
64
|
data-side={side}
|
|
65
65
|
className={cn(
|
|
66
|
-
"fixed z-50 flex flex-col gap-4 bg-background bg-clip-padding text-sm shadow-lg outline-none duration-300 ease-out data-[side=bottom]:inset-x-0 data-[side=bottom]:bottom-0 data-[side=bottom]:h-auto data-[side=bottom]:border-t data-[side=left]:inset-y-0 data-[side=left]:
|
|
66
|
+
"fixed z-50 flex flex-col gap-4 bg-background bg-clip-padding text-sm shadow-lg outline-none duration-300 ease-out data-[side=bottom]:inset-x-0 data-[side=bottom]:bottom-0 data-[side=bottom]:h-auto data-[side=bottom]:border-t data-[side=left]:inset-y-0 data-[side=left]:start-0 data-[side=left]:h-full data-[side=left]:w-3/4 data-[side=left]:border-e data-[side=right]:inset-y-0 data-[side=right]:end-0 data-[side=right]:h-full data-[side=right]:w-3/4 data-[side=right]:border-s data-[side=top]:inset-x-0 data-[side=top]:top-0 data-[side=top]:h-auto data-[side=top]:border-b data-[side=left]:sm:max-w-sm data-[side=right]:sm:max-w-sm data-open:animate-in data-open:fade-in-0 data-[side=bottom]:data-open:slide-in-from-bottom-6 data-[side=left]:data-open:slide-in-from-start-6 data-[side=right]:data-open:slide-in-from-end-6 data-[side=top]:data-open:slide-in-from-top-6 data-closed:animate-out data-closed:fade-out-0 data-[side=bottom]:data-closed:slide-out-to-bottom-6 data-[side=left]:data-closed:slide-out-to-start-6 data-[side=right]:data-closed:slide-out-to-end-6 data-[side=top]:data-closed:slide-out-to-top-6",
|
|
67
67
|
className
|
|
68
68
|
)}
|
|
69
69
|
{...props}
|
|
@@ -121,7 +121,7 @@ function SidebarProvider({
|
|
|
121
121
|
// `SidebarAutoCollapse` route) must NOT trigger a re-reconcile that reads the
|
|
122
122
|
// saved cookie back as "expanded" and snaps the rail open again.
|
|
123
123
|
// The functional setter avoids closing over a stale `open` value.
|
|
124
|
-
//
|
|
124
|
+
// Deps are `[openProp]` only — not `open` — so incidental collapses do not re-read the cookie.
|
|
125
125
|
React.useLayoutEffect(() => {
|
|
126
126
|
if (typeof window === "undefined") return
|
|
127
127
|
if (typeof document !== "undefined" && document.cookie.includes(`${SIDEBAR_COOKIE_LEGACY_NAME}=`)) {
|
|
@@ -132,7 +132,7 @@ function SidebarProvider({
|
|
|
132
132
|
const fromCookie = readSidebarStateCookie()
|
|
133
133
|
if (fromCookie === undefined) return
|
|
134
134
|
_setOpen((prev) => (prev === fromCookie ? prev : fromCookie))
|
|
135
|
-
}, [])
|
|
135
|
+
}, [openProp])
|
|
136
136
|
|
|
137
137
|
const setOpen = React.useCallback(
|
|
138
138
|
(
|
|
@@ -246,7 +246,7 @@ function Sidebar({
|
|
|
246
246
|
collapsible = "offcanvas",
|
|
247
247
|
className,
|
|
248
248
|
children,
|
|
249
|
-
dir,
|
|
249
|
+
dir: _dir,
|
|
250
250
|
...props
|
|
251
251
|
}: React.ComponentProps<"div"> & {
|
|
252
252
|
side?: "left" | "right"
|
|
@@ -50,7 +50,7 @@ function TooltipContent({
|
|
|
50
50
|
data-slot="tooltip-content"
|
|
51
51
|
sideOffset={sideOffset}
|
|
52
52
|
className={cn(
|
|
53
|
-
"z-50 inline-flex w-fit max-w-xs origin-(--radix-tooltip-content-transform-origin) items-center gap-1.5 rounded-md bg-foreground px-3 py-1.5 text-xs text-background has-data-[slot=kbd]:pe-1.5 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-
|
|
53
|
+
"z-50 inline-flex w-fit max-w-xs origin-(--radix-tooltip-content-transform-origin) items-center gap-1.5 rounded-md bg-foreground px-3 py-1.5 text-xs text-background has-data-[slot=kbd]:pe-1.5 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-end-2 data-[side=right]:slide-in-from-start-2 data-[side=top]:slide-in-from-bottom-2 **:data-[slot=kbd]:relative **:data-[slot=kbd]:isolate **:data-[slot=kbd]:z-50 **:data-[slot=kbd]:rounded-sm data-[state=delayed-open]:animate-in data-[state=delayed-open]:fade-in-0 data-[state=delayed-open]:zoom-in-95 data-open:animate-in data-open:fade-in-0 data-open:zoom-in-95 data-closed:animate-out data-closed:fade-out-0 data-closed:zoom-out-95",
|
|
54
54
|
className
|
|
55
55
|
)}
|
|
56
56
|
{...props}
|
package/src/index.ts
CHANGED
|
@@ -46,6 +46,7 @@ export {
|
|
|
46
46
|
export * from "./lib/list-page-table-properties"
|
|
47
47
|
export * from "./lib/data-list-view"
|
|
48
48
|
export * from "./lib/data-list-view-registry"
|
|
49
|
+
export * from "./lib/nav-active"
|
|
49
50
|
export * from "./lib/data-list-view-surface"
|
|
50
51
|
export * from "./lib/data-list-display-options"
|
|
51
52
|
|
|
@@ -6,6 +6,7 @@
|
|
|
6
6
|
* `getDataListViewRenderKind` + `ListPageConnectedViewBody` (never a dashboard fallback).
|
|
7
7
|
*
|
|
8
8
|
* @see `docs/data-views-pattern.md` — "View registry and connected bodies"
|
|
9
|
+
* @see `.cursor/rules/exxat-hub-supported-views.mdc` — default hubs use **`FULL_HUB_SUPPORTED_VIEWS`** (seven views)
|
|
9
10
|
*/
|
|
10
11
|
|
|
11
12
|
import {
|
|
@@ -47,6 +48,36 @@ const BY_VALUE = new Map<DataListViewType, DataListViewDefinition>(
|
|
|
47
48
|
|
|
48
49
|
export const DATA_LIST_VIEW_REGISTRY: readonly DataListViewDefinition[] = DEFINITIONS
|
|
49
50
|
|
|
51
|
+
/**
|
|
52
|
+
* Default view allowlist for **primary list hubs** (Placements / Team / Students-style pages).
|
|
53
|
+
* Pair with `ListPageTemplate` + `HubTable` when the hub implements table, list, board,
|
|
54
|
+
* and dashboard renderers. Omit `supportedViewTypes` on both components to use this default.
|
|
55
|
+
*/
|
|
56
|
+
export const PRIMARY_HUB_SUPPORTED_VIEWS = [
|
|
57
|
+
"table",
|
|
58
|
+
"list",
|
|
59
|
+
"board",
|
|
60
|
+
"dashboard",
|
|
61
|
+
] as const satisfies readonly DataListViewType[]
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* Default allowlist for **list-page hubs** in this design system (Library / Column types /
|
|
65
|
+
* Tokens, etc.). Matches the All questions hub: table, list, board, dashboard, folder,
|
|
66
|
+
* panel, and tree-panel. Pair with renderers for each kind on `HubTable` + `ListPageTemplate`.
|
|
67
|
+
*/
|
|
68
|
+
export const FULL_HUB_SUPPORTED_VIEWS = [
|
|
69
|
+
"table",
|
|
70
|
+
"list",
|
|
71
|
+
"board",
|
|
72
|
+
"dashboard",
|
|
73
|
+
"folder",
|
|
74
|
+
"panel",
|
|
75
|
+
"tree-panel",
|
|
76
|
+
] as const satisfies readonly DataListViewType[]
|
|
77
|
+
|
|
78
|
+
/** Every registered view type (includes folder, panel, calendar, tree-panel). */
|
|
79
|
+
export const ALL_DATA_LIST_VIEW_TYPES = DATA_LIST_VIEW_REGISTRY.map(d => d.value)
|
|
80
|
+
|
|
50
81
|
export function dataListViewDefinition(view: DataListViewType): DataListViewDefinition {
|
|
51
82
|
const def = BY_VALUE.get(view)
|
|
52
83
|
if (!def) {
|
|
@@ -0,0 +1,162 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Sidebar / secondary-nav active-state helpers.
|
|
3
|
+
*
|
|
4
|
+
* Only one nav target should be "selected" at a time. Prefix matching
|
|
5
|
+
* (`/dashboard` active on `/dashboard/students`) is correct only when no
|
|
6
|
+
* more-specific nav href also matches the current pathname.
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
export function normalizePathname(pathname: string): string {
|
|
10
|
+
if (pathname.length > 1 && pathname.endsWith("/")) return pathname.slice(0, -1)
|
|
11
|
+
return pathname
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
/** Path segment before `#` (if any). */
|
|
15
|
+
export function navUrlPath(url: string): string {
|
|
16
|
+
const hash = url.indexOf("#")
|
|
17
|
+
return hash >= 0 ? url.slice(0, hash) : url
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
/** Hash fragment after `#`, or `null` when the href has no fragment. */
|
|
21
|
+
export function navUrlFragment(url: string): string | null {
|
|
22
|
+
const i = url.indexOf("#")
|
|
23
|
+
return i >= 0 ? url.slice(i + 1) : null
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export function normalizedLocationHash(locationHash: string): string {
|
|
27
|
+
if (!locationHash) return ""
|
|
28
|
+
return locationHash.startsWith("#") ? locationHash.slice(1) : locationHash
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/** Path → hash fragments claimed by *another* nav item at the same path. */
|
|
32
|
+
export function buildNavHashClaims(
|
|
33
|
+
urls: readonly string[],
|
|
34
|
+
): ReadonlyMap<string, ReadonlySet<string>> {
|
|
35
|
+
const map = new Map<string, Set<string>>()
|
|
36
|
+
for (const url of urls) {
|
|
37
|
+
const p = navUrlPath(url)
|
|
38
|
+
const f = navUrlFragment(url)
|
|
39
|
+
if (!p || f === null) continue
|
|
40
|
+
let set = map.get(p)
|
|
41
|
+
if (!set) {
|
|
42
|
+
set = new Set<string>()
|
|
43
|
+
map.set(p, set)
|
|
44
|
+
}
|
|
45
|
+
set.add(f)
|
|
46
|
+
}
|
|
47
|
+
return map
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
function navHasMoreSpecificHashMatch(
|
|
51
|
+
pathname: string,
|
|
52
|
+
locationHash: string,
|
|
53
|
+
hashClaimsByPath: ReadonlyMap<string, ReadonlySet<string>>,
|
|
54
|
+
): boolean {
|
|
55
|
+
const claims = hashClaimsByPath.get(pathname)
|
|
56
|
+
if (!claims) return false
|
|
57
|
+
const h = normalizedLocationHash(locationHash)
|
|
58
|
+
if (h === "") return false
|
|
59
|
+
return claims.has(h)
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
function pathnameMatchesNavPath(
|
|
63
|
+
pathname: string,
|
|
64
|
+
pathOnly: string,
|
|
65
|
+
locationHash: string,
|
|
66
|
+
hashClaimsByPath: ReadonlyMap<string, ReadonlySet<string>>,
|
|
67
|
+
): boolean {
|
|
68
|
+
const norm = normalizePathname(pathname)
|
|
69
|
+
const h = normalizedLocationHash(locationHash)
|
|
70
|
+
|
|
71
|
+
if (pathOnly === "/") {
|
|
72
|
+
if (norm !== "/" || h !== "") return false
|
|
73
|
+
return !navHasMoreSpecificHashMatch("/", locationHash, hashClaimsByPath)
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
if (norm === pathOnly) {
|
|
77
|
+
return !navHasMoreSpecificHashMatch(pathOnly, locationHash, hashClaimsByPath)
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
if (pathOnly === "/library") {
|
|
81
|
+
return norm.startsWith("/library/")
|
|
82
|
+
}
|
|
83
|
+
if (pathOnly.startsWith("/library/")) {
|
|
84
|
+
return norm === pathOnly
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
return norm.startsWith(`${pathOnly}/`)
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
/**
|
|
91
|
+
* Longest matching nav path for `pathname` among `candidateUrls` (path + prefix rules).
|
|
92
|
+
* Returns the winning href string, or `null`.
|
|
93
|
+
*/
|
|
94
|
+
export function resolveActiveNavHref(
|
|
95
|
+
pathname: string,
|
|
96
|
+
candidateUrls: readonly string[],
|
|
97
|
+
options?: {
|
|
98
|
+
locationHash?: string
|
|
99
|
+
hashClaimsByPath?: ReadonlyMap<string, ReadonlySet<string>>
|
|
100
|
+
},
|
|
101
|
+
): string | null {
|
|
102
|
+
const norm = normalizePathname(pathname)
|
|
103
|
+
const hashClaims = options?.hashClaimsByPath ?? buildNavHashClaims(candidateUrls)
|
|
104
|
+
const locationHash = options?.locationHash ?? ""
|
|
105
|
+
|
|
106
|
+
let bestHref: string | null = null
|
|
107
|
+
let bestLen = -1
|
|
108
|
+
|
|
109
|
+
for (const url of candidateUrls) {
|
|
110
|
+
const pathOnly = navUrlPath(url)
|
|
111
|
+
const frag = navUrlFragment(url)
|
|
112
|
+
if (!pathOnly || pathOnly === "#") continue
|
|
113
|
+
|
|
114
|
+
let matches = false
|
|
115
|
+
if (frag !== null) {
|
|
116
|
+
const h = normalizedLocationHash(locationHash)
|
|
117
|
+
if (pathOnly === "/") matches = norm === "/" && h === frag
|
|
118
|
+
else if (pathOnly === "/library") matches = norm.startsWith("/library/") && h === frag
|
|
119
|
+
else if (pathOnly.startsWith("/library/")) matches = norm === pathOnly && h === frag
|
|
120
|
+
else matches = norm === pathOnly && h === frag
|
|
121
|
+
} else {
|
|
122
|
+
matches = pathnameMatchesNavPath(norm, pathOnly, locationHash, hashClaims)
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
if (matches && pathOnly.length > bestLen) {
|
|
126
|
+
bestHref = url
|
|
127
|
+
bestLen = pathOnly.length
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
return bestHref
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
/** True when `url` is the single best match for `pathname` among all nav hrefs. */
|
|
135
|
+
export function isNavHrefActive(
|
|
136
|
+
pathname: string,
|
|
137
|
+
url: string,
|
|
138
|
+
allNavUrls: readonly string[],
|
|
139
|
+
options?: {
|
|
140
|
+
locationHash?: string
|
|
141
|
+
hashClaimsByPath?: ReadonlyMap<string, ReadonlySet<string>>
|
|
142
|
+
},
|
|
143
|
+
): boolean {
|
|
144
|
+
const active = resolveActiveNavHref(pathname, allNavUrls, options)
|
|
145
|
+
if (!active) return false
|
|
146
|
+
return navUrlPath(active) === navUrlPath(url) && navUrlFragment(active) === navUrlFragment(url)
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
/** Collect every `url` from a nav tree (primary rows + children). */
|
|
150
|
+
export function collectNavUrls(
|
|
151
|
+
items: ReadonlyArray<{ url?: string; children?: ReadonlyArray<{ url?: string; children?: unknown }> }>,
|
|
152
|
+
): string[] {
|
|
153
|
+
const out: string[] = []
|
|
154
|
+
const walk = (list: ReadonlyArray<{ url?: string; children?: ReadonlyArray<{ url?: string; children?: unknown }> }>) => {
|
|
155
|
+
for (const it of list) {
|
|
156
|
+
if (typeof it.url === "string" && it.url.length > 0 && it.url !== "#") out.push(it.url)
|
|
157
|
+
if (Array.isArray(it.children)) walk(it.children)
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
walk(items)
|
|
161
|
+
return out
|
|
162
|
+
}
|
|
@@ -112,7 +112,8 @@ ListPageTemplate
|
|
|
112
112
|
```
|
|
113
113
|
|
|
114
114
|
**Reference implementations:**
|
|
115
|
-
- `components/
|
|
115
|
+
- `components/library-table.tsx` + `library-client.tsx` — canonical seven-view hub (start here)
|
|
116
|
+
- `components/columns-showcase.tsx` — cell catalog via `LibraryTable` (custom `columnDefs`, same Add view as Library)
|
|
116
117
|
- `components/tokens-themes-client.tsx` + `components/tokens-secondary-nav.tsx` — hub with secondary panel + URL-driven scope + built-in pagination chrome
|
|
117
118
|
- `components/library-table.tsx` + `components/library-hub-client.tsx` — full multi-view hub (table, board, dashboard)
|
|
118
119
|
|
package/template/AGENTS.md
CHANGED
|
@@ -81,7 +81,7 @@ If two documents conflict, prefer the **more specific** rule for the file type,
|
|
|
81
81
|
|
|
82
82
|
**MUST:** If the main surface is a **`DataTable`** (or equivalent data grid), wrap it in **`ListPageTemplate`** so the **views toolbar** exists (tabs, add view, per-tab settings). Do **not** place `DataTable` only under `PageHeader` without the tab shell.
|
|
83
83
|
|
|
84
|
-
**Reference implementations:** `components/library-
|
|
84
|
+
**Reference implementations:** `components/library-client.tsx` + `components/library-table.tsx` (canonical seven-view hub), `components/columns-showcase.tsx` (cell catalog via `LibraryTable`), `components/tokens-themes-client.tsx` + `components/tokens-hub-auxiliary-views.tsx`.
|
|
85
85
|
|
|
86
86
|
**Rationale:** Consistent navigation, saved views, per-tab view type (table / list / board / dashboard), export at template level.
|
|
87
87
|
|
|
@@ -99,6 +99,20 @@ If two documents conflict, prefer the **more specific** rule for the file type,
|
|
|
99
99
|
|
|
100
100
|
**Details:** `docs/data-views-pattern.md` (mock data, connected views, dashboard view).
|
|
101
101
|
|
|
102
|
+
### 4.1.1 Add view parity (`supportedViewTypes`)
|
|
103
|
+
|
|
104
|
+
**MUST:** Every **`ListPageTemplate`** hub that mounts **`HubTable`** uses the same **seven** Add view options as Library (All questions) unless a narrower list is **documented** in `lib/<entity>-supported-views.ts`:
|
|
105
|
+
|
|
106
|
+
- Registry constant: **`FULL_HUB_SUPPORTED_VIEWS`** (`@/lib/data-list-view-registry` or `@/lib/full-hub-supported-views.ts`).
|
|
107
|
+
- Pass the **same** allowlist to **`ListPageTemplate`** and **`HubTable`** (or omit on both for the default).
|
|
108
|
+
- Implement a **real renderer** for each allowed view (list = **`ListPageBoardCard`** via `renderListRow` — copy **`library-table.tsx`**).
|
|
109
|
+
- **`LibraryItem`** catalogs (Column types): use **`LibraryTable`** with `columnDefs` + `folders` — do not trim to four views or placeholder list rows.
|
|
110
|
+
- **Tokens:** **`tokens-hub-auxiliary-views.tsx`** + **`FULL_HUB_SUPPORTED_VIEWS`**.
|
|
111
|
+
|
|
112
|
+
**MUST NOT:** `supportedViewTypes={["table"]}`, bare two-line `renderListRow`, or `PRIMARY_HUB_SUPPORTED_VIEWS` without documented product exception.
|
|
113
|
+
|
|
114
|
+
**Binding rule:** `.cursor/rules/exxat-hub-supported-views.mdc`. **Pattern doc:** `docs/hub-supported-views-pattern.md`.
|
|
115
|
+
|
|
102
116
|
### 4.2 `TablePropertiesDrawer` and the active view
|
|
103
117
|
|
|
104
118
|
**MUST:** Any page that uses **`ListPageTemplate`** with **`tab.viewType`** (table / list / board / dashboard) and renders **`TablePropertiesDrawer`** **MUST** pass:
|
|
@@ -634,6 +648,7 @@ Copy and complete when implementing or reviewing:
|
|
|
634
648
|
- [ ] **Primary hub + large data:** Same composition as `PlacementsClient` / `TeamClient` (template + metrics when applicable).
|
|
635
649
|
- [ ] **All view tabs:** List/board/dashboard use **`tableState.rows`**; dashboard view uses **`KeyMetrics`** + shared KPI helpers — no “not wired” placeholders or duplicate metric cards.
|
|
636
650
|
- [ ] **Properties drawer:** **`TablePropertiesDrawer`** receives **`currentView`** and **`onViewChange`** from **`renderContent`** / **`updateTab`** + **`dataListViewIcon`** (§4.2) — not table-only copy on Board/List/Dashboard.
|
|
651
|
+
- [ ] **Add view parity:** **`FULL_HUB_SUPPORTED_VIEWS`** on **`ListPageTemplate`** + **`HubTable`** (in sync); every allowed view has a renderer; list uses **`ListPageBoardCard`** — **`.cursor/rules/exxat-hub-supported-views.mdc`**, **`docs/hub-supported-views-pattern.md`**.
|
|
637
652
|
- [ ] **Data view dashboard (Placements / Team / Compliance):** Charts use **`ChartFigure`** + **`ChartDataTable`**; **Edit layout** on toolbar; **`activeBar` / `activeShape`** keyboard styling from **`lib/chart-keyboard-selection`** — not opacity-only **`Cell`** hacks (§4.3).
|
|
638
653
|
- [ ] **Dashboard layout persistence:** **`lib/data-view-dashboard-storage`** (or **`saveDashboardLayout`** / **`loadDashboardLayout`** on Placements); **`mergeDashboardLayout`** on load — no new ad-hoc storage keys for the same layout (§4.3).
|
|
639
654
|
- [ ] **⌘K palette (§7.1):** If adding or changing **`dataGroups`**, map rows in **`lib/command-menu-search-data.ts`** (not `command-menu.tsx`); use **`searchOnly`** on bulky groups; keep **`docs/command-menu-pattern.md`** aligned.
|
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
* (`PrimaryPageTemplate` + `ListPageTemplate`):
|
|
8
8
|
* - `header` : `PageHeader` with title + one-line subtitle describing the demo.
|
|
9
9
|
* - `metrics` : `KeyMetrics` `variant="flat"` — patterns, pinned, sortable, demo rows.
|
|
10
|
-
* - tabs :
|
|
10
|
+
* - tabs : default `table` tab; Add view offers list / board / dashboard (same as Library).
|
|
11
11
|
* - `renderContent` : the `<ColumnsShowcase />` DataTable surface.
|
|
12
12
|
*
|
|
13
13
|
* Cell patterns are exercised inside `columns-showcase.tsx` so the rendered
|
|
@@ -33,6 +33,7 @@ import {
|
|
|
33
33
|
COLUMNS_SHOWCASE_PATTERN_COUNT,
|
|
34
34
|
COLUMNS_SHOWCASE_PINNED_COUNT,
|
|
35
35
|
COLUMNS_SHOWCASE_SORTABLE_COUNT,
|
|
36
|
+
COLUMNS_SUPPORTED_VIEWS,
|
|
36
37
|
} from "@/components/columns-showcase"
|
|
37
38
|
|
|
38
39
|
const COLUMNS_DEFAULT_TABS: ViewTab[] = [
|
|
@@ -127,8 +128,8 @@ export function ColumnsClient() {
|
|
|
127
128
|
onTabsChange={setTabs}
|
|
128
129
|
activeTabId={activeTabId}
|
|
129
130
|
onActiveTabChange={setActiveTabId}
|
|
130
|
-
supportedViewTypes={["table"]}
|
|
131
131
|
getTabCount={getTabCount}
|
|
132
|
+
supportedViewTypes={COLUMNS_SUPPORTED_VIEWS}
|
|
132
133
|
header={
|
|
133
134
|
<PageHeader
|
|
134
135
|
title="Column types"
|
|
@@ -48,7 +48,6 @@
|
|
|
48
48
|
|
|
49
49
|
import * as React from "react"
|
|
50
50
|
import {
|
|
51
|
-
HubTable,
|
|
52
51
|
AttachmentCountCell,
|
|
53
52
|
BooleanToggleCell,
|
|
54
53
|
CurrencyCell,
|
|
@@ -66,6 +65,9 @@ import {
|
|
|
66
65
|
type RowActionDef,
|
|
67
66
|
} from "@/components/data-views"
|
|
68
67
|
import type { DataListViewType } from "@/lib/data-list-view"
|
|
68
|
+
import { FULL_HUB_SUPPORTED_VIEWS } from "@/lib/data-list-view-registry"
|
|
69
|
+
import { LibraryTable } from "@/components/library-table"
|
|
70
|
+
import { DEFAULT_LIBRARY_FOLDERS, type LibraryFolder } from "@/lib/mock/library-folders"
|
|
69
71
|
import { AvatarInitials } from "@/components/ui/avatar"
|
|
70
72
|
import { cn } from "@/lib/utils"
|
|
71
73
|
import {
|
|
@@ -477,7 +479,8 @@ export const COLUMNS_SHOWCASE_PATTERN_COUNT = 18
|
|
|
477
479
|
export const COLUMNS_SHOWCASE_PINNED_COUNT = 3 // select + name + actions
|
|
478
480
|
export const COLUMNS_SHOWCASE_SORTABLE_COUNT = 11 // name, owner, type, level, rating, progress, cost, count, files, lastActivityAt, updatedAt
|
|
479
481
|
|
|
480
|
-
|
|
482
|
+
/** Same seven views as Library / All questions (Add view + Properties). */
|
|
483
|
+
export const COLUMNS_SUPPORTED_VIEWS = FULL_HUB_SUPPORTED_VIEWS
|
|
481
484
|
|
|
482
485
|
export interface ColumnsShowcaseProps {
|
|
483
486
|
/** Active view from `ListPageTemplate.renderContent`. */
|
|
@@ -493,6 +496,9 @@ export interface ColumnsShowcaseProps {
|
|
|
493
496
|
*/
|
|
494
497
|
export function ColumnsShowcase({ view, onViewChange }: ColumnsShowcaseProps) {
|
|
495
498
|
const [rows, setRows] = React.useState<LibraryItem[]>(() => buildRows())
|
|
499
|
+
const [folders, setFolders] = React.useState<LibraryFolder[]>(() =>
|
|
500
|
+
DEFAULT_LIBRARY_FOLDERS.map(f => ({ ...f })),
|
|
501
|
+
)
|
|
496
502
|
const [pagination, setPagination] = React.useState(false)
|
|
497
503
|
|
|
498
504
|
const toggleFavorite = React.useCallback((row: LibraryItem) => {
|
|
@@ -514,28 +520,26 @@ export function ColumnsShowcase({ view, onViewChange }: ColumnsShowcaseProps) {
|
|
|
514
520
|
const columns = useColumns(toggleFavorite, togglePublished)
|
|
515
521
|
|
|
516
522
|
return (
|
|
517
|
-
<
|
|
518
|
-
|
|
519
|
-
|
|
523
|
+
<LibraryTable
|
|
524
|
+
items={rows}
|
|
525
|
+
onItemsChange={setRows}
|
|
526
|
+
folders={folders}
|
|
527
|
+
onFoldersChange={setFolders}
|
|
520
528
|
view={view}
|
|
521
529
|
onViewChange={onViewChange}
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
530
|
+
columnDefs={columns}
|
|
531
|
+
hubLabels={{
|
|
532
|
+
hubLabel: "Column types",
|
|
533
|
+
lifecycleTabLabel: "Column types",
|
|
534
|
+
searchAriaLabel: "Search columns showcase",
|
|
535
|
+
listAriaLabel: "Column types",
|
|
536
|
+
defaultSort: { key: "stem", dir: "asc" },
|
|
537
|
+
}}
|
|
529
538
|
pagination={pagination}
|
|
530
539
|
onPaginationChange={setPagination}
|
|
531
540
|
paginationInitialPageSize={5}
|
|
532
541
|
paginationPageSizeOptions={[5, 10, 25]}
|
|
533
|
-
|
|
534
|
-
<p className="text-sm text-muted-foreground">
|
|
535
|
-
No rows match your filters.
|
|
536
|
-
</p>
|
|
537
|
-
}
|
|
538
|
-
renderers={{}}
|
|
542
|
+
showBulkActions={false}
|
|
539
543
|
/>
|
|
540
544
|
)
|
|
541
545
|
}
|
|
@@ -186,7 +186,7 @@ function ExxatLogoBase({
|
|
|
186
186
|
export function ExxatProductLogo({
|
|
187
187
|
product,
|
|
188
188
|
className,
|
|
189
|
-
variant = "default",
|
|
189
|
+
variant: _variant = "default",
|
|
190
190
|
}: ExxatProductLogoProps) {
|
|
191
191
|
const customProductBrand = useAppStore(s => s.customProductBrand)
|
|
192
192
|
const productBrandColors = useAppStore(s => s.productBrandColors)
|