framepexls-ui-lib 1.16.0 → 2.0.0

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.
@@ -11,7 +11,7 @@ type CheckboxProps = Omit<React__default.InputHTMLAttributes<HTMLInputElement>,
11
11
  className?: string;
12
12
  inputClassName?: string;
13
13
  };
14
- declare const Checkbox: React__default.ForwardRefExoticComponent<Omit<React__default.InputHTMLAttributes<HTMLInputElement>, "size" | "type"> & {
14
+ declare const Checkbox: React__default.ForwardRefExoticComponent<Omit<React__default.InputHTMLAttributes<HTMLInputElement>, "type" | "size"> & {
15
15
  label?: React__default.ReactNode;
16
16
  description?: React__default.ReactNode;
17
17
  error?: boolean;
@@ -11,7 +11,7 @@ type CheckboxProps = Omit<React__default.InputHTMLAttributes<HTMLInputElement>,
11
11
  className?: string;
12
12
  inputClassName?: string;
13
13
  };
14
- declare const Checkbox: React__default.ForwardRefExoticComponent<Omit<React__default.InputHTMLAttributes<HTMLInputElement>, "size" | "type"> & {
14
+ declare const Checkbox: React__default.ForwardRefExoticComponent<Omit<React__default.InputHTMLAttributes<HTMLInputElement>, "type" | "size"> & {
15
15
  label?: React__default.ReactNode;
16
16
  description?: React__default.ReactNode;
17
17
  error?: boolean;
@@ -54,6 +54,8 @@ type Props<Row, K extends string> = {
54
54
  columns: Array<DataTableColumn<Row, K, any>>;
55
55
  getRowId: (row: Row) => string;
56
56
  className?: string;
57
+ ariaLabel?: string;
58
+ loading?: boolean;
57
59
  dense?: boolean;
58
60
  stickyHeader?: boolean;
59
61
  striped?: boolean;
@@ -90,6 +92,6 @@ type Props<Row, K extends string> = {
90
92
  previous: unknown;
91
93
  }) => void;
92
94
  };
93
- declare function DataTable<Row, K extends string>({ rows, columns, getRowId, className, dense, stickyHeader, striped, hoverable, empty, resizableColumns, persistKey, animateSort, quickEdit, edicionRapida, rowClassName, sort, defaultSort, onSortChange, manualSort, onRowsChange, onCellCommit, onCellError, }: Props<Row, K>): react_jsx_runtime.JSX.Element;
95
+ declare function DataTable<Row, K extends string>({ rows, columns, getRowId, className, ariaLabel, loading, dense, stickyHeader, striped, hoverable, empty, resizableColumns, persistKey, animateSort, quickEdit, edicionRapida, rowClassName, sort, defaultSort, onSortChange, manualSort, onRowsChange, onCellCommit, onCellError, }: Props<Row, K>): react_jsx_runtime.JSX.Element;
94
96
 
95
97
  export { type DataTableColumn, type DataTableEditorArgs, type DataTableSort, DataTable as default };
@@ -54,6 +54,8 @@ type Props<Row, K extends string> = {
54
54
  columns: Array<DataTableColumn<Row, K, any>>;
55
55
  getRowId: (row: Row) => string;
56
56
  className?: string;
57
+ ariaLabel?: string;
58
+ loading?: boolean;
57
59
  dense?: boolean;
58
60
  stickyHeader?: boolean;
59
61
  striped?: boolean;
@@ -90,6 +92,6 @@ type Props<Row, K extends string> = {
90
92
  previous: unknown;
91
93
  }) => void;
92
94
  };
93
- declare function DataTable<Row, K extends string>({ rows, columns, getRowId, className, dense, stickyHeader, striped, hoverable, empty, resizableColumns, persistKey, animateSort, quickEdit, edicionRapida, rowClassName, sort, defaultSort, onSortChange, manualSort, onRowsChange, onCellCommit, onCellError, }: Props<Row, K>): react_jsx_runtime.JSX.Element;
95
+ declare function DataTable<Row, K extends string>({ rows, columns, getRowId, className, ariaLabel, loading, dense, stickyHeader, striped, hoverable, empty, resizableColumns, persistKey, animateSort, quickEdit, edicionRapida, rowClassName, sort, defaultSort, onSortChange, manualSort, onRowsChange, onCellCommit, onCellError, }: Props<Row, K>): react_jsx_runtime.JSX.Element;
94
96
 
95
97
  export { type DataTableColumn, type DataTableEditorArgs, type DataTableSort, DataTable as default };
package/dist/DataTable.js CHANGED
@@ -113,6 +113,8 @@ function DataTable({
113
113
  columns,
114
114
  getRowId,
115
115
  className,
116
+ ariaLabel,
117
+ loading = false,
116
118
  dense = false,
117
119
  stickyHeader = false,
118
120
  striped = false,
@@ -226,6 +228,7 @@ function DataTable({
226
228
  return withIdx.map((x) => x.row);
227
229
  }, [activeSort, collator, columns, localRows]);
228
230
  const displayRows = manualSort ? localRows : sortedRows;
231
+ const emptyState = loading ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "p-8", children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "text-sm text-[var(--muted)]", children: "Cargando..." }) }) : /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "p-8", children: empty != null ? empty : /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "text-sm text-[var(--muted)]", children: "Sin resultados." }) });
229
232
  const resizingRef = import_react.default.useRef(null);
230
233
  const onGripDown = (colKey, e) => {
231
234
  var _a2, _b2, _c2, _d;
@@ -594,7 +597,7 @@ function DataTable({
594
597
  );
595
598
  }) });
596
599
  return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: cx("w-full", className), children: [
597
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_Table.default, { dense, className: cx(resizableColumns ? "table-fixed" : "table-auto"), children: [
600
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_Table.default, { "aria-label": ariaLabel, dense, className: cx(resizableColumns ? "table-fixed" : "table-auto"), children: [
598
601
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)("colgroup", { children: columns.map((c) => {
599
602
  const rawW = typeof widths[c.key] === "number" ? widths[c.key] : c.width;
600
603
  const w = typeof rawW === "number" ? Math.round(clampColumnWidth(c, rawW)) : rawW;
@@ -616,7 +619,7 @@ function DataTable({
616
619
  children: headerRow
617
620
  }
618
621
  ),
619
- animateSort ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_Table.AnimatedBody, { children: displayRows.length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("tr", { children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_Table.Td, { colSpan: columns.length, children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "p-8", children: empty != null ? empty : /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "text-sm text-[var(--muted)]", children: "Sin resultados." }) }) }) }) : displayRows.map((row, idx) => {
622
+ animateSort ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_Table.AnimatedBody, { children: displayRows.length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("tr", { children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_Table.Td, { colSpan: columns.length, children: emptyState }) }) : displayRows.map((row, idx) => {
620
623
  var _a2;
621
624
  const rowId = getRowId(row);
622
625
  return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
@@ -634,7 +637,7 @@ function DataTable({
634
637
  },
635
638
  rowId
636
639
  );
637
- }) }) : /* @__PURE__ */ (0, import_jsx_runtime.jsx)("tbody", { children: displayRows.length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("tr", { children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_Table.Td, { colSpan: columns.length, children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "p-8", children: empty != null ? empty : /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "text-sm text-[var(--muted)]", children: "Sin resultados." }) }) }) }) : displayRows.map((row, idx) => {
640
+ }) }) : /* @__PURE__ */ (0, import_jsx_runtime.jsx)("tbody", { children: displayRows.length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("tr", { children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_Table.Td, { colSpan: columns.length, children: emptyState }) }) : displayRows.map((row, idx) => {
638
641
  var _a2;
639
642
  const rowId = getRowId(row);
640
643
  return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
@@ -80,6 +80,8 @@ function DataTable({
80
80
  columns,
81
81
  getRowId,
82
82
  className,
83
+ ariaLabel,
84
+ loading = false,
83
85
  dense = false,
84
86
  stickyHeader = false,
85
87
  striped = false,
@@ -193,6 +195,7 @@ function DataTable({
193
195
  return withIdx.map((x) => x.row);
194
196
  }, [activeSort, collator, columns, localRows]);
195
197
  const displayRows = manualSort ? localRows : sortedRows;
198
+ const emptyState = loading ? /* @__PURE__ */ jsx("div", { className: "p-8", children: /* @__PURE__ */ jsx("div", { className: "text-sm text-[var(--muted)]", children: "Cargando..." }) }) : /* @__PURE__ */ jsx("div", { className: "p-8", children: empty != null ? empty : /* @__PURE__ */ jsx("div", { className: "text-sm text-[var(--muted)]", children: "Sin resultados." }) });
196
199
  const resizingRef = React.useRef(null);
197
200
  const onGripDown = (colKey, e) => {
198
201
  var _a2, _b2, _c2, _d;
@@ -561,7 +564,7 @@ function DataTable({
561
564
  );
562
565
  }) });
563
566
  return /* @__PURE__ */ jsxs("div", { className: cx("w-full", className), children: [
564
- /* @__PURE__ */ jsxs(Table, { dense, className: cx(resizableColumns ? "table-fixed" : "table-auto"), children: [
567
+ /* @__PURE__ */ jsxs(Table, { "aria-label": ariaLabel, dense, className: cx(resizableColumns ? "table-fixed" : "table-auto"), children: [
565
568
  /* @__PURE__ */ jsx("colgroup", { children: columns.map((c) => {
566
569
  const rawW = typeof widths[c.key] === "number" ? widths[c.key] : c.width;
567
570
  const w = typeof rawW === "number" ? Math.round(clampColumnWidth(c, rawW)) : rawW;
@@ -583,7 +586,7 @@ function DataTable({
583
586
  children: headerRow
584
587
  }
585
588
  ),
586
- animateSort ? /* @__PURE__ */ jsx(AnimatedBody, { children: displayRows.length === 0 ? /* @__PURE__ */ jsx("tr", { children: /* @__PURE__ */ jsx(Td, { colSpan: columns.length, children: /* @__PURE__ */ jsx("div", { className: "p-8", children: empty != null ? empty : /* @__PURE__ */ jsx("div", { className: "text-sm text-[var(--muted)]", children: "Sin resultados." }) }) }) }) : displayRows.map((row, idx) => {
589
+ animateSort ? /* @__PURE__ */ jsx(AnimatedBody, { children: displayRows.length === 0 ? /* @__PURE__ */ jsx("tr", { children: /* @__PURE__ */ jsx(Td, { colSpan: columns.length, children: emptyState }) }) : displayRows.map((row, idx) => {
587
590
  var _a2;
588
591
  const rowId = getRowId(row);
589
592
  return /* @__PURE__ */ jsx(
@@ -601,7 +604,7 @@ function DataTable({
601
604
  },
602
605
  rowId
603
606
  );
604
- }) }) : /* @__PURE__ */ jsx("tbody", { children: displayRows.length === 0 ? /* @__PURE__ */ jsx("tr", { children: /* @__PURE__ */ jsx(Td, { colSpan: columns.length, children: /* @__PURE__ */ jsx("div", { className: "p-8", children: empty != null ? empty : /* @__PURE__ */ jsx("div", { className: "text-sm text-[var(--muted)]", children: "Sin resultados." }) }) }) }) : displayRows.map((row, idx) => {
607
+ }) }) : /* @__PURE__ */ jsx("tbody", { children: displayRows.length === 0 ? /* @__PURE__ */ jsx("tr", { children: /* @__PURE__ */ jsx(Td, { colSpan: columns.length, children: emptyState }) }) : displayRows.map((row, idx) => {
605
608
  var _a2;
606
609
  const rowId = getRowId(row);
607
610
  return /* @__PURE__ */ jsx(
@@ -76,12 +76,13 @@ type PageScaffoldProps = {
76
76
  loading?: boolean;
77
77
  loadingMinDurationMs?: number;
78
78
  loadingBody?: React__default.ReactNode;
79
+ loadingBehavior?: "auto" | "full" | "content";
79
80
  empty?: boolean;
80
81
  emptyState?: React__default.ReactElement | EmptyStateProps | string | number;
81
82
  emptyStateContainer?: "card" | "none";
82
83
  className?: string;
83
84
  children?: React__default.ReactNode;
84
85
  };
85
- declare function PageScaffold({ tools, backgroundImage, backgroundMode, backgroundImageOpacity, backgroundImageBlur, backgroundImageClassName, breadcrumb, kpis, kpiClassName, kpiColumnsClassName, search, filters, sort, columns, tabs, actions, primaryAction, top, belowToolbar, loading, loadingMinDurationMs, loadingBody, empty, emptyState, emptyStateContainer, className, children, }: PageScaffoldProps): react_jsx_runtime.JSX.Element;
86
+ declare function PageScaffold({ tools, backgroundImage, backgroundMode, backgroundImageOpacity, backgroundImageBlur, backgroundImageClassName, breadcrumb, kpis, kpiClassName, kpiColumnsClassName, search, filters, sort, columns, tabs, actions, primaryAction, top, belowToolbar, loading, loadingMinDurationMs, loadingBody, loadingBehavior, empty, emptyState, emptyStateContainer, className, children, }: PageScaffoldProps): react_jsx_runtime.JSX.Element;
86
87
 
87
88
  export { type PageKpi, type PageKpiItem, type PageScaffoldPrimaryAction, type PageScaffoldProps, type PageScaffoldSearch, PageScaffold as default };
@@ -76,12 +76,13 @@ type PageScaffoldProps = {
76
76
  loading?: boolean;
77
77
  loadingMinDurationMs?: number;
78
78
  loadingBody?: React__default.ReactNode;
79
+ loadingBehavior?: "auto" | "full" | "content";
79
80
  empty?: boolean;
80
81
  emptyState?: React__default.ReactElement | EmptyStateProps | string | number;
81
82
  emptyStateContainer?: "card" | "none";
82
83
  className?: string;
83
84
  children?: React__default.ReactNode;
84
85
  };
85
- declare function PageScaffold({ tools, backgroundImage, backgroundMode, backgroundImageOpacity, backgroundImageBlur, backgroundImageClassName, breadcrumb, kpis, kpiClassName, kpiColumnsClassName, search, filters, sort, columns, tabs, actions, primaryAction, top, belowToolbar, loading, loadingMinDurationMs, loadingBody, empty, emptyState, emptyStateContainer, className, children, }: PageScaffoldProps): react_jsx_runtime.JSX.Element;
86
+ declare function PageScaffold({ tools, backgroundImage, backgroundMode, backgroundImageOpacity, backgroundImageBlur, backgroundImageClassName, breadcrumb, kpis, kpiClassName, kpiColumnsClassName, search, filters, sort, columns, tabs, actions, primaryAction, top, belowToolbar, loading, loadingMinDurationMs, loadingBody, loadingBehavior, empty, emptyState, emptyStateContainer, className, children, }: PageScaffoldProps): react_jsx_runtime.JSX.Element;
86
87
 
87
88
  export { type PageKpi, type PageKpiItem, type PageScaffoldPrimaryAction, type PageScaffoldProps, type PageScaffoldSearch, PageScaffold as default };
@@ -68,6 +68,7 @@ function PageScaffold({
68
68
  loading = false,
69
69
  loadingMinDurationMs = import_skeleton.DEFAULT_SKELETON_MIN_DURATION_MS,
70
70
  loadingBody,
71
+ loadingBehavior = "auto",
71
72
  empty = false,
72
73
  emptyState,
73
74
  emptyStateContainer = "card",
@@ -76,10 +77,13 @@ function PageScaffold({
76
77
  }) {
77
78
  var _a, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k, _l, _m, _n, _o, _p, _q, _r, _s, _t, _u, _v, _w, _x, _y, _z, _A, _B, _C, _D, _E;
78
79
  const showLoading = (0, import_skeleton.useMinVisible)(Boolean(loading), loadingMinDurationMs);
80
+ const resolvedLoadingBehavior = loadingBehavior === "auto" ? search ? "content" : "full" : loadingBehavior;
81
+ const showFullLoading = showLoading && resolvedLoadingBehavior === "full";
82
+ const showContentLoading = showLoading && resolvedLoadingBehavior === "content";
79
83
  const [mounted, setMounted] = import_react.default.useState(false);
80
84
  import_react.default.useEffect(() => setMounted(true), []);
81
85
  const primaryActionShortcut = (_a = primaryAction == null ? void 0 : primaryAction.shortcut) == null ? void 0 : _a.trim();
82
- const primaryActionDisabled = Boolean(primaryAction == null ? void 0 : primaryAction.disabled) || Boolean(primaryAction == null ? void 0 : primaryAction.loading) || showLoading;
86
+ const primaryActionDisabled = Boolean(primaryAction == null ? void 0 : primaryAction.disabled) || Boolean(primaryAction == null ? void 0 : primaryAction.loading) || showFullLoading;
83
87
  const primaryActionOnClick = primaryAction == null ? void 0 : primaryAction.onClick;
84
88
  import_react.default.useEffect(() => {
85
89
  if (!primaryActionShortcut) return;
@@ -109,7 +113,7 @@ function PageScaffold({
109
113
  search && ((_g = tools == null ? void 0 : tools.search) != null ? _g : true) || filters && ((_h = tools == null ? void 0 : tools.filters) != null ? _h : true) || sort && ((_i = tools == null ? void 0 : tools.sort) != null ? _i : true) || columns && ((_j = tools == null ? void 0 : tools.columns) != null ? _j : true) || tabs && ((_k = tools == null ? void 0 : tools.tabs) != null ? _k : true) || primaryAction && ((_l = tools == null ? void 0 : tools.primaryAction) != null ? _l : true) || actions && ((_m = tools == null ? void 0 : tools.actions) != null ? _m : true)
110
114
  );
111
115
  const filtersProps = (_n = filters == null ? void 0 : filters.props) != null ? _n : {};
112
- const showEmptyState = Boolean(empty) && !showLoading && ((_o = tools == null ? void 0 : tools.emptyState) != null ? _o : true);
116
+ const showEmptyState = Boolean(empty) && !showFullLoading && !showContentLoading && ((_o = tools == null ? void 0 : tools.emptyState) != null ? _o : true);
113
117
  const resolvedEmptyState = import_react.default.useMemo(() => {
114
118
  if (!emptyState) return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_EmptyState.default, {});
115
119
  if (import_react.default.isValidElement(emptyState)) return emptyState;
@@ -122,8 +126,8 @@ function PageScaffold({
122
126
  {
123
127
  variant: (_q = primaryAction.variant) != null ? _q : "primary",
124
128
  size: (_r = primaryAction.size) != null ? _r : "md",
125
- disabled: primaryAction.disabled || showLoading,
126
- loading: Boolean(primaryAction.loading) || showLoading,
129
+ disabled: primaryAction.disabled || showFullLoading,
130
+ loading: Boolean(primaryAction.loading) || showFullLoading,
127
131
  id: primaryAction.id,
128
132
  "data-tour": primaryAction.dataTour,
129
133
  onClick: () => primaryAction.onClick(),
@@ -134,8 +138,8 @@ function PageScaffold({
134
138
  {
135
139
  variant: (_s = primaryAction.variant) != null ? _s : "primary",
136
140
  size: (_t = primaryAction.size) != null ? _t : "md",
137
- disabled: primaryAction.disabled || showLoading,
138
- loading: Boolean(primaryAction.loading) || showLoading,
141
+ disabled: primaryAction.disabled || showFullLoading,
142
+ loading: Boolean(primaryAction.loading) || showFullLoading,
139
143
  id: primaryAction.id,
140
144
  "data-tour": primaryAction.dataTour,
141
145
  onClick: () => primaryAction.onClick(),
@@ -150,8 +154,8 @@ function PageScaffold({
150
154
  {
151
155
  groups: filters.groups,
152
156
  ...filtersProps,
153
- disabled: filtersProps.disabled || showLoading,
154
- loading: filtersProps.loading || showLoading
157
+ disabled: filtersProps.disabled || showFullLoading,
158
+ loading: filtersProps.loading || showFullLoading
155
159
  }
156
160
  ) : null,
157
161
  ((_w = tools == null ? void 0 : tools.sort) != null ? _w : true) ? sort : null,
@@ -198,7 +202,7 @@ function PageScaffold({
198
202
  items: breadcrumb.items,
199
203
  ...(_z = breadcrumb == null ? void 0 : breadcrumb.props) != null ? _z : {},
200
204
  className: cx("px-1", (_A = breadcrumb == null ? void 0 : breadcrumb.props) == null ? void 0 : _A.className),
201
- loading: Boolean((_B = breadcrumb == null ? void 0 : breadcrumb.props) == null ? void 0 : _B.loading) || showLoading,
205
+ loading: Boolean((_B = breadcrumb == null ? void 0 : breadcrumb.props) == null ? void 0 : _B.loading) || showFullLoading,
202
206
  loadingMinDurationMs: (_D = (_C = breadcrumb == null ? void 0 : breadcrumb.props) == null ? void 0 : _C.loadingMinDurationMs) != null ? _D : loadingMinDurationMs
203
207
  }
204
208
  ) : null,
@@ -206,7 +210,7 @@ function PageScaffold({
206
210
  showKpis ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: kpiColumnsClassName, children: kpiItems.map((kpi, i) => {
207
211
  var _a2, _b2;
208
212
  if (import_react.default.isValidElement(kpi)) return import_react.default.cloneElement(kpi, { key: (_a2 = kpi.key) != null ? _a2 : i });
209
- return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_Card.default, { className: cx("p-4", kpiClassName), loading: showLoading, children: [
213
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_Card.default, { className: cx("p-4", kpiClassName), loading: showFullLoading || showContentLoading, children: [
210
214
  /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "flex items-center justify-between gap-3", children: [
211
215
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "text-sm font-semibold text-[var(--muted)]", children: kpi.label }),
212
216
  kpi.badge ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_Badge.default, { tone: (_b2 = kpi.badgeTone) != null ? _b2 : "slate", children: kpi.badge }) : null
@@ -215,7 +219,7 @@ function PageScaffold({
215
219
  kpi.helper ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "mt-1 text-xs text-[var(--muted)]", children: kpi.helper }) : null
216
220
  ] }, i);
217
221
  }) }) : null,
218
- showToolbar ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_Card.default, { className: "p-4", loading: showLoading, children: search && ((_E = tools == null ? void 0 : tools.search) != null ? _E : true) ? /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "space-y-3", children: [
222
+ showToolbar ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_Card.default, { className: "p-4", loading: showFullLoading, children: search && ((_E = tools == null ? void 0 : tools.search) != null ? _E : true) ? /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "space-y-3", children: [
219
223
  /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "flex flex-col gap-3 lg:flex-row lg:items-center lg:justify-between", children: [
220
224
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "min-w-0 flex-1", children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
221
225
  import_SearchInput.default,
@@ -224,7 +228,7 @@ function PageScaffold({
224
228
  value: search.value,
225
229
  onChange: search.onChange,
226
230
  placeholder: search.placeholder,
227
- disabled: search.disabled || showLoading
231
+ disabled: showFullLoading || (showContentLoading ? false : Boolean(search.disabled))
228
232
  }
229
233
  ) }),
230
234
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "w-full lg:w-auto", children: toolbarActions })
@@ -235,10 +239,16 @@ function PageScaffold({
235
239
  toolbarActions
236
240
  ] }) }) : null,
237
241
  belowToolbar,
238
- showLoading ? loadingBody != null ? loadingBody : /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_Card.default, { className: "p-4", children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "space-y-3", children: [
239
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "fx-skeleton", style: { height: 12, width: "38%", borderRadius: 10 } }),
240
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "fx-skeleton", style: { height: 12, width: "72%", borderRadius: 10 } }),
241
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "fx-skeleton", style: { height: 260, width: "100%", borderRadius: 18 } })
242
+ showFullLoading || showContentLoading ? loadingBody != null ? loadingBody : /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_Card.default, { className: "overflow-hidden p-0", children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "space-y-4 p-4", children: [
243
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "fx-skeleton", style: { height: 14, width: "26%", borderRadius: 10 } }),
244
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "grid gap-3", children: Array.from({ length: 6 }).map((_, index) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
245
+ "div",
246
+ {
247
+ className: "fx-skeleton",
248
+ style: { height: 56, width: "100%", borderRadius: 16 }
249
+ },
250
+ index
251
+ )) })
242
252
  ] }) }) : showEmptyState ? emptyStateContainer === "card" ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_Card.default, { className: "p-6", children: resolvedEmptyState }) : resolvedEmptyState : children
243
253
  ] });
244
254
  }
@@ -35,6 +35,7 @@ function PageScaffold({
35
35
  loading = false,
36
36
  loadingMinDurationMs = DEFAULT_SKELETON_MIN_DURATION_MS,
37
37
  loadingBody,
38
+ loadingBehavior = "auto",
38
39
  empty = false,
39
40
  emptyState,
40
41
  emptyStateContainer = "card",
@@ -43,10 +44,13 @@ function PageScaffold({
43
44
  }) {
44
45
  var _a, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k, _l, _m, _n, _o, _p, _q, _r, _s, _t, _u, _v, _w, _x, _y, _z, _A, _B, _C, _D, _E;
45
46
  const showLoading = useMinVisible(Boolean(loading), loadingMinDurationMs);
47
+ const resolvedLoadingBehavior = loadingBehavior === "auto" ? search ? "content" : "full" : loadingBehavior;
48
+ const showFullLoading = showLoading && resolvedLoadingBehavior === "full";
49
+ const showContentLoading = showLoading && resolvedLoadingBehavior === "content";
46
50
  const [mounted, setMounted] = React.useState(false);
47
51
  React.useEffect(() => setMounted(true), []);
48
52
  const primaryActionShortcut = (_a = primaryAction == null ? void 0 : primaryAction.shortcut) == null ? void 0 : _a.trim();
49
- const primaryActionDisabled = Boolean(primaryAction == null ? void 0 : primaryAction.disabled) || Boolean(primaryAction == null ? void 0 : primaryAction.loading) || showLoading;
53
+ const primaryActionDisabled = Boolean(primaryAction == null ? void 0 : primaryAction.disabled) || Boolean(primaryAction == null ? void 0 : primaryAction.loading) || showFullLoading;
50
54
  const primaryActionOnClick = primaryAction == null ? void 0 : primaryAction.onClick;
51
55
  React.useEffect(() => {
52
56
  if (!primaryActionShortcut) return;
@@ -76,7 +80,7 @@ function PageScaffold({
76
80
  search && ((_g = tools == null ? void 0 : tools.search) != null ? _g : true) || filters && ((_h = tools == null ? void 0 : tools.filters) != null ? _h : true) || sort && ((_i = tools == null ? void 0 : tools.sort) != null ? _i : true) || columns && ((_j = tools == null ? void 0 : tools.columns) != null ? _j : true) || tabs && ((_k = tools == null ? void 0 : tools.tabs) != null ? _k : true) || primaryAction && ((_l = tools == null ? void 0 : tools.primaryAction) != null ? _l : true) || actions && ((_m = tools == null ? void 0 : tools.actions) != null ? _m : true)
77
81
  );
78
82
  const filtersProps = (_n = filters == null ? void 0 : filters.props) != null ? _n : {};
79
- const showEmptyState = Boolean(empty) && !showLoading && ((_o = tools == null ? void 0 : tools.emptyState) != null ? _o : true);
83
+ const showEmptyState = Boolean(empty) && !showFullLoading && !showContentLoading && ((_o = tools == null ? void 0 : tools.emptyState) != null ? _o : true);
80
84
  const resolvedEmptyState = React.useMemo(() => {
81
85
  if (!emptyState) return /* @__PURE__ */ jsx(EmptyState, {});
82
86
  if (React.isValidElement(emptyState)) return emptyState;
@@ -89,8 +93,8 @@ function PageScaffold({
89
93
  {
90
94
  variant: (_q = primaryAction.variant) != null ? _q : "primary",
91
95
  size: (_r = primaryAction.size) != null ? _r : "md",
92
- disabled: primaryAction.disabled || showLoading,
93
- loading: Boolean(primaryAction.loading) || showLoading,
96
+ disabled: primaryAction.disabled || showFullLoading,
97
+ loading: Boolean(primaryAction.loading) || showFullLoading,
94
98
  id: primaryAction.id,
95
99
  "data-tour": primaryAction.dataTour,
96
100
  onClick: () => primaryAction.onClick(),
@@ -101,8 +105,8 @@ function PageScaffold({
101
105
  {
102
106
  variant: (_s = primaryAction.variant) != null ? _s : "primary",
103
107
  size: (_t = primaryAction.size) != null ? _t : "md",
104
- disabled: primaryAction.disabled || showLoading,
105
- loading: Boolean(primaryAction.loading) || showLoading,
108
+ disabled: primaryAction.disabled || showFullLoading,
109
+ loading: Boolean(primaryAction.loading) || showFullLoading,
106
110
  id: primaryAction.id,
107
111
  "data-tour": primaryAction.dataTour,
108
112
  onClick: () => primaryAction.onClick(),
@@ -117,8 +121,8 @@ function PageScaffold({
117
121
  {
118
122
  groups: filters.groups,
119
123
  ...filtersProps,
120
- disabled: filtersProps.disabled || showLoading,
121
- loading: filtersProps.loading || showLoading
124
+ disabled: filtersProps.disabled || showFullLoading,
125
+ loading: filtersProps.loading || showFullLoading
122
126
  }
123
127
  ) : null,
124
128
  ((_w = tools == null ? void 0 : tools.sort) != null ? _w : true) ? sort : null,
@@ -165,7 +169,7 @@ function PageScaffold({
165
169
  items: breadcrumb.items,
166
170
  ...(_z = breadcrumb == null ? void 0 : breadcrumb.props) != null ? _z : {},
167
171
  className: cx("px-1", (_A = breadcrumb == null ? void 0 : breadcrumb.props) == null ? void 0 : _A.className),
168
- loading: Boolean((_B = breadcrumb == null ? void 0 : breadcrumb.props) == null ? void 0 : _B.loading) || showLoading,
172
+ loading: Boolean((_B = breadcrumb == null ? void 0 : breadcrumb.props) == null ? void 0 : _B.loading) || showFullLoading,
169
173
  loadingMinDurationMs: (_D = (_C = breadcrumb == null ? void 0 : breadcrumb.props) == null ? void 0 : _C.loadingMinDurationMs) != null ? _D : loadingMinDurationMs
170
174
  }
171
175
  ) : null,
@@ -173,7 +177,7 @@ function PageScaffold({
173
177
  showKpis ? /* @__PURE__ */ jsx("div", { className: kpiColumnsClassName, children: kpiItems.map((kpi, i) => {
174
178
  var _a2, _b2;
175
179
  if (React.isValidElement(kpi)) return React.cloneElement(kpi, { key: (_a2 = kpi.key) != null ? _a2 : i });
176
- return /* @__PURE__ */ jsxs(Card, { className: cx("p-4", kpiClassName), loading: showLoading, children: [
180
+ return /* @__PURE__ */ jsxs(Card, { className: cx("p-4", kpiClassName), loading: showFullLoading || showContentLoading, children: [
177
181
  /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between gap-3", children: [
178
182
  /* @__PURE__ */ jsx("div", { className: "text-sm font-semibold text-[var(--muted)]", children: kpi.label }),
179
183
  kpi.badge ? /* @__PURE__ */ jsx(Badge, { tone: (_b2 = kpi.badgeTone) != null ? _b2 : "slate", children: kpi.badge }) : null
@@ -182,7 +186,7 @@ function PageScaffold({
182
186
  kpi.helper ? /* @__PURE__ */ jsx("div", { className: "mt-1 text-xs text-[var(--muted)]", children: kpi.helper }) : null
183
187
  ] }, i);
184
188
  }) }) : null,
185
- showToolbar ? /* @__PURE__ */ jsx(Card, { className: "p-4", loading: showLoading, children: search && ((_E = tools == null ? void 0 : tools.search) != null ? _E : true) ? /* @__PURE__ */ jsxs("div", { className: "space-y-3", children: [
189
+ showToolbar ? /* @__PURE__ */ jsx(Card, { className: "p-4", loading: showFullLoading, children: search && ((_E = tools == null ? void 0 : tools.search) != null ? _E : true) ? /* @__PURE__ */ jsxs("div", { className: "space-y-3", children: [
186
190
  /* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-3 lg:flex-row lg:items-center lg:justify-between", children: [
187
191
  /* @__PURE__ */ jsx("div", { className: "min-w-0 flex-1", children: /* @__PURE__ */ jsx(
188
192
  SearchInput,
@@ -191,7 +195,7 @@ function PageScaffold({
191
195
  value: search.value,
192
196
  onChange: search.onChange,
193
197
  placeholder: search.placeholder,
194
- disabled: search.disabled || showLoading
198
+ disabled: showFullLoading || (showContentLoading ? false : Boolean(search.disabled))
195
199
  }
196
200
  ) }),
197
201
  /* @__PURE__ */ jsx("div", { className: "w-full lg:w-auto", children: toolbarActions })
@@ -202,10 +206,16 @@ function PageScaffold({
202
206
  toolbarActions
203
207
  ] }) }) : null,
204
208
  belowToolbar,
205
- showLoading ? loadingBody != null ? loadingBody : /* @__PURE__ */ jsx(Card, { className: "p-4", children: /* @__PURE__ */ jsxs("div", { className: "space-y-3", children: [
206
- /* @__PURE__ */ jsx("div", { className: "fx-skeleton", style: { height: 12, width: "38%", borderRadius: 10 } }),
207
- /* @__PURE__ */ jsx("div", { className: "fx-skeleton", style: { height: 12, width: "72%", borderRadius: 10 } }),
208
- /* @__PURE__ */ jsx("div", { className: "fx-skeleton", style: { height: 260, width: "100%", borderRadius: 18 } })
209
+ showFullLoading || showContentLoading ? loadingBody != null ? loadingBody : /* @__PURE__ */ jsx(Card, { className: "overflow-hidden p-0", children: /* @__PURE__ */ jsxs("div", { className: "space-y-4 p-4", children: [
210
+ /* @__PURE__ */ jsx("div", { className: "fx-skeleton", style: { height: 14, width: "26%", borderRadius: 10 } }),
211
+ /* @__PURE__ */ jsx("div", { className: "grid gap-3", children: Array.from({ length: 6 }).map((_, index) => /* @__PURE__ */ jsx(
212
+ "div",
213
+ {
214
+ className: "fx-skeleton",
215
+ style: { height: 56, width: "100%", borderRadius: 16 }
216
+ },
217
+ index
218
+ )) })
209
219
  ] }) }) : showEmptyState ? emptyStateContainer === "card" ? /* @__PURE__ */ jsx(Card, { className: "p-6", children: resolvedEmptyState }) : resolvedEmptyState : children
210
220
  ] });
211
221
  }
@@ -5,10 +5,45 @@ type Spark = {
5
5
  values: number[];
6
6
  color?: string;
7
7
  };
8
+ type StatCardComparisonPeriod = "day" | "week" | "month" | "quarter" | "year";
9
+ type StatCardComparisonTrend = "up" | "down" | "flat" | "auto";
10
+ type StatCardComparison = {
11
+ value: number;
12
+ period?: StatCardComparisonPeriod;
13
+ trend?: StatCardComparisonTrend;
14
+ label?: React__default.ReactNode;
15
+ locale?: "en" | "es";
16
+ precision?: number;
17
+ suffix?: string;
18
+ showSign?: boolean;
19
+ positiveIsGood?: boolean;
20
+ formatter?: (value: number) => React__default.ReactNode;
21
+ };
22
+ type StatCardMenuItem = {
23
+ label: React__default.ReactNode;
24
+ onSelect?: () => void;
25
+ icon?: React__default.ReactNode;
26
+ kbd?: string;
27
+ danger?: boolean;
28
+ disabled?: boolean;
29
+ require?: string | string[];
30
+ hideIfUnauthorized?: boolean;
31
+ disableIfUnauthorized?: boolean;
32
+ };
33
+ type StatCardMenu = {
34
+ label?: React__default.ReactNode;
35
+ items: StatCardMenuItem[];
36
+ align?: "start" | "end";
37
+ contentClassName?: string;
38
+ maxHeight?: number;
39
+ triggerAriaLabel?: string;
40
+ };
8
41
  type StatCardProps = {
9
42
  title: string;
10
43
  value: React__default.ReactNode;
11
44
  hint?: string;
45
+ comparison?: StatCardComparison;
46
+ menu?: StatCardMenu;
12
47
  spark?: Spark;
13
48
  loading?: boolean;
14
49
  loadingMinDurationMs?: number;
@@ -19,6 +54,6 @@ type StatCardProps = {
19
54
  withSpark?: boolean;
20
55
  };
21
56
  };
22
- declare function StatCard({ title, value, hint, spark, loading, loadingMinDurationMs, skeleton }: StatCardProps): react_jsx_runtime.JSX.Element;
57
+ declare function StatCard({ title, value, hint, comparison, menu, spark, loading, loadingMinDurationMs, skeleton }: StatCardProps): react_jsx_runtime.JSX.Element;
23
58
 
24
- export { type StatCardProps, StatCard as default };
59
+ export { type StatCardComparison, type StatCardComparisonPeriod, type StatCardComparisonTrend, type StatCardMenu, type StatCardMenuItem, type StatCardProps, StatCard as default };
@@ -5,10 +5,45 @@ type Spark = {
5
5
  values: number[];
6
6
  color?: string;
7
7
  };
8
+ type StatCardComparisonPeriod = "day" | "week" | "month" | "quarter" | "year";
9
+ type StatCardComparisonTrend = "up" | "down" | "flat" | "auto";
10
+ type StatCardComparison = {
11
+ value: number;
12
+ period?: StatCardComparisonPeriod;
13
+ trend?: StatCardComparisonTrend;
14
+ label?: React__default.ReactNode;
15
+ locale?: "en" | "es";
16
+ precision?: number;
17
+ suffix?: string;
18
+ showSign?: boolean;
19
+ positiveIsGood?: boolean;
20
+ formatter?: (value: number) => React__default.ReactNode;
21
+ };
22
+ type StatCardMenuItem = {
23
+ label: React__default.ReactNode;
24
+ onSelect?: () => void;
25
+ icon?: React__default.ReactNode;
26
+ kbd?: string;
27
+ danger?: boolean;
28
+ disabled?: boolean;
29
+ require?: string | string[];
30
+ hideIfUnauthorized?: boolean;
31
+ disableIfUnauthorized?: boolean;
32
+ };
33
+ type StatCardMenu = {
34
+ label?: React__default.ReactNode;
35
+ items: StatCardMenuItem[];
36
+ align?: "start" | "end";
37
+ contentClassName?: string;
38
+ maxHeight?: number;
39
+ triggerAriaLabel?: string;
40
+ };
8
41
  type StatCardProps = {
9
42
  title: string;
10
43
  value: React__default.ReactNode;
11
44
  hint?: string;
45
+ comparison?: StatCardComparison;
46
+ menu?: StatCardMenu;
12
47
  spark?: Spark;
13
48
  loading?: boolean;
14
49
  loadingMinDurationMs?: number;
@@ -19,6 +54,6 @@ type StatCardProps = {
19
54
  withSpark?: boolean;
20
55
  };
21
56
  };
22
- declare function StatCard({ title, value, hint, spark, loading, loadingMinDurationMs, skeleton }: StatCardProps): react_jsx_runtime.JSX.Element;
57
+ declare function StatCard({ title, value, hint, comparison, menu, spark, loading, loadingMinDurationMs, skeleton }: StatCardProps): react_jsx_runtime.JSX.Element;
23
58
 
24
- export { type StatCardProps, StatCard as default };
59
+ export { type StatCardComparison, type StatCardComparisonPeriod, type StatCardComparisonTrend, type StatCardMenu, type StatCardMenuItem, type StatCardProps, StatCard as default };