@@ -14,42 +14,44 @@ import OrderBy from "../search_options/order_by.js";
14
14
import SearchScript from "../search_options/search_script.js" ;
15
15
import Limit from "../search_options/limit.js" ;
16
16
import Debug from "../search_options/debug.js" ;
17
- import appContext from "../../components/app_context.js" ;
17
+ import appContext , { type EventData } from "../../components/app_context.js" ;
18
18
import bulkActionService from "../../services/bulk_action.js" ;
19
19
import { Dropdown } from "bootstrap" ;
20
+ import type FNote from "../../entities/fnote.js" ;
21
+ import type { AttributeType } from "../../entities/fattribute.js" ;
20
22
21
23
const TPL = `
22
24
<div class="search-definition-widget">
23
- <style>
25
+ <style>
24
26
.search-setting-table {
25
27
margin-top: 0;
26
28
margin-bottom: 7px;
27
29
width: 100%;
28
30
border-collapse: separate;
29
31
border-spacing: 10px;
30
32
}
31
-
33
+
32
34
.search-setting-table div {
33
35
white-space: nowrap;
34
36
}
35
-
37
+
36
38
.search-setting-table .button-column {
37
39
/* minimal width so that table remains static sized and most space remains for middle column with settings */
38
40
width: 50px;
39
41
white-space: nowrap;
40
42
text-align: right;
41
43
}
42
-
44
+
43
45
.search-setting-table .title-column {
44
46
/* minimal width so that table remains static sized and most space remains for middle column with settings */
45
47
width: 50px;
46
- white-space: nowrap;
48
+ white-space: nowrap;
47
49
}
48
-
50
+
49
51
.search-setting-table .button-column .dropdown-menu {
50
52
white-space: normal;
51
53
}
52
-
54
+
53
55
.attribute-list hr {
54
56
height: 1px;
55
57
border-color: var(--main-border-color);
@@ -58,15 +60,15 @@ const TPL = `
58
60
margin-top: 5px;
59
61
margin-bottom: 0;
60
62
}
61
-
63
+
62
64
.search-definition-widget input:invalid {
63
65
border: 3px solid red;
64
66
}
65
67
66
68
.add-search-option button {
67
69
margin-top: 5px; /* to give some spacing when buttons overflow on the next line */
68
70
}
69
-
71
+
70
72
.dropdown-header {
71
73
background-color: var(--accented-background-color);
72
74
}
@@ -78,47 +80,47 @@ const TPL = `
78
80
<td class="title-column">${ t ( "search_definition.add_search_option" ) } </td>
79
81
<td colspan="2" class="add-search-option">
80
82
<button type="button" class="btn btn-sm" data-search-option-add="searchString">
81
- <span class="bx bx-text"></span>
83
+ <span class="bx bx-text"></span>
82
84
${ t ( "search_definition.search_string" ) }
83
85
</button>
84
-
86
+
85
87
<button type="button" class="btn btn-sm" data-search-option-add="searchScript">
86
- <span class="bx bx-code"></span>
88
+ <span class="bx bx-code"></span>
87
89
${ t ( "search_definition.search_script" ) }
88
90
</button>
89
-
91
+
90
92
<button type="button" class="btn btn-sm" data-search-option-add="ancestor">
91
- <span class="bx bx-filter-alt"></span>
93
+ <span class="bx bx-filter-alt"></span>
92
94
${ t ( "search_definition.ancestor" ) }
93
95
</button>
94
-
96
+
95
97
<button type="button" class="btn btn-sm" data-search-option-add="fastSearch"
96
98
title="${ t ( "search_definition.fast_search_description" ) } ">
97
99
<span class="bx bx-run"></span>
98
100
${ t ( "search_definition.fast_search" ) }
99
101
</button>
100
-
102
+
101
103
<button type="button" class="btn btn-sm" data-search-option-add="includeArchivedNotes"
102
104
title="${ t ( "search_definition.include_archived_notes_description" ) } ">
103
105
<span class="bx bx-archive"></span>
104
106
${ t ( "search_definition.include_archived" ) }
105
107
</button>
106
-
108
+
107
109
<button type="button" class="btn btn-sm" data-search-option-add="orderBy">
108
110
<span class="bx bx-arrow-from-top"></span>
109
111
${ t ( "search_definition.order_by" ) }
110
112
</button>
111
-
113
+
112
114
<button type="button" class="btn btn-sm" data-search-option-add="limit" title="${ t ( "search_definition.limit_description" ) } ">
113
115
<span class="bx bx-stop"></span>
114
116
${ t ( "search_definition.limit" ) }
115
117
</button>
116
-
118
+
117
119
<button type="button" class="btn btn-sm" data-search-option-add="debug" title="${ t ( "search_definition.debug_description" ) } ">
118
120
<span class="bx bx-bug"></span>
119
121
${ t ( "search_definition.debug" ) }
120
122
</button>
121
-
123
+
122
124
<div class="dropdown" style="display: inline-block;">
123
125
<button class="btn btn-sm dropdown-toggle action-add-toggle" type="button" id="dropdownMenuButton" data-bs-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
124
126
<span class="bx bxs-zap"></span>
@@ -138,12 +140,12 @@ const TPL = `
138
140
<span class="bx bx-search"></span>
139
141
${ t ( "search_definition.search_button" ) }
140
142
</button>
141
-
143
+
142
144
<button type="button" class="btn btn-sm search-and-execute-button">
143
145
<span class="bx bxs-zap"></span>
144
146
${ t ( "search_definition.search_execute" ) }
145
147
</button>
146
-
148
+
147
149
<button type="button" class="btn btn-sm save-to-note-button">
148
150
<span class="bx bx-save"></span>
149
151
${ t ( "search_definition.save_to_note" ) }
@@ -158,7 +160,21 @@ const TPL = `
158
160
159
161
const OPTION_CLASSES = [ SearchString , SearchScript , Ancestor , FastSearch , IncludeArchivedNotes , OrderBy , Limit , Debug ] ;
160
162
163
+ // TODO: Deduplicate with server
164
+ interface SaveSearchNoteResponse {
165
+ notePath : string ;
166
+ }
167
+
161
168
export default class SearchDefinitionWidget extends NoteContextAwareWidget {
169
+
170
+ private $component ! : JQuery < HTMLElement > ;
171
+ private $actionList ! : JQuery < HTMLElement > ;
172
+ private $searchOptions ! : JQuery < HTMLElement > ;
173
+ private $searchButton ! : JQuery < HTMLElement > ;
174
+ private $searchAndExecuteButton ! : JQuery < HTMLElement > ;
175
+ private $saveToNoteButton ! : JQuery < HTMLElement > ;
176
+ private $actionOptions ! : JQuery < HTMLElement > ;
177
+
162
178
get name ( ) {
163
179
return "searchDefinition" ;
164
180
}
@@ -194,7 +210,7 @@ export default class SearchDefinitionWidget extends NoteContextAwareWidget {
194
210
const searchOptionName = $ ( event . target ) . attr ( "data-search-option-add" ) ;
195
211
const clazz = OPTION_CLASSES . find ( ( SearchOptionClass ) => SearchOptionClass . optionName === searchOptionName ) ;
196
212
197
- if ( clazz ) {
213
+ if ( clazz && this . noteId ) {
198
214
await clazz . create ( this . noteId ) ;
199
215
} else {
200
216
logError ( t ( "search_definition.unknown_search_option" , { searchOptionName } ) ) ;
@@ -204,11 +220,13 @@ export default class SearchDefinitionWidget extends NoteContextAwareWidget {
204
220
} ) ;
205
221
206
222
this . $widget . on ( "click" , "[data-action-add]" , async ( event ) => {
207
- Dropdown . getOrCreateInstance ( this . $widget . find ( ".action-add-toggle" ) ) ;
223
+ Dropdown . getOrCreateInstance ( this . $widget . find ( ".action-add-toggle" ) [ 0 ] ) ;
208
224
209
225
const actionName = $ ( event . target ) . attr ( "data-action-add" ) ;
210
226
211
- await bulkActionService . addAction ( this . noteId , actionName ) ;
227
+ if ( this . noteId && actionName ) {
228
+ await bulkActionService . addAction ( this . noteId , actionName ) ;
229
+ }
212
230
213
231
this . refresh ( ) ;
214
232
} ) ;
@@ -224,7 +242,7 @@ export default class SearchDefinitionWidget extends NoteContextAwareWidget {
224
242
225
243
this . $saveToNoteButton = this . $widget . find ( ".save-to-note-button" ) ;
226
244
this . $saveToNoteButton . on ( "click" , async ( ) => {
227
- const { notePath } = await server . post ( "special-notes/save-search-note" , { searchNoteId : this . noteId } ) ;
245
+ const { notePath } = await server . post < SaveSearchNoteResponse > ( "special-notes/save-search-note" , { searchNoteId : this . noteId } ) ;
228
246
229
247
await ws . waitForMaxKnownEntityChangeId ( ) ;
230
248
@@ -236,24 +254,32 @@ export default class SearchDefinitionWidget extends NoteContextAwareWidget {
236
254
}
237
255
238
256
async refreshResultsCommand ( ) {
257
+ if ( ! this . noteId ) {
258
+ return ;
259
+ }
260
+
239
261
try {
240
- const { error } = await froca . loadSearchNote ( this . noteId ) ;
262
+ const result = await froca . loadSearchNote ( this . noteId ) ;
241
263
242
- if ( error ) {
243
- this . handleEvent ( "showSearchError" , { error } ) ;
264
+ if ( result && result . error ) {
265
+ this . handleEvent ( "showSearchError" , { error : result . error } ) ;
244
266
}
245
- } catch ( e ) {
267
+ } catch ( e : any ) {
246
268
toastService . showError ( e . message ) ;
247
269
}
248
270
249
- this . triggerEvent ( "searchRefreshed" , { ntxId : this . noteContext . ntxId } ) ;
271
+ this . triggerEvent ( "searchRefreshed" , { ntxId : this . noteContext ? .ntxId } ) ;
250
272
}
251
273
252
274
async refreshSearchDefinitionCommand ( ) {
253
275
await this . refresh ( ) ;
254
276
}
255
277
256
- async refreshWithNote ( note ) {
278
+ async refreshWithNote ( note : FNote ) {
279
+ if ( ! this . note ) {
280
+ return ;
281
+ }
282
+
257
283
this . $component . show ( ) ;
258
284
259
285
this . $saveToNoteButton . toggle ( note . isHiddenCompletely ( ) ) ;
@@ -263,22 +289,27 @@ export default class SearchDefinitionWidget extends NoteContextAwareWidget {
263
289
for ( const OptionClass of OPTION_CLASSES ) {
264
290
const { attributeType, optionName } = OptionClass ;
265
291
266
- const attr = this . note . getAttribute ( attributeType , optionName ) ;
292
+ const attr = this . note . getAttribute ( attributeType as AttributeType , optionName ) ;
267
293
268
294
this . $widget . find ( `[data-search-option-add='${ optionName } '` ) . toggle ( ! attr ) ;
269
295
270
296
if ( attr ) {
271
297
const searchOption = new OptionClass ( attr , this . note ) . setParent ( this ) ;
272
298
this . child ( searchOption ) ;
273
299
274
- this . $searchOptions . append ( searchOption . render ( ) ) ;
300
+ const renderedEl = searchOption . render ( ) ;
301
+ if ( renderedEl ) {
302
+ this . $searchOptions . append ( renderedEl ) ;
303
+ }
275
304
}
276
305
}
277
306
278
307
const actions = bulkActionService . parseActions ( this . note ) ;
308
+ const renderedEls = actions
309
+ . map ( ( action ) => action . render ( ) )
310
+ . filter ( ( e ) => e ) as JQuery < HTMLElement > [ ] ;
279
311
280
- this . $actionOptions . empty ( ) . append ( ...actions . map ( ( action ) => action . render ( ) ) ) ;
281
-
312
+ this . $actionOptions . empty ( ) . append ( ...renderedEls ) ;
282
313
this . $searchAndExecuteButton . css ( "visibility" , actions . length > 0 ? "visible" : "_hidden" ) ;
283
314
}
284
315
@@ -294,7 +325,7 @@ export default class SearchDefinitionWidget extends NoteContextAwareWidget {
294
325
toastService . showMessage ( t ( "search_definition.actions_executed" ) , 3000 ) ;
295
326
}
296
327
297
- entitiesReloadedEvent ( { loadResults } ) {
328
+ entitiesReloadedEvent ( { loadResults } : EventData < "entitiesReloaded" > ) {
298
329
// only refreshing deleted attrs, otherwise components update themselves
299
330
if ( loadResults . getAttributeRows ( ) . find ( ( attrRow ) => attrRow . type === "label" && attrRow . name === "action" && attrRow . isDeleted ) ) {
300
331
this . refresh ( ) ;
0 commit comments