@dmsi/wedgekit-react 0.0.414 → 0.0.417

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.
Files changed (124) hide show
  1. package/dist/chunk-3HBYDOYE.js +114 -0
  2. package/dist/{chunk-JIMJIJOF.js → chunk-5TGP5EFM.js} +2 -2
  3. package/dist/{chunk-2HMCS25C.js → chunk-6LHBY2IH.js} +1 -1
  4. package/dist/{chunk-6I5LZ2ZC.js → chunk-7AXHAWJX.js} +3 -3
  5. package/dist/{chunk-MQX7GFLX.js → chunk-CAQWLY5V.js} +24 -6
  6. package/dist/{chunk-QQ5G773N.js → chunk-DCLNAUC4.js} +1 -1
  7. package/dist/{chunk-GB4ZTUQV.js → chunk-DTW7JCBR.js} +36 -0
  8. package/dist/{chunk-OBY5EH47.js → chunk-HYJIDHAK.js} +6 -6
  9. package/dist/{chunk-LZGYABCX.js → chunk-IMOIZFJZ.js} +8 -8
  10. package/dist/chunk-KBJZUVLM.js +65 -0
  11. package/dist/{chunk-AA6GE3TH.js → chunk-LUPHOXAQ.js} +1 -1
  12. package/dist/chunk-NRNWEQD7.js +66 -0
  13. package/dist/{chunk-44TDIHUP.js → chunk-P3MIP2FD.js} +1 -1
  14. package/dist/chunk-PQWWVBSR.js +61 -0
  15. package/dist/{chunk-ERW5DEIO.js → chunk-RQLWSLVE.js} +1 -1
  16. package/dist/chunk-TYAQWVIM.js +159 -0
  17. package/dist/{chunk-KZZKQLKF.js → chunk-W55J2KJZ.js} +1 -1
  18. package/dist/chunk-Y2GK27RX.js +79 -0
  19. package/dist/components/Accordion.cjs +74 -2
  20. package/dist/components/Accordion.js +3 -3
  21. package/dist/components/CalendarRange.cjs +232 -53
  22. package/dist/components/CalendarRange.css +178 -65
  23. package/dist/components/CalendarRange.js +25 -17
  24. package/dist/components/Card.cjs +38 -2
  25. package/dist/components/Card.js +1 -1
  26. package/dist/components/CompactImagesPreview.cjs +59 -5
  27. package/dist/components/CompactImagesPreview.js +2 -2
  28. package/dist/components/DataGrid/ColumnSelectorHeaderCell/ColumnSelectorMenuOption.cjs +190 -11
  29. package/dist/components/DataGrid/ColumnSelectorHeaderCell/ColumnSelectorMenuOption.css +178 -65
  30. package/dist/components/DataGrid/ColumnSelectorHeaderCell/ColumnSelectorMenuOption.js +25 -17
  31. package/dist/components/DataGrid/ColumnSelectorHeaderCell/index.cjs +200 -21
  32. package/dist/components/DataGrid/ColumnSelectorHeaderCell/index.css +178 -65
  33. package/dist/components/DataGrid/ColumnSelectorHeaderCell/index.js +25 -17
  34. package/dist/components/DataGrid/PinnedColumns.cjs +215 -36
  35. package/dist/components/DataGrid/PinnedColumns.css +178 -65
  36. package/dist/components/DataGrid/PinnedColumns.js +25 -17
  37. package/dist/components/DataGrid/TableBody/LoadingCell.cjs +191 -12
  38. package/dist/components/DataGrid/TableBody/LoadingCell.css +178 -65
  39. package/dist/components/DataGrid/TableBody/LoadingCell.js +25 -17
  40. package/dist/components/DataGrid/TableBody/TableBodyRow.cjs +197 -18
  41. package/dist/components/DataGrid/TableBody/TableBodyRow.css +178 -65
  42. package/dist/components/DataGrid/TableBody/TableBodyRow.js +25 -17
  43. package/dist/components/DataGrid/TableBody/index.cjs +212 -33
  44. package/dist/components/DataGrid/TableBody/index.css +178 -65
  45. package/dist/components/DataGrid/TableBody/index.js +25 -17
  46. package/dist/components/DataGrid/index.cjs +301 -122
  47. package/dist/components/DataGrid/index.css +178 -65
  48. package/dist/components/DataGrid/index.js +25 -17
  49. package/dist/components/DataGrid/utils.cjs +191 -12
  50. package/dist/components/DataGrid/utils.css +178 -65
  51. package/dist/components/DataGrid/utils.js +25 -17
  52. package/dist/components/DateInput.cjs +251 -72
  53. package/dist/components/DateInput.css +178 -65
  54. package/dist/components/DateInput.js +25 -17
  55. package/dist/components/DateRangeInput.cjs +251 -72
  56. package/dist/components/DateRangeInput.css +178 -65
  57. package/dist/components/DateRangeInput.js +25 -17
  58. package/dist/components/Grid.cjs +81 -76
  59. package/dist/components/Grid.js +1 -1
  60. package/dist/components/ListGroup.cjs +553 -0
  61. package/dist/components/ListGroup.js +11 -0
  62. package/dist/components/MobileDataGrid/ColumnList.cjs +36 -0
  63. package/dist/components/MobileDataGrid/ColumnList.js +3 -3
  64. package/dist/components/MobileDataGrid/ColumnSelector/index.cjs +198 -19
  65. package/dist/components/MobileDataGrid/ColumnSelector/index.css +178 -65
  66. package/dist/components/MobileDataGrid/ColumnSelector/index.js +25 -17
  67. package/dist/components/MobileDataGrid/MobileDataGridCard/index.cjs +36 -0
  68. package/dist/components/MobileDataGrid/MobileDataGridCard/index.js +2 -2
  69. package/dist/components/MobileDataGrid/MobileDataGridHeader.cjs +242 -27
  70. package/dist/components/MobileDataGrid/MobileDataGridHeader.css +178 -65
  71. package/dist/components/MobileDataGrid/MobileDataGridHeader.js +25 -17
  72. package/dist/components/MobileDataGrid/RowDetailModalProvider/ModalContent.cjs +36 -0
  73. package/dist/components/MobileDataGrid/RowDetailModalProvider/ModalContent.js +2 -2
  74. package/dist/components/MobileDataGrid/RowDetailModalProvider/index.cjs +36 -0
  75. package/dist/components/MobileDataGrid/RowDetailModalProvider/index.js +5 -5
  76. package/dist/components/MobileDataGrid/index.cjs +742 -527
  77. package/dist/components/MobileDataGrid/index.css +178 -65
  78. package/dist/components/MobileDataGrid/index.js +25 -17
  79. package/dist/components/Modal.js +2 -2
  80. package/dist/components/Notification.cjs +36 -0
  81. package/dist/components/Notification.js +1 -1
  82. package/dist/components/PDFViewer/PDFElement.cjs +36 -0
  83. package/dist/components/PDFViewer/PDFElement.js +2 -2
  84. package/dist/components/PDFViewer/PDFNavigation.cjs +36 -0
  85. package/dist/components/PDFViewer/PDFNavigation.js +2 -2
  86. package/dist/components/PDFViewer/index.cjs +36 -0
  87. package/dist/components/PDFViewer/index.js +8 -111
  88. package/dist/components/Pagination.cjs +427 -0
  89. package/dist/components/Pagination.js +10 -0
  90. package/dist/components/ProductImagePreview/index.cjs +139 -127
  91. package/dist/components/ProductImagePreview/index.js +5 -5
  92. package/dist/components/SideMenuGroup.cjs +36 -0
  93. package/dist/components/SideMenuGroup.js +1 -1
  94. package/dist/components/SideMenuItem.cjs +36 -0
  95. package/dist/components/SideMenuItem.js +1 -1
  96. package/dist/components/SimpleTable.cjs +521 -0
  97. package/dist/components/SimpleTable.js +10 -0
  98. package/dist/components/Stack.cjs +36 -0
  99. package/dist/components/Stack.js +1 -1
  100. package/dist/components/Swatch.cjs +36 -0
  101. package/dist/components/Swatch.js +1 -1
  102. package/dist/components/Time.cjs +36 -0
  103. package/dist/components/Time.js +1 -1
  104. package/dist/components/Tooltip.cjs +1 -1
  105. package/dist/components/Tooltip.js +1 -1
  106. package/dist/components/Upload.cjs +36 -0
  107. package/dist/components/Upload.js +1 -1
  108. package/dist/components/index.cjs +739 -131
  109. package/dist/components/index.css +178 -65
  110. package/dist/components/index.js +37 -17
  111. package/dist/index.css +178 -65
  112. package/package.json +1 -1
  113. package/src/components/Card.tsx +60 -2
  114. package/src/components/CompactImagesPreview.tsx +54 -30
  115. package/src/components/Grid.tsx +59 -91
  116. package/src/components/ListGroup.tsx +82 -0
  117. package/src/components/Pagination.tsx +182 -0
  118. package/src/components/SimpleTable.tsx +77 -0
  119. package/src/components/Stack.tsx +76 -0
  120. package/src/components/Tooltip.tsx +4 -3
  121. package/src/components/index.ts +4 -0
  122. package/dist/chunk-ER6RCOH3.js +0 -97
  123. package/dist/chunk-EZCH4PQS.js +0 -29
  124. package/dist/{chunk-D6YCMQPO.js → chunk-SBCFBHNG.js} +3 -3
@@ -1,128 +1,96 @@
1
- import { ComponentProps, Children } from "react";
1
+ import { ComponentProps } from "react";
2
2
  import { Sizing } from "../types";
3
3
  import clsx from "clsx";
4
4
 
5
- type GridProps = {
5
+ type Breakpoint = "mobile" | "compact" | "desktop";
6
+
7
+ export type GridColumnCount = 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12;
8
+
9
+ export type GridProps = {
6
10
  children: React.ReactNode;
11
+ /** Design system sizing context controlling gap + optional padding token sets */
7
12
  sizing?: Sizing;
13
+ /** Adds the sizing-based padding classes when true */
8
14
  padding?: boolean;
9
- hasSideMenu?: boolean;
10
- columns?: 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12;
15
+ /** Base column count applied at all breakpoints unless overridden */
16
+ columns: GridColumnCount;
17
+ /** Explicit responsive column overrides. e.g. { mobile:2, desktop-compact:3, desktop:6 } */
18
+ responsive?: Partial<Record<Breakpoint, GridColumnCount>>;
11
19
  id?: string;
12
20
  testid?: string;
21
+ className?: string;
22
+ style?: ComponentProps<"div">["style"];
13
23
  } & Omit<ComponentProps<"div">, "className">;
14
24
 
15
- // Grid class configuration maps for better maintainability
16
- const COLUMNS_WITH_SIDE_MENU_MAP: Record<number, string> = {
17
- 1: "grid-cols-1",
18
- 2: "sm:grid-cols-2",
19
- 3: "sm:grid-cols-2 md:grid-cols-3",
20
- 4: "sm:grid-cols-2 md:grid-cols-3 xl:grid-cols-4",
21
- 5: "sm:grid-cols-2 md:grid-cols-3 xl:grid-cols-4",
22
- 6: "sm:grid-cols-2 md:grid-cols-3 xl:grid-cols-4",
23
- 7: "sm:grid-cols-2 md:grid-cols-3 xl:grid-cols-4 2xl:grid-cols-6",
24
- 8: "sm:grid-cols-2 md:grid-cols-3 xl:grid-cols-4 2xl:grid-cols-6",
25
- 9: "sm:grid-cols-2 md:grid-cols-3 xl:grid-cols-4 2xl:grid-cols-6",
26
- 10: "sm:grid-cols-2 md:grid-cols-3 xl:grid-cols-4 2xl:grid-cols-6",
27
- 11: "sm:grid-cols-2 md:grid-cols-3 xl:grid-cols-4 2xl:grid-cols-8",
28
- 12: "sm:grid-cols-2 md:grid-cols-3 xl:grid-cols-4 2xl:grid-cols-8",
25
+ // Mapping helpers for sizing tokens -> gap & padding classes (centralised to reduce repetition)
26
+ const GAP_BY_SIZING: Partial<Record<Sizing, string>> = {
27
+ none: "gap-0",
28
+ "layout-group":
29
+ "gap-mobile-layout-group-gap desktop:gap-desktop-layout-group-gap compact:gap-compact-layout-group-gap",
30
+ layout:
31
+ "gap-mobile-layout-gap desktop:gap-desktop-layout-gap compact:gap-compact-layout-gap",
32
+ container:
33
+ "gap-mobile-container-gap desktop:gap-desktop-container-gap compact:gap-compact-container-gap",
34
+ component:
35
+ "gap-mobile-component-gap desktop:gap-desktop-component-gap compact:gap-compact-component-gap",
29
36
  };
30
37
 
31
- const COLUMNS_WITHOUT_SIDE_MENU_MAP: Record<number, string> = {
32
- 1: "grid-cols-1",
33
- 2: "sm:grid-cols-2",
34
- 3: "sm:grid-cols-2 md:grid-cols-3",
35
- 4: "sm:grid-cols-2 md:grid-cols-3 xl:grid-cols-4",
36
- 5: "sm:grid-cols-2 md:grid-cols-3 lg:grid-cols-4 xl:grid-cols-5",
37
- 6: "sm:grid-cols-2 md:grid-cols-3 lg:grid-cols-4 xl:grid-cols-6",
38
- 7: "sm:grid-cols-2 md:grid-cols-3 lg:grid-cols-4 xl:grid-cols-6 2xl:grid-cols-7",
39
- 8: "sm:grid-cols-2 md:grid-cols-3 lg:grid-cols-4 xl:grid-cols-6 2xl:grid-cols-8",
40
- 9: "sm:grid-cols-2 md:grid-cols-3 lg:grid-cols-4 xl:grid-cols-6 2xl:grid-cols-9",
41
- 10: "sm:grid-cols-2 md:grid-cols-3 lg:grid-cols-4 xl:grid-cols-8 2xl:grid-cols-10",
42
- 11: "sm:grid-cols-2 md:grid-cols-3 lg:grid-cols-4 xl:grid-cols-8 2xl:grid-cols-11",
43
- 12: "sm:grid-cols-2 md:grid-cols-3 lg:grid-cols-4 xl:grid-cols-8 2xl:grid-cols-12",
38
+ const PADDING_BY_SIZING: Partial<Record<Sizing, string>> = {
39
+ none: "p-0",
40
+ container:
41
+ "p-mobile-container-padding desktop:p-desktop-container-padding compact:p-desktop-compact-container-padding",
42
+ layout:
43
+ "p-mobile-layout-padding desktop:p-desktop-layout-padding compact:p-desktop-compact-layout-padding",
44
+ "layout-group":
45
+ "p-mobile-layout-group-padding desktop:p-desktop-layout-group-padding compact:p-desktop-compact-layout-group-padding",
46
+ component:
47
+ "p-mobile-component-padding desktop:p-desktop-component-padding compact:p-desktop-compact-component-padding",
44
48
  };
45
49
 
46
- const getChildCountBasedClasses = (
47
- childCount: number,
48
- hasSideMenu: boolean,
50
+ /** Build column classes from provided props. No child-count heuristics. */
51
+ const buildColumnClasses = (
52
+ base: GridColumnCount,
53
+ responsive?: Partial<Record<Breakpoint, GridColumnCount>>,
49
54
  ): string => {
50
- if (hasSideMenu) {
51
- if (childCount <= 1) return "grid-cols-1";
52
- if (childCount <= 2) return "sm:grid-cols-2";
53
- if (childCount === 3) return "sm:grid-cols-2 md:grid-cols-3";
54
- if (childCount <= 5) return "sm:grid-cols-2 md:grid-cols-3";
55
- return "sm:grid-cols-2 md:grid-cols-3 xl:grid-cols-4 2xl:grid-cols-6";
56
- }
57
-
58
- if (childCount <= 1) return "grid-cols-1";
59
- if (childCount <= 2) return "sm:grid-cols-2";
60
- if (childCount === 3) return "sm:grid-cols-2 md:grid-cols-3";
61
- if (childCount <= 5) return "sm:grid-cols-2 md:grid-cols-3 lg:grid-cols-4";
62
- if (childCount <= 6)
63
- return "sm:grid-cols-2 md:grid-cols-3 lg:grid-cols-4 xl:grid-cols-6";
64
- if (childCount === 7)
65
- return "sm:grid-cols-2 md:grid-cols-3 lg:grid-cols-4 xl:grid-cols-6";
66
- if (childCount <= 11)
67
- return "sm:grid-cols-2 md:grid-cols-3 lg:grid-cols-4 xl:grid-cols-6 2xl:grid-cols-8";
68
- return "sm:grid-cols-2 md:grid-cols-3 lg:grid-cols-4 xl:grid-cols-8 2xl:grid-cols-12";
69
- };
70
-
71
- const getGridClasses = (
72
- childCount: number,
73
- hasSideMenu?: boolean,
74
- columns?: number,
75
- ) => {
76
- if (columns) {
77
- const map = hasSideMenu
78
- ? COLUMNS_WITH_SIDE_MENU_MAP
79
- : COLUMNS_WITHOUT_SIDE_MENU_MAP;
80
- return map[columns] ?? map[12];
55
+ const classes: string[] = [`grid-cols-${base}`];
56
+ if (responsive) {
57
+ (
58
+ Object.entries(responsive) as Array<[Breakpoint, GridColumnCount]>
59
+ ).forEach(([bp, value]) => {
60
+ if (value && value > 0) classes.push(`${bp}:grid-cols-${value}`);
61
+ });
81
62
  }
82
-
83
- return getChildCountBasedClasses(childCount, hasSideMenu ?? false);
63
+ return classes.join(" ");
84
64
  };
85
65
 
86
66
  export const Grid = ({
87
67
  children,
88
68
  sizing = "container",
89
69
  padding,
90
- hasSideMenu = false,
91
70
  columns,
71
+ responsive,
92
72
  id,
93
73
  testid,
94
74
  style,
75
+ className,
76
+ ...rest
95
77
  }: GridProps) => {
96
- const childCount = Children.count(children);
78
+ const columnClasses = buildColumnClasses(columns, responsive);
79
+
97
80
  return (
98
81
  <div
99
82
  id={id}
100
83
  data-testid={testid}
84
+ style={style}
85
+ {...rest}
101
86
  className={clsx(
102
- padding &&
103
- sizing === "container" &&
104
- "p-mobile-container-padding desktop:p-desktop-container-padding compact:p-desktop-compact-container-padding",
105
- padding &&
106
- sizing === "layout" &&
107
- "p-mobile-layout-padding desktop:p-desktop-layout-padding compact:p-desktop-compact-layout-padding",
108
- padding &&
109
- sizing === "layout-group" &&
110
- "p-mobile-layout-group-padding desktop:p-desktop-layout-group-padding compact:p-desktop-compact-layout-group-padding",
111
- padding &&
112
- sizing === "component" &&
113
- "p-mobile-component-padding desktop:p-desktop-component-padding compact:p-desktop-compact-component-padding",
114
- sizing === "layout-group" &&
115
- "gap-mobile-layout-group-gap desktop:gap-desktop-layout-group-gap compact:gap-compact-layout-group-gap",
116
- sizing === "layout" &&
117
- "gap-mobile-layout-gap desktop:gap-desktop-layout-gap compact:gap-compact-layout-gap",
118
- sizing === "container" &&
119
- "gap-mobile-container-gap desktop:gap-desktop-container-gap compact:gap-compact-container-gap",
120
- sizing === "component" &&
121
- "gap-mobile-component-gap desktop:gap-desktop-component-gap compact:gap-compact-component-gap",
122
87
  "w-full grid",
123
- getGridClasses(childCount, hasSideMenu, columns),
88
+ GAP_BY_SIZING[sizing] ?? GAP_BY_SIZING.container,
89
+ padding && (PADDING_BY_SIZING[sizing] ?? PADDING_BY_SIZING.container),
90
+ columnClasses,
91
+ "grid-cols-1 desktop:grid-cols-4",
92
+ className,
124
93
  )}
125
- style={style}
126
94
  >
127
95
  {children}
128
96
  </div>
@@ -0,0 +1,82 @@
1
+ import React, { useState } from "react";
2
+ import clsx from "clsx";
3
+ import { Stack } from "./Stack";
4
+ import { Icon } from "./Icon";
5
+ import { Label } from "./Label";
6
+
7
+ export type ListGroupProps = {
8
+ /** Heading title displayed in the clickable header */
9
+ title: React.ReactNode;
10
+ /** Start open by default */
11
+ defaultOpen?: boolean;
12
+ /** Controlled open state (if provided component becomes controlled) */
13
+ open?: boolean;
14
+ /** Called when the accordion toggles (newState) */
15
+ onToggle?: (open: boolean) => void;
16
+ /** Optional test id */
17
+ testid?: string;
18
+ /** Disable user toggling */
19
+ disabled?: boolean;
20
+ className?: string;
21
+ /** Adds a border around the group */
22
+ bordered?: boolean;
23
+ /** Adds padding to the content area (uses layout sizing tokens) */
24
+ padded?: boolean;
25
+ children: React.ReactNode;
26
+ };
27
+
28
+ /**
29
+ * ListGroup is a simple accordion-like container that reveals its children.
30
+ * It uses Stack for consistent spacing and layout.
31
+ */
32
+ export function ListGroup({
33
+ title,
34
+ defaultOpen = false,
35
+ open: controlledOpen,
36
+ onToggle,
37
+ testid,
38
+ disabled,
39
+ className,
40
+ children,
41
+ }: ListGroupProps) {
42
+ const [uncontrolledOpen, setUncontrolledOpen] = useState(defaultOpen);
43
+ const isControlled = controlledOpen !== undefined;
44
+ const isOpen = isControlled ? controlledOpen : uncontrolledOpen;
45
+
46
+ const toggle = () => {
47
+ if (disabled) return;
48
+ const next = !isOpen;
49
+ if (!isControlled) setUncontrolledOpen(next);
50
+ onToggle?.(next);
51
+ };
52
+
53
+ return (
54
+ <div data-testid={testid} className={clsx("rounded-sm", className)}>
55
+ <Stack sizing="layout" noGap>
56
+ <button
57
+ type="button"
58
+ onClick={toggle}
59
+ className={clsx(
60
+ "w-full flex items-center justify-between text-left",
61
+ disabled && "opacity-50 cursor-not-allowed",
62
+ )}
63
+ aria-expanded={isOpen}
64
+ >
65
+ <Label>{title}</Label>
66
+ <Icon
67
+ name="expand_more"
68
+ className={clsx(
69
+ "transition-transform duration-200",
70
+ isOpen ? "rotate-180" : "rotate-0",
71
+ )}
72
+ />
73
+ </button>
74
+ {isOpen && (
75
+ <Stack sizing="layout" paddingY>
76
+ {children}
77
+ </Stack>
78
+ )}
79
+ </Stack>
80
+ </div>
81
+ );
82
+ }
@@ -0,0 +1,182 @@
1
+ import React, { useCallback, useMemo } from "react";
2
+ import clsx from "clsx";
3
+ import { Icon } from "./Icon";
4
+ import { paddingUsingComponentGap } from "../classNames";
5
+ import { Subheader } from "./Subheader";
6
+
7
+ export interface PaginationProps {
8
+ /** Total number of pages (>=1). Component returns null if < 2. */
9
+ totalPages: number;
10
+ /** Current page (1-based, controlled). */
11
+ currentPage: number;
12
+ /** Parent controls state; we just notify desired target page. */
13
+ onPageChange: (page: number) => void;
14
+ id?: string;
15
+ testid?: string;
16
+ className?: string;
17
+ disabled?: boolean;
18
+ }
19
+
20
+ export const Pagination = ({
21
+ totalPages,
22
+ currentPage,
23
+ onPageChange,
24
+ id,
25
+ testid,
26
+ className,
27
+ disabled,
28
+ }: PaginationProps) => {
29
+ const goTo = useCallback(
30
+ (page: number) => {
31
+ if (disabled) return;
32
+ onPageChange(page);
33
+ },
34
+ [onPageChange, disabled],
35
+ );
36
+
37
+ const handleKey = (e: React.KeyboardEvent) => {
38
+ if (disabled) return;
39
+ if (e.key === "ArrowLeft") {
40
+ e.preventDefault();
41
+ goTo(currentPage - 1);
42
+ } else if (e.key === "ArrowRight") {
43
+ e.preventDefault();
44
+ goTo(currentPage + 1);
45
+ }
46
+ };
47
+
48
+ type PageToken = number | "ellipsis";
49
+
50
+ const pageTokens: PageToken[] = useMemo(() => {
51
+ // If 5 or fewer pages, show all directly
52
+ if (totalPages <= 5) {
53
+ return Array.from({ length: totalPages }, (_, i) => i + 1);
54
+ }
55
+
56
+ // Build a focused window around current page
57
+ const pages = new Set<number>();
58
+ pages.add(1);
59
+ pages.add(totalPages);
60
+
61
+ if (currentPage <= 3) {
62
+ // Near the start – show first 4 pages
63
+ pages.add(2);
64
+ pages.add(3);
65
+ pages.add(4);
66
+ } else if (currentPage >= totalPages - 2) {
67
+ // Near the end – show last 4 pages
68
+ pages.add(totalPages - 1);
69
+ pages.add(totalPages - 2);
70
+ pages.add(totalPages - 3);
71
+ } else {
72
+ // Middle – show current, one before & one after
73
+ pages.add(currentPage - 1);
74
+ pages.add(currentPage);
75
+ pages.add(currentPage + 1);
76
+ }
77
+
78
+ const sorted = Array.from(pages).sort((a, b) => a - b);
79
+
80
+ const tokens: PageToken[] = [];
81
+ for (let i = 0; i < sorted.length; i++) {
82
+ const value = sorted[i];
83
+ const prev = sorted[i - 1];
84
+ if (i > 0) {
85
+ if (value - prev === 2) {
86
+ // Single gap – include the missing number
87
+ tokens.push(prev + 1);
88
+ } else if (value - prev > 2) {
89
+ // Larger gap – ellipsis
90
+ tokens.push("ellipsis");
91
+ }
92
+ }
93
+ tokens.push(value);
94
+ }
95
+ return tokens;
96
+ }, [totalPages, currentPage]);
97
+
98
+ if (totalPages <= 1) return null;
99
+
100
+ const buttonClass = clsx(
101
+ "cursor-pointer disabled:cursor-default",
102
+ paddingUsingComponentGap,
103
+ "w-8 h-8",
104
+ "flex items-center justify-center",
105
+ "bg-transparent",
106
+ "box-content",
107
+ "hover:bg-background-grouped-secondary-normal",
108
+ "focus:bg-background-grouped-secondary-normal focus:outline-0",
109
+ "disabled:bg-transparent disabled:text-text-action-primary-disabled",
110
+ );
111
+
112
+ return (
113
+ <nav
114
+ id={id}
115
+ data-testid={testid}
116
+ aria-label="Pagination"
117
+ onKeyDown={handleKey}
118
+ className={clsx(
119
+ "flex items-center",
120
+ "border border-border-primary-normal",
121
+ "bg-background-grouped-primary-normal",
122
+ "rounded-sm",
123
+ className,
124
+ )}
125
+ >
126
+ <button
127
+ disabled={disabled || currentPage <= 1}
128
+ aria-label="Previous page"
129
+ onClick={() => goTo(currentPage - 1)}
130
+ className={clsx(buttonClass, "border-r-1 border-border-primary-normal")}
131
+ >
132
+ <Icon name="keyboard_arrow_left" />
133
+ </button>
134
+
135
+ <ul className={clsx("flex items-center")}>
136
+ {pageTokens.map((token, index) => {
137
+ if (token === "ellipsis") {
138
+ return (
139
+ <li
140
+ key={`ellipsis-${index}`}
141
+ className="w-8 h-8 select-none text-text-action-primary-disabled"
142
+ >
143
+
144
+ </li>
145
+ );
146
+ }
147
+ const selected = token === currentPage;
148
+ return (
149
+ <li key={token}>
150
+ <button
151
+ aria-label={`Page ${token}`}
152
+ aria-current={selected ? "page" : undefined}
153
+ disabled={disabled}
154
+ onClick={() => goTo(token)}
155
+ className={clsx(
156
+ buttonClass,
157
+ selected &&
158
+ "border-x-1 bg-background-grouped-secondary-normal border-border-primary-normal",
159
+ )}
160
+ >
161
+ <Subheader align="center" weight="bold">
162
+ {token}
163
+ </Subheader>
164
+ </button>
165
+ </li>
166
+ );
167
+ })}
168
+ </ul>
169
+
170
+ <button
171
+ disabled={disabled || currentPage >= totalPages}
172
+ aria-label="Next page"
173
+ onClick={() => goTo(currentPage + 1)}
174
+ className={clsx(buttonClass, "border-l-1 border-border-primary-normal")}
175
+ >
176
+ <Icon name="keyboard_arrow_right" />
177
+ </button>
178
+ </nav>
179
+ );
180
+ };
181
+
182
+ Pagination.displayName = "Pagination";
@@ -0,0 +1,77 @@
1
+ import { ColumnDef } from "@tanstack/react-table";
2
+ import { ReactNode } from "react";
3
+ import { Subheader } from "./Subheader";
4
+ import clsx from "clsx";
5
+ import { Stack } from "./Stack";
6
+
7
+ type SimpleTableProps<T> = {
8
+ columns: ColumnDef<T>[];
9
+ data: T[];
10
+ /** Optional additional rows appended after the main data (e.g. totals) */
11
+ additionalRows?: T[] | null;
12
+ };
13
+
14
+ export function SimpleTable<T extends Record<string, unknown>>({
15
+ columns,
16
+ data,
17
+ additionalRows,
18
+ }: SimpleTableProps<T>) {
19
+ const allRows =
20
+ additionalRows && additionalRows.length > 0
21
+ ? [...data, ...additionalRows]
22
+ : data;
23
+
24
+ return (
25
+ <Stack elevation={4} rounded overflowX="hidden" overflowY="hidden">
26
+ <table className="w-full border-collapse text-left text-sm border overflow-hidden rounded border-border-primary-normal">
27
+ <thead>
28
+ <tr className="bg-background-primary-normal divide-x divide-border-primary-normal">
29
+ {columns.map((column, index) => (
30
+ <th
31
+ key={index}
32
+ scope="col"
33
+ className={clsx(
34
+ "p-mobile-layout-padding desktop:p-desktop-layout-padding font-semibold text-text-primary-normal",
35
+ column.meta?.headerWidth ? column.meta.headerWidth : "w-2/3",
36
+ )}
37
+ >
38
+ <Subheader>{column.header?.toString()}</Subheader>
39
+ </th>
40
+ ))}
41
+ </tr>
42
+ </thead>
43
+ <tbody>
44
+ {allRows.map((row, rowIndex) => (
45
+ <tr
46
+ key={rowIndex}
47
+ className="border-t border-border-primary-normal divide-x divide-border-primary-normal"
48
+ >
49
+ {columns.map((column, colIndex) => {
50
+ const rawValue = column.id
51
+ ? (row as Record<string, unknown>)[column.id]
52
+ : null;
53
+ const cellValue: ReactNode = (rawValue as ReactNode) ?? null;
54
+ return (
55
+ <td
56
+ key={colIndex}
57
+ className={clsx(
58
+ "p-mobile-layout-padding desktop:p-desktop-layout-padding align-middle text-text-primary-normal",
59
+ column.meta?.headerWidth,
60
+ )}
61
+ >
62
+ {cellValue !== null && cellValue !== undefined
63
+ ? typeof cellValue === "string" ||
64
+ typeof cellValue === "number"
65
+ ? cellValue.toString()
66
+ : cellValue
67
+ : ""}
68
+ </td>
69
+ );
70
+ })}
71
+ </tr>
72
+ ))}
73
+ </tbody>
74
+ </table>
75
+ </Stack>
76
+ );
77
+ }
@@ -17,6 +17,10 @@ type StackProps = {
17
17
  padding?: boolean;
18
18
  paddingX?: boolean;
19
19
  paddingY?: boolean;
20
+ paddingBottom?: boolean;
21
+ paddingTop?: boolean;
22
+ paddingLeft?: boolean;
23
+ paddingRight?: boolean;
20
24
  margin?: boolean;
21
25
  marginX?: boolean;
22
26
  marginY?: boolean;
@@ -33,6 +37,10 @@ type StackProps = {
33
37
  minHeight?: number | string;
34
38
  maxHeight?: number | string;
35
39
  borderColor?: BorderColorTokens;
40
+ borderTopColor?: BorderColorTokens;
41
+ borderRightColor?: BorderColorTokens;
42
+ borderBottomColor?: BorderColorTokens;
43
+ borderLeftColor?: BorderColorTokens;
36
44
  backgroundColor?: BackgroundColorTokens;
37
45
  overflowY?: "hidden" | "scroll" | "auto" | "inherit";
38
46
  overflowX?: "hidden" | "scroll" | "auto" | "inherit";
@@ -96,6 +104,10 @@ export const Stack = ({
96
104
  padding,
97
105
  paddingX,
98
106
  paddingY,
107
+ paddingBottom,
108
+ paddingTop,
109
+ paddingLeft,
110
+ paddingRight,
99
111
  margin,
100
112
  marginX,
101
113
  marginY,
@@ -111,6 +123,10 @@ export const Stack = ({
111
123
  height,
112
124
  maxHeight,
113
125
  borderColor,
126
+ borderTopColor,
127
+ borderRightColor,
128
+ borderBottomColor,
129
+ borderLeftColor,
114
130
  backgroundColor,
115
131
  sizing = "none",
116
132
  overflowY = "inherit",
@@ -172,6 +188,18 @@ export const Stack = ({
172
188
  border: borderColor
173
189
  ? `1px solid var(--color-${borderColor})`
174
190
  : undefined,
191
+ borderTop: borderTopColor
192
+ ? `1px solid var(--color-${borderTopColor})`
193
+ : undefined,
194
+ borderRight: borderRightColor
195
+ ? `1px solid var(--color-${borderRightColor})`
196
+ : undefined,
197
+ borderBottom: borderBottomColor
198
+ ? `1px solid var(--color-${borderBottomColor})`
199
+ : undefined,
200
+ borderLeft: borderLeftColor
201
+ ? `1px solid var(--color-${borderLeftColor})`
202
+ : undefined,
175
203
  backgroundColor: backgroundColor
176
204
  ? `var(--color-${backgroundColor})`
177
205
  : undefined,
@@ -235,6 +263,54 @@ export const Stack = ({
235
263
  paddingY &&
236
264
  sizing === "component" &&
237
265
  "py-mobile-component-padding desktop:py-desktop-component-padding compact:py-desktop-compact-component-padding",
266
+ paddingBottom &&
267
+ sizing === "container" &&
268
+ "pb-mobile-container-padding desktop:pb-desktop-container-padding compact:pb-desktop-compact-container-padding",
269
+ paddingBottom &&
270
+ sizing === "layout" &&
271
+ "pb-mobile-layout-padding desktop:pb-desktop-layout-padding compact:pb-desktop-compact-layout-padding",
272
+ paddingBottom &&
273
+ sizing === "layout-group" &&
274
+ "pb-mobile-layout-group-padding desktop:pb-desktop-layout-group-padding compact:pb-desktop-compact-layout-group-padding",
275
+ paddingBottom &&
276
+ sizing === "component" &&
277
+ "pb-mobile-component-padding desktop:pb-desktop-component-padding compact:pb-desktop-compact-component-padding",
278
+ paddingTop &&
279
+ sizing === "container" &&
280
+ "pt-mobile-container-padding desktop:pt-desktop-container-padding compact:pt-desktop-compact-container-padding",
281
+ paddingTop &&
282
+ sizing === "layout" &&
283
+ "pt-mobile-layout-padding desktop:pt-desktop-layout-padding compact:pt-desktop-compact-layout-padding",
284
+ paddingTop &&
285
+ sizing === "layout-group" &&
286
+ "pt-mobile-layout-group-padding desktop:pt-desktop-layout-group-padding compact:pt-desktop-compact-layout-group-padding",
287
+ paddingTop &&
288
+ sizing === "component" &&
289
+ "pt-mobile-component-padding desktop:pt-desktop-component-padding compact:pt-desktop-compact-component-padding",
290
+ paddingLeft &&
291
+ sizing === "container" &&
292
+ "pl-mobile-container-padding desktop:pl-desktop-container-padding compact:pl-desktop-compact-container-padding",
293
+ paddingLeft &&
294
+ sizing === "layout" &&
295
+ "pl-mobile-layout-padding desktop:pl-desktop-layout-padding compact:pl-desktop-compact-layout-padding",
296
+ paddingLeft &&
297
+ sizing === "layout-group" &&
298
+ "pl-mobile-layout-group-padding desktop:pl-desktop-layout-group-padding compact:pl-desktop-compact-layout-group-padding",
299
+ paddingLeft &&
300
+ sizing === "component" &&
301
+ "pl-mobile-component-padding desktop:pl-desktop-component-padding compact:pl-desktop-compact-component-padding",
302
+ paddingRight &&
303
+ sizing === "container" &&
304
+ "pr-mobile-container-padding desktop:pr-desktop-container-padding compact:pr-desktop-compact-container-padding",
305
+ paddingRight &&
306
+ sizing === "layout" &&
307
+ "pr-mobile-layout-padding desktop:pr-desktop-layout-padding compact:pr-desktop-compact-layout-padding",
308
+ paddingRight &&
309
+ sizing === "layout-group" &&
310
+ "pr-mobile-layout-group-padding desktop:pr-desktop-layout-group-padding compact:pr-desktop-compact-layout-group-padding",
311
+ paddingRight &&
312
+ sizing === "component" &&
313
+ "pr-mobile-component-padding desktop:pr-desktop-component-padding compact:pr-desktop-compact-component-padding",
238
314
  margin &&
239
315
  sizing === "container" &&
240
316
  "m-mobile-container-padding desktop:m-desktop-container-padding compact:m-compact-container-padding",
@@ -30,7 +30,7 @@ export const Tooltip = ({
30
30
  children,
31
31
  showOnTruncation = false,
32
32
  offset = 8,
33
- keepHidden = false
33
+ keepHidden = false,
34
34
  }: TooltipProps) => {
35
35
  const ref = useRef<HTMLDivElement>(null);
36
36
  const tooltipRef = useRef<HTMLDivElement>(null);
@@ -105,13 +105,14 @@ export const Tooltip = ({
105
105
  id={id}
106
106
  data-testid={testid}
107
107
  ref={ref}
108
- className="relative inline-grid grid-cols-[auto_1fr] items-center"
108
+ className="relative inline-grid grid-cols-[auto_1fr] items-center cursor-pointer"
109
109
  onMouseEnter={handleMouseEnter}
110
110
  onMouseLeave={handleMouseLeave}
111
111
  >
112
112
  {children}
113
113
 
114
- {!keepHidden && isVisible &&
114
+ {!keepHidden &&
115
+ isVisible &&
115
116
  typeof document !== "undefined" &&
116
117
  createPortal(
117
118
  <div