@visns-studio/visns-components 5.14.11 → 5.14.13

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.
@@ -1,4 +1,4 @@
1
- import React from 'react';
1
+ import React, { useState, useEffect } from 'react';
2
2
  import Popup from 'reactjs-popup';
3
3
  import { X } from 'lucide-react';
4
4
  import formStyles from '../styles/Form.module.scss';
@@ -33,6 +33,40 @@ const StandardModal = ({
33
33
  showHeader = false,
34
34
  customStyles = {}
35
35
  }) => {
36
+ // Device detection state
37
+ const [windowWidth, setWindowWidth] = useState(typeof window !== 'undefined' ? window.innerWidth : 1024);
38
+
39
+ // Device detection utility
40
+ const getDeviceType = () => {
41
+ const hasTouch = typeof window !== 'undefined' && ('ontouchstart' in window || navigator.maxTouchPoints > 0);
42
+ const userAgent = typeof window !== 'undefined' ? navigator.userAgent : '';
43
+
44
+ // Mobile detection: width + user agent + touch
45
+ const isMobileUA = /Android|webOS|iPhone|iPod|BlackBerry|IEMobile|Opera Mini/i.test(userAgent);
46
+ const isMobileWidth = windowWidth <= 768;
47
+ const isMobile = isMobileUA || (isMobileWidth && hasTouch);
48
+
49
+ // Tablet detection: iPad or Android tablets or medium width with touch
50
+ const isTabletUA = /iPad|Android(?!.*Mobile)/i.test(userAgent);
51
+ const isTabletWidth = windowWidth > 768 && windowWidth <= 1024;
52
+ const isTablet = isTabletUA || (isTabletWidth && hasTouch && !isMobile);
53
+
54
+ return { isMobile, isTablet, isDesktop: !isMobile && !isTablet };
55
+ };
56
+
57
+ const { isMobile, isTablet } = getDeviceType();
58
+
59
+ // Handle window resize
60
+ useEffect(() => {
61
+ if (typeof window === 'undefined') return;
62
+
63
+ const handleResize = () => {
64
+ setWindowWidth(window.innerWidth);
65
+ };
66
+
67
+ window.addEventListener('resize', handleResize);
68
+ return () => window.removeEventListener('resize', handleResize);
69
+ }, []);
36
70
  // Apply vertical alignment class if needed
37
71
  const getModalClasses = () => {
38
72
  let classes = formStyles.modal;
@@ -42,36 +76,52 @@ const StandardModal = ({
42
76
  return classes;
43
77
  };
44
78
 
45
- // Get size-based styles for the popup content
79
+ // Get size-based styles for the popup content with device-aware positioning
46
80
  const getContentStyle = () => {
47
81
  const baseStyle = {
48
82
  ...customStyles.modal
49
83
  };
50
84
 
51
- // Apply size-based styles
85
+ // Device-specific positioning for virtual keyboard accommodation
86
+ let positioningStyle = {};
87
+ if (isMobile || isTablet) {
88
+ // Position modal higher on mobile/tablet to accommodate virtual keyboard
89
+ positioningStyle = {
90
+ top: '5%', // Position higher (5% from top)
91
+ left: '50%', // Center horizontally
92
+ transform: 'translateX(-50%)', // Only center horizontally, no vertical transform
93
+ maxHeight: '85vh', // Limit height to prevent overflow
94
+ overflowY: 'auto', // Allow scrolling within modal if needed
95
+ position: 'fixed' // Ensure fixed positioning for proper centering
96
+ };
97
+ }
98
+
99
+ // Apply size-based styles with device considerations
52
100
  switch (size) {
53
101
  case 'small':
54
102
  return {
55
103
  ...baseStyle,
56
- width: '400px',
57
- minWidth: '400px',
58
- maxWidth: '600px'
104
+ ...positioningStyle,
105
+ width: isMobile ? '95vw' : '400px',
106
+ minWidth: isMobile ? '300px' : '400px',
107
+ maxWidth: isMobile ? '95vw' : '600px'
59
108
  };
60
109
  case 'large':
61
110
  return {
62
111
  ...baseStyle,
112
+ ...positioningStyle,
63
113
  width: '95vw',
64
- minWidth: '800px',
65
- maxWidth: '1600px'
114
+ minWidth: isMobile ? '300px' : '800px',
115
+ maxWidth: isMobile ? '95vw' : '1600px'
66
116
  };
67
117
  case 'medium':
68
118
  default:
69
- // Use the same dimensions as modalWide class (original modal sizing)
70
119
  return {
71
120
  ...baseStyle,
72
- width: '90vw',
73
- minWidth: '600px',
74
- maxWidth: '1400px'
121
+ ...positioningStyle,
122
+ width: isMobile ? '95vw' : '90vw',
123
+ minWidth: isMobile ? '300px' : '600px',
124
+ maxWidth: isMobile ? '95vw' : '1400px'
75
125
  };
76
126
  }
77
127
  };
@@ -845,66 +845,146 @@ input[type='file']:hover {
845
845
  .file-upload-container {
846
846
  display: flex;
847
847
  flex-direction: column;
848
- gap: 0.5em;
848
+ gap: 1em;
849
849
  width: 100%;
850
+ border: 2px dashed var(--border-color, #ddd);
851
+ border-radius: var(--br, 8px);
852
+ padding: 1.5em;
853
+ background: var(--tertiary-color, #fafafa);
854
+ transition: all 0.3s ease;
855
+ position: relative;
850
856
  }
851
857
 
858
+ /* Removed hover effect on main container */
859
+
852
860
  .file-upload-container input[type='file'] {
853
- padding: 8px;
854
- border: 1px solid var(--border-color);
855
- border-radius: var(--br);
856
- width: auto;
857
- background: var(--tertiary-color);
861
+ position: absolute;
862
+ width: 0.1px;
863
+ height: 0.1px;
864
+ opacity: 0;
865
+ overflow: hidden;
866
+ z-index: -1;
867
+ }
868
+
869
+ .file-upload-button-wrapper {
870
+ display: flex;
871
+ flex-direction: column;
872
+ align-items: center;
873
+ gap: 0.5em;
874
+ padding: 1em;
875
+ border: 2px dashed transparent;
876
+ border-radius: var(--br, 6px);
877
+ cursor: pointer;
878
+ transition: all 0.3s ease;
879
+ }
880
+
881
+ /* Removed hover effect on button wrapper */
882
+
883
+ .file-upload-button {
884
+ display: inline-flex;
885
+ align-items: center;
886
+ gap: 0.5em;
887
+ padding: 0.75em 1.5em;
888
+ background: var(--primary-color, #007bff);
889
+ color: #ffffff;
890
+ border: none;
891
+ border-radius: var(--br, 6px);
892
+ font-weight: 500;
893
+ font-size: 0.9em;
894
+ cursor: pointer;
895
+ transition: all 0.3s ease;
896
+ box-shadow: 0 2px 4px rgba(var(--primary-color-rgb, 0, 123, 255), 0.2);
897
+ }
898
+
899
+ .file-upload-button:hover {
900
+ background: rgba(var(--primary-color-rgb, 0, 123, 255), 0.9);
901
+ color: #ffffff;
902
+ transform: translateY(-1px);
903
+ box-shadow: 0 4px 8px rgba(var(--primary-color-rgb, 0, 123, 255), 0.3);
904
+ }
905
+
906
+ .file-upload-help-text {
907
+ font-size: 0.85em;
908
+ color: var(--muted-text-color, #6c757d);
909
+ text-align: center;
910
+ margin-top: 0.5em;
858
911
  }
859
912
 
860
913
  .file-list {
861
914
  width: 100%;
862
- border: 1px solid var(--border-color);
863
- border-radius: var(--br);
864
- background: var(--tertiary-color);
865
- max-height: 200px;
915
+ background: transparent;
916
+ max-height: 220px;
866
917
  overflow-y: auto;
867
- padding: 0.5em;
918
+ padding: 0;
919
+ margin-top: 0.75em;
920
+ display: flex;
921
+ flex-direction: column;
922
+ gap: 0.3em;
868
923
  }
869
924
 
870
925
  .file-list li {
871
926
  display: flex;
872
927
  justify-content: space-between;
873
928
  align-items: center;
874
- padding: 0.75em 1em;
875
- border-bottom: 1px solid var(--border-color);
876
- font-size: 0.9rem;
877
- background: white;
878
- border-radius: 5px;
879
- transition: background 0.3s;
880
- margin-bottom: 5px;
929
+ padding: 0.5em 0.75em;
930
+ background: var(--item-color, #fff);
931
+ border: 1px solid var(--border-color, #e0e6ed);
932
+ border-radius: var(--br, 6px);
933
+ font-size: 0.8rem;
934
+ transition: all 0.2s ease;
935
+ min-height: 36px;
936
+ box-shadow: 0 1px 2px rgba(0, 0, 0, 0.04);
937
+ position: relative;
938
+ overflow: hidden;
881
939
  }
882
940
 
883
- .file-list li:last-child {
884
- border-bottom: none;
941
+ .file-list li:hover {
942
+ border-color: var(--primary-color, #007bff);
943
+ box-shadow: 0 2px 8px rgba(var(--primary-color-rgb, 0, 123, 255), 0.15);
944
+ transform: translateY(-1px);
885
945
  }
886
946
 
887
- .file-list li:hover {
888
- background: rgba(var(--paragraph-color-rgb), 0.1);
947
+ .file-list li:hover .file-icon {
948
+ background: rgba(var(--primary-color-rgb, 0, 123, 255), 0.15);
949
+ border-color: rgba(var(--primary-color-rgb, 0, 123, 255), 0.3);
950
+ transform: scale(1.05);
951
+ }
952
+
953
+ .file-list li:hover .file-image-preview {
954
+ transform: scale(1.05);
955
+ }
956
+
957
+ .file-list li:hover .file-name {
958
+ color: var(--primary-color, #007bff);
889
959
  }
890
960
 
891
961
  .trash-button {
892
- background: transparent;
893
- border: none;
962
+ background: rgba(var(--danger-color-rgb, 220, 53, 69), 0.08);
963
+ border: 1px solid rgba(var(--danger-color-rgb, 220, 53, 69), 0.2);
894
964
  cursor: pointer;
895
- color: var(--primary-color);
965
+ color: var(--danger-color, #dc3545);
896
966
  display: flex;
897
967
  align-items: center;
898
- transition: color 0.3s;
968
+ justify-content: center;
969
+ padding: 0.35em;
970
+ border-radius: 4px;
971
+ transition: all 0.2s ease;
972
+ min-width: 28px;
973
+ height: 28px;
974
+ flex-shrink: 0;
899
975
  }
900
976
 
901
977
  .trash-button:hover {
902
- color: var(--secondary-color);
978
+ background: var(--danger-color, #dc3545);
979
+ color: var(--secondary-color, #fff);
980
+ border-color: var(--danger-color, #dc3545);
981
+ transform: translateY(-1px);
982
+ box-shadow: 0 2px 4px rgba(var(--danger-color-rgb, 220, 53, 69), 0.3);
903
983
  }
904
984
 
905
985
  .trash-button svg {
906
- width: 1.2em;
907
- height: 1.2em;
986
+ width: 0.9em;
987
+ height: 0.9em;
908
988
  }
909
989
 
910
990
  /* Enhanced File Upload Styling */
@@ -1034,15 +1114,16 @@ input[type='file']:hover {
1034
1114
  .file-list-item-image {
1035
1115
  display: flex;
1036
1116
  align-items: center;
1037
- gap: 0.75em;
1038
- padding: 0.75em 1em;
1039
- border-bottom: 1px solid var(--border-color, #e9ecef);
1040
- font-size: 0.9rem;
1041
- background: var(--item-color, #fff);
1042
- border-radius: var(--br, 6px);
1043
- transition: all 0.3s ease;
1044
- margin-bottom: 0.5em;
1117
+ gap: 0.5em;
1118
+ padding: 0.5em 0.75em;
1119
+ border-bottom: 1px solid rgba(var(--border-color-rgb, 224, 230, 237), 0.3);
1120
+ font-size: 0.85rem;
1121
+ background: transparent;
1122
+ border-radius: 4px;
1123
+ transition: all 0.2s ease;
1124
+ margin-bottom: 0.25em;
1045
1125
  border: 1px solid transparent;
1126
+ min-height: 36px;
1046
1127
  }
1047
1128
 
1048
1129
  .file-list-item-image {
@@ -1063,23 +1144,37 @@ input[type='file']:hover {
1063
1144
  border-color: var(--success-color, #28a745);
1064
1145
  }
1065
1146
 
1147
+ /* Enhanced image file styling */
1148
+ .file-list-item-image .file-icon,
1149
+ .file-list-item-image .file-image-preview {
1150
+ background: rgba(var(--success-color-rgb, 40, 167, 69), 0.12) !important;
1151
+ border-color: rgba(var(--success-color-rgb, 40, 167, 69), 0.3) !important;
1152
+ }
1153
+
1066
1154
  .file-icon {
1067
- font-size: 1.5em;
1155
+ font-size: 1.1em;
1068
1156
  display: flex;
1069
1157
  align-items: center;
1070
1158
  justify-content: center;
1071
- width: 2em;
1072
- height: 2em;
1159
+ width: 1.8em;
1160
+ height: 1.8em;
1073
1161
  flex-shrink: 0;
1162
+ background: rgba(var(--primary-color-rgb, 0, 123, 255), 0.08);
1163
+ border-radius: 4px;
1164
+ border: 1px solid rgba(var(--primary-color-rgb, 0, 123, 255), 0.15);
1165
+ transition: all 0.2s ease;
1074
1166
  }
1075
1167
 
1076
1168
  .file-name {
1077
1169
  flex: 1;
1078
- color: var(--paragraph-color, #495057);
1170
+ color: var(--paragraph-color, #2c3e50);
1079
1171
  font-weight: 500;
1080
1172
  overflow: hidden;
1081
1173
  text-overflow: ellipsis;
1082
1174
  white-space: nowrap;
1175
+ margin-left: 0.75em;
1176
+ font-size: 0.85em;
1177
+ line-height: 1.3;
1083
1178
  }
1084
1179
 
1085
1180
  /* Image Preview Styling */
@@ -1123,21 +1218,22 @@ input[type='file']:hover {
1123
1218
  display: flex;
1124
1219
  align-items: center;
1125
1220
  justify-content: center;
1126
- width: 3em;
1127
- height: 3em;
1221
+ width: 1.8em;
1222
+ height: 1.8em;
1128
1223
  flex-shrink: 0;
1129
- background: rgba(var(--item-color-rgb, 255, 255, 255), 0.9);
1130
- border: 1px solid var(--border-color, #e9ecef);
1131
- border-radius: var(--br, 4px);
1224
+ background: rgba(var(--success-color-rgb, 40, 167, 69), 0.08);
1225
+ border: 1px solid rgba(var(--success-color-rgb, 40, 167, 69), 0.2);
1226
+ border-radius: 4px;
1132
1227
  overflow: hidden;
1228
+ transition: all 0.2s ease;
1133
1229
  }
1134
1230
 
1135
1231
  .file-thumbnail {
1136
1232
  width: 100%;
1137
1233
  height: 100%;
1138
1234
  object-fit: cover;
1139
- border-radius: var(--br, 4px);
1140
- transition: transform 0.3s ease;
1235
+ border-radius: 3px;
1236
+ transition: transform 0.2s ease;
1141
1237
  }
1142
1238
 
1143
1239
  .file-thumbnail:hover {