electron-findbar 2.0.0 → 3.0.0

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.
Files changed (3) hide show
  1. package/README.md +46 -30
  2. package/index.js +87 -25
  3. package/package.json +1 -1
package/README.md CHANGED
@@ -1,8 +1,5 @@
1
1
  <p align='center'>
2
- <a href="https://github.com/ECRomaneli/handbook" style='text-decoration:none'>
3
- <img src="https://i.postimg.cc/sXwqJP59/findbar-v2-light.png" alt='Findbar Light Theme'>
4
- <img src="https://i.postimg.cc/j26XXRVV/findbar-v2-dark.png" alt='Findbar Dark Theme'>
5
- </a>
2
+ <a href="https://github.com/ECRomaneli/electron-findbar" style='text-decoration:none'><img src="https://i.postimg.cc/sXwqJP59/findbar-v2-light.png" alt='Findbar Light Theme'><img src="https://i.postimg.cc/j26XXRVV/findbar-v2-dark.png" alt='Findbar Dark Theme'></a>
6
3
  </p>
7
4
  <p align='center'>
8
5
  Chrome-like findbar for your Electron application
@@ -47,7 +44,7 @@ const Findbar = require('electron-findbar')
47
44
  You can pass a `BrowserWindow` instance as a single parameter to use it as the parent window. The `BrowserWindow.WebContents` will be used as the findable content:
48
45
 
49
46
  ```js
50
- // Create or retrieve the findbar associated to the browserWindow.webContents. If a new findbar is created, the browserWindow is used as parent.
47
+ // Create or retrieve the findbar associated to the browserWindow.webContents or baseWindow.contentView.children[0]. If a new findbar is created, the browserWindow is used as parent.
51
48
  const findbar = Findbar.from(browserWindow)
52
49
  ```
53
50
 
@@ -58,10 +55,10 @@ Alternatively, you can provide a custom `WebContents` as the second parameter. I
58
55
  const findbar = Findbar.from(baseWindow, webContents)
59
56
  ```
60
57
 
61
- Is also possible to create a findbar without a parent window (even though it is not recommended):
58
+ Is also possible to create a findbar providing only the web contents. The BaseWindow.getAllWindows() will be used to query for the parent window:
62
59
 
63
60
  ```js
64
- // Create or retrieve the findbar associated to the webContents. If a new findbar is created, it will be displayed in the middle of the screen without a parent to connect to.
61
+ // Create or retrieve the findbar associated to the webContents.
65
62
  const findbar = Findbar.from(webContents)
66
63
  ```
67
64
 
@@ -149,9 +146,20 @@ app.whenReady().then(() => {
149
146
  })
150
147
  ```
151
148
 
152
- ### Configuring Keyboard Shortcuts
149
+ ### Keyboard Shortcuts
153
150
 
154
- The Findbar component can be controlled using keyboard shortcuts. Below are two implementation approaches to help you integrate search functionality seamlessly into your application's user experience.
151
+ The Findbar component can be controlled using keyboard shortcuts. The following shortcuts are available by default:
152
+
153
+ | Shortcut | Description |
154
+ |----------|-------------|
155
+ | Enter | Move to next match |
156
+ | Shift+Enter | Move to previous match |
157
+ | Esc | Close the findbar |
158
+
159
+
160
+ ### Configuring Other Shortcuts
161
+
162
+ Below are two implementation approaches to help you integrate search functionality seamlessly into your application's user experience.
155
163
 
156
164
  **Note:** The following examples demonstrate only the ideal (happy path) scenarios. For production use, make sure to thoroughly validate all inputs and handle edge cases appropriately.
157
165
 
@@ -160,27 +168,35 @@ The Findbar component can be controlled using keyboard shortcuts. Below are two
160
168
  The `before-input-event` approach allows you to capture keyboard events directly in the main process before they're processed by the web contents, giving you precise control:
161
169
 
162
170
  ```js
163
- window.webContents.on('before-input-event', (event, input) => {
164
- // Detect Ctrl+F (Windows/Linux) or Command+F (macOS)
165
- if ((input.control || input.meta) && input.key.toLowerCase() === 'f') {
166
- // Prevent default browser behavior
167
- event.preventDefault()
168
-
169
- // Access and open the findbar
170
- const findbar = Findbar.from(window)
171
- if (findbar) {
172
- findbar.open()
171
+ webContents.on('before-input-event', (event, input) => {
172
+ if (input.shift || input.alt) { return }
173
+
174
+ const key = input.key.toLowerCase()
175
+
176
+ // Detect Ctrl+F (Windows/Linux) or Command+F (macOS)
177
+ if (input.control || input.meta) {
178
+ if (key === 'f') {
179
+ // Prevent default behavior
180
+ event.preventDefault()
181
+
182
+ // Access and open the findbar
183
+ Findbar.from(webContents).open()
184
+ }
185
+ return
173
186
  }
174
- }
175
-
176
- // Handle Escape key to close the findbar
177
- if (input.key === 'Escape') {
178
- const findbar = Findbar.from(window)
179
- if (findbar && findbar.isOpen()) {
180
- event.preventDefault()
181
- findbar.close()
187
+
188
+ // Handle Escape key to close the findbar
189
+ if (key === 'escape') {
190
+ const findbar = Findbar.fromIfExists(webContents)
191
+
192
+ if (findbar?.isOpen()) {
193
+ // Prevent default behavior
194
+ event.preventDefault()
195
+
196
+ // Close the findbar
197
+ findbar.close()
198
+ }
182
199
  }
183
- }
184
200
  })
185
201
  ```
186
202
 
@@ -201,12 +217,12 @@ appMenu.append(new MenuItem({
201
217
  submenu: [
202
218
  {
203
219
  label: 'Find in Page',
204
- click: () => Findbar.from(parent)?.open(),
220
+ click: () => Findbar.from(parent).open(),
205
221
  accelerator: 'CommandOrControl+F'
206
222
  },
207
223
  {
208
224
  label: 'Close Find',
209
- click: () => Findbar.from(parent)?.close(),
225
+ click: () => Findbar.from(parent).close(),
210
226
  accelerator: 'Esc'
211
227
  }
212
228
  ]
package/index.js CHANGED
@@ -44,28 +44,29 @@ class Findbar {
44
44
  * Configure the findbar and link to the web contents.
45
45
  *
46
46
  * @overload
47
+ * @param {WebContents} webContents Findable web contents. The parent window will be defined by using BaseWindow.getAllWindows() and
48
+ * matching the webContents with the webContents of the window or its contentView children.
49
+ * @returns {Findbar} The findbar instance if it exists.
50
+ * @throws {Error} If no webContents is provided.
51
+ *
52
+ * @overload
47
53
  * @param {BrowserWindow} browserWindow Parent window.
48
- * @param {WebContents} [customWebContents] Custom findable web contents. If not provided, the web contents of the BrowserWindow will be used.
54
+ * @param {WebContents} [customWebContents] Custom findable web contents. If not provided, the browserWindow.webContents will be used.
49
55
  * @returns {Findbar} The findbar instance if it exists.
50
56
  *
51
57
  * @overload
52
58
  * @param {BaseWindow} baseWindow Parent window.
53
- * @param {WebContents} webContents Findable web contents.
54
- * @returns {Findbar} The findbar instance if it exists.
55
- * @throws {Error} If no webContents is provided.
56
- * *
57
- * @overload
58
- * @param {WebContents} webContents Findable web contents. The parent window will be undefined.
59
+ * @param {WebContents} [webContents] Custom findable web contents. If not provided, the win.contentView.children[0] will be used.
59
60
  * @returns {Findbar} The findbar instance if it exists.
60
61
  * @throws {Error} If no webContents is provided.
61
62
  */
62
63
  constructor (parent, webContents) {
63
64
  if (isFindable(parent)) {
64
- this.#parent = void 0
65
65
  this.#findableContents = parent
66
+ this.#parent = Findbar.#getBaseWindowFromWebContents(this.#findableContents)
66
67
  } else {
67
68
  this.#parent = parent
68
- this.#findableContents = webContents ?? parent.webContents
69
+ this.#findableContents = webContents ?? Findbar.#retrieveWebContents(parent)
69
70
  }
70
71
 
71
72
  if (!this.#findableContents) {
@@ -74,7 +75,7 @@ class Findbar {
74
75
 
75
76
  this.#findableContents._findbar = this
76
77
  }
77
-
78
+
78
79
  /**
79
80
  * Open the findbar. If the findbar is already opened, focus the input text.
80
81
  * @returns {void}
@@ -92,7 +93,6 @@ class Findbar {
92
93
  this.#registerListeners()
93
94
 
94
95
  this.#windowHandler && this.#windowHandler(this.#window)
95
-
96
96
  this.#window.loadFile(`${__dirname}/web/findbar.html`)
97
97
  }
98
98
 
@@ -101,10 +101,25 @@ class Findbar {
101
101
  * @returns {void}
102
102
  */
103
103
  close() {
104
- if (!this.#window || this.#window.isDestroyed()) { return }
105
- if (!this.#window.isVisible()) { this.#window.close(); return }
106
- this.#window.on('hide', () => { this.#window.close() })
107
- this.#window.hide()
104
+ if (this.#window && !this.#window.isDestroyed()) {
105
+ this.#window.close()
106
+ }
107
+ }
108
+
109
+ /**
110
+ * Detach the findbar from the web contents and close it if opened. After detaching, the findbar instance will be unusable.
111
+ * @returns {void}
112
+ */
113
+ detach() {
114
+ this.close()
115
+ this.#findableContents._findbar = void 0
116
+ if (this.#window) { this.#window.webContents._findbar = void 0 }
117
+ }
118
+
119
+ setTheme(theme) {
120
+ if (this.#window && !this.#window.isDestroyed()) {
121
+ this.#window.webContents.send('electron-findbar/set-theme', theme)
122
+ }
108
123
  }
109
124
 
110
125
  /**
@@ -235,6 +250,32 @@ class Findbar {
235
250
  this.#boundsHandler = boundsHandler
236
251
  }
237
252
 
253
+ #registerKeyboardShortcuts(event, input) {
254
+ if (input.meta || input.control || input.alt) { return }
255
+
256
+ const key = input.key.toLowerCase()
257
+
258
+ if (input.shift) {
259
+ if (key === 'enter') {
260
+ this.findPrevious()
261
+ event.preventDefault()
262
+ }
263
+ return;
264
+ }
265
+
266
+ if (key === 'enter') {
267
+ this.findNext()
268
+ event.preventDefault()
269
+ return;
270
+ }
271
+
272
+ if (key === 'escape') {
273
+ if (this.isOpen()) {
274
+ this.close()
275
+ event.preventDefault()
276
+ }
277
+ }
278
+ }
238
279
  /**
239
280
  * @param {Electron.FindInPageOptions} options
240
281
  */
@@ -281,6 +322,7 @@ class Findbar {
281
322
  })
282
323
 
283
324
  this.#window.prependOnceListener('ready-to-show', () => { this.#window.show() })
325
+ this.#window.webContents.prependListener('before-input-event', this.#registerKeyboardShortcuts.bind(this))
284
326
 
285
327
  this.#findableContents.prependOnceListener('destroyed', () => { this.close() })
286
328
  this.#findableContents.prependListener('found-in-page', (_e, result) => { this.#sendMatchesCount(result.activeMatchOrdinal, result.matches) })
@@ -310,6 +352,24 @@ class Findbar {
310
352
  this.#window.webContents.send('electron-findbar/input-focus')
311
353
  }
312
354
 
355
+ /**
356
+ * Retrieve web contents from a BrowserWindow or BaseWindow.
357
+ * @param {BrowserWindow | BaseWindow} window
358
+ * @returns {WebContents | undefined} The web contents if any.
359
+ */
360
+ static #retrieveWebContents(window) {
361
+ return window.webContents ?? window.contentView?.children[0]
362
+ }
363
+
364
+ /**
365
+ * Get the parent window from web contents.
366
+ * @param {WebContents} cont
367
+ * @returns {BaseWindow | undefined} Parent window if any.
368
+ */
369
+ static #getBaseWindowFromWebContents(cont) {
370
+ return BaseWindow.getAllWindows().find(win => win.webContents === cont || win.contentView.children.some(child => child.webContents === cont))
371
+ }
372
+
313
373
  /**
314
374
  * Set default findbar position.
315
375
  * @param {Rectangle} parentBounds
@@ -358,25 +418,25 @@ class Findbar {
358
418
  * If no findbar instance exists, it will return a new one linked to the web contents.
359
419
  *
360
420
  * @overload
361
- * @param {BrowserWindow} browserWindow Parent window.
362
- * @param {WebContents} [customWebContents] Custom findable web contents. If not provided, the web contents of the BrowserWindow will be used.
421
+ * @param {WebContents} webContents Findable web contents. The parent window will be defined by using BaseWindow.getAllWindows() and
422
+ * matching the webContents with the webContents of the window or its contentView children.
363
423
  * @returns {Findbar} The findbar instance if it exists.
424
+ * @throws {Error} If no webContents is provided.
364
425
  *
365
426
  * @overload
366
- * @param {WebContents} webContents Findable web contents. The parent window will be undefined.
427
+ * @param {BrowserWindow} browserWindow Parent window.
428
+ * @param {WebContents} [customWebContents] Custom findable web contents. If not provided, the browserWindow.webContents will be used.
367
429
  * @returns {Findbar} The findbar instance if it exists.
368
- * @throws {Error} If no webContents is provided.
369
- *
430
+ *
370
431
  * @overload
371
432
  * @param {BaseWindow} baseWindow Parent window.
372
- * @param {WebContents} webContents Findable web contents.
433
+ * @param {WebContents} [webContents] Custom findable web contents. If not provided, the win.contentView.children[0] will be used.
373
434
  * @returns {Findbar} The findbar instance if it exists.
374
435
  * @throws {Error} If no webContents is provided.
375
436
  */
376
437
  static from(windowOrWebContents, customWebContents) {
377
- let webContents = isFindable(windowOrWebContents) ?
378
- windowOrWebContents : customWebContents ?? windowOrWebContents.webContents
379
-
438
+ const webContents = isFindable(windowOrWebContents) ? windowOrWebContents : customWebContents ?? Findbar.#retrieveWebContents(windowOrWebContents)
439
+ if (!webContents) { throw new Error('[Findbar] There are no searchable web contents.') }
380
440
  return webContents._findbar || new Findbar(windowOrWebContents, customWebContents)
381
441
  }
382
442
 
@@ -386,7 +446,9 @@ class Findbar {
386
446
  * @returns {Findbar | undefined} The findbar instance if it exists, otherwise undefined.
387
447
  */
388
448
  static fromIfExists(windowOrWebContents) {
389
- return (isFindable(windowOrWebContents) ? windowOrWebContents : windowOrWebContents.webContents)._findbar
449
+ const webContents = isFindable(windowOrWebContents) ? windowOrWebContents : Findbar.#retrieveWebContents(windowOrWebContents)
450
+ if (!webContents) { throw new Error('[Findbar] There are no searchable web contents.') }
451
+ return webContents._findbar
390
452
  }
391
453
  }
392
454
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "electron-findbar",
3
- "version": "2.0.0",
3
+ "version": "3.0.0",
4
4
  "description": "Chrome-like findbar for your Electron app.",
5
5
  "main": "index.js",
6
6
  "files": [