(folder)
| 1039 | } |
| 1040 | |
| 1041 | function renderFolder(folder) { |
| 1042 | const isTrash = folder.id === TRASH_ID; |
| 1043 | const isUnsorted = folder.id === UNSORTED_ID; |
| 1044 | const isSubfolder = Boolean(folder.parentId); |
| 1045 | if (!isTrash) folder.color = normalizeFolderColor(folder.color); |
| 1046 | |
| 1047 | const el = document.createElement("div"); |
| 1048 | el.className = `folder${folder.collapsed ? " collapsed" : ""}${isSubfolder ? " subfolder" : ""}`; |
| 1049 | el.dataset.id = folder.id; |
| 1050 | |
| 1051 | const head = document.createElement("div"); |
| 1052 | head.className = "folder-head"; |
| 1053 | if (!isTrash) head.style.setProperty("--folder-color", folder.color); |
| 1054 | |
| 1055 | const folderIcon = isTrash |
| 1056 | ? `<svg class="f-icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><polyline points="3 6 5 6 21 6"></polyline><path d="M19 6v14a2 2 0 0 1-2 2H7a2 2 0 0 1-2-2V6m3 0V4a1 1 0 0 1 1-1h4a1 1 0 0 1 1 1v2"></path></svg>` |
| 1057 | : `<svg class="f-icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M22 19a2 2 0 0 1-2 2H4a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h5l2 3h9a2 2 0 0 1 2 2z"></path></svg>`; |
| 1058 | |
| 1059 | head.innerHTML = ` |
| 1060 | ${isTrash ? "" : `<span class="f-grip" title="Drag to reorder"> |
| 1061 | <svg viewBox="0 0 24 24" width="10" height="10" fill="currentColor" aria-hidden="true"> |
| 1062 | <circle cx="9" cy="5" r="1.5"/><circle cx="15" cy="5" r="1.5"/> |
| 1063 | <circle cx="9" cy="12" r="1.5"/><circle cx="15" cy="12" r="1.5"/> |
| 1064 | <circle cx="9" cy="19" r="1.5"/><circle cx="15" cy="19" r="1.5"/> |
| 1065 | </svg> |
| 1066 | </span>`} |
| 1067 | <svg class="f-chevron" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><polyline points="18 15 12 9 6 15"></polyline></svg> |
| 1068 | ${folderIcon} |
| 1069 | <span class="f-name">${esc(folder.name)}</span> |
| 1070 | <span class="f-count">${folder.items.length}</span> |
| 1071 | ${isTrash ? "" : ` |
| 1072 | <button class="f-subfolder" type="button" aria-label="New subfolder" title="New subfolder"> |
| 1073 | <svg viewBox="0 0 24 24" width="11" height="11" fill="none" stroke="currentColor" stroke-width="2" aria-hidden="true"> |
| 1074 | <path d="M3 7a2 2 0 0 1 2-2h4l2 2h8a2 2 0 0 1 2 2v9a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2z"/> |
| 1075 | <path d="M12 11v6M9 14h6"/> |
| 1076 | </svg> |
| 1077 | </button> |
| 1078 | ${isUnsorted ? "" : `<button class="f-del" type="button" aria-label="Delete folder" title="Delete folder"> |
| 1079 | <svg viewBox="0 0 24 24" width="12" height="12" fill="none" stroke="currentColor" stroke-width="2"><polyline points="3 6 5 6 21 6"></polyline><path d="M19 6v14a2 2 0 0 1-2 2H7a2 2 0 0 1-2-2V6m3 0V4a1 1 0 0 1 1-1h4a1 1 0 0 1 1 1v2"></path></svg> |
| 1080 | </button>`} |
| 1081 | `} |
| 1082 | `; |
| 1083 | |
| 1084 | const body = document.createElement("div"); |
| 1085 | body.className = "folder-body"; |
| 1086 | |
| 1087 | const visibleItems = folder.items.filter((id) => trackMatchesSearch(tracks[id])); |
| 1088 | const childFolders = folders.filter((f) => f.parentId === folder.id); |
| 1089 | |
| 1090 | if (catalogSearchQuery && visibleItems.length === 0 && childFolders.length === 0) { |
| 1091 | return null; |
| 1092 | } |
| 1093 | |
| 1094 | if (visibleItems.length === 0 && childFolders.length === 0) { |
| 1095 | body.innerHTML = '<span class="folder-empty">Empty folder</span>'; |
| 1096 | } else { |
| 1097 | for (const id of visibleItems) { |
| 1098 | const item = renderTrackItem(id); |
no test coverage detected