@godxjp/ui 18.11.0 → 18.11.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.
@@ -41,7 +41,10 @@ interface DataTableProps<T> {
41
41
  onColumnVisibilityChange?: OnChangeFn<VisibilityState>;
42
42
  /**
43
43
  * Manual (server) flags. Default `false` so the simple `data`+`columns` case
44
- * sorts / filters / paginates in-browser with no extra wiring. Set the
44
+ * sorts / filters in-browser with no extra wiring. Client pagination slices
45
+ * rows ONLY when engaged — a numbered `<DataTable.Pagination>` child is
46
+ * composed, or `pagination`/`onPaginationChange` is supplied; a plain table
47
+ * with neither renders every row (no silent 10-row cap, gh#270). Set the
45
48
  * relevant flag `true` and drive the matching state from your query for
46
49
  * server-side sort / filter / pagination.
47
50
  */
@@ -178,6 +178,10 @@ function DataTable({
178
178
  }
179
179
  };
180
180
  const tanstackColumns = React.useMemo(() => toTanstackColumns(columns), [columns]);
181
+ const hasNumberedPager = React.Children.toArray(children).some(
182
+ (c) => React.isValidElement(c) && c.type.displayName === "DataTable.Pagination" && typeof c.props.onChange !== "function"
183
+ );
184
+ const paginationEngaged = manualPagination || controlledPagination !== void 0 || onPaginationChange !== void 0 || hasNumberedPager;
181
185
  const table = useReactTable({
182
186
  data,
183
187
  columns: tanstackColumns,
@@ -185,7 +189,7 @@ function DataTable({
185
189
  getCoreRowModel: getCoreRowModel(),
186
190
  ...manualSorting ? {} : { getSortedRowModel: getSortedRowModel() },
187
191
  ...manualFiltering ? {} : { getFilteredRowModel: getFilteredRowModel() },
188
- ...manualPagination ? {} : { getPaginationRowModel: getPaginationRowModel() },
192
+ ...manualPagination || !paginationEngaged ? {} : { getPaginationRowModel: getPaginationRowModel() },
189
193
  manualSorting,
190
194
  manualFiltering,
191
195
  manualPagination,
@@ -419,6 +423,26 @@ DataTable.Content = function DataTableContent() {
419
423
  );
420
424
  }
421
425
  }, [missingHeaderNames]);
426
+ const scrollRef = React.useRef(null);
427
+ const [hasOverflowEnd, setHasOverflowEnd] = React.useState(false);
428
+ React.useEffect(() => {
429
+ const el = scrollRef.current;
430
+ if (!el) return;
431
+ const update = () => {
432
+ const overflowing = el.scrollWidth - el.clientWidth > 1;
433
+ const atEnd = Math.abs(el.scrollLeft) + el.clientWidth >= el.scrollWidth - 1;
434
+ setHasOverflowEnd(overflowing && !atEnd);
435
+ };
436
+ update();
437
+ el.addEventListener("scroll", update, { passive: true });
438
+ const observer = new ResizeObserver(update);
439
+ observer.observe(el);
440
+ if (el.firstElementChild) observer.observe(el.firstElementChild);
441
+ return () => {
442
+ el.removeEventListener("scroll", update);
443
+ observer.disconnect();
444
+ };
445
+ }, []);
422
446
  const activeSort = sort ?? sortingStateToSort(table.getState().sorting);
423
447
  const isControlledSort = sort !== void 0 || !!onSortChange;
424
448
  const onHeaderClick = (col) => {
@@ -440,7 +464,12 @@ DataTable.Content = function DataTableContent() {
440
464
  return /* @__PURE__ */ jsx(
441
465
  "div",
442
466
  {
443
- className: cn("ui-data-table-scroll", hasPinEnd && "ui-data-table-has-pin-end"),
467
+ ref: scrollRef,
468
+ className: cn(
469
+ "ui-data-table-scroll",
470
+ hasPinEnd && "ui-data-table-has-pin-end",
471
+ hasOverflowEnd && "ui-data-table-has-overflow-end"
472
+ ),
444
473
  "aria-busy": loading,
445
474
  tabIndex: presetAttr ? void 0 : 0,
446
475
  children: /* @__PURE__ */ jsx(
@@ -9,7 +9,7 @@
9
9
  align-items: flex-start;
10
10
  gap: var(--alert-space-gap);
11
11
  width: 100%;
12
- border-radius: var(--radius-md);
12
+ border-radius: var(--alert-radius);
13
13
  border-width: 1px;
14
14
  padding: var(--alert-space-inset);
15
15
  font-size: var(--font-size-base);
@@ -77,6 +77,17 @@
77
77
  background-color: var(--app-shell-main-background);
78
78
  }
79
79
 
80
+ /* `.app-main` carries tabindex="0" (axe scrollable-region-focusable, dbad118), so keyboard
81
+ * focus can land on the region — without a rule here the USER-AGENT default outline (blue
82
+ * in Chrome) wraps the whole content area. Use the design-system ring instead; INSET because
83
+ * `.app-main` is the shell's clip boundary (`contain: paint`) and an outset shadow would be
84
+ * clipped by the grid. Never remove the indicator outright — the region is focusable and
85
+ * must show focus (WCAG 2.4.7, gh#271). */
86
+ .app-main:focus-visible {
87
+ outline: none;
88
+ box-shadow: inset 0 0 0 var(--focus-ring-width) hsl(var(--focus-ring-color, var(--ring)));
89
+ }
90
+
80
91
  .app-footer {
81
92
  grid-area: footer;
82
93
  border-top: 1px solid hsl(var(--border));
@@ -169,6 +169,11 @@
169
169
  padding-inline: var(--space-page-active-x);
170
170
  }
171
171
 
172
+ /* Scroll-hint fade — a SCROLL affordance, so it only shows while the region
173
+ * actually overflows and is not scrolled to the inline-end. The component
174
+ * measures the scroll box and stamps `.ui-data-table-has-overflow-end`
175
+ * (gh#267 — the fade used to render unconditionally, washing out the last
176
+ * column of tables that never scroll). */
172
177
  .ui-data-table-scroll::after {
173
178
  content: "";
174
179
  position: absolute;
@@ -178,9 +183,15 @@
178
183
  z-index: 20;
179
184
  width: 1.75rem;
180
185
  pointer-events: none;
186
+ opacity: 0;
187
+ transition: opacity 150ms ease;
181
188
  background: linear-gradient(to left, hsl(var(--background) / 0.88), transparent);
182
189
  }
183
190
 
191
+ .ui-data-table-has-overflow-end::after {
192
+ opacity: 1;
193
+ }
194
+
184
195
  [data-slot="card-content"][data-flush] .ui-data-table-scroll {
185
196
  margin-inline: 0;
186
197
  padding-inline: 0;
@@ -21,6 +21,10 @@
21
21
  --dialog-space-inset: var(--dialog-space-y) var(--dialog-space-x);
22
22
  --dialog-space-gap: var(--space-stack-md);
23
23
  --dialog-close-space-offset: var(--space-4);
24
+ /* Alert corner radius (gh#268 — rule #45): a full-width Alert often sits in the
25
+ * same page column as a Card (--card-radius); a service aligns them by
26
+ * overriding this once. Default keeps the historical --radius-md. */
27
+ --alert-radius: var(--radius-md);
24
28
  --alert-space-inset: var(--space-section-active);
25
29
  --alert-space-gap: var(--space-inline-md);
26
30
  --alert-inner-space-gap: var(--space-stack-sm);
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@godxjp/ui",
3
- "version": "18.11.0",
4
- "godxUiMcp": "18.11.0",
3
+ "version": "18.11.1",
4
+ "godxUiMcp": "18.11.1",
5
5
  "license": "Apache-2.0",
6
6
  "repository": {
7
7
  "type": "git",