@visns-studio/visns-components 5.12.6 → 5.12.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -24,13 +24,13 @@
24
24
  "dayjs": "^1.11.13",
25
25
  "fabric": "^6.7.0",
26
26
  "file-saver": "^2.0.5",
27
- "framer-motion": "^12.19.2",
27
+ "framer-motion": "^12.23.0",
28
28
  "html-react-parser": "^5.2.5",
29
29
  "lodash": "^4.17.21",
30
30
  "lodash.debounce": "^4.0.8",
31
- "lucide-react": "^0.518.0",
31
+ "lucide-react": "^0.525.0",
32
32
  "moment": "^2.30.1",
33
- "motion": "^12.19.2",
33
+ "motion": "^12.23.0",
34
34
  "numeral": "^2.0.6",
35
35
  "pluralize": "^8.0.0",
36
36
  "qrcode.react": "^4.2.0",
@@ -47,10 +47,10 @@
47
47
  "react-reveal": "^1.2.2",
48
48
  "react-router-dom": "^6.30.1",
49
49
  "react-select": "^5.10.1",
50
- "react-signature-pad-wrapper": "^4.1.0",
50
+ "react-signature-pad-wrapper": "^4.1.1",
51
51
  "react-slugify": "^4.0.1",
52
52
  "react-sortable-hoc": "^2.0.0",
53
- "react-to-print": "^3.1.0",
53
+ "react-to-print": "^3.1.1",
54
54
  "react-toastify": "^11.0.5",
55
55
  "react-toggle": "^4.1.3",
56
56
  "react-tooltip": "^5.29.1",
@@ -58,19 +58,19 @@
58
58
  "reactjs-popup": "^2.0.6",
59
59
  "style-loader": "^4.0.0",
60
60
  "swapy": "^1.0.5",
61
- "sweetalert2": "^11.22.1",
61
+ "sweetalert2": "^11.22.2",
62
62
  "tesseract.js": "^6.0.1",
63
63
  "truncate": "^3.0.0",
64
64
  "uuid": "^11.1.0",
65
65
  "validator": "^13.15.15",
66
66
  "vite": "^6.3.5",
67
67
  "yarn": "^1.22.22",
68
- "yet-another-react-lightbox": "^3.23.4"
68
+ "yet-another-react-lightbox": "^3.24.0"
69
69
  },
70
70
  "devDependencies": {
71
- "@babel/core": "^7.27.7",
72
- "@babel/plugin-transform-runtime": "^7.27.4",
73
- "@babel/preset-env": "^7.27.2",
71
+ "@babel/core": "^7.28.0",
72
+ "@babel/plugin-transform-runtime": "^7.28.0",
73
+ "@babel/preset-env": "^7.28.0",
74
74
  "@babel/preset-react": "^7.27.1",
75
75
  "babel-loader": "^10.0.0",
76
76
  "copy-webpack-plugin": "^13.0.0",
@@ -87,7 +87,7 @@
87
87
  "react-dom": "^17.0.0 || ^18.0.0"
88
88
  },
89
89
  "name": "@visns-studio/visns-components",
90
- "version": "5.12.6",
90
+ "version": "5.12.7",
91
91
  "description": "Various packages to assist in the development of our Custom Applications.",
92
92
  "main": "src/index.js",
93
93
  "files": [
@@ -37,6 +37,7 @@ import Tools from './sketch/tools';
37
37
  import VisnsAsyncSelect from './AsyncSelect';
38
38
  import VisnsAutocomplete from './Autocomplete';
39
39
  import VisnsDropZone from './DropZone';
40
+ import DatePickerPortal from './utils/DatePickerPortal';
40
41
 
41
42
  import 'react-toggle/style.css';
42
43
  import 'react-datepicker/dist/react-datepicker.css';
@@ -72,6 +73,7 @@ function Field({
72
73
  }) {
73
74
  const editorRef = useRef(null);
74
75
  const fileImageInputRef = useRef(null);
76
+ const containerRef = useRef(null);
75
77
  const [autoValue, setAutoValue] = useState('');
76
78
  const [colorPickerShow, setColorPickerShow] = useState(false);
77
79
  const [counter, setCounter] = useState(0);
@@ -2129,31 +2131,38 @@ function Field({
2129
2131
  case 'date':
2130
2132
  case 'datetime':
2131
2133
  return (
2132
- <DatePicker
2133
- dateFormat={
2134
- settings.format
2135
- ? settings.format
2136
- : settings.type === 'datetime'
2137
- ? 'dd-MM-yyyy hh:mm aa'
2138
- : 'dd-MM-yyyy'
2139
- }
2140
- isClearable
2141
- className={inputClass[settings.id]}
2142
- selected={inputValue || ''}
2143
- showMonthDropdown
2144
- showYearDropdown
2145
- scrollableYearDropdown
2146
- onChange={(date) => onChangeDate(date, settings.id)}
2147
- showTimeSelect={settings.type === 'datetime'}
2148
- showTimeSelectOnly={settings.type === 'time'}
2149
- timeIntervals={
2150
- settings.type === 'time' ? 15 : undefined
2151
- }
2152
- timeCaption={
2153
- settings.type === 'time' ? 'Time' : undefined
2154
- }
2155
- shouldCloseOnSelect={true}
2156
- />
2134
+ <DatePickerPortal
2135
+ containerRef={containerRef}
2136
+ centerScreen={settings.centerScreen}
2137
+ modalContext={settings.modalContext}
2138
+ usePortal={settings.usePortal}
2139
+ >
2140
+ <DatePicker
2141
+ dateFormat={
2142
+ settings.format
2143
+ ? settings.format
2144
+ : settings.type === 'datetime'
2145
+ ? 'dd-MM-yyyy hh:mm aa'
2146
+ : 'dd-MM-yyyy'
2147
+ }
2148
+ isClearable
2149
+ className={inputClass[settings.id]}
2150
+ selected={inputValue || ''}
2151
+ showMonthDropdown
2152
+ showYearDropdown
2153
+ scrollableYearDropdown
2154
+ onChange={(date) => onChangeDate(date, settings.id)}
2155
+ showTimeSelect={settings.type === 'datetime'}
2156
+ showTimeSelectOnly={settings.type === 'time'}
2157
+ timeIntervals={
2158
+ settings.type === 'time' ? 15 : undefined
2159
+ }
2160
+ timeCaption={
2161
+ settings.type === 'time' ? 'Time' : undefined
2162
+ }
2163
+ shouldCloseOnSelect={true}
2164
+ />
2165
+ </DatePickerPortal>
2157
2166
  );
2158
2167
  case 'plaintext':
2159
2168
  if (settings.description) {
@@ -2254,21 +2263,28 @@ function Field({
2254
2263
  );
2255
2264
  case 'time':
2256
2265
  return (
2257
- <DatePicker
2258
- dateFormat="hh:mm aa"
2259
- isClearable
2260
- className={inputClass[settings.id]}
2261
- selected={inputValue || ''}
2262
- showMonthDropdown
2263
- showYearDropdown
2264
- scrollableYearDropdown
2265
- onChange={(date) => onChangeDate(date, settings.id)}
2266
- showTimeSelect={true}
2267
- showTimeSelectOnly={true}
2268
- timeIntervals={15}
2269
- timeCaption={'Time'}
2270
- shouldCloseOnSelect={true}
2271
- />
2266
+ <DatePickerPortal
2267
+ containerRef={containerRef}
2268
+ centerScreen={settings.centerScreen}
2269
+ modalContext={settings.modalContext}
2270
+ usePortal={settings.usePortal}
2271
+ >
2272
+ <DatePicker
2273
+ dateFormat="hh:mm aa"
2274
+ isClearable
2275
+ className={inputClass[settings.id]}
2276
+ selected={inputValue || ''}
2277
+ showMonthDropdown
2278
+ showYearDropdown
2279
+ scrollableYearDropdown
2280
+ onChange={(date) => onChangeDate(date, settings.id)}
2281
+ showTimeSelect={true}
2282
+ showTimeSelectOnly={true}
2283
+ timeIntervals={15}
2284
+ timeCaption={'Time'}
2285
+ shouldCloseOnSelect={true}
2286
+ />
2287
+ </DatePickerPortal>
2272
2288
  );
2273
2289
  case 'toggle':
2274
2290
  return (
@@ -2522,7 +2538,7 @@ function Field({
2522
2538
  }, [counter]);
2523
2539
 
2524
2540
  return (
2525
- <div className={formItemClass} style={formItemStyle}>
2541
+ <div className={formItemClass} style={formItemStyle} ref={containerRef}>
2526
2542
  <label
2527
2543
  className={
2528
2544
  ['richeditor', 'textarea'].includes(settings.type)
@@ -451,15 +451,7 @@ select:not(:placeholder-shown) + .fi__span {
451
451
  width: 100% !important;
452
452
  }
453
453
 
454
- .react-datepicker-popper {
455
- z-index: 99999 !important;
456
- position: fixed !important;
457
- transform: none !important;
458
- top: 50% !important;
459
- left: 50% !important;
460
- margin-top: -150px !important;
461
- margin-left: -150px !important;
462
- }
454
+ // DatePicker positioning removed - handled by global.css
463
455
 
464
456
  .react-datepicker__close-icon:after {
465
457
  background-color: rgba(var(--primary-rgb), 0.45) !important;
@@ -828,15 +828,7 @@ select:not(:placeholder-shown) + .fi__span {
828
828
  width: 100% !important;
829
829
  }
830
830
 
831
- .react-datepicker-popper {
832
- z-index: 99999 !important;
833
- position: fixed !important;
834
- transform: none !important;
835
- top: 50% !important;
836
- left: 50% !important;
837
- margin-top: -150px !important;
838
- margin-left: -150px !important;
839
- }
831
+ // DatePicker positioning removed - handled by global.css
840
832
 
841
833
  .react-datepicker__close-icon:after {
842
834
  background-color: rgba(var(--primary-rgb), 0.45) !important;
@@ -196,7 +196,7 @@ input[type]:not([type='search']):not([type='url']):not([type='hidden']):not(
196
196
  .inovua-react-toolkit-text-input__input
197
197
  ):not(.inovua-react-toolkit-numeric-input__input):not(
198
198
  .variable-inserter-search-input
199
- ) {
199
+ ):not(.react-datepicker__month-select):not(.react-datepicker__year-select) {
200
200
  background: white;
201
201
  border-radius: var(--br);
202
202
  border: 1px solid rgba(var(--paragraph-color-rgb), 0.15);
@@ -339,29 +339,38 @@ input[type]:not([type='search']):not([type='url']):not([type='hidden']):not(
339
339
  z-index: 999 !important;
340
340
  }
341
341
 
342
- /* DatePicker z-index fix */
342
+ /* DatePicker proper z-index hierarchy */
343
343
  .react-datepicker-popper {
344
- z-index: 99999 !important;
344
+ z-index: 1060 !important; /* Above Bootstrap modals (1050) */
345
+ position: absolute !important; /* Use absolute positioning for proper context */
346
+ }
347
+
348
+ /* Center-screen positioning for modal contexts */
349
+ .react-datepicker-popper.center-screen {
345
350
  position: fixed !important;
346
- transform: none !important;
347
351
  top: 50% !important;
348
352
  left: 50% !important;
349
- margin-top: -150px !important;
350
- margin-left: -150px !important;
353
+ transform: translate(-50%, -50%) !important;
354
+ z-index: 1060 !important;
355
+ }
356
+
357
+ /* Portal mode for modal contexts */
358
+ .react-datepicker-popper.portal-mode {
359
+ position: fixed !important;
360
+ z-index: 1060 !important;
351
361
  }
352
362
 
353
363
  .react-datepicker {
354
- position: absolute !important;
364
+ position: relative !important;
355
365
  display: inline-block !important;
356
- z-index: 99999 !important;
366
+ z-index: 1 !important;
357
367
  background-color: white !important;
358
368
  border-radius: 0.3rem !important;
359
369
  box-shadow: 0 5px 15px rgba(0, 0, 0, 0.3) !important;
360
370
  }
361
371
 
362
372
  .react-datepicker-portal {
363
- position: fixed !important;
364
- z-index: 99999 !important;
373
+ z-index: 1060 !important;
365
374
  }
366
375
 
367
376
  /* Fix for specific modal header that's causing issues */
@@ -369,12 +378,9 @@ input[type]:not([type='search']):not([type='url']):not([type='hidden']):not(
369
378
  z-index: 1 !important;
370
379
  }
371
380
 
372
- /* Make sure popup content doesn't constrain the datepicker */
373
- .popup-content {
374
- overflow: visible !important;
375
- }
376
-
377
- .modal__content {
381
+ /* Conditional overflow visibility only for modal contexts */
382
+ .modal-with-datepicker .popup-content,
383
+ .modal-with-datepicker .modal__content {
378
384
  overflow: visible !important;
379
385
  }
380
386
 
@@ -384,26 +390,179 @@ input[type]:not([type='search']):not([type='url']):not([type='hidden']):not(
384
390
  border: 1px solid #aeaeae !important;
385
391
  border-radius: 0.3rem !important;
386
392
  box-shadow: 0 3px 10px rgba(0, 0, 0, 0.2) !important;
393
+ float: left !important;
394
+ }
395
+
396
+ .react-datepicker {
397
+ position: relative !important;
398
+ display: inline-block !important;
399
+ background-color: white !important;
400
+ border-radius: 0.3rem !important;
401
+ box-shadow: 0 5px 15px rgba(0, 0, 0, 0.3) !important;
402
+ min-width: 385px !important; /* Wider to accommodate month/year dropdowns */
403
+ width: auto !important;
404
+ }
405
+
406
+ /* Ensure time container appears on the right side */
407
+ .react-datepicker__time-container {
408
+ float: right !important;
409
+ border-left: 1px solid #aeaeae !important;
410
+ width: 85px !important;
411
+ display: block !important;
412
+ }
413
+
414
+ /* Fix navigation button positioning */
415
+ .react-datepicker__navigation {
416
+ position: absolute !important;
417
+ top: 2px !important;
418
+ z-index: 1 !important;
419
+ height: 32px !important;
420
+ width: 32px !important;
421
+ cursor: pointer !important;
422
+ }
423
+
424
+ .react-datepicker__navigation--previous {
425
+ left: 2px !important;
426
+ }
427
+
428
+ .react-datepicker__navigation--next {
429
+ right: 2px !important;
430
+ }
431
+
432
+ /* Fix next button positioning when time selector is present */
433
+ .react-datepicker__navigation--next--with-time:not(
434
+ .react-datepicker__navigation--next--with-today-button
435
+ ) {
436
+ right: 87px !important; /* Account for time container width + border */
437
+ }
438
+
439
+ /* Ensure proper clearfix for float layout */
440
+ .react-datepicker::after {
441
+ content: '' !important;
442
+ display: table !important;
443
+ clear: both !important;
444
+ }
445
+
446
+ /* Fix header layout to accommodate navigation buttons */
447
+ .react-datepicker__header {
448
+ text-align: center !important;
449
+ background-color: #f0f0f0 !important;
450
+ border-bottom: 1px solid #aeaeae !important;
451
+ border-top-left-radius: 0.3rem !important;
452
+ border-top-right-radius: 0.3rem !important;
453
+ padding: 8px 0 !important;
454
+ position: relative !important;
455
+ }
456
+
457
+ /* Ensure current month text doesn't interfere with navigation */
458
+ .react-datepicker__current-month,
459
+ .react-datepicker-time__header,
460
+ .react-datepicker-year-header {
461
+ margin-top: 0 !important;
462
+ color: #000 !important;
463
+ font-weight: bold !important;
464
+ font-size: 0.944rem !important;
465
+ padding: 0 40px !important; /* Add padding to avoid navigation button overlap */
387
466
  }
388
467
 
389
- /* Hide the arrow since we're centering the calendar */
468
+ /* Alternative approach: Force specific width for month container */
469
+ .react-datepicker__month-container {
470
+ width: calc(
471
+ 100% - 87px
472
+ ) !important; /* Full width minus time container and border */
473
+ box-sizing: border-box !important;
474
+ min-width: 295px !important; /* Ensure enough space for month/year dropdowns */
475
+ }
476
+
477
+ /* Ensure time container has proper sizing and positioning */
478
+ .react-datepicker__time-container {
479
+ height: auto !important;
480
+ min-height: 250px !important; /* Match typical calendar height */
481
+ }
482
+
483
+ /* Force container to maintain layout */
484
+ .react-datepicker {
485
+ overflow: hidden !important; /* Contain floated children */
486
+ }
487
+
488
+ /* Make month and year select dropdowns more compact */
489
+ .react-datepicker__month-dropdown-container,
490
+ .react-datepicker__year-dropdown-container {
491
+ display: inline-block !important;
492
+ margin: 0 5px !important;
493
+ }
494
+
495
+ /* Target the actual select elements with higher specificity */
496
+ .react-datepicker .react-datepicker__header .react-datepicker__month-select,
497
+ .react-datepicker .react-datepicker__header .react-datepicker__year-select {
498
+ width: auto !important;
499
+ min-width: 70px !important;
500
+ max-width: 90px !important;
501
+ padding: 4px 8px !important;
502
+ font-size: 0.9rem !important;
503
+ border: 1px solid #aeaeae !important;
504
+ border-radius: 4px !important;
505
+ background-color: white !important;
506
+ cursor: pointer !important;
507
+ text-align: center !important;
508
+ margin: 0 2px !important;
509
+ box-sizing: border-box !important;
510
+ }
511
+
512
+ .react-datepicker .react-datepicker__header .react-datepicker__year-select {
513
+ min-width: 60px !important;
514
+ max-width: 70px !important;
515
+ }
516
+
517
+ /* Also target read-view elements for dropdowns */
518
+ .react-datepicker__month-read-view,
519
+ .react-datepicker__year-read-view {
520
+ width: auto !important;
521
+ min-width: 70px !important;
522
+ max-width: 90px !important;
523
+ padding: 4px 8px !important;
524
+ font-size: 0.9rem !important;
525
+ border: 1px solid #aeaeae !important;
526
+ border-radius: 4px !important;
527
+ background-color: white !important;
528
+ cursor: pointer !important;
529
+ text-align: center !important;
530
+ }
531
+
532
+ .react-datepicker__year-read-view {
533
+ min-width: 60px !important;
534
+ max-width: 70px !important;
535
+ }
536
+
537
+ /* Ensure dropdowns fit in header */
538
+ .react-datepicker__header {
539
+ display: flex !important;
540
+ flex-direction: column !important;
541
+ align-items: center !important;
542
+ gap: 8px !important;
543
+ }
544
+
545
+ /* Container for month/year dropdowns */
546
+ .react-datepicker__current-month {
547
+ display: flex !important;
548
+ align-items: center !important;
549
+ justify-content: center !important;
550
+ gap: 8px !important;
551
+ padding: 0 40px !important;
552
+ }
553
+
554
+ /* Show arrow for proper positioning context */
390
555
  .react-datepicker__triangle {
391
- display: none !important;
556
+ display: block !important;
392
557
  }
393
558
 
394
- /* Ensure all containers allow the datepicker to be visible */
395
- .modal,
396
- .popup-content,
397
- .modal__content,
398
- .formItem,
399
- .fi__label,
400
- .dynamicform,
401
- .dynamic__left {
402
- overflow: visible !important;
559
+ /* Hide arrow only for center-screen mode */
560
+ .react-datepicker-popper.center-screen .react-datepicker__triangle {
561
+ display: none !important;
403
562
  }
404
563
 
405
- /* Add a backdrop to make the datepicker stand out */
406
- .react-datepicker-popper::before {
564
+ /* Backdrop only for center-screen mode */
565
+ .react-datepicker-popper.center-screen::before {
407
566
  content: '' !important;
408
567
  position: fixed !important;
409
568
  top: 0 !important;
@@ -466,8 +625,7 @@ form div:has(.react-toggle) input[type='checkbox'] {
466
625
  box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.1);
467
626
 
468
627
  /* Checkerboard pattern for transparency */
469
- background-image:
470
- linear-gradient(45deg, #f0f0f0 25%, transparent 25%),
628
+ background-image: linear-gradient(45deg, #f0f0f0 25%, transparent 25%),
471
629
  linear-gradient(-45deg, #f0f0f0 25%, transparent 25%),
472
630
  linear-gradient(45deg, transparent 75%, #f0f0f0 75%),
473
631
  linear-gradient(-45deg, transparent 75%, #f0f0f0 75%);
@@ -478,8 +636,7 @@ form div:has(.react-toggle) input[type='checkbox'] {
478
636
  .relation-color-preview:hover {
479
637
  border-color: #adb5bd;
480
638
  transform: scale(1.1);
481
- box-shadow:
482
- inset 0 1px 2px rgba(0, 0, 0, 0.1),
639
+ box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.1),
483
640
  0 2px 8px rgba(0, 0, 0, 0.15);
484
641
  }
485
642
 
@@ -535,8 +692,7 @@ form div:has(.react-toggle) input[type='checkbox'] {
535
692
  flex-shrink: 0;
536
693
 
537
694
  /* Checkerboard pattern for transparency */
538
- background-image:
539
- linear-gradient(45deg, #f0f0f0 25%, transparent 25%),
695
+ background-image: linear-gradient(45deg, #f0f0f0 25%, transparent 25%),
540
696
  linear-gradient(-45deg, #f0f0f0 25%, transparent 25%),
541
697
  linear-gradient(45deg, transparent 75%, #f0f0f0 75%),
542
698
  linear-gradient(-45deg, transparent 75%, #f0f0f0 75%);
@@ -545,18 +701,19 @@ form div:has(.react-toggle) input[type='checkbox'] {
545
701
  }
546
702
 
547
703
  /* When no color is selected */
548
- .cpicker__box[style=""] {
549
- background:
550
- linear-gradient(45deg,
551
- transparent 40%,
552
- #dc3545 42%,
553
- #dc3545 58%,
704
+ .cpicker__box[style=''] {
705
+ background: linear-gradient(
706
+ 45deg,
707
+ transparent 40%,
708
+ #dc3545 42%,
709
+ #dc3545 58%,
554
710
  transparent 60%
555
711
  ),
556
- linear-gradient(-45deg,
557
- transparent 40%,
558
- #dc3545 42%,
559
- #dc3545 58%,
712
+ linear-gradient(
713
+ -45deg,
714
+ transparent 40%,
715
+ #dc3545 42%,
716
+ #dc3545 58%,
560
717
  transparent 60%
561
718
  ),
562
719
  #ffffff;
@@ -600,7 +757,7 @@ form div:has(.react-toggle) input[type='checkbox'] {
600
757
  .cpicker-container {
601
758
  padding: 6px;
602
759
  }
603
-
760
+
604
761
  .cpicker-container .w-color-sketch {
605
762
  transform: scale(0.75) !important;
606
763
  }
@@ -610,7 +767,7 @@ form div:has(.react-toggle) input[type='checkbox'] {
610
767
  .cpicker-container {
611
768
  padding: 4px;
612
769
  }
613
-
770
+
614
771
  .cpicker-container .w-color-sketch {
615
772
  transform: scale(0.65) !important;
616
773
  }
@@ -0,0 +1,127 @@
1
+ import React, { useEffect, useState } from 'react';
2
+ import { createPortal } from 'react-dom';
3
+
4
+ /**
5
+ * DatePickerPortal - Smart portal component for DatePicker positioning
6
+ *
7
+ * Automatically detects if the DatePicker is inside a modal context
8
+ * and renders it appropriately to prevent z-index conflicts
9
+ */
10
+ const DatePickerPortal = ({
11
+ children,
12
+ usePortal = null, // null = auto-detect, true = force portal, false = no portal
13
+ modalContext = null, // null = auto-detect, true = force modal styling, false = normal
14
+ centerScreen = false, // true = force center-screen positioning
15
+ containerRef = null // reference to the input container for positioning
16
+ }) => {
17
+ const [shouldUsePortal, setShouldUsePortal] = useState(false);
18
+ const [isModalContext, setIsModalContext] = useState(false);
19
+ const [portalContainer, setPortalContainer] = useState(null);
20
+
21
+ useEffect(() => {
22
+ // Auto-detect modal context if not explicitly set
23
+ if (modalContext === null) {
24
+ const detectModalContext = () => {
25
+ if (containerRef && containerRef.current) {
26
+ // Check if the container is inside a modal
27
+ const modalSelectors = [
28
+ '.modal',
29
+ '.popup-content',
30
+ '.modal__content',
31
+ '.swal2-container',
32
+ '[role="dialog"]',
33
+ '[aria-modal="true"]'
34
+ ];
35
+
36
+ for (const selector of modalSelectors) {
37
+ if (containerRef.current.closest(selector)) {
38
+ setIsModalContext(true);
39
+ setShouldUsePortal(true);
40
+ return;
41
+ }
42
+ }
43
+ }
44
+ setIsModalContext(false);
45
+ setShouldUsePortal(false);
46
+ };
47
+
48
+ detectModalContext();
49
+ } else {
50
+ setIsModalContext(modalContext);
51
+ setShouldUsePortal(modalContext);
52
+ }
53
+ }, [modalContext, containerRef]);
54
+
55
+ useEffect(() => {
56
+ // Override auto-detection if explicitly set
57
+ if (usePortal !== null) {
58
+ setShouldUsePortal(usePortal);
59
+ }
60
+ }, [usePortal]);
61
+
62
+ useEffect(() => {
63
+ // Create portal container
64
+ if (shouldUsePortal) {
65
+ const container = document.createElement('div');
66
+ container.className = 'datepicker-portal-container';
67
+
68
+ // Add appropriate classes based on context
69
+ if (isModalContext || centerScreen) {
70
+ container.classList.add('modal-context');
71
+ }
72
+
73
+ document.body.appendChild(container);
74
+ setPortalContainer(container);
75
+
76
+ // Add modal-context class to body for CSS targeting
77
+ if (isModalContext) {
78
+ document.body.classList.add('modal-with-datepicker');
79
+ }
80
+
81
+ return () => {
82
+ document.body.removeChild(container);
83
+ document.body.classList.remove('modal-with-datepicker');
84
+ };
85
+ }
86
+ }, [shouldUsePortal, isModalContext, centerScreen]);
87
+
88
+ // Clone children and add appropriate classes
89
+ const enhancedChildren = React.Children.map(children, child => {
90
+ if (React.isValidElement(child)) {
91
+ const additionalProps = {};
92
+
93
+ // Add popperClassName for react-datepicker
94
+ if (child.props.popperClassName) {
95
+ additionalProps.popperClassName = child.props.popperClassName;
96
+ }
97
+
98
+ // Add classes based on context
99
+ const classes = [];
100
+ if (isModalContext || centerScreen) {
101
+ classes.push('center-screen');
102
+ }
103
+ if (shouldUsePortal) {
104
+ classes.push('portal-mode');
105
+ }
106
+
107
+ if (classes.length > 0) {
108
+ additionalProps.popperClassName = [
109
+ child.props.popperClassName,
110
+ ...classes
111
+ ].filter(Boolean).join(' ');
112
+ }
113
+
114
+ return React.cloneElement(child, additionalProps);
115
+ }
116
+ return child;
117
+ });
118
+
119
+ // Render with or without portal
120
+ if (shouldUsePortal && portalContainer) {
121
+ return createPortal(enhancedChildren, portalContainer);
122
+ }
123
+
124
+ return enhancedChildren;
125
+ };
126
+
127
+ export default DatePickerPortal;
package/src/index.js CHANGED
@@ -29,6 +29,7 @@ import DataGridSearch from './components/controls/DataGridSearch';
29
29
  import AutoRefreshControls from './components/controls/AutoRefreshControls';
30
30
  import AudioPlayer from './components/controls/AudioPlayer';
31
31
  import GalleryModal from './components/modals/GalleryModal';
32
+ import DatePickerPortal from './components/utils/DatePickerPortal';
32
33
  export * from './components/columns/ColumnRenderers.jsx';
33
34
 
34
35
  /** Auth Specific Components */
@@ -96,6 +97,7 @@ export {
96
97
  CustomFetch,
97
98
  DataGrid,
98
99
  DataGridSearch,
100
+ DatePickerPortal,
99
101
  Download,
100
102
  DropZone,
101
103
  Field,