@payglocal_ui/flux-ui 0.2.0 → 0.2.2

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/dist/index.d.cts CHANGED
@@ -777,10 +777,31 @@ interface DataTableProps<T> {
777
777
  label: string;
778
778
  onClick?: (row: T) => void;
779
779
  };
780
+ /**
781
+ * Custom action revealed on row hover — typically a `<Button>` or
782
+ * `<ButtonGroup>`, but any `ReactNode` is accepted. Pass a **function** to
783
+ * render per-row: it receives `(row, index)`, so the action always has the
784
+ * record for its row (e.g. to navigate or open a drawer for that row).
785
+ *
786
+ * It is not a real column: it floats as an overlay **pinned to the right edge
787
+ * of the viewport**, so it stays in view as the table scrolls horizontally
788
+ * (no scrolling to the end to reach it) while the last data column stays
789
+ * flush with nothing trailing it. Takes precedence over `rowCta`.
790
+ */
791
+ rowAction?: ReactNode | ((row: T, index: number) => ReactNode);
780
792
  /** Row / cell vertical rhythm and horizontal gutters */
781
793
  density?: DataTableDensity;
782
- /** `auto` lets columns breathe; `fixed` uses `colgroup` hints */
783
- tableLayout?: "auto" | "fixed";
794
+ /**
795
+ * Column-sizing strategy:
796
+ * - `fixed` — widths come only from `colgroup` hints (`width`/`minWidth`/
797
+ * `maxWidth`); content is ignored and overflow is clipped. Table fills 100%.
798
+ * - `auto` — columns size to content but the table still fills 100%, so any
799
+ * leftover space is distributed into the columns (they stretch).
800
+ * - `content` — columns size to their content's intrinsic width and the table
801
+ * shrinks to fit. Leftover space stays empty to the right of the last
802
+ * column; it scrolls horizontally only once content exceeds the container.
803
+ */
804
+ tableLayout?: "auto" | "fixed" | "content";
784
805
  theadClassName?: string;
785
806
  headerStyle?: DataTableHeaderStyle;
786
807
  /** Footer: paginated range vs simple `n items` */
@@ -793,7 +814,7 @@ interface DataTableProps<T> {
793
814
  /** With `density="compact"`, use tighter cell gutters (`pl-1.5 pr-2.5` vs `px-3`). Footer keeps normal horizontal padding. */
794
815
  snug?: boolean;
795
816
  }
796
- declare function DataTable<T>({ columns, data, isLoading, skeletonRows, emptyTitle, emptyDescription, pageSize, page: controlledPage, onPageChange, totalRows, className, rowKey, rowCta, density, tableLayout, theadClassName, headerStyle, footerSummary, footerCountLabels, snug, }: DataTableProps<T>): React$1.JSX.Element;
817
+ declare function DataTable<T>({ columns, data, isLoading, skeletonRows, emptyTitle, emptyDescription, pageSize, page: controlledPage, onPageChange, totalRows, className, rowKey, rowCta, rowAction, density, tableLayout, theadClassName, headerStyle, footerSummary, footerCountLabels, snug, }: DataTableProps<T>): React$1.JSX.Element;
797
818
 
798
819
  interface EmptyStateProps {
799
820
  icon?: LucideIcon;
package/dist/index.d.ts CHANGED
@@ -777,10 +777,31 @@ interface DataTableProps<T> {
777
777
  label: string;
778
778
  onClick?: (row: T) => void;
779
779
  };
780
+ /**
781
+ * Custom action revealed on row hover — typically a `<Button>` or
782
+ * `<ButtonGroup>`, but any `ReactNode` is accepted. Pass a **function** to
783
+ * render per-row: it receives `(row, index)`, so the action always has the
784
+ * record for its row (e.g. to navigate or open a drawer for that row).
785
+ *
786
+ * It is not a real column: it floats as an overlay **pinned to the right edge
787
+ * of the viewport**, so it stays in view as the table scrolls horizontally
788
+ * (no scrolling to the end to reach it) while the last data column stays
789
+ * flush with nothing trailing it. Takes precedence over `rowCta`.
790
+ */
791
+ rowAction?: ReactNode | ((row: T, index: number) => ReactNode);
780
792
  /** Row / cell vertical rhythm and horizontal gutters */
781
793
  density?: DataTableDensity;
782
- /** `auto` lets columns breathe; `fixed` uses `colgroup` hints */
783
- tableLayout?: "auto" | "fixed";
794
+ /**
795
+ * Column-sizing strategy:
796
+ * - `fixed` — widths come only from `colgroup` hints (`width`/`minWidth`/
797
+ * `maxWidth`); content is ignored and overflow is clipped. Table fills 100%.
798
+ * - `auto` — columns size to content but the table still fills 100%, so any
799
+ * leftover space is distributed into the columns (they stretch).
800
+ * - `content` — columns size to their content's intrinsic width and the table
801
+ * shrinks to fit. Leftover space stays empty to the right of the last
802
+ * column; it scrolls horizontally only once content exceeds the container.
803
+ */
804
+ tableLayout?: "auto" | "fixed" | "content";
784
805
  theadClassName?: string;
785
806
  headerStyle?: DataTableHeaderStyle;
786
807
  /** Footer: paginated range vs simple `n items` */
@@ -793,7 +814,7 @@ interface DataTableProps<T> {
793
814
  /** With `density="compact"`, use tighter cell gutters (`pl-1.5 pr-2.5` vs `px-3`). Footer keeps normal horizontal padding. */
794
815
  snug?: boolean;
795
816
  }
796
- declare function DataTable<T>({ columns, data, isLoading, skeletonRows, emptyTitle, emptyDescription, pageSize, page: controlledPage, onPageChange, totalRows, className, rowKey, rowCta, density, tableLayout, theadClassName, headerStyle, footerSummary, footerCountLabels, snug, }: DataTableProps<T>): React$1.JSX.Element;
817
+ declare function DataTable<T>({ columns, data, isLoading, skeletonRows, emptyTitle, emptyDescription, pageSize, page: controlledPage, onPageChange, totalRows, className, rowKey, rowCta, rowAction, density, tableLayout, theadClassName, headerStyle, footerSummary, footerCountLabels, snug, }: DataTableProps<T>): React$1.JSX.Element;
797
818
 
798
819
  interface EmptyStateProps {
799
820
  icon?: LucideIcon;
package/dist/index.js CHANGED
@@ -3605,6 +3605,7 @@ function DataTable({
3605
3605
  className,
3606
3606
  rowKey,
3607
3607
  rowCta,
3608
+ rowAction,
3608
3609
  density = "default",
3609
3610
  tableLayout = "fixed",
3610
3611
  theadClassName,
@@ -3617,9 +3618,18 @@ function DataTable({
3617
3618
  const [internalPage, setInternalPage] = useState6(1);
3618
3619
  const page = isControlled ? controlledPage : internalPage;
3619
3620
  const setPage = isControlled ? (p) => onPageChange?.(p) : (p) => setInternalPage(p);
3621
+ const hasAction = rowAction != null || rowCta != null;
3622
+ const hasSpacer = tableLayout === "content";
3620
3623
  const total = totalRows ?? data.length;
3621
3624
  const totalPages = Math.ceil(total / pageSize);
3622
3625
  const paginated = isControlled ? data : data.slice((page - 1) * pageSize, page * pageSize);
3626
+ const seenKeys = /* @__PURE__ */ new Map();
3627
+ const rowKeys = paginated.map((row) => {
3628
+ const base = rowKey(row);
3629
+ const seen = seenKeys.get(base) ?? 0;
3630
+ seenKeys.set(base, seen + 1);
3631
+ return seen === 0 ? base : `${base}__${seen}`;
3632
+ });
3623
3633
  const comfortable = density === "comfortable";
3624
3634
  const compact = density === "compact";
3625
3635
  const compactCellPad = compact ? snug ? "pl-1.5 pr-2.5 py-2.5" : "px-3 py-2.5" : "px-4 py-3.5";
@@ -3627,7 +3637,7 @@ function DataTable({
3627
3637
  const headPad = comfortable ? "px-5 py-4" : compactCellPad;
3628
3638
  const footerPad = comfortable ? "px-5 py-4" : compact ? "px-4 py-2.5" : "px-4 py-3.5";
3629
3639
  const headText = comfortable ? "text-[12px] font-medium text-muted-foreground tracking-normal" : compact ? "text-[11px] font-semibold text-muted-foreground" : "text-[11px] font-semibold text-foreground/75 dark:text-foreground/85";
3630
- const rowCtaColWidth = compact ? 108 : 130;
3640
+ const actionGutter = comfortable ? 20 : compact ? 12 : 16;
3631
3641
  return /* @__PURE__ */ jsxs30(
3632
3642
  "div",
3633
3643
  {
@@ -3650,7 +3660,13 @@ function DataTable({
3650
3660
  "table",
3651
3661
  {
3652
3662
  className: cn(tableLayout === "auto" && "min-w-[920px]"),
3653
- style: { tableLayout, width: "100%" },
3663
+ style: {
3664
+ // `content` uses the automatic algorithm but stays 100% wide; a
3665
+ // greedy spacer column (below) soaks up the slack so the data
3666
+ // columns collapse to their content width while the table fills.
3667
+ tableLayout: tableLayout === "fixed" ? "fixed" : "auto",
3668
+ width: "100%"
3669
+ },
3654
3670
  children: [
3655
3671
  tableLayout === "fixed" && /* @__PURE__ */ jsxs30("colgroup", { children: [
3656
3672
  columns.map((col) => /* @__PURE__ */ jsx49(
@@ -3664,7 +3680,7 @@ function DataTable({
3664
3680
  },
3665
3681
  col.key
3666
3682
  )),
3667
- rowCta ? /* @__PURE__ */ jsx49("col", { style: { width: rowCtaColWidth } }) : null
3683
+ hasAction ? /* @__PURE__ */ jsx49("col", { style: { width: 0 } }) : null
3668
3684
  ] }),
3669
3685
  /* @__PURE__ */ jsx49(
3670
3686
  "thead",
@@ -3696,7 +3712,8 @@ function DataTable({
3696
3712
  },
3697
3713
  col.key
3698
3714
  )),
3699
- rowCta ? /* @__PURE__ */ jsx49("th", { className: cn(headPad, "w-[1%]"), "aria-hidden": true }) : null
3715
+ hasSpacer ? /* @__PURE__ */ jsx49("th", { className: "w-full p-0", "aria-hidden": true }) : null,
3716
+ hasAction ? /* @__PURE__ */ jsx49("th", { className: "sticky right-0 z-[1] w-0 p-0", "aria-hidden": true }) : null
3700
3717
  ]
3701
3718
  }
3702
3719
  )
@@ -3705,12 +3722,12 @@ function DataTable({
3705
3722
  /* @__PURE__ */ jsx49("tbody", { children: isLoading ? Array.from({ length: skeletonRows }).map((_, i) => /* @__PURE__ */ jsx49(
3706
3723
  TableRowSkeleton,
3707
3724
  {
3708
- cols: columns.length + (rowCta ? 1 : 0),
3725
+ cols: columns.length,
3709
3726
  density,
3710
3727
  snug
3711
3728
  },
3712
3729
  i
3713
- )) : paginated.length === 0 ? /* @__PURE__ */ jsx49("tr", { children: /* @__PURE__ */ jsx49("td", { colSpan: columns.length + (rowCta ? 1 : 0), children: /* @__PURE__ */ jsx49(EmptyState, { title: emptyTitle, description: emptyDescription }) }) }) : paginated.map((row, i) => /* @__PURE__ */ jsxs30(
3730
+ )) : paginated.length === 0 ? /* @__PURE__ */ jsx49("tr", { children: /* @__PURE__ */ jsx49("td", { colSpan: columns.length + (hasSpacer ? 1 : 0) + (hasAction ? 1 : 0), children: /* @__PURE__ */ jsx49(EmptyState, { title: emptyTitle, description: emptyDescription }) }) }) : paginated.map((row, i) => /* @__PURE__ */ jsxs30(
3714
3731
  "tr",
3715
3732
  {
3716
3733
  className: cn(
@@ -3718,7 +3735,7 @@ function DataTable({
3718
3735
  comfortable && "min-h-[56px]",
3719
3736
  compact && "min-h-[44px]",
3720
3737
  "hover:bg-muted/40 dark:hover:bg-muted/25",
3721
- rowCta && "hover:shadow-[0_1px_0_rgba(0,0,0,0.04)] dark:hover:shadow-none"
3738
+ hasAction && "hover:shadow-[0_1px_0_rgba(0,0,0,0.04)] dark:hover:shadow-none"
3722
3739
  ),
3723
3740
  children: [
3724
3741
  columns.map((col) => /* @__PURE__ */ jsx49(
@@ -3742,31 +3759,36 @@ function DataTable({
3742
3759
  },
3743
3760
  col.key
3744
3761
  )),
3745
- rowCta ? /* @__PURE__ */ jsx49(
3746
- "td",
3747
- {
3748
- className: cn(
3749
- cellPad,
3750
- "text-left align-middle whitespace-nowrap",
3751
- comfortable ? "pl-2 pr-5" : compact ? snug ? "pl-1.5 pr-2" : "pl-1.5 pr-3" : "pl-3 pr-4"
3752
- ),
3753
- children: /* @__PURE__ */ jsx49(
3754
- "button",
3755
- {
3756
- type: "button",
3757
- onClick: () => rowCta.onClick?.(row),
3758
- className: cn(
3759
- "opacity-0 group-hover:opacity-100 transition-opacity duration-150 inline-flex items-center font-medium text-foreground bg-card rounded-lg border border-border hover:border-muted-foreground/50 whitespace-nowrap shadow-sm",
3760
- compact ? "px-2.5 py-1 text-[11px]" : "px-3 py-1.5 text-[12px]"
3761
- ),
3762
- children: rowCta.label
3763
- }
3764
- )
3765
- }
3762
+ hasSpacer ? /* @__PURE__ */ jsx49("td", { className: "p-0", "aria-hidden": true }) : null,
3763
+ hasAction ? (
3764
+ // Zero-width sticky cell pinned to the right edge of the
3765
+ // viewport. Its children are positioned absolutely so they
3766
+ // float over the row (out of the 0-width cell) — the action
3767
+ // stays in view while scrolling and nothing trails the last
3768
+ // data column. Everything is revealed on hover only.
3769
+ /* @__PURE__ */ jsx49("td", { className: "sticky right-0 z-[1] w-0 p-0 align-middle", children: /* @__PURE__ */ jsx49(
3770
+ "span",
3771
+ {
3772
+ className: "absolute top-1/2 -translate-y-1/2 z-[1] inline-flex items-center opacity-0 transition-opacity duration-150 group-hover:opacity-100 group-focus-within:opacity-100",
3773
+ style: { right: actionGutter },
3774
+ children: rowAction != null ? typeof rowAction === "function" ? rowAction(row, i) : rowAction : /* @__PURE__ */ jsx49(
3775
+ "button",
3776
+ {
3777
+ type: "button",
3778
+ onClick: () => rowCta?.onClick?.(row),
3779
+ className: cn(
3780
+ "inline-flex items-center font-medium text-foreground bg-card rounded-lg border border-border hover:border-muted-foreground/50 whitespace-nowrap shadow-sm",
3781
+ compact ? "px-2.5 py-1 text-[11px]" : "px-3 py-1.5 text-[12px]"
3782
+ ),
3783
+ children: rowCta?.label
3784
+ }
3785
+ )
3786
+ }
3787
+ ) })
3766
3788
  ) : null
3767
3789
  ]
3768
3790
  },
3769
- rowKey(row)
3791
+ rowKeys[i]
3770
3792
  )) })
3771
3793
  ]
3772
3794
  }