- Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathFolderMultiSelectModal.ts
More file actions
Latest commit
130 lines (114 loc) · 5.25 KB
/
Copy pathFolderMultiSelectModal.ts
File metadata and controls
130 lines (114 loc) · 5.25 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
import{App,Modal,TAbstractFile,TFolder}from"obsidian";
/** ──────────────────────────────────────────────────────────────────────────
* FolderMultiSelectModal: searchable multi-select of vault folders
* ────────────────────────────────────────────────────────────────────────── */
exportclassFolderMultiSelectModalextendsModal{
privateallFolders: string[]=[];
privatefiltered: string[]=[];
privateselected=newSet<string>();
privateonDone: (selected: string[])=>void;
privatesearchEl!: HTMLInputElement;
privatelistEl!: HTMLDivElement;
constructor(app: App,preselected: string[],onDone: (selected: string[])=>void){
super(app);
preselected.forEach(p=>this.selected.add(p));
this.onDone=onDone;
}
onOpen(): void{
this.setTitle("Select folders to exclude");
constroot=this.contentEl.createDiv({cls: "folder-select"});
// Search box
constsearchWrap=root.createDiv({cls: "folder-select-search"});
this.searchEl=searchWrap.createEl("input",{
type: "search",
placeholder: "Search folders…",
});
this.searchEl.addEventListener("input",()=>this.applyFilter());
// Scrollable list
this.listEl=root.createDiv({cls: "folder-select-list"});
// Footer actions
constfooter=root.createDiv({cls: "folder-select-footer"});
constselectAllBtn=footer.createEl("button",{text: "Select all (filtered)"});
constclearBtn=footer.createEl("button",{text: "Clear"});
constdoneBtn=footer.createEl("button",{text: "Done"});
selectAllBtn.onclick=()=>{
for(constpofthis.filtered)this.selected.add(p);
this.renderList();
};
clearBtn.onclick=()=>{
this.selected.clear();
this.renderList();
};
doneBtn.onclick=()=>{
this.onDone(Array.from(this.selected).sort());
this.close();
};
// Load folders and render
this.allFolders=getAllFolderPaths(this.app);
this.filtered=[...this.allFolders];
this.renderList();
// Keyboard affordances (Esc to close; Enter to confirm)
this.scope.register([],"Escape",()=>this.close());
this.scope.register([],"Enter",()=>{
this.onDone(Array.from(this.selected).sort());
this.close();
});
// Styles
conststyle=document.createElement("style");
style.textContent=`
.folder-select { display:flex; flex-direction:column; gap: 8px; }
.folder-select-search input { width:100%; padding:6px 8px; }
.folder-select-list { max-height: 320px; overflow:auto; border: 1px solid var(--background-modifier-border); border-radius: 6px; padding:6px; }
.folder-row { display:flex; align-items:center; gap: 8px; padding: 4px 2px; border-radius:4px; }
.folder-row:hover { background: var(--background-modifier-hover); }
.folder-path { font-family: var(--font-monospace); font-size: 12px; opacity: 0.9; }
.folder-select-footer { display:flex; justify-content: space-between; gap:8px; margin-top: 4px; }
.folder-select-footer button { padding: 6px 10px; }
`;
this.contentEl.appendChild(style);
// Focus search by default
setTimeout(()=>this.searchEl.focus(),0);
}
privateapplyFilter(){
constq=this.searchEl.value.toLowerCase().trim();
this.filtered=!q
? [...this.allFolders]
: this.allFolders.filter(p=>p.toLowerCase().includes(q));
this.renderList();
}
privaterenderList(){
this.listEl.empty();
for(constpathofthis.filtered){
constrow=this.listEl.createDiv({cls: "folder-row"});
constcb=row.createEl("input",{type: "checkbox"});
cb.checked=this.selected.has(path);
cb.onchange=()=>{
if(cb.checked)this.selected.add(path);
elsethis.selected.delete(path);
};
row.createSpan({cls: "folder-path",text: path});
// Click row toggles
row.onclick=(e)=>{
// ignore clicks directly on checkbox
if(e.target===cb)return;
cb.checked=!cb.checked;
cb.onchange!(nullasany);
};
}
}
}
/** ──────────────────────────────────────────────────────────────────────────
* Utilities: gather all folder paths
* ────────────────────────────────────────────────────────────────────────── */
functiongetAllFolderPaths(app: App): string[]{
constarr: string[]=[];
constall=app.vault.getAllLoadedFiles();// TAbstractFile[]
for(constafofall){
if(afinstanceofTFolder){
// Use root as ""? Prefer "/" for clarity; skip the root itself.
arr.push(af.path);// path like "Folder/Sub"
}
}
// Ensure deterministic order
returnArray.from(newSet(arr)).sort((a,b)=>a.localeCompare(b));
}