@olwiba/ui 0.2.24 → 0.2.25

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.ts CHANGED
@@ -1217,8 +1217,14 @@ interface FooterProps {
1217
1217
  icon: ReactNode;
1218
1218
  }>;
1219
1219
  legal?: string;
1220
+ /**
1221
+ * Live status pill. `label` is a ReactNode, not a string: a real status
1222
+ * pill often carries per-service detail in a tooltip, and typing it as
1223
+ * string forced consumers to smuggle that through `as unknown as string`.
1224
+ * A plain string still works and is the common case.
1225
+ */
1220
1226
  status?: {
1221
- label: string;
1227
+ label: ReactNode;
1222
1228
  operational?: boolean;
1223
1229
  };
1224
1230
  renderLink?: AppShellRenderLink;
@@ -1513,6 +1519,23 @@ interface DataTableProps<TData> {
1513
1519
  onSelectionChange?: (rows: TData[]) => void;
1514
1520
  /** Rows per page. Set to `0` to disable pagination entirely. @default 10 */
1515
1521
  pageSize?: number;
1522
+ /**
1523
+ * Hands pagination to the caller, for data the client does not hold all of.
1524
+ *
1525
+ * With this set, `data` is treated as the current page rather than the whole
1526
+ * set: the table stops slicing, and the footer reports and drives the
1527
+ * caller's page instead of its own. Without it nothing changes — a table
1528
+ * given every row keeps paginating locally, which is right for the common
1529
+ * case and avoids making every consumer implement paging to get a table.
1530
+ */
1531
+ pagination?: {
1532
+ pageIndex: number;
1533
+ /** Total pages. Omit when unknown — the footer then relies on `hasNext`. */
1534
+ pageCount?: number;
1535
+ hasNext?: boolean;
1536
+ hasPrevious?: boolean;
1537
+ onPageChange: (pageIndex: number) => void;
1538
+ };
1516
1539
  /** Slot rendered top-right of the toolbar — e.g. an "Add" button. */
1517
1540
  toolbar?: React.ReactNode;
1518
1541
  onRowClick?: (row: TData) => void;
@@ -1524,7 +1547,7 @@ interface DataTableProps<TData> {
1524
1547
  * toggle `searchKey`/`selectable`/`pageSize` rather than reaching for a
1525
1548
  * different table component per use case.
1526
1549
  */
1527
- declare function DataTable<TData>({ columns, data, searchKey, searchPlaceholder, selectable, onSelectionChange, pageSize, toolbar, onRowClick, emptyMessage, className, }: DataTableProps<TData>): react_jsx_runtime.JSX.Element;
1550
+ declare function DataTable<TData>({ columns, data, searchKey, searchPlaceholder, selectable, onSelectionChange, pageSize, pagination, toolbar, onRowClick, emptyMessage, className, }: DataTableProps<TData>): react_jsx_runtime.JSX.Element;
1528
1551
 
1529
1552
  interface LoadMoreProps extends React.HTMLAttributes<HTMLDivElement> {
1530
1553
  hasNextPage: boolean;
@@ -1635,6 +1658,27 @@ interface ChartProps {
1635
1658
  grid?: boolean;
1636
1659
  /** Legend. @default true for multiple series or donut, false for one series */
1637
1660
  legend?: boolean;
1661
+ /**
1662
+ * Stack series instead of overlaying them. Area and bar only — a stacked
1663
+ * line is a shape people misread as absolute values.
1664
+ *
1665
+ * Off by default: stacking answers "what does the total split into", and
1666
+ * silently switching a two-series comparison to it would change what an
1667
+ * existing chart claims.
1668
+ */
1669
+ stacked?: boolean;
1670
+ /**
1671
+ * Formats x-axis ticks. Separate from `valueFormatter`, which formats the
1672
+ * measured value — an axis of ISO dates wants shortening without changing
1673
+ * what the tooltip reports.
1674
+ */
1675
+ xTickFormatter?: (value: string) => string;
1676
+ /**
1677
+ * Fixes the y-axis range. Without it recharts scales to the data, which for
1678
+ * a percentage series makes a 98-100%% band fill the plot and read as
1679
+ * volatility.
1680
+ */
1681
+ yDomain?: [number, number];
1638
1682
  /** Formats values in tooltips and the y-axis, e.g. `(v) => \`$${v}\``. */
1639
1683
  valueFormatter?: (value: number) => string;
1640
1684
  className?: string;
@@ -1646,7 +1690,7 @@ interface ChartProps {
1646
1690
  * comparison, donut for part-of-whole identity. More than 5 donut slices:
1647
1691
  * fold the tail into an "Other" slice in your data instead of adding hues.
1648
1692
  */
1649
- declare function Chart({ type, data, xKey, series, height, grid, legend, valueFormatter, className, }: ChartProps): react_jsx_runtime.JSX.Element;
1693
+ declare function Chart({ type, data, xKey, series, height, grid, legend, stacked, xTickFormatter, yDomain, valueFormatter, className, }: ChartProps): react_jsx_runtime.JSX.Element;
1650
1694
 
1651
1695
  interface FileUploadEntry {
1652
1696
  id: string;
@@ -1706,6 +1750,10 @@ interface NotificationToastProps {
1706
1750
  */
1707
1751
  declare function NotificationToast({ variant, title, description, avatar, action, secondaryAction, onDismiss, }: NotificationToastProps): react_jsx_runtime.JSX.Element;
1708
1752
  interface NotifyOptions extends Omit<NotificationToastProps, 'onDismiss'> {
1753
+ /**
1754
+ * Overrides the per-variant default below. Explicit always wins — a caller
1755
+ * that has thought about the timing knows more than this heuristic does.
1756
+ */
1709
1757
  duration?: number;
1710
1758
  }
1711
1759
  /** Fires a `NotificationToast` through sonner. Requires `<Toaster />` from `@olwiba/cn` mounted once in your app. */
package/dist/index.js CHANGED
@@ -962,6 +962,7 @@ function DataTable({
962
962
  selectable = false,
963
963
  onSelectionChange,
964
964
  pageSize = 10,
965
+ pagination,
965
966
  toolbar,
966
967
  onRowClick,
967
968
  emptyMessage = "No results.",
@@ -988,8 +989,11 @@ function DataTable({
988
989
  },
989
990
  getCoreRowModel: getCoreRowModel(),
990
991
  getSortedRowModel: getSortedRowModel(),
991
- ...pageSize > 0 ? { getPaginationRowModel: getPaginationRowModel() } : {},
992
- initialState: pageSize > 0 ? { pagination: { pageSize } } : void 0,
992
+ // Server-paginated tables must not slice: `data` is already the page, and
993
+ // running the client model over it would paginate a single page again.
994
+ ...pageSize > 0 && !pagination ? { getPaginationRowModel: getPaginationRowModel() } : {},
995
+ ...pagination ? { manualPagination: true } : {},
996
+ initialState: pageSize > 0 && !pagination ? { pagination: { pageSize } } : void 0,
993
997
  enableRowSelection: selectable
994
998
  });
995
999
  React21.useEffect(() => {
@@ -997,6 +1001,12 @@ function DataTable({
997
1001
  onSelectionChange(table.getSelectedRowModel().rows.map((r) => r.original));
998
1002
  }, [rowSelection]);
999
1003
  const rows = table.getRowModel().rows;
1004
+ const canPrevious = pagination ? pagination.hasPrevious ?? pagination.pageIndex > 0 : table.getCanPreviousPage();
1005
+ const canNext = pagination ? pagination.hasNext ?? (pagination.pageCount !== void 0 && pagination.pageIndex < pagination.pageCount - 1) : table.getCanNextPage();
1006
+ const goPrevious = () => pagination ? pagination.onPageChange(pagination.pageIndex - 1) : table.previousPage();
1007
+ const goNext = () => pagination ? pagination.onPageChange(pagination.pageIndex + 1) : table.nextPage();
1008
+ const showPager = pagination ? canPrevious || canNext : pageSize > 0 && table.getPageCount() > 1;
1009
+ const pageLabel = pagination ? pagination.pageCount !== void 0 ? `Page ${pagination.pageIndex + 1} of ${pagination.pageCount}` : `Page ${pagination.pageIndex + 1}` : `Page ${table.getState().pagination.pageIndex + 1} of ${table.getPageCount()}`;
1000
1010
  return /* @__PURE__ */ jsxs("div", { className: cn$1("space-y-4", className), children: [
1001
1011
  (searchKey || toolbar) && /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between gap-4", children: [
1002
1012
  searchKey ? /* @__PURE__ */ jsxs("div", { className: "relative max-w-sm flex-1", children: [
@@ -1052,18 +1062,18 @@ function DataTable({
1052
1062
  row.id
1053
1063
  )) : /* @__PURE__ */ jsx(TableRow, { children: /* @__PURE__ */ jsx(TableCell, { colSpan: allColumns.length, className: "h-24 text-center text-muted-foreground", children: emptyMessage }) }) })
1054
1064
  ] }) }),
1055
- (selectable || pageSize > 0 && table.getPageCount() > 1) && /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between", children: [
1065
+ (selectable || showPager) && /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between", children: [
1056
1066
  /* @__PURE__ */ jsxs("p", { className: "text-sm text-muted-foreground", children: [
1057
1067
  selectable && `${table.getFilteredSelectedRowModel().rows.length} of ${table.getFilteredRowModel().rows.length} selected`,
1058
- selectable && pageSize > 0 && table.getPageCount() > 1 && " \xB7 ",
1059
- pageSize > 0 && table.getPageCount() > 1 && `Page ${table.getState().pagination.pageIndex + 1} of ${table.getPageCount()}`
1068
+ selectable && showPager && " \xB7 ",
1069
+ showPager && pageLabel
1060
1070
  ] }),
1061
- pageSize > 0 && table.getPageCount() > 1 && /* @__PURE__ */ jsxs("div", { className: "flex gap-2", children: [
1062
- /* @__PURE__ */ jsxs(Button, { variant: "outline", size: "sm", onClick: () => table.previousPage(), disabled: !table.getCanPreviousPage(), children: [
1071
+ showPager && /* @__PURE__ */ jsxs("div", { className: "flex gap-2", children: [
1072
+ /* @__PURE__ */ jsxs(Button, { variant: "outline", size: "sm", onClick: goPrevious, disabled: !canPrevious, children: [
1063
1073
  /* @__PURE__ */ jsx(ChevronLeft, { className: "size-4" }),
1064
1074
  " Previous"
1065
1075
  ] }),
1066
- /* @__PURE__ */ jsxs(Button, { variant: "outline", size: "sm", onClick: () => table.nextPage(), disabled: !table.getCanNextPage(), children: [
1076
+ /* @__PURE__ */ jsxs(Button, { variant: "outline", size: "sm", onClick: goNext, disabled: !canNext, children: [
1067
1077
  "Next ",
1068
1078
  /* @__PURE__ */ jsx(ChevronRight, { className: "size-4" })
1069
1079
  ] })
@@ -4328,6 +4338,9 @@ function Chart({
4328
4338
  height = 300,
4329
4339
  grid = true,
4330
4340
  legend,
4341
+ stacked = false,
4342
+ xTickFormatter,
4343
+ yDomain,
4331
4344
  valueFormatter = (v) => String(v),
4332
4345
  className
4333
4346
  }) {
@@ -4370,12 +4383,13 @@ function Chart({
4370
4383
  if (type === "bar") {
4371
4384
  chart = /* @__PURE__ */ jsxs(BarChart, { data, barCategoryGap: "25%", children: [
4372
4385
  gridEl,
4373
- /* @__PURE__ */ jsx(XAxis, { dataKey: xKey, ...axisProps }),
4374
- /* @__PURE__ */ jsx(YAxis, { ...axisProps, width: 48, tickFormatter: valueFormatter }),
4386
+ /* @__PURE__ */ jsx(XAxis, { dataKey: xKey, ...axisProps, tickFormatter: xTickFormatter }),
4387
+ /* @__PURE__ */ jsx(YAxis, { ...axisProps, width: 48, domain: yDomain, tickFormatter: valueFormatter }),
4375
4388
  tooltip,
4376
4389
  series.map((s, i) => /* @__PURE__ */ jsx(
4377
4390
  Bar,
4378
4391
  {
4392
+ stackId: stacked ? "stack" : void 0,
4379
4393
  dataKey: s.key,
4380
4394
  name: s.label ?? s.key,
4381
4395
  fill: seriesColor(s, i),
@@ -4388,8 +4402,8 @@ function Chart({
4388
4402
  } else if (type === "area") {
4389
4403
  chart = /* @__PURE__ */ jsxs(AreaChart, { data, children: [
4390
4404
  gridEl,
4391
- /* @__PURE__ */ jsx(XAxis, { dataKey: xKey, ...axisProps }),
4392
- /* @__PURE__ */ jsx(YAxis, { ...axisProps, width: 48, tickFormatter: valueFormatter }),
4405
+ /* @__PURE__ */ jsx(XAxis, { dataKey: xKey, ...axisProps, tickFormatter: xTickFormatter }),
4406
+ /* @__PURE__ */ jsx(YAxis, { ...axisProps, width: 48, domain: yDomain, tickFormatter: valueFormatter }),
4393
4407
  tooltip,
4394
4408
  series.map((s, i) => /* @__PURE__ */ jsx(
4395
4409
  Area,
@@ -4397,10 +4411,11 @@ function Chart({
4397
4411
  type: "monotone",
4398
4412
  dataKey: s.key,
4399
4413
  name: s.label ?? s.key,
4414
+ stackId: stacked ? "stack" : void 0,
4400
4415
  stroke: seriesColor(s, i),
4401
4416
  strokeWidth: 2,
4402
4417
  fill: seriesColor(s, i),
4403
- fillOpacity: 0.12,
4418
+ fillOpacity: stacked ? 0.35 : 0.12,
4404
4419
  dot: false,
4405
4420
  activeDot: { r: 4 }
4406
4421
  },
@@ -4410,8 +4425,8 @@ function Chart({
4410
4425
  } else {
4411
4426
  chart = /* @__PURE__ */ jsxs(LineChart, { data, children: [
4412
4427
  gridEl,
4413
- /* @__PURE__ */ jsx(XAxis, { dataKey: xKey, ...axisProps }),
4414
- /* @__PURE__ */ jsx(YAxis, { ...axisProps, width: 48, tickFormatter: valueFormatter }),
4428
+ /* @__PURE__ */ jsx(XAxis, { dataKey: xKey, ...axisProps, tickFormatter: xTickFormatter }),
4429
+ /* @__PURE__ */ jsx(YAxis, { ...axisProps, width: 48, domain: yDomain, tickFormatter: valueFormatter }),
4415
4430
  tooltip,
4416
4431
  series.map((s, i) => /* @__PURE__ */ jsx(
4417
4432
  Line,
@@ -4638,9 +4653,18 @@ function NotificationToast({
4638
4653
  ] })
4639
4654
  ] }) });
4640
4655
  }
4656
+ function defaultDuration(variant, hasAction) {
4657
+ if (variant === "error") return 1e4;
4658
+ if (variant === "warning") return 8e3;
4659
+ if (hasAction) return 8e3;
4660
+ return void 0;
4661
+ }
4641
4662
  function notify(options) {
4642
4663
  const { duration, ...toastProps } = options;
4643
- return toast.custom((id) => /* @__PURE__ */ jsx(NotificationToast, { ...toastProps, onDismiss: () => toast.dismiss(id) }), { duration });
4664
+ const resolved = duration ?? defaultDuration(toastProps.variant, Boolean(toastProps.action || toastProps.secondaryAction));
4665
+ return toast.custom((id) => /* @__PURE__ */ jsx(NotificationToast, { ...toastProps, onDismiss: () => toast.dismiss(id) }), {
4666
+ duration: resolved
4667
+ });
4644
4668
  }
4645
4669
  function NotificationsPopover({
4646
4670
  notifications,