// ==UserScript== // @name SUUMO物件非表示マネージャ 🏠 // @name:ja SUUMO物件非表示マネージャ 🏠 // @name:en SUUMO Hidden Property Manager 🏠 // @version 3.7.1 // @description SUUMOの検索結果(建物ごとに表示)で「非表示」ボタンから不要な物件を隠せる!モーダルUIから復活も簡単。保存はローカル。 // @description:ja SUUMOの検索結果(建物ごとに表示)で「非表示」ボタンから不要な物件を隠せる!モーダルUIから復活も簡単。保存はローカル。 // @description:en Hide unwanted listings in SUUMO's grouped-by-building search results! Restore via modal UI. Data saved locally. // @namespace https://github.com/koyasi777/suumo-hidden-property-manager // @author koyasi777 // @match https://suumo.jp/jj/chintai/ichiran/FR* // @grant GM_addStyle // @run-at document-idle // @license MIT // @homepageURL https://github.com/koyasi777/suumo-hidden-property-manager // @supportURL https://github.com/koyasi777/suumo-hidden-property-manager/issues // @icon https://suumo.jp/front/img/favicon.ico // ==/UserScript== (function() { 'use strict'; const STORAGE_KEY = 'suumoHiddenProperties'; const PROPERTY_LI_PREFIX = 'property-li-'; console.log('SUUMO非表示スクリプト: 実行開始'); // --- データ管理ロジック (変更なし) --- const storage = { get: () => JSON.parse(localStorage.getItem(STORAGE_KEY) || '[]'), save: (data) => localStorage.setItem(STORAGE_KEY, JSON.stringify(data)), add: (property) => { const properties = storage.get(); if (!properties.some(p => p.id === property.id)) { properties.push(property); storage.save(properties); } }, remove: (propertyId) => { let properties = storage.get(); properties = properties.filter(p => p.id !== propertyId); storage.save(properties); }, clear: () => localStorage.removeItem(STORAGE_KEY) }; // --- UI/DOM操作 --- const ui = { injectCSS: () => { GM_addStyle(` /* ページスクロールロック用クラス */ body.hide-modal-open { overflow: hidden; } .suumo-control-button { padding: 8px 14px; color: white !important; border: none; cursor: pointer; border-radius: 4px; font-size: 14px; font-weight: bold; font-family: "メイリオ", Meiryo, "ヒラギノ角ゴ Pro W3", "Hiragino Kaku Gothic Pro", "MS Pゴシック", sans-serif; line-height: 1.2; text-decoration: none !important; display: inline-block; white-space: nowrap; box-shadow: 0 2px 4px rgba(0,0,0,0.15); transition: all 0.15s ease-in-out; } .suumo-control-button:hover { transform: translateY(-1px); box-shadow: 0 4px 8px rgba(0,0,0,0.2); filter: brightness(1.1); } .suumo-control-button:active { transform: translateY(1px); box-shadow: 0 1px 2px rgba(0,0,0,0.2); filter: brightness(0.95); } .suumo-control-button--small { padding: 5px 10px; font-size: 12px; font-weight: normal; } .hide-modal-overlay { position: fixed; top: 0; left: 0; width: 100%; height: 100%; background: rgba(0,0,0,0.7); z-index: 9998; display: none; } .hide-modal-content { position: fixed; top: 50%; left: 50%; transform: translate(-50%, -50%); background: #fff; z-index: 9999; padding: 20px 30px; border-radius: 8px; width: 90%; max-width: 600px; max-height: 80vh; box-shadow: 0 5px 15px rgba(0,0,0,0.3); display: none; flex-direction: column; overflow: hidden; } .hide-modal-header { display: flex; justify-content: flex-start; align-items: center; flex-shrink: 0; border-bottom: 1px solid #ccc; padding-bottom: 10px; } .hide-modal-header h2 { margin: 0; font-family: "メイリオ", Meiryo, sans-serif; } .hide-modal-header .suumo-control-button { margin-left: 15px; } .hide-modal-close { position: absolute; top: 15px; right: 20px; font-size: 24px; font-weight: bold; cursor: pointer; color: #aaa; z-index: 10; } .hide-modal-body { flex-grow: 1; overflow-y: auto; margin-top: 15px; min-height: 0; } .hidden-property-list { list-style: none; padding: 0; } .hidden-property-list li { display: flex; justify-content: space-between; align-items: center; padding: 10px; border-bottom: 1px solid #eee; } .hidden-property-info a { text-decoration: none; color: #0073e6; font-weight: bold; font-family: "メイリオ", Meiryo, sans-serif;} .hidden-property-info span { display: block; font-size: 0.9em; color: #555; font-family: "メイリオ", Meiryo, sans-serif;} .inquiry.inquiry--top { display: flex; justify-content: space-between; align-items: center; padding-right: 10px; } `); }, createControls: () => { if (document.getElementById('suumo-hide-controls')) return; const targetParent = document.querySelector('.inquiry.inquiry--top'); if (!targetParent) return; const manageButton = document.createElement('button'); manageButton.type = 'button'; manageButton.id = 'suumo-hide-controls'; manageButton.textContent = '非表示リスト管理'; manageButton.className = 'suumo-control-button'; manageButton.style.backgroundColor = '#5bc0de'; manageButton.addEventListener('click', () => ui.toggleModal(true)); targetParent.appendChild(manageButton); }, createModal: () => { if (document.getElementById('hide-modal')) return; document.body.insertAdjacentHTML('beforeend', `
`); document.getElementById('hide-modal-overlay').addEventListener('click', () => ui.toggleModal(false)); document.querySelector('#hide-modal-content .hide-modal-close').addEventListener('click', () => ui.toggleModal(false)); const restoreAllBtn = document.getElementById('restore-all-btn'); restoreAllBtn.type = 'button'; restoreAllBtn.addEventListener('click', () => { if (confirm('非表示中の全ての物件を復活させますか?')) { const hiddenProperties = storage.get(); if(hiddenProperties.length === 0) { alert('非表示の物件はありません。'); return; } hiddenProperties.forEach(p => ui.restoreProperty(p.id)); storage.clear(); ui.populateModal(); alert(`${hiddenProperties.length}件の物件を全て復活させました。`); } }); }, populateModal: () => { const listElement = document.getElementById('hidden-property-list'); const hiddenProperties = storage.get(); listElement.innerHTML = ''; if (hiddenProperties.length === 0) { listElement.innerHTML = '