@redotech/redo-hydrogen 1.3.1 → 1.4.1

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.
@@ -0,0 +1,432 @@
1
+ import { ReactNode, useState, useEffect } from "react";
2
+ import { createPortal } from "react-dom";
3
+ import InfoIcon from "./icons/info.svg";
4
+ import ShieldIcon from "./icons/shield-tick.svg";
5
+ import { useRedoCoverageClient } from "../providers/redo-coverage-client";
6
+ import FeaturedPackageCheckIcon from "./icons/featured-package-check.svg";
7
+ import FeaturedLaptopIcon from "./icons/featured-laptop-2.svg";
8
+ import FeaturedRefreshIcon from "./icons/featured-refresh-cw-3.svg";
9
+ import RedoLogo from "./icons/logo.svg";
10
+ import CloseIcon from "./icons/modal-close-button.svg"
11
+
12
+
13
+ interface RedoInfoModalProps {
14
+ showInfoIcon?: boolean;
15
+ onInfoClick?: () => void;
16
+ infoCardImageUrl?: string;
17
+ infoModalLogoUrl?: string;
18
+ infoModalImageUrl?: string;
19
+ infoModalContent?: ReactNode;
20
+ }
21
+
22
+ function useInjectStyle(styleContent: string) {
23
+ useEffect(() => {
24
+ const styleTag = document.createElement('style');
25
+ styleTag.textContent = styleContent;
26
+ document.head.appendChild(styleTag);
27
+ return () => {
28
+ document.head.removeChild(styleTag);
29
+ };
30
+ }, [styleContent]);
31
+ }
32
+
33
+ const Modal = ({ open, onClose, infoModalLogoUrl, infoModalImageUrl, modalContent }:
34
+ {
35
+ open: boolean;
36
+ onClose: () => void;
37
+ infoModalLogoUrl?: string;
38
+ infoModalImageUrl?: string;
39
+ modalContent?: ReactNode;
40
+ }) => {
41
+ useInjectStyle( `
42
+ ${fadeInKeyframes}
43
+ ${slideInKeyframes}
44
+
45
+ @media (max-width: 768px) {
46
+ .redo-info-modal__container {
47
+ flex-direction: column !important;
48
+ align-items: stretch !important;
49
+ overflow: auto !important;
50
+ width: 95% !important;
51
+ }
52
+
53
+ .redo-info-modal__sideImageContainer {
54
+ width: 100% !important;
55
+ min-width: unset !important;
56
+ max-height: 140px !important;
57
+ overflow: hidden !important;
58
+ }
59
+
60
+ .redo-info-modal__sideImageContainer img {
61
+ height: 140px !important;
62
+ max-height: 140px !important;
63
+ }
64
+
65
+ .redo-info-modal__contentContainer {
66
+ width: 100% !important;
67
+ box-sizing: border-box !important;
68
+ }
69
+ }
70
+ `);
71
+
72
+ if (!open) return <></>;
73
+
74
+ const fullModalContent = (
75
+ <div className="redo-info-modal__backgroundContainer" style={{
76
+ position: 'fixed',
77
+ top: 0,
78
+ left: 0,
79
+ right: 0,
80
+ bottom: 0,
81
+ backgroundColor: 'rgba(0, 0, 0, 0.5)',
82
+ display: 'flex',
83
+ alignItems: 'center',
84
+ justifyContent: 'center',
85
+ zIndex: 99999,
86
+ transform: 'translateZ(0)',
87
+ opacity: 1,
88
+ transition: 'opacity 0.2s ease-in-out',
89
+ animation: 'fadeIn 0.2s ease-in-out'
90
+ }} onClick={onClose}>
91
+ <div className="redo-info-modal__container" style={{
92
+ backgroundColor: 'white',
93
+ borderRadius: '8px',
94
+ width: '700px',
95
+ maxWidth: '900px',
96
+ position: 'absolute',
97
+ top: '50%',
98
+ left: '50%',
99
+ transform: 'translate(-50%, -50%)',
100
+ zIndex: 100000,
101
+ display: 'flex',
102
+ flexDirection: 'row',
103
+ alignItems: 'center',
104
+ justifyContent: 'left',
105
+ maxHeight: '95%',
106
+ boxShadow: '0 4px 8px 0 rgba(0, 0, 0, 0.2)',
107
+ opacity: 1,
108
+ transition: 'opacity 0.2s ease-in-out, transform 0.2s ease-in-out',
109
+ animation: 'slideIn 0.2s ease-in-out forwards',
110
+ minHeight: '100px',
111
+ }} onClick={e => e.stopPropagation()}>
112
+ <button
113
+ className="redo-info-modal__closeButton"
114
+ onClick={onClose}
115
+ style={{
116
+ position: 'absolute',
117
+ right: '16px',
118
+ top: '16px',
119
+ border: 'none',
120
+ background: 'none',
121
+ fontSize: '20px',
122
+ cursor: 'pointer',
123
+ padding: '4px',
124
+ zIndex: 1
125
+ }}
126
+ >
127
+ <CloseIcon />
128
+ </button>
129
+ {infoModalImageUrl ? (
130
+ <div className="redo-info-modal__sideImageContainer" style={{
131
+ minWidth: '200px',
132
+ width: '200px',
133
+ backgroundImage: `url(${infoModalImageUrl})`,
134
+ backgroundSize: 'cover',
135
+ backgroundPosition: 'center center',
136
+ borderTopLeftRadius: '8px',
137
+ borderBottomLeftRadius: '8px',
138
+ alignSelf: 'stretch',
139
+ }}>
140
+ </div>
141
+ ) : null}
142
+ {modalContent ? modalContent : (
143
+ <div className="redo-info-modal__contentContainer" style={{
144
+ padding: '24px',
145
+ fontFamily: 'Inter, "Helvetica Neue", Arial, sans-serif',
146
+ }}>
147
+ <div className="redo-info-modal__logoContainer" style={{
148
+ width: '100%',
149
+ height: '100%',
150
+ display: 'block',
151
+ marginBottom: '8px'
152
+ }}>
153
+ {infoModalLogoUrl ? (
154
+ <img src={infoModalLogoUrl} className="redo-info-modal__logo" style={{
155
+ width: 'auto',
156
+ height: '112px',
157
+ display: 'block',
158
+ }}/>
159
+ ) : <RedoLogo width="112px" height="112px" display="block"/>}
160
+ </div>
161
+ <p style={{
162
+ fontSize: '20px',
163
+ fontWeight: '600'
164
+ }} className="redo-info-modal__title">
165
+ Checkout with confidence
166
+ </p>
167
+
168
+ <p style={{
169
+ fontSize: '14px',
170
+ color: '#666',
171
+ marginBottom: '24px'
172
+ }} className="redo-info-modal__description">
173
+ Shop with confidence, knowing your purchases are protected every step of the way.
174
+ </p>
175
+
176
+ <div className="redo-info-modal__benefitsContainer" style={{ marginBottom: '24px' }}>
177
+ <div className="redo-info-modal__benefit" style={{ marginBottom: '16px' }}>
178
+ <div className="redo-info-modal__benefitIconContainer" style={{ display: 'flex', flexDirection: 'row', alignItems: 'flex-start', justifyContent: 'flex-start', gap: '10px' }}>
179
+ <div className="redo-info-modal__benefitIcon" style={{ display: 'flex', alignItems: 'center', justifyContent: 'center', width: '36px', height: '36px' }}>
180
+ <FeaturedRefreshIcon height="32" width="32" />
181
+ </div>
182
+ <div className="redo-info-modal__benefitTextContainer" style={{ display: 'flex', flexDirection: 'column', alignItems: 'flex-start' }}>
183
+ <p className="redo-info-modal__benefitText" style={{ fontSize: '16px', fontWeight: '600', marginBottom: '4px' }}>
184
+ Free returns & exchanges
185
+ </p>
186
+ <p className="redo-info-modal__benefitSmallText" style={{ fontSize: '12px', color: '#666' }}>
187
+ Return or exchange your items for free. If you're not completely satisfied, we've got you covered.
188
+ </p>
189
+ </div>
190
+ </div>
191
+ </div>
192
+
193
+ <div className="redo-info-modal__benefit" style={{ marginBottom: '16px' }}>
194
+ <div className="redo-info-modal__benefitIconContainer" style={{ display: 'flex', flexDirection: 'row', alignItems: 'flex-start', gap: '10px' }}>
195
+ <div className="redo-info-modal__benefitIcon" style={{ display: 'flex', alignItems: 'center', justifyContent: 'center', width: '36px', height: '36px' }}>
196
+ <FeaturedPackageCheckIcon height="32" width="32" />
197
+ </div>
198
+ <div className="redo-info-modal__benefitTextContainer" style={{ display: 'flex', flexDirection: 'column', alignItems: 'flex-start' }}>
199
+ <p className="redo-info-modal__benefitText" style={{
200
+ fontSize: '16px',
201
+ fontWeight: '600',
202
+ marginBottom: '4px'
203
+ }}>
204
+ Package protection
205
+ </p>
206
+ <p className="redo-info-modal__benefitSmallText" style={{
207
+ fontSize: '12px',
208
+ color: '#666'
209
+ }}>
210
+ Rest assured, if your package is lost, stolen, or damaged, we've got you covered.
211
+ </p>
212
+ </div>
213
+ </div>
214
+ </div>
215
+
216
+ <div className="redo-info-modal__benefit" style={{ marginBottom: '16px' }}>
217
+ <div className="redo-info-modal__benefitIconContainer" style={{ display: 'flex', flexDirection: 'row', alignItems: 'flex-start', gap: '10px' }}>
218
+ <div className="redo-info-modal__benefitIcon" style={{ display: 'flex', alignItems: 'center', justifyContent: 'center', width: '36px', height: '36px' }}>
219
+ <FeaturedLaptopIcon height="32" width="32" />
220
+ </div>
221
+ <div className="redo-info-modal__benefitTextContainer" style={{ display: 'flex', flexDirection: 'column', alignItems: 'flex-start' }}>
222
+ <p className="redo-info-modal__benefitText" style={{
223
+ fontSize: '16px',
224
+ fontWeight: '600',
225
+ marginBottom: '4px'
226
+ }}>
227
+ Easy return portal
228
+ </p>
229
+ <p className="redo-info-modal__benefitSmallText" style={{
230
+ fontSize: '12px',
231
+ color: '#666'
232
+ }}>
233
+ Skip all the back and forth, and submit your return in a few clicks.
234
+ </p>
235
+ </div>
236
+ </div>
237
+ </div>
238
+ </div>
239
+
240
+ <p className="redo-info-modal__footerText" style={{
241
+ fontSize: '12px',
242
+ color: '#666',
243
+ }}>
244
+ By purchasing Redo, you agree and have read the{' '}
245
+ <a
246
+ href="https://www.getredo.com/privacy-policy"
247
+ target="_blank"
248
+ rel="noopener noreferrer"
249
+ style={{ color: '#000', textDecoration: 'underline' }}
250
+ >Privacy Policy</a> and{' '}
251
+ <a
252
+ href="https://www.getredo.com/terms-conditions"
253
+ target="_blank"
254
+ rel="noopener noreferrer"
255
+ style={{ color: '#000', textDecoration: 'underline' }}
256
+ >Terms and Conditions</a>.
257
+ {' '}Redo is subject to Merchant's Return Policy.
258
+ </p>
259
+ </div>
260
+ )}
261
+ </div>
262
+ </div>
263
+ );
264
+
265
+ return createPortal(fullModalContent, document.body);
266
+ };
267
+
268
+ const RedoInfoCard = ({
269
+ showInfoIcon = true,
270
+ onInfoClick,
271
+ infoCardImageUrl,
272
+ infoModalLogoUrl,
273
+ infoModalImageUrl,
274
+ infoModalContent,
275
+ }: RedoInfoModalProps) => {
276
+ const { price, eligible } = useRedoCoverageClient();
277
+ const [isModalOpen, setIsModalOpen] = useState(false);
278
+
279
+ const handleInfoClick = () => {
280
+ if (onInfoClick) {
281
+ onInfoClick();
282
+ } else {
283
+ setIsModalOpen(true);
284
+ }
285
+ };
286
+
287
+ if (!eligible) {
288
+ return <></>;
289
+ }
290
+
291
+ return (
292
+ <>
293
+ <div className="redo-info-card__container" data-target="info-card-container" style={{
294
+ display: 'flex',
295
+ borderRadius: '4px',
296
+ padding: '12px',
297
+ alignItems: 'center',
298
+ gap: '12px',
299
+ marginTop: '8px'
300
+ }}>
301
+ <div className="redo-info-card__imageContainer" style={{
302
+ width: '44px',
303
+ height: '44px',
304
+ borderRadius: '100%',
305
+ display: 'flex',
306
+ justifyContent: 'center',
307
+ alignItems: 'center',
308
+ flexShrink: 0,
309
+ }}>
310
+ {infoCardImageUrl ? (
311
+ <img src={infoCardImageUrl} alt="Redo Info" className="redo-info-card__image" style={{
312
+ width: '100%',
313
+ height: '100%',
314
+ objectFit: 'contain'
315
+ }} />
316
+ ) : (
317
+ <ShieldIcon style={{
318
+ width: '100%',
319
+ height: '100%',
320
+ color: 'black',
321
+ display: 'block',
322
+ viewBox: '0 0 24 24',
323
+ }}/>
324
+ )}
325
+ </div>
326
+
327
+ <div className="redo-info-card__content" data-target="text-and-buttons-container" style={{
328
+ flex: 1
329
+ }}>
330
+ <div className="redo-info-card__textWrapper" style={{
331
+ display: 'flex',
332
+ flexDirection: 'column',
333
+ }}>
334
+ <span className="redo-info-card__title" data-target="toggle-label" style={{
335
+ color: '#000000',
336
+ fontSize: '14px',
337
+ fontWeight: 600,
338
+ display: 'flex',
339
+ alignItems: 'center',
340
+ gap: '5px',
341
+ verticalAlign: 'middle'
342
+ }}>
343
+ Checkout+
344
+ {showInfoIcon && (
345
+ <span className="redo-info-card__infoIconWrapper" data-target="toggle-info">
346
+ <button
347
+ className="redo-info-card__infoButton"
348
+ data-target="toggle-info-button"
349
+ onClick={handleInfoClick}
350
+ type="button"
351
+ style={{
352
+ border: 'none',
353
+ background: 'none',
354
+ padding: 0,
355
+ cursor: 'pointer',
356
+ color: '#969696',
357
+ display: 'flex',
358
+ alignItems: 'center',
359
+ justifyContent: 'center'
360
+ }}
361
+ >
362
+ <InfoIcon
363
+ style={{
364
+ width: '16px',
365
+ height: '16px',
366
+ verticalAlign: 'middle'
367
+ }}
368
+ className="redo-info-card__infoIcon"
369
+ />
370
+ </button>
371
+ </span>
372
+ )}
373
+ </span>
374
+ <p className="redo-info-card__subtext" data-target="toggle-subtext" style={{
375
+ color: '#000000',
376
+ fontSize: '12px',
377
+ lineHeight: '16px'
378
+ }}>
379
+ Free Returns + Package Protection
380
+ </p>
381
+ </div>
382
+ </div>
383
+
384
+ <div className="redo-info-card__priceContainer">
385
+ <p style={{
386
+ color: '#000000',
387
+ fontSize: '14px',
388
+ fontWeight: 400,
389
+ }} className="redo-info-card__price" data-target="price">${price}</p>
390
+ </div>
391
+ </div>
392
+
393
+ {!onInfoClick && (
394
+ <Modal
395
+ open={isModalOpen}
396
+ onClose={() => setIsModalOpen(false)}
397
+ infoModalLogoUrl={infoModalLogoUrl}
398
+ infoModalImageUrl={infoModalImageUrl}
399
+ modalContent={infoModalContent}
400
+ />
401
+ )}
402
+ </>
403
+ );
404
+ };
405
+
406
+ const fadeInKeyframes = `
407
+ @keyframes fadeIn {
408
+ from {
409
+ opacity: 0;
410
+ }
411
+ to {
412
+ opacity: 1;
413
+ }
414
+ }
415
+ `;
416
+
417
+ const slideInKeyframes = `
418
+ @keyframes slideIn {
419
+ from {
420
+ opacity: 0;
421
+ transform: translate(-50%, -48%);
422
+ }
423
+ to {
424
+ opacity: 1;
425
+ transform: translate(-50%, -50%);
426
+ }
427
+ }
428
+ `;
429
+
430
+
431
+
432
+ export { RedoInfoCard };
package/src/index.ts CHANGED
@@ -3,6 +3,7 @@ import { RedoCheckoutButtons } from "./components/redo-checkout-buttons";
3
3
  import { REDO_REQUIRED_HOSTNAMES } from "./utils/security";
4
4
  import { CartProductVariantFragment, CartAttributeKey, CartInfoToEnable, RedoContextValue, RedoCoverageClient, RedoError, RedoErrorType } from "./types";
5
5
  import { LoadState, Loader, useLoad } from './utils/react-utils'
6
+ import { RedoInfoCard } from "./components/redo-info-modal";
6
7
 
7
8
  export {
8
9
  RedoCheckoutButtons,
@@ -10,7 +11,8 @@ export {
10
11
  useRedoCoverageClient,
11
12
  useLoad,
12
13
  REDO_REQUIRED_HOSTNAMES,
13
- RedoErrorType
14
+ RedoErrorType,
15
+ RedoInfoCard
14
16
  };
15
17
 
16
18
  export type {
package/src/svg.d.ts CHANGED
@@ -1,4 +1,5 @@
1
- declare module '*.svg' {
2
- const content: string;
3
- export default content;
1
+ declare module "*.svg" {
2
+ import { JSX } from "react";
3
+ function component(props: any): JSX.Element;
4
+ export default component;
4
5
  }