@undevy-org/tablewright 0.5.0 → 0.5.1

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.
@@ -3,6 +3,7 @@ import { useState, useRef, useEffect } from "react";
3
3
  import { Minimize2, Maximize2, X } from "lucide-react";
4
4
  import { Button } from "../ui/button.js";
5
5
  import { cn } from "../../lib/utils.js";
6
+ import { SCROLL_REGION_TAB_INDEX } from "../../lib/scroll-region-tab-index.js";
6
7
  import { useScrollActivity } from "../../hooks/use-scroll-activity.js";
7
8
  import { DrawerExpandContext } from "../../context/drawer-expand-context.js";
8
9
  function DrawerShell({
@@ -86,6 +87,7 @@ function DrawerShell({
86
87
  "div",
87
88
  {
88
89
  ref: bodyScrollRef,
90
+ tabIndex: SCROLL_REGION_TAB_INDEX,
89
91
  className: "scrollbar-auto-hide min-h-0 flex-1 overflow-y-auto px-6 py-6",
90
92
  children
91
93
  }
@@ -1,12 +1,21 @@
1
1
  import { jsx } from "react/jsx-runtime";
2
2
  import { cn } from "../../lib/utils.js";
3
+ import { SCROLL_REGION_TAB_INDEX } from "../../lib/scroll-region-tab-index.js";
3
4
  import { useScrollActivity } from "../../hooks/use-scroll-activity.js";
4
5
  function TableLayout({ children, className }) {
5
6
  return /* @__PURE__ */ jsx("div", { className: cn("relative flex flex-1 flex-col overflow-hidden", className), children });
6
7
  }
7
8
  function ScrollArea({ children, className }) {
8
9
  const scrollRef = useScrollActivity();
9
- return /* @__PURE__ */ jsx("div", { ref: scrollRef, className: cn("scrollbar-auto-hide flex-1 overflow-auto", className), children });
10
+ return /* @__PURE__ */ jsx(
11
+ "div",
12
+ {
13
+ ref: scrollRef,
14
+ tabIndex: SCROLL_REGION_TAB_INDEX,
15
+ className: cn("scrollbar-auto-hide flex-1 overflow-auto", className),
16
+ children
17
+ }
18
+ );
10
19
  }
11
20
  TableLayout.ScrollArea = ScrollArea;
12
21
  export {
@@ -1,6 +1,7 @@
1
1
  import { jsxs, jsx, Fragment } from "react/jsx-runtime";
2
2
  import * as React from "react";
3
3
  import { ChevronRight, ChevronLeft } from "lucide-react";
4
+ import { collapsedNavLinkAriaLabel } from "../../lib/collapsed-nav-link-a11y.js";
4
5
  import { cn } from "../../lib/utils.js";
5
6
  import { useAppShell } from "./app-shell-context.js";
6
7
  function isItemActive(href, activeKey) {
@@ -75,10 +76,15 @@ const Sidebar = React.forwardRef(
75
76
  ),
76
77
  showExpanded && /* @__PURE__ */ jsx("span", { className: "truncate text-[13px]", children: item.label })
77
78
  ] });
79
+ const collapsedLinkLabel = collapsedNavLinkAriaLabel(
80
+ showExpanded,
81
+ item.label
82
+ );
78
83
  if (renderLink) {
79
84
  return /* @__PURE__ */ jsx(React.Fragment, { children: renderLink(item.href, {
80
85
  className: itemClassName,
81
- children
86
+ children,
87
+ ...collapsedLinkLabel ? { "aria-label": collapsedLinkLabel } : {}
82
88
  }) }, item.key);
83
89
  }
84
90
  return /* @__PURE__ */ jsx(
@@ -86,6 +92,7 @@ const Sidebar = React.forwardRef(
86
92
  {
87
93
  href: item.href,
88
94
  className: itemClassName,
95
+ "aria-label": collapsedLinkLabel,
89
96
  children
90
97
  },
91
98
  item.key
@@ -2,6 +2,7 @@ import { jsx, jsxs, Fragment } from "react/jsx-runtime";
2
2
  import { ChevronDown, SunMoon, LogOut } from "lucide-react";
3
3
  import { DropdownMenu, DropdownMenuTrigger, DropdownMenuContent, DropdownMenuItem } from "../ui/dropdown-menu.js";
4
4
  import { ThemeSwitch } from "../ui/theme-switch.js";
5
+ import { decorativeAvatarAlt } from "../../lib/decorative-avatar-alt.js";
5
6
  import { cn } from "../../lib/utils.js";
6
7
  import { useAppShell } from "./app-shell-context.js";
7
8
  function getInitials(name) {
@@ -26,7 +27,7 @@ function SidebarAccountMenu({
26
27
  "img",
27
28
  {
28
29
  src: avatarUrl,
29
- alt: name,
30
+ alt: decorativeAvatarAlt(!effectiveCollapsed, name),
30
31
  className: "h-7 w-7 shrink-0 rounded-full object-cover"
31
32
  }
32
33
  ) : /* @__PURE__ */ jsx("div", { className: "flex h-7 w-7 shrink-0 items-center justify-center rounded-full bg-[var(--bg-tertiary)] text-[11px] font-semibold text-[var(--text-secondary)]", children: getInitials(name) });
@@ -62,7 +63,7 @@ function SidebarAccountMenu({
62
63
  "img",
63
64
  {
64
65
  src: avatarUrl,
65
- alt: name,
66
+ alt: decorativeAvatarAlt(true, name),
66
67
  className: "h-8 w-8 shrink-0 rounded-full object-cover"
67
68
  }
68
69
  ) : /* @__PURE__ */ jsx("div", { className: "flex h-8 w-8 shrink-0 items-center justify-center rounded-full bg-[var(--bg-tertiary)] text-[11px] font-semibold text-[var(--text-secondary)]", children: getInitials(name) }),
@@ -1,5 +1,6 @@
1
1
  import { jsxs, jsx } from "react/jsx-runtime";
2
2
  import * as React from "react";
3
+ import { decorativeAvatarAlt } from "../../lib/decorative-avatar-alt.js";
3
4
  import { cn } from "../../lib/utils.js";
4
5
  import { useAppShell } from "./app-shell-context.js";
5
6
  function getInitials(name) {
@@ -23,7 +24,7 @@ const SidebarUser = React.forwardRef(
23
24
  "img",
24
25
  {
25
26
  src: avatarUrl,
26
- alt: name,
27
+ alt: decorativeAvatarAlt(!effectiveCollapsed, name),
27
28
  className: "h-8 w-8 shrink-0 rounded-full object-cover"
28
29
  }
29
30
  ) : /* @__PURE__ */ jsx("div", { className: "flex h-8 w-8 shrink-0 items-center justify-center rounded-full bg-[var(--bg-tertiary)] text-[11px] font-semibold text-[var(--text-secondary)]", children: getInitials(name) }),
@@ -0,0 +1,6 @@
1
+ function collapsedNavLinkAriaLabel(showExpanded, label) {
2
+ return showExpanded ? void 0 : label;
3
+ }
4
+ export {
5
+ collapsedNavLinkAriaLabel
6
+ };
@@ -0,0 +1,6 @@
1
+ function decorativeAvatarAlt(adjacentNameVisible, name) {
2
+ return adjacentNameVisible ? "" : name;
3
+ }
4
+ export {
5
+ decorativeAvatarAlt
6
+ };
@@ -0,0 +1,4 @@
1
+ const SCROLL_REGION_TAB_INDEX = 0;
2
+ export {
3
+ SCROLL_REGION_TAB_INDEX
4
+ };
package/dist/styles.css CHANGED
@@ -580,6 +580,9 @@
580
580
  .h-9 {
581
581
  height: 2.25rem
582
582
  }
583
+ .h-96 {
584
+ height: 24rem
585
+ }
583
586
  .h-\[18px\] {
584
587
  height: 18px
585
588
  }
@@ -682,6 +682,7 @@ function CopyableText({
682
682
  )
683
683
  ] });
684
684
  }
685
+ const SCROLL_REGION_TAB_INDEX = 0;
685
686
  function useScrollActivity(idleMs = 700) {
686
687
  const nodeRef = React.useRef(null);
687
688
  const [nodeVersion, setNodeVersion] = React.useState(0);
@@ -797,6 +798,7 @@ function DrawerShell({
797
798
  "div",
798
799
  {
799
800
  ref: bodyScrollRef,
801
+ tabIndex: SCROLL_REGION_TAB_INDEX,
800
802
  className: "scrollbar-auto-hide min-h-0 flex-1 overflow-y-auto px-6 py-6",
801
803
  children
802
804
  }
@@ -862,7 +864,15 @@ function TableLayout({ children, className }) {
862
864
  }
863
865
  function ScrollArea({ children, className }) {
864
866
  const scrollRef = useScrollActivity();
865
- return /* @__PURE__ */ jsxRuntime.jsx("div", { ref: scrollRef, className: cn("scrollbar-auto-hide flex-1 overflow-auto", className), children });
867
+ return /* @__PURE__ */ jsxRuntime.jsx(
868
+ "div",
869
+ {
870
+ ref: scrollRef,
871
+ tabIndex: SCROLL_REGION_TAB_INDEX,
872
+ className: cn("scrollbar-auto-hide flex-1 overflow-auto", className),
873
+ children
874
+ }
875
+ );
866
876
  }
867
877
  TableLayout.ScrollArea = ScrollArea;
868
878
  function stickyStyle(meta, isHeader) {
@@ -1845,6 +1855,9 @@ const AppShellContext = React.createContext(null);
1845
1855
  function useAppShell() {
1846
1856
  return React.useContext(AppShellContext);
1847
1857
  }
1858
+ function collapsedNavLinkAriaLabel(showExpanded, label) {
1859
+ return showExpanded ? void 0 : label;
1860
+ }
1848
1861
  function isItemActive(href, activeKey) {
1849
1862
  if (href === "" || href === "/") return activeKey === "" || activeKey === "/";
1850
1863
  return activeKey === href || activeKey.startsWith(href + "/");
@@ -1917,10 +1930,15 @@ const Sidebar = React__namespace.forwardRef(
1917
1930
  ),
1918
1931
  showExpanded && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "truncate text-[13px]", children: item.label })
1919
1932
  ] });
1933
+ const collapsedLinkLabel = collapsedNavLinkAriaLabel(
1934
+ showExpanded,
1935
+ item.label
1936
+ );
1920
1937
  if (renderLink) {
1921
1938
  return /* @__PURE__ */ jsxRuntime.jsx(React__namespace.Fragment, { children: renderLink(item.href, {
1922
1939
  className: itemClassName,
1923
- children
1940
+ children,
1941
+ ...collapsedLinkLabel ? { "aria-label": collapsedLinkLabel } : {}
1924
1942
  }) }, item.key);
1925
1943
  }
1926
1944
  return /* @__PURE__ */ jsxRuntime.jsx(
@@ -1928,6 +1946,7 @@ const Sidebar = React__namespace.forwardRef(
1928
1946
  {
1929
1947
  href: item.href,
1930
1948
  className: itemClassName,
1949
+ "aria-label": collapsedLinkLabel,
1931
1950
  children
1932
1951
  },
1933
1952
  item.key
@@ -2088,6 +2107,9 @@ const AppShell = Object.assign(AppShellRoot, {
2088
2107
  Header: AppShellHeader,
2089
2108
  Content: AppShellContent
2090
2109
  });
2110
+ function decorativeAvatarAlt(adjacentNameVisible, name) {
2111
+ return adjacentNameVisible ? "" : name;
2112
+ }
2091
2113
  function getInitials$1(name) {
2092
2114
  return name.split(" ").map((w) => w[0]).slice(0, 2).join("").toUpperCase();
2093
2115
  }
@@ -2109,7 +2131,7 @@ const SidebarUser = React__namespace.forwardRef(
2109
2131
  "img",
2110
2132
  {
2111
2133
  src: avatarUrl,
2112
- alt: name,
2134
+ alt: decorativeAvatarAlt(!effectiveCollapsed, name),
2113
2135
  className: "h-8 w-8 shrink-0 rounded-full object-cover"
2114
2136
  }
2115
2137
  ) : /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex h-8 w-8 shrink-0 items-center justify-center rounded-full bg-[var(--bg-tertiary)] text-[11px] font-semibold text-[var(--text-secondary)]", children: getInitials$1(name) }),
@@ -2141,7 +2163,7 @@ function SidebarAccountMenu({
2141
2163
  "img",
2142
2164
  {
2143
2165
  src: avatarUrl,
2144
- alt: name,
2166
+ alt: decorativeAvatarAlt(!effectiveCollapsed, name),
2145
2167
  className: "h-7 w-7 shrink-0 rounded-full object-cover"
2146
2168
  }
2147
2169
  ) : /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex h-7 w-7 shrink-0 items-center justify-center rounded-full bg-[var(--bg-tertiary)] text-[11px] font-semibold text-[var(--text-secondary)]", children: getInitials(name) });
@@ -2177,7 +2199,7 @@ function SidebarAccountMenu({
2177
2199
  "img",
2178
2200
  {
2179
2201
  src: avatarUrl,
2180
- alt: name,
2202
+ alt: decorativeAvatarAlt(true, name),
2181
2203
  className: "h-8 w-8 shrink-0 rounded-full object-cover"
2182
2204
  }
2183
2205
  ) : /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex h-8 w-8 shrink-0 items-center justify-center rounded-full bg-[var(--bg-tertiary)] text-[11px] font-semibold text-[var(--text-secondary)]", children: getInitials(name) }),
@@ -428,6 +428,9 @@
428
428
  .h-9 {
429
429
  height: 2.25rem
430
430
  }
431
+ .h-96 {
432
+ height: 24rem
433
+ }
431
434
  .h-\[18px\] {
432
435
  height: 18px
433
436
  }
@@ -8,6 +8,7 @@ export interface SidebarProps {
8
8
  renderLink?: (href: string, props: {
9
9
  className: string;
10
10
  children: React.ReactNode;
11
+ "aria-label"?: string;
11
12
  }) => React.ReactNode;
12
13
  header?: React.ReactNode;
13
14
  footer?: React.ReactNode;
@@ -0,0 +1,2 @@
1
+ /** Accessible name for icon-only nav links when the sidebar rail is collapsed. */
2
+ export declare function collapsedNavLinkAriaLabel(showExpanded: boolean, label: string): string | undefined;
@@ -0,0 +1,2 @@
1
+ /** Avatar alt when the same name is shown as visible text beside the image. */
2
+ export declare function decorativeAvatarAlt(adjacentNameVisible: boolean, name: string): string;
@@ -0,0 +1,2 @@
1
+ /** Makes overflow scroll regions reachable from the keyboard (WCAG 2.1.1). */
2
+ export declare const SCROLL_REGION_TAB_INDEX = 0;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@undevy-org/tablewright",
3
- "version": "0.5.0",
3
+ "version": "0.5.1",
4
4
  "description": "React data-table and application-shell components: filters, drawers, row controls, sortable and resizable columns, built on Radix UI and TanStack Table.",
5
5
  "license": "MIT",
6
6
  "repository": {