@payglocal_ui/flux-ui 0.2.6 → 0.3.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.
- package/dist/index.cjs +3145 -592
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +1206 -26
- package/dist/index.d.ts +1206 -26
- package/dist/index.js +3094 -546
- package/dist/index.js.map +1 -1
- package/package.json +14 -4
- package/src/__tests__/data-card-list.test.tsx +74 -0
- package/src/__tests__/data-table-row-click.test.tsx +113 -0
- package/src/__tests__/filter-chips.test.tsx +237 -0
- package/src/calendar-date-chip.tsx +262 -0
- package/src/column-manager.tsx +590 -0
- package/src/copyable-cell.tsx +253 -0
- package/src/data-card-list.tsx +188 -0
- package/src/data-table-card.tsx +205 -0
- package/src/data-table.tsx +876 -144
- package/src/date-picker.tsx +15 -3
- package/src/filter-chips.tsx +1874 -0
- package/src/format-datetime.ts +170 -0
- package/src/index.ts +95 -2
- package/src/popover.tsx +24 -15
- package/src/rotating-search-input.tsx +164 -0
- package/src/tab-presets.tsx +189 -0
package/dist/index.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { ClassValue } from 'clsx';
|
|
2
2
|
import * as React$1 from 'react';
|
|
3
|
-
import { ButtonHTMLAttributes, HTMLAttributes, ReactNode, ComponentProps, AnchorHTMLAttributes } from 'react';
|
|
3
|
+
import { ButtonHTMLAttributes, HTMLAttributes, ReactNode, ComponentProps, AnchorHTMLAttributes, CSSProperties, ComponentPropsWithoutRef } from 'react';
|
|
4
4
|
import * as class_variance_authority_types from 'class-variance-authority/types';
|
|
5
5
|
import * as LabelPrimitive from '@radix-ui/react-label';
|
|
6
6
|
import { VariantProps } from 'class-variance-authority';
|
|
@@ -654,6 +654,80 @@ declare const TabsList: React$1.ForwardRefExoticComponent<Omit<TabsPrimitive.Tab
|
|
|
654
654
|
declare const TabsTrigger: React$1.ForwardRefExoticComponent<Omit<TabsPrimitive.TabsTriggerProps & React$1.RefAttributes<HTMLButtonElement>, "ref"> & React$1.RefAttributes<HTMLButtonElement>>;
|
|
655
655
|
declare const TabsContent: React$1.ForwardRefExoticComponent<Omit<TabsPrimitive.TabsContentProps & React$1.RefAttributes<HTMLDivElement>, "ref"> & React$1.RefAttributes<HTMLDivElement>>;
|
|
656
656
|
|
|
657
|
+
/**
|
|
658
|
+
* Two presets over the `Tabs` primitives, for the two jobs a tab row actually
|
|
659
|
+
* does. They are separate components on purpose, so a page can show both
|
|
660
|
+
* without the reader having to work out which row governs what:
|
|
661
|
+
*
|
|
662
|
+
* - {@link UnderlineTabs} is the **page-level** bar that segments a view into
|
|
663
|
+
* sections — full-width, one sliding indicator, the thing a URL usually
|
|
664
|
+
* follows.
|
|
665
|
+
* - {@link SegmentedTabs} is the **compact scoping strip** for a required
|
|
666
|
+
* single choice that qualifies the content beside it: which status a list is
|
|
667
|
+
* filtered by, which period a summary describes.
|
|
668
|
+
*
|
|
669
|
+
* Neither is a filter chip. A chip is for an *optional* filter that can be
|
|
670
|
+
* cleared; both of these always have exactly one option selected, so neither
|
|
671
|
+
* ever renders a clear affordance.
|
|
672
|
+
*/
|
|
673
|
+
interface UnderlineTab {
|
|
674
|
+
value: string;
|
|
675
|
+
/**
|
|
676
|
+
* A plain string for an ordinary tab; a node when the tab carries an
|
|
677
|
+
* annotation beside its name, such as a status badge showing the state of
|
|
678
|
+
* the section behind it.
|
|
679
|
+
*/
|
|
680
|
+
label: ReactNode;
|
|
681
|
+
}
|
|
682
|
+
/**
|
|
683
|
+
* Page-level tab bar with a single shared indicator that slides between tabs,
|
|
684
|
+
* rather than each tab drawing its own underline.
|
|
685
|
+
*
|
|
686
|
+
* The indicator's position and width are measured from the DOM: text tabs have
|
|
687
|
+
* different widths, so this cannot be derived from props or state. It sits
|
|
688
|
+
* flush on the row's own bottom border instead of floating below the label.
|
|
689
|
+
*
|
|
690
|
+
* `actions` renders flush right on the same row, tabs staying left-aligned,
|
|
691
|
+
* which is where a page puts its primary CTA.
|
|
692
|
+
*/
|
|
693
|
+
declare function UnderlineTabs({ tabs, value, onValueChange, actions, className, }: {
|
|
694
|
+
tabs: readonly UnderlineTab[];
|
|
695
|
+
value: string;
|
|
696
|
+
onValueChange: (value: string) => void;
|
|
697
|
+
actions?: ReactNode;
|
|
698
|
+
className?: string;
|
|
699
|
+
}): React$1.JSX.Element;
|
|
700
|
+
interface SegmentedTabOption<T extends string = string> {
|
|
701
|
+
value: T;
|
|
702
|
+
label: string;
|
|
703
|
+
}
|
|
704
|
+
/**
|
|
705
|
+
* The compact strip that scopes the content beside it.
|
|
706
|
+
*
|
|
707
|
+
* Plain underlined triggers, not the `Tabs` pill look: no container
|
|
708
|
+
* background, border or padding — a gap row of triggers, each just an
|
|
709
|
+
* underline and a colour change when active. Still Radix `Tabs` underneath, so
|
|
710
|
+
* keyboard navigation and `aria-selected` come free; only the classes differ.
|
|
711
|
+
*
|
|
712
|
+
* With `collapseToSelect`, the strip becomes a `Select` below `md`. Both
|
|
713
|
+
* controls drive the same state, so resizing mid-session can never leave the
|
|
714
|
+
* two disagreeing about which option is chosen.
|
|
715
|
+
*/
|
|
716
|
+
declare function SegmentedTabs<T extends string>({ options, value, onValueChange, label, collapseToSelect, className, }: {
|
|
717
|
+
options: readonly SegmentedTabOption<T>[];
|
|
718
|
+
value: T;
|
|
719
|
+
onValueChange: (value: T) => void;
|
|
720
|
+
/** Accessible name for both controls. */
|
|
721
|
+
label?: string;
|
|
722
|
+
/**
|
|
723
|
+
* Swap to a `Select` below `md`. Leave on for a strip that would otherwise
|
|
724
|
+
* crowd a narrow screen; turn it off where the row already has the room and
|
|
725
|
+
* a dropdown would read as a different control appearing.
|
|
726
|
+
*/
|
|
727
|
+
collapseToSelect?: boolean;
|
|
728
|
+
className?: string;
|
|
729
|
+
}): React$1.JSX.Element;
|
|
730
|
+
|
|
657
731
|
declare const Accordion: React$1.ForwardRefExoticComponent<(AccordionPrimitive.AccordionSingleProps | AccordionPrimitive.AccordionMultipleProps) & React$1.RefAttributes<HTMLDivElement>>;
|
|
658
732
|
declare const AccordionItem: React$1.ForwardRefExoticComponent<Omit<AccordionPrimitive.AccordionItemProps & React$1.RefAttributes<HTMLDivElement>, "ref"> & React$1.RefAttributes<HTMLDivElement>>;
|
|
659
733
|
declare const AccordionTrigger: React$1.ForwardRefExoticComponent<Omit<AccordionPrimitive.AccordionTriggerProps & React$1.RefAttributes<HTMLButtonElement>, "ref"> & React$1.RefAttributes<HTMLButtonElement>>;
|
|
@@ -753,7 +827,106 @@ declare function ChartSkeleton({ height }: {
|
|
|
753
827
|
|
|
754
828
|
type DataTableDensity = "default" | "comfortable" | "compact";
|
|
755
829
|
type DataTableHeaderStyle = "surface" | "minimal";
|
|
756
|
-
|
|
830
|
+
/**
|
|
831
|
+
* Footer summary text.
|
|
832
|
+
* - `range` — "Showing 1–15 of 141 results" (or "Showing 1–15" when no total
|
|
833
|
+
* is knowable, i.e. cursor pagination).
|
|
834
|
+
* - `count` — "141 items".
|
|
835
|
+
* - `none` — no summary, just the pager.
|
|
836
|
+
*/
|
|
837
|
+
type DataTableFooterSummary = "range" | "count" | "none";
|
|
838
|
+
type SortOrder = "ascend" | "descend";
|
|
839
|
+
/** `null` means unsorted — the table is in the order the data arrived in. */
|
|
840
|
+
type DataTableSortState = {
|
|
841
|
+
columnKey: string;
|
|
842
|
+
order: SortOrder;
|
|
843
|
+
} | null;
|
|
844
|
+
/** Controls shared by every paginated mode. */
|
|
845
|
+
type PagerCommon = {
|
|
846
|
+
/**
|
|
847
|
+
* Summary text at the left of the footer. Defaults to `range`.
|
|
848
|
+
*/
|
|
849
|
+
summary?: DataTableFooterSummary;
|
|
850
|
+
/** Noun after the number when `summary="count"`. Default `item` / `items`. */
|
|
851
|
+
countLabels?: {
|
|
852
|
+
singular: string;
|
|
853
|
+
plural: string;
|
|
854
|
+
};
|
|
855
|
+
/**
|
|
856
|
+
* Page-size choices. Pass these — with `onPageSizeChange` — and the footer
|
|
857
|
+
* grows a "Rows per page" picker at its far left. Omit and there is none.
|
|
858
|
+
*
|
|
859
|
+
* This lives here rather than being hand-passed as a footer slot because a
|
|
860
|
+
* grid that had to hand-roll its own page-size control is how two different
|
|
861
|
+
* pagers end up in the same app.
|
|
862
|
+
*/
|
|
863
|
+
pageSizeOptions?: readonly number[];
|
|
864
|
+
onPageSizeChange?: (size: number) => void;
|
|
865
|
+
/** Escape hatch: an extra control at the far left, before the summary. */
|
|
866
|
+
leading?: ReactNode;
|
|
867
|
+
};
|
|
868
|
+
/**
|
|
869
|
+
* How the table pages. Which member you use is decided by the endpoint, not by
|
|
870
|
+
* taste:
|
|
871
|
+
*
|
|
872
|
+
* - `client` — every row is already in `data`; the table slices it. The
|
|
873
|
+
* default when `pagination` is omitted.
|
|
874
|
+
* - `page` — the response carries a row **total**, so the footer can show
|
|
875
|
+
* "Showing 1–15 of 141 results" and a full numbered strip with ellipses.
|
|
876
|
+
* - `cursor` — the response carries no total (a `nextCursor` /
|
|
877
|
+
* `exclusiveStartKey` API). The footer shows "Showing 1–15" with **no**
|
|
878
|
+
* total and no page count, and the numbered strip is only ever the page
|
|
879
|
+
* before, the current page, and — when `hasNext` says so — the page after.
|
|
880
|
+
* Those are the only pages a cursor can actually reach in one step, so they
|
|
881
|
+
* are the only ones offered.
|
|
882
|
+
* - `none` — no footer at all.
|
|
883
|
+
*/
|
|
884
|
+
type DataTablePagination = ({
|
|
885
|
+
mode: "client";
|
|
886
|
+
/** Rows per page. Default 10. */
|
|
887
|
+
pageSize?: number;
|
|
888
|
+
} & PagerCommon) | ({
|
|
889
|
+
mode: "page";
|
|
890
|
+
/** 1-indexed. */
|
|
891
|
+
page: number;
|
|
892
|
+
pageSize: number;
|
|
893
|
+
/** Total rows across all pages, from the response. */
|
|
894
|
+
total: number;
|
|
895
|
+
onPageChange: (page: number) => void;
|
|
896
|
+
} & PagerCommon) | ({
|
|
897
|
+
mode: "cursor";
|
|
898
|
+
/** 1-indexed, for the "Showing x–y" range and the page marker. */
|
|
899
|
+
page: number;
|
|
900
|
+
pageSize: number;
|
|
901
|
+
/** Whether a page exists after this one. Drives the next control. */
|
|
902
|
+
hasNext: boolean;
|
|
903
|
+
/** Defaults to `page > 1`. */
|
|
904
|
+
hasPrev?: boolean;
|
|
905
|
+
onNext: () => void;
|
|
906
|
+
onPrev: () => void;
|
|
907
|
+
} & PagerCommon) | {
|
|
908
|
+
mode: "none";
|
|
909
|
+
};
|
|
910
|
+
/**
|
|
911
|
+
* Sorting, modelled on antd's `Table`: a column opts in with `sorter`, and the
|
|
912
|
+
* table reports state as `{ columnKey, order }` with antd's `"ascend"` /
|
|
913
|
+
* `"descend"` vocabulary.
|
|
914
|
+
*
|
|
915
|
+
* The one deliberate difference is that client and server sorting are told
|
|
916
|
+
* apart by the **column**, not by a table-level flag: `sorter: true` means "the
|
|
917
|
+
* caller orders this", a comparator means "the table orders this". A grid can
|
|
918
|
+
* therefore mix the two, which matters when one column is a derived value the
|
|
919
|
+
* server does not know about.
|
|
920
|
+
*/
|
|
921
|
+
type DataTableSorting = {
|
|
922
|
+
/**
|
|
923
|
+
* Controlled sort state. Omit for uncontrolled — the table remembers, which
|
|
924
|
+
* is all a client-sorted grid needs.
|
|
925
|
+
*/
|
|
926
|
+
value?: DataTableSortState;
|
|
927
|
+
/** Fires on every header activation, with the state being moved to. */
|
|
928
|
+
onChange?: (next: DataTableSortState) => void;
|
|
929
|
+
};
|
|
757
930
|
/**
|
|
758
931
|
* Row expansion: a disclosure column plus a full-width panel rendered directly
|
|
759
932
|
* beneath the expanded row. Use it when the detail belongs *with* the row in
|
|
@@ -797,6 +970,33 @@ type Column<T> = {
|
|
|
797
970
|
wrap?: boolean;
|
|
798
971
|
/** Extra classes on `<th>` / `<td>` (e.g. wider horizontal padding per column) */
|
|
799
972
|
cellClassName?: string;
|
|
973
|
+
/**
|
|
974
|
+
* Inline styles on `<th>` / `<td>`.
|
|
975
|
+
*
|
|
976
|
+
* For a value Tailwind cannot generate a class for because it is computed —
|
|
977
|
+
* a sticky column's `left`, which is the running total of the widths before
|
|
978
|
+
* it. Expressing that as a class means keeping a hand-written lookup table of
|
|
979
|
+
* every offset the layout can produce, and silently getting the wrong one the
|
|
980
|
+
* moment a column width changes. See `frozenColumn`.
|
|
981
|
+
*/
|
|
982
|
+
cellStyle?: CSSProperties;
|
|
983
|
+
/**
|
|
984
|
+
* Makes this header a sort control.
|
|
985
|
+
*
|
|
986
|
+
* - `true` — the **caller** orders the rows (a server-side `sortBy` query).
|
|
987
|
+
* The table reports the change through `sorting.onChange` and leaves `data`
|
|
988
|
+
* exactly as given.
|
|
989
|
+
* - a comparator — the **table** orders the rows with it, before paging.
|
|
990
|
+
* Same contract as `Array.prototype.sort`'s argument, and the same as
|
|
991
|
+
* antd's `sorter`.
|
|
992
|
+
*/
|
|
993
|
+
sorter?: boolean | ((a: T, b: T) => number);
|
|
994
|
+
/**
|
|
995
|
+
* The orders this header cycles through before returning to unsorted.
|
|
996
|
+
* Default `["ascend", "descend"]`. Pass `["descend", "ascend"]` for a column
|
|
997
|
+
* where "most recent" or "largest" is the obvious first click.
|
|
998
|
+
*/
|
|
999
|
+
sortDirections?: SortOrder[];
|
|
800
1000
|
render: (row: T, index: number) => ReactNode;
|
|
801
1001
|
};
|
|
802
1002
|
interface DataTableProps<T> {
|
|
@@ -806,13 +1006,13 @@ interface DataTableProps<T> {
|
|
|
806
1006
|
skeletonRows?: number;
|
|
807
1007
|
emptyTitle?: string;
|
|
808
1008
|
emptyDescription?: string;
|
|
809
|
-
|
|
810
|
-
|
|
811
|
-
|
|
812
|
-
|
|
813
|
-
|
|
814
|
-
/**
|
|
815
|
-
|
|
1009
|
+
/**
|
|
1010
|
+
* Pagination and the footer that carries it. Omit for client-side paging at
|
|
1011
|
+
* 10 rows a page. See {@link DataTablePagination}.
|
|
1012
|
+
*/
|
|
1013
|
+
pagination?: DataTablePagination;
|
|
1014
|
+
/** Column sorting. See {@link DataTableSorting}. */
|
|
1015
|
+
sorting?: DataTableSorting;
|
|
816
1016
|
className?: string;
|
|
817
1017
|
rowKey: (row: T) => string;
|
|
818
1018
|
/** Optional hover CTA shown on the right of every row */
|
|
@@ -863,28 +1063,448 @@ interface DataTableProps<T> {
|
|
|
863
1063
|
tableLayout?: "auto" | "fixed" | "content";
|
|
864
1064
|
theadClassName?: string;
|
|
865
1065
|
headerStyle?: DataTableHeaderStyle;
|
|
866
|
-
/** Footer: paginated range vs simple `n items` */
|
|
867
|
-
footerSummary?: DataTableFooterSummary;
|
|
868
|
-
/** Noun after the count when `footerSummary="count"` (default singular / plural `item` / `items`). */
|
|
869
|
-
footerCountLabels?: {
|
|
870
|
-
singular: string;
|
|
871
|
-
plural: string;
|
|
872
|
-
};
|
|
873
1066
|
/** With `density="compact"`, use tighter cell gutters (`pl-1.5 pr-2.5` vs `px-3`). Footer keeps normal horizontal padding. */
|
|
874
1067
|
snug?: boolean;
|
|
1068
|
+
/** Per-row disclosure panel rendered beneath the row. See `DataTableExpandable`. */
|
|
1069
|
+
expandable?: DataTableExpandable<T>;
|
|
1070
|
+
}
|
|
1071
|
+
declare function DataTable<T>({ columns, data, isLoading, skeletonRows, emptyTitle, emptyDescription, pagination, sorting, className, rowKey, rowCta, rowAction, onRowClick, density, tableLayout, theadClassName, headerStyle, snug, expandable, }: DataTableProps<T>): React$1.JSX.Element;
|
|
1072
|
+
/**
|
|
1073
|
+
* The classes and offset for a column frozen to the left edge, so a grid that
|
|
1074
|
+
* pins its identifier columns does not have to reinvent the recipe. Five
|
|
1075
|
+
* feature files in the internal console had five copies of it, all carrying the
|
|
1076
|
+
* same two faults below.
|
|
1077
|
+
*
|
|
1078
|
+
* Spread the result onto the column:
|
|
1079
|
+
*
|
|
1080
|
+
* ```tsx
|
|
1081
|
+
* let left = 0;
|
|
1082
|
+
* columns.map((col) => {
|
|
1083
|
+
* if (!FROZEN.includes(col.key)) return col;
|
|
1084
|
+
* const frozen = { ...col, ...frozenColumn({ left, isLast: col.key === lastFrozen }) };
|
|
1085
|
+
* left += widthOf(col);
|
|
1086
|
+
* return frozen;
|
|
1087
|
+
* });
|
|
1088
|
+
* ```
|
|
1089
|
+
*
|
|
1090
|
+
* Two things it gets right that a hand-rolled version tends not to:
|
|
1091
|
+
*
|
|
1092
|
+
* **The background is opaque and is the table's own.** It has to be opaque or
|
|
1093
|
+
* the rows scrolling underneath show through the pinned block. It should not be
|
|
1094
|
+
* a tint, because a tint at full strength next to a row that highlights at 40%
|
|
1095
|
+
* makes the frozen block the heaviest thing on screen — the divider and the
|
|
1096
|
+
* shadow are what say "pinned", and the shadow is the honest signal anyway,
|
|
1097
|
+
* since it is what reads as content passing underneath.
|
|
1098
|
+
*
|
|
1099
|
+
* **It follows the row's hover.** The frozen cells are part of the row; pinning
|
|
1100
|
+
* them to a fixed colour makes a hovered row highlight in two different shades
|
|
1101
|
+
* and read as two rows. The hover colour is mixed rather than given an alpha,
|
|
1102
|
+
* for the same opacity reason.
|
|
1103
|
+
*/
|
|
1104
|
+
declare function frozenColumn({ left, right, isLast, }: {
|
|
1105
|
+
/** Offset from the left edge in px — the widths of the frozen columns before this one. */
|
|
1106
|
+
left?: number;
|
|
1107
|
+
/** Offset from the right edge in px, for a column pinned to that side instead. */
|
|
1108
|
+
right?: number;
|
|
1109
|
+
/** The column at the boundary, which carries the divider and the shadow. */
|
|
1110
|
+
isLast?: boolean;
|
|
1111
|
+
}): Pick<Column<unknown>, "cellClassName" | "cellStyle">;
|
|
1112
|
+
|
|
1113
|
+
/**
|
|
1114
|
+
* The canonical table surface: one bordered card holding a title, tabs, a
|
|
1115
|
+
* filter toolbar, the grid, and a footer — in that order, with the same
|
|
1116
|
+
* dividers and gutters every time.
|
|
1117
|
+
*
|
|
1118
|
+
* `DataTable` on its own is the grid. This is everything around it, and it
|
|
1119
|
+
* exists because that surrounding chrome is where tables actually drift: one
|
|
1120
|
+
* feature puts its filters above the card, another inside it; one draws a
|
|
1121
|
+
* divider under the tabs, another does not; one pads the toolbar `py-3` and the
|
|
1122
|
+
* next `py-2.5`. None of that is a decision a feature should be making.
|
|
1123
|
+
*
|
|
1124
|
+
* Pagination goes through `pagination`, in every mode — including cursor APIs
|
|
1125
|
+
* that carry no row total. The `footer` slot is for a footer that is genuinely
|
|
1126
|
+
* not a pager; passing one hides the table's own.
|
|
1127
|
+
*/
|
|
1128
|
+
interface DataTableCardProps<T> {
|
|
1129
|
+
columns: Column<T>[];
|
|
1130
|
+
data: T[];
|
|
1131
|
+
rowKey: (row: T) => string;
|
|
1132
|
+
isLoading?: boolean;
|
|
875
1133
|
/**
|
|
876
|
-
*
|
|
877
|
-
* "
|
|
1134
|
+
* Section title. For a grid that names itself — an analytics section like
|
|
1135
|
+
* "Top 10 Merchants by Volume" — rather than a page-level grid, whose name is
|
|
1136
|
+
* the page header. Renders above `tabs`.
|
|
1137
|
+
*/
|
|
1138
|
+
title?: string;
|
|
1139
|
+
/** One line under the title. Only meaningful with `title`. */
|
|
1140
|
+
description?: string;
|
|
1141
|
+
/** Controls on the title row, flush right (a period toggle, Refresh, …). */
|
|
1142
|
+
actions?: ReactNode;
|
|
1143
|
+
/** Tab bar near the top of the card, above the toolbar. */
|
|
1144
|
+
tabs?: ReactNode;
|
|
1145
|
+
/** Filters / search / action buttons, as a row inside the card top. */
|
|
1146
|
+
toolbar?: ReactNode;
|
|
1147
|
+
/**
|
|
1148
|
+
* A non-pager footer inside the card bottom. Hides the table's own footer, so
|
|
1149
|
+
* do NOT use it for pagination — that is what `pagination` is for, in every
|
|
1150
|
+
* mode. Hand-rolling a pager here is how two different pagers end up in one
|
|
1151
|
+
* app.
|
|
1152
|
+
*/
|
|
1153
|
+
footer?: ReactNode;
|
|
1154
|
+
emptyTitle?: string;
|
|
1155
|
+
emptyDescription?: string;
|
|
1156
|
+
/**
|
|
1157
|
+
* Replaces the grid when there are no rows — an illustrated placeholder,
|
|
1158
|
+
* typically.
|
|
878
1159
|
*
|
|
879
|
-
*
|
|
880
|
-
*
|
|
881
|
-
*
|
|
1160
|
+
* `emptyTitle` / `emptyDescription` give the table's own text-only empty
|
|
1161
|
+
* state, which keeps the column headers and is right for "nothing matched
|
|
1162
|
+
* your filters". This is for the first-run case, where there is no data yet
|
|
1163
|
+
* because none has ever existed, and a drawn state says that better than a
|
|
1164
|
+
* header row over nothing.
|
|
882
1165
|
*/
|
|
883
|
-
|
|
884
|
-
/**
|
|
1166
|
+
emptyState?: ReactNode;
|
|
1167
|
+
/**
|
|
1168
|
+
* Replaces the rows entirely when the request failed.
|
|
1169
|
+
*
|
|
1170
|
+
* Distinct from an empty result with error-worded copy: that keeps the
|
|
1171
|
+
* column headers, which is right for "nothing matched" and wrong for "we
|
|
1172
|
+
* could not load this" — headers imply data was fetched and found empty.
|
|
1173
|
+
*/
|
|
1174
|
+
errorState?: ReactNode;
|
|
1175
|
+
/** See {@link DataTablePagination}. Omit for client-side paging at 10/page. */
|
|
1176
|
+
pagination?: DataTablePagination;
|
|
1177
|
+
/** See {@link DataTableSorting}. */
|
|
1178
|
+
sorting?: DataTableSorting;
|
|
1179
|
+
rowAction?: ReactNode | ((row: T, index: number) => ReactNode);
|
|
1180
|
+
/**
|
|
1181
|
+
* Makes the whole row a click target, for a grid that drills into a detail
|
|
1182
|
+
* view. Passed straight through, so it brings the keyboard affordances with
|
|
1183
|
+
* it and does not fire for clicks landing on a button, link or form control
|
|
1184
|
+
* inside a cell.
|
|
1185
|
+
*/
|
|
1186
|
+
onRowClick?: (row: T, index: number) => void;
|
|
1187
|
+
/** Defaults to "content"; pass "fixed" for grids with frozen sticky columns. */
|
|
1188
|
+
tableLayout?: "auto" | "fixed" | "content";
|
|
1189
|
+
/** Per-row disclosure panel rendered beneath the row. */
|
|
885
1190
|
expandable?: DataTableExpandable<T>;
|
|
1191
|
+
/** Row rhythm. Defaults to `compact`, which is what a data-dense grid wants. */
|
|
1192
|
+
density?: DataTableDensity;
|
|
1193
|
+
skeletonRows?: number;
|
|
1194
|
+
/**
|
|
1195
|
+
* CSS max-height for the internally scrolling body, so the toolbar and footer
|
|
1196
|
+
* stay put while the rows scroll and the page itself does not grow.
|
|
1197
|
+
*
|
|
1198
|
+
* The default assumes a page header plus this card's toolbar; a card that
|
|
1199
|
+
* also carries a `tabs` row needs a smaller cap, or the page starts scrolling
|
|
1200
|
+
* as well. Pass `"none"` to let the card grow with its content instead.
|
|
1201
|
+
*/
|
|
1202
|
+
maxBodyHeight?: string;
|
|
1203
|
+
className?: string;
|
|
886
1204
|
}
|
|
887
|
-
declare function
|
|
1205
|
+
declare function DataTableCard<T>({ columns, data, rowKey, isLoading, title, description, actions, tabs, toolbar, footer, emptyTitle, emptyDescription, emptyState, errorState, pagination, sorting, rowAction, onRowClick, tableLayout, expandable, density, skeletonRows, maxBodyHeight, className, }: DataTableCardProps<T>): React$1.JSX.Element;
|
|
1206
|
+
/** Right-aligned group for toolbar action buttons. */
|
|
1207
|
+
declare function TableToolbarActions({ children }: {
|
|
1208
|
+
children: ReactNode;
|
|
1209
|
+
}): React$1.JSX.Element;
|
|
1210
|
+
|
|
1211
|
+
/**
|
|
1212
|
+
* The narrow-viewport counterpart to {@link DataTableCard}: the same records as
|
|
1213
|
+
* a stack of cards.
|
|
1214
|
+
*
|
|
1215
|
+
* It is a separate component rather than a mode of the table on purpose. A
|
|
1216
|
+
* card list is not a table with its columns hidden — it chooses a handful of
|
|
1217
|
+
* fields, gives them a hierarchy, and drops the rest. Folding that into
|
|
1218
|
+
* `DataTableCard` would mean one component carrying two layouts and a
|
|
1219
|
+
* breakpoint, and every table paying for props it does not use.
|
|
1220
|
+
*
|
|
1221
|
+
* Pair the two with CSS, not a media-query hook:
|
|
1222
|
+
*
|
|
1223
|
+
* ```tsx
|
|
1224
|
+
* <DataTableCard className="hidden lg:block" … />
|
|
1225
|
+
* <DataCardList className="lg:hidden" … />
|
|
1226
|
+
* ```
|
|
1227
|
+
*
|
|
1228
|
+
* Both render; CSS shows one. A JS breakpoint would have to start with a guess
|
|
1229
|
+
* on the server, so one cohort sees the wrong layout on first paint, and a
|
|
1230
|
+
* resize across the breakpoint unmounts the visible half — taking scroll
|
|
1231
|
+
* position and any open row with it.
|
|
1232
|
+
*
|
|
1233
|
+
* What it owns is the surface, not the card: the bordered container, the
|
|
1234
|
+
* loading skeletons, the empty state and the pager. Those are the four things
|
|
1235
|
+
* every hand-rolled card list in the apps reimplemented, and the four that had
|
|
1236
|
+
* drifted. The card itself stays with the feature, via `renderCard` — that is
|
|
1237
|
+
* the part that genuinely differs per record.
|
|
1238
|
+
*/
|
|
1239
|
+
interface DataCardListProps<T> {
|
|
1240
|
+
rows: T[];
|
|
1241
|
+
rowKey: (row: T) => string;
|
|
1242
|
+
/** One record as a card. The only part a feature has to write. */
|
|
1243
|
+
renderCard: (row: T, index: number) => ReactNode;
|
|
1244
|
+
isLoading?: boolean;
|
|
1245
|
+
/**
|
|
1246
|
+
* The loading placeholder for one card. Omit for a generic card-shaped
|
|
1247
|
+
* shimmer — good enough for most lists, and worth replacing only where the
|
|
1248
|
+
* real card has a distinctive shape worth pre-announcing.
|
|
1249
|
+
*/
|
|
1250
|
+
renderSkeleton?: (index: number) => ReactNode;
|
|
1251
|
+
skeletonCount?: number;
|
|
1252
|
+
emptyTitle?: string;
|
|
1253
|
+
emptyDescription?: string;
|
|
1254
|
+
/**
|
|
1255
|
+
* Replaces the list when there are no rows — an illustrated placeholder,
|
|
1256
|
+
* typically. Same split as `DataTableCard`: the title/description pair is the
|
|
1257
|
+
* plain "nothing matched" state, this is the drawn first-run one.
|
|
1258
|
+
*/
|
|
1259
|
+
emptyState?: ReactNode;
|
|
1260
|
+
/**
|
|
1261
|
+
* Replaces the rows entirely when the request failed.
|
|
1262
|
+
*
|
|
1263
|
+
* Distinct from an empty result with error-worded copy: that keeps the
|
|
1264
|
+
* column headers, which is right for "nothing matched" and wrong for "we
|
|
1265
|
+
* could not load this" — headers imply data was fetched and found empty.
|
|
1266
|
+
*/
|
|
1267
|
+
errorState?: ReactNode;
|
|
1268
|
+
/**
|
|
1269
|
+
* The same {@link DataTablePagination} the table takes, so a list and the
|
|
1270
|
+
* table beside it cannot disagree about which page they are on. Rendered as
|
|
1271
|
+
* a compact Prev / Next pager rather than a numbered strip: a row of page
|
|
1272
|
+
* numbers is the first thing to go wrong on a phone.
|
|
1273
|
+
*/
|
|
1274
|
+
pagination?: DataTablePagination;
|
|
1275
|
+
/** Wraps the list in the same bordered card the table uses. Default true. */
|
|
1276
|
+
bordered?: boolean;
|
|
1277
|
+
className?: string;
|
|
1278
|
+
}
|
|
1279
|
+
declare function DataCardList<T>({ rows, rowKey, renderCard, isLoading, renderSkeleton, skeletonCount, emptyTitle, emptyDescription, emptyState, errorState, pagination, bordered, className, }: DataCardListProps<T>): React$1.JSX.Element;
|
|
1280
|
+
|
|
1281
|
+
interface CopyableCellProps {
|
|
1282
|
+
/**
|
|
1283
|
+
* The full value. This is what reaches the clipboard, the tooltip and the
|
|
1284
|
+
* accessible name — always, even when `display` shortens what is on screen.
|
|
1285
|
+
* Shortening what is shown must never shorten what the user walks away with.
|
|
1286
|
+
*/
|
|
1287
|
+
value?: string | null;
|
|
1288
|
+
/** What to render instead of `value` — an elided form, typically. */
|
|
1289
|
+
display?: string;
|
|
1290
|
+
/** The noun in the tooltip and the toast: "Transaction ID copied". */
|
|
1291
|
+
label?: string;
|
|
1292
|
+
/**
|
|
1293
|
+
* Makes the value the handle that opens the row, rendered as a link. Without
|
|
1294
|
+
* it the value is plain text that can still be copied.
|
|
1295
|
+
*
|
|
1296
|
+
* The copy button stops propagation, so an id that opens a row does not also
|
|
1297
|
+
* open it when copied.
|
|
1298
|
+
*/
|
|
1299
|
+
onClick?: () => void;
|
|
1300
|
+
/** Render the value in the primary colour. */
|
|
1301
|
+
accent?: boolean;
|
|
1302
|
+
monospace?: boolean;
|
|
1303
|
+
/** What an absent value renders as. Default the em dash every grid uses. */
|
|
1304
|
+
fallback?: string;
|
|
1305
|
+
/**
|
|
1306
|
+
* - `inline` (default) — the value, with its own copy button beside it.
|
|
1307
|
+
* - `cell` — the **whole** element is the copy target and the value
|
|
1308
|
+
* underlines on hover. For a fixed-width column where a separate button
|
|
1309
|
+
* would cost more room than the value it copies.
|
|
1310
|
+
*
|
|
1311
|
+
* `cell` ignores `onClick`: a cell cannot both copy and open the row on the
|
|
1312
|
+
* same click.
|
|
1313
|
+
*/
|
|
1314
|
+
variant?: "inline" | "cell";
|
|
1315
|
+
/**
|
|
1316
|
+
* Keep the copy button invisible until the row (or any `group` ancestor) is
|
|
1317
|
+
* hovered, or the button itself is focused. Opacity only — it keeps its
|
|
1318
|
+
* space, so revealing it never shifts the row.
|
|
1319
|
+
*
|
|
1320
|
+
* Defaults to `true`, which is what a table wants: twelve permanent copy
|
|
1321
|
+
* buttons are twelve pieces of chrome competing with the data. Pass `false`
|
|
1322
|
+
* for a detail field, where there is no row to hover and the control would
|
|
1323
|
+
* simply never appear. Pointer-coarse devices have no hover to give, so it
|
|
1324
|
+
* stays visible there regardless.
|
|
1325
|
+
*/
|
|
1326
|
+
revealOnHover?: boolean;
|
|
1327
|
+
/**
|
|
1328
|
+
* Announce the copy with a toast. Off for a field whose tick and tooltip are
|
|
1329
|
+
* feedback enough, and where a toast per copy would be noise.
|
|
1330
|
+
*/
|
|
1331
|
+
showToast?: boolean;
|
|
1332
|
+
/** Extra classes on the value itself, e.g. a muted secondary placement. */
|
|
1333
|
+
valueClassName?: string;
|
|
1334
|
+
className?: string;
|
|
1335
|
+
}
|
|
1336
|
+
/**
|
|
1337
|
+
* The canonical identifier cell: a value plus a copy button that fades in on
|
|
1338
|
+
* the row's hover.
|
|
1339
|
+
*
|
|
1340
|
+
* Reveal-on-hover is the point. A table of twelve ids with twelve permanent
|
|
1341
|
+
* copy buttons is twelve pieces of chrome competing with the data; the row the
|
|
1342
|
+
* pointer is on is the only one whose button is useful.
|
|
1343
|
+
*
|
|
1344
|
+
* It relies on the row's own `group` class, which every `DataTable` `<tr>`
|
|
1345
|
+
* already carries. Outside a DataTable row, pass `className="group"` on an
|
|
1346
|
+
* ancestor or the button stays hidden.
|
|
1347
|
+
*/
|
|
1348
|
+
declare function CopyableCell({ value, display, label, onClick, accent, monospace, fallback, variant, revealOnHover, showToast, valueClassName, className, }: CopyableCellProps): React$1.JSX.Element;
|
|
1349
|
+
|
|
1350
|
+
interface RotatingSearchInputProps {
|
|
1351
|
+
/** Controlled value. Omit to let the field own it. */
|
|
1352
|
+
value?: string;
|
|
1353
|
+
/** Fires on the debounced value, not on every keystroke. */
|
|
1354
|
+
onSearch: (value: string) => void;
|
|
1355
|
+
/** The hints to cycle through: "Amount", "Transaction ID", "Email". */
|
|
1356
|
+
words: string[];
|
|
1357
|
+
/** Debounce before `onSearch` fires. Default 300ms. */
|
|
1358
|
+
debounceDelay?: number;
|
|
1359
|
+
className?: string;
|
|
1360
|
+
/** Screen-reader name for the field. */
|
|
1361
|
+
ariaLabel?: string;
|
|
1362
|
+
}
|
|
1363
|
+
/**
|
|
1364
|
+
* The table search box: one field whose placeholder cycles through what it can
|
|
1365
|
+
* actually match — "Search by Amount", then "Transaction ID", then "Email".
|
|
1366
|
+
*
|
|
1367
|
+
* That rotation is the whole point. A single grid search usually spans half a
|
|
1368
|
+
* dozen fields, and a static "Search" placeholder tells the user none of them,
|
|
1369
|
+
* so they guess at what is searchable and conclude the box is broken when their
|
|
1370
|
+
* guess misses. Naming the fields in turn costs no space and answers it.
|
|
1371
|
+
*
|
|
1372
|
+
* `onSearch` is debounced, so a search that hits the network fires once the
|
|
1373
|
+
* user pauses rather than once per keystroke.
|
|
1374
|
+
*/
|
|
1375
|
+
declare function RotatingSearchInput({ value, onSearch, words, debounceDelay, className, ariaLabel, }: RotatingSearchInputProps): React$1.JSX.Element;
|
|
1376
|
+
|
|
1377
|
+
/**
|
|
1378
|
+
* A column as the manager sees it: a key and something to call it in the list.
|
|
1379
|
+
* `label` is separate from the table's `header` because a header can be a node
|
|
1380
|
+
* (an icon, a tooltip, a two-line stack) and this list needs plain text.
|
|
1381
|
+
*/
|
|
1382
|
+
interface ManagedColumn {
|
|
1383
|
+
key: string;
|
|
1384
|
+
label: string;
|
|
1385
|
+
}
|
|
1386
|
+
interface ColumnManagerProps {
|
|
1387
|
+
/** Every manageable column, in the table's *declared* order. */
|
|
1388
|
+
columns: ManagedColumn[];
|
|
1389
|
+
/** Current arrangement, as column keys. */
|
|
1390
|
+
order: string[];
|
|
1391
|
+
onOrderChange: (order: string[]) => void;
|
|
1392
|
+
/**
|
|
1393
|
+
* Column keys currently hidden. Omit — along with `onHiddenKeysChange` — to
|
|
1394
|
+
* drop the tick boxes entirely and keep this a reorder-only popover.
|
|
1395
|
+
*/
|
|
1396
|
+
hiddenKeys?: string[];
|
|
1397
|
+
onHiddenKeysChange?: (hidden: string[]) => void;
|
|
1398
|
+
/**
|
|
1399
|
+
* Columns that cannot be **hidden**. Says nothing about where they sit — a
|
|
1400
|
+
* column the table cannot do without is still one the user may want to move.
|
|
1401
|
+
*
|
|
1402
|
+
* They keep a tick box rather than losing it, so the list reads as one set of
|
|
1403
|
+
* columns with some locked rather than as two lists — but the box is grey,
|
|
1404
|
+
* not primary, because nobody chose it.
|
|
1405
|
+
*/
|
|
1406
|
+
fixedKeys?: string[];
|
|
1407
|
+
/**
|
|
1408
|
+
* Columns that cannot be **reordered**. Says nothing about whether they can
|
|
1409
|
+
* be hidden.
|
|
1410
|
+
*
|
|
1411
|
+
* The usual case is a frozen (sticky) column: its left offset is the running
|
|
1412
|
+
* total of the widths of the frozen columns before it, so the block only
|
|
1413
|
+
* works while they stay first and contiguous — drag one into the middle and
|
|
1414
|
+
* it keeps `left-0`, leaving a pinned column floating over the scrolling
|
|
1415
|
+
* ones. Hiding it is fine; that just shortens the block.
|
|
1416
|
+
*
|
|
1417
|
+
* A key can appear in both lists, and then neither control is offered.
|
|
1418
|
+
*/
|
|
1419
|
+
pinnedKeys?: string[];
|
|
1420
|
+
/**
|
|
1421
|
+
* Why a fixed column cannot be hidden, shown on hover and focus. A disabled
|
|
1422
|
+
* control that stays silent leaves the user to guess whether they are doing
|
|
1423
|
+
* something wrong, so every caller should say something; the default is
|
|
1424
|
+
* deliberately generic so a missing one is still an answer.
|
|
1425
|
+
*/
|
|
1426
|
+
fixedReason?: string;
|
|
1427
|
+
/**
|
|
1428
|
+
* Why a pinned column cannot be moved, shown on hover. Same reasoning as
|
|
1429
|
+
* `fixedReason`: a dead affordance should say why it is dead.
|
|
1430
|
+
*/
|
|
1431
|
+
pinnedReason?: string;
|
|
1432
|
+
/**
|
|
1433
|
+
* Discards the saved arrangement so the table falls back to `columns`' own
|
|
1434
|
+
* order with nothing hidden. Separate from `onOrderChange` rather than
|
|
1435
|
+
* passing the default order through it, since "no saved preference" is its
|
|
1436
|
+
* own state in the caller, not just another arrangement.
|
|
1437
|
+
*/
|
|
1438
|
+
onReset: () => void;
|
|
1439
|
+
/** Trigger label. Default "Columns". */
|
|
1440
|
+
label?: string;
|
|
1441
|
+
/** Render the trigger as an icon-only button — for a crowded toolbar. */
|
|
1442
|
+
iconOnly?: boolean;
|
|
1443
|
+
/** Extra classes on the trigger button. */
|
|
1444
|
+
className?: string;
|
|
1445
|
+
/** Popover alignment against the trigger. Default "end". */
|
|
1446
|
+
align?: "start" | "center" | "end";
|
|
1447
|
+
}
|
|
1448
|
+
/**
|
|
1449
|
+
* Column manager: drag to reorder, tick to show or hide, with locked columns
|
|
1450
|
+
* and a reset. One implementation for every grid in every app, so a merchant
|
|
1451
|
+
* and an internal operator arrange their columns the same way.
|
|
1452
|
+
*
|
|
1453
|
+
* Reordering is @dnd-kit, the same stack the dashboard's widget grid uses, so
|
|
1454
|
+
* the rows animate out of each other's way as one is dragged past them. A
|
|
1455
|
+
* hand-rolled pointer drag can reorder the list correctly and still feel wrong:
|
|
1456
|
+
* without a per-row transform the rows simply teleport into their new slots,
|
|
1457
|
+
* and there is nothing to follow.
|
|
1458
|
+
*
|
|
1459
|
+
* dnd-kit is `external` in the build rather than bundled, because both
|
|
1460
|
+
* consuming apps already depend on it — two copies of `DndContext` in one app
|
|
1461
|
+
* is the kind of thing that breaks only in the app, never in the library.
|
|
1462
|
+
*
|
|
1463
|
+
* Keyboard users reorder without a mouse at all: Space picks a row up, the
|
|
1464
|
+
* arrows move it, Space drops it, Escape abandons it, and dnd-kit announces
|
|
1465
|
+
* each step as it goes.
|
|
1466
|
+
*/
|
|
1467
|
+
declare function ColumnManager({ columns, order, onOrderChange, hiddenKeys, onHiddenKeysChange, fixedKeys, pinnedKeys, fixedReason, pinnedReason, onReset, label, iconOnly, className, align, }: ColumnManagerProps): React$1.JSX.Element;
|
|
1468
|
+
interface ColumnPreferences {
|
|
1469
|
+
order: string[];
|
|
1470
|
+
hidden: string[];
|
|
1471
|
+
}
|
|
1472
|
+
interface UseColumnPreferencesOptions {
|
|
1473
|
+
/**
|
|
1474
|
+
* `localStorage` key. Omit and the arrangement lives only for the session —
|
|
1475
|
+
* which is what a grid whose columns depend on the signed-in user's role
|
|
1476
|
+
* wants, since a saved order from another role would resurrect columns that
|
|
1477
|
+
* no longer exist.
|
|
1478
|
+
*/
|
|
1479
|
+
storageKey?: string;
|
|
1480
|
+
}
|
|
1481
|
+
interface UseColumnPreferencesResult extends ColumnPreferences {
|
|
1482
|
+
setOrder: (order: string[]) => void;
|
|
1483
|
+
setHidden: (hidden: string[]) => void;
|
|
1484
|
+
reset: () => void;
|
|
1485
|
+
/** Spread straight onto `<ColumnManager>`. */
|
|
1486
|
+
managerProps: Pick<ColumnManagerProps, "order" | "onOrderChange" | "hiddenKeys" | "onHiddenKeysChange" | "onReset">;
|
|
1487
|
+
}
|
|
1488
|
+
/**
|
|
1489
|
+
* Owns a grid's column arrangement, optionally persisted.
|
|
1490
|
+
*
|
|
1491
|
+
* `defaultOrder` is the source of truth for which columns exist: a stored order
|
|
1492
|
+
* is reconciled against it on every read, so a column added in a release shows
|
|
1493
|
+
* up for someone who saved an arrangement before it existed, and a removed one
|
|
1494
|
+
* disappears instead of leaving a hole.
|
|
1495
|
+
*/
|
|
1496
|
+
declare function useColumnPreferences(defaultOrder: string[], { storageKey }?: UseColumnPreferencesOptions): UseColumnPreferencesResult;
|
|
1497
|
+
/**
|
|
1498
|
+
* Applies a saved arrangement to a built column list.
|
|
1499
|
+
*
|
|
1500
|
+
* `pinnedKeys` stay where they are declared regardless of the saved order —
|
|
1501
|
+
* for the trailing "action" column, which is a utility, not a data field
|
|
1502
|
+
* anybody wants to move. Columns missing from `order` (a field that only
|
|
1503
|
+
* exists for some roles, say) are appended before them rather than dropped.
|
|
1504
|
+
*/
|
|
1505
|
+
declare function applyColumnPreferences<T extends {
|
|
1506
|
+
key: string;
|
|
1507
|
+
}>(columns: T[], { order, hidden }?: Partial<ColumnPreferences>, pinnedKeys?: string[]): T[];
|
|
888
1508
|
|
|
889
1509
|
interface EmptyStateProps {
|
|
890
1510
|
icon?: LucideIcon;
|
|
@@ -963,10 +1583,19 @@ interface DatePickerProps {
|
|
|
963
1583
|
onChange: (v: string) => void;
|
|
964
1584
|
placeholder?: string;
|
|
965
1585
|
className?: string;
|
|
1586
|
+
/** Earliest selectable date, `YYYY-MM-DD`. Days before it are struck out. */
|
|
966
1587
|
min?: string;
|
|
1588
|
+
/**
|
|
1589
|
+
* Latest selectable date, `YYYY-MM-DD`.
|
|
1590
|
+
*
|
|
1591
|
+
* Its absence is why a feature ended up hand-rolling a whole date chip to
|
|
1592
|
+
* enforce an upper bound on Apply instead — an error after the fact, where
|
|
1593
|
+
* the calendar could have said so before the click.
|
|
1594
|
+
*/
|
|
1595
|
+
max?: string;
|
|
967
1596
|
label?: string;
|
|
968
1597
|
}
|
|
969
|
-
declare function DatePicker({ value, onChange, placeholder, className, min, label }: DatePickerProps): React$1.JSX.Element;
|
|
1598
|
+
declare function DatePicker({ value, onChange, placeholder, className, min, max, label }: DatePickerProps): React$1.JSX.Element;
|
|
970
1599
|
|
|
971
1600
|
/**
|
|
972
1601
|
* Chart primitives from shadcn/ui (Recharts composition layer).
|
|
@@ -1333,4 +1962,555 @@ interface VisuallyHiddenProps extends React$1.HTMLAttributes<HTMLElement> {
|
|
|
1333
1962
|
}
|
|
1334
1963
|
declare const VisuallyHidden: React$1.ForwardRefExoticComponent<VisuallyHiddenProps & React$1.RefAttributes<HTMLElement>>;
|
|
1335
1964
|
|
|
1336
|
-
|
|
1965
|
+
interface FilterChipOption {
|
|
1966
|
+
value: string;
|
|
1967
|
+
label: string;
|
|
1968
|
+
/** Optional leading glyph — a flag, a brand mark, a status dot. */
|
|
1969
|
+
icon?: ReactNode;
|
|
1970
|
+
/** Secondary text to the right, e.g. a matching count. */
|
|
1971
|
+
hint?: string;
|
|
1972
|
+
}
|
|
1973
|
+
/**
|
|
1974
|
+
* Wraps a row of filter chips so only one popover is open at a time — and,
|
|
1975
|
+
* crucially, so switching between two chips does not make the second one flash.
|
|
1976
|
+
*
|
|
1977
|
+
* The flash comes from every chip sharing one `openChip` value while Radix
|
|
1978
|
+
* reports the two halves of the switch as separate events: the chip being
|
|
1979
|
+
* *opened* fires `onOpenChange(true)` and the chip being *dismissed* fires
|
|
1980
|
+
* `onOpenChange(false)`. A naive `setOpenChip(open ? key : null)` lets whichever
|
|
1981
|
+
* event lands second win, so when the dismissal lands second it wipes out the
|
|
1982
|
+
* chip that just opened — it mounts, paints, and unmounts.
|
|
1983
|
+
*
|
|
1984
|
+
* The fix is that a close only counts if the chip closing is still the one on
|
|
1985
|
+
* screen. A stale dismissal from the chip the user just left is then a no-op,
|
|
1986
|
+
* whatever order the events arrive in. This lives here rather than in each
|
|
1987
|
+
* toolbar because it is invisible until it is wrong, and it was wrong in every
|
|
1988
|
+
* toolbar that hand-rolled it.
|
|
1989
|
+
*/
|
|
1990
|
+
declare function FilterChipGroup({ children, className, }: {
|
|
1991
|
+
children: ReactNode;
|
|
1992
|
+
className?: string;
|
|
1993
|
+
}): React$1.JSX.Element;
|
|
1994
|
+
/**
|
|
1995
|
+
* Open state for one chip. Inside a {@link FilterChipGroup} the group owns it
|
|
1996
|
+
* so opening this chip closes its siblings; outside one, the chip keeps its own
|
|
1997
|
+
* state, so a lone chip works with no wrapper.
|
|
1998
|
+
*
|
|
1999
|
+
* Every chip below calls this rather than taking `open` / `onOpenChange` props,
|
|
2000
|
+
* which is what stops a call site from reintroducing the flicker by wiring the
|
|
2001
|
+
* state up itself. A chip that genuinely needs outside control can still pass
|
|
2002
|
+
* `open` / `onOpenChange` — a mounted-but-hidden twin of a chip, say — and
|
|
2003
|
+
* those win.
|
|
2004
|
+
*/
|
|
2005
|
+
declare function useFilterChipState(key: string, controlled?: {
|
|
2006
|
+
open?: boolean;
|
|
2007
|
+
onOpenChange?: (open: boolean) => void;
|
|
2008
|
+
}): {
|
|
2009
|
+
open: boolean;
|
|
2010
|
+
onOpenChange: (open: boolean) => void;
|
|
2011
|
+
/** Spread onto the chip's `PopoverContent`. See below. */
|
|
2012
|
+
onCloseAutoFocus: (event: Event) => void;
|
|
2013
|
+
};
|
|
2014
|
+
/** The `open` / `onOpenChange` pair every chip accepts for outside control. */
|
|
2015
|
+
interface FilterChipControl {
|
|
2016
|
+
open?: boolean;
|
|
2017
|
+
onOpenChange?: (open: boolean) => void;
|
|
2018
|
+
}
|
|
2019
|
+
/**
|
|
2020
|
+
* Every filter chip is built from three pieces: this visual shell (the dashed
|
|
2021
|
+
* pill), a label trigger that opens the popover, and — only once the filter has
|
|
2022
|
+
* a value — a separate clear button to its left.
|
|
2023
|
+
*
|
|
2024
|
+
* The clear button and the trigger are two independent `<button>`s side by side
|
|
2025
|
+
* rather than one button whose leading icon doubles as a clear action: clicking
|
|
2026
|
+
* × must clear *without* opening the popover, and a real `<button>` cannot nest
|
|
2027
|
+
* inside another. Keeping them siblings means stopping the clear click from
|
|
2028
|
+
* also opening the popover needs no `stopPropagation` gymnastics — they are
|
|
2029
|
+
* simply two separate click targets.
|
|
2030
|
+
*
|
|
2031
|
+
* Inactive it reads as an "add a filter" affordance: a dashed outline in the
|
|
2032
|
+
* muted border colour. Active it flips to a solid primary ring with a tinted
|
|
2033
|
+
* fill, so an applied filter is unmistakable at a glance rather than a subtle
|
|
2034
|
+
* recolour of the same dashed outline.
|
|
2035
|
+
*/
|
|
2036
|
+
declare function FilterChipShell({ active, children, className, }: {
|
|
2037
|
+
active: boolean;
|
|
2038
|
+
children: ReactNode;
|
|
2039
|
+
className?: string;
|
|
2040
|
+
}): React$1.JSX.Element;
|
|
2041
|
+
/**
|
|
2042
|
+
* Leading × segment, rendered only when the filter is active.
|
|
2043
|
+
*
|
|
2044
|
+
* A `Button` rather than an `IconButton` so the `h-auto` / `min-h-0` height
|
|
2045
|
+
* override behaves the same way the label trigger's already does: IconButton
|
|
2046
|
+
* sizes with Tailwind's `size-*` utility, which a plain `h-auto` does not
|
|
2047
|
+
* reliably beat the way it beats Button's `h-9`.
|
|
2048
|
+
*/
|
|
2049
|
+
declare function FilterChipClearButton({ label, onClick, }: {
|
|
2050
|
+
label: string;
|
|
2051
|
+
onClick: () => void;
|
|
2052
|
+
}): React$1.JSX.Element;
|
|
2053
|
+
/**
|
|
2054
|
+
* Trailing label segment — the actual `PopoverTrigger` target.
|
|
2055
|
+
*
|
|
2056
|
+
* A leading plus shows only while inactive, since once active the clear button
|
|
2057
|
+
* to its left already carries a leading icon. A trailing dot then marks
|
|
2058
|
+
* "active"; `count` replaces it with a number when *how many* values are
|
|
2059
|
+
* applied is worth saying.
|
|
2060
|
+
*
|
|
2061
|
+
* Must forward its ref and spread the rest of its props onto the underlying
|
|
2062
|
+
* Button: `PopoverTrigger asChild` clones its single child to inject
|
|
2063
|
+
* onClick/ref/aria-*, and a component that swallows those renders a chip that
|
|
2064
|
+
* looks right and does nothing when clicked.
|
|
2065
|
+
*/
|
|
2066
|
+
declare const FilterChipLabelTrigger: React$1.ForwardRefExoticComponent<{
|
|
2067
|
+
label: string;
|
|
2068
|
+
active: boolean;
|
|
2069
|
+
/** Show this number instead of the plain active dot. */
|
|
2070
|
+
count?: number;
|
|
2071
|
+
} & Omit<Omit<ButtonProps & React$1.RefAttributes<HTMLButtonElement>, "ref">, "children"> & React$1.RefAttributes<HTMLButtonElement>>;
|
|
2072
|
+
/**
|
|
2073
|
+
* Apply / Clear footer shared by every panel, so the two buttons sit in the
|
|
2074
|
+
* same place and read the same wherever a chip's editor puts them.
|
|
2075
|
+
*
|
|
2076
|
+
* Both buttons **commit and close**. Clear is not "untick everything and let me
|
|
2077
|
+
* carry on" — that reading leaves the panel open over a filter that is still
|
|
2078
|
+
* applied, so the chip still reads "Type 1" while the list in front of you
|
|
2079
|
+
* shows nothing ticked, and closing the panel silently keeps the old filter.
|
|
2080
|
+
* Clear is the same act as the chip's own little x, reached from inside the
|
|
2081
|
+
* panel: it drops the filter and gets out of the way.
|
|
2082
|
+
*/
|
|
2083
|
+
declare function FilterChipActions({ onClear, onApply, clearDisabled, applyDisabled, }: {
|
|
2084
|
+
onClear: () => void;
|
|
2085
|
+
onApply: () => void;
|
|
2086
|
+
clearDisabled?: boolean;
|
|
2087
|
+
applyDisabled?: boolean;
|
|
2088
|
+
}): React$1.JSX.Element;
|
|
2089
|
+
/**
|
|
2090
|
+
* The full chip — shell, clear button, trigger and popover — with the editor
|
|
2091
|
+
* supplied as `children`. Build a bespoke chip on this rather than reassembling
|
|
2092
|
+
* the pieces, so a one-off filter still opens, closes and clears like the rest.
|
|
2093
|
+
*/
|
|
2094
|
+
declare function FilterChip({ chipKey, label, active, count, onClear, children, align, contentClassName, open: controlledOpen, onOpenChange: controlledOnOpenChange, onOpen, }: {
|
|
2095
|
+
/** Identity within a {@link FilterChipGroup}. Must be unique in the row. */
|
|
2096
|
+
chipKey: string;
|
|
2097
|
+
label: string;
|
|
2098
|
+
active: boolean;
|
|
2099
|
+
count?: number;
|
|
2100
|
+
/** Omit to hide the × segment — for a chip that cannot be emptied. */
|
|
2101
|
+
onClear?: () => void;
|
|
2102
|
+
children: ReactNode;
|
|
2103
|
+
align?: "start" | "center" | "end";
|
|
2104
|
+
contentClassName?: string;
|
|
2105
|
+
/** Fires when the popover opens — the hook for seeding a draft from the
|
|
2106
|
+
* applied value, so an abandoned edit never leaks into the next open. */
|
|
2107
|
+
onOpen?: () => void;
|
|
2108
|
+
} & FilterChipControl): React$1.JSX.Element;
|
|
2109
|
+
interface SelectFilterChipProps extends FilterChipControl {
|
|
2110
|
+
/** Identity within a group. Defaults to `label`. */
|
|
2111
|
+
chipKey?: string;
|
|
2112
|
+
label: string;
|
|
2113
|
+
options: FilterChipOption[];
|
|
2114
|
+
selected: string[];
|
|
2115
|
+
onChange: (next: string[]) => void;
|
|
2116
|
+
/** Show a search box above the list once there are this many options. Default 8. */
|
|
2117
|
+
searchThreshold?: number;
|
|
2118
|
+
/** Show the applied count on the chip instead of the plain active dot. */
|
|
2119
|
+
showCount?: boolean;
|
|
2120
|
+
/**
|
|
2121
|
+
* Adds an "Invert filter" tick below the list, turning the chosen set into an
|
|
2122
|
+
* exclusion. Pass both to enable it; omit for a plain include-only chip.
|
|
2123
|
+
*
|
|
2124
|
+
* It is staged with the options and applied with them, because inverting
|
|
2125
|
+
* without changing the set is still a change to what the table shows, and
|
|
2126
|
+
* committing it on the tick would make this one control in the panel behave
|
|
2127
|
+
* differently from the rest.
|
|
2128
|
+
*/
|
|
2129
|
+
invert?: boolean;
|
|
2130
|
+
onInvertChange?: (next: boolean) => void;
|
|
2131
|
+
/** Label for the invert tick. Default "Invert filter". */
|
|
2132
|
+
invertLabel?: string;
|
|
2133
|
+
/** Empty-list line, for options that arrive from a request. */
|
|
2134
|
+
emptyText?: string;
|
|
2135
|
+
align?: "start" | "center" | "end";
|
|
2136
|
+
}
|
|
2137
|
+
/**
|
|
2138
|
+
* The workhorse chip: a checkbox list staged behind Apply, so ticking four
|
|
2139
|
+
* boxes is one query rather than four. Escaping or clicking away discards the
|
|
2140
|
+
* draft — the applied value only changes on Apply or Clear.
|
|
2141
|
+
*/
|
|
2142
|
+
declare function SelectFilterChip({ chipKey, label, options, selected, onChange, searchThreshold, showCount, invert, onInvertChange, invertLabel, emptyText, align, open, onOpenChange, }: SelectFilterChipProps): React$1.JSX.Element;
|
|
2143
|
+
interface SingleSelectFilterChipProps extends FilterChipControl {
|
|
2144
|
+
chipKey?: string;
|
|
2145
|
+
label: string;
|
|
2146
|
+
options: FilterChipOption[];
|
|
2147
|
+
value: string;
|
|
2148
|
+
onChange: (next: string) => void;
|
|
2149
|
+
align?: "start" | "center" | "end";
|
|
2150
|
+
/** Show the chosen option's label on the chip instead of the field name. */
|
|
2151
|
+
showValueInLabel?: boolean;
|
|
2152
|
+
}
|
|
2153
|
+
/**
|
|
2154
|
+
* One-of-many. Picking applies immediately — there is nothing to stage when a
|
|
2155
|
+
* choice replaces rather than accumulates, and an Apply button for a single
|
|
2156
|
+
* click is a step that only costs the user time.
|
|
2157
|
+
*/
|
|
2158
|
+
declare function SingleSelectFilterChip({ chipKey, label, options, value, onChange, align, showValueInLabel, open, onOpenChange, }: SingleSelectFilterChipProps): React$1.JSX.Element;
|
|
2159
|
+
interface DateRangeValue {
|
|
2160
|
+
/** `yyyy-mm-dd`, or "" for unset. */
|
|
2161
|
+
from: string;
|
|
2162
|
+
to: string;
|
|
2163
|
+
}
|
|
2164
|
+
/**
|
|
2165
|
+
* "Last N weeks / days / hours / minutes", counted back from now.
|
|
2166
|
+
*
|
|
2167
|
+
* A duration rather than a pair of dates, because that is what it is: "last 2
|
|
2168
|
+
* days" means two days before *now*, and resolving it to fixed timestamps when
|
|
2169
|
+
* the user picks it quietly freezes it at the moment of the click. The chip
|
|
2170
|
+
* reports the duration; the caller resolves it at request time with
|
|
2171
|
+
* {@link relativeRangeToMillis}.
|
|
2172
|
+
*
|
|
2173
|
+
* Every field is a string because each is a text input, and "" is a field the
|
|
2174
|
+
* user has not filled in — distinct from "0".
|
|
2175
|
+
*/
|
|
2176
|
+
interface RelativeRangeValue {
|
|
2177
|
+
weeks: string;
|
|
2178
|
+
days: string;
|
|
2179
|
+
hours: string;
|
|
2180
|
+
minutes: string;
|
|
2181
|
+
}
|
|
2182
|
+
declare const EMPTY_RELATIVE_RANGE: RelativeRangeValue;
|
|
2183
|
+
/** Whether a relative range names any span at all. */
|
|
2184
|
+
declare function hasRelativeRange(value: RelativeRangeValue | undefined): boolean;
|
|
2185
|
+
/**
|
|
2186
|
+
* Resolves a relative range to absolute epoch millis, evaluated at call time.
|
|
2187
|
+
*
|
|
2188
|
+
* Deliberately not memoised and never computed during render: "last 2 days"
|
|
2189
|
+
* means two days before now, and now moves. Call it in the handler that builds
|
|
2190
|
+
* the request.
|
|
2191
|
+
*/
|
|
2192
|
+
declare function relativeRangeToMillis(value: RelativeRangeValue): {
|
|
2193
|
+
startTime: number;
|
|
2194
|
+
endTime: number;
|
|
2195
|
+
} | null;
|
|
2196
|
+
interface DateRangeFilterChipProps extends FilterChipControl {
|
|
2197
|
+
chipKey?: string;
|
|
2198
|
+
label?: string;
|
|
2199
|
+
value: DateRangeValue;
|
|
2200
|
+
onChange: (next: DateRangeValue) => void;
|
|
2201
|
+
/**
|
|
2202
|
+
* Turns on the "Last…" tab beside the date range. Omit both and the chip is
|
|
2203
|
+
* absolute-only.
|
|
2204
|
+
*
|
|
2205
|
+
* The two modes are exclusive by construction: applying one clears the other,
|
|
2206
|
+
* because a range that is both "last 7 days" and "1–31 Jan" cannot be
|
|
2207
|
+
* honoured and nothing downstream should have to guess which half won.
|
|
2208
|
+
*/
|
|
2209
|
+
relativeValue?: RelativeRangeValue;
|
|
2210
|
+
onRelativeChange?: (next: RelativeRangeValue) => void;
|
|
2211
|
+
/** Earliest / latest selectable date, `YYYY-MM-DD`. */
|
|
2212
|
+
min?: string;
|
|
2213
|
+
max?: string;
|
|
2214
|
+
/** Shown under the fields when a picked date falls outside `min`/`max`. */
|
|
2215
|
+
outOfRangeHint?: string;
|
|
2216
|
+
align?: "start" | "center" | "end";
|
|
2217
|
+
}
|
|
2218
|
+
/**
|
|
2219
|
+
* From / To, staged behind Apply. A half-filled range cannot be applied: an
|
|
2220
|
+
* open-ended date filter reads as a bug far more often than it is what someone
|
|
2221
|
+
* meant, and the disabled Apply says so without an error message.
|
|
2222
|
+
*/
|
|
2223
|
+
declare function DateRangeFilterChip({ chipKey, label, value, onChange, relativeValue, onRelativeChange, min, max, outOfRangeHint, align, open, onOpenChange, }: DateRangeFilterChipProps): React$1.JSX.Element;
|
|
2224
|
+
interface MonthRange {
|
|
2225
|
+
/** Inclusive "YYYY-MM" bounds. Both ends compare as plain strings. */
|
|
2226
|
+
start: string;
|
|
2227
|
+
end: string;
|
|
2228
|
+
}
|
|
2229
|
+
/**
|
|
2230
|
+
* Month RANGE chip: pick a start month and an end month on the same year grid.
|
|
2231
|
+
*
|
|
2232
|
+
* Distinct from MonthFilterChip below, which ticks an arbitrary SET of months.
|
|
2233
|
+
* A range is the right shape when the value is going into a request rather than
|
|
2234
|
+
* being matched client-side — a start/end pair is what a "from month, to month"
|
|
2235
|
+
* endpoint takes, and a set of months is not expressible in one.
|
|
2236
|
+
*
|
|
2237
|
+
* The value is never empty: a caller sending it to an API always has some window
|
|
2238
|
+
* in force, so "Reset" restores `defaultRange` rather than clearing to nothing,
|
|
2239
|
+
* and the chip renders the range it is on at all times. That is deliberate — a
|
|
2240
|
+
* filter that silently governs a request should say what it is set to, not read
|
|
2241
|
+
* as unset while quietly bounding every row on screen.
|
|
2242
|
+
*
|
|
2243
|
+
* Clicking cycles the way a date-range picker does: the first click starts a new
|
|
2244
|
+
* range, the second closes it, and a click before the open start moves the start
|
|
2245
|
+
* instead of making a backwards range.
|
|
2246
|
+
*/
|
|
2247
|
+
declare function MonthRangeFilterChip({ chipKey, label, bounds, value, defaultRange, monthsWithData, onChange, }: {
|
|
2248
|
+
chipKey?: string;
|
|
2249
|
+
label?: string;
|
|
2250
|
+
/** The outer limits the grid lets the merchant navigate and pick within. */
|
|
2251
|
+
bounds: MonthRange;
|
|
2252
|
+
/** The range currently in force. Always set — see the note above. */
|
|
2253
|
+
value: MonthRange;
|
|
2254
|
+
/** What Reset goes back to, typically the window the page opens on. */
|
|
2255
|
+
defaultRange: MonthRange;
|
|
2256
|
+
/** Months with a row behind them, as "YYYY-MM". Drives the grid's dots. */
|
|
2257
|
+
monthsWithData: Set<string>;
|
|
2258
|
+
onChange: (next: MonthRange) => void;
|
|
2259
|
+
}): React$1.JSX.Element;
|
|
2260
|
+
interface TextFilterChipProps extends FilterChipControl {
|
|
2261
|
+
chipKey?: string;
|
|
2262
|
+
label?: string;
|
|
2263
|
+
value: string;
|
|
2264
|
+
onChange: (next: string) => void;
|
|
2265
|
+
/** Field label inside the panel. Defaults to `<label> contains`. */
|
|
2266
|
+
fieldLabel?: string;
|
|
2267
|
+
placeholder?: string;
|
|
2268
|
+
/** One line under the field — what the match actually does, typically. */
|
|
2269
|
+
hint?: string;
|
|
2270
|
+
/** Soft keyboard hint on touch devices. */
|
|
2271
|
+
inputMode?: "text" | "email" | "tel" | "numeric" | "url" | "search";
|
|
2272
|
+
align?: "start" | "center" | "end";
|
|
2273
|
+
}
|
|
2274
|
+
/**
|
|
2275
|
+
* One free-text value, staged behind Apply.
|
|
2276
|
+
*
|
|
2277
|
+
* Deliberately not a live-filtering input: this chip sits in a toolbar whose
|
|
2278
|
+
* other chips all commit on Apply, and a field that filtered as you typed would
|
|
2279
|
+
* be the one control on the row that behaves differently. Enter applies, so it
|
|
2280
|
+
* still costs one keystroke.
|
|
2281
|
+
*
|
|
2282
|
+
* The applied value is trimmed — a trailing space pasted in with an address is
|
|
2283
|
+
* not something the user meant to search for.
|
|
2284
|
+
*/
|
|
2285
|
+
declare function TextFilterChip({ chipKey, label, value, onChange, fieldLabel, placeholder, hint, inputMode, align, open, onOpenChange, }: TextFilterChipProps): React$1.JSX.Element;
|
|
2286
|
+
interface NumberRangeValue {
|
|
2287
|
+
min: string;
|
|
2288
|
+
max: string;
|
|
2289
|
+
}
|
|
2290
|
+
interface NumberRangeFilterChipProps extends FilterChipControl {
|
|
2291
|
+
chipKey?: string;
|
|
2292
|
+
label?: string;
|
|
2293
|
+
value: NumberRangeValue;
|
|
2294
|
+
onChange: (next: NumberRangeValue) => void;
|
|
2295
|
+
/** Prefix inside each field — a currency symbol, typically. */
|
|
2296
|
+
prefix?: string;
|
|
2297
|
+
/** One line under the fields — what the bounds mean, or which field they match. */
|
|
2298
|
+
hint?: string;
|
|
2299
|
+
align?: "start" | "center" | "end";
|
|
2300
|
+
}
|
|
2301
|
+
/**
|
|
2302
|
+
* Min / Max, staged behind Apply. Unlike a date range, one end alone is a
|
|
2303
|
+
* perfectly ordinary request ("over ₹10,000"), so a half-filled range applies.
|
|
2304
|
+
*/
|
|
2305
|
+
declare function NumberRangeFilterChip({ chipKey, label, value, onChange, prefix, hint, align, open, onOpenChange, }: NumberRangeFilterChipProps): React$1.JSX.Element;
|
|
2306
|
+
interface AddFilterDefinition {
|
|
2307
|
+
key: string;
|
|
2308
|
+
label: string;
|
|
2309
|
+
/**
|
|
2310
|
+
* The values this filter accepts, so the search can match them directly.
|
|
2311
|
+
* Omit for a filter whose values are not a list — a date or amount range —
|
|
2312
|
+
* and it will still be findable by name.
|
|
2313
|
+
*/
|
|
2314
|
+
options?: FilterChipOption[];
|
|
2315
|
+
/** How many values are currently applied. Drives the count beside the name. */
|
|
2316
|
+
activeCount?: number;
|
|
2317
|
+
}
|
|
2318
|
+
interface AddFilterMenuProps extends FilterChipControl {
|
|
2319
|
+
chipKey?: string;
|
|
2320
|
+
/** Every filter this toolbar can offer, including ones already shown. */
|
|
2321
|
+
filters: AddFilterDefinition[];
|
|
2322
|
+
/** Keys already on screen as their own chip. */
|
|
2323
|
+
visibleKeys?: string[];
|
|
2324
|
+
/** Reveal a filter as its own chip. */
|
|
2325
|
+
onAddFilter: (key: string) => void;
|
|
2326
|
+
/**
|
|
2327
|
+
* Take a filter back out of the toolbar. Omit and a shown filter is simply
|
|
2328
|
+
* marked as shown; supply it and the row becomes a toggle.
|
|
2329
|
+
*
|
|
2330
|
+
* Removing must also clear whatever that filter had selected — a filter that
|
|
2331
|
+
* is still narrowing the table from somewhere the user cannot see it is worse
|
|
2332
|
+
* than one they have to scroll to.
|
|
2333
|
+
*/
|
|
2334
|
+
onRemoveFilter?: (key: string) => void;
|
|
2335
|
+
/** Apply a value picked straight out of the search results. */
|
|
2336
|
+
onSelectValue?: (filterKey: string, value: string) => void;
|
|
2337
|
+
label?: string;
|
|
2338
|
+
align?: "start" | "center" | "end";
|
|
2339
|
+
}
|
|
2340
|
+
/**
|
|
2341
|
+
* "Filter" — a searchable way to reach every filter a table has, instead of a
|
|
2342
|
+
* second-class drawer of leftovers.
|
|
2343
|
+
*
|
|
2344
|
+
* Two things it does that a nested accordion of checkbox groups does not.
|
|
2345
|
+
* Typing searches filter **names and their values at once**, so someone who
|
|
2346
|
+
* knows they want "USD" finds it without first knowing it lives under
|
|
2347
|
+
* Currency. And choosing anything here promotes that filter to a real chip in
|
|
2348
|
+
* the toolbar, so there is exactly one place a filter can be — beside its
|
|
2349
|
+
* peers — rather than some being chips and some being hidden rows.
|
|
2350
|
+
*
|
|
2351
|
+
* That also means the toolbar scales: a table with twenty filters shows the
|
|
2352
|
+
* three or four in use and keeps the rest one keystroke away.
|
|
2353
|
+
*/
|
|
2354
|
+
declare function AddFilterMenu({ chipKey, filters, visibleKeys, onAddFilter, onRemoveFilter, onSelectValue, label, align, open: controlledOpen, onOpenChange: controlledOnOpenChange, }: AddFilterMenuProps): React$1.JSX.Element;
|
|
2355
|
+
/**
|
|
2356
|
+
* The row a table's filters live in: search at the left, chips beside it,
|
|
2357
|
+
* actions pinned right.
|
|
2358
|
+
*
|
|
2359
|
+
* Search and chips share one wrapping flex, so a chip that does not fit wraps
|
|
2360
|
+
* to the next line starting **under the search box** — a toolbar with three
|
|
2361
|
+
* filters is one line, one with eight grows a second, and nothing is ever
|
|
2362
|
+
* scrolled out of sight. Giving each group its own box instead would wrap the
|
|
2363
|
+
* chips inside their own column and leave a ragged left edge.
|
|
2364
|
+
*
|
|
2365
|
+
* Wraps its chips in a {@link FilterChipGroup}, so a toolbar built with it gets
|
|
2366
|
+
* the one-open-at-a-time behaviour without opting in.
|
|
2367
|
+
*/
|
|
2368
|
+
declare function FilterToolbar({ search, chips, actions, className, }: {
|
|
2369
|
+
search?: ReactNode;
|
|
2370
|
+
chips?: ReactNode;
|
|
2371
|
+
actions?: ReactNode;
|
|
2372
|
+
className?: string;
|
|
2373
|
+
}): React$1.JSX.Element;
|
|
2374
|
+
/**
|
|
2375
|
+
* The pill action button that sits at the right of a table toolbar — Refresh,
|
|
2376
|
+
* Columns, Export, Report.
|
|
2377
|
+
*
|
|
2378
|
+
* It exists because `Button size="sm"` is `h-9`, which towers over the chips
|
|
2379
|
+
* beside it; every toolbar that wanted a level row was overriding the same four
|
|
2380
|
+
* classes by hand. Having it here means a toolbar's actions match its chips
|
|
2381
|
+
* without each one rediscovering that.
|
|
2382
|
+
*/
|
|
2383
|
+
declare function ToolbarButton({ className, ...props }: ComponentPropsWithoutRef<typeof Button>): React$1.JSX.Element;
|
|
2384
|
+
|
|
2385
|
+
type DatePickMode = "single" | "range";
|
|
2386
|
+
/** What the calendar hands back while the user is picking. */
|
|
2387
|
+
type CalendarRange = {
|
|
2388
|
+
from: Date | undefined;
|
|
2389
|
+
to?: Date | undefined;
|
|
2390
|
+
};
|
|
2391
|
+
/**
|
|
2392
|
+
* A named span offered above the calendar — "Today", "Last 30 Days".
|
|
2393
|
+
*
|
|
2394
|
+
* `resolve` runs when the preset is chosen, not when it is declared, so "last
|
|
2395
|
+
* 7 days" is counted from the day the user picks it rather than from whenever
|
|
2396
|
+
* the options array happened to be built.
|
|
2397
|
+
*/
|
|
2398
|
+
interface CalendarDatePreset {
|
|
2399
|
+
value: string;
|
|
2400
|
+
label: string;
|
|
2401
|
+
resolve: () => {
|
|
2402
|
+
from: string;
|
|
2403
|
+
to: string;
|
|
2404
|
+
};
|
|
2405
|
+
}
|
|
2406
|
+
/**
|
|
2407
|
+
* The applied value: a span, plus which preset produced it.
|
|
2408
|
+
*
|
|
2409
|
+
* `preset` is `""` when the dates were picked by hand, and `to` equals `from`
|
|
2410
|
+
* for a single day — so a caller that only wants a window can read `from`/`to`
|
|
2411
|
+
* and ignore the rest.
|
|
2412
|
+
*/
|
|
2413
|
+
interface CalendarDateValue {
|
|
2414
|
+
preset: string;
|
|
2415
|
+
/** YYYY-MM-DD */
|
|
2416
|
+
from: string;
|
|
2417
|
+
/** YYYY-MM-DD */
|
|
2418
|
+
to: string;
|
|
2419
|
+
}
|
|
2420
|
+
interface CalendarDateFilterChipProps extends FilterChipControl {
|
|
2421
|
+
chipKey?: string;
|
|
2422
|
+
label?: string;
|
|
2423
|
+
value?: CalendarDateValue;
|
|
2424
|
+
onChange: (next: CalendarDateValue | undefined) => void;
|
|
2425
|
+
/** Named spans above the calendar. Omit for a calendar-only chip. */
|
|
2426
|
+
presets?: readonly CalendarDatePreset[];
|
|
2427
|
+
/** Offer "Single date" alongside "Date range". Default true. */
|
|
2428
|
+
allowSingle?: boolean;
|
|
2429
|
+
/** Months shown side by side in range mode. Default 2. */
|
|
2430
|
+
numberOfMonths?: number;
|
|
2431
|
+
align?: "start" | "center" | "end";
|
|
2432
|
+
}
|
|
2433
|
+
/**
|
|
2434
|
+
* A date filter over a real calendar, with optional named spans.
|
|
2435
|
+
*
|
|
2436
|
+
* Distinct from {@link DateRangeFilterChip}, which is two typed date fields.
|
|
2437
|
+
* This one is for a filter people reach for by *looking* — "the week of the
|
|
2438
|
+
* 14th", "that Tuesday" — where a pair of text inputs makes you count days in
|
|
2439
|
+
* your head. Both exist because both are right somewhere, and picking between
|
|
2440
|
+
* them is a call about the filter, not about the toolbar.
|
|
2441
|
+
*
|
|
2442
|
+
* Five features in pg-dashboard-v2 had built this chip separately, each with
|
|
2443
|
+
* its own preset list and its own value shape. They are the same control.
|
|
2444
|
+
*/
|
|
2445
|
+
declare function CalendarDateFilterChip({ chipKey, label, value, onChange, presets, allowSingle, numberOfMonths, align, open, onOpenChange, }: CalendarDateFilterChipProps): React$1.JSX.Element;
|
|
2446
|
+
|
|
2447
|
+
/**
|
|
2448
|
+
* The app-wide date and time format.
|
|
2449
|
+
*
|
|
2450
|
+
* Every timestamp a PayGlocal dashboard shows a user goes through here, so a
|
|
2451
|
+
* transaction row, a settlement detail page, an audit log line and a chart
|
|
2452
|
+
* tooltip all read the same: `27 Jul '26, 09:49 AM`.
|
|
2453
|
+
*
|
|
2454
|
+
* Nothing here goes through `toLocaleDateString` / `toLocaleTimeString`. That
|
|
2455
|
+
* is deliberate: Intl output varies with the machine's locale, so the same
|
|
2456
|
+
* record would read differently for an operator in Bengaluru and a merchant in
|
|
2457
|
+
* Frankfurt, and a screenshot in a support ticket would not match what the
|
|
2458
|
+
* agent sees. These build the string from fixed tables instead.
|
|
2459
|
+
*
|
|
2460
|
+
* Times are rendered in the **viewer's own timezone**, which is what every
|
|
2461
|
+
* `Date` getter below returns. That is the right default for an operations
|
|
2462
|
+
* console — "did this settle before close of business *here*" is the question
|
|
2463
|
+
* being asked — but it does mean two people in different zones see different
|
|
2464
|
+
* clock times for one event, so anywhere that matters should label the zone.
|
|
2465
|
+
*/
|
|
2466
|
+
declare const MONTHS_SHORT: readonly ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"];
|
|
2467
|
+
declare const DAYS_SHORT: readonly ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"];
|
|
2468
|
+
/** What an absent or unparseable value renders as, everywhere. */
|
|
2469
|
+
declare const EMPTY_DATE = "\u2014";
|
|
2470
|
+
/**
|
|
2471
|
+
* Parses the shapes PayGlocal APIs actually send, in the order they are most
|
|
2472
|
+
* likely to appear:
|
|
2473
|
+
*
|
|
2474
|
+
* - `DD/MM/YYYY HH:mm:ss` — the transactions search response's
|
|
2475
|
+
* `formattedCreationDateTime`. Tried **first**, because `new Date()` reads
|
|
2476
|
+
* `03/07/2026` as *March 7th* under US parsing rules, silently swapping the
|
|
2477
|
+
* day and month for the first twelve days of every month.
|
|
2478
|
+
* - epoch milliseconds, as a number **or a string** — several endpoints send
|
|
2479
|
+
* `"1771329858260"`. The string form needs `Number()` first: the `Date`
|
|
2480
|
+
* constructor reads a string as a date *format*, not a count of
|
|
2481
|
+
* milliseconds, so `new Date("1771329858260")` is an Invalid Date.
|
|
2482
|
+
* - ISO 8601 — `settlementDate`, and most newer endpoints.
|
|
2483
|
+
*/
|
|
2484
|
+
declare function parseApiDate(value: string | number | Date | null | undefined): Date | null;
|
|
2485
|
+
/** `09:49 AM` — 12-hour, zero-padded, uppercase meridiem. */
|
|
2486
|
+
declare function formatTime(date: Date): string;
|
|
2487
|
+
/** `27 Jul '26` — the date half, on its own. */
|
|
2488
|
+
declare function formatDateOnly(date: Date): string;
|
|
2489
|
+
/** `27 Jul '26, 09:49 AM` — the canonical form. */
|
|
2490
|
+
declare function formatDateTime(date: Date): string;
|
|
2491
|
+
/**
|
|
2492
|
+
* Any API value → `27 Jul '26, 09:49 AM`.
|
|
2493
|
+
*
|
|
2494
|
+
* This is the one to reach for in a column renderer or a detail field: it takes
|
|
2495
|
+
* whatever shape the endpoint sends, and returns the em dash rather than
|
|
2496
|
+
* "Invalid Date" when there is nothing to show.
|
|
2497
|
+
*
|
|
2498
|
+
* `fallback` is what an absent or unparseable value renders as. It defaults to
|
|
2499
|
+
* the em dash; pass `""` where the timestamp sits inside a sentence that should
|
|
2500
|
+
* simply omit it rather than show a placeholder.
|
|
2501
|
+
*/
|
|
2502
|
+
declare function formatTimestamp(value: string | number | Date | null | undefined, fallback?: string): string;
|
|
2503
|
+
/** Any API value → `27 Jul '26`, with no time of day. */
|
|
2504
|
+
declare function formatDateStamp(value: string | number | Date | null | undefined, fallback?: string): string;
|
|
2505
|
+
/** Any API value → `09:49 AM`, with no date. */
|
|
2506
|
+
declare function formatTimeStamp(value: string | number | Date | null | undefined, fallback?: string): string;
|
|
2507
|
+
/**
|
|
2508
|
+
* `Mon, 27 Jul` — weekday and date, no year. For a date close enough to the
|
|
2509
|
+
* present that naming the day of the week reads better than a bare calendar
|
|
2510
|
+
* date, such as a "next settlement" line.
|
|
2511
|
+
*/
|
|
2512
|
+
declare function formatWeekdayDate(value: string | number | Date | null | undefined, fallback?: string): string;
|
|
2513
|
+
/** `Jan 2026` — a month key (`YYYY-MM`) as a label. */
|
|
2514
|
+
declare function formatMonthLabel(monthKey: string): string;
|
|
2515
|
+
|
|
2516
|
+
export { Accordion, AccordionContent, AccordionItem, AccordionTrigger, type AddFilterDefinition, AddFilterMenu, type AddFilterMenuProps, Alert, AlertDescription, type AlertProps, AlertTitle, type AttentionListItem, AttentionListTemplate, type AttentionListTemplateProps, Avatar, AvatarFallback, AvatarGroup, type AvatarGroupItem, type AvatarGroupProps, AvatarImage, AvatarTag, type AvatarTagProps, type AvatarTagSize, Badge, type BadgeProps, type BadgeTrailIcon, type BadgeVariant, Banner, type BannerProps, Blanket, type BlanketProps, Box, type BoxProps, Breadcrumb, BreadcrumbEllipsis, BreadcrumbItem, BreadcrumbLink, BreadcrumbList, BreadcrumbPage, BreadcrumbSeparator, type Breakpoint, Button, ButtonGroup, type ButtonGroupProps, type ButtonProps, COUNTRIES, Calendar, CalendarDateFilterChip, type CalendarDateFilterChipProps, type CalendarDatePreset, type CalendarDateValue, CalendarDayButton, type CalendarProps, type CalendarRange, Callout, CalloutIcon, type CalloutProps, CalloutText, CalloutTitle, type CalloutVariant, Card, CardAction, CardContent, CardDescription, CardFooter, CardHeader, CardTitle, CategoryBarChartTemplate, type CategoryBarChartTemplateProps, type CategoryBarPoint, type ChartConfig, ChartContainer, ChartLegend, ChartLegendContent, ChartSkeleton, ChartStyle, ChartTooltip, ChartTooltipContent, Checkbox, type CheckboxProps, CheckboxSelect, type CheckboxSelectOption, type CheckboxSelectProps, Code, CodeBlock, type CodeBlockProps, type CodeProps, type Column, ColumnManager, type ColumnManagerProps, type ColumnPreferences, Command, CommandEmpty, type CommandEmptyProps, CommandGroup, type CommandGroupProps, CommandInput, type CommandInputProps, CommandItem, type CommandItemProps, CommandList, type CommandListProps, type CommandProps, CommandSeparator, type CommandSeparatorProps, CommandShortcut, type CommandShortcutProps, CopyableCell, type CopyableCellProps, type Country, CountrySelect, type CountrySelectProps, CurrencyAmountInput, DAYS_SHORT, type DashboardAreaChartPoint, DashboardAreaChartTemplate, type DashboardAreaChartTemplateProps, DataCardList, type DataCardListProps, DataTable, DataTableCard, type DataTableCardProps, type DataTableDensity, type DataTableExpandable, type DataTableFooterSummary, type DataTableHeaderStyle, type DataTablePagination, type DataTableSortState, type DataTableSorting, type DatePickMode, DatePicker, DateRangeFilterChip, type DateRangeFilterChipProps, type DateRangeValue, Dialog, DialogClose, DialogContent, DialogDescription, DialogPortal, DialogTitle, DialogTrigger, Drawer, DrawerClose, DrawerContent, DrawerDescription, DrawerFooter, DrawerHeader, DrawerTitle, DrawerTrigger, DropdownMenu, DropdownMenuCheckboxItem, DropdownMenuContent, DropdownMenuGroup, DropdownMenuItem, DropdownMenuLabel, DropdownMenuPortal, DropdownMenuRadioGroup, DropdownMenuRadioItem, DropdownMenuSeparator, DropdownMenuShortcut, DropdownMenuSub, DropdownMenuSubContent, DropdownMenuSubTrigger, DropdownMenuTrigger, EMPTY_DATE, EMPTY_RELATIVE_RANGE, EmptyState, Field, type FieldConfig, FieldContent, FieldDescription, FieldError, FieldGroup, FieldLabel, FieldLegend, FieldSeparator, FieldSet, FieldTitle, type FieldsConfig, FilterChip, FilterChipActions, FilterChipClearButton, type FilterChipControl, FilterChipGroup, FilterChipLabelTrigger, type FilterChipOption, FilterChipShell, FilterToolbar, Flag, type FlagAction, FlagGroup, type FlagGroupPosition, type FlagGroupProps, type FlagProps, type FlagVariant, Flex, type FlexAlign, type FlexDirection, type FlexJustify, type FlexProps, type FlexWrap, Form, FormControl, FormDescription, FormError, type FormErrors, FormField, type FormFieldProps, FormItem, FormLabel, type FormProps, type FormValues, Grid, type GridCols, type GridFlow, type GridProps, GroupedBarChartTemplate, type GroupedBarChartTemplateProps, type GroupedBarSeries, Heading, type HeadingProps, Hide, type HideProps, IconButton, type IconButtonProps, Inline, InlineDialog, InlineDialogContent, type InlineDialogContentProps, type InlineDialogProps, InlineDialogTrigger, InlineEdit, type InlineEditProps, type InlineProps, Input, InputGroup, InputGroupAddon, InputGroupButton, InputGroupInput, InputGroupText, InputGroupTextarea, Label, type LayoutSpacing, Link, type LinkProps, Lozenge, type LozengeProps, MONTHS_SHORT, type ManagedColumn, Menu, MenuDivider, MenuItem, type MenuItemProps, type MenuProps, MenuSection, type MenuSectionProps, MetricSparklineCard, type MetricSparklineCardProps, type MetricSparklinePoint, MetricText, type MetricTextProps, MiniSparklineChartCard, type MiniSparklineChartCardProps, type MiniSparklinePoint, type MiniSparklineStat, type MonthRange, MonthRangeFilterChip, NumberRangeFilterChip, type NumberRangeFilterChipProps, type NumberRangeValue, OtpInput, type OtpInputProps, PageHeader, Pagination, PaginationContent, type PaginationContentProps, PaginationEllipsis, type PaginationEllipsisProps, PaginationItem, type PaginationItemProps, PaginationLink, type PaginationLinkProps, PaginationNext, type PaginationNextProps, PaginationPrevious, type PaginationPreviousProps, type PaginationProps, PasswordInput, type PasswordInputProps, Popover, PopoverAnchor, PopoverContent, PopoverTrigger, Progress, ProgressIndicator, type ProgressIndicatorProps, type ProgressProps, ProgressTracker, type ProgressTrackerProps, type ProgressTrackerStep, RadioGroup, RadioGroupItem, type RadioGroupItemProps, type RankedBarItem, RankedBarListTemplate, type RankedBarListTemplateProps, type RegisterResult, type RelativeRangeValue, type ResponsiveCols, RotatingSearchInput, type RotatingSearchInputProps, ScrollArea, ScrollBar, SectionMessage, SectionMessageActions, SectionMessageContent, type SectionMessageProps, SectionMessageTitle, type SectionMessageVariant, type SegmentedTabOption, SegmentedTabs, Select, SelectContent, SelectFilterChip, type SelectFilterChipProps, SelectGroup, SelectItem, SelectLabel, SelectScrollDownButton, SelectScrollUpButton, SelectSeparator, SelectTrigger, type SelectTriggerSize, SelectValue, Separator, Shimmer, Show, type ShowProps, SideNav, SideNavFooter, SideNavHeader, SideNavItem, type SideNavItemProps, type SideNavProps, SideNavSection, SingleSelectFilterChip, type SingleSelectFilterChipProps, Slider, type SliderProps, type SortOrder, Spinner, type SpinnerProps, SplitButton, SplitButtonItem, type SplitButtonItemProps, type SplitButtonProps, Spotlight, SpotlightCard, type SpotlightCardProps, type SpotlightProps, type SpotlightStep, Stack, type StackProps, StatCardSkeleton, StatusBadge, type StatusBadgeProps, Switch, type SwitchProps, TableRowSkeleton, TableToolbarActions, Tabs, TabsContent, TabsList, TabsTrigger, Tag, TagGroup, type TagGroupProps, type TagProps, Text, TextFilterChip, type TextFilterChipProps, type TextProps, Textarea, TimePicker, type TimePickerProps, Toaster, ToolbarButton, Tooltip, TooltipContent, TooltipProvider, TooltipTrigger, type UnderlineTab, UnderlineTabs, type UseBreakpointReturn, type UseColumnPreferencesOptions, type UseColumnPreferencesResult, type UseFlagGroupReturn, type UseFormReturn, type UseSpotlightReturn, type ValidatorRule, VisuallyHidden, type VisuallyHiddenProps, applyColumnPreferences, cn, formatDateOnly, formatDateStamp, formatDateTime, formatMonthLabel, formatTime, formatTimeStamp, formatTimestamp, formatWeekdayDate, frozenColumn, hasRelativeRange, parseApiDate, relativeRangeToMillis, useBreakpoint, useColumnPreferences, useFilterChipState, useFlagGroup, useForm, useSpotlight };
|