smartsell-ui-kit 0.0.2 → 0.0.4

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,634 @@
1
+ // src/components/Modal.tsx
2
+ import { useEffect, useMemo, useState } from "react";
3
+ import { Fragment, jsx, jsxs } from "react/jsx-runtime";
4
+ var defaultThemeColors = {
5
+ background: "#f8fafc",
6
+ surface: "#ffffff",
7
+ onSurface: "#0f172a",
8
+ onBackground: "#0f172a",
9
+ primary: "#0f766e",
10
+ onPrimary: "#ffffff",
11
+ secondary: "#1d4ed8",
12
+ onSecondary: "#ffffff",
13
+ negative: "#dc2626",
14
+ warning: "#d97706"
15
+ };
16
+ var withAlpha = (hex, alpha) => {
17
+ const normalized = hex.replace("#", "").trim();
18
+ const value = normalized.length === 3 ? normalized.split("").map((char) => char + char).join("") : normalized;
19
+ const red = Number.parseInt(value.slice(0, 2), 16);
20
+ const green = Number.parseInt(value.slice(2, 4), 16);
21
+ const blue = Number.parseInt(value.slice(4, 6), 16);
22
+ return `rgba(${red}, ${green}, ${blue}, ${alpha})`;
23
+ };
24
+ var isUiKitThemeTokens = (theme) => {
25
+ return Boolean(theme && typeof theme === "object" && "theme" in theme);
26
+ };
27
+ var resolveTheme = (theme) => {
28
+ var _a;
29
+ const colorOverrides = isUiKitThemeTokens(theme) ? theme.theme.colors : (_a = theme == null ? void 0 : theme.colors) != null ? _a : {};
30
+ const colors = {
31
+ ...defaultThemeColors,
32
+ ...colorOverrides
33
+ };
34
+ return {
35
+ colors,
36
+ borderSoft: isUiKitThemeTokens(theme) ? theme.borderSoft : withAlpha(colors.onSurface, 0.1),
37
+ borderStrong: isUiKitThemeTokens(theme) ? theme.borderStrong : withAlpha(colors.onSurface, 0.16),
38
+ surfaceMuted: isUiKitThemeTokens(theme) ? theme.surfaceMuted : withAlpha(colors.onSurface, 0.04),
39
+ surfaceRaised: isUiKitThemeTokens(theme) ? theme.surfaceRaised : withAlpha(colors.surface, 0.94),
40
+ overlay: isUiKitThemeTokens(theme) ? theme.overlay : withAlpha("#030712", 0.62),
41
+ mutedText: isUiKitThemeTokens(theme) ? theme.mutedText : withAlpha(colors.onSurface, 0.72)
42
+ };
43
+ };
44
+ var normalizeWidth = (maxWidth) => {
45
+ if (typeof maxWidth === "number") {
46
+ return `${maxWidth}px`;
47
+ }
48
+ return maxWidth != null ? maxWidth : "720px";
49
+ };
50
+ var isLikelyMobileViewport = () => {
51
+ if (typeof window === "undefined" || typeof navigator === "undefined") {
52
+ return false;
53
+ }
54
+ return /Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(
55
+ navigator.userAgent
56
+ );
57
+ };
58
+ var isLegacyModalProps = (props) => {
59
+ return "showModal" in props;
60
+ };
61
+ function LegacyActionButton({
62
+ label,
63
+ onClick,
64
+ disabled,
65
+ type = "contained",
66
+ theme,
67
+ style
68
+ }) {
69
+ var _a, _b;
70
+ const { colors } = theme;
71
+ const buttonStyle = {
72
+ width: "100%",
73
+ display: "flex",
74
+ alignItems: "center",
75
+ justifyContent: "center",
76
+ borderRadius: "0.4rem",
77
+ padding: type === "link" ? 0 : "1.3rem",
78
+ boxSizing: "border-box",
79
+ cursor: disabled ? "not-allowed" : "pointer",
80
+ fontWeight: 700,
81
+ fontSize: "1.3rem",
82
+ margin: 0,
83
+ textAlign: "center",
84
+ transition: "0.5s",
85
+ opacity: disabled ? 0.5 : 1,
86
+ filter: disabled ? "saturate(0)" : void 0
87
+ };
88
+ if (type === "text") {
89
+ buttonStyle.background = "none";
90
+ buttonStyle.border = "1px solid transparent";
91
+ buttonStyle.color = colors.primary;
92
+ buttonStyle.boxShadow = "none";
93
+ } else if (type === "link") {
94
+ buttonStyle.background = "none";
95
+ buttonStyle.border = "none";
96
+ buttonStyle.color = colors.primary;
97
+ buttonStyle.boxShadow = "none";
98
+ } else if (type === "outlined") {
99
+ buttonStyle.background = "none";
100
+ buttonStyle.border = `2px solid ${colors.primary}`;
101
+ buttonStyle.color = colors.primary;
102
+ buttonStyle.boxShadow = `0 14px 28px ${withAlpha(colors.onSurface, 0.08)}`;
103
+ } else {
104
+ buttonStyle.background = colors.primary;
105
+ buttonStyle.border = "1px solid transparent";
106
+ buttonStyle.color = colors.onPrimary;
107
+ buttonStyle.boxShadow = `0 14px 28px ${withAlpha(colors.primary, 0.22)}`;
108
+ }
109
+ return /* @__PURE__ */ jsx(
110
+ "div",
111
+ {
112
+ style: {
113
+ width: (_a = style == null ? void 0 : style.width) != null ? _a : (style == null ? void 0 : style.maxWidth) ? "100%" : "max-content",
114
+ display: "flex",
115
+ alignItems: "center",
116
+ justifyContent: "center",
117
+ margin: "0 5px",
118
+ marginBottom: (_b = style == null ? void 0 : style.marginBottom) != null ? _b : 0,
119
+ ...style
120
+ },
121
+ children: /* @__PURE__ */ jsx(
122
+ "button",
123
+ {
124
+ type: "button",
125
+ onClick: disabled ? void 0 : onClick,
126
+ disabled,
127
+ style: buttonStyle,
128
+ children: label
129
+ }
130
+ )
131
+ }
132
+ );
133
+ }
134
+ function StandardModal({
135
+ isOpen,
136
+ onClose,
137
+ children,
138
+ footer,
139
+ title,
140
+ description,
141
+ headerContent,
142
+ maxWidth,
143
+ closeOnBackdrop = true,
144
+ closeOnEscape = true,
145
+ hideCloseButton = false,
146
+ hideExpandButton = false,
147
+ maximized,
148
+ contentStyle,
149
+ theme
150
+ }) {
151
+ const modalTheme = useMemo(() => resolveTheme(theme), [theme]);
152
+ const [isMaximized, setIsMaximized] = useState(maximized != null ? maximized : false);
153
+ const titleId = useMemo(
154
+ () => `smartsell-modal-${String(title != null ? title : "dialog").toLowerCase().replace(/[^a-z0-9]+/g, "-")}`,
155
+ [title]
156
+ );
157
+ useEffect(() => {
158
+ if (typeof maximized === "boolean") {
159
+ setIsMaximized(maximized);
160
+ return;
161
+ }
162
+ if (isLikelyMobileViewport()) {
163
+ setIsMaximized(true);
164
+ }
165
+ }, [maximized]);
166
+ useEffect(() => {
167
+ if (!isOpen || typeof window === "undefined" || typeof document === "undefined") {
168
+ return;
169
+ }
170
+ const previousOverflow = document.body.style.overflow;
171
+ document.body.style.overflow = "hidden";
172
+ const handleKeyDown = (event) => {
173
+ if (closeOnEscape && event.key === "Escape") {
174
+ onClose();
175
+ }
176
+ };
177
+ window.addEventListener("keydown", handleKeyDown);
178
+ return () => {
179
+ document.body.style.overflow = previousOverflow;
180
+ window.removeEventListener("keydown", handleKeyDown);
181
+ };
182
+ }, [closeOnEscape, isOpen, onClose]);
183
+ if (!isOpen) {
184
+ return null;
185
+ }
186
+ const header = headerContent != null ? headerContent : /* @__PURE__ */ jsxs("div", { style: { display: "flex", flexDirection: "column", gap: 8 }, children: [
187
+ title ? /* @__PURE__ */ jsx(
188
+ "h2",
189
+ {
190
+ id: titleId,
191
+ style: {
192
+ margin: 0,
193
+ color: modalTheme.colors.onSurface,
194
+ fontSize: 30,
195
+ fontWeight: 800,
196
+ lineHeight: 1.1
197
+ },
198
+ children: title
199
+ }
200
+ ) : null,
201
+ description ? /* @__PURE__ */ jsx(
202
+ "p",
203
+ {
204
+ style: {
205
+ margin: 0,
206
+ color: modalTheme.mutedText,
207
+ fontSize: 15,
208
+ lineHeight: 1.6
209
+ },
210
+ children: description
211
+ }
212
+ ) : null
213
+ ] });
214
+ return /* @__PURE__ */ jsx(
215
+ "div",
216
+ {
217
+ "aria-label": headerContent ? typeof title === "string" ? title : "Dialog" : void 0,
218
+ "aria-labelledby": headerContent ? void 0 : title ? titleId : void 0,
219
+ "aria-modal": "true",
220
+ role: "dialog",
221
+ onMouseDown: (event) => {
222
+ if (closeOnBackdrop && event.target === event.currentTarget) {
223
+ onClose();
224
+ }
225
+ },
226
+ style: {
227
+ position: "fixed",
228
+ inset: 0,
229
+ zIndex: 70,
230
+ display: "flex",
231
+ alignItems: "center",
232
+ justifyContent: "center",
233
+ padding: isMaximized ? 0 : 16,
234
+ backgroundColor: modalTheme.overlay,
235
+ backdropFilter: "blur(8px)"
236
+ },
237
+ children: /* @__PURE__ */ jsxs(
238
+ "div",
239
+ {
240
+ style: {
241
+ position: "relative",
242
+ display: "flex",
243
+ flexDirection: "column",
244
+ width: isMaximized ? "100vw" : "min(100%, 100%)",
245
+ maxWidth: isMaximized ? "100vw" : normalizeWidth(maxWidth),
246
+ height: isMaximized ? "100vh" : void 0,
247
+ maxHeight: isMaximized ? "100vh" : "calc(100vh - 32px)",
248
+ borderRadius: isMaximized ? 0 : 30,
249
+ border: `1px solid ${modalTheme.borderSoft}`,
250
+ background: `linear-gradient(180deg, ${modalTheme.surfaceRaised}, ${modalTheme.colors.surface})`,
251
+ boxShadow: `0 34px 80px ${withAlpha("#020617", 0.28)}`,
252
+ color: modalTheme.colors.onSurface,
253
+ overflow: "hidden",
254
+ ...contentStyle
255
+ },
256
+ children: [
257
+ /* @__PURE__ */ jsxs(
258
+ "div",
259
+ {
260
+ style: {
261
+ display: "flex",
262
+ alignItems: "flex-start",
263
+ justifyContent: "space-between",
264
+ gap: 16,
265
+ padding: "24px 24px 16px",
266
+ borderBottom: `1px solid ${withAlpha(modalTheme.colors.onSurface, 0.06)}`
267
+ },
268
+ children: [
269
+ /* @__PURE__ */ jsx("div", { style: { flex: 1 }, children: header }),
270
+ /* @__PURE__ */ jsxs("div", { style: { display: "flex", alignItems: "center", gap: 10 }, children: [
271
+ !hideExpandButton ? /* @__PURE__ */ jsx(
272
+ "button",
273
+ {
274
+ "aria-label": isMaximized ? "Minimize modal" : "Maximize modal",
275
+ title: isMaximized ? "Minimize" : "Maximize",
276
+ type: "button",
277
+ onClick: () => setIsMaximized((current) => !current),
278
+ style: {
279
+ height: 42,
280
+ width: 42,
281
+ borderRadius: 999,
282
+ border: `1px solid ${modalTheme.borderSoft}`,
283
+ backgroundColor: "transparent",
284
+ color: modalTheme.colors.onSurface,
285
+ cursor: "pointer",
286
+ fontSize: 18,
287
+ fontWeight: 700
288
+ },
289
+ children: isMaximized ? "\u25A2" : "\u25A1"
290
+ }
291
+ ) : null,
292
+ !hideCloseButton ? /* @__PURE__ */ jsx(
293
+ "button",
294
+ {
295
+ "aria-label": "Close modal",
296
+ type: "button",
297
+ onClick: onClose,
298
+ style: {
299
+ height: 42,
300
+ width: 42,
301
+ borderRadius: 999,
302
+ border: `1px solid ${modalTheme.borderSoft}`,
303
+ backgroundColor: "transparent",
304
+ color: modalTheme.colors.onSurface,
305
+ cursor: "pointer",
306
+ fontSize: 20,
307
+ fontWeight: 700
308
+ },
309
+ children: "\xD7"
310
+ }
311
+ ) : null
312
+ ] })
313
+ ]
314
+ }
315
+ ),
316
+ /* @__PURE__ */ jsx(
317
+ "div",
318
+ {
319
+ style: {
320
+ flex: 1,
321
+ minHeight: 0,
322
+ overflow: "auto",
323
+ padding: 24
324
+ },
325
+ children
326
+ }
327
+ ),
328
+ footer ? /* @__PURE__ */ jsx(
329
+ "div",
330
+ {
331
+ style: {
332
+ display: "flex",
333
+ justifyContent: "flex-end",
334
+ gap: 12,
335
+ padding: "16px 24px 24px",
336
+ borderTop: `1px solid ${withAlpha(modalTheme.colors.onSurface, 0.06)}`
337
+ },
338
+ children: footer
339
+ }
340
+ ) : null
341
+ ]
342
+ }
343
+ )
344
+ }
345
+ );
346
+ }
347
+ function LegacyModal({
348
+ title,
349
+ showModal,
350
+ closeModal,
351
+ children,
352
+ action,
353
+ actionNameBtn,
354
+ hideActionButton = false,
355
+ cancelButton,
356
+ disableActionBtn = false,
357
+ isLoading = false,
358
+ onlyCloseButton = false,
359
+ hideCloseIcon = false,
360
+ style,
361
+ bodyStyle,
362
+ additionalActions,
363
+ maximized,
364
+ theme
365
+ }) {
366
+ const modalTheme = useMemo(() => resolveTheme(theme), [theme]);
367
+ const [isMaximized, setIsMaximized] = useState(maximized != null ? maximized : false);
368
+ const resolvedAction = action != null ? action : (() => null);
369
+ useEffect(() => {
370
+ if (maximized === void 0 && isLikelyMobileViewport()) {
371
+ setIsMaximized(true);
372
+ }
373
+ }, [maximized]);
374
+ if (!showModal) {
375
+ return null;
376
+ }
377
+ const actionButtons = () => {
378
+ return /* @__PURE__ */ jsxs(Fragment, { children: [
379
+ additionalActions == null ? void 0 : additionalActions.map((item) => {
380
+ var _a, _b, _c;
381
+ return /* @__PURE__ */ jsx(
382
+ LegacyActionButton,
383
+ {
384
+ label: item.name,
385
+ onClick: item.onClick,
386
+ type: (_a = item.type) != null ? _a : "contained",
387
+ style: (_b = item.style) != null ? _b : { maxWidth: "150px" },
388
+ theme: modalTheme,
389
+ disabled: (_c = item.disable) != null ? _c : disableActionBtn || isLoading
390
+ },
391
+ item.name
392
+ );
393
+ }),
394
+ !hideActionButton ? /* @__PURE__ */ jsx(
395
+ LegacyActionButton,
396
+ {
397
+ label: actionNameBtn != null ? actionNameBtn : "Save",
398
+ onClick: resolvedAction,
399
+ type: "contained",
400
+ style: { maxWidth: "100px" },
401
+ theme: modalTheme,
402
+ disabled: disableActionBtn || isLoading
403
+ }
404
+ ) : null
405
+ ] });
406
+ };
407
+ return /* @__PURE__ */ jsx(
408
+ "div",
409
+ {
410
+ "aria-labelledby": "modal-title",
411
+ "aria-modal": "true",
412
+ role: "dialog",
413
+ style: {
414
+ background: `${modalTheme.colors.background}90`,
415
+ backdropFilter: "blur(2px)",
416
+ width: "100%",
417
+ height: "100%",
418
+ position: "fixed",
419
+ zIndex: 999,
420
+ top: 0,
421
+ left: 0,
422
+ display: "flex",
423
+ justifyContent: "center",
424
+ alignItems: "center",
425
+ boxSizing: "border-box"
426
+ },
427
+ children: /* @__PURE__ */ jsxs(
428
+ "div",
429
+ {
430
+ id: isLoading ? "ldg1" : void 0,
431
+ style: {
432
+ position: isMaximized ? "fixed" : "relative",
433
+ left: isMaximized ? 0 : void 0,
434
+ top: isMaximized ? 0 : void 0,
435
+ background: modalTheme.colors.background,
436
+ display: "flex",
437
+ alignItems: "center",
438
+ flexDirection: "column",
439
+ borderRadius: "0.8rem",
440
+ boxSizing: "border-box",
441
+ maxHeight: isMaximized ? "100vh" : "80vh",
442
+ maxWidth: isMaximized ? "100vw" : "80vw",
443
+ minWidth: isMaximized ? "100vw" : 300,
444
+ minHeight: isMaximized ? "100vh" : void 0,
445
+ width: isMaximized ? "100vw" : void 0,
446
+ boxShadow: "0 24px 64px rgba(15, 23, 42, 0.18)",
447
+ ...isLoading ? { opacity: 0.5 } : null,
448
+ ...style
449
+ },
450
+ children: [
451
+ /* @__PURE__ */ jsxs(
452
+ "div",
453
+ {
454
+ style: {
455
+ background: modalTheme.colors.surface,
456
+ color: modalTheme.colors.onSurface,
457
+ position: "absolute",
458
+ top: 0,
459
+ zIndex: 500,
460
+ display: "flex",
461
+ justifyContent: "space-between",
462
+ alignItems: "center",
463
+ width: "100%",
464
+ height: "4rem",
465
+ padding: "1rem",
466
+ boxSizing: "border-box",
467
+ borderTopLeftRadius: "0.8rem",
468
+ borderTopRightRadius: "0.8rem",
469
+ borderBottom: `1px solid ${withAlpha(modalTheme.colors.onBackground, 0.1)}`
470
+ },
471
+ children: [
472
+ /* @__PURE__ */ jsx("div", { id: "modal-title", style: { fontWeight: "bold", fontSize: "1.3rem" }, children: title }),
473
+ /* @__PURE__ */ jsxs(
474
+ "div",
475
+ {
476
+ id: "modal-header-icons",
477
+ style: { display: "flex", alignItems: "center", gap: "0.5rem" },
478
+ children: [
479
+ /* @__PURE__ */ jsx(
480
+ "button",
481
+ {
482
+ "aria-label": isMaximized ? "Minimize modal" : "Maximize modal",
483
+ title: isMaximized ? "Minimize" : "Maximize",
484
+ type: "button",
485
+ onClick: () => setIsMaximized((current) => !current),
486
+ style: {
487
+ border: "none",
488
+ background: "transparent",
489
+ color: modalTheme.colors.onSurface,
490
+ cursor: "pointer",
491
+ fontSize: 18,
492
+ lineHeight: 1,
493
+ padding: 0
494
+ },
495
+ children: isMaximized ? "\u25A2" : "\u25A1"
496
+ }
497
+ ),
498
+ !hideCloseIcon ? /* @__PURE__ */ jsx(
499
+ "button",
500
+ {
501
+ "aria-label": "Close modal",
502
+ title: "Close",
503
+ type: "button",
504
+ onClick: isLoading ? void 0 : closeModal,
505
+ style: {
506
+ border: "none",
507
+ background: "transparent",
508
+ color: modalTheme.colors.onSurface,
509
+ cursor: isLoading ? "not-allowed" : "pointer",
510
+ fontSize: 22,
511
+ lineHeight: 1,
512
+ padding: 0,
513
+ opacity: isLoading ? 0.5 : 1
514
+ },
515
+ children: "\xD7"
516
+ }
517
+ ) : null
518
+ ]
519
+ }
520
+ )
521
+ ]
522
+ }
523
+ ),
524
+ /* @__PURE__ */ jsx(
525
+ "div",
526
+ {
527
+ style: {
528
+ scrollbarWidth: "thin",
529
+ width: "100%",
530
+ maxWidth: "100%",
531
+ paddingTop: "4rem",
532
+ paddingBottom: "4rem",
533
+ overflow: "auto",
534
+ ...bodyStyle
535
+ },
536
+ children
537
+ }
538
+ ),
539
+ onlyCloseButton ? /* @__PURE__ */ jsx(
540
+ "div",
541
+ {
542
+ style: {
543
+ background: modalTheme.colors.surface,
544
+ color: modalTheme.colors.onSurface,
545
+ position: "absolute",
546
+ bottom: 0,
547
+ width: "100%",
548
+ height: "4rem",
549
+ padding: "0.5rem",
550
+ borderTop: `1px solid ${withAlpha(modalTheme.colors.onBackground, 0.1)}`,
551
+ boxSizing: "border-box",
552
+ borderBottomLeftRadius: "0.8rem",
553
+ borderBottomRightRadius: "0.8rem"
554
+ },
555
+ children: /* @__PURE__ */ jsx(
556
+ "div",
557
+ {
558
+ className: "buttons-footer",
559
+ style: { flex: 1, display: "flex", justifyContent: "end" },
560
+ children: /* @__PURE__ */ jsx(
561
+ LegacyActionButton,
562
+ {
563
+ label: "Close",
564
+ onClick: closeModal,
565
+ theme: modalTheme,
566
+ disabled: isLoading
567
+ }
568
+ )
569
+ }
570
+ )
571
+ }
572
+ ) : /* @__PURE__ */ jsx(
573
+ "div",
574
+ {
575
+ style: {
576
+ background: modalTheme.colors.surface,
577
+ color: modalTheme.colors.onSurface,
578
+ position: "absolute",
579
+ bottom: 0,
580
+ width: "100%",
581
+ height: "4rem",
582
+ padding: "0.5rem",
583
+ borderTop: `1px solid ${withAlpha(modalTheme.colors.onBackground, 0.1)}`,
584
+ boxSizing: "border-box",
585
+ borderBottomLeftRadius: "0.8rem",
586
+ borderBottomRightRadius: "0.8rem"
587
+ },
588
+ children: cancelButton ? /* @__PURE__ */ jsxs(
589
+ "div",
590
+ {
591
+ className: "buttons-footer",
592
+ style: { flex: 1, display: "flex", justifyContent: "end" },
593
+ children: [
594
+ /* @__PURE__ */ jsx(
595
+ LegacyActionButton,
596
+ {
597
+ label: "Cancel",
598
+ onClick: cancelButton,
599
+ type: "text",
600
+ style: { maxWidth: "100px" },
601
+ theme: modalTheme,
602
+ disabled: false
603
+ }
604
+ ),
605
+ actionButtons()
606
+ ]
607
+ }
608
+ ) : /* @__PURE__ */ jsx(
609
+ "div",
610
+ {
611
+ className: "buttons-footer",
612
+ style: { flex: 1, display: "flex", justifyContent: "end" },
613
+ children: actionButtons()
614
+ }
615
+ )
616
+ }
617
+ )
618
+ ]
619
+ }
620
+ )
621
+ }
622
+ );
623
+ }
624
+ function Modal(props) {
625
+ if (isLegacyModalProps(props)) {
626
+ return /* @__PURE__ */ jsx(LegacyModal, { ...props });
627
+ }
628
+ return /* @__PURE__ */ jsx(StandardModal, { ...props });
629
+ }
630
+
631
+ export {
632
+ Modal
633
+ };
634
+ //# sourceMappingURL=chunk-BI3QBKFG.js.map