mac-human-design 0.1.22 → 0.1.23

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 CHANGED
@@ -2,6 +2,23 @@
2
2
 
3
3
  This file tracks changes between published npm versions of `mac-human-design`.
4
4
 
5
+ ## 0.1.23
6
+
7
+ ### Added
8
+
9
+ - Added `grid`/`table` semantics, row and column metadata, busy state, selected
10
+ row state, keyboard row activation, and optional accessible labels to
11
+ `MacDataTable`.
12
+ - Added focus-visible styling for interactive `MacDataTable` rows.
13
+ - Extended the macOS design verifier to protect interactive table/grid
14
+ accessibility invariants.
15
+
16
+ ### Changed
17
+
18
+ - Interactive table rows can now be selected with Space and opened/activated
19
+ with Enter when row handlers are provided.
20
+ - Bumped the package to `0.1.23`.
21
+
5
22
  ## 0.1.22
6
23
 
7
24
  ### Added
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "mac-human-design",
3
- "version": "0.1.22",
3
+ "version": "0.1.23",
4
4
  "description": "Reusable macOS-oriented UI primitives and SF Symbols bridge for Tauri apps.",
5
5
  "type": "module",
6
6
  "main": "src/index.ts",
@@ -8,6 +8,7 @@ const packageJsonPath = path.join(repoRoot, "package.json");
8
8
  const wrapperPath = path.join(repoRoot, "src", "components", "MacBaseUI.tsx");
9
9
  const symbolIconButtonPath = path.join(repoRoot, "src", "components", "SymbolIconButton.tsx");
10
10
  const galleryPath = path.join(repoRoot, "src", "components", "MacBaseUIGallery.tsx");
11
+ const dataTablePath = path.join(repoRoot, "src", "components", "MacDataTable.tsx");
11
12
  const symbolServicePath = path.join(repoRoot, "src", "symbols", "systemSymbolService.ts");
12
13
 
13
14
  function readText(filePath) {
@@ -48,6 +49,7 @@ const packageJson = JSON.parse(readText(packageJsonPath));
48
49
  const wrapperSource = readText(wrapperPath);
49
50
  const symbolIconButtonSource = readText(symbolIconButtonPath);
50
51
  const gallerySource = readText(galleryPath);
52
+ const dataTableSource = readText(dataTablePath);
51
53
  const symbolServiceSource = readText(symbolServicePath);
52
54
 
53
55
  function getCssRuleBody(selector) {
@@ -249,6 +251,32 @@ if (
249
251
  ]);
250
252
  }
251
253
 
254
+ const missingDataTableRequirements = [
255
+ ["interactive grid role", /const tableRole = hasInteractiveRows \? "grid" : "table"/],
256
+ ["aria busy state", /aria-busy=\{loading \|\| undefined\}/],
257
+ ["row count metadata", /aria-rowcount=\{loading \? undefined : rows\.length \+ 1\}/],
258
+ ["column count metadata", /aria-colcount=\{columns\.length\}/],
259
+ ["header row index", /aria-rowindex=\{1\}/],
260
+ ["cell column index", /aria-colindex=\{columnIndex \+ 1\}/],
261
+ ["interactive row selected state", /aria-selected=\{hasInteractiveRows \? selected : undefined\}/],
262
+ ["interactive row tab stop", /tabIndex=\{hasInteractiveRows \? 0 : undefined\}/],
263
+ ["keyboard row handler", /handleRowKeyDown/],
264
+ ["enter key activation", /event\.key === "Enter"/],
265
+ ["space key activation", /event\.key === " "/],
266
+ ].filter(([, pattern]) => !pattern.test(dataTableSource));
267
+
268
+ if (missingDataTableRequirements.length > 0) {
269
+ fail(
270
+ "Missing MacDataTable interactive grid accessibility invariants.",
271
+ missingDataTableRequirements.map(([label]) => label),
272
+ );
273
+ }
274
+
275
+ requireCssDeclarations(".hd-mac-data-table-row-interactive:focus-visible", [
276
+ ["focus outline reset", /outline\s*:\s*none\s*;/],
277
+ ["focus ring", /var\(--hd-mac-focus-ring\)/],
278
+ ]);
279
+
252
280
  const missingSymbolServiceRequirements = [
253
281
  ["transparent symbol padding trim", /trimTransparentSymbolPadding/],
254
282
  ["alpha threshold trim", /TRANSPARENT_ALPHA_THRESHOLD/],
@@ -17,6 +17,8 @@ export type MacDataTableProps<Row> = {
17
17
  columns: readonly MacDataTableColumn<Row>[];
18
18
  rows: readonly Row[];
19
19
  getRowKey: (row: Row, index: number) => React.Key;
20
+ ariaLabel?: string;
21
+ ariaLabelledBy?: string;
20
22
  isRowSelected?: (row: Row, index: number) => boolean;
21
23
  onRowClick?: (row: Row, index: number) => void;
22
24
  onRowDoubleClick?: (row: Row, index: number) => void;
@@ -53,6 +55,8 @@ export function MacDataTable<Row>({
53
55
  columns,
54
56
  rows,
55
57
  getRowKey,
58
+ ariaLabel,
59
+ ariaLabelledBy,
56
60
  isRowSelected,
57
61
  onRowClick,
58
62
  onRowDoubleClick,
@@ -67,6 +71,30 @@ export function MacDataTable<Row>({
67
71
  }: MacDataTableProps<Row>) {
68
72
  const gridTemplateColumns = columns.map((column) => column.width).join(" ");
69
73
  const hasInteractiveRows = Boolean(onRowClick || onRowDoubleClick);
74
+ const tableRole = hasInteractiveRows ? "grid" : "table";
75
+
76
+ const handleRowKeyDown = React.useCallback(
77
+ (event: React.KeyboardEvent, row: Row, index: number) => {
78
+ if (!hasInteractiveRows) {
79
+ return;
80
+ }
81
+
82
+ if (event.key === "Enter") {
83
+ event.preventDefault();
84
+ if (onRowDoubleClick) {
85
+ onRowDoubleClick(row, index);
86
+ return;
87
+ }
88
+ onRowClick?.(row, index);
89
+ }
90
+
91
+ if (event.key === " ") {
92
+ event.preventDefault();
93
+ onRowClick?.(row, index);
94
+ }
95
+ },
96
+ [hasInteractiveRows, onRowClick, onRowDoubleClick],
97
+ );
70
98
 
71
99
  return (
72
100
  <motion.div
@@ -74,12 +102,19 @@ export function MacDataTable<Row>({
74
102
  animate={{ opacity: 1 }}
75
103
  transition={tableTransition}
76
104
  className={joinClasses("hd-mac-data-table", bordered && "hd-mac-data-table-bordered", className)}
105
+ role={tableRole}
106
+ aria-label={ariaLabel}
107
+ aria-labelledby={ariaLabelledBy}
108
+ aria-busy={loading || undefined}
109
+ aria-rowcount={loading ? undefined : rows.length + 1}
110
+ aria-colcount={columns.length}
77
111
  style={style}
78
112
  >
79
113
  <motion.div
80
114
  layout
81
115
  className="hd-mac-data-table-header"
82
116
  role="row"
117
+ aria-rowindex={1}
83
118
  style={{ gridTemplateColumns }}
84
119
  transition={tableTransition}
85
120
  >
@@ -92,6 +127,7 @@ export function MacDataTable<Row>({
92
127
  column.headerClassName,
93
128
  )}
94
129
  role="columnheader"
130
+ aria-colindex={columns.indexOf(column) + 1}
95
131
  style={column.headerStyle}
96
132
  >
97
133
  {column.header}
@@ -139,10 +175,14 @@ export function MacDataTable<Row>({
139
175
  data-selected={selected || undefined}
140
176
  onClick={onRowClick ? () => onRowClick(row, index) : undefined}
141
177
  onDoubleClick={onRowDoubleClick ? () => onRowDoubleClick(row, index) : undefined}
178
+ onKeyDown={hasInteractiveRows ? (event) => handleRowKeyDown(event, row, index) : undefined}
142
179
  role="row"
180
+ aria-rowindex={index + 2}
181
+ aria-selected={hasInteractiveRows ? selected : undefined}
182
+ tabIndex={hasInteractiveRows ? 0 : undefined}
143
183
  style={{ gridTemplateColumns, ...rowStyle?.(row, index) }}
144
184
  >
145
- {columns.map((column) => (
185
+ {columns.map((column, columnIndex) => (
146
186
  <div
147
187
  key={column.id}
148
188
  className={joinClasses(
@@ -151,6 +191,7 @@ export function MacDataTable<Row>({
151
191
  column.className,
152
192
  )}
153
193
  role="cell"
194
+ aria-colindex={columnIndex + 1}
154
195
  style={column.style}
155
196
  >
156
197
  {column.render(row, index)}
@@ -1481,6 +1481,13 @@
1481
1481
  cursor: default;
1482
1482
  }
1483
1483
 
1484
+ .hd-mac-data-table-row-interactive:focus-visible {
1485
+ outline: none;
1486
+ box-shadow:
1487
+ var(--hd-mac-focus-ring),
1488
+ inset 0 0 0 1px color-mix(in srgb, var(--hd-mac-blue) 24%, transparent);
1489
+ }
1490
+
1484
1491
  .hd-mac-data-table-row-interactive:hover {
1485
1492
  background: var(--hd-mac-fill-hover);
1486
1493
  }
@@ -1494,6 +1501,12 @@
1494
1501
  box-shadow: inset 3px 0 0 var(--hd-mac-blue);
1495
1502
  }
1496
1503
 
1504
+ .hd-mac-data-table-row[data-selected]:focus-visible {
1505
+ box-shadow:
1506
+ var(--hd-mac-focus-ring),
1507
+ inset 3px 0 0 var(--hd-mac-blue);
1508
+ }
1509
+
1497
1510
  .hd-mac-data-table-row[data-selected]:hover {
1498
1511
  background: color-mix(in srgb, var(--hd-mac-blue) 24%, transparent);
1499
1512
  }