jupyterlab_advanced_image_viewer_extension 1.1.3 → 1.1.4

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/lib/controller.js CHANGED
@@ -53,7 +53,11 @@ export class ViewerController {
53
53
  this.zoomAt(factor, e.clientX - r.left, e.clientY - r.top);
54
54
  };
55
55
  this.onDown = (e) => {
56
+ // preventDefault stops the browser dragging the image out, and with it the
57
+ // focus a click would give the viewer; focus it here, or after a toolbar
58
+ // click the stock keys and Left/Right go to the button instead.
56
59
  e.preventDefault();
60
+ this.host.focus();
57
61
  this.panning = true;
58
62
  this.start = { x: e.clientX, y: e.clientY, tx: this.tx, ty: this.ty };
59
63
  this.host.style.cursor = 'grabbing';
package/lib/index.js CHANGED
@@ -98,6 +98,7 @@ const plugin = {
98
98
  widget.toolbar.addItem('advanced-refresh', new ToolbarButton({
99
99
  icon: refreshIcon,
100
100
  tooltip: 'Reload image from disk',
101
+ noFocusOnClick: true,
101
102
  onClick: async () => {
102
103
  await widget.context.revert();
103
104
  widget.update();
@@ -106,16 +107,19 @@ const plugin = {
106
107
  widget.toolbar.addItem('advanced-zoom-out', new ToolbarButton({
107
108
  label: '-',
108
109
  tooltip: 'Zoom out',
110
+ noFocusOnClick: true,
109
111
  onClick: () => app.commands.execute(CommandIDs.zoomOut)
110
112
  }));
111
113
  widget.toolbar.addItem('advanced-zoom-in', new ToolbarButton({
112
114
  label: '+',
113
115
  tooltip: 'Zoom in',
116
+ noFocusOnClick: true,
114
117
  onClick: () => app.commands.execute(CommandIDs.zoomIn)
115
118
  }));
116
119
  widget.toolbar.addItem('advanced-reset-fit', new ToolbarButton({
117
120
  label: 'Fit',
118
121
  tooltip: 'Reset to fit',
122
+ noFocusOnClick: true,
119
123
  onClick: () => app.commands.execute(CommandIDs.resetFit)
120
124
  }));
121
125
  widget.toolbar.addItem('advanced-spacer', Toolbar.createSpacerItem());
@@ -226,12 +230,17 @@ const plugin = {
226
230
  // sort plugin decide what comes next: a C-locale sort puts '.' before
227
231
  // '_' where localeCompare puts '_' first, and a re-sort here skipped
228
232
  // files. FileBrowser keeps `listing` protected, but it is the only
229
- // source of the displayed order. Otherwise fall back to natural name
230
- // order, the stock file browser default.
233
+ // source of the displayed order. The listing re-sorts on a refresh only
234
+ // while it is visible, so a hidden one is re-sorted here; re-sorting a
235
+ // visible one would re-render every row on each step. Otherwise fall
236
+ // back to natural name order, the stock file browser default.
231
237
  let images = [];
232
238
  if (fileBrowser && fileBrowser.model.path === dirPath) {
233
239
  const shown = fileBrowser
234
240
  .listing;
241
+ if (!shown.isVisible) {
242
+ shown.sort(shown.sortState);
243
+ }
235
244
  images = Array.from(shown.sortedItems()).filter(isImage);
236
245
  }
237
246
  if (!images.some(item => item.path === path)) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "jupyterlab_advanced_image_viewer_extension",
3
- "version": "1.1.3",
3
+ "version": "1.1.4",
4
4
  "description": "JupyterLab extension for advanced image viewing: fit-to-screen, cursor-anchored wheel zoom, drag-to-pan, and Left/Right arrow keys to move to the previous and next image in a folder",
5
5
  "keywords": [
6
6
  "jupyter",
@@ -297,6 +297,23 @@ describe('ViewerController', () => {
297
297
  c.dispose();
298
298
  });
299
299
 
300
+ // DEF-KEYS-2: the mousedown handler cancels the browser's image drag, which
301
+ // also cancels the focus a click gives; the viewer must take focus itself.
302
+ it('takes keyboard focus from a toolbar button when the image is pressed', () => {
303
+ host.tabIndex = 0;
304
+ const button = document.createElement('button');
305
+ document.body.appendChild(button);
306
+ new ViewerController(host, img, 0.1);
307
+ button.focus();
308
+ expect(document.activeElement).toBe(button);
309
+ host.dispatchEvent(
310
+ new MouseEvent('mousedown', { clientX: 0, clientY: 0, cancelable: true })
311
+ );
312
+ window.dispatchEvent(new MouseEvent('mouseup'));
313
+ expect(document.activeElement).toBe(host);
314
+ button.remove();
315
+ });
316
+
300
317
  it('stops observing the host and the image on dispose', () => {
301
318
  stubGeometry(80, 60);
302
319
  const c = new ViewerController(host, img, 0.1);
package/src/controller.ts CHANGED
@@ -147,7 +147,11 @@ export class ViewerController {
147
147
  }
148
148
 
149
149
  private onDown = (e: MouseEvent): void => {
150
+ // preventDefault stops the browser dragging the image out, and with it the
151
+ // focus a click would give the viewer; focus it here, or after a toolbar
152
+ // click the stock keys and Left/Right go to the button instead.
150
153
  e.preventDefault();
154
+ this.host.focus();
151
155
  this.panning = true;
152
156
  this.start = { x: e.clientX, y: e.clientY, tx: this.tx, ty: this.ty };
153
157
  this.host.style.cursor = 'grabbing';
package/src/index.ts CHANGED
@@ -147,6 +147,7 @@ const plugin: JupyterFrontEndPlugin<void> = {
147
147
  new ToolbarButton({
148
148
  icon: refreshIcon,
149
149
  tooltip: 'Reload image from disk',
150
+ noFocusOnClick: true,
150
151
  onClick: async () => {
151
152
  await widget.context.revert();
152
153
  widget.update();
@@ -158,6 +159,7 @@ const plugin: JupyterFrontEndPlugin<void> = {
158
159
  new ToolbarButton({
159
160
  label: '-',
160
161
  tooltip: 'Zoom out',
162
+ noFocusOnClick: true,
161
163
  onClick: () => app.commands.execute(CommandIDs.zoomOut)
162
164
  })
163
165
  );
@@ -166,6 +168,7 @@ const plugin: JupyterFrontEndPlugin<void> = {
166
168
  new ToolbarButton({
167
169
  label: '+',
168
170
  tooltip: 'Zoom in',
171
+ noFocusOnClick: true,
169
172
  onClick: () => app.commands.execute(CommandIDs.zoomIn)
170
173
  })
171
174
  );
@@ -174,6 +177,7 @@ const plugin: JupyterFrontEndPlugin<void> = {
174
177
  new ToolbarButton({
175
178
  label: 'Fit',
176
179
  tooltip: 'Reset to fit',
180
+ noFocusOnClick: true,
177
181
  onClick: () => app.commands.execute(CommandIDs.resetFit)
178
182
  })
179
183
  );
@@ -296,12 +300,17 @@ const plugin: JupyterFrontEndPlugin<void> = {
296
300
  // sort plugin decide what comes next: a C-locale sort puts '.' before
297
301
  // '_' where localeCompare puts '_' first, and a re-sort here skipped
298
302
  // files. FileBrowser keeps `listing` protected, but it is the only
299
- // source of the displayed order. Otherwise fall back to natural name
300
- // order, the stock file browser default.
303
+ // source of the displayed order. The listing re-sorts on a refresh only
304
+ // while it is visible, so a hidden one is re-sorted here; re-sorting a
305
+ // visible one would re-render every row on each step. Otherwise fall
306
+ // back to natural name order, the stock file browser default.
301
307
  let images: Array<{ name: string; path: string; type: string }> = [];
302
308
  if (fileBrowser && fileBrowser.model.path === dirPath) {
303
309
  const shown = (fileBrowser as unknown as { listing: DirListing })
304
310
  .listing;
311
+ if (!shown.isVisible) {
312
+ shown.sort(shown.sortState);
313
+ }
305
314
  images = Array.from(shown.sortedItems()).filter(isImage);
306
315
  }
307
316
  if (!images.some(item => item.path === path)) {