@dmsi/wedgekit-react 0.0.369 → 0.0.371

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 (205) hide show
  1. package/dist/{chunk-RLLQRVM7.js → chunk-2H35FETR.js} +18 -10
  2. package/dist/chunk-2IKT6IHB.js +190 -0
  3. package/dist/chunk-4UNWXB4A.js +89 -0
  4. package/dist/chunk-5IFPG6TS.js +17 -0
  5. package/dist/{chunk-6GAYJCFE.js → chunk-6DPFKSCT.js} +1 -1
  6. package/dist/{chunk-ZFOANBWG.js → chunk-AG43RS4Q.js} +2 -1
  7. package/dist/chunk-AJ5M6MVX.js +7 -0
  8. package/dist/chunk-AT4AWD6B.js +44 -0
  9. package/dist/chunk-BQNPOGD5.js +105 -0
  10. package/dist/chunk-CQFPNZTN.js +172 -0
  11. package/dist/chunk-EJSPFQCY.js +29 -0
  12. package/dist/chunk-ER6RCOH3.js +97 -0
  13. package/dist/{chunk-4VER5OEU.js → chunk-FBE2HGEF.js} +35 -11
  14. package/dist/chunk-HPQWEZJL.js +45 -0
  15. package/dist/{chunk-URCLLHO5.js → chunk-IBX6DVHU.js} +376 -102
  16. package/dist/{chunk-I3WFZOFY.js → chunk-J5V2JRIK.js} +1 -1
  17. package/dist/chunk-JGJUVJKD.js +283 -0
  18. package/dist/chunk-KEMCFN4U.js +78 -0
  19. package/dist/chunk-M6TSTDNZ.js +22 -0
  20. package/dist/chunk-M7INAUAJ.js +140 -0
  21. package/dist/chunk-MBZ55T2D.js +51 -0
  22. package/dist/chunk-N6PNLLNS.js +77 -0
  23. package/dist/{chunk-ZA5E7ZYM.js → chunk-NXGUDYRR.js} +1 -1
  24. package/dist/chunk-P36QKH26.js +143 -0
  25. package/dist/chunk-PTRZHGHA.js +89 -0
  26. package/dist/chunk-QVWYTQKL.js +29 -0
  27. package/dist/{chunk-6CPGOW6J.js → chunk-T36HX6QY.js} +6 -4
  28. package/dist/chunk-U6PUOGG4.js +114 -0
  29. package/dist/{chunk-NQXZBWDZ.js → chunk-V6U7LU6M.js} +15 -6
  30. package/dist/chunk-VJVY6NPF.js +32 -0
  31. package/dist/chunk-VVXPGI2P.js +61 -0
  32. package/dist/{chunk-ARQBSR3I.js → chunk-YCKRVNJ3.js} +4 -4
  33. package/dist/chunk-YYHQLQDQ.js +68 -0
  34. package/dist/components/Accordion.cjs +47 -14
  35. package/dist/components/Accordion.js +2 -2
  36. package/dist/components/CalendarRange.cjs +700 -46
  37. package/dist/components/CalendarRange.css +186 -3
  38. package/dist/components/CalendarRange.js +43 -11
  39. package/dist/components/CompactImagesPreview.cjs +485 -0
  40. package/dist/components/CompactImagesPreview.js +13 -0
  41. package/dist/components/ContentTabs.cjs +3 -2
  42. package/dist/components/ContentTabs.js +3 -2
  43. package/dist/components/DataGrid/ColumnSelectorHeaderCell/ColumnSelectorMenuOption.cjs +4687 -0
  44. package/dist/components/DataGrid/ColumnSelectorHeaderCell/ColumnSelectorMenuOption.css +5051 -0
  45. package/dist/components/DataGrid/ColumnSelectorHeaderCell/ColumnSelectorMenuOption.js +62 -0
  46. package/dist/components/DataGrid/ColumnSelectorHeaderCell/index.cjs +4687 -0
  47. package/dist/components/DataGrid/ColumnSelectorHeaderCell/index.css +5051 -0
  48. package/dist/components/DataGrid/ColumnSelectorHeaderCell/index.js +62 -0
  49. package/dist/components/DataGrid/PinnedColumns.cjs +4687 -0
  50. package/dist/components/DataGrid/PinnedColumns.css +5051 -0
  51. package/dist/components/DataGrid/PinnedColumns.js +62 -0
  52. package/dist/components/DataGrid/TableBody/LoadingCell.cjs +4689 -0
  53. package/dist/components/DataGrid/TableBody/LoadingCell.css +5051 -0
  54. package/dist/components/DataGrid/TableBody/LoadingCell.js +62 -0
  55. package/dist/components/DataGrid/TableBody/TableBodyRow.cjs +4689 -0
  56. package/dist/components/DataGrid/TableBody/TableBodyRow.css +5051 -0
  57. package/dist/components/DataGrid/TableBody/TableBodyRow.js +62 -0
  58. package/dist/components/DataGrid/TableBody/index.cjs +4689 -0
  59. package/dist/components/DataGrid/TableBody/index.css +5051 -0
  60. package/dist/components/DataGrid/TableBody/index.js +62 -0
  61. package/dist/components/DataGrid/index.cjs +4692 -0
  62. package/dist/components/DataGrid/index.css +5051 -0
  63. package/dist/components/DataGrid/index.js +65 -0
  64. package/dist/components/DataGrid/utils.cjs +4687 -0
  65. package/dist/components/DataGrid/utils.css +5051 -0
  66. package/dist/components/DataGrid/utils.js +62 -0
  67. package/dist/components/DataGridCell.js +6 -6
  68. package/dist/components/DateInput.cjs +721 -67
  69. package/dist/components/DateInput.css +186 -3
  70. package/dist/components/DateInput.js +45 -13
  71. package/dist/components/DateRangeInput.cjs +721 -67
  72. package/dist/components/DateRangeInput.css +186 -3
  73. package/dist/components/DateRangeInput.js +45 -13
  74. package/dist/components/FilterGroup.js +3 -3
  75. package/dist/components/Grid.cjs +3 -1
  76. package/dist/components/Grid.js +3 -92
  77. package/dist/components/ImagePlaceholder.cjs +65 -0
  78. package/dist/components/ImagePlaceholder.js +7 -0
  79. package/dist/components/Input.js +2 -2
  80. package/dist/components/MenuOption.js +2 -2
  81. package/dist/components/MobileDataGrid/ColumnList.cjs +845 -0
  82. package/dist/components/MobileDataGrid/ColumnList.js +17 -0
  83. package/dist/components/MobileDataGrid/ColumnSelector/index.cjs +4797 -0
  84. package/dist/components/MobileDataGrid/ColumnSelector/index.css +5051 -0
  85. package/dist/components/MobileDataGrid/ColumnSelector/index.js +62 -0
  86. package/dist/components/MobileDataGrid/GridContextProvider/GridContext.cjs +31 -0
  87. package/dist/components/MobileDataGrid/GridContextProvider/GridContext.js +7 -0
  88. package/dist/components/MobileDataGrid/GridContextProvider/index.cjs +177 -0
  89. package/dist/components/MobileDataGrid/GridContextProvider/index.js +8 -0
  90. package/dist/components/MobileDataGrid/MobileDataGridCard/MobileDataGridColumn.cjs +269 -0
  91. package/dist/components/MobileDataGrid/MobileDataGridCard/MobileDataGridColumn.js +9 -0
  92. package/dist/components/MobileDataGrid/MobileDataGridCard/index.cjs +790 -0
  93. package/dist/components/MobileDataGrid/MobileDataGridCard/index.js +16 -0
  94. package/dist/components/MobileDataGrid/MobileDataGridHeader.cjs +5059 -0
  95. package/dist/components/MobileDataGrid/MobileDataGridHeader.css +5051 -0
  96. package/dist/components/MobileDataGrid/MobileDataGridHeader.js +62 -0
  97. package/dist/components/MobileDataGrid/RowDetailModalProvider/ModalContent.cjs +509 -0
  98. package/dist/components/MobileDataGrid/RowDetailModalProvider/ModalContent.js +13 -0
  99. package/dist/components/MobileDataGrid/RowDetailModalProvider/index.cjs +1261 -0
  100. package/dist/components/MobileDataGrid/RowDetailModalProvider/index.js +27 -0
  101. package/dist/components/MobileDataGrid/index.cjs +5521 -0
  102. package/dist/components/MobileDataGrid/index.css +5051 -0
  103. package/dist/components/MobileDataGrid/index.js +62 -0
  104. package/dist/components/Modal.cjs +24 -13
  105. package/dist/components/Modal.js +3 -3
  106. package/dist/components/ModalHeader.cjs +6 -4
  107. package/dist/components/ModalHeader.js +1 -1
  108. package/dist/components/ModalScrim.cjs +2 -1
  109. package/dist/components/ModalScrim.js +1 -1
  110. package/dist/components/NestedMenu.js +4 -4
  111. package/dist/components/Notification.cjs +15 -6
  112. package/dist/components/Notification.js +1 -1
  113. package/dist/components/PDFViewer/DownloadIcon.cjs +394 -0
  114. package/dist/components/PDFViewer/DownloadIcon.js +10 -0
  115. package/dist/components/PDFViewer/PDFElement.cjs +515 -0
  116. package/dist/components/PDFViewer/PDFElement.js +11 -0
  117. package/dist/components/{MobileDataGrid.cjs → PDFViewer/PDFNavigation.cjs} +318 -402
  118. package/dist/components/PDFViewer/PDFNavigation.js +13 -0
  119. package/dist/components/PDFViewer/PDFPage.cjs +56 -0
  120. package/dist/components/PDFViewer/PDFPage.js +7 -0
  121. package/dist/components/{PDFViewer.cjs → PDFViewer/index.cjs} +290 -202
  122. package/dist/components/PDFViewer/index.js +29 -0
  123. package/dist/components/Password.js +2 -2
  124. package/dist/components/ProductImagePreview/CarouselPagination.cjs +75 -0
  125. package/dist/components/ProductImagePreview/CarouselPagination.js +7 -0
  126. package/dist/components/ProductImagePreview/MobileImageCarousel.cjs +214 -0
  127. package/dist/components/ProductImagePreview/MobileImageCarousel.js +7 -0
  128. package/dist/components/ProductImagePreview/ProductPrimaryImage.cjs +255 -0
  129. package/dist/components/ProductImagePreview/ProductPrimaryImage.js +9 -0
  130. package/dist/components/ProductImagePreview/Thumbnail.cjs +105 -0
  131. package/dist/components/ProductImagePreview/Thumbnail.js +8 -0
  132. package/dist/components/ProductImagePreview/ZoomWindow.cjs +198 -0
  133. package/dist/components/ProductImagePreview/ZoomWindow.js +8 -0
  134. package/dist/components/ProductImagePreview/index.cjs +1369 -0
  135. package/dist/components/ProductImagePreview/index.js +22 -0
  136. package/dist/components/Search.js +3 -3
  137. package/dist/components/Select.js +3 -3
  138. package/dist/components/SideMenuGroup.cjs +15 -6
  139. package/dist/components/SideMenuGroup.js +1 -1
  140. package/dist/components/SideMenuItem.cjs +15 -6
  141. package/dist/components/SideMenuItem.js +1 -1
  142. package/dist/components/SkeletonParagraph.cjs +33 -0
  143. package/dist/components/SkeletonParagraph.js +10 -0
  144. package/dist/components/Stack.cjs +15 -6
  145. package/dist/components/Stack.js +1 -1
  146. package/dist/components/Stepper.cjs +61 -53
  147. package/dist/components/Stepper.js +63 -55
  148. package/dist/components/Surface.js +3 -39
  149. package/dist/components/Swatch.cjs +15 -6
  150. package/dist/components/Swatch.js +4 -4
  151. package/dist/components/Time.cjs +15 -6
  152. package/dist/components/Time.js +5 -5
  153. package/dist/components/Upload.cjs +15 -6
  154. package/dist/components/Upload.js +1 -1
  155. package/dist/components/index.cjs +2559 -14
  156. package/dist/components/index.css +186 -3
  157. package/dist/components/index.js +57 -14
  158. package/dist/index.css +186 -3
  159. package/package.json +1 -1
  160. package/src/components/Accordion.tsx +23 -4
  161. package/src/components/CompactImagesPreview.tsx +99 -0
  162. package/src/components/ContentTabs.tsx +3 -1
  163. package/src/components/DataGrid/types.ts +5 -0
  164. package/src/components/Grid.tsx +2 -0
  165. package/src/components/ImagePlaceholder.tsx +22 -0
  166. package/src/components/MobileDataGrid/ColumnList.tsx +66 -0
  167. package/src/components/MobileDataGrid/ColumnSelector/index.tsx +97 -0
  168. package/src/components/MobileDataGrid/GridContextProvider/GridContext.tsx +25 -0
  169. package/src/components/MobileDataGrid/GridContextProvider/index.tsx +132 -0
  170. package/src/components/MobileDataGrid/GridContextProvider/useGridContext.ts +10 -0
  171. package/src/components/MobileDataGrid/MobileDataGridCard/MobileDataGridColumn.tsx +20 -0
  172. package/src/components/MobileDataGrid/MobileDataGridCard/index.tsx +129 -0
  173. package/src/components/MobileDataGrid/MobileDataGridHeader.tsx +80 -0
  174. package/src/components/MobileDataGrid/RowDetailModalProvider/ModalContent.tsx +42 -0
  175. package/src/components/MobileDataGrid/RowDetailModalProvider/index.tsx +68 -0
  176. package/src/components/MobileDataGrid/dataGridReducer.ts +55 -0
  177. package/src/components/MobileDataGrid/index.tsx +92 -0
  178. package/src/components/MobileDataGrid/types.ts +4 -0
  179. package/src/components/Modal.tsx +31 -12
  180. package/src/components/ModalButtons.tsx +1 -1
  181. package/src/components/ModalHeader.tsx +5 -2
  182. package/src/components/ModalScrim.tsx +3 -2
  183. package/src/components/PDFViewer/DownloadIcon.tsx +22 -0
  184. package/src/components/PDFViewer/PDFElement.tsx +90 -0
  185. package/src/components/PDFViewer/PDFNavigation.tsx +68 -0
  186. package/src/components/PDFViewer/PDFPage.tsx +34 -0
  187. package/src/components/PDFViewer/index.tsx +128 -0
  188. package/src/components/ProductImagePreview/CarouselPagination.tsx +54 -0
  189. package/src/components/ProductImagePreview/MobileImageCarousel.tsx +226 -0
  190. package/src/components/ProductImagePreview/ProductPrimaryImage.tsx +218 -0
  191. package/src/components/ProductImagePreview/Thumbnail.tsx +49 -0
  192. package/src/components/ProductImagePreview/ZoomWindow.tsx +136 -0
  193. package/src/components/ProductImagePreview/index.tsx +182 -0
  194. package/src/components/ProductImagePreview/useProductImagePreview.ts +211 -0
  195. package/src/components/SkeletonParagraph.tsx +5 -0
  196. package/src/components/Stack.tsx +29 -6
  197. package/src/components/Stepper.tsx +5 -1
  198. package/src/components/index.ts +4 -0
  199. package/src/types.ts +2 -1
  200. package/dist/components/MobileDataGrid.js +0 -150
  201. package/dist/components/PDFViewer.js +0 -250
  202. package/src/components/MobileDataGrid.tsx +0 -163
  203. package/src/components/PDFViewer.tsx +0 -264
  204. package/dist/{chunk-OXSBIBGT.js → chunk-CKQNJNU3.js} +3 -3
  205. package/dist/{chunk-RJUN52HJ.js → chunk-ZL5X7KP6.js} +3 -3
@@ -0,0 +1,92 @@
1
+ import { ComponentProps } from "react";
2
+ import { MobileDataGridHeader } from "./MobileDataGridHeader";
3
+ import { ColumnDef } from "@tanstack/react-table";
4
+ import { GridContextProvider } from "./GridContextProvider";
5
+ import { RowDetailModalProvider } from "./RowDetailModalProvider";
6
+ import { GridContextType } from "./GridContextProvider/GridContext";
7
+ import { ColumnList } from "./ColumnList";
8
+ import { Stack } from "../Stack";
9
+ import { RowActions, RowData } from "./types";
10
+
11
+ export type MobileDataGridProps<T> = {
12
+ columns: ColumnDef<T>[];
13
+ data: T[];
14
+ header?: string | ((ctx: GridContextType<T>) => React.ReactNode);
15
+ getId: (data: T) => string | number | undefined;
16
+ renderLink?: (data: T) => React.ReactNode;
17
+ primaryKey: keyof T;
18
+ id?: string;
19
+ testid?: string;
20
+ enableColumnSelector?: boolean;
21
+ enableRowSelection?: boolean;
22
+ onRowSelect?: (row: T, selectedIds: (string | number)[]) => void;
23
+ withBorder?: boolean;
24
+ footer?: (ctx: GridContextType<T>) => React.ReactNode;
25
+ numberOfColumnsToShow?: number;
26
+ renderChevron?: boolean;
27
+ hideHeader?: boolean;
28
+ rowActions?: RowActions<T>;
29
+ rounded?: boolean;
30
+ };
31
+
32
+ export function MobileDataGrid<T extends RowData>(
33
+ props: MobileDataGridProps<T> & ComponentProps<"div">,
34
+ ) {
35
+ const {
36
+ columns,
37
+ data,
38
+ renderLink,
39
+ renderChevron,
40
+ getId,
41
+ id,
42
+ testid,
43
+ enableColumnSelector,
44
+ enableRowSelection,
45
+ header,
46
+ withBorder = true,
47
+ footer,
48
+ onRowSelect,
49
+ numberOfColumnsToShow,
50
+ primaryKey,
51
+ hideHeader,
52
+ rowActions,
53
+ rounded,
54
+ } = props;
55
+
56
+ return (
57
+ <GridContextProvider<T>
58
+ initialColumns={columns}
59
+ id={id}
60
+ testid={testid}
61
+ data={data}
62
+ getId={getId}
63
+ onRowSelect={onRowSelect}
64
+ numberOfColumnsToShow={numberOfColumnsToShow}
65
+ primaryKey={primaryKey}
66
+ >
67
+ <Stack
68
+ height="full"
69
+ rounded={rounded}
70
+ overflowX="hidden"
71
+ overflowY="hidden"
72
+ >
73
+ {!hideHeader && (
74
+ <MobileDataGridHeader<T>
75
+ header={header}
76
+ enableColumnSelector={enableColumnSelector}
77
+ enableRowSelection={enableRowSelection}
78
+ />
79
+ )}
80
+ <ColumnList<T>
81
+ withBorder={withBorder}
82
+ renderLink={renderLink}
83
+ renderChevron={renderChevron}
84
+ enableRowSelection={enableRowSelection}
85
+ footer={footer}
86
+ rowActions={rowActions}
87
+ />
88
+ </Stack>
89
+ <RowDetailModalProvider />
90
+ </GridContextProvider>
91
+ );
92
+ }
@@ -0,0 +1,4 @@
1
+ import { ReactNode } from "react";
2
+
3
+ export type RowActions<T> = (rowData: T) => ReactNode;
4
+ export type RowData = Record<string, unknown>;
@@ -17,7 +17,7 @@ type ModalProps = PropsWithChildren<{
17
17
  testid?: string;
18
18
  title?: string;
19
19
  open?: boolean;
20
- size?: "small" | "medium" | "large" | "x-large";
20
+ size?: "small" | "medium" | "large" | "x-large" | "screen";
21
21
  className?: string;
22
22
  onClose?: () => void;
23
23
  onContinue?: () => void;
@@ -28,6 +28,10 @@ type ModalProps = PropsWithChildren<{
28
28
  headerIconAlign?: "left" | "center" | "right";
29
29
  fixedHeightScrolling?: boolean;
30
30
  customActions?: React.ReactNode;
31
+ headerClassname?: string;
32
+ backgroundColor?: string; // full twcss color with bg- prefix
33
+ noWrapper?: boolean;
34
+ customFooter?: boolean;
31
35
  }>;
32
36
 
33
37
  const fadeInScale = (element: HTMLElement, duration = 300) =>
@@ -102,6 +106,9 @@ const sizes = {
102
106
  sizeClass:
103
107
  "desktop:max-w-300 w-screen desktop:h-fit h-screen desktop:w-full max-w-240",
104
108
  },
109
+ screen: {
110
+ sizeClass: "w-screen h-screen max-w-screen !rounded-none",
111
+ },
105
112
  };
106
113
 
107
114
  export const Modal = ({
@@ -121,6 +128,9 @@ export const Modal = ({
121
128
  headerIconAlign,
122
129
  fixedHeightScrolling = false,
123
130
  customActions,
131
+ headerClassname,
132
+ customFooter,
133
+ noWrapper,
124
134
  }: ModalProps) => {
125
135
  const mounted = useMounted();
126
136
 
@@ -246,10 +256,12 @@ export const Modal = ({
246
256
  data-testid={testid}
247
257
  ref={modalRef}
248
258
  className={clsx(
249
- "bg-white shadow-md rounded-sm flex flex-col overflow-hidden w-full opacity-0 h-fit",
259
+ "shadow-md rounded-sm flex flex-col overflow-hidden w-full opacity-0 h-fit",
250
260
  computedFixedHeightScrolling &&
261
+ size !== "screen" &&
251
262
  "desktop:max-h-[calc(100vh-32px)] desktop:h-auto",
252
263
  className,
264
+ !className?.includes("bg-") && "bg-white",
253
265
  sizeClass,
254
266
  )}
255
267
  onClick={(e) => e.stopPropagation()}
@@ -262,8 +274,9 @@ export const Modal = ({
262
274
  hideCloseIcon={hideCloseIcon}
263
275
  headerIcon={headerIcon}
264
276
  headerIconAlign={headerIconAlign}
277
+ headerClassname={headerClassname}
265
278
  />
266
- {children && (
279
+ {children && (size !== "screen" || noWrapper) ? (
267
280
  <ModalContent
268
281
  id={id ? `${id}-content` : undefined}
269
282
  testid={testid ? `${testid}-content` : undefined}
@@ -271,16 +284,22 @@ export const Modal = ({
271
284
  >
272
285
  {children}
273
286
  </ModalContent>
287
+ ) : (
288
+ children
274
289
  )}
275
- {showButtons && (
276
- <ModalButtons
277
- id={id ? `${id}-buttons` : undefined}
278
- testid={testid ? `${testid}-buttons` : undefined}
279
- onClose={handleClose}
280
- onContinue={onContinue}
281
- customActions={customActions}
282
- />
283
- )}
290
+ {showButtons ? (
291
+ customFooter ? (
292
+ customActions
293
+ ) : (
294
+ <ModalButtons
295
+ id={id ? `${id}-buttons` : undefined}
296
+ testid={testid ? `${testid}-buttons` : undefined}
297
+ onClose={handleClose}
298
+ onContinue={onContinue}
299
+ customActions={customActions}
300
+ />
301
+ )
302
+ ) : null}
284
303
  </div>
285
304
  </ModalScrim>,
286
305
  findDocumentRoot(bgRef.current),
@@ -18,7 +18,7 @@ export const ModalButtons = ({
18
18
  onContinue,
19
19
  customActions,
20
20
  id,
21
- testid
21
+ testid,
22
22
  }: ModalButtonsProps) => {
23
23
  return (
24
24
  <div
@@ -13,6 +13,7 @@ type ModalHeaderProps = {
13
13
  onClose?: () => void;
14
14
  id?: string;
15
15
  testid?: string;
16
+ headerClassname?: string;
16
17
  };
17
18
 
18
19
  export const ModalHeader = ({
@@ -23,6 +24,7 @@ export const ModalHeader = ({
23
24
  onClose,
24
25
  id,
25
26
  testid,
27
+ headerClassname,
26
28
  }: ModalHeaderProps) => {
27
29
  return (
28
30
  <div
@@ -35,9 +37,10 @@ export const ModalHeader = ({
35
37
  headerIconAlign === "left" && "justify-start",
36
38
  layoutPaddding,
37
39
  layoutGroupGap,
40
+ headerClassname,
38
41
  )}
39
42
  >
40
- <div className={clsx("flex items-center", layoutGroupGap)}>
43
+ <div className={clsx("flex items-center flex-1", layoutGroupGap)}>
41
44
  {headerIcon}
42
45
  {title && (
43
46
  <Heading2
@@ -58,7 +61,7 @@ export const ModalHeader = ({
58
61
  variant="tertiary"
59
62
  onClick={onClose}
60
63
  leftIcon={
61
- <span className="text-icon-primary-normal contents">
64
+ <span className="text-brand-text-action-primary-normal desktop:text-icon-primary-normal contents">
62
65
  <Icon name="close" size={24} />
63
66
  </span>
64
67
  }
@@ -4,7 +4,7 @@ import { PropsWithChildren } from "react";
4
4
  type ModalScrimProps = PropsWithChildren<{
5
5
  sampleWidth?: boolean;
6
6
  show?: boolean;
7
- size?: "small" | "medium" | "large" | "x-large";
7
+ size?: "small" | "medium" | "large" | "x-large" | "screen";
8
8
  onClick?: (e: React.MouseEvent<HTMLDivElement>) => void;
9
9
  ref?: React.Ref<HTMLDivElement>;
10
10
  id?: string;
@@ -25,9 +25,10 @@ export const ModalScrim = ({
25
25
  id={id}
26
26
  data-testid={testid}
27
27
  className={clsx(
28
- "overflow-y-auto h-screen transition-[visibility,opacity,background] ease-in-out duration-100 grid place-content-center desktop:p-4 group bg-neutral-600/50 fixed opacity-0",
28
+ "overflow-y-auto h-screen transition-[visibility,opacity,background] ease-in-out duration-100 grid place-content-center group bg-neutral-600/50 fixed opacity-0",
29
29
  !show && " pointer-events-none",
30
30
  size === "small" && "p-4",
31
+ size === "screen" ? "desktop:p-0" : "desktop:p-4",
31
32
  "inset-0 z-50",
32
33
  )}
33
34
  onMouseDown={onClick}
@@ -0,0 +1,22 @@
1
+ import { Button } from "../Button";
2
+ import { Icon } from "../Icon";
3
+
4
+ export function DownloadIcon({
5
+ onClick,
6
+ isDownloading,
7
+ testid,
8
+ }: {
9
+ onClick: () => void;
10
+ isDownloading?: boolean;
11
+ testid?: string;
12
+ }) {
13
+ return (
14
+ <Button
15
+ testid={testid}
16
+ iconOnly
17
+ variant="tertiary"
18
+ leftIcon={<Icon name={isDownloading ? "cached" : "download"} />}
19
+ onClick={onClick}
20
+ />
21
+ );
22
+ }
@@ -0,0 +1,90 @@
1
+ import { usePdf } from "@mikecousins/react-pdf";
2
+ import { useRef } from "react";
3
+ import { Spinner } from "../Spinner";
4
+ import { Stack } from "../Stack";
5
+ import { PdfPage } from "./PDFPage";
6
+ import clsx from "clsx";
7
+
8
+ export function PDFElement({
9
+ b64,
10
+ testid,
11
+ isMobile,
12
+ error,
13
+ }: {
14
+ b64: string;
15
+ testid?: string;
16
+ isMobile?: boolean;
17
+ error?: React.ReactNode;
18
+ }) {
19
+ const canvasRef = useRef<HTMLCanvasElement>(null);
20
+
21
+ const { pdfDocument } = usePdf({
22
+ file: `data:application/pdf;base64,${b64}`,
23
+ workerSrc: "/scripts/pdf.worker.min.mjs",
24
+ scale: isMobile ? 1 : 1.3,
25
+ canvasRef,
26
+ });
27
+
28
+ const pagesArr = new Array(pdfDocument?.numPages ?? 1).fill(null);
29
+
30
+ return (
31
+ <div
32
+ className="flex flex-col space-y-4"
33
+ style={
34
+ !isMobile
35
+ ? {
36
+ minHeight: 871,
37
+ minWidth: 654,
38
+ }
39
+ : undefined
40
+ }
41
+ >
42
+ {!!pdfDocument && !!b64 && !error ? (
43
+ <Stack sizing="layout-group">
44
+ {pagesArr.length > 1 ? (
45
+ pagesArr.map((_, i) => (
46
+ <div
47
+ key={`${testid}-pdf-page-${i + 1}`}
48
+ className={clsx(
49
+ "flex justify-center border-border-primary-normal",
50
+ isMobile ? "border-0" : "border",
51
+ )}
52
+ >
53
+ <PdfPage
54
+ testid={testid ? `${testid}-pdf_page_${i + 1}` : undefined}
55
+ file={`data:application/pdf;base64,${b64}`}
56
+ pageNumber={i + 1}
57
+ />
58
+ </div>
59
+ ))
60
+ ) : (
61
+ <div
62
+ className={clsx(
63
+ "flex justify-center border-border-primary-normal",
64
+ isMobile ? "border-0" : "border",
65
+ )}
66
+ >
67
+ <canvas
68
+ data-testid={testid ? `${testid}-pdf-content` : undefined}
69
+ ref={canvasRef}
70
+ style={{ width: "100%", height: "auto" }}
71
+ />
72
+ </div>
73
+ )}
74
+ </Stack>
75
+ ) : (
76
+ <Stack
77
+ justify="center"
78
+ items="center"
79
+ height="full"
80
+ flexGrow={1}
81
+ data-testid={
82
+ testid ? `${testid}-pdf-${error ? "error" : "loading"}` : undefined
83
+ }
84
+ >
85
+ {error ? error : <Spinner size="large" />}
86
+ </Stack>
87
+ )}
88
+ </div>
89
+ );
90
+ }
@@ -0,0 +1,68 @@
1
+ import { Button } from "../Button";
2
+ import { Heading3 } from "../Heading";
3
+ import { Icon } from "../Icon";
4
+ import { Paragraph } from "../Paragraph";
5
+ import { Stack } from "../Stack";
6
+
7
+ export function PdfNavigation({
8
+ currentIndex,
9
+ total,
10
+ onPrev,
11
+ onNext,
12
+ disablePrev,
13
+ disableNext,
14
+ extraActions,
15
+ testid,
16
+ fileName,
17
+ }: {
18
+ currentIndex: number;
19
+ total: number;
20
+ onPrev: () => void;
21
+ onNext: () => void;
22
+ disablePrev: boolean;
23
+ disableNext: boolean;
24
+ extraActions?: React.ReactNode;
25
+ testid?: string;
26
+ fileName?: string;
27
+ }) {
28
+ // TODO: Bring back horizontalMobile for 1st and 2nd stack after confirming navigation for multi-page/file
29
+ return (
30
+ <div className="w-full">
31
+ <Stack
32
+ horizontal
33
+ items="center"
34
+ justify="between"
35
+ sizing="layout-group"
36
+ testid={testid ? `${testid}-pdf-navigation` : undefined}
37
+ >
38
+ <Stack horizontal items="center">
39
+ <Button
40
+ iconOnly
41
+ variant="tertiary"
42
+ onClick={onPrev}
43
+ leftIcon={<Icon name="chevron_backward" />}
44
+ disabled={disablePrev}
45
+ testid={testid ? `${testid}-pdf-file-previous-button` : undefined}
46
+ />
47
+ <Heading3 className="text-text-primary-normal whitespace-nowrap">
48
+ {currentIndex + 1} / {total}
49
+ </Heading3>
50
+ <Button
51
+ iconOnly
52
+ variant="tertiary"
53
+ onClick={onNext}
54
+ rightIcon={<Icon name="chevron_forward" />}
55
+ disabled={disableNext}
56
+ testid={testid ? `${testid}-pdf-file-next-button` : undefined}
57
+ />
58
+ <Paragraph>
59
+ {fileName?.endsWith(".pdf") ? fileName : `${fileName}.pdf`}
60
+ </Paragraph>
61
+ </Stack>
62
+ {extraActions && (
63
+ <div className="flex items-center gap-2">{extraActions}</div>
64
+ )}
65
+ </Stack>
66
+ </div>
67
+ );
68
+ }
@@ -0,0 +1,34 @@
1
+ import { usePdf } from "@mikecousins/react-pdf";
2
+ import { useRef } from "react";
3
+
4
+ export function PdfPage({
5
+ file,
6
+ pageNumber,
7
+ testid,
8
+ isMobile,
9
+ }: {
10
+ file: string;
11
+ pageNumber: number;
12
+ testid?: string;
13
+ isMobile?: boolean;
14
+ }) {
15
+ const canvasRef = useRef<HTMLCanvasElement>(null);
16
+
17
+ const { pdfDocument } = usePdf({
18
+ file,
19
+ page: pageNumber,
20
+ canvasRef,
21
+ workerSrc: "/scripts/pdf.worker.min.mjs",
22
+ scale: isMobile ? 1 : 1.3,
23
+ });
24
+
25
+ if (!pdfDocument) return null;
26
+
27
+ return (
28
+ <canvas
29
+ ref={canvasRef}
30
+ data-testid={testid}
31
+ style={{ width: "100%", height: "auto" }}
32
+ />
33
+ );
34
+ }
@@ -0,0 +1,128 @@
1
+ "use client";
2
+
3
+ import { useCallback, useState } from "react";
4
+ import { Modal } from "../Modal";
5
+ import { PDFElement } from "./PDFElement";
6
+ import { DownloadIcon } from "./DownloadIcon";
7
+ import { PdfNavigation } from "./PDFNavigation";
8
+
9
+ type PDFViewerProps = {
10
+ isOpen: boolean;
11
+ onClose: () => void;
12
+ encodedPdfs: { fileName: string; base64: string }[];
13
+ customActions?: React.ReactNode;
14
+ testid?: string;
15
+ isMobile?: boolean;
16
+ title?: string;
17
+ withPagination?: boolean;
18
+ customFooter?: boolean;
19
+ error?: React.ReactNode;
20
+ };
21
+
22
+ export function PDFViewer(props: PDFViewerProps) {
23
+ const {
24
+ isOpen,
25
+ onClose,
26
+ encodedPdfs,
27
+ customActions,
28
+ testid,
29
+ isMobile,
30
+ title,
31
+ customFooter = false,
32
+ withPagination = true,
33
+ error,
34
+ } = props;
35
+ const [currentIndex, setCurrentIndex] = useState(0);
36
+ const [isDownloading, setIsDownloading] = useState(false);
37
+
38
+ const handleDownload = useCallback(() => {
39
+ setIsDownloading(true);
40
+ const link = document.createElement("a");
41
+ const currentPdf = encodedPdfs[currentIndex];
42
+ if (!currentPdf) {
43
+ setIsDownloading(false);
44
+ return;
45
+ }
46
+ link.href = `data:application/pdf;base64,${currentPdf.base64}`;
47
+ link.download = currentPdf.fileName.endsWith(".pdf")
48
+ ? currentPdf.fileName
49
+ : `${currentPdf.fileName}.pdf`;
50
+ document.body.appendChild(link);
51
+ link.click();
52
+ document.body.removeChild(link);
53
+ setIsDownloading(false);
54
+ }, [currentIndex, encodedPdfs]);
55
+
56
+ if (!encodedPdfs.length) return null;
57
+
58
+ function handleNextFile() {
59
+ if (currentIndex < encodedPdfs.length - 1) {
60
+ setCurrentIndex((prev) => prev + 1);
61
+ }
62
+ }
63
+
64
+ function handlePreviousFile() {
65
+ if (currentIndex > 0) {
66
+ setCurrentIndex((prev) => prev - 1);
67
+ }
68
+ }
69
+
70
+ function handleClose() {
71
+ setCurrentIndex(0);
72
+ setIsDownloading(false);
73
+ onClose();
74
+ }
75
+
76
+ return (
77
+ <Modal
78
+ testid={testid}
79
+ open={isOpen}
80
+ customFooter={customFooter}
81
+ onClose={handleClose}
82
+ noWrapper
83
+ showButtons={
84
+ isMobile && customFooter ? !!customActions : !!encodedPdfs.length
85
+ }
86
+ customActions={
87
+ !!encodedPdfs.length && !isMobile && withPagination ? (
88
+ <PdfNavigation
89
+ testid={testid}
90
+ currentIndex={currentIndex}
91
+ total={encodedPdfs.length}
92
+ onPrev={handlePreviousFile}
93
+ onNext={handleNextFile}
94
+ disablePrev={currentIndex === 0}
95
+ disableNext={currentIndex === encodedPdfs.length - 1}
96
+ extraActions={customActions}
97
+ fileName={encodedPdfs[currentIndex].fileName}
98
+ />
99
+ ) : (
100
+ customActions
101
+ )
102
+ }
103
+ fixedHeightScrolling
104
+ headerIconAlign="right"
105
+ headerIcon={
106
+ !isMobile ? (
107
+ <DownloadIcon
108
+ testid={testid ? `${testid}-download-icon` : undefined}
109
+ onClick={handleDownload}
110
+ isDownloading={isDownloading}
111
+ />
112
+ ) : undefined
113
+ }
114
+ title={
115
+ isMobile ? (title ?? encodedPdfs[currentIndex].fileName) : undefined
116
+ }
117
+ size={isMobile ? "screen" : "large"}
118
+ headerClassname="bg-brand-400 desktop:bg-background-grouped-primary-normal p-mobile-layout-padding text-brand-text-action-primary-normal"
119
+ >
120
+ <PDFElement
121
+ testid={testid}
122
+ b64={encodedPdfs[currentIndex].base64}
123
+ isMobile={isMobile}
124
+ error={error}
125
+ />
126
+ </Modal>
127
+ );
128
+ }
@@ -0,0 +1,54 @@
1
+ import { clsx } from "clsx";
2
+ import type { ProductPreviewImage } from "./index";
3
+
4
+ export type CarouselPaginationProps = {
5
+ images: ProductPreviewImage[];
6
+ currentIndex: number;
7
+ onSelect: (index: number) => void;
8
+ className?: string;
9
+ };
10
+ export function CarouselPagination({
11
+ images,
12
+ currentIndex,
13
+ onSelect,
14
+ className,
15
+ }: CarouselPaginationProps) {
16
+ if (!images?.length) return null;
17
+
18
+ return (
19
+ <div
20
+ className={clsx(
21
+ "flex items-center justify-center w-full px-4 md:hidden",
22
+ className,
23
+ )}
24
+ aria-label="Image navigation"
25
+ >
26
+ {/* Pagination squares in grid layout */}
27
+ <div
28
+ className="grid gap-2 place-items-center"
29
+ style={{
30
+ gridTemplateColumns: `repeat(${Math.min(images.length, 8)}, 1fr)`,
31
+ }}
32
+ role="tablist"
33
+ >
34
+ {images.map((_, index) => (
35
+ <button
36
+ key={index}
37
+ type="button"
38
+ onClick={() => onSelect(index)}
39
+ className={clsx(
40
+ "w-4 h-4 transition-all duration-200 focus:outline-none",
41
+ index === currentIndex
42
+ ? "ring-2 ring-brand-400"
43
+ : "ring ring-neutral-300",
44
+ )}
45
+ aria-label={`Go to image ${index + 1}`}
46
+ role="tab"
47
+ aria-selected={index === currentIndex}
48
+ tabIndex={index === currentIndex ? 0 : -1}
49
+ />
50
+ ))}
51
+ </div>
52
+ </div>
53
+ );
54
+ }