touch and sort
Wed Jul 23 2025 17:00:31 GMT+0000 (Coordinated Universal Time)
Saved by @touchSort
```dataviewjs class PersistentData { static #constructKey = Symbol() static #instance static _dataFilePath static _defaultData _dataFilePath _defaultData _data constructor(constructKey, dataFilePath, defaultData) { if (constructKey !== PersistentData.#constructKey) throw new Error('constructor is private') this._dataFilePath = dataFilePath this._defaultData = defaultData } static setConfig(dataFilePath, dafaultData){ this._dataFilePath = dataFilePath this._defaultData = dafaultData } static async getInstance(){ if (!this.#instance){ this.#instance = new PersistentData( this.#constructKey, this._dataFilePath, this._defaultData ) await this.#instance.initialize() } return this.#instance } async initialize(){ const exists = await app.vault.adapter.exists(this._dataFilePath) if (exists) { const str = await app.vault.adapter.read(this._dataFilePath) const data = JSON.parse(str) // 保存されているデータとdefaultDataのキーが不一致の場合、データの仕様が変わったと判断し、保存データをdefaultDataにリセットする if (this._haveSameKeys(data, this._defaultData)){ this._data = data return } } this._data = this._defaultData await this.saveData() } getData(key){ return this._data[key] } async setData(key, value){ this._data[key] = value await this.saveData() } async saveData(){ await app.vault.adapter.write(this._dataFilePath, this._toJSON(this._data)) } _haveSameKeys(obj1, obj2) { const keys1 = Object.keys(obj1).sort() const keys2 = Object.keys(obj2).sort() return keys1.length === keys2.length && keys1.every((key, index) => key === keys2[index]) } _toJSON(obj){ return JSON.stringify(obj, null, 2) } } function debounce(func, wait) { let timeout = null return function(...args) { if (timeout) clearTimeout(timeout) timeout = setTimeout(() => { func(...args) timeout = null }, wait) } } async function updateList() { listContainer.innerHTML = '' const daysBack = selectDays.value const now = dv.date('now') let pages const startDate = now.minus({ days: parseInt(daysBack) - 1 }).startOf('day') pages = dv.pages() .where(p => { const mtime = dv.date(p.file.mtime) return mtime >= startDate && mtime <= now }) if (searchString) pages = pages.where(p => p.file.name.toLowerCase().includes(searchString.toLowerCase())) const folderCounts = {} pages.forEach(page => { const folder = page.file.folder || '' folderCounts[folder] = (folderCounts[folder] || 0) + 1 }) const folders = Array.from(pages.file.folder.distinct()).sort() folders.unshift('すべてのフォルダ') if (!folders.includes(currentFolder)){ currentFolder = 'すべてのフォルダ' await persistentData.setData('currentFolder', currentFolder) } selectFolder.innerHTML = '' folders.forEach(folder => { const count = folder === 'すべてのフォルダ' ? pages.length : (folderCounts[folder] || 0) const displayText = folder === 'すべてのフォルダ' ? `${folder} (${count})` : `${folder} (${count})` const option = dv.el('option', displayText, { container: selectFolder }) option.value = folder if (folder === currentFolder) option.selected = true }) let filteredPages = pages if (currentFolder !== 'すべてのフォルダ') filteredPages = pages.where(p => p.file.folder === currentFolder) const ul = dv.el('ul', '', { container: listContainer }) filteredPages .sort(p => p.file.mtime, 'desc') .forEach((page) => { const li = dv.el('li', '', { container: ul }) const a = dv.el('a', page.file.name, { container: li }) a.addEventListener('click', () => { app.workspace.openLinkText(page.file.path, '', true) }) }) } const config = { persistentData: { // 永続データ保存ファイルのVault内パス // 親ディレクトリは前もって作成しておく dataFilePath: '/Scripts/Dataview/PersistentData/touchSort.json', // 永続データのデフォルト値 defaultData: {selectDaysValue: 10, currentFolder: 'すべてのフォルダ', searchString: ''} }, // 日数の選択肢 selectDaysOptions: [1, 2, 3, 5, 7, 10, 14, 20, 30, 60] } PersistentData.setConfig( config.persistentData.dataFilePath, config.persistentData.defaultData ) const persistentData = await PersistentData.getInstance() let currentFolder = persistentData.getData('currentFolder') let searchString = persistentData.getData('searchString') const selectDays = dv.el('select', '') const options = config.selectDaysOptions options.forEach(option => { const opt = dv.el('option', `${option}日`, { container: selectDays }) opt.value = option }) const selectFolder = dv.el('select', '') const searchInput = dv.el('input', '', { attr: { type: 'text', placeholder: '検索', value: searchString} }) const listContainer = dv.el('div', '') selectDays.addEventListener('change', async () => { updateList() await persistentData.setData('selectDaysValue', selectDays.value) }) selectFolder.addEventListener('change', async () => { currentFolder = selectFolder.value updateList() await persistentData.setData('currentFolder', currentFolder) }) searchInput.addEventListener('input', debounce(async () => { searchString = searchInput.value.trim() updateList() await persistentData.setData('searchString', searchString) }, 1000)) selectDays.value = persistentData.getData('selectDaysValue') selectFolder.value = currentFolder updateList() ```
Comments