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 +4 -0
- package/lib/index.js +11 -2
- package/package.json +1 -1
- package/src/__tests__/controller.spec.ts +17 -0
- package/src/controller.ts +4 -0
- package/src/index.ts +11 -2
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.
|
|
230
|
-
//
|
|
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
|
+
"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.
|
|
300
|
-
//
|
|
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)) {
|