Group add to background folder (#5237) * group add * make button not wide * make single and batch add to folder use the same style popup * prevent stuck UI, allow scrolling, prevent ::after conflicts * extremely large commit, beware * clear everything after performing bulk actions * Unify bulk selection design language --------- Co-authored-by: Cohee <18619528+Cohee1207@users.noreply.github.com>

90892fd2367363ff1211ffbc6656e8cac79d56a2

Lucas Scala <123923688+Vibecoder9000@users.noreply.github.com>

Signed
4 files changed, +352 -48Ignore whitespace
public/css/backgrounds.css+39 -15
@@ -136,19 +136,32 @@
136 outline-offset: 0;136 outline-offset: 0;
137}137}
138138
139.bg_example.locked-background::after {139.bg_example.locked-background::after,
140 content: '\f023';140.bg_example.selected-background::before,
141#Backgrounds.bg-selection-mode #bg_menu_content .bg_example.folder-group-selected::before {
141 font-family: 'Font Awesome 6 Free';142 font-family: 'Font Awesome 6 Free';
142 font-weight: 900;143 font-weight: 900;
143
144 position: absolute;144 position: absolute;
145 z-index: 4;
146 filter: drop-shadow(0 1px 3px rgba(0, 0, 0, 0.8));
147 pointer-events: none;
148}
149
150.bg_example.selected-background::before,
151#Backgrounds.bg-selection-mode #bg_menu_content .bg_example.folder-group-selected::before {
152 content: '\f00c';
153 top: 5px;
154 color: var(--white100);
155 font-size: calc(var(--mainFontSize) * 0.9);
156}
157
158.bg_example.locked-background::after {
159 content: '\f023';
145 bottom: 5px;160 bottom: 5px;
146 right: 5px;161 right: 5px;
147 z-index: 4;
148 color: var(--golden);162 color: var(--golden);
149 filter: drop-shadow(0 1px 2px rgba(0, 0, 0, 0.8));163 filter: drop-shadow(0 1px 2px rgba(0, 0, 0, 0.8));
150 font-size: calc(var(--mainFontSize) * 0.8);164 font-size: calc(var(--mainFontSize) * 0.8);
151 pointer-events: none;
152}165}
153166
154.bg_example:not(.locked-background) .jg-unlock,167.bg_example:not(.locked-background) .jg-unlock,
@@ -162,17 +175,28 @@
162}175}
163176
164.bg_example.selected-background::before {177.bg_example.selected-background::before {
165 content: '\f00c';
166 font-family: 'Font Awesome 6 Free';
167 font-weight: 900;
168 position: absolute;
169 top: 5px;
170 left: 5px;178 left: 5px;
171 z-index: 4;179}
172 color: var(--white100);180
173 filter: drop-shadow(0 1px 3px rgba(0, 0, 0, 0.8));181#bg_selection_mode_button.active {
174 font-size: calc(var(--mainFontSize) * 0.9);182 color: var(--golden);
175 pointer-events: none;183}
184
185#bg_group_select_count {
186 font-weight: 700;
187}
188
189#Backgrounds.bg-selection-mode #bg_menu_content .bg_example .jg-menu {
190 display: none;
191}
192
193#Backgrounds.bg-selection-mode #bg_menu_content .bg_example.folder-group-selected {
194 outline: 2px solid var(--interactable-outline-color);
195 outline-offset: 0;
196}
197
198#Backgrounds.bg-selection-mode #bg_menu_content .bg_example.folder-group-selected::before {
199 right: 5px;
176}200}
177201
178.bg_example .jg-menu {202.bg_example .jg-menu {
public/css/mobile-styles.css+21 -1
@@ -153,10 +153,30 @@
153153
154 #add_background_button_top>span,154 #add_background_button_top>span,
155 #auto_background>span,155 #auto_background>span,
156 #bg_add_folder_button>span {156 #bg_add_folder_button>span,
157 #bg_selection_mode_button>span:not(#bg_group_select_count),
158 #bg_group_add_to_folder_button>span,
159 #bg_folder_remove_selected_button>span {
157 display: none;160 display: none;
158 }161 }
159162
163 #bg_group_select_count:not(:empty) {
164 display: inline !important;
165 }
166
167 .bg_folder_breadcrumb {
168 gap: 5px;
169 }
170
171 .bg_folder_breadcrumb .expander {
172 min-width: 0;
173 }
174
175 #bg_current_folder_name {
176 min-width: 0;
177 flex: 1 1 auto;
178 }
179
160 #extensions_settings,180 #extensions_settings,
161 #extensions_settings2 {181 #extensions_settings2 {
162 width: 100% !important;182 width: 100% !important;
public/index.html+13 -0
@@ -5635,6 +5635,14 @@
5635 <a href="#bg_chat_tab" data-i18n="Chat">Chat</a>5635 <a href="#bg_chat_tab" data-i18n="Chat">Chat</a>
5636 </li>5636 </li>
5637 <div class="heading-controls">5637 <div class="heading-controls">
5638 <button id="bg_group_add_to_folder_button" class="menu_button menu_button_icon" style="display:none;" data-i18n="[title]Add selected backgrounds to folder" title="Add selected backgrounds to folder">
5639 <i class="fa-solid fa-folder-tree"></i>
5640 <span data-i18n="Add to Folder">Add to Folder</span>
5641 </button>
5642 <button id="bg_selection_mode_button" class="menu_button menu_button_icon" data-i18n="[title]Select backgrounds for bulk folder actions" title="Select backgrounds for bulk folder actions">
5643 <i class="fa-solid fa-edit"></i>
5644 <span id="bg_group_select_count" style="display:none;"></span>
5645 </button>
5638 <button id="bg_thumb_zoom_out" class="menu_button menu_button_icon" title="Make thumbnails smaller" data-i18n="[title]Make thumbnails smaller">5646 <button id="bg_thumb_zoom_out" class="menu_button menu_button_icon" title="Make thumbnails smaller" data-i18n="[title]Make thumbnails smaller">
5639 <i class="fa-solid fa-minus"></i>5647 <i class="fa-solid fa-minus"></i>
5640 </button>5648 </button>
@@ -5650,6 +5658,11 @@
5650 <span data-i18n="Back">Back</span>5658 <span data-i18n="Back">Back</span>
5651 </button>5659 </button>
5652 <span id="bg_current_folder_name" class="bg_current_folder_name"></span>5660 <span id="bg_current_folder_name" class="bg_current_folder_name"></span>
5661 <span class="expander"></span>
5662 <button id="bg_folder_remove_selected_button" class="menu_button menu_button_icon" style="display:none;" data-i18n="[title]Remove selected backgrounds from this folder" title="Remove selected backgrounds from this folder">
5663 <i class="fa-solid fa-folder-minus"></i>
5664 <span data-i18n="Remove from Folder">Remove from Folder</span>
5665 </button>
5653 </div>5666 </div>
5654 <div id="bg_folder_grid" class="bg_list bg_folder_grid"></div>5667 <div id="bg_folder_grid" class="bg_list bg_folder_grid"></div>
5655 <div id="bg_menu_content" class="bg_list"></div>5668 <div id="bg_menu_content" class="bg_list"></div>
public/scripts/backgrounds.js+279 -32
@@ -20,6 +20,10 @@ let folderList = [];
20let imageFolderMap = {};20let imageFolderMap = {};
21/** @type {string|null} Currently active folder drill-in, or null for root */21/** @type {string|null} Currently active folder drill-in, or null for root */
22let activeFolderId = null;22let activeFolderId = null;
23/** @type {Set<string>} Selected system backgrounds for group folder actions */
24const selectedSystemBackgroundFiles = new Set();
25/** @type {boolean} Whether click-to-select mode is active for system backgrounds */
26let isBackgroundSelectionMode = false;
2327
24// A single transparent PNG pixel used as a placeholder for errored backgrounds28// A single transparent PNG pixel used as a placeholder for errored backgrounds
25const PNG_PIXEL = 'iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAAC1HAwCAAAAC0lEQVR42mNkYAAAAAYAAjCB0C8AAAAASUVORK5CYII=';29const PNG_PIXEL = 'iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAAC1HAwCAAAAC0lEQVR42mNkYAAAAAYAAjCB0C8AAAAASUVORK5CYII=';
@@ -365,6 +369,11 @@ function removeBackgroundMetadata() {
365function onSelectBackgroundClick(e) {369function onSelectBackgroundClick(e) {
366 const bgFile = $(this).attr('bgfile');370 const bgFile = $(this).attr('bgfile');
367 const isCustom = $(this).attr('custom') === 'true';371 const isCustom = $(this).attr('custom') === 'true';
372 if (isBackgroundSelectionMode && !isCustom) {
373 toggleBackgroundGroupSelection(bgFile);
374 return;
375 }
376
368 const backgroundCssUrl = getUrlParameter(this);377 const backgroundCssUrl = getUrlParameter(this);
369 const bypassGlobalLock = !isCustom && e.shiftKey;378 const bypassGlobalLock = !isCustom && e.shiftKey;
370379
@@ -576,6 +585,7 @@ async function onDeleteBackgroundClick(e) {
576 if (deletedBg) {585 if (deletedBg) {
577 const cachedIdx = cachedSystemBackgrounds.findIndex(img => img.filename === deletedBg);586 const cachedIdx = cachedSystemBackgrounds.findIndex(img => img.filename === deletedBg);
578 if (cachedIdx !== -1) cachedSystemBackgrounds.splice(cachedIdx, 1);587 if (cachedIdx !== -1) cachedSystemBackgrounds.splice(cachedIdx, 1);
588 selectedSystemBackgroundFiles.delete(deletedBg);
579589
580 // Update folder map and clear folder thumbnail if it referenced this image590 // Update folder map and clear folder thumbnail if it referenced this image
581 if (imageFolderMap[deletedBg]) {591 if (imageFolderMap[deletedBg]) {
@@ -605,6 +615,7 @@ async function onDeleteBackgroundClick(e) {
605615
606 highlightLockedBackground();616 highlightLockedBackground();
607 highlightSelectedBackground();617 highlightSelectedBackground();
618 syncGroupSelectionUi();
608 }619 }
609}620}
610621
@@ -652,7 +663,10 @@ function renderSystemBackgrounds(backgrounds) {
652 const container = $('#bg_menu_content');663 const container = $('#bg_menu_content');
653 container.empty();664 container.empty();
654665
655 if (sourceList.length === 0) return;666 if (sourceList.length === 0) {
667 syncGroupSelectionUi();
668 return;
669 }
656670
657 const sortedList = sortBackgrounds(sourceList.map(bg => bg.filename), false);671 const sortedList = sortBackgrounds(sourceList.map(bg => bg.filename), false);
658 const metadataByFilename = new Map(sourceList.map(bg => [bg.filename, bg]));672 const metadataByFilename = new Map(sourceList.map(bg => [bg.filename, bg]));
@@ -663,6 +677,7 @@ function renderSystemBackgrounds(backgrounds) {
663 container.append(thumbnail);677 container.append(thumbnail);
664 });678 });
665679
680 syncGroupSelectionUi();
666 activateLazyLoader();681 activateLazyLoader();
667}682}
668683
@@ -700,6 +715,12 @@ export async function getBackgrounds() {
700 const { images, config } = await response.json();715 const { images, config } = await response.json();
701 Object.assign(THUMBNAIL_CONFIG, config);716 Object.assign(THUMBNAIL_CONFIG, config);
702 cachedSystemBackgrounds = images;717 cachedSystemBackgrounds = images;
718 const existingFiles = new Set(images.map(x => x.filename));
719 for (const selectedFile of selectedSystemBackgroundFiles) {
720 if (!existingFiles.has(selectedFile)) {
721 selectedSystemBackgroundFiles.delete(selectedFile);
722 }
723 }
703724
704 // Load folders first so getFilteredImages() works correctly in folder view725 // Load folders first so getFilteredImages() works correctly in folder view
705 await loadFolders();726 await loadFolders();
@@ -859,6 +880,7 @@ function onFolderDrillIn(folderId) {
859 const folder = folderList.find(f => f.id === folderId);880 const folder = folderList.find(f => f.id === folderId);
860 if (!folder) return;881 if (!folder) return;
861882
883 clearBackgroundGroupSelection();
862 activeFolderId = folderId;884 activeFolderId = folderId;
863 $('#Backgrounds').addClass('in-folder-view');885 $('#Backgrounds').addClass('in-folder-view');
864886
@@ -876,6 +898,7 @@ function onFolderDrillIn(folderId) {
876 * Returns to the root folder overview.898 * Returns to the root folder overview.
877 */899 */
878function onBackToFolders() {900function onBackToFolders() {
901 clearBackgroundGroupSelection();
879 activeFolderId = null;902 activeFolderId = null;
880 $('#Backgrounds').removeClass('in-folder-view');903 $('#Backgrounds').removeClass('in-folder-view');
881904
@@ -890,6 +913,245 @@ function onBackToFolders() {
890}913}
891914
892/**915/**
916 * Refreshes click-to-select and group action UI state.
917 */
918function syncGroupSelectionUi() {
919 const selectedCount = selectedSystemBackgroundFiles.size;
920 const isGlobalTab = getActiveBackgroundTab() === BG_SOURCES.GLOBAL;
921 const showAddButton = isGlobalTab && isBackgroundSelectionMode && selectedCount > 0;
922 const showRemoveFromCurrentFolderButton = isGlobalTab && Boolean(activeFolderId) && isBackgroundSelectionMode && selectedCount > 0;
923
924 $('#Backgrounds').toggleClass('bg-selection-mode', isBackgroundSelectionMode);
925 $('#bg_selection_mode_button').toggleClass('active', isBackgroundSelectionMode);
926 $('#bg_group_select_count').text(selectedCount > 0 ? ` (${selectedCount})` : '').toggle(selectedCount > 0);
927
928 $('#bg_group_add_to_folder_button').toggle(showAddButton);
929 $('#bg_folder_remove_selected_button').toggle(showRemoveFromCurrentFolderButton);
930
931 $('#bg_menu_content .bg_example').each(function () {
932 const bgFile = String($(this).attr('bgfile') || '');
933 $(this).toggleClass('folder-group-selected', selectedSystemBackgroundFiles.has(bgFile));
934 });
935}
936
937/**
938 * Enables/disables click-to-select mode for system backgrounds.
939 * @param {boolean} enabled
940 */
941function setBackgroundSelectionMode(enabled) {
942 isBackgroundSelectionMode = enabled;
943 if (!enabled) {
944 selectedSystemBackgroundFiles.clear();
945 }
946 // Clear any open mobile menus
947 $('#bg_menu_content .bg_example.mobile-menu-open').removeClass('mobile-menu-open');
948 syncGroupSelectionUi();
949}
950
951/**
952 * Toggles selected state of a system background for group folder actions.
953 * @param {string} bgFile
954 */
955function toggleBackgroundGroupSelection(bgFile) {
956 if (!bgFile) return;
957 if (selectedSystemBackgroundFiles.has(bgFile)) {
958 selectedSystemBackgroundFiles.delete(bgFile);
959 } else {
960 selectedSystemBackgroundFiles.add(bgFile);
961 }
962 syncGroupSelectionUi();
963}
964
965/**
966 * Clears all selected system backgrounds for group folder actions.
967 */
968function clearBackgroundGroupSelection() {
969 selectedSystemBackgroundFiles.clear();
970 syncGroupSelectionUi();
971}
972
973/**
974 * Updates selection/folder action control visibility for the active tab.
975 */
976function updateGroupFolderControlsVisibility() {
977 const isGlobalTab = getActiveBackgroundTab() === BG_SOURCES.GLOBAL;
978 $('#bg_selection_mode_button').toggle(isGlobalTab);
979
980 if (!isGlobalTab && isBackgroundSelectionMode) {
981 setBackgroundSelectionMode(false);
982 return;
983 }
984 syncGroupSelectionUi();
985}
986
987/**
988 * Shows a folder selection popup and returns the selected folder id.
989 * @param {string} headingText
990 * @returns {Promise<string[]|null>} Array of selected folder IDs, or null if cancelled
991 */
992async function selectFoldersForGroupAction(headingText) {
993 if (folderList.length === 0) {
994 toastr.info(t`Create a folder first`);
995 return null;
996 }
997
998 const contentEl = document.createElement('div');
999 const heading = document.createElement('h3');
1000 heading.textContent = headingText;
1001 contentEl.appendChild(heading);
1002
1003 for (const folder of folderList) {
1004 const label = document.createElement('label');
1005 label.className = 'checkbox_label flexGap5';
1006 label.style.margin = '4px 0';
1007
1008 const checkbox = document.createElement('input');
1009 checkbox.type = 'checkbox';
1010 checkbox.dataset.folderId = folder.id;
1011
1012 const span = document.createElement('span');
1013 span.textContent = folder.name;
1014
1015 label.appendChild(checkbox);
1016 label.appendChild(span);
1017 contentEl.appendChild(label);
1018 }
1019
1020 const content = $(contentEl);
1021 const result = await callGenericPopup(content, POPUP_TYPE.CONFIRM, '', {
1022 okButton: t`Apply`,
1023 cancelButton: t`Cancel`,
1024 allowVerticalScrolling: true,
1025 leftAlign: true,
1026 });
1027 if (!result) return null;
1028
1029 const selectedIds = [];
1030 content.find('input[type="checkbox"]:checked').each(function () {
1031 selectedIds.push($(this).data('folderId'));
1032 });
1033 return selectedIds.length > 0 ? selectedIds : null;
1034}
1035
1036/**
1037 * Sends a folder assign/unassign request and updates local imageFolderMap state.
1038 * @param {string[]} bgFiles - Background filenames to update
1039 * @param {string} folderId - Target folder ID
1040 * @param {boolean} isRemove - Whether to remove (unassign) or add (assign)
1041 */
1042async function updateFolderAssignments(bgFiles, folderId, isRemove) {
1043 const paths = bgFiles.map(getBackgroundRelativePath);
1044 const endpoint = isRemove ? '/api/image-metadata/folders/unassign' : '/api/image-metadata/folders/assign';
1045
1046 const response = await fetch(endpoint, {
1047 method: 'POST',
1048 headers: getRequestHeaders(),
1049 body: JSON.stringify({ id: folderId, paths }),
1050 });
1051
1052 if (!response.ok) {
1053 throw new Error(`Folder ${isRemove ? 'unassign' : 'assign'} failed: ${response.status}`);
1054 }
1055
1056 for (const bgFile of bgFiles) {
1057 const currentFolderIds = imageFolderMap[bgFile] || [];
1058 if (isRemove) {
1059 const nextFolderIds = currentFolderIds.filter(id => id !== folderId);
1060 if (nextFolderIds.length > 0) {
1061 imageFolderMap[bgFile] = nextFolderIds;
1062 } else {
1063 delete imageFolderMap[bgFile];
1064 }
1065 } else if (!currentFolderIds.includes(folderId)) {
1066 imageFolderMap[bgFile] = [...currentFolderIds, folderId];
1067 }
1068 }
1069}
1070
1071/**
1072 * Adds selected system backgrounds to a chosen folder.
1073 */
1074async function onAddSelectedToFolder() {
1075 if (getActiveBackgroundTab() !== BG_SOURCES.GLOBAL) {
1076 toastr.warning(t`Folder actions are only available in the Global tab`);
1077 return;
1078 }
1079
1080 const bgFiles = Array.from(selectedSystemBackgroundFiles);
1081 if (bgFiles.length === 0) {
1082 toastr.info(t`Select one or more backgrounds first`);
1083 return;
1084 }
1085
1086 const folderIds = await selectFoldersForGroupAction(t`Add selected backgrounds to folders`);
1087 if (!folderIds) return;
1088
1089 try {
1090 let totalAdded = 0;
1091 for (const folderId of folderIds) {
1092 const actionableBgFiles = bgFiles.filter(bgFile => {
1093 const currentFolderIds = imageFolderMap[bgFile] || [];
1094 return !currentFolderIds.includes(folderId);
1095 });
1096 if (actionableBgFiles.length > 0) {
1097 await updateFolderAssignments(actionableBgFiles, folderId, false);
1098 totalAdded += actionableBgFiles.length;
1099 }
1100 }
1101
1102 renderFolderGrid();
1103
1104 if (activeFolderId) {
1105 renderSystemBackgrounds(getFilteredImages());
1106 highlightSelectedBackground();
1107 }
1108
1109 setBackgroundSelectionMode(false);
1110 if (totalAdded > 0) {
1111 toastr.success(t`Added backgrounds to ${folderIds.length} folder(s)`);
1112 } else {
1113 toastr.info(t`Selected backgrounds are already in the chosen folders`);
1114 }
1115 } catch (error) {
1116 console.error('Error adding selected backgrounds to folder:', error);
1117 toastr.error(t`Failed to update folder assignment`);
1118 }
1119}
1120
1121/**
1122 * Removes selected system backgrounds from the currently drilled-in folder.
1123 */
1124async function onRemoveSelectedFromCurrentFolder() {
1125 if (getActiveBackgroundTab() !== BG_SOURCES.GLOBAL) {
1126 toastr.warning(t`Folder actions are only available in the Global tab`);
1127 return;
1128 }
1129
1130 if (!activeFolderId) {
1131 toastr.info(t`Open a folder first`);
1132 return;
1133 }
1134
1135 const bgFiles = Array.from(selectedSystemBackgroundFiles);
1136 if (bgFiles.length === 0) {
1137 toastr.info(t`Select one or more backgrounds first`);
1138 return;
1139 }
1140
1141 try {
1142 await updateFolderAssignments(bgFiles, activeFolderId, true);
1143 renderFolderGrid();
1144 renderSystemBackgrounds(getFilteredImages());
1145 highlightSelectedBackground();
1146 setBackgroundSelectionMode(false);
1147 toastr.success(t`Removed ${bgFiles.length} background(s) from folder`);
1148 } catch (error) {
1149 console.error('Error removing selected backgrounds from current folder:', error);
1150 toastr.error(t`Failed to update folder assignment`);
1151 }
1152}
1153
1154/**
893 * Creates a new folder via API.1155 * Creates a new folder via API.
894 */1156 */
895async function onCreateFolder() {1157async function onCreateFolder() {
@@ -1037,40 +1299,12 @@ async function onAssignToFolder(bgFile) {
1037 if (!isChecked && wasChecked) toUnassign.push(fid);1299 if (!isChecked && wasChecked) toUnassign.push(fid);
1038 });1300 });
10391301
1040 const relativePath = getBackgroundRelativePath(bgFile);
1041
1042 try {1302 try {
1043 for (const fid of toAssign) {1303 for (const fid of toAssign) {
1044 const resp = await fetch('/api/image-metadata/folders/assign', {1304 await updateFolderAssignments([bgFile], fid, false);
1045 method: 'POST',
1046 headers: getRequestHeaders(),
1047 body: JSON.stringify({ id: fid, paths: [relativePath] }),
1048 });
1049 if (!resp.ok) throw new Error(`Assign to folder ${fid} failed: ${resp.status}`);
1050 }1305 }
1051 for (const fid of toUnassign) {1306 for (const fid of toUnassign) {
1052 const resp = await fetch('/api/image-metadata/folders/unassign', {1307 await updateFolderAssignments([bgFile], fid, true);
1053 method: 'POST',
1054 headers: getRequestHeaders(),
1055 body: JSON.stringify({ id: fid, paths: [relativePath] }),
1056 });
1057 if (!resp.ok) throw new Error(`Unassign from folder ${fid} failed: ${resp.status}`);
1058 }
1059
1060 // Update local state
1061 const newFolderIds = folderList
1062 .filter(f => {
1063 const wasIn = currentFolderIds.includes(f.id);
1064 if (toAssign.includes(f.id)) return true;
1065 if (toUnassign.includes(f.id)) return false;
1066 return wasIn;
1067 })
1068 .map(f => f.id);
1069
1070 if (newFolderIds.length > 0) {
1071 imageFolderMap[bgFile] = newFolderIds;
1072 } else {
1073 delete imageFolderMap[bgFile];
1074 }1308 }
10751309
1076 renderFolderGrid();1310 renderFolderGrid();
@@ -1449,7 +1683,11 @@ const debouncedOnBackgroundFilterInput = debounce(onBackgroundFilterInput, debou
1449 * @returns {BG_SOURCES} Active background tab source1683 * @returns {BG_SOURCES} Active background tab source
1450 */1684 */
1451export function getActiveBackgroundTab() {1685export function getActiveBackgroundTab() {
1452 return $('#bg_tabs').tabs('option', 'active');1686 const tabs = $('#bg_tabs');
1687 if (!tabs.length || !tabs.data('ui-tabs')) {
1688 return BG_SOURCES.GLOBAL;
1689 }
1690 return tabs.tabs('option', 'active');
1453}1691}
14541692
1455export function initBackgrounds() {1693export function initBackgrounds() {
@@ -1511,6 +1749,9 @@ export function initBackgrounds() {
1511 })1749 })
1512 .off('click', '.jg-button').on('click', '.jg-button', function (e) {1750 .off('click', '.jg-button').on('click', '.jg-button', function (e) {
1513 e.stopPropagation();1751 e.stopPropagation();
1752 if (isBackgroundSelectionMode && $(this).closest('#bg_menu_content').length) {
1753 return;
1754 }
1514 const action = $(this).data('action');1755 const action = $(this).data('action');
15151756
1516 switch (action) {1757 switch (action) {
@@ -1553,6 +1794,9 @@ export function initBackgrounds() {
1553 applyThumbnailColumns(background_settings.thumbnailColumns + 1);1794 applyThumbnailColumns(background_settings.thumbnailColumns + 1);
1554 });1795 });
1555 $('#auto_background').on('click', autoBackgroundCommand);1796 $('#auto_background').on('click', autoBackgroundCommand);
1797 $('#bg_selection_mode_button').on('click', () => setBackgroundSelectionMode(!isBackgroundSelectionMode));
1798 $('#bg_group_add_to_folder_button').on('click', onAddSelectedToFolder);
1799 $('#bg_folder_remove_selected_button').on('click', onRemoveSelectedFromCurrentFolder);
1556 $('#add_bg_button').on('change', (e) => onBackgroundUploadSelected(e.originalEvent));1800 $('#add_bg_button').on('change', (e) => onBackgroundUploadSelected(e.originalEvent));
1557 $('#bg-filter').on('input', () => debouncedOnBackgroundFilterInput());1801 $('#bg-filter').on('input', () => debouncedOnBackgroundFilterInput());
1558 $('#bg-sort').on('change', function () {1802 $('#bg-sort').on('change', function () {
@@ -1615,4 +1859,7 @@ export function initBackgrounds() {
1615 });1859 });
16161860
1617 $('#bg_tabs').tabs();1861 $('#bg_tabs').tabs();
1862 $('#bg_tabs').on('tabsactivate', () => updateGroupFolderControlsVisibility());
1863 updateGroupFolderControlsVisibility();
1864 syncGroupSelectionUi();
1618}1865}