Badge Picker Upgrades

removes the names of each of the badges and turns it into a neat array of columns to allow for you to be able to view more at once, useful for people with more badges

  1. // ==UserScript==
  2. // @name Badge Picker Upgrades
  3. // @namespace https://github.com/encumber/SteamBadgeShowcaseTools/blob/main/BadgePickerUpgrades.userscript.js
  4. // @version 1.1
  5. // @description removes the names of each of the badges and turns it into a neat array of columns to allow for you to be able to view more at once, useful for people with more badges
  6. // @author Nitoned
  7. // @match https://steamcommunity.com/*/*/edit/showcases
  8. // @icon https://www.google.com/s2/favicons?sz=64&domain=steamcommunity.com
  9. // @grant none
  10. // ==/UserScript==
  11.  
  12. (function() {
  13. 'use strict';
  14.  
  15. // Function to dynamically add or remove styles
  16. const styleElement = document.createElement("style");
  17. document.head.appendChild(styleElement);
  18.  
  19. function updateStyles(content) {
  20. let styles = `
  21. /* Shared Boilerplate CSS */
  22. .group_list_results {
  23. display: flex;
  24. flex-wrap: wrap;
  25. gap: 10px;
  26. overflow: auto; /* Enable scrolling for overflow */
  27. max-height: 90%; /* Ensure badges stay within the newmodal bounds */
  28. padding: 10px;
  29. padding-bottom: 50px;
  30. box-sizing: border-box;
  31. }
  32. .group_list_option {
  33. flex: 1 0 calc(100% / var(--columns) - 10px);
  34. box-sizing: border-box;
  35. padding: 5px;
  36. margin: 0;
  37. text-align: center;
  38. }
  39. .badge_icon img {
  40. width: 80px;
  41. height: 80px;
  42. display: block;
  43. margin: 0 auto;
  44. }
  45. .group_list_groupname {
  46. display: none !important;
  47. }
  48. .search-container {
  49. display: flex;
  50. align-items: center;
  51. margin-bottom: 10px;
  52. position: absolute;
  53. top: 25px;
  54. right: calc(5%);
  55. }
  56. .search-container input {
  57. margin-left: 10px;
  58. padding: 5px;
  59. font-size: 14px;
  60. }
  61. .search-container button {
  62. margin-left: 5px;
  63. padding: 5px 10px;
  64. font-size: 14px;
  65. cursor: pointer;
  66. }
  67. .newmodal {
  68. overflow: hidden;
  69. position: relative;
  70. max-height: 100%; /* Restricts badges to fit within the modal */
  71. left: 50px !important;
  72. right: 40px !important;
  73. top: 30px !important;
  74. bottom: 40px !important;
  75. }
  76. `;
  77.  
  78. switch (content) {
  79. case "Choose a badge to feature":
  80. styles += `
  81. .group_list_groupname {
  82. display: none !important;
  83. }
  84. `;
  85. break;
  86.  
  87. case "Select a Group to Feature":
  88. styles += `
  89. .group_list_groupname {
  90. visibility: hidden;
  91. position: absolute;
  92. }
  93. .group_list_option {
  94. padding: 30px !important;
  95. }
  96. text-align: center;
  97. .group_list_option:hover .group_list_groupname {
  98. visibility: visible;
  99. display: block;
  100. position: absolute;
  101. background: rgba(0, 0, 0, 0.75);
  102. color: #fff;
  103. padding: 5px;
  104. border-radius: 4px;
  105. z-index: 10;
  106. white-space: nowrap;
  107. }
  108. .group_list_option .playerAvatar {
  109. width: 48px !important;
  110. height: 48px !important;
  111. }
  112. .playerAvatar img, .friend_block_holder .friend_block_avatar img, .friend_activity .friend_block_avatar img {
  113. width: 48px !important;
  114. height: 48px !important;
  115. }
  116. `;
  117. break;
  118.  
  119. case "Select an achievement to feature":
  120. styles += `
  121. .achievement_list_desc {
  122. visibility: hidden;
  123. position: absolute;
  124. }
  125. .group_list_option:hover .achievement_list_desc {
  126. visibility: visible;
  127. display: block;
  128. position: absolute;
  129. background: rgba(0, 0, 0, 0.75);
  130. color: #fff;
  131. padding: 5px;
  132. border-radius: 4px;
  133. z-index: 10;
  134. white-space: normal;
  135. }
  136. `;
  137. break;
  138.  
  139. case "Select a Game You've Publicly Reviewed":
  140. // No additional styles needed for this context
  141. break;
  142.  
  143. default:
  144. styles = ""; // Unload styles
  145. break;
  146. }
  147.  
  148. styleElement.textContent = styles;
  149. }
  150.  
  151. // Function to manage the addition of search functionality
  152. function addSearchFunctionality(content) {
  153. // Remove search bar for "Select a Game You've Publicly Reviewed"
  154. if (content === "Select a Game You've Publicly Reviewed") return;
  155.  
  156. const existingSearchContainer = document.querySelector(".search-container");
  157. if (existingSearchContainer) return; // Avoid adding duplicate search boxes
  158.  
  159. const titleText = Array.from(document.querySelectorAll(".title_text")).find(
  160. (el) => el.textContent.trim() === content
  161. );
  162.  
  163. if (titleText) {
  164. const searchContainer = document.createElement("div");
  165. searchContainer.className = "search-container";
  166.  
  167. const searchInput = document.createElement("input");
  168. searchInput.type = "text";
  169. searchInput.placeholder = "Search...";
  170.  
  171. const searchButton = document.createElement("button");
  172. searchButton.textContent = "Search";
  173.  
  174. searchContainer.appendChild(document.createTextNode("Search:"));
  175. searchContainer.appendChild(searchInput);
  176. searchContainer.appendChild(searchButton);
  177.  
  178. titleText.parentNode.insertBefore(searchContainer, titleText.nextSibling);
  179.  
  180. const badges = document.querySelectorAll(".group_list_option");
  181.  
  182. const filterBadges = () => {
  183. const query = searchInput.value.toLowerCase().trim();
  184. badges.forEach((badge) => {
  185. const badgeName = badge.getAttribute("data-badge-name") || "";
  186. const groupName = badge.getAttribute("data-group-name") || "";
  187. const achievementDesc = badge.getAttribute("data-achievement-desc") || "";
  188.  
  189. if (
  190. query === "" ||
  191. badgeName.includes(query) ||
  192. groupName.includes(query) ||
  193. achievementDesc.includes(query)
  194. ) {
  195. badge.style.display = "";
  196. } else {
  197. badge.style.display = "none";
  198. }
  199. });
  200. };
  201.  
  202. searchButton.addEventListener("click", filterBadges);
  203. searchInput.addEventListener("input", filterBadges);
  204. }
  205. }
  206.  
  207. // Function to prepare group names and set hover/search functionality
  208. function prepareGroupNames() {
  209. document.querySelectorAll(".group_list_option").forEach((option) => {
  210. const groupNameElement = option.querySelector(".group_list_groupname");
  211. if (groupNameElement) {
  212. const groupName = groupNameElement.textContent.trim();
  213. option.setAttribute("data-group-name", groupName.toLowerCase()); // Add for search
  214. option.setAttribute("title", groupName); // Add hover text to the option itself
  215. }
  216. });
  217. }
  218.  
  219. // Function to prepare badge, group, and achievement descriptions
  220. function prepareBadgeData() {
  221. document.querySelectorAll(".group_list_option").forEach((option) => {
  222. const groupNameElement = option.querySelector(".group_list_groupname");
  223. const badgeIcon = option.querySelector(".badge_icon");
  224. const achievementDescElement = option.querySelector(".achievement_list_desc");
  225.  
  226. if (groupNameElement) {
  227. const groupName = groupNameElement.textContent.trim();
  228. option.setAttribute("data-group-name", groupName.toLowerCase()); // Add for search
  229. option.setAttribute("title", groupName); // Set hover text directly
  230. }
  231.  
  232. if (badgeIcon) {
  233. const badgeName = badgeIcon.getAttribute("title") || "";
  234. option.setAttribute("data-badge-name", badgeName.toLowerCase());
  235. }
  236.  
  237. if (achievementDescElement) {
  238. const achievementDesc = achievementDescElement.innerText.trim(); // Includes children like <b> and <div>
  239. option.setAttribute("data-achievement-desc", achievementDesc.toLowerCase());
  240. }
  241. });
  242. }
  243.  
  244. // MutationObserver to monitor DOM changes
  245. const observer = new MutationObserver((mutationsList) => {
  246. for (const mutation of mutationsList) {
  247. if (
  248. mutation.type === "childList" &&
  249. mutation.addedNodes.length > 0 &&
  250. Array.from(mutation.addedNodes).some((node) =>
  251. node.querySelector
  252. ? node.querySelector(".title_text")
  253. : node.className === "title_text"
  254. )
  255. ) {
  256. const titleText = Array.from(document.querySelectorAll(".title_text")).find(
  257. (el) =>
  258. [
  259. "Choose a badge to feature",
  260. "Select a Game You've Publicly Reviewed",
  261. "Select an achievement to feature",
  262. "Select a Group to Feature",
  263. ].includes(el.textContent.trim())
  264. );
  265.  
  266. if (titleText) {
  267. const content = titleText.textContent.trim();
  268. updateStyles(content); // Update styles based on the title
  269. prepareBadgeData(); // Prepare badge data
  270. if (content === "Select a Group to Feature") {
  271. prepareGroupNames(); // Handle group-specific hover/search immediately
  272. }
  273. addSearchFunctionality(content); // Add search functionality
  274. } else {
  275. // If the modal is no longer present, unload styles and clean up
  276. updateStyles(""); // Unload styles
  277. document.querySelector(".search-container")?.remove();
  278. }
  279. }
  280. }
  281. });
  282.  
  283. // Run the group name preparation immediately on load
  284. if (document.querySelector(".title_text")) {
  285. const content = document.querySelector(".title_text").textContent.trim();
  286. updateStyles(content);
  287. prepareBadgeData();
  288. if (content === "Select a Group to Feature") {
  289. prepareGroupNames();
  290. }
  291. addSearchFunctionality(content);
  292. }
  293.  
  294. // Start observing the document for changes
  295. observer.observe(document.body, { childList: true, subtree: true });
  296.  
  297.  
  298.  
  299.  
  300. })();