pulsar-select-list 1.0.1 → 1.0.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +410 -436
- package/package.json +2 -2
- package/select.js +876 -806
- package/select.less +66 -53
package/README.md
CHANGED
|
@@ -1,436 +1,410 @@
|
|
|
1
|
-
# pulsar-select-list
|
|
2
|
-
|
|
3
|
-
This module is an [etch component](https://github.com/atom/etch) that can be used in Pulsar packages to show a select list with fuzzy filtering, keyboard/mouse navigation and other cool features.
|
|
4
|
-
|
|
5
|
-
## Installation
|
|
6
|
-
|
|
7
|
-
```json
|
|
8
|
-
"dependencies": {
|
|
9
|
-
"pulsar-select-list": "^1.0.0"
|
|
10
|
-
}
|
|
11
|
-
```
|
|
12
|
-
|
|
13
|
-
## Usage
|
|
14
|
-
|
|
15
|
-
After installing the module, you can simply require it and use it as a standalone component:
|
|
16
|
-
|
|
17
|
-
```js
|
|
18
|
-
const SelectListView = require(
|
|
19
|
-
|
|
20
|
-
const usersSelectList = new SelectListView({
|
|
21
|
-
items: [
|
|
22
|
-
elementForItem: (item) => {
|
|
23
|
-
const li = document.createElement(
|
|
24
|
-
li.textContent = item
|
|
25
|
-
return li
|
|
26
|
-
},
|
|
27
|
-
didConfirmSelection: (item) => {
|
|
28
|
-
console.log(
|
|
29
|
-
}
|
|
30
|
-
})
|
|
31
|
-
|
|
32
|
-
// Show as modal panel
|
|
33
|
-
usersSelectList.show()
|
|
34
|
-
```
|
|
35
|
-
|
|
36
|
-
Or within another etch component:
|
|
37
|
-
|
|
38
|
-
```jsx
|
|
39
|
-
render () {
|
|
40
|
-
return (
|
|
41
|
-
<SelectListView items={this.items} />
|
|
42
|
-
)
|
|
43
|
-
}
|
|
44
|
-
```
|
|
45
|
-
|
|
46
|
-
## API
|
|
47
|
-
|
|
48
|
-
### Constructor Props
|
|
49
|
-
|
|
50
|
-
When creating a new instance of a select list, or when calling `update` on an existing one, you can supply a JavaScript object that can contain any of the following properties:
|
|
51
|
-
|
|
52
|
-
#### Required
|
|
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
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
const matches =
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
<
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
const
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
}
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
}
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
-
|
|
335
|
-
-this.
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
-
|
|
344
|
-
|
|
345
|
-
-
|
|
346
|
-
-
|
|
347
|
-
- if (
|
|
348
|
-
- this.
|
|
349
|
-
- }
|
|
350
|
-
-
|
|
351
|
-
-
|
|
352
|
-
-}
|
|
353
|
-
|
|
354
|
-
-
|
|
355
|
-
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
```
|
|
387
|
-
|
|
388
|
-
###
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
```diff
|
|
393
|
-
|
|
394
|
-
-
|
|
395
|
-
+
|
|
396
|
-
}
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
```
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
```
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
### Help Message
|
|
415
|
-
|
|
416
|
-
Replace `infoMessage` with `helpMarkdown` and the built-in `select-list:help` command:
|
|
417
|
-
|
|
418
|
-
```diff
|
|
419
|
-
this.slv = new SelectListView({
|
|
420
|
-
- // No help by default
|
|
421
|
-
+ helpMarkdown: fs.readFileSync(path.join(__dirname, 'help.md'), 'utf8'),
|
|
422
|
-
})
|
|
423
|
-
|
|
424
|
-
-atom.config.observe('my-package.showKeystrokes', (value) => {
|
|
425
|
-
- this.slv.update({ infoMessage: value ? [...] : null })
|
|
426
|
-
-})
|
|
427
|
-
```
|
|
428
|
-
|
|
429
|
-
Create a `help.md` file with your help content:
|
|
430
|
-
|
|
431
|
-
```markdown
|
|
432
|
-
- **Enter** — Confirm selection
|
|
433
|
-
- **Alt+Enter** — Alternative action
|
|
434
|
-
```
|
|
435
|
-
|
|
436
|
-
Press ` in editor to switch into help view.
|
|
1
|
+
# pulsar-select-list
|
|
2
|
+
|
|
3
|
+
This module is an [etch component](https://github.com/atom/etch) that can be used in Pulsar packages to show a select list with fuzzy filtering, keyboard/mouse navigation and other cool features.
|
|
4
|
+
|
|
5
|
+
## Installation
|
|
6
|
+
|
|
7
|
+
```json
|
|
8
|
+
"dependencies": {
|
|
9
|
+
"pulsar-select-list": "^1.0.0"
|
|
10
|
+
}
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
## Usage
|
|
14
|
+
|
|
15
|
+
After installing the module, you can simply require it and use it as a standalone component:
|
|
16
|
+
|
|
17
|
+
```js
|
|
18
|
+
const SelectListView = require("pulsar-select-list");
|
|
19
|
+
|
|
20
|
+
const usersSelectList = new SelectListView({
|
|
21
|
+
items: ["Alice", "Bob", "Carol"],
|
|
22
|
+
elementForItem: (item) => {
|
|
23
|
+
const li = document.createElement("li");
|
|
24
|
+
li.textContent = item;
|
|
25
|
+
return li;
|
|
26
|
+
},
|
|
27
|
+
didConfirmSelection: (item) => {
|
|
28
|
+
console.log("Selected:", item);
|
|
29
|
+
},
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
// Show as modal panel
|
|
33
|
+
usersSelectList.show();
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
Or within another etch component:
|
|
37
|
+
|
|
38
|
+
```jsx
|
|
39
|
+
render () {
|
|
40
|
+
return (
|
|
41
|
+
<SelectListView items={this.items} />
|
|
42
|
+
)
|
|
43
|
+
}
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
## API
|
|
47
|
+
|
|
48
|
+
### Constructor Props
|
|
49
|
+
|
|
50
|
+
When creating a new instance of a select list, or when calling `update` on an existing one, you can supply a JavaScript object that can contain any of the following properties:
|
|
51
|
+
|
|
52
|
+
#### Required
|
|
53
|
+
|
|
54
|
+
- `items: [Object]`: an array containing the objects you want to show in the select list.
|
|
55
|
+
- `elementForItem: (item: Object, options: Object) -> HTMLElement`: a function that is called whenever an item needs to be displayed.
|
|
56
|
+
- `options: Object`:
|
|
57
|
+
- `selected: Boolean`: indicating whether item is selected or not.
|
|
58
|
+
- `index: Number`: item's index.
|
|
59
|
+
|
|
60
|
+
#### Optional
|
|
61
|
+
|
|
62
|
+
- `className: String`: CSS class name(s) to add to the select list element. Multiple classes can be space-separated.
|
|
63
|
+
- `maxResults: Number`: the number of maximum items that are shown.
|
|
64
|
+
- `filter: (items: [Object], query: String) -> [Object]`: a function that allows to decide which items to show whenever the query changes. By default, it uses Pulsar's built-in fuzzy matcher.
|
|
65
|
+
- `filterKeyForItem: (item: Object) -> String`: when `filter` is not provided, this function will be called to retrieve a string property on each item and that will be used to filter them.
|
|
66
|
+
- `filterQuery: (query: String) -> String`: a function that allows to apply a transformation to the user query and whose return value will be used to filter items.
|
|
67
|
+
- `replaceDiacritics: Boolean`: when `true` (default), removes diacritical marks from both the query and item text before filtering, enabling accent-insensitive matching (e.g., "cafe" matches "café"). Set to `false` to disable.
|
|
68
|
+
- `filterScoreModifier: (score: Number, item: Object) -> Number`: a function to modify the fuzzy match score for each item. Useful for applying custom ranking factors (e.g., boosting by recency or proximity).
|
|
69
|
+
- `query: String`: a string that will replace the contents of the query editor.
|
|
70
|
+
- `selectQuery: Boolean`: a boolean indicating whether the query text should be selected or not.
|
|
71
|
+
- `order: (item1: Object, item2: Object) -> Number`: a function that allows to change the order in which items are shown.
|
|
72
|
+
- `emptyMessage: String`: a string shown when the list is empty.
|
|
73
|
+
- `errorMessage: String`: a string that needs to be set when you want to notify the user that an error occurred.
|
|
74
|
+
- `infoMessage: String`: a string that needs to be set when you want to provide some information to the user.
|
|
75
|
+
- `helpMessage: String|Array`: content to display when help is toggled. Can be a string or JSX array for rich formatting.
|
|
76
|
+
- `helpMarkdown: String`: markdown content to display when help is toggled. Rendered using Pulsar's built-in markdown renderer.
|
|
77
|
+
- `loadingMessage: String`: a string that needs to be set when you are loading items in the background.
|
|
78
|
+
- `loadingBadge: String/Number`: a string or number that needs to be set when the progress status changes.
|
|
79
|
+
- `itemsClassList: [String]`: an array of strings that will be added as class names to the items element.
|
|
80
|
+
- `initialSelectionIndex: Number`: the index of the item to initially select; defaults to `0`.
|
|
81
|
+
- `placeholderText: String`: placeholder text to display in the query editor when empty.
|
|
82
|
+
- `skipCommandsRegistration: Boolean`: when `true`, skips registering default keyboard commands.
|
|
83
|
+
|
|
84
|
+
### Registered Commands
|
|
85
|
+
|
|
86
|
+
By default, the component registers these commands on its element:
|
|
87
|
+
|
|
88
|
+
- `core:move-up` / `core:move-down`: Navigate items
|
|
89
|
+
- `core:move-to-top` / `core:move-to-bottom`: Jump to first/last item
|
|
90
|
+
- `core:confirm`: Confirm selection
|
|
91
|
+
- `core:cancel`: Cancel selection
|
|
92
|
+
- `select-list:help`: Toggle help message visibility (requires `helpMessage` or `helpMarkdown`)
|
|
93
|
+
|
|
94
|
+
#### Callbacks
|
|
95
|
+
|
|
96
|
+
- `didChangeQuery: (query: String) -> Void`: called when the query changes.
|
|
97
|
+
- `didChangeSelection: (item: Object) -> Void`: called when the selected item changes.
|
|
98
|
+
- `didConfirmSelection: (item: Object) -> Void`: called when the user clicks or presses Enter on an item.
|
|
99
|
+
- `didConfirmEmptySelection: () -> Void`: called when the user presses Enter but the list is empty.
|
|
100
|
+
- `didCancelSelection: () -> Void`: called when the user presses Esc or the list loses focus.
|
|
101
|
+
- `willShow: () -> Void`: called when transitioning from hidden to visible, useful for data preparation.
|
|
102
|
+
|
|
103
|
+
### Instance Properties
|
|
104
|
+
|
|
105
|
+
- `processedQuery: String`: The cached result of `getFilterQuery()`, updated after each query change. Useful in `elementForItem` to avoid calling `getFilterQuery()` multiple times.
|
|
106
|
+
- `selectionIndex: Number|undefined`: The index of the currently selected item, or `undefined` if nothing is selected.
|
|
107
|
+
- `refs.queryEditor`: The underlying TextEditor component for the query input.
|
|
108
|
+
|
|
109
|
+
### Instance Methods
|
|
110
|
+
|
|
111
|
+
#### Panel Management
|
|
112
|
+
|
|
113
|
+
- `show()`: Shows the select list as a modal panel and focuses the query editor. Calls `willShow` callback if provided.
|
|
114
|
+
- `hide()`: Hides the panel and restores focus to the previously focused element.
|
|
115
|
+
- `toggle()`: Toggles the visibility of the panel.
|
|
116
|
+
- `isVisible()`: Returns `true` if the panel is currently visible.
|
|
117
|
+
- `isHelpMode()`: Returns `true` if help is currently displayed.
|
|
118
|
+
- `toggleHelp()`: Toggles help message visibility. Only works if `helpMessage` is set.
|
|
119
|
+
- `hideHelp()`: Hides help message if currently shown.
|
|
120
|
+
|
|
121
|
+
#### Other Methods
|
|
122
|
+
|
|
123
|
+
- `focus()`: Focuses the query editor.
|
|
124
|
+
- `reset()`: Clears the query editor text.
|
|
125
|
+
- `destroy()`: Disposes of the component and cleans up resources.
|
|
126
|
+
- `update(props)`: Updates the component with new props.
|
|
127
|
+
- `getQuery()`: Returns the current query string.
|
|
128
|
+
- `getMatchIndices(item)`: Returns the cached match indices for an item from the last filter operation, or `null` if no matches. Use this in `elementForItem` instead of calling `atom.ui.fuzzyMatcher.match()` directly.
|
|
129
|
+
- `getFilterQuery()`: Returns the filtered query string (applies `filterQuery` transformation).
|
|
130
|
+
- `setQueryFromSelection()`: Sets the query text from the active editor's selection. Returns `true` if successful, `false` if no editor, no selection, or selection contains newlines.
|
|
131
|
+
- `getSelectedItem()`: Returns the currently selected item.
|
|
132
|
+
- `selectPrevious()`: Selects the previous item.
|
|
133
|
+
- `selectNext()`: Selects the next item.
|
|
134
|
+
- `selectFirst()`: Selects the first item.
|
|
135
|
+
- `selectLast()`: Selects the last item.
|
|
136
|
+
- `selectNone()`: Deselects all items.
|
|
137
|
+
- `selectIndex(index)`: Selects the item at the given index.
|
|
138
|
+
- `selectItem(item)`: Selects the given item.
|
|
139
|
+
- `confirmSelection()`: Confirms the current selection.
|
|
140
|
+
- `cancelSelection()`: Cancels the selection.
|
|
141
|
+
|
|
142
|
+
### Static Methods
|
|
143
|
+
|
|
144
|
+
#### `SelectListView.highlightMatches(text, matchIndices, options)`
|
|
145
|
+
|
|
146
|
+
Creates a DocumentFragment with highlighted match characters.
|
|
147
|
+
|
|
148
|
+
```js
|
|
149
|
+
// In elementForItem, use getMatchIndices() to get cached match indices:
|
|
150
|
+
const matches = this.selectList.getMatchIndices(item) || [];
|
|
151
|
+
const fragment = SelectListView.highlightMatches(item.name, matches);
|
|
152
|
+
element.appendChild(fragment);
|
|
153
|
+
|
|
154
|
+
// With custom class name
|
|
155
|
+
const fragment = SelectListView.highlightMatches(item.name, matches, {
|
|
156
|
+
className: "my-highlight",
|
|
157
|
+
});
|
|
158
|
+
```
|
|
159
|
+
|
|
160
|
+
- `text: String`: the text to highlight.
|
|
161
|
+
- `matchIndices: [Number]`: array of character indices to highlight.
|
|
162
|
+
- `options: Object` (optional):
|
|
163
|
+
- `className: String`: CSS class for highlighted spans; defaults to `'character-match'`.
|
|
164
|
+
|
|
165
|
+
Returns a `DocumentFragment` containing text nodes and `<span>` elements with the specified class.
|
|
166
|
+
|
|
167
|
+
#### `SelectListView.replaceDiacritics(str)`
|
|
168
|
+
|
|
169
|
+
Removes diacritical marks (accents) from a string.
|
|
170
|
+
|
|
171
|
+
```js
|
|
172
|
+
SelectListView.replaceDiacritics("café"); // => 'cafe'
|
|
173
|
+
SelectListView.replaceDiacritics("naïve"); // => 'naive'
|
|
174
|
+
SelectListView.replaceDiacritics("Müller"); // => 'Muller'
|
|
175
|
+
```
|
|
176
|
+
|
|
177
|
+
- `str: String`: the string to process.
|
|
178
|
+
|
|
179
|
+
Returns the string with diacritical marks removed. Uses `String.normalize('NFD')` internally.
|
|
180
|
+
|
|
181
|
+
#### `SelectListView.createTwoLineItem(options)`
|
|
182
|
+
|
|
183
|
+
Creates a two-line list item element with primary and optional secondary lines. This is a convenience helper for the common Atom/Pulsar two-line item pattern.
|
|
184
|
+
|
|
185
|
+
```js
|
|
186
|
+
const li = SelectListView.createTwoLineItem({
|
|
187
|
+
primary: SelectListView.highlightMatches(item.name, matches),
|
|
188
|
+
secondary: item.description,
|
|
189
|
+
icon: ["icon-file-text"],
|
|
190
|
+
});
|
|
191
|
+
```
|
|
192
|
+
|
|
193
|
+
- `options: Object`:
|
|
194
|
+
- `primary: String|Node`: Primary line content (text string or DOM node)
|
|
195
|
+
- `secondary: String|Node` (optional): Secondary line content
|
|
196
|
+
- `icon: [String]` (optional): Icon class names to add to primary line (adds `icon` class automatically)
|
|
197
|
+
|
|
198
|
+
Returns an `HTMLLIElement` with the structure:
|
|
199
|
+
|
|
200
|
+
```html
|
|
201
|
+
<li class="two-lines">
|
|
202
|
+
<div class="primary-line icon [icon]">[primary]</div>
|
|
203
|
+
<div class="secondary-line">[secondary]</div>
|
|
204
|
+
</li>
|
|
205
|
+
```
|
|
206
|
+
|
|
207
|
+
#### `SelectListView.setScheduler(scheduler)`
|
|
208
|
+
|
|
209
|
+
Sets the etch scheduler.
|
|
210
|
+
|
|
211
|
+
#### `SelectListView.getScheduler()`
|
|
212
|
+
|
|
213
|
+
Gets the current etch scheduler.
|
|
214
|
+
|
|
215
|
+
## Example
|
|
216
|
+
|
|
217
|
+
```js
|
|
218
|
+
const SelectListView = require("pulsar-select-list");
|
|
219
|
+
const fs = require("fs");
|
|
220
|
+
const path = require("path");
|
|
221
|
+
|
|
222
|
+
class MyFileList {
|
|
223
|
+
constructor() {
|
|
224
|
+
this.selectList = new SelectListView({
|
|
225
|
+
className: "my-package my-file-list",
|
|
226
|
+
items: [],
|
|
227
|
+
filterKeyForItem: (item) => item.name,
|
|
228
|
+
emptyMessage: "No files found",
|
|
229
|
+
helpMarkdown: fs.readFileSync(path.join(__dirname, "help.md"), "utf8"),
|
|
230
|
+
|
|
231
|
+
willShow: () => {
|
|
232
|
+
this.loadFiles();
|
|
233
|
+
},
|
|
234
|
+
|
|
235
|
+
elementForItem: (item, options) => {
|
|
236
|
+
const li = document.createElement("li");
|
|
237
|
+
const matches = this.selectList.getMatchIndices(item) || [];
|
|
238
|
+
li.appendChild(SelectListView.highlightMatches(item.name, matches));
|
|
239
|
+
|
|
240
|
+
li.addEventListener("contextmenu", () => {
|
|
241
|
+
this.selectList.selectIndex(options.index);
|
|
242
|
+
});
|
|
243
|
+
|
|
244
|
+
return li;
|
|
245
|
+
},
|
|
246
|
+
|
|
247
|
+
didConfirmSelection: (item) => {
|
|
248
|
+
atom.workspace.open(item.path);
|
|
249
|
+
this.selectList.hide();
|
|
250
|
+
},
|
|
251
|
+
|
|
252
|
+
didCancelSelection: () => {
|
|
253
|
+
this.selectList.hide();
|
|
254
|
+
},
|
|
255
|
+
});
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
loadFiles() {
|
|
259
|
+
// Load files and update items
|
|
260
|
+
this.selectList.update({ items: this.files });
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
toggle() {
|
|
264
|
+
this.selectList.toggle();
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
destroy() {
|
|
268
|
+
this.selectList.destroy();
|
|
269
|
+
}
|
|
270
|
+
}
|
|
271
|
+
```
|
|
272
|
+
|
|
273
|
+
### Advanced: Custom Score Modifier
|
|
274
|
+
|
|
275
|
+
Use `filterScoreModifier` to customize ranking:
|
|
276
|
+
|
|
277
|
+
```js
|
|
278
|
+
const selectList = new SelectListView({
|
|
279
|
+
items: files,
|
|
280
|
+
elementForItem: (item) => {
|
|
281
|
+
const li = document.createElement("li");
|
|
282
|
+
li.textContent = item.path;
|
|
283
|
+
return li;
|
|
284
|
+
},
|
|
285
|
+
filterKeyForItem: (item) => item.path,
|
|
286
|
+
// Boost score by proximity (items closer to current file rank higher)
|
|
287
|
+
filterScoreModifier: (score, item) => score / item.distance,
|
|
288
|
+
didConfirmSelection: (item) => {
|
|
289
|
+
atom.workspace.open(item.path);
|
|
290
|
+
},
|
|
291
|
+
});
|
|
292
|
+
```
|
|
293
|
+
|
|
294
|
+
## Migration from atom-select-list
|
|
295
|
+
|
|
296
|
+
If you're migrating from `atom-select-list`, here are the key changes:
|
|
297
|
+
|
|
298
|
+
### Package.json
|
|
299
|
+
|
|
300
|
+
```diff
|
|
301
|
+
"dependencies": {
|
|
302
|
+
- "atom-select-list": "^0.8.1",
|
|
303
|
+
+ "pulsar-select-list": "^1.0.0"
|
|
304
|
+
}
|
|
305
|
+
```
|
|
306
|
+
|
|
307
|
+
### Import
|
|
308
|
+
|
|
309
|
+
```diff
|
|
310
|
+
-const SelectListView = require('atom-select-list')
|
|
311
|
+
+const SelectListView = require('pulsar-select-list')
|
|
312
|
+
```
|
|
313
|
+
|
|
314
|
+
### Panel Management
|
|
315
|
+
|
|
316
|
+
The component now manages its own panel. Remove custom panel handling:
|
|
317
|
+
|
|
318
|
+
```diff
|
|
319
|
+
-this.panel = null
|
|
320
|
+
-this.previouslyFocusedElement = null
|
|
321
|
+
|
|
322
|
+
this.selectList = new SelectListView({
|
|
323
|
+
+ className: 'my-list',
|
|
324
|
+
items: [],
|
|
325
|
+
+ willShow: () => this.onWillShow(),
|
|
326
|
+
// ...
|
|
327
|
+
})
|
|
328
|
+
-this.selectList.element.classList.add('my-list')
|
|
329
|
+
|
|
330
|
+
-showView() {
|
|
331
|
+
- this.previouslyFocusedElement = document.activeElement
|
|
332
|
+
- if (!this.panel) {
|
|
333
|
+
- this.panel = atom.workspace.addModalPanel({ item: this.selectList })
|
|
334
|
+
- }
|
|
335
|
+
- this.panel.show()
|
|
336
|
+
- this.selectList.focus()
|
|
337
|
+
-}
|
|
338
|
+
-
|
|
339
|
+
-hideView() {
|
|
340
|
+
- this.panel.hide()
|
|
341
|
+
- if (this.previouslyFocusedElement) {
|
|
342
|
+
- this.previouslyFocusedElement.focus()
|
|
343
|
+
- }
|
|
344
|
+
-}
|
|
345
|
+
-
|
|
346
|
+
-toggleView() {
|
|
347
|
+
- if (this.panel && this.panel.isVisible()) {
|
|
348
|
+
- this.hideView()
|
|
349
|
+
- } else {
|
|
350
|
+
- this.showView()
|
|
351
|
+
- }
|
|
352
|
+
-}
|
|
353
|
+
|
|
354
|
+
// Use built-in methods:
|
|
355
|
+
-this.toggleView()
|
|
356
|
+
+this.selectList.toggle()
|
|
357
|
+
|
|
358
|
+
-this.hideView()
|
|
359
|
+
+this.selectList.hide()
|
|
360
|
+
```
|
|
361
|
+
|
|
362
|
+
### Diacritics
|
|
363
|
+
|
|
364
|
+
Replace external diacritics library with built-in static method:
|
|
365
|
+
|
|
366
|
+
```diff
|
|
367
|
+
-const Diacritics = require('diacritic')
|
|
368
|
+
|
|
369
|
+
-Diacritics.clean(text)
|
|
370
|
+
+SelectListView.replaceDiacritics(text)
|
|
371
|
+
```
|
|
372
|
+
|
|
373
|
+
### Match Highlighting
|
|
374
|
+
|
|
375
|
+
Use `getMatchIndices(item)` to get cached match indices from the filter:
|
|
376
|
+
|
|
377
|
+
```diff
|
|
378
|
+
elementForItem(item, options) {
|
|
379
|
+
- const query = this.query || ''
|
|
380
|
+
- const matches = query
|
|
381
|
+
- ? atom.ui.fuzzyMatcher.match(item.name, query, { recordMatchIndexes: true }).matchIndexes
|
|
382
|
+
- : []
|
|
383
|
+
+ const matches = this.selectList.getMatchIndices(item) || []
|
|
384
|
+
el.appendChild(SelectListView.highlightMatches(item.name, matches))
|
|
385
|
+
}
|
|
386
|
+
```
|
|
387
|
+
|
|
388
|
+
### Help Message
|
|
389
|
+
|
|
390
|
+
Replace `infoMessage` with `helpMarkdown` and the built-in `select-list:help` command:
|
|
391
|
+
|
|
392
|
+
```diff
|
|
393
|
+
this.selectList = new SelectListView({
|
|
394
|
+
- // No help by default
|
|
395
|
+
+ helpMarkdown: fs.readFileSync(path.join(__dirname, 'help.md'), 'utf8'),
|
|
396
|
+
})
|
|
397
|
+
|
|
398
|
+
-atom.config.observe('my-package.showKeystrokes', (value) => {
|
|
399
|
+
- this.selectList.update({ infoMessage: value ? [...] : null })
|
|
400
|
+
-})
|
|
401
|
+
```
|
|
402
|
+
|
|
403
|
+
Create a `help.md` file with your help content:
|
|
404
|
+
|
|
405
|
+
```markdown
|
|
406
|
+
- **Enter** — Confirm selection
|
|
407
|
+
- **Alt+Enter** — Alternative action
|
|
408
|
+
```
|
|
409
|
+
|
|
410
|
+
Press ` in editor to switch into help view.
|