@exxatdesignux/ui 0.5.5 → 0.5.6

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.
Files changed (28) hide show
  1. package/CHANGELOG.md +10 -0
  2. package/consumer-extras/cursor-rules/exxat-ds-agents.mdc +16 -5
  3. package/consumer-extras/cursor-rules/exxat-ux-discovery-protocol.mdc +122 -0
  4. package/consumer-extras/cursor-rules/exxat-ux-principles.mdc +186 -0
  5. package/consumer-extras/cursor-skills/exxat-senior-ux/SKILL.md +145 -0
  6. package/consumer-extras/patterns/jobs/README.md +59 -0
  7. package/consumer-extras/patterns/jobs/record-detail.md +177 -0
  8. package/consumer-extras/patterns/modern-saas-patterns.md +165 -0
  9. package/dist/components/data-table/index.js +28 -22
  10. package/dist/components/data-table/index.js.map +1 -1
  11. package/dist/components/data-table/pagination.js +28 -22
  12. package/dist/components/data-table/pagination.js.map +1 -1
  13. package/dist/components/data-table/use-table-state.js +20 -17
  14. package/dist/components/data-table/use-table-state.js.map +1 -1
  15. package/dist/components/data-views/hub-table.js +28 -22
  16. package/dist/components/data-views/hub-table.js.map +1 -1
  17. package/dist/components/data-views/index.js +28 -22
  18. package/dist/components/data-views/index.js.map +1 -1
  19. package/dist/hooks/use-app-theme.d.ts +1 -1
  20. package/dist/index.js +28 -22
  21. package/dist/index.js.map +1 -1
  22. package/package.json +1 -1
  23. package/src/components/data-table/index.tsx +10 -6
  24. package/src/components/data-table/use-table-state.ts +33 -26
  25. package/template/docs/jobs/README.md +59 -0
  26. package/template/docs/jobs/record-detail.md +177 -0
  27. package/template/docs/modern-saas-patterns.md +165 -0
  28. package/tokens/hooks-index.json +2 -2
@@ -10,7 +10,7 @@ declare function useAppTheme(): {
10
10
  brand: Brand;
11
11
  setBrand: (b: Brand) => void;
12
12
  /** The user's preference: "system" | "normal" | "high" | "windows" */
13
- contrastPref: "system" | "normal" | "high" | "windows";
13
+ contrastPref: "normal" | "high" | "system" | "windows";
14
14
  /** The resolved contrast mode actually applied to the DOM. */
15
15
  contrast: ContrastMode;
16
16
  /** Set the contrast preference. */
package/dist/index.js CHANGED
@@ -1905,15 +1905,6 @@ function useTableState(data, columns, defaultSort, paginationOverride, syncedSea
1905
1905
  });
1906
1906
  return [...groups.entries()].sort(([a], [b]) => a.localeCompare(b)).map(([key, groupRows]) => ({ groupKey: key, groupLabel: key, rows: groupRows }));
1907
1907
  }, [rows, groupBy]);
1908
- const LOCKED_KEYS = React10.useMemo(() => new Set(Object.keys(lockedPins)), [lockedPins]);
1909
- const effectivePins = React10.useMemo(() => {
1910
- if (isReflowViewport || !isOverflowing) return {};
1911
- const result = {};
1912
- for (const [key, pin] of Object.entries(colPins)) {
1913
- result[key] = pin;
1914
- }
1915
- return result;
1916
- }, [colPins, isOverflowing, isReflowViewport]);
1917
1908
  const displayCols = React10.useMemo(() => {
1918
1909
  const leftPinned = [];
1919
1910
  const free = [];
@@ -1933,6 +1924,19 @@ function useTableState(data, columns, defaultSort, paginationOverride, syncedSea
1933
1924
  }
1934
1925
  return out;
1935
1926
  }, [colOrder, colPins, hiddenCols, columnsByKey]);
1927
+ const totalWidth = React10.useMemo(
1928
+ () => displayCols.reduce((s, c) => s + (colWidths[c.key] ?? c.width ?? 100), 0),
1929
+ [displayCols, colWidths]
1930
+ );
1931
+ const LOCKED_KEYS = React10.useMemo(() => new Set(Object.keys(lockedPins)), [lockedPins]);
1932
+ const effectivePins = React10.useMemo(() => {
1933
+ if (isReflowViewport || !isOverflowing) return {};
1934
+ const result = {};
1935
+ for (const [key, pin] of Object.entries(colPins)) {
1936
+ result[key] = pin;
1937
+ }
1938
+ return result;
1939
+ }, [colPins, isOverflowing, isReflowViewport]);
1936
1940
  function startResize(key, e) {
1937
1941
  e.preventDefault();
1938
1942
  e.stopPropagation();
@@ -1993,18 +1997,21 @@ function useTableState(data, columns, defaultSort, paginationOverride, syncedSea
1993
1997
  function toggleWrap(key) {
1994
1998
  setColWrap((p) => ({ ...p, [key]: !p[key] }));
1995
1999
  }
1996
- function checkOverflow() {
2000
+ const checkOverflow = React10.useCallback(() => {
1997
2001
  const el = scrollRef.current;
1998
2002
  if (!el) return;
1999
- setIsOverflowing(el.scrollWidth > el.clientWidth + 1);
2000
- }
2003
+ setIsOverflowing(totalWidth > el.clientWidth + 1);
2004
+ }, [totalWidth]);
2001
2005
  function handleScroll() {
2002
2006
  const el = scrollRef.current;
2003
2007
  if (!el) return;
2004
2008
  setScrolled(el.scrollLeft > 1);
2005
2009
  setScrollEnd(el.scrollLeft >= el.scrollWidth - el.clientWidth - 1);
2006
- setIsOverflowing(el.scrollWidth > el.clientWidth + 1);
2010
+ setIsOverflowing(totalWidth > el.clientWidth + 1);
2007
2011
  }
2012
+ React10.useLayoutEffect(() => {
2013
+ checkOverflow();
2014
+ }, [checkOverflow]);
2008
2015
  function getRowId(row, index, getIdFn) {
2009
2016
  return getIdFn ? getIdFn(row, index) : row.id ?? index;
2010
2017
  }
@@ -2057,10 +2064,6 @@ function useTableState(data, columns, defaultSort, paginationOverride, syncedSea
2057
2064
  },
2058
2065
  [effectivePins, isReflowViewport, stickyOffsets]
2059
2066
  );
2060
- const totalWidth = React10.useMemo(
2061
- () => displayCols.reduce((s, c) => s + (colWidths[c.key] ?? c.width ?? 100), 0),
2062
- [displayCols, colWidths]
2063
- );
2064
2067
  return {
2065
2068
  // Sort
2066
2069
  sortRules,
@@ -2800,7 +2803,7 @@ function DataTableInner({
2800
2803
  setSheetOpen,
2801
2804
  setSheetInitialPanel
2802
2805
  } = state;
2803
- React10.useEffect(() => {
2806
+ React10.useLayoutEffect(() => {
2804
2807
  const syncScrollport = () => {
2805
2808
  const el2 = scrollRef.current;
2806
2809
  if (el2) {
@@ -2813,8 +2816,10 @@ function DataTableInner({
2813
2816
  if (!el) return;
2814
2817
  const ro = new ResizeObserver(syncScrollport);
2815
2818
  ro.observe(el);
2819
+ const table = el.querySelector("table");
2820
+ if (table) ro.observe(table);
2816
2821
  return () => ro.disconnect();
2817
- }, []);
2822
+ }, [totalWidth, displayCols.length, checkOverflow]);
2818
2823
  const columnMenuPendingActionRef = React10.useRef(null);
2819
2824
  const pinnedScrollHintDoneRef = React10.useRef(false);
2820
2825
  React10.useEffect(() => {
@@ -3051,11 +3056,12 @@ function DataTableInner({
3051
3056
  children: /* @__PURE__ */ jsxs(
3052
3057
  "table",
3053
3058
  {
3054
- className: "w-full text-sm border-separate border-spacing-0",
3059
+ className: "text-sm border-separate border-spacing-0",
3055
3060
  style: {
3056
3061
  tableLayout: "fixed",
3057
- minWidth: totalWidth,
3058
- width: headerIsStuck ? floatingHeaderTableWidth : void 0
3062
+ // Explicit column-sum width — `w-full` made the grid stretch to the scrollport
3063
+ // so scrollWidth === clientWidth and the overflow-gated pin rule never fired.
3064
+ width: totalWidth
3059
3065
  },
3060
3066
  children: [
3061
3067
  /* @__PURE__ */ jsx("colgroup", { children: displayCols.map((col) => /* @__PURE__ */ jsx("col", { style: { width: colWidths[col.key] ?? col.width ?? 100 } }, col.key)) }),