mock-service-cli 4.1.0 → 4.2.0
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/dist/file-explorer.html +378 -1
- package/dist/runtime.js +6 -6
- package/package.json +5 -3
package/dist/file-explorer.html
CHANGED
|
@@ -156,6 +156,100 @@
|
|
|
156
156
|
background: #e0e0e0;
|
|
157
157
|
}
|
|
158
158
|
|
|
159
|
+
.toolbar-action-btn {
|
|
160
|
+
padding: 6px 10px;
|
|
161
|
+
background: #f0f0f0;
|
|
162
|
+
border: none;
|
|
163
|
+
border-radius: 4px;
|
|
164
|
+
font-size: 12px;
|
|
165
|
+
cursor: pointer;
|
|
166
|
+
transition: all 0.2s;
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
.toolbar-action-btn:hover:not(:disabled) {
|
|
170
|
+
background: #e0e0e0;
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
.toolbar-action-btn.danger:hover:not(:disabled) {
|
|
174
|
+
background: #dc3545;
|
|
175
|
+
color: white;
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
.toolbar-action-btn:disabled {
|
|
179
|
+
opacity: 0.45;
|
|
180
|
+
cursor: not-allowed;
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
.edit-mode-toggle {
|
|
184
|
+
display: inline-flex;
|
|
185
|
+
align-items: center;
|
|
186
|
+
gap: 8px;
|
|
187
|
+
font-size: 12px;
|
|
188
|
+
color: #666;
|
|
189
|
+
user-select: none;
|
|
190
|
+
white-space: nowrap;
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
.edit-mode-toggle input {
|
|
194
|
+
position: absolute;
|
|
195
|
+
width: 1px;
|
|
196
|
+
height: 1px;
|
|
197
|
+
opacity: 0;
|
|
198
|
+
pointer-events: none;
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
.edit-mode-switch {
|
|
202
|
+
width: 38px;
|
|
203
|
+
height: 20px;
|
|
204
|
+
background: #d1d5db;
|
|
205
|
+
border-radius: 999px;
|
|
206
|
+
position: relative;
|
|
207
|
+
transition: background 0.2s;
|
|
208
|
+
flex-shrink: 0;
|
|
209
|
+
cursor: pointer;
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
.edit-mode-switch::after {
|
|
213
|
+
content: '';
|
|
214
|
+
position: absolute;
|
|
215
|
+
width: 16px;
|
|
216
|
+
height: 16px;
|
|
217
|
+
border-radius: 50%;
|
|
218
|
+
top: 2px;
|
|
219
|
+
left: 2px;
|
|
220
|
+
background: #fff;
|
|
221
|
+
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.2);
|
|
222
|
+
transition: transform 0.2s;
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
.edit-mode-toggle input:checked + .edit-mode-switch {
|
|
226
|
+
background: #667eea;
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
.edit-mode-toggle input:checked + .edit-mode-switch::after {
|
|
230
|
+
transform: translateX(18px);
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
body:not(.editing) .edit-only {
|
|
234
|
+
display: none !important;
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
body.editing .edit-inline {
|
|
238
|
+
display: inline-flex !important;
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
body.editing .edit-inline-block {
|
|
242
|
+
display: inline-block !important;
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
body.editing .edit-flex {
|
|
246
|
+
display: flex !important;
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
body.editing .edit-block {
|
|
250
|
+
display: block !important;
|
|
251
|
+
}
|
|
252
|
+
|
|
159
253
|
.back-btn {
|
|
160
254
|
padding: 6px 12px;
|
|
161
255
|
background: #f0f0f0;
|
|
@@ -318,6 +412,11 @@
|
|
|
318
412
|
background: #f8f9fa;
|
|
319
413
|
}
|
|
320
414
|
|
|
415
|
+
.file-item.selected {
|
|
416
|
+
background: #eef2ff;
|
|
417
|
+
outline: 1px solid #667eea;
|
|
418
|
+
}
|
|
419
|
+
|
|
321
420
|
.list-view .file-item {
|
|
322
421
|
flex-direction: row;
|
|
323
422
|
padding: 6px 12px;
|
|
@@ -348,6 +447,17 @@
|
|
|
348
447
|
height: 100%;
|
|
349
448
|
}
|
|
350
449
|
|
|
450
|
+
.file-select {
|
|
451
|
+
align-self: flex-start;
|
|
452
|
+
margin-bottom: 6px;
|
|
453
|
+
cursor: pointer;
|
|
454
|
+
}
|
|
455
|
+
|
|
456
|
+
.list-view .file-select {
|
|
457
|
+
align-self: center;
|
|
458
|
+
margin: 0 8px 0 0;
|
|
459
|
+
}
|
|
460
|
+
|
|
351
461
|
.file-info {
|
|
352
462
|
text-align: center;
|
|
353
463
|
word-break: break-all;
|
|
@@ -426,6 +536,16 @@
|
|
|
426
536
|
color: white;
|
|
427
537
|
}
|
|
428
538
|
|
|
539
|
+
.action-btn.rename:hover {
|
|
540
|
+
background: #ffc107;
|
|
541
|
+
color: #333;
|
|
542
|
+
}
|
|
543
|
+
|
|
544
|
+
.action-btn.delete:hover {
|
|
545
|
+
background: #dc3545;
|
|
546
|
+
color: white;
|
|
547
|
+
}
|
|
548
|
+
|
|
429
549
|
.modal {
|
|
430
550
|
display: none;
|
|
431
551
|
position: fixed;
|
|
@@ -746,6 +866,30 @@
|
|
|
746
866
|
返回上级
|
|
747
867
|
</button>
|
|
748
868
|
<button class="refresh-btn" onclick="loadDirectory(currentPath)" title="刷新">🔄</button>
|
|
869
|
+
<label class="edit-mode-toggle" title="启用编辑模式">
|
|
870
|
+
<input id="editModeToggle" type="checkbox" onchange="toggleEditMode(this.checked)" />
|
|
871
|
+
<span class="edit-mode-switch"></span>
|
|
872
|
+
<span class="edit-mode-label">编辑</span>
|
|
873
|
+
</label>
|
|
874
|
+
<button
|
|
875
|
+
class="toolbar-action-btn edit-only edit-inline"
|
|
876
|
+
onclick="createEntry('directory')"
|
|
877
|
+
title="新建文件夹"
|
|
878
|
+
>
|
|
879
|
+
新建文件夹
|
|
880
|
+
</button>
|
|
881
|
+
<button class="toolbar-action-btn edit-only edit-inline" onclick="createEntry('file')" title="新建文件">
|
|
882
|
+
新建文件
|
|
883
|
+
</button>
|
|
884
|
+
<button
|
|
885
|
+
class="toolbar-action-btn danger edit-only edit-inline"
|
|
886
|
+
id="deleteSelectedBtn"
|
|
887
|
+
onclick="deleteSelectedPaths()"
|
|
888
|
+
title="删除选中项"
|
|
889
|
+
disabled
|
|
890
|
+
>
|
|
891
|
+
删除选中
|
|
892
|
+
</button>
|
|
749
893
|
<button
|
|
750
894
|
class="toggle-hidden-btn"
|
|
751
895
|
id="toggleHiddenBtn"
|
|
@@ -803,6 +947,13 @@
|
|
|
803
947
|
🖥️ 在文件管理器中打开
|
|
804
948
|
</div>
|
|
805
949
|
<div class="context-menu-item" id="ctxDownload" onclick="handleContextMenuAction('download')">⬇️ 下载</div>
|
|
950
|
+
<div class="context-menu-divider edit-only edit-block"></div>
|
|
951
|
+
<div class="context-menu-item edit-only edit-flex" id="ctxRename" onclick="handleContextMenuAction('rename')">
|
|
952
|
+
✏️ 重命名
|
|
953
|
+
</div>
|
|
954
|
+
<div class="context-menu-item edit-only edit-flex" id="ctxDelete" onclick="handleContextMenuAction('delete')">
|
|
955
|
+
🗑️ 删除
|
|
956
|
+
</div>
|
|
806
957
|
</div>
|
|
807
958
|
|
|
808
959
|
<script>
|
|
@@ -821,6 +972,8 @@
|
|
|
821
972
|
let currentContextMenuFile = null;
|
|
822
973
|
let currentImageIndex = -1;
|
|
823
974
|
let imageFiles = [];
|
|
975
|
+
let selectedPaths = new Set();
|
|
976
|
+
let editMode = false;
|
|
824
977
|
|
|
825
978
|
let eventHandlers = [];
|
|
826
979
|
let searchDebounceTimer = null;
|
|
@@ -848,6 +1001,34 @@
|
|
|
848
1001
|
eventHandlers = [];
|
|
849
1002
|
}
|
|
850
1003
|
|
|
1004
|
+
function initializeEditMode() {
|
|
1005
|
+
editMode = false;
|
|
1006
|
+
}
|
|
1007
|
+
|
|
1008
|
+
function syncEditModeUI() {
|
|
1009
|
+
const body = document.body;
|
|
1010
|
+
const toggle = document.getElementById('editModeToggle');
|
|
1011
|
+
|
|
1012
|
+
body.classList.toggle('editing', editMode);
|
|
1013
|
+
if (toggle) {
|
|
1014
|
+
toggle.checked = editMode;
|
|
1015
|
+
}
|
|
1016
|
+
|
|
1017
|
+
if (!editMode) {
|
|
1018
|
+
selectedPaths.clear();
|
|
1019
|
+
hideContextMenu();
|
|
1020
|
+
currentContextMenuFile = null;
|
|
1021
|
+
}
|
|
1022
|
+
|
|
1023
|
+
updateSelectionToolbar();
|
|
1024
|
+
}
|
|
1025
|
+
|
|
1026
|
+
function toggleEditMode(enabled) {
|
|
1027
|
+
editMode = Boolean(enabled);
|
|
1028
|
+
syncEditModeUI();
|
|
1029
|
+
processAndRenderFiles();
|
|
1030
|
+
}
|
|
1031
|
+
|
|
851
1032
|
function handleFileGridClick(e) {
|
|
852
1033
|
const actionBtn = e.target.closest('[data-action]');
|
|
853
1034
|
if (actionBtn) {
|
|
@@ -866,6 +1047,15 @@
|
|
|
866
1047
|
case 'download':
|
|
867
1048
|
downloadFile(path, filename);
|
|
868
1049
|
break;
|
|
1050
|
+
case 'rename':
|
|
1051
|
+
renamePath(path, filename);
|
|
1052
|
+
break;
|
|
1053
|
+
case 'delete':
|
|
1054
|
+
deletePath(path, filename);
|
|
1055
|
+
break;
|
|
1056
|
+
case 'toggleSelect':
|
|
1057
|
+
toggleSelectedPath(path, actionBtn.checked, actionBtn.closest('.file-item'));
|
|
1058
|
+
break;
|
|
869
1059
|
}
|
|
870
1060
|
return;
|
|
871
1061
|
}
|
|
@@ -940,6 +1130,8 @@
|
|
|
940
1130
|
}
|
|
941
1131
|
|
|
942
1132
|
document.addEventListener('DOMContentLoaded', () => {
|
|
1133
|
+
initializeEditMode();
|
|
1134
|
+
syncEditModeUI();
|
|
943
1135
|
loadDirectory('/');
|
|
944
1136
|
|
|
945
1137
|
const fileGrid = document.getElementById('fileGrid');
|
|
@@ -1007,6 +1199,12 @@
|
|
|
1007
1199
|
downloadFile(path, path.split('/').pop());
|
|
1008
1200
|
}
|
|
1009
1201
|
break;
|
|
1202
|
+
case 'rename':
|
|
1203
|
+
renamePath(path, path.split('/').pop());
|
|
1204
|
+
break;
|
|
1205
|
+
case 'delete':
|
|
1206
|
+
deletePath(path, path.split('/').pop());
|
|
1207
|
+
break;
|
|
1010
1208
|
}
|
|
1011
1209
|
|
|
1012
1210
|
hideContextMenu();
|
|
@@ -1022,6 +1220,8 @@
|
|
|
1022
1220
|
// 重置所有状态
|
|
1023
1221
|
allFiles = [];
|
|
1024
1222
|
currentFiles = [];
|
|
1223
|
+
selectedPaths.clear();
|
|
1224
|
+
updateSelectionToolbar();
|
|
1025
1225
|
|
|
1026
1226
|
// 显示加载状态
|
|
1027
1227
|
fileGrid.innerHTML = '<div class="loading"><div class="spinner"></div><p>加载中...</p></div>';
|
|
@@ -1209,6 +1409,77 @@
|
|
|
1209
1409
|
}
|
|
1210
1410
|
}
|
|
1211
1411
|
|
|
1412
|
+
async function requestJson(url, options) {
|
|
1413
|
+
const response = await fetch(url, options);
|
|
1414
|
+
const data = await response.json();
|
|
1415
|
+
if (!response.ok || data.error) {
|
|
1416
|
+
throw new Error(data.error || '请求失败');
|
|
1417
|
+
}
|
|
1418
|
+
return data;
|
|
1419
|
+
}
|
|
1420
|
+
|
|
1421
|
+
async function createEntry(type) {
|
|
1422
|
+
const label = type === 'directory' ? '文件夹' : '文件';
|
|
1423
|
+
const name = window.prompt(`请输入${label}名称`);
|
|
1424
|
+
if (name === null) return;
|
|
1425
|
+
|
|
1426
|
+
const trimmedName = name.trim();
|
|
1427
|
+
if (!trimmedName) {
|
|
1428
|
+
alert(`${label}名称不能为空`);
|
|
1429
|
+
return;
|
|
1430
|
+
}
|
|
1431
|
+
|
|
1432
|
+
try {
|
|
1433
|
+
await requestJson('/__api/path', {
|
|
1434
|
+
method: 'POST',
|
|
1435
|
+
headers: {
|
|
1436
|
+
'Content-Type': 'application/json'
|
|
1437
|
+
},
|
|
1438
|
+
body: JSON.stringify({
|
|
1439
|
+
parentPath: currentPath,
|
|
1440
|
+
name: trimmedName,
|
|
1441
|
+
type
|
|
1442
|
+
})
|
|
1443
|
+
});
|
|
1444
|
+
|
|
1445
|
+
showToast(`新建${label}成功`);
|
|
1446
|
+
await loadDirectory(currentPath);
|
|
1447
|
+
} catch (error) {
|
|
1448
|
+
console.error(`新建${label}失败:`, error);
|
|
1449
|
+
alert(`新建${label}失败: ` + error.message);
|
|
1450
|
+
}
|
|
1451
|
+
}
|
|
1452
|
+
|
|
1453
|
+
async function renamePath(filePath, fileName) {
|
|
1454
|
+
const nextName = window.prompt('请输入新名称', fileName || filePath.split('/').pop());
|
|
1455
|
+
if (nextName === null) return;
|
|
1456
|
+
|
|
1457
|
+
const trimmedName = nextName.trim();
|
|
1458
|
+
if (!trimmedName) {
|
|
1459
|
+
alert('名称不能为空');
|
|
1460
|
+
return;
|
|
1461
|
+
}
|
|
1462
|
+
|
|
1463
|
+
try {
|
|
1464
|
+
await requestJson('/__api/path', {
|
|
1465
|
+
method: 'PATCH',
|
|
1466
|
+
headers: {
|
|
1467
|
+
'Content-Type': 'application/json'
|
|
1468
|
+
},
|
|
1469
|
+
body: JSON.stringify({
|
|
1470
|
+
path: filePath,
|
|
1471
|
+
name: trimmedName
|
|
1472
|
+
})
|
|
1473
|
+
});
|
|
1474
|
+
|
|
1475
|
+
showToast('重命名成功');
|
|
1476
|
+
await loadDirectory(currentPath);
|
|
1477
|
+
} catch (error) {
|
|
1478
|
+
console.error('重命名失败:', error);
|
|
1479
|
+
alert('重命名失败: ' + error.message);
|
|
1480
|
+
}
|
|
1481
|
+
}
|
|
1482
|
+
|
|
1212
1483
|
function downloadFile(filePath, fileName) {
|
|
1213
1484
|
const url = `/__api/file?path=${encodeURIComponent(filePath)}`;
|
|
1214
1485
|
const a = document.createElement('a');
|
|
@@ -1219,6 +1490,85 @@
|
|
|
1219
1490
|
document.body.removeChild(a);
|
|
1220
1491
|
}
|
|
1221
1492
|
|
|
1493
|
+
async function deletePath(filePath, fileName) {
|
|
1494
|
+
const label = fileName || filePath.split('/').pop() || filePath;
|
|
1495
|
+
const confirmed = window.confirm(`确定要删除 "${label}" 吗?此操作不可恢复。`);
|
|
1496
|
+
|
|
1497
|
+
if (!confirmed) return;
|
|
1498
|
+
|
|
1499
|
+
try {
|
|
1500
|
+
const response = await fetch('/__api/path', {
|
|
1501
|
+
method: 'DELETE',
|
|
1502
|
+
headers: {
|
|
1503
|
+
'Content-Type': 'application/json'
|
|
1504
|
+
},
|
|
1505
|
+
body: JSON.stringify({ path: filePath })
|
|
1506
|
+
});
|
|
1507
|
+
|
|
1508
|
+
const data = await response.json();
|
|
1509
|
+
if (!response.ok || !data.success) {
|
|
1510
|
+
throw new Error(data.error || '删除失败');
|
|
1511
|
+
}
|
|
1512
|
+
|
|
1513
|
+
closeModal();
|
|
1514
|
+
showToast('删除成功');
|
|
1515
|
+
await loadDirectory(currentPath);
|
|
1516
|
+
} catch (error) {
|
|
1517
|
+
console.error('删除失败:', error);
|
|
1518
|
+
alert('删除失败: ' + error.message);
|
|
1519
|
+
}
|
|
1520
|
+
}
|
|
1521
|
+
|
|
1522
|
+
async function deleteSelectedPaths() {
|
|
1523
|
+
const paths = Array.from(selectedPaths);
|
|
1524
|
+
if (paths.length === 0) return;
|
|
1525
|
+
|
|
1526
|
+
const confirmed = window.confirm(`确定要删除选中的 ${paths.length} 项吗?此操作不可恢复。`);
|
|
1527
|
+
if (!confirmed) return;
|
|
1528
|
+
|
|
1529
|
+
try {
|
|
1530
|
+
await requestJson('/__api/paths', {
|
|
1531
|
+
method: 'DELETE',
|
|
1532
|
+
headers: {
|
|
1533
|
+
'Content-Type': 'application/json'
|
|
1534
|
+
},
|
|
1535
|
+
body: JSON.stringify({ paths })
|
|
1536
|
+
});
|
|
1537
|
+
|
|
1538
|
+
selectedPaths.clear();
|
|
1539
|
+
updateSelectionToolbar();
|
|
1540
|
+
showToast('批量删除成功');
|
|
1541
|
+
await loadDirectory(currentPath);
|
|
1542
|
+
} catch (error) {
|
|
1543
|
+
console.error('批量删除失败:', error);
|
|
1544
|
+
alert('批量删除失败: ' + error.message);
|
|
1545
|
+
await loadDirectory(currentPath);
|
|
1546
|
+
}
|
|
1547
|
+
}
|
|
1548
|
+
|
|
1549
|
+
function toggleSelectedPath(filePath, checked, fileItem) {
|
|
1550
|
+
if (checked) {
|
|
1551
|
+
selectedPaths.add(filePath);
|
|
1552
|
+
} else {
|
|
1553
|
+
selectedPaths.delete(filePath);
|
|
1554
|
+
}
|
|
1555
|
+
|
|
1556
|
+
if (fileItem) {
|
|
1557
|
+
fileItem.classList.toggle('selected', checked);
|
|
1558
|
+
}
|
|
1559
|
+
|
|
1560
|
+
updateSelectionToolbar();
|
|
1561
|
+
}
|
|
1562
|
+
|
|
1563
|
+
function updateSelectionToolbar() {
|
|
1564
|
+
const deleteSelectedBtn = document.getElementById('deleteSelectedBtn');
|
|
1565
|
+
if (!deleteSelectedBtn) return;
|
|
1566
|
+
|
|
1567
|
+
const count = selectedPaths.size;
|
|
1568
|
+
deleteSelectedBtn.disabled = count === 0;
|
|
1569
|
+
deleteSelectedBtn.textContent = count ? `删除选中(${count})` : '删除选中';
|
|
1570
|
+
}
|
|
1571
|
+
|
|
1222
1572
|
function renderBreadcrumb(currentPath, parentPath) {
|
|
1223
1573
|
const breadcrumb = document.getElementById('breadcrumb');
|
|
1224
1574
|
const parts = currentPath.split('/').filter(Boolean);
|
|
@@ -1266,14 +1616,26 @@
|
|
|
1266
1616
|
const mtime = formatDate(file.mtime);
|
|
1267
1617
|
const birthtime = formatDate(file.birthtime);
|
|
1268
1618
|
const hiddenClass = file.isHidden ? 'hidden-file' : '';
|
|
1619
|
+
const selectedClass = selectedPaths.has(file.path) ? 'selected' : '';
|
|
1620
|
+
const selectedAttr = selectedPaths.has(file.path) ? 'checked' : '';
|
|
1621
|
+
const selectLabel = file.isDirectory ? '选择文件夹' : '选择文件';
|
|
1269
1622
|
|
|
1270
1623
|
const fileItem = document.createElement('div');
|
|
1271
|
-
fileItem.className = `file-item ${hiddenClass}`;
|
|
1624
|
+
fileItem.className = `file-item ${hiddenClass} ${selectedClass}`;
|
|
1272
1625
|
fileItem.dataset.path = file.path;
|
|
1273
1626
|
fileItem.dataset.isDirectory = file.isDirectory;
|
|
1274
1627
|
|
|
1275
1628
|
if (currentView === 'list') {
|
|
1276
1629
|
fileItem.innerHTML = `
|
|
1630
|
+
<input
|
|
1631
|
+
class="file-select edit-only edit-inline-block"
|
|
1632
|
+
type="checkbox"
|
|
1633
|
+
data-action="toggleSelect"
|
|
1634
|
+
data-path="${file.path}"
|
|
1635
|
+
data-filename="${escapeHtml(file.name)}"
|
|
1636
|
+
title="${selectLabel}"
|
|
1637
|
+
${selectedAttr}
|
|
1638
|
+
/>
|
|
1277
1639
|
<div class="file-icon">${icon}</div>
|
|
1278
1640
|
<div class="file-info">
|
|
1279
1641
|
<div class="file-name">${escapeHtml(file.name)}</div>
|
|
@@ -1295,10 +1657,25 @@
|
|
|
1295
1657
|
}" data-filename="${escapeHtml(file.name)}" title="下载">⬇️</button>`
|
|
1296
1658
|
: ''
|
|
1297
1659
|
}
|
|
1660
|
+
<button class="action-btn rename edit-only edit-inline" data-action="rename" data-path="${file.path}" data-filename="${escapeHtml(
|
|
1661
|
+
file.name
|
|
1662
|
+
)}" title="重命名">✏️</button>
|
|
1663
|
+
<button class="action-btn delete edit-only edit-inline" data-action="delete" data-path="${file.path}" data-filename="${escapeHtml(
|
|
1664
|
+
file.name
|
|
1665
|
+
)}" title="删除">🗑️</button>
|
|
1298
1666
|
</div>
|
|
1299
1667
|
`;
|
|
1300
1668
|
} else {
|
|
1301
1669
|
fileItem.innerHTML = `
|
|
1670
|
+
<input
|
|
1671
|
+
class="file-select edit-only edit-inline-block"
|
|
1672
|
+
type="checkbox"
|
|
1673
|
+
data-action="toggleSelect"
|
|
1674
|
+
data-path="${file.path}"
|
|
1675
|
+
data-filename="${escapeHtml(file.name)}"
|
|
1676
|
+
title="${selectLabel}"
|
|
1677
|
+
${selectedAttr}
|
|
1678
|
+
/>
|
|
1302
1679
|
<div class="file-icon">${icon}</div>
|
|
1303
1680
|
<div class="file-info">
|
|
1304
1681
|
<div class="file-name">${escapeHtml(file.name)}</div>
|