@visns-studio/visns-components 5.14.10 → 5.14.12

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
  };
@@ -2,6 +2,132 @@ import parse from 'html-react-parser';
2
2
  import moment from 'moment';
3
3
  import numeral from 'numeral';
4
4
 
5
+ /**
6
+ * Smart boolean/binary value interpreter based on column name and context
7
+ * @param {string|number} value - The value (0/1, true/false, etc.)
8
+ * @param {Object} column - Column configuration with id/name
9
+ * @returns {string} Human-friendly representation
10
+ */
11
+ const interpretBooleanValue = (value, column) => {
12
+ const columnName = (column.id || column.name || '').toLowerCase();
13
+ const isTrue = value === 1 || value === true || value === 'true' || value === 'yes' || value === 'Yes';
14
+ const isFalse = value === 0 || value === false || value === 'false' || value === 'no' || value === 'No';
15
+
16
+ if (!isTrue && !isFalse) return value; // Return original if not clearly boolean
17
+
18
+ // Contextual interpretations based on column name patterns
19
+ const interpretations = {
20
+ // Status/State patterns
21
+ 'active': { true: 'Active', false: 'Inactive' },
22
+ 'enabled': { true: 'Enabled', false: 'Disabled' },
23
+ 'status': { true: 'Active', false: 'Inactive' },
24
+ 'visible': { true: 'Visible', false: 'Hidden' },
25
+ 'published': { true: 'Published', false: 'Draft' },
26
+ 'public': { true: 'Public', false: 'Private' },
27
+ 'live': { true: 'Live', false: 'Offline' },
28
+
29
+ // Completion/Progress patterns
30
+ 'completed': { true: 'Completed', false: 'Pending' },
31
+ 'finished': { true: 'Finished', false: 'In Progress' },
32
+ 'done': { true: 'Done', false: 'To Do' },
33
+ 'complete': { true: 'Complete', false: 'Incomplete' },
34
+ 'resolved': { true: 'Resolved', false: 'Open' },
35
+ 'closed': { true: 'Closed', false: 'Open' },
36
+
37
+ // Approval/Verification patterns
38
+ 'approved': { true: 'Approved', false: 'Pending' },
39
+ 'verified': { true: 'Verified', false: 'Unverified' },
40
+ 'confirmed': { true: 'Confirmed', false: 'Unconfirmed' },
41
+ 'accepted': { true: 'Accepted', false: 'Rejected' },
42
+ 'valid': { true: 'Valid', false: 'Invalid' },
43
+
44
+ // Access/Permission patterns
45
+ 'allowed': { true: 'Allowed', false: 'Denied' },
46
+ 'permitted': { true: 'Permitted', false: 'Restricted' },
47
+ 'authorized': { true: 'Authorized', false: 'Unauthorized' },
48
+ 'locked': { true: 'Locked', false: 'Unlocked' },
49
+
50
+ // Notification/Communication patterns
51
+ 'sent': { true: 'Sent', false: 'Not Sent' },
52
+ 'delivered': { true: 'Delivered', false: 'Pending' },
53
+ 'read': { true: 'Read', false: 'Unread' },
54
+ 'notified': { true: 'Notified', false: 'Not Notified' },
55
+
56
+ // Business/Financial patterns
57
+ 'paid': { true: 'Paid', false: 'Unpaid' },
58
+ 'invoiced': { true: 'Invoiced', false: 'Not Invoiced' },
59
+ 'billed': { true: 'Billed', false: 'Unbilled' },
60
+ 'taxable': { true: 'Taxable', false: 'Tax Free' },
61
+
62
+ // Quality/Condition patterns
63
+ 'available': { true: 'Available', false: 'Unavailable' },
64
+ 'ready': { true: 'Ready', false: 'Not Ready' },
65
+ 'urgent': { true: 'Urgent', false: 'Normal' },
66
+ 'priority': { true: 'High Priority', false: 'Normal Priority' },
67
+ 'critical': { true: 'Critical', false: 'Normal' },
68
+
69
+ // Agreement/Contract patterns
70
+ 'signed': { true: 'Signed', false: 'Unsigned' },
71
+ 'executed': { true: 'Executed', false: 'Draft' },
72
+ 'expired': { true: 'Expired', false: 'Current' },
73
+
74
+ // Project/Work patterns
75
+ 'assigned': { true: 'Assigned', false: 'Unassigned' },
76
+ 'allocated': { true: 'Allocated', false: 'Available' },
77
+ 'booked': { true: 'Booked', false: 'Available' },
78
+ 'scheduled': { true: 'Scheduled', false: 'Unscheduled' },
79
+ 'stage': { true: 'Completed', false: 'Pending' },
80
+ 'final': { true: 'Completed', false: 'Incomplete' },
81
+ 'finished': { true: 'Finished', false: 'In Progress' },
82
+ 'done': { true: 'Done', false: 'To Do' },
83
+
84
+ // System/Technical patterns
85
+ 'online': { true: 'Online', false: 'Offline' },
86
+ 'connected': { true: 'Connected', false: 'Disconnected' },
87
+ 'synced': { true: 'Synced', false: 'Not Synced' },
88
+ 'backup': { true: 'Backed Up', false: 'Not Backed Up' },
89
+ };
90
+
91
+ // Try to find a match based on column name
92
+ for (const [pattern, values] of Object.entries(interpretations)) {
93
+ if (columnName.includes(pattern)) {
94
+ return isTrue ? values.true : values.false;
95
+ }
96
+ }
97
+
98
+ // Special handling for specific common patterns
99
+ if (columnName.includes('is_') || columnName.startsWith('has_') || columnName.endsWith('_flag')) {
100
+ // Extract the meaningful part
101
+ const meaningfulPart = columnName
102
+ .replace(/^(is_|has_)/, '')
103
+ .replace(/_flag$/, '')
104
+ .replace(/_/g, ' ')
105
+ .replace(/\b\w/g, l => l.toUpperCase()); // Title case
106
+
107
+ return isTrue ? meaningfulPart : `Not ${meaningfulPart}`;
108
+ }
109
+
110
+ // Default fallback - still better than raw 0/1
111
+ return isTrue ? 'Yes' : 'No';
112
+ };
113
+
114
+ /**
115
+ * Auto-detect appropriate date format based on value content
116
+ * @param {*} value - The date value to analyze
117
+ * @param {string} fallbackFormat - Default format if detection fails
118
+ * @returns {string} Appropriate moment.js format string
119
+ */
120
+ export const autoDetectDateFormat = (value, fallbackFormat = 'DD-MM-YYYY') => {
121
+ if (!value || !moment(value).isValid()) {
122
+ return fallbackFormat;
123
+ }
124
+
125
+ const momentValue = moment(value);
126
+ const hasTime = momentValue.format('HH:mm:ss') !== '00:00:00';
127
+
128
+ return hasTime ? 'DD-MM-YYYY HH:mm' : 'DD-MM-YYYY';
129
+ };
130
+
5
131
  /**
6
132
  * Format quarter columns with status, contacts, and reasons in tooltips
7
133
  * @param {*} value - The date value
@@ -29,7 +155,7 @@ const formatQuarterWithStatus = (value, column, row) => {
29
155
  // If status is 'sent' and we have a date
30
156
  if (status === 'sent' && value) {
31
157
  const formattedDate = moment(value).isValid()
32
- ? moment(value).format('DD/MM/YYYY')
158
+ ? moment(value).format('DD-MM-YYYY')
33
159
  : value;
34
160
 
35
161
  const contactCount = Array.isArray(contacts) ? contacts.length : 0;
@@ -71,7 +197,7 @@ const formatQuarterWithStatus = (value, column, row) => {
71
197
  // If we have a date but no clear status (legacy data)
72
198
  if (value) {
73
199
  const formattedDate = moment(value).isValid()
74
- ? moment(value).format('DD/MM/YYYY')
200
+ ? moment(value).format('DD-MM-YYYY')
75
201
  : value;
76
202
 
77
203
  const contactCount = Array.isArray(contacts) ? contacts.length : 0;
@@ -125,11 +251,11 @@ export const formatCellContent = (value, column, row = null) => {
125
251
  switch (valueType) {
126
252
  case 'date':
127
253
  return moment(value).isValid()
128
- ? moment(value).format(column.format || 'DD/MM/YYYY')
254
+ ? moment(value).format(column.format || autoDetectDateFormat(value, 'DD-MM-YYYY'))
129
255
  : value;
130
256
  case 'datetime':
131
257
  return moment(value).isValid()
132
- ? moment(value).format(column.format || 'DD/MM/YYYY HH:mm')
258
+ ? moment(value).format(column.format || autoDetectDateFormat(value, 'DD-MM-YYYY HH:mm'))
133
259
  : value;
134
260
  case 'currency':
135
261
  return numeral(value).format(column.format || '$0,0.00');
@@ -139,25 +265,7 @@ export const formatCellContent = (value, column, row = null) => {
139
265
  ? numeral(value).format(column.format || '0,0')
140
266
  : value;
141
267
  case 'boolean':
142
- if (
143
- value === 1 ||
144
- value === true ||
145
- value === 'true' ||
146
- value === 'yes' ||
147
- value === 'Yes'
148
- ) {
149
- return 'Yes';
150
- }
151
- if (
152
- value === 0 ||
153
- value === false ||
154
- value === 'false' ||
155
- value === 'no' ||
156
- value === 'No'
157
- ) {
158
- return 'No';
159
- }
160
- return value;
268
+ return interpretBooleanValue(value, column);
161
269
  case 'year':
162
270
  // Special handling for year values
163
271
  return value.toString();
@@ -174,6 +282,27 @@ export const formatCellContent = (value, column, row = null) => {
174
282
  // Parse rich text content
175
283
  return parse(String(value));
176
284
  default:
285
+ // Check if this might be a boolean value based on its content
286
+ if ((value === 0 || value === 1) && column.id) {
287
+ // Only apply smart boolean interpretation if the column name suggests it's boolean
288
+ const columnName = (column.id || column.name || '').toLowerCase();
289
+ const booleanIndicators = [
290
+ 'is_', 'has_', 'can_', 'should_', 'will_', 'did_',
291
+ 'active', 'enabled', 'completed', 'confirmed', 'approved', 'verified',
292
+ 'sent', 'delivered', 'paid', 'signed', 'expired', 'urgent', 'critical',
293
+ 'public', 'visible', 'available', 'ready', 'locked', 'online',
294
+ '_flag', '_status', '_state'
295
+ ];
296
+
297
+ const seemsBooleanish = booleanIndicators.some(indicator =>
298
+ columnName.includes(indicator)
299
+ );
300
+
301
+ if (seemsBooleanish) {
302
+ return interpretBooleanValue(value, column);
303
+ }
304
+ }
305
+
177
306
  // For text values, check if it contains HTML tags
178
307
  if (
179
308
  typeof value === 'string' &&
@@ -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 {