@payglocal_ui/flux-ui 0.2.4 → 0.2.5

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.cjs CHANGED
@@ -3144,7 +3144,10 @@ var DropdownMenuItem = React27.forwardRef(({ className, inset, ...props }, ref)
3144
3144
  {
3145
3145
  ref,
3146
3146
  className: cn(
3147
- "relative flex cursor-default select-none items-center gap-2 rounded-lg px-3 py-2.5 text-[15px] outline-none transition-colors",
3147
+ // 13px / tighter padding: a menu drops out of a toolbar button or a
3148
+ // compact table control, and at 15px with 10px vertical padding it read
3149
+ // as a larger, separate UI than the control that opened it.
3150
+ "relative flex cursor-default select-none items-center gap-2 rounded-lg px-2.5 py-1.5 text-[13px] outline-none transition-colors",
3148
3151
  "focus:bg-muted focus:text-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50",
3149
3152
  inset && "pl-8",
3150
3153
  className
@@ -3211,12 +3214,17 @@ var import_jsx_runtime38 = require("react/jsx-runtime");
3211
3214
  var Select = SelectPrimitive.Root;
3212
3215
  var SelectGroup = SelectPrimitive.Group;
3213
3216
  var SelectValue = SelectPrimitive.Value;
3214
- var SelectTrigger = React28.forwardRef(({ className, children, ...props }, ref) => /* @__PURE__ */ (0, import_jsx_runtime38.jsxs)(
3217
+ var selectTriggerSizes = {
3218
+ md: "h-11 min-h-11 gap-2.5 px-4 py-2 text-[15px]",
3219
+ sm: "h-7 min-h-7 w-auto gap-1 px-2 py-0 text-[12px]"
3220
+ };
3221
+ var SelectTrigger = React28.forwardRef(({ className, children, size = "md", ...props }, ref) => /* @__PURE__ */ (0, import_jsx_runtime38.jsxs)(
3215
3222
  SelectPrimitive.Trigger,
3216
3223
  {
3217
3224
  ref,
3218
3225
  className: cn(
3219
- "flex h-11 min-h-11 w-full items-center justify-between gap-2.5 rounded-lg border border-border bg-card px-4 py-2 text-[15px] text-foreground shadow-sm outline-none",
3226
+ "flex w-full items-center justify-between rounded-lg border border-border bg-card text-foreground shadow-sm outline-none",
3227
+ selectTriggerSizes[size],
3220
3228
  "ring-ring/50 focus-visible:ring-2 disabled:cursor-not-allowed disabled:opacity-50",
3221
3229
  "[&>span]:line-clamp-1",
3222
3230
  className
@@ -3863,7 +3871,9 @@ function DataTable({
3863
3871
  headerStyle = "surface",
3864
3872
  footerSummary = "range",
3865
3873
  footerCountLabels = { singular: "item", plural: "items" },
3866
- snug = false
3874
+ snug = false,
3875
+ expandable,
3876
+ footerLeading
3867
3877
  }) {
3868
3878
  const isControlled = controlledPage !== void 0;
3869
3879
  const [internalPage, setInternalPage] = (0, import_react8.useState)(1);
@@ -3871,6 +3881,19 @@ function DataTable({
3871
3881
  const setPage = isControlled ? (p) => onPageChange?.(p) : (p) => setInternalPage(p);
3872
3882
  const hasAction = rowAction != null || rowCta != null;
3873
3883
  const hasSpacer = tableLayout === "content";
3884
+ const [internalExpanded, setInternalExpanded] = (0, import_react8.useState)([]);
3885
+ const isExpandControlled = expandable?.expandedKeys !== void 0;
3886
+ const expandedKeys = isExpandControlled ? expandable.expandedKeys : internalExpanded;
3887
+ const hasExpand = expandable != null;
3888
+ const totalColSpan = columns.length + (hasExpand ? 1 : 0) + (hasSpacer ? 1 : 0) + (hasAction ? 1 : 0);
3889
+ const rowExpanded = (row, index) => hasExpand && (expandable.isExpandable?.(row, index) ?? true) && expandedKeys.includes(rowKeys[index]);
3890
+ const toggleExpanded = (key, row, index) => {
3891
+ const isOpen = expandedKeys.includes(key);
3892
+ const next = isOpen ? expandedKeys.filter((k) => k !== key) : [...expandedKeys, key];
3893
+ if (isExpandControlled) expandable.onExpandedChange?.(next);
3894
+ else setInternalExpanded(next);
3895
+ if (!isOpen) expandable.onExpand?.(row, index);
3896
+ };
3874
3897
  const total = totalRows ?? data.length;
3875
3898
  const totalPages = Math.ceil(total / pageSize);
3876
3899
  const paginated = isControlled ? data : data.slice((page - 1) * pageSize, page * pageSize);
@@ -3888,6 +3911,9 @@ function DataTable({
3888
3911
  const headPad = comfortable ? "px-5 py-4" : compactCellPad;
3889
3912
  const footerPad = comfortable ? "px-5 py-4" : compact ? "px-4 py-2.5" : "px-4 py-3.5";
3890
3913
  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";
3914
+ const cellPadLeft = comfortable ? 20 : compact ? snug ? 6 : 12 : 16;
3915
+ const expandIndent = 40 + cellPadLeft;
3916
+ const expandGuideLeft = cellPadLeft + 10;
3891
3917
  const actionGutter = comfortable ? 20 : compact ? 12 : 16;
3892
3918
  const ROW_CLICK_IGNORE = 'button, a, input, select, textarea, label, [role="button"], [role="link"], [role="checkbox"], [role="menuitem"], [role="menu"], [role="dialog"], [data-row-click-ignore]';
3893
3919
  const isRowClickTarget = (target, rowEl) => {
@@ -3926,6 +3952,7 @@ function DataTable({
3926
3952
  },
3927
3953
  children: [
3928
3954
  tableLayout === "fixed" && /* @__PURE__ */ (0, import_jsx_runtime49.jsxs)("colgroup", { children: [
3955
+ hasExpand ? /* @__PURE__ */ (0, import_jsx_runtime49.jsx)("col", { style: { width: 40 } }) : null,
3929
3956
  columns.map((col) => /* @__PURE__ */ (0, import_jsx_runtime49.jsx)(
3930
3957
  "col",
3931
3958
  {
@@ -3955,6 +3982,7 @@ function DataTable({
3955
3982
  headerStyle === "surface" ? "border-border" : "border-border/70"
3956
3983
  ),
3957
3984
  children: [
3985
+ hasExpand ? /* @__PURE__ */ (0, import_jsx_runtime49.jsx)("th", { className: cn(headPad, "w-10 p-0"), "aria-hidden": true }) : null,
3958
3986
  columns.map((col) => /* @__PURE__ */ (0, import_jsx_runtime49.jsx)(
3959
3987
  "th",
3960
3988
  {
@@ -3979,89 +4007,144 @@ function DataTable({
3979
4007
  /* @__PURE__ */ (0, import_jsx_runtime49.jsx)("tbody", { children: isLoading ? Array.from({ length: skeletonRows }).map((_, i) => /* @__PURE__ */ (0, import_jsx_runtime49.jsx)(
3980
4008
  TableRowSkeleton,
3981
4009
  {
3982
- cols: columns.length,
4010
+ cols: columns.length + (hasExpand ? 1 : 0),
3983
4011
  density,
3984
4012
  snug
3985
4013
  },
3986
4014
  i
3987
- )) : paginated.length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime49.jsx)("tr", { children: /* @__PURE__ */ (0, import_jsx_runtime49.jsx)("td", { colSpan: columns.length + (hasSpacer ? 1 : 0) + (hasAction ? 1 : 0), children: /* @__PURE__ */ (0, import_jsx_runtime49.jsx)(EmptyState, { title: emptyTitle, description: emptyDescription }) }) }) : paginated.map((row, i) => /* @__PURE__ */ (0, import_jsx_runtime49.jsxs)(
3988
- "tr",
3989
- {
3990
- className: cn(
3991
- "group transition-colors duration-150 border-b border-border/60 last:border-b-0",
3992
- comfortable && "min-h-[56px]",
3993
- compact && "min-h-[44px]",
3994
- "hover:bg-muted/40 dark:hover:bg-muted/25",
3995
- hasAction && "hover:shadow-[0_1px_0_rgba(0,0,0,0.04)] dark:hover:shadow-none",
3996
- // Clickable rows read as clickable, and show a focus ring
3997
- // when reached by keyboard. `focus-visible` only, so a
3998
- // mouse click does not leave a ring behind on the row.
3999
- onRowClick && "cursor-pointer focus-visible:outline-none focus-visible:bg-muted/40 dark:focus-visible:bg-muted/25"
4000
- ),
4001
- tabIndex: onRowClick ? 0 : void 0,
4002
- onClick: onRowClick ? (e) => {
4003
- if (!isRowClickTarget(e.target, e.currentTarget)) return;
4004
- onRowClick(row, i);
4005
- } : void 0,
4006
- onKeyDown: onRowClick ? (e) => {
4007
- if (e.key !== "Enter" && e.key !== " ") return;
4008
- if (e.target !== e.currentTarget) return;
4009
- e.preventDefault();
4010
- onRowClick(row, i);
4011
- } : void 0,
4012
- children: [
4013
- columns.map((col) => /* @__PURE__ */ (0, import_jsx_runtime49.jsx)(
4014
- "td",
4015
- {
4016
- className: cn(
4017
- cellPad,
4018
- "align-middle",
4019
- comfortable ? cn(
4020
- "text-[13px] leading-snug",
4021
- !col.wrap && "whitespace-nowrap"
4022
- ) : compact ? cn(
4023
- "text-[13px] leading-tight",
4024
- !col.wrap && "whitespace-nowrap",
4025
- "overflow-hidden"
4026
- ) : "whitespace-nowrap overflow-hidden",
4027
- col.align === "right" ? "text-right" : col.align === "center" ? "text-center" : "text-left",
4028
- col.cellClassName
4029
- ),
4030
- children: col.render(row, i)
4031
- },
4032
- col.key
4033
- )),
4034
- hasSpacer ? /* @__PURE__ */ (0, import_jsx_runtime49.jsx)("td", { className: "p-0", "aria-hidden": true }) : null,
4035
- hasAction ? (
4036
- // Zero-width sticky cell pinned to the right edge of the
4037
- // viewport. Its children are positioned absolutely so they
4038
- // float over the row (out of the 0-width cell) — the action
4039
- // stays in view while scrolling and nothing trails the last
4040
- // data column. Everything is revealed on hover only.
4041
- /* @__PURE__ */ (0, import_jsx_runtime49.jsx)("td", { className: "sticky right-0 z-[1] w-0 p-0 align-middle", children: /* @__PURE__ */ (0, import_jsx_runtime49.jsx)(
4042
- "span",
4015
+ )) : paginated.length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime49.jsx)("tr", { children: /* @__PURE__ */ (0, import_jsx_runtime49.jsx)("td", { colSpan: totalColSpan, children: /* @__PURE__ */ (0, import_jsx_runtime49.jsx)(EmptyState, { title: emptyTitle, description: emptyDescription }) }) }) : paginated.flatMap((row, i) => [
4016
+ /* @__PURE__ */ (0, import_jsx_runtime49.jsxs)(
4017
+ "tr",
4018
+ {
4019
+ className: cn(
4020
+ "group transition-colors duration-150 border-b border-border/60 last:border-b-0",
4021
+ comfortable && "min-h-[56px]",
4022
+ compact && "min-h-[44px]",
4023
+ "hover:bg-muted/40 dark:hover:bg-muted/25",
4024
+ hasAction && "hover:shadow-[0_1px_0_rgba(0,0,0,0.04)] dark:hover:shadow-none",
4025
+ // Clickable rows read as clickable, and show a focus ring
4026
+ // when reached by keyboard. `focus-visible` only, so a
4027
+ // mouse click does not leave a ring behind on the row.
4028
+ onRowClick && "cursor-pointer focus-visible:outline-none focus-visible:bg-muted/40 dark:focus-visible:bg-muted/25",
4029
+ // An open row takes its panel's background and drops the
4030
+ // divider beneath it, so the row and its detail read as one
4031
+ // block. Hover is pinned to the same value, or moving the
4032
+ // mouse over an open row would make it flicker away from
4033
+ // the panel it belongs to.
4034
+ rowExpanded(row, i) && "border-b-0 bg-muted/40 hover:bg-muted/40 dark:bg-muted/25 dark:hover:bg-muted/25"
4035
+ ),
4036
+ tabIndex: onRowClick ? 0 : void 0,
4037
+ onClick: onRowClick ? (e) => {
4038
+ if (!isRowClickTarget(e.target, e.currentTarget)) return;
4039
+ onRowClick(row, i);
4040
+ } : void 0,
4041
+ onKeyDown: onRowClick ? (e) => {
4042
+ if (e.key !== "Enter" && e.key !== " ") return;
4043
+ if (e.target !== e.currentTarget) return;
4044
+ e.preventDefault();
4045
+ onRowClick(row, i);
4046
+ } : void 0,
4047
+ children: [
4048
+ hasExpand ? /* @__PURE__ */ (0, import_jsx_runtime49.jsx)("td", { className: cn(cellPad, "w-10 align-middle"), children: expandable.isExpandable?.(row, i) ?? true ? /* @__PURE__ */ (0, import_jsx_runtime49.jsx)(
4049
+ "button",
4043
4050
  {
4044
- 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",
4045
- style: { right: actionGutter },
4046
- children: rowAction != null ? typeof rowAction === "function" ? rowAction(row, i) : rowAction : /* @__PURE__ */ (0, import_jsx_runtime49.jsx)(
4047
- "button",
4051
+ type: "button",
4052
+ "aria-expanded": rowExpanded(row, i),
4053
+ "aria-label": expandable.toggleLabel ?? "Toggle row details",
4054
+ onClick: () => toggleExpanded(rowKeys[i], row, i),
4055
+ className: "inline-flex h-5 w-5 items-center justify-center rounded-md text-muted-foreground transition-colors hover:bg-muted hover:text-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring",
4056
+ children: /* @__PURE__ */ (0, import_jsx_runtime49.jsx)(
4057
+ import_lucide_react25.ChevronDown,
4048
4058
  {
4049
- type: "button",
4050
- onClick: () => rowCta?.onClick?.(row),
4051
4059
  className: cn(
4052
- "inline-flex items-center font-medium text-foreground bg-card rounded-lg border border-border hover:border-muted-foreground/50 whitespace-nowrap shadow-sm",
4053
- compact ? "px-2.5 py-1 text-[11px]" : "px-3 py-1.5 text-[12px]"
4054
- ),
4055
- children: rowCta?.label
4060
+ "h-3.5 w-3.5 transition-transform duration-150",
4061
+ rowExpanded(row, i) && "rotate-180"
4062
+ )
4056
4063
  }
4057
4064
  )
4058
4065
  }
4059
- ) })
4060
- ) : null
4061
- ]
4062
- },
4063
- rowKeys[i]
4064
- )) })
4066
+ ) : null }) : null,
4067
+ columns.map((col) => /* @__PURE__ */ (0, import_jsx_runtime49.jsx)(
4068
+ "td",
4069
+ {
4070
+ className: cn(
4071
+ cellPad,
4072
+ "align-middle",
4073
+ comfortable ? cn(
4074
+ "text-[13px] leading-snug",
4075
+ !col.wrap && "whitespace-nowrap"
4076
+ ) : compact ? cn(
4077
+ "text-[13px] leading-tight",
4078
+ !col.wrap && "whitespace-nowrap",
4079
+ "overflow-hidden"
4080
+ ) : "whitespace-nowrap overflow-hidden",
4081
+ col.align === "right" ? "text-right" : col.align === "center" ? "text-center" : "text-left",
4082
+ col.cellClassName
4083
+ ),
4084
+ children: col.render(row, i)
4085
+ },
4086
+ col.key
4087
+ )),
4088
+ hasSpacer ? /* @__PURE__ */ (0, import_jsx_runtime49.jsx)("td", { className: "p-0", "aria-hidden": true }) : null,
4089
+ hasAction ? (
4090
+ // Zero-width sticky cell pinned to the right edge of the
4091
+ // viewport. Its children are positioned absolutely so they
4092
+ // float over the row (out of the 0-width cell) — the action
4093
+ // stays in view while scrolling and nothing trails the last
4094
+ // data column. Everything is revealed on hover only.
4095
+ /* @__PURE__ */ (0, import_jsx_runtime49.jsx)("td", { className: "sticky right-0 z-[1] w-0 p-0 align-middle", children: /* @__PURE__ */ (0, import_jsx_runtime49.jsx)(
4096
+ "span",
4097
+ {
4098
+ 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",
4099
+ style: { right: actionGutter },
4100
+ children: rowAction != null ? typeof rowAction === "function" ? rowAction(row, i) : rowAction : /* @__PURE__ */ (0, import_jsx_runtime49.jsx)(
4101
+ "button",
4102
+ {
4103
+ type: "button",
4104
+ onClick: () => rowCta?.onClick?.(row),
4105
+ className: cn(
4106
+ "inline-flex items-center font-medium text-foreground bg-card rounded-lg border border-border hover:border-muted-foreground/50 whitespace-nowrap shadow-sm",
4107
+ compact ? "px-2.5 py-1 text-[11px]" : "px-3 py-1.5 text-[12px]"
4108
+ ),
4109
+ children: rowCta?.label
4110
+ }
4111
+ )
4112
+ }
4113
+ ) })
4114
+ ) : null
4115
+ ]
4116
+ },
4117
+ rowKeys[i]
4118
+ ),
4119
+ // The panel spans every column, including the toggle, spacer and
4120
+ // action cells, so it reads as one band under its row rather
4121
+ // than as a cell inside the grid.
4122
+ rowExpanded(row, i) ? /* @__PURE__ */ (0, import_jsx_runtime49.jsx)(
4123
+ "tr",
4124
+ {
4125
+ className: "border-b border-border/60 bg-muted/40 last:border-b-0 dark:bg-muted/25",
4126
+ children: /* @__PURE__ */ (0, import_jsx_runtime49.jsx)("td", { colSpan: totalColSpan, className: "p-0 align-top", children: /* @__PURE__ */ (0, import_jsx_runtime49.jsxs)(
4127
+ "div",
4128
+ {
4129
+ className: "relative",
4130
+ style: { paddingLeft: expandIndent, paddingRight: cellPadLeft },
4131
+ children: [
4132
+ /* @__PURE__ */ (0, import_jsx_runtime49.jsx)(
4133
+ "span",
4134
+ {
4135
+ "aria-hidden": true,
4136
+ className: "absolute top-0 bottom-4 w-px bg-border",
4137
+ style: { left: expandGuideLeft }
4138
+ }
4139
+ ),
4140
+ expandable.render(row, i)
4141
+ ]
4142
+ }
4143
+ ) })
4144
+ },
4145
+ `${rowKeys[i]}__panel`
4146
+ ) : null
4147
+ ]) })
4065
4148
  ]
4066
4149
  }
4067
4150
  )
@@ -4073,27 +4156,30 @@ function DataTable({
4073
4156
  className: cn(
4074
4157
  "flex items-center gap-4 flex-wrap border-t border-border",
4075
4158
  footerPad,
4076
- footerSummary === "count" && totalPages <= 1 ? "justify-start" : "justify-between"
4159
+ footerSummary === "count" && totalPages <= 1 && !footerLeading ? "justify-start" : "justify-between"
4077
4160
  ),
4078
4161
  children: [
4079
- footerSummary === "count" ? /* @__PURE__ */ (0, import_jsx_runtime49.jsxs)("span", { className: "text-[12px] text-muted-foreground tabular-nums", children: [
4080
- /* @__PURE__ */ (0, import_jsx_runtime49.jsx)("span", { className: "font-medium text-foreground", children: total }),
4081
- " ",
4082
- total === 1 ? footerCountLabels.singular : footerCountLabels.plural
4083
- ] }) : /* @__PURE__ */ (0, import_jsx_runtime49.jsxs)("span", { className: "text-[12px] text-muted-foreground tabular-nums", children: [
4084
- "Showing",
4085
- " ",
4086
- /* @__PURE__ */ (0, import_jsx_runtime49.jsxs)("span", { className: "text-foreground font-medium", children: [
4087
- Math.min((page - 1) * pageSize + 1, total),
4088
- "\u2013",
4089
- Math.min(page * pageSize, total)
4090
- ] }),
4091
- " ",
4092
- "of",
4093
- " ",
4094
- /* @__PURE__ */ (0, import_jsx_runtime49.jsx)("span", { className: "text-foreground font-medium", children: total.toLocaleString() }),
4095
- " ",
4096
- total !== 1 ? "results" : "result"
4162
+ /* @__PURE__ */ (0, import_jsx_runtime49.jsxs)("div", { className: "flex items-center gap-3", children: [
4163
+ footerLeading,
4164
+ footerSummary === "count" ? /* @__PURE__ */ (0, import_jsx_runtime49.jsxs)("span", { className: "text-[12px] text-muted-foreground tabular-nums", children: [
4165
+ /* @__PURE__ */ (0, import_jsx_runtime49.jsx)("span", { className: "font-medium text-foreground", children: total }),
4166
+ " ",
4167
+ total === 1 ? footerCountLabels.singular : footerCountLabels.plural
4168
+ ] }) : /* @__PURE__ */ (0, import_jsx_runtime49.jsxs)("span", { className: "text-[12px] text-muted-foreground tabular-nums", children: [
4169
+ "Showing",
4170
+ " ",
4171
+ /* @__PURE__ */ (0, import_jsx_runtime49.jsxs)("span", { className: "text-foreground font-medium", children: [
4172
+ Math.min((page - 1) * pageSize + 1, total),
4173
+ "\u2013",
4174
+ Math.min(page * pageSize, total)
4175
+ ] }),
4176
+ " ",
4177
+ "of",
4178
+ " ",
4179
+ /* @__PURE__ */ (0, import_jsx_runtime49.jsx)("span", { className: "text-foreground font-medium", children: total.toLocaleString() }),
4180
+ " ",
4181
+ total !== 1 ? "results" : "result"
4182
+ ] })
4097
4183
  ] }),
4098
4184
  (footerSummary === "range" || footerSummary === "count") && totalPages > 1 && /* @__PURE__ */ (0, import_jsx_runtime49.jsxs)("div", { className: "flex items-center gap-1", children: [
4099
4185
  /* @__PURE__ */ (0, import_jsx_runtime49.jsx)(