@fails-components/jupyter-applet-view 0.0.1-alpha.10

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.
@@ -0,0 +1,751 @@
1
+ import { WidgetRenderer } from '@jupyter-widgets/jupyterlab-manager';
2
+ import { nullTranslator } from '@jupyterlab/translation';
3
+ import { notebookIcon } from '@jupyterlab/ui-components';
4
+ import { ArrayExt } from '@lumino/algorithm';
5
+ import { UUID } from '@lumino/coreutils';
6
+ import { Signal } from '@lumino/signaling';
7
+ import { AccordionPanel, Panel, BoxLayout } from '@lumino/widgets';
8
+ import { domToBlob } from 'modern-screenshot';
9
+ // portions used from Jupyterlab:
10
+ /* -----------------------------------------------------------------------------
11
+ | Copyright (c) Jupyter Development Team.
12
+ | Distributed under the terms of the Modified BSD License.
13
+ |----------------------------------------------------------------------------*/
14
+ // This code contains portions from or is inspired by Jupyter lab's notebook extension, especially the createOutputView part
15
+ // Also a lot is taken from the cell toolbar related parts.
16
+ /**
17
+ * A widget hosting applet views
18
+ */
19
+ export class AppletViewOutputArea extends AccordionPanel {
20
+ constructor(options) {
21
+ super({ renderer: new AppletViewRenderer() });
22
+ this._viewChanged = new Signal(this);
23
+ const trans = (options.translator || nullTranslator).load('jupyterlab');
24
+ this._notebook = options.notebook;
25
+ this._inLecture = false;
26
+ this._interceptor = options.interceptor;
27
+ if (options.applets !== undefined) {
28
+ this._applets = options.applets.map(({ parts, appid: oldAppid, appname }, index) => {
29
+ const appid = oldAppid !== null && oldAppid !== void 0 ? oldAppid : UUID.uuid4();
30
+ return {
31
+ appid,
32
+ appname: appname || 'Applet ' + (index + 1),
33
+ observer: new ResizeObserver((entries, observer) => this.resizeEvent(appid, entries, observer)),
34
+ parts: parts.map(el => {
35
+ var _a;
36
+ return new AppletViewOutputAreaPart({
37
+ index: (_a = el.index) !== null && _a !== void 0 ? _a : -1,
38
+ cell: el.cell || undefined,
39
+ notebook: this._notebook
40
+ });
41
+ })
42
+ };
43
+ });
44
+ }
45
+ else {
46
+ const appid = UUID.uuid4();
47
+ this._applets = [];
48
+ this.addApplet({ appid, appname: 'Applet 1' });
49
+ }
50
+ this.id = `AppletView-${UUID.uuid4()}`;
51
+ this.title.label = 'Applets Preview';
52
+ this.title.icon = notebookIcon;
53
+ this.title.caption = this._notebook.title.label
54
+ ? trans.__('For Notebook: %1', this._notebook.title.label)
55
+ : trans.__('For Notebook:');
56
+ this.addClass('fl-jp-AppletView');
57
+ // Wait for the notebook to be loaded before
58
+ // cloning the output area.
59
+ void this._notebook.context.ready.then(() => {
60
+ this._applets.forEach(({ parts, appid }) => {
61
+ // TODO: Count applets
62
+ parts.forEach((part, index) => {
63
+ if (!part.cell &&
64
+ typeof part.index !== 'undefined' &&
65
+ part.index >= 0) {
66
+ const currentcell = this._notebook.content.widgets[part.index];
67
+ part.cell = currentcell;
68
+ const codeCell = part.cell;
69
+ const outputAreaModel = codeCell.outputArea.model;
70
+ for (let i = 0; i < outputAreaModel.length; i++) {
71
+ const cur = outputAreaModel.get(i);
72
+ cur.changed.connect(() => {
73
+ // console.log('Model changed', i, cur, outputAreaModel.get(i));
74
+ });
75
+ }
76
+ }
77
+ if (!part.cell /* || part.cell.model.type !== 'code' */) {
78
+ // this.dispose(); // no dispose, just do not add
79
+ return;
80
+ }
81
+ if (part.added) {
82
+ return; // already added
83
+ }
84
+ part.clone = this.addCell(appid, part.cell, part.id || 'undefinedid');
85
+ if (part.cell.model.type === 'code') {
86
+ let managerProm;
87
+ for (const codecell of part.cell.outputArea.widgets) {
88
+ // We use Array.from instead of using Lumino 2 (JLab 4) iterator
89
+ // This is to support Lumino 1 (JLab 3) as well
90
+ for (const output of Array.from(codecell.children())) {
91
+ if (output instanceof WidgetRenderer) {
92
+ if (output['_manager']) {
93
+ managerProm = output['_manager'].promise;
94
+ }
95
+ }
96
+ }
97
+ }
98
+ managerProm === null || managerProm === void 0 ? void 0 : managerProm.then(manager => {
99
+ for (const codecell of part.clone.widgets) {
100
+ for (const output of Array.from(codecell.children())) {
101
+ if (output instanceof WidgetRenderer) {
102
+ output.manager = manager;
103
+ }
104
+ }
105
+ }
106
+ });
107
+ }
108
+ });
109
+ });
110
+ this._viewChanged.emit();
111
+ });
112
+ }
113
+ cloneCell(cell, cellid) {
114
+ if (cell.model.type === 'code') {
115
+ const codeCell = cell;
116
+ const clone = codeCell.cloneOutputArea();
117
+ if (this._interceptor) {
118
+ let unsupported = false;
119
+ const outputs = codeCell.model.outputs;
120
+ for (let i = 0; i < outputs.length; i++) {
121
+ const outputModel = outputs.get(i);
122
+ const keys = Object.keys(outputModel.data);
123
+ if (keys.some(key => { var _a; return !((_a = this._interceptor) === null || _a === void 0 ? void 0 : _a.isMimeTypeSupported(key)); })) {
124
+ unsupported = true;
125
+ break;
126
+ }
127
+ }
128
+ if (unsupported) {
129
+ clone.addClass('fl-jl-cell-interceptor-unsupported');
130
+ }
131
+ else {
132
+ clone.removeClass('fl-jl-cell-interceptor-unsupported');
133
+ }
134
+ }
135
+ // @ts-expect-error cellid does not exist on type
136
+ clone.cellid = cellid;
137
+ // @ts-expect-error cellid does not exist on type
138
+ clone.widgetid = UUID.uuid4();
139
+ return clone;
140
+ }
141
+ else {
142
+ const clone = cell.clone();
143
+ // @ts-expect-error cellid does not exist on type
144
+ clone.cellid = cellid;
145
+ // @ts-expect-error cellid does not exist on type
146
+ clone.widgetid = UUID.uuid4();
147
+ return clone;
148
+ }
149
+ }
150
+ getWidgetAppId(widgetid) {
151
+ const index = this.widgets.findIndex(el =>
152
+ // @ts-expect-error widgetid does not exist on type
153
+ Array.from(el.children()).some(el => el.widgetid === widgetid));
154
+ if (index === -1) {
155
+ return;
156
+ }
157
+ return this._applets[index].appid;
158
+ }
159
+ addToObserver(appIndex, widget) {
160
+ const observer = this._applets[appIndex].observer;
161
+ observer.observe(widget.node, { box: 'border-box' });
162
+ }
163
+ removeFromObserver(appIndex, widget) {
164
+ const observer = this._applets[appIndex].observer;
165
+ observer.unobserve(widget.node);
166
+ }
167
+ addCell(appid, cell, cellid) {
168
+ const appIndex = this._applets.findIndex(applet => applet.appid === appid);
169
+ if (appIndex === -1) {
170
+ throw new Error('Applet not found in addcell');
171
+ }
172
+ const app = this.widgets[appIndex];
173
+ const clone = this.cloneCell(cell, cellid);
174
+ this.addToObserver(appIndex, clone);
175
+ app.addWidget(clone);
176
+ // this.informResize(this._applets[appIndex]) // not neccessary
177
+ // trigger an update ?
178
+ this._viewChanged.emit();
179
+ return clone;
180
+ }
181
+ insertCell(appid, index, cell, cellid) {
182
+ const appIndex = this._applets.findIndex(applet => applet.appid === appid);
183
+ if (appIndex === -1) {
184
+ return;
185
+ }
186
+ const clone = this.cloneCell(cell, cellid);
187
+ const app = this.widgets[appIndex];
188
+ this.addToObserver(appIndex, clone);
189
+ const layout = app.layout;
190
+ layout.insertWidget(index, clone);
191
+ // this.informResize(this._applets[appIndex]) // not neccessary
192
+ // trigger an update ?
193
+ this._viewChanged.emit();
194
+ return clone;
195
+ }
196
+ deletePart(appid, cellid) {
197
+ const appIndex = this._applets.findIndex(applet => applet.appid === appid);
198
+ if (appIndex === -1) {
199
+ return;
200
+ }
201
+ const applet = this._applets[appIndex];
202
+ const todeleteIndex = applet.parts.findIndex(part => part.id === cellid);
203
+ if (todeleteIndex === -1) {
204
+ return;
205
+ }
206
+ const removedPart = applet.parts.splice(todeleteIndex, 1);
207
+ if (removedPart.length > 0) {
208
+ const cell = removedPart[0].cell;
209
+ if (typeof cell !== 'undefined') {
210
+ this.removeFromObserver(appIndex, cell);
211
+ }
212
+ }
213
+ const app = this.widgets[appIndex];
214
+ const layout = app.layout;
215
+ layout.removeWidgetAt(todeleteIndex);
216
+ this.informResize(this._applets[appIndex]);
217
+ // trigger an update ?
218
+ this._viewChanged.emit();
219
+ }
220
+ movePart(appid, cellid, delta) {
221
+ const appIndex = this._applets.findIndex(applet => applet.appid === appid);
222
+ if (appIndex === -1) {
223
+ return;
224
+ }
225
+ const applet = this._applets[appIndex];
226
+ const tomoveIndex = applet.parts.findIndex(part => part.id === cellid);
227
+ if (tomoveIndex === -1) {
228
+ return;
229
+ }
230
+ if (tomoveIndex + delta < 0) {
231
+ return;
232
+ }
233
+ if (tomoveIndex + delta >= applet.parts.length) {
234
+ return;
235
+ }
236
+ const [moveme] = applet.parts.splice(tomoveIndex, 1);
237
+ applet.parts.splice(tomoveIndex + delta + (delta > 1 ? -1 : 0), 0, moveme);
238
+ const app = this.widgets[appIndex];
239
+ const layout = app.layout;
240
+ layout.insertWidget(tomoveIndex + delta, layout.widgets[tomoveIndex]);
241
+ this.informResize(this._applets[appIndex]);
242
+ // trigger an update ?
243
+ this._viewChanged.emit();
244
+ }
245
+ /*
246
+ canMoveApp(appid: string, cellid: string, delta: number): boolean {
247
+ console.log('canmoveapp debug');
248
+ const appIndex = this._applets.findIndex(
249
+ applet => applet.appid === appid
250
+ );
251
+ if (appIndex + delta < 0) {
252
+ return false;
253
+ }
254
+ if (appIndex + delta >= this._applets.length) {
255
+ // only add new apps, if current app will not be empty
256
+ if (this._applets[appIndex].parts.length <= 1) {
257
+ return false;
258
+ }
259
+ }
260
+ return true;
261
+ }
262
+ */
263
+ moveApp(appid, cellid, delta) {
264
+ const appIndex = this._applets.findIndex(applet => applet.appid === appid);
265
+ if (appIndex === -1) {
266
+ return;
267
+ }
268
+ const applet = this._applets[appIndex];
269
+ const partIndex = applet.parts.findIndex(part => part.id === cellid);
270
+ if (partIndex === -1) {
271
+ return;
272
+ }
273
+ if (delta === 0) {
274
+ return;
275
+ }
276
+ if (appIndex + delta < 0) {
277
+ return;
278
+ }
279
+ if (appIndex + delta >= this._applets.length) {
280
+ // only add new apps, if current app will not be empty
281
+ if (this._applets[appIndex].parts.length <= 1) {
282
+ return false;
283
+ }
284
+ // in this case we create a new app
285
+ this.addApplet({ appid: UUID.uuid4() });
286
+ }
287
+ const destApplet = this._applets[appIndex + delta];
288
+ if (destApplet.parts.some(el => el.id === cellid)) {
289
+ // per convention an elment can not be added twice to an app
290
+ return;
291
+ }
292
+ let destPartIndex = 0;
293
+ if (delta < 0) {
294
+ destPartIndex = destApplet.parts.length;
295
+ }
296
+ /*console.log(
297
+ 'app move me debug 0',
298
+ applet.parts.map(el => el.id).join(',')
299
+ );
300
+ console.log(
301
+ 'app move me debug 0 dst',
302
+ destApplet.parts.map(el => el.id).join(',')
303
+ );*/
304
+ const [moveme] = applet.parts.splice(partIndex, 1); // remove
305
+ destApplet.parts.splice(destPartIndex, 0, moveme);
306
+ /* console.log(
307
+ 'app move me debug 1',
308
+ applet.parts.map(el => el.id).join(',')
309
+ );
310
+ console.log(
311
+ 'app move me debug 1 dst',
312
+ destApplet.parts.map(el => el.id).join(',')
313
+ );*/
314
+ const srcApp = this.widgets[appIndex];
315
+ const destApp = this.widgets[appIndex + delta];
316
+ const srcLayout = srcApp.layout;
317
+ const destLayout = destApp.layout;
318
+ const widget = srcLayout.widgets[partIndex];
319
+ this.removeFromObserver(appIndex, widget);
320
+ this.addToObserver(appIndex + delta, widget);
321
+ destLayout.insertWidget(destPartIndex, widget);
322
+ // srcLayout.removeWidgetAt(partIndex); // not necessary
323
+ if (appIndex === this._applets.length - 1 && applet.parts.length === 0) {
324
+ // if the last applet is empty, we remove it
325
+ this._applets.splice(appIndex, 1);
326
+ const appSrcLayout = this.layout;
327
+ appSrcLayout.removeWidgetAt(appIndex);
328
+ }
329
+ this.informResize(this._applets[appIndex]);
330
+ this.informResize(this._applets[appIndex + delta]);
331
+ // trigger an update ?
332
+ this._viewChanged.emit();
333
+ }
334
+ saveData() {
335
+ const applets = this._applets.map(applet => ({
336
+ parts: applet.parts.map(part => ({
337
+ index: part.index,
338
+ id: part.id
339
+ })),
340
+ appid: applet.appid,
341
+ appname: applet.appname
342
+ }));
343
+ return { applets };
344
+ }
345
+ loadData(data) {
346
+ var _a;
347
+ if (!data) {
348
+ return;
349
+ }
350
+ let applets = data.applets;
351
+ if (data.parts && typeof applets === 'undefined') {
352
+ applets = [{ appid: UUID.uuid4(), parts: data.parts }];
353
+ }
354
+ if (!Array.isArray(applets) ||
355
+ applets.some(({ parts }) => !Array.isArray(parts))) {
356
+ return;
357
+ }
358
+ // clear applets
359
+ this._applets = [];
360
+ if (this.layout) {
361
+ this.layout.widgets.forEach((widget) => { var _a; return (_a = this.layout) === null || _a === void 0 ? void 0 : _a.removeWidget(widget); });
362
+ }
363
+ if (applets.length === 0) {
364
+ // we need a minimum of 1 applet!
365
+ const appid = UUID.uuid4();
366
+ this.addApplet({ appid, appname: 'Applet 1' });
367
+ return;
368
+ }
369
+ for (const applet of applets) {
370
+ const appid = (_a = applet.appid) !== null && _a !== void 0 ? _a : UUID.uuid4();
371
+ const appname = applet.appname;
372
+ this.addApplet({ appid, appname });
373
+ if (typeof this._selectedAppid !== 'undefined' &&
374
+ appid !== this._selectedAppid) {
375
+ this.collapse(this._applets.length - 1);
376
+ }
377
+ for (const part of applet.parts) {
378
+ if (typeof part.index !== 'undefined' || part.id) {
379
+ this.addPart(appid, {
380
+ index: part.index,
381
+ id: part.id
382
+ });
383
+ }
384
+ }
385
+ }
386
+ }
387
+ addApplet({ appid, appname }) {
388
+ // figure out, if it is already added
389
+ let appletIndex = this._applets.findIndex(applet => applet.appid === appid);
390
+ if (appletIndex !== -1) {
391
+ return this.widgets[appletIndex];
392
+ }
393
+ // TODO add element to widgets
394
+ appletIndex = this._applets.length;
395
+ appname = appname || 'Applet ' + Math.random().toString(36).slice(2, 6);
396
+ this._applets.push({
397
+ appid,
398
+ appname: appname,
399
+ observer: new ResizeObserver((entries, observer) => this.resizeEvent(appid, entries, observer)),
400
+ parts: []
401
+ });
402
+ const layout = this.layout;
403
+ const panel = new Panel({});
404
+ BoxLayout.setStretch(panel, 1);
405
+ panel.addClass('fl-jp-Applet');
406
+ panel.title.label = appname;
407
+ panel.title.caption = panel.title.label;
408
+ panel.title.changed.connect((title) => {
409
+ this._applets[appletIndex].appname = title.label;
410
+ this._viewChanged.emit();
411
+ });
412
+ layout.insertWidget(appletIndex, panel);
413
+ return panel;
414
+ }
415
+ addPart(appidOrUndefined, part) {
416
+ var _a;
417
+ const topush = new AppletViewOutputAreaPart({
418
+ index: part.index !== undefined ? part.index : -1,
419
+ cell: part.cell || undefined,
420
+ id: part.id || undefined,
421
+ notebook: this._notebook
422
+ });
423
+ let appletIndex = typeof appidOrUndefined === 'undefined'
424
+ ? 0
425
+ : this._applets.findIndex(applet => applet.appid === appidOrUndefined);
426
+ if (appletIndex === -1) {
427
+ appletIndex = 0;
428
+ }
429
+ const appid = this._applets[appletIndex].appid;
430
+ const applet = this._applets[appletIndex];
431
+ // we need to figure out, if it is already added
432
+ if (applet.parts.some(el => el.id === topush.id ||
433
+ (typeof el.cell !== 'undefined' && el.cell === topush.cell))) {
434
+ return;
435
+ }
436
+ (_a = this._notebook.content.model) === null || _a === void 0 ? void 0 : _a.cells.changed.connect((sender) => {
437
+ var _a;
438
+ for (const cell of sender) {
439
+ if (cell.id === topush.id) {
440
+ // we found it and are happy that it is still there
441
+ // but is it still the same
442
+ const index = ArrayExt.findFirstIndex(this._notebook.content.widgets, wcell => wcell === topush.cell);
443
+ if (index !== -1) {
444
+ return;
445
+ } // still the same cell
446
+ const oldclone = topush.clone;
447
+ const partind = applet.parts.indexOf(topush); // our position in the list
448
+ const newindex = ArrayExt.findFirstIndex(this._notebook.content.widgets, wcell => { var _a; return wcell.id === ((_a = topush.cell) === null || _a === void 0 ? void 0 : _a.id); });
449
+ if (newindex === -1) {
450
+ throw new Error('Cell does not exist');
451
+ }
452
+ topush.cell = this._notebook.content.widgets[newindex];
453
+ oldclone === null || oldclone === void 0 ? void 0 : oldclone.dispose();
454
+ topush.clone = this.insertCell(applet.appid, partind, topush.cell, topush.id);
455
+ return;
456
+ }
457
+ }
458
+ // not found case, it is gone forever so remove from parts and dispose
459
+ const appIndex = this._applets.findIndex(applet => applet.appid === appid);
460
+ if (appIndex === -1) {
461
+ return;
462
+ }
463
+ const apps = this._applets[appIndex];
464
+ const ind = apps.parts.indexOf(topush);
465
+ if (ind !== -1) {
466
+ apps.parts.splice(ind, 1);
467
+ }
468
+ (_a = topush.clone) === null || _a === void 0 ? void 0 : _a.dispose();
469
+ });
470
+ applet.parts.push(topush);
471
+ if (this._notebook.context.isReady) {
472
+ // it is already ready, so we can not rely on the global code for adding to the view
473
+ if (!topush.cell &&
474
+ typeof part.index !== 'undefined' &&
475
+ part.index >= 0) {
476
+ topush.cell = this._notebook.content.widgets[part.index];
477
+ }
478
+ if (topush.cell) {
479
+ topush.clone = this.addCell(appid, topush.cell, topush.id || 'undefinedid');
480
+ }
481
+ }
482
+ // trigger an update ?
483
+ this._viewChanged.emit();
484
+ }
485
+ firstHasIndex(index) {
486
+ if (this._applets.length === 0) {
487
+ return false;
488
+ }
489
+ return this._applets[0].parts.some(el => el.index === index);
490
+ }
491
+ selectApplet(selectedAppid) {
492
+ this._selectedAppid = selectedAppid;
493
+ for (let i = 0; i < this._applets.length; i++) {
494
+ const applet = this._applets[i];
495
+ if (applet.appid === selectedAppid) {
496
+ this.expand(i);
497
+ }
498
+ else {
499
+ this.collapse(i);
500
+ }
501
+ }
502
+ }
503
+ unselectApplet() {
504
+ for (let i = 0; i < this._applets.length; i++) {
505
+ this.expand(i);
506
+ }
507
+ }
508
+ async takeAppScreenshot(opts) {
509
+ if (typeof this._selectedAppid === 'undefined') {
510
+ throw new Error('No app selected');
511
+ }
512
+ const { dpi = undefined } = opts;
513
+ const appletIndex = this._applets.findIndex(applet => applet.appid === this._selectedAppid);
514
+ if (appletIndex === -1) {
515
+ throw new Error('No invalid app selected');
516
+ }
517
+ try {
518
+ const blob = await domToBlob(this.widgets[appletIndex].node, {
519
+ maximumCanvasSize: 4096,
520
+ scale: (dpi && dpi / 96) || undefined
521
+ });
522
+ if (blob === null) {
523
+ return undefined;
524
+ }
525
+ return blob;
526
+ }
527
+ catch (error) {
528
+ //only throw if not returned
529
+ console.log('takeAppScreenshot error', error);
530
+ }
531
+ }
532
+ informResize(applet) {
533
+ // inform about the new sizes
534
+ let width = 0;
535
+ let height = 0;
536
+ for (const part of applet.parts) {
537
+ if (typeof part.sizes === 'undefined') {
538
+ continue;
539
+ }
540
+ const { width: ewidth, height: eheight } = part.sizes;
541
+ height += eheight;
542
+ width = Math.max(ewidth, width);
543
+ }
544
+ this._notebook.appletResizeinfo({
545
+ appid: applet.appid,
546
+ width,
547
+ height
548
+ });
549
+ }
550
+ resizeEvent(appid, entries, observer) {
551
+ const applet = this._applets.find(applet => applet.appid === appid);
552
+ if (typeof applet === 'undefined') {
553
+ return;
554
+ }
555
+ let updated = false;
556
+ for (const entry of entries) {
557
+ const part = applet.parts.find(part => { var _a; return ((_a = part === null || part === void 0 ? void 0 : part.clone) === null || _a === void 0 ? void 0 : _a.node) === entry.target; });
558
+ if (!part) {
559
+ continue;
560
+ }
561
+ if (!entry.borderBoxSize[0]) {
562
+ continue;
563
+ }
564
+ const size = entry.borderBoxSize[0];
565
+ if (size.inlineSize === 0 || size.blockSize === 0) {
566
+ continue;
567
+ } // do not store collapsed values
568
+ part.sizes = {
569
+ width: size.inlineSize,
570
+ height: size.blockSize
571
+ };
572
+ updated = true;
573
+ }
574
+ if (!updated) {
575
+ return;
576
+ }
577
+ this.informResize(applet);
578
+ }
579
+ /* hasId(id: string): boolean {
580
+ return this._parts.some(el => el.id === id);
581
+ } */
582
+ get applets() {
583
+ return this._applets;
584
+ }
585
+ /**
586
+ * The index of the cell in the notebook.
587
+ */
588
+ /*
589
+ get index(): number {
590
+ return this._cell
591
+ ? ArrayExt.findFirstIndex(
592
+ this._notebook.content.widgets,
593
+ c => c === this._cell
594
+ )
595
+ : this._index;
596
+ }
597
+ */
598
+ /**
599
+ * The path of the notebook for the cloned output area.
600
+ */
601
+ get path() {
602
+ return this._notebook.context.path;
603
+ }
604
+ get viewChanged() {
605
+ return this._viewChanged;
606
+ }
607
+ set inLecture(value) {
608
+ if (value === this._inLecture) {
609
+ return;
610
+ }
611
+ this._inLecture = value;
612
+ const splitLayout = this.layout;
613
+ if (value) {
614
+ splitLayout.titleSpace = 0;
615
+ }
616
+ else {
617
+ splitLayout.titleSpace = 22;
618
+ }
619
+ }
620
+ // override base class
621
+ handleEvent(event) {
622
+ if (event.type === 'click') {
623
+ const target = event.target;
624
+ if (target.tagName === 'SPAN' || target.tagName === 'INPUT') {
625
+ return;
626
+ }
627
+ }
628
+ if (['keydown', 'keypress', 'keyup'].includes(event.type)) {
629
+ const target = event.target;
630
+ if (target.tagName === 'INPUT') {
631
+ return; // do not call preventDefault
632
+ }
633
+ }
634
+ super.handleEvent(event);
635
+ }
636
+ }
637
+ export class AppletViewOutputAreaPart {
638
+ constructor(args) {
639
+ var _a, _b;
640
+ this._cloned = new Signal(this);
641
+ this._cell = args.cell;
642
+ this._index = (_a = args.index) !== null && _a !== void 0 ? _a : -1;
643
+ this._id = args.id || ((_b = this._cell) === null || _b === void 0 ? void 0 : _b.model.id);
644
+ this._notebook = args.notebook;
645
+ }
646
+ /**
647
+ * The index of the cell in the notebook.
648
+ */
649
+ get index() {
650
+ if (this._id) {
651
+ const ind = ArrayExt.findFirstIndex(this._notebook.content.widgets, c => c.model.id === this._id);
652
+ if (ind !== -1) {
653
+ return ind;
654
+ }
655
+ }
656
+ return this._cell
657
+ ? ArrayExt.findFirstIndex(this._notebook.content.widgets, c => c === this._cell)
658
+ : this._index;
659
+ }
660
+ get cell() {
661
+ return this._cell;
662
+ }
663
+ set cell(value) {
664
+ if ((value === null || value === void 0 ? void 0 : value.model.id) !== this._id) {
665
+ // throw new Error('Can not assign a cell with different id');
666
+ /* console.log(
667
+ 'ASSIGNING CELL with different id',
668
+ value?.model.id,
669
+ this._id
670
+ ); */
671
+ }
672
+ /* if (this._cell?.model.id !== value?.model.id) {
673
+ console.log('ASSIGNING CELL id change', value?.model.id, this._id);
674
+ } */
675
+ this._cell = value;
676
+ }
677
+ get added() {
678
+ return !!this._clone;
679
+ }
680
+ get id() {
681
+ return this._id;
682
+ }
683
+ set clone(value) {
684
+ this._clone = value;
685
+ this._cloned.emit(); // inform that we have been cloned
686
+ }
687
+ get clone() {
688
+ return this._clone;
689
+ }
690
+ get path() {
691
+ return this._notebook.context.path;
692
+ }
693
+ get cloned() {
694
+ return this._cloned;
695
+ }
696
+ }
697
+ export class AppletViewRenderer extends AccordionPanel.Renderer {
698
+ createSectionTitle(data) {
699
+ const handle = document.createElement('h3');
700
+ handle.setAttribute('tabindex', '0');
701
+ handle.id = this.createTitleKey(data);
702
+ handle.className = this.titleClassName;
703
+ for (const aData in data.dataset) {
704
+ handle.dataset[aData] = data.dataset[aData];
705
+ }
706
+ const collapser = handle.appendChild(this.createCollapseIcon(data));
707
+ collapser.className = 'lm-AccordionPanel-titleCollapser';
708
+ let title = data.caption || data.label;
709
+ const staticLabel = document.createElement('span');
710
+ staticLabel.className = 'lm-AccordionPanel-titleLabel';
711
+ staticLabel.textContent = data.label;
712
+ staticLabel.title = title;
713
+ handle.appendChild(staticLabel);
714
+ const editLabel = document.createElement('input');
715
+ editLabel.className = 'lm-AccordionPanel-titleLabelEdit';
716
+ editLabel.type = 'text';
717
+ editLabel.value = data.label;
718
+ editLabel.title = title;
719
+ staticLabel.addEventListener('click', (ev) => {
720
+ handle.removeChild(staticLabel);
721
+ handle.appendChild(editLabel);
722
+ });
723
+ editLabel.addEventListener('blur', (ev) => {
724
+ handle.removeChild(editLabel);
725
+ handle.appendChild(staticLabel);
726
+ });
727
+ editLabel.addEventListener('keydown', (ev) => {
728
+ if (ev.key === 'Enter') {
729
+ handle.removeChild(editLabel);
730
+ handle.appendChild(staticLabel);
731
+ }
732
+ return true;
733
+ });
734
+ editLabel.addEventListener('change', event => {
735
+ const target = event.target;
736
+ if (target === editLabel) {
737
+ const newValue = target === null || target === void 0 ? void 0 : target.value;
738
+ if (newValue && newValue !== title) {
739
+ title = newValue;
740
+ data.caption = title;
741
+ data.label = title;
742
+ staticLabel.title = title;
743
+ staticLabel.textContent = title;
744
+ }
745
+ }
746
+ handle.removeChild(editLabel);
747
+ handle.appendChild(staticLabel);
748
+ });
749
+ return handle;
750
+ }
751
+ }