@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.
- package/README.md +51 -7
- package/package.json +4 -4
- package/src/components/Field.jsx +15 -0
- package/src/components/Form.jsx +57 -8
- package/src/components/columns/ColumnRenderers.jsx +16 -6
- package/src/components/generic/GenericReport.jsx +1662 -540
- package/src/components/generic/StandardModal.jsx +62 -12
- package/src/components/generic/shared/formatters.js +152 -23
- package/src/components/styles/Field.module.scss +144 -48
- package/src/components/styles/GenericReport.module.scss +328 -0
|
@@ -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
|
-
//
|
|
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
|
-
|
|
57
|
-
|
|
58
|
-
|
|
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
|
-
|
|
73
|
-
|
|
74
|
-
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
-
|
|
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:
|
|
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
|
-
|
|
854
|
-
|
|
855
|
-
|
|
856
|
-
|
|
857
|
-
|
|
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
|
-
|
|
863
|
-
|
|
864
|
-
background: var(--tertiary-color);
|
|
865
|
-
max-height: 200px;
|
|
915
|
+
background: transparent;
|
|
916
|
+
max-height: 220px;
|
|
866
917
|
overflow-y: auto;
|
|
867
|
-
padding: 0
|
|
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
|
|
875
|
-
|
|
876
|
-
|
|
877
|
-
|
|
878
|
-
|
|
879
|
-
transition:
|
|
880
|
-
|
|
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:
|
|
884
|
-
border-
|
|
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(--
|
|
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:
|
|
893
|
-
border:
|
|
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(--
|
|
965
|
+
color: var(--danger-color, #dc3545);
|
|
896
966
|
display: flex;
|
|
897
967
|
align-items: center;
|
|
898
|
-
|
|
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
|
-
|
|
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:
|
|
907
|
-
height:
|
|
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.
|
|
1038
|
-
padding: 0.75em
|
|
1039
|
-
border-bottom: 1px solid var(--border-color,
|
|
1040
|
-
font-size: 0.
|
|
1041
|
-
background:
|
|
1042
|
-
border-radius:
|
|
1043
|
-
transition: all 0.
|
|
1044
|
-
margin-bottom: 0.
|
|
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.
|
|
1155
|
+
font-size: 1.1em;
|
|
1068
1156
|
display: flex;
|
|
1069
1157
|
align-items: center;
|
|
1070
1158
|
justify-content: center;
|
|
1071
|
-
width:
|
|
1072
|
-
height:
|
|
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, #
|
|
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:
|
|
1127
|
-
height:
|
|
1221
|
+
width: 1.8em;
|
|
1222
|
+
height: 1.8em;
|
|
1128
1223
|
flex-shrink: 0;
|
|
1129
|
-
background: rgba(var(--
|
|
1130
|
-
border: 1px solid var(--
|
|
1131
|
-
border-radius:
|
|
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:
|
|
1140
|
-
transition: transform 0.
|
|
1235
|
+
border-radius: 3px;
|
|
1236
|
+
transition: transform 0.2s ease;
|
|
1141
1237
|
}
|
|
1142
1238
|
|
|
1143
1239
|
.file-thumbnail:hover {
|