|
60 | 60 | }, |
61 | 61 |
|
62 | 62 | filteredItems() { |
| 63 | + let items |
63 | 64 | if (this.filter === "") { |
64 | | - return this.orderedItems |
| 65 | + items = this.orderedItems |
| 66 | + |
65 | 67 | } else { |
66 | 68 | const searchResults = fuzzysort.go(this.filter, this.items, { |
67 | 69 | keys: ["name", "folder"], |
68 | 70 | }) |
69 | | - return searchResults.map((result) => { |
| 71 | + items = searchResults.map((result) => { |
70 | 72 | const obj = {...result.obj} |
71 | 73 | const nameHighlight = result[0].highlight("<b>", "</b>") |
72 | 74 | const folderHighlight = result[1].highlight("<b>", "</b>") |
|
75 | 77 | return obj |
76 | 78 | }) |
77 | 79 | } |
| 80 | + |
| 81 | + const newNoteItem = { |
| 82 | + name: "Create new…", |
| 83 | + createNew:true, |
| 84 | + } |
| 85 | + return [ |
| 86 | + ...items, |
| 87 | + newNoteItem, |
| 88 | + ] |
78 | 89 | }, |
79 | 90 | }, |
80 | 91 |
|
|
83 | 94 | "updateNotes", |
84 | 95 | "editNote", |
85 | 96 | "deleteNote", |
| 97 | + "openCreateNote", |
86 | 98 | ]), |
87 | 99 |
|
88 | 100 | buildItems() { |
|
99 | 111 |
|
100 | 112 | onKeydown(event) { |
101 | 113 | if (event.key === "Escape") { |
102 | | - console.log("escape") |
103 | 114 | event.preventDefault() |
104 | 115 | if (this.actionButton !== 0) { |
105 | 116 | this.hideActionButtons() |
|
112 | 123 | if (this.filteredItems.length === 0) { |
113 | 124 | return |
114 | 125 | } |
115 | | -
|
116 | | - const path = this.filteredItems[this.selected].path |
| 126 | + |
| 127 | + const item = this.filteredItems[this.selected] |
117 | 128 | if (event.key === "ArrowDown") { |
118 | 129 | if (this.selected === this.filteredItems.length - 1) { |
119 | 130 | this.selected = 0 |
120 | 131 | } else { |
121 | 132 | this.selected = Math.min(this.selected + 1, this.filteredItems.length - 1) |
122 | 133 | } |
123 | 134 | event.preventDefault() |
124 | | - if (this.selected === this.filteredItems.length - 1) { |
125 | | - this.$refs.container.scrollIntoView({block: "end"}) |
126 | | - } else { |
127 | | - this.$refs.item[this.selected].scrollIntoView({block: "nearest"}) |
128 | | - } |
| 135 | + this.$nextTick(() => { |
| 136 | + this.$refs.container.querySelector(".selected").scrollIntoView({block: "nearest"}) |
| 137 | + }) |
129 | 138 | this.actionButton = 0 |
130 | 139 | } else if (event.key === "ArrowUp") { |
131 | 140 | if (this.selected === 0) { |
|
134 | 143 | this.selected = Math.max(this.selected - 1, 0) |
135 | 144 | } |
136 | 145 | event.preventDefault() |
137 | | - if (this.selected === 0) { |
138 | | - this.$refs.container.scrollIntoView({block: "start"}) |
139 | | - } else { |
140 | | - this.$refs.item[this.selected].scrollIntoView({block: "nearest"}) |
141 | | - } |
| 146 | + this.$nextTick(() => { |
| 147 | + this.$refs.container.querySelector(".selected").scrollIntoView({block: "nearest"}) |
| 148 | + }) |
142 | 149 | this.actionButton = 0 |
143 | | - } else if (event.key === "ArrowRight" && path !== SCRATCH_FILE_NAME) { |
| 150 | + } else if (event.key === "ArrowRight" && this.itemHasActionButtons(item)) { |
144 | 151 | event.preventDefault() |
145 | 152 | this.actionButton = Math.min(2, this.actionButton + 1) |
146 | | - } else if (event.key === "ArrowLeft" && path !== SCRATCH_FILE_NAME) { |
| 153 | + } else if (event.key === "ArrowLeft" && this.itemHasActionButtons(item)) { |
147 | 154 | event.preventDefault() |
148 | 155 | this.actionButton = Math.max(0, this.actionButton - 1) |
149 | 156 | this.deleteConfirm = false |
150 | 157 | } else if (event.key === "Enter") { |
151 | 158 | event.preventDefault() |
152 | | - if (this.actionButton === 1) { |
153 | | - console.log("edit file:", path) |
154 | | - this.editNote(path) |
| 159 | + if (item.createNew) { |
| 160 | + if (this.filteredItems.length === 1) { |
| 161 | + this.openCreateNote("new", this.filter) |
| 162 | + } else { |
| 163 | + this.openCreateNote("new", "") |
| 164 | + } |
| 165 | + } else if (this.actionButton === 1) { |
| 166 | + //console.log("edit file:", path) |
| 167 | + this.editNote(item.path) |
155 | 168 | } else if (this.actionButton === 2) { |
156 | | - this.deleteConfirmNote(path) |
| 169 | + this.deleteConfirmNote(item.path) |
157 | 170 | } else { |
158 | | - this.selectItem(path) |
| 171 | + this.selectItem(item.path) |
159 | 172 | } |
160 | 173 | } |
161 | 174 | }, |
|
164 | 177 | this.$emit("openNote", path) |
165 | 178 | }, |
166 | 179 |
|
| 180 | + itemHasActionButtons(item) { |
| 181 | + return !item.createNew && item.path !== SCRATCH_FILE_NAME |
| 182 | + }, |
| 183 | +
|
167 | 184 | onInput(event) { |
168 | 185 | // reset selection |
169 | 186 | this.selected = 0 |
|
178 | 195 | |
179 | 196 | getItemClass(item, idx) { |
180 | 197 | return { |
| 198 | + "item": true, |
181 | 199 | "selected": idx === this.selected, |
182 | 200 | "action-buttons-visible": this.actionButton > 0, |
183 | 201 | "scratch": item.scratch, |
| 202 | + "new-note": item.createNew, |
184 | 203 | } |
185 | 204 | }, |
186 | 205 |
|
|
198 | 217 |
|
199 | 218 | async deleteConfirmNote(path) { |
200 | 219 | if (this.deleteConfirm) { |
201 | | - console.log("delete file:", path) |
| 220 | + //console.log("delete file:", path) |
202 | 221 | await this.deleteNote(path) |
203 | 222 | this.hideActionButtons() |
204 | 223 | this.buildItems() |
|
214 | 233 | </script> |
215 | 234 |
|
216 | 235 | <template> |
217 | | - <div class="scroller"> |
218 | | - <form class="note-selector" tabindex="-1" @focusout="onFocusOut" ref="container"> |
| 236 | + <form class="note-selector" tabindex="-1" @focusout="onFocusOut" ref="container"> |
| 237 | + <div class="input-container"> |
219 | 238 | <input |
220 | 239 | type="text" |
221 | 240 | ref="input" |
|
224 | 243 | v-model="filter" |
225 | 244 | autocomplete="off" |
226 | 245 | /> |
227 | | - <ul class="items"> |
228 | | - <li |
| 246 | + </div> |
| 247 | + <div class="scroller"> |
| 248 | + <ul class="items" ref="itemsContainer"> |
| 249 | + <template |
229 | 250 | v-for="item, idx in filteredItems" |
230 | 251 | :key="item.path" |
231 | | - :class="getItemClass(item, idx)" |
232 | | - @click="selectItem(item.path)" |
233 | | - ref="item" |
234 | 252 | > |
235 | | - <span class="name" v-html="item.name" /> |
236 | | - <span class="path" v-html="item.folder" /> |
237 | | - <span :class="{'action-buttons':true, 'visible':actionButton > 0 && idx === selected}"> |
238 | | - <button |
239 | | - v-if="actionButton > 0 && idx === selected" |
240 | | - :class="{'selected':actionButton === 1}" |
241 | | - @click.stop.prevent="editNote(item.path)" |
242 | | - >Edit</button> |
243 | | - <button |
244 | | - v-if="actionButton > 0 && idx === selected" |
245 | | - :class="{'delete':true, 'selected':actionButton === 2, 'confirm':deleteConfirm}" |
246 | | - @click.stop.prevent="deleteConfirmNote(item.path)" |
247 | | - > |
248 | | - <template v-if="deleteConfirm"> |
249 | | - Really Delete? |
250 | | - </template> |
251 | | - <template v-else> |
252 | | - Delete |
253 | | - </template> |
254 | | - </button> |
255 | | - <button |
256 | | - class="show-actions" |
257 | | - v-if="item.path !== SCRATCH_FILE_NAME && (actionButton === 0 || idx !== selected)" |
258 | | - @click.stop.prevent="showActionButtons(idx)" |
259 | | - ></button> |
260 | | - </span> |
261 | | - </li> |
| 253 | + <li v-if="item.createNew" class="line-separator"></li> |
| 254 | + <li |
| 255 | + :class="getItemClass(item, idx)" |
| 256 | + @click="selectItem(item.path)" |
| 257 | + ref="item" |
| 258 | + > |
| 259 | + <span class="name" v-html="item.name" /> |
| 260 | + <span class="path" v-html="item.folder" /> |
| 261 | + <span :class="{'action-buttons':true, 'visible':actionButton > 0 && idx === selected}"> |
| 262 | + <button |
| 263 | + v-if="actionButton > 0 && idx === selected" |
| 264 | + :class="{'selected':actionButton === 1}" |
| 265 | + @click.stop.prevent="editNote(item.path)" |
| 266 | + >Edit</button> |
| 267 | + <button |
| 268 | + v-if="actionButton > 0 && idx === selected" |
| 269 | + :class="{'delete':true, 'selected':actionButton === 2, 'confirm':deleteConfirm}" |
| 270 | + @click.stop.prevent="deleteConfirmNote(item.path)" |
| 271 | + > |
| 272 | + <template v-if="deleteConfirm"> |
| 273 | + Really Delete? |
| 274 | + </template> |
| 275 | + <template v-else> |
| 276 | + Delete |
| 277 | + </template> |
| 278 | + </button> |
| 279 | + <button |
| 280 | + class="show-actions" |
| 281 | + v-if="itemHasActionButtons(item) && (actionButton === 0 || idx !== selected)" |
| 282 | + @click.stop.prevent="showActionButtons(idx)" |
| 283 | + ></button> |
| 284 | + </span> |
| 285 | + </li> |
| 286 | + </template> |
262 | 287 | </ul> |
263 | | - </form> |
264 | | - </div> |
| 288 | + </div> |
| 289 | + </form> |
265 | 290 | </template> |
266 | 291 |
|
267 | 292 | <style scoped lang="sass"> |
268 | | - .scroller |
269 | | - //overflow: auto |
270 | | - //position: fixed |
271 | | - //top: 0 |
272 | | - //left: 0 |
273 | | - //bottom: 0 |
274 | | - //right: 0 |
275 | 293 | .note-selector |
276 | 294 | font-size: 13px |
277 | | - padding: 10px |
278 | 295 | //background: #48b57e |
279 | 296 | background: #efefef |
280 | 297 | position: absolute |
|
294 | 311 | +webapp-mobile |
295 | 312 | max-width: calc(100% - 80px) |
296 | 313 |
|
297 | | - input |
298 | | - background: #fff |
299 | | - padding: 4px 5px |
300 | | - border: 1px solid #ccc |
301 | | - box-sizing: border-box |
302 | | - border-radius: 2px |
303 | | - width: 100% |
304 | | - margin-bottom: 10px |
305 | | - &:focus |
306 | | - outline: none |
307 | | - border: 1px solid #fff |
308 | | - outline: 2px solid #48b57e |
309 | | - +dark-mode |
310 | | - background: #3b3b3b |
311 | | - color: rgba(255,255,255, 0.9) |
312 | | - border: 1px solid #5a5a5a |
| 314 | + .input-container |
| 315 | + padding: 10px |
| 316 | + input |
| 317 | + background: #fff |
| 318 | + padding: 4px 5px |
| 319 | + border: 1px solid #ccc |
| 320 | + box-sizing: border-box |
| 321 | + border-radius: 2px |
| 322 | + width: 100% |
313 | 323 | &:focus |
314 | | - border: 1px solid #3b3b3b |
315 | | - +webapp-mobile |
316 | | - font-size: 16px |
317 | | - max-width: 100% |
| 324 | + outline: none |
| 325 | + border: 1px solid #fff |
| 326 | + outline: 2px solid #48b57e |
| 327 | + +dark-mode |
| 328 | + background: #3b3b3b |
| 329 | + color: rgba(255,255,255, 0.9) |
| 330 | + border: 1px solid #5a5a5a |
| 331 | + &:focus |
| 332 | + border: 1px solid #3b3b3b |
| 333 | + +webapp-mobile |
| 334 | + font-size: 16px |
| 335 | + max-width: 100% |
318 | 336 | |
319 | | - .items |
| 337 | + .scroller |
320 | 338 | overflow-y: auto |
321 | | - > li |
| 339 | + padding: 0 10px 5px 10px |
| 340 | + |
| 341 | + .items |
| 342 | + > li.line-separator |
| 343 | + height: 1px |
| 344 | + background: rgba(0,0,0, 0.05) |
| 345 | + margin-left: -10px |
| 346 | + margin-right: -10px |
| 347 | + margin-top: 3px |
| 348 | + margin-bottom: 3px |
| 349 | + +dark-mode |
| 350 | + background: rgba(255,255,255, 0.1) |
| 351 | + > li.item |
322 | 352 | position: relative |
323 | 353 | border-radius: 3px |
324 | 354 | padding: 3px 12px |
325 | 355 | line-height: 18px |
326 | 356 | display: flex |
327 | 357 | align-items: center |
| 358 | + scroll-margin-top: 6px |
| 359 | + scroll-margin-bottom: 6px |
328 | 360 | &:hover |
329 | 361 | background: #e2e2e2 |
330 | 362 | .action-buttons .show-actions |
|
357 | 389 | color: rgba(255,255,255, 0.65) |
358 | 390 | &.scratch |
359 | 391 | font-weight: 600 |
| 392 | + &.new-note |
| 393 | + font-size: 12px |
360 | 394 | .name |
361 | 395 | margin-right: 12px |
362 | 396 | flex-shrink: 0 |
|
0 commit comments