@jupyter/collaboration 3.0.0-alpha.2 → 3.0.0-beta.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.
@@ -1,27 +1,26 @@
1
- import * as React from 'react';
2
- import { Awareness } from 'y-protocols/awareness';
3
- import { Panel } from '@lumino/widgets';
4
- import { ReactWidget } from '@jupyterlab/apputils';
1
+ import { DocumentRegistry } from '@jupyterlab/docregistry';
5
2
  import { User } from '@jupyterlab/services';
3
+ import { ISignal } from '@lumino/signaling';
4
+ import { Panel } from '@lumino/widgets';
5
+ import { Awareness } from 'y-protocols/awareness';
6
6
  import { ICollaboratorAwareness } from './tokens';
7
7
  export declare class CollaboratorsPanel extends Panel {
8
- private _currentUser;
9
- private _awareness;
10
- private _body;
11
- constructor(currentUser: User.IManager, awareness: Awareness, fileopener: (path: string) => void);
8
+ constructor(currentUser: User.IManager, awareness: Awareness, fileopener: (path: string) => void, docRegistry?: DocumentRegistry);
12
9
  /**
13
10
  * Handle collaborator change.
14
11
  */
15
12
  private _onAwarenessChanged;
13
+ private _currentUser;
14
+ private _awareness;
15
+ private _collaboratorsChanged;
16
16
  }
17
- /**
18
- * The collaborators list.
19
- */
20
- export declare class CollaboratorsBody extends ReactWidget {
21
- private _collaborators;
22
- private _fileopener;
23
- constructor(fileopener: (path: string) => void);
24
- get collaborators(): ICollaboratorAwareness[];
25
- set collaborators(value: ICollaboratorAwareness[]);
26
- render(): React.ReactElement<any>[];
27
- }
17
+ export declare function CollaboratorsBody(props: {
18
+ collaboratorsChanged: ISignal<CollaboratorsPanel, ICollaboratorAwareness[]>;
19
+ fileopener: (path: string) => void;
20
+ docRegistry?: DocumentRegistry;
21
+ }): JSX.Element;
22
+ export declare function Collaborator(props: {
23
+ collaborator: ICollaboratorAwareness;
24
+ fileopener: (path: string) => void;
25
+ docRegistry?: DocumentRegistry;
26
+ }): JSX.Element;
@@ -1,9 +1,10 @@
1
1
  // Copyright (c) Jupyter Development Team.
2
2
  // Distributed under the terms of the Modified BSD License.
3
- import * as React from 'react';
4
- import { Panel } from '@lumino/widgets';
5
3
  import { ReactWidget } from '@jupyterlab/apputils';
6
- import { PathExt } from '@jupyterlab/coreutils';
4
+ import { LabIcon, caretDownIcon, fileIcon } from '@jupyterlab/ui-components';
5
+ import { Signal } from '@lumino/signaling';
6
+ import { Panel } from '@lumino/widgets';
7
+ import React, { useState } from 'react';
7
8
  /**
8
9
  * The CSS class added to collaborators panel.
9
10
  */
@@ -17,15 +18,31 @@ const COLLABORATORS_LIST_CLASS = 'jp-CollaboratorsList';
17
18
  */
18
19
  const COLLABORATOR_CLASS = 'jp-Collaborator';
19
20
  /**
20
- * The CSS class added to each collaborator element.
21
+ * The CSS class added to each collaborator header.
22
+ */
23
+ const COLLABORATOR_HEADER_CLASS = 'jp-CollaboratorHeader';
24
+ /**
25
+ * The CSS class added to each collaborator header collapser.
26
+ */
27
+ const COLLABORATOR_HEADER_COLLAPSER_CLASS = 'jp-CollaboratorHeaderCollapser';
28
+ /**
29
+ * The CSS class added to each collaborator header with document.
21
30
  */
22
31
  const CLICKABLE_COLLABORATOR_CLASS = 'jp-ClickableCollaborator';
23
32
  /**
24
33
  * The CSS class added to each collaborator icon.
25
34
  */
26
35
  const COLLABORATOR_ICON_CLASS = 'jp-CollaboratorIcon';
36
+ /**
37
+ * The CSS class added to the files list.
38
+ */
39
+ const COLLABORATOR_FILES_CLASS = 'jp-CollaboratorFiles';
40
+ /**
41
+ * The CSS class added to the files in the list.
42
+ */
43
+ const COLLABORATOR_FILE_CLASS = 'jp-CollaboratorFile';
27
44
  export class CollaboratorsPanel extends Panel {
28
- constructor(currentUser, awareness, fileopener) {
45
+ constructor(currentUser, awareness, fileopener, docRegistry) {
29
46
  super({});
30
47
  /**
31
48
  * Handle collaborator change.
@@ -39,61 +56,76 @@ export class CollaboratorsPanel extends Panel {
39
56
  collaborators.push(value);
40
57
  }
41
58
  });
42
- this._body.collaborators = collaborators;
59
+ this._collaboratorsChanged.emit(collaborators);
43
60
  };
61
+ this._collaboratorsChanged = new Signal(this);
44
62
  this._awareness = awareness;
45
63
  this._currentUser = currentUser;
46
64
  this.addClass(COLLABORATORS_PANEL_CLASS);
47
- this._body = new CollaboratorsBody(fileopener);
48
- this.addWidget(this._body);
49
- this.update();
65
+ this.addWidget(ReactWidget.create(React.createElement(CollaboratorsBody, { fileopener: fileopener, collaboratorsChanged: this._collaboratorsChanged, docRegistry: docRegistry })));
50
66
  this._awareness.on('change', this._onAwarenessChanged);
51
67
  }
52
68
  }
53
- /**
54
- * The collaborators list.
55
- */
56
- export class CollaboratorsBody extends ReactWidget {
57
- constructor(fileopener) {
58
- super();
59
- this._collaborators = [];
60
- this._fileopener = fileopener;
61
- this.addClass(COLLABORATORS_LIST_CLASS);
62
- }
63
- get collaborators() {
64
- return this._collaborators;
65
- }
66
- set collaborators(value) {
67
- this._collaborators = value;
68
- this.update();
69
- }
70
- render() {
71
- return this._collaborators.map((value, i) => {
72
- let canOpenCurrent = false;
73
- let current = '';
74
- let separator = '';
75
- let currentFileLocation = '';
76
- if (value.current) {
77
- canOpenCurrent = true;
78
- const path = value.current.split(':');
79
- currentFileLocation = `${path[1]}:${path[2]}`;
80
- current = PathExt.basename(path[2]);
81
- current =
82
- current.length > 25 ? current.slice(0, 12).concat('…') : current;
83
- separator = '•';
84
- }
85
- const onClick = () => {
86
- if (canOpenCurrent) {
87
- this._fileopener(currentFileLocation);
88
- }
89
- };
90
- const displayName = `${value.user.display_name} ${separator} ${current}`;
91
- return (React.createElement("div", { className: canOpenCurrent
92
- ? `${CLICKABLE_COLLABORATOR_CLASS} ${COLLABORATOR_CLASS}`
93
- : COLLABORATOR_CLASS, key: i, onClick: onClick },
94
- React.createElement("div", { className: COLLABORATOR_ICON_CLASS, style: { backgroundColor: value.user.color } },
95
- React.createElement("span", null, value.user.initials)),
96
- React.createElement("span", null, displayName)));
97
- });
69
+ export function CollaboratorsBody(props) {
70
+ const [collaborators, setCollaborators] = useState([]);
71
+ props.collaboratorsChanged.connect((_, value) => {
72
+ setCollaborators(value);
73
+ });
74
+ return (React.createElement("div", { className: COLLABORATORS_LIST_CLASS }, collaborators.map((collaborator, i) => {
75
+ return (React.createElement(Collaborator, { collaborator: collaborator, fileopener: props.fileopener, docRegistry: props.docRegistry }));
76
+ })));
77
+ }
78
+ export function Collaborator(props) {
79
+ const [open, setOpen] = useState(false);
80
+ const { collaborator, fileopener } = props;
81
+ let currentMain = '';
82
+ if (collaborator.current) {
83
+ const path = collaborator.current.split(':');
84
+ currentMain = `${path[1]}:${path[2]}`;
98
85
  }
86
+ const documents = collaborator.documents || [];
87
+ const docs = documents.map(document => {
88
+ var _a, _b;
89
+ const path = document.split(':');
90
+ const fileTypes = (_b = (_a = props.docRegistry) === null || _a === void 0 ? void 0 : _a.getFileTypesForPath(path[1])) === null || _b === void 0 ? void 0 : _b.filter(ft => ft.icon !== undefined);
91
+ const icon = fileTypes ? fileTypes[0].icon : fileIcon;
92
+ const iconClass = fileTypes
93
+ ? fileTypes[0].iconClass
94
+ : undefined;
95
+ return {
96
+ filepath: path[1],
97
+ filename: path[1].length > 40
98
+ ? path[1]
99
+ .slice(0, 10)
100
+ .concat('…')
101
+ .concat(path[1].slice(path[1].length - 15))
102
+ : path[1],
103
+ fileLocation: document,
104
+ icon,
105
+ iconClass
106
+ };
107
+ });
108
+ const onClick = () => {
109
+ if (docs.length) {
110
+ setOpen(!open);
111
+ }
112
+ };
113
+ return (React.createElement("div", { className: COLLABORATOR_CLASS },
114
+ React.createElement("div", { className: docs.length
115
+ ? `${CLICKABLE_COLLABORATOR_CLASS} ${COLLABORATOR_HEADER_CLASS}`
116
+ : COLLABORATOR_HEADER_CLASS, onClick: documents ? onClick : undefined },
117
+ React.createElement(LabIcon.resolveReact, { icon: caretDownIcon, className: COLLABORATOR_HEADER_COLLAPSER_CLASS +
118
+ (open ? ' jp-mod-expanded' : ''), tag: 'div' }),
119
+ React.createElement("div", { className: COLLABORATOR_ICON_CLASS, style: { backgroundColor: collaborator.user.color } },
120
+ React.createElement("span", null, collaborator.user.initials)),
121
+ React.createElement("span", null, collaborator.user.display_name)),
122
+ React.createElement("div", { className: `${COLLABORATOR_FILES_CLASS} jp-DirListing`, style: open ? {} : { display: 'none' } },
123
+ React.createElement("ul", { className: 'jp-DirListing-content' }, docs.map(doc => {
124
+ return (React.createElement("li", { className: 'jp-DirListing-item ' +
125
+ (doc.fileLocation === currentMain
126
+ ? `${COLLABORATOR_FILE_CLASS} jp-mod-running`
127
+ : COLLABORATOR_FILE_CLASS), key: doc.filename, onClick: () => fileopener(doc.fileLocation) },
128
+ React.createElement(LabIcon.resolveReact, { icon: doc.icon, iconClass: doc.iconClass, tag: 'span', className: 'jp-DirListing-itemIcon', stylesheet: 'listing' }),
129
+ React.createElement("span", { className: 'jp-DirListing-itemText', title: doc.filepath }, doc.filename)));
130
+ })))));
99
131
  }
package/lib/tokens.d.ts CHANGED
@@ -68,7 +68,11 @@ export interface ICollaboratorAwareness {
68
68
  */
69
69
  user: User.IIdentity;
70
70
  /**
71
- * The current file/context the user is working on.
71
+ * The current file/context the user is working on (current panel in main area).
72
72
  */
73
73
  current?: string;
74
+ /**
75
+ * The shared documents opened by the user.
76
+ */
77
+ documents?: string[];
74
78
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@jupyter/collaboration",
3
- "version": "3.0.0-alpha.2",
3
+ "version": "3.0.0-beta.0",
4
4
  "description": "JupyterLab - Real-Time Collaboration Widgets",
5
5
  "homepage": "https://github.com/jupyterlab/jupyter-collaboration",
6
6
  "bugs": {
@@ -41,12 +41,14 @@
41
41
  "dependencies": {
42
42
  "@codemirror/state": "^6.2.0",
43
43
  "@codemirror/view": "^6.7.0",
44
- "@jupyter/docprovider": "^3.0.0-alpha.2",
44
+ "@jupyter/docprovider": "^3.0.0-beta.0",
45
45
  "@jupyterlab/apputils": "^4.0.5",
46
46
  "@jupyterlab/coreutils": "^6.0.5",
47
+ "@jupyterlab/docregistry": "^4.0.5",
47
48
  "@jupyterlab/services": "^7.0.5",
48
49
  "@jupyterlab/ui-components": "^4.0.5",
49
50
  "@lumino/coreutils": "^2.1.0",
51
+ "@lumino/signaling": "^2.0.0",
50
52
  "@lumino/virtualdom": "^2.0.0",
51
53
  "@lumino/widgets": "^2.1.0",
52
54
  "react": "^18.2.0",
@@ -54,7 +56,7 @@
54
56
  "yjs": "^13.5.40"
55
57
  },
56
58
  "devDependencies": {
57
- "@types/react": "^18.0.27",
59
+ "@types/react": "~18.3.1",
58
60
  "rimraf": "^4.1.2",
59
61
  "typescript": "~5.0.4"
60
62
  },
@@ -86,7 +86,7 @@
86
86
  z-index: 1000;
87
87
  }
88
88
 
89
- .jp-Collaborator {
89
+ .jp-CollaboratorHeader {
90
90
  padding: 10px;
91
91
  display: flex;
92
92
  align-items: center;
@@ -95,6 +95,10 @@
95
95
  color: var(--jp-ui-font-color1);
96
96
  }
97
97
 
98
+ .jp-CollaboratorHeader > span {
99
+ padding-left: 7px;
100
+ }
101
+
98
102
  .jp-ClickableCollaborator:hover {
99
103
  cursor: pointer;
100
104
  background-color: var(--jp-layout-color2);
@@ -102,8 +106,18 @@
102
106
  color: var(--jp-ui-font-color0);
103
107
  }
104
108
 
105
- .jp-Collaborator > span {
106
- padding-left: 7px;
109
+ .jp-CollaboratorHeaderCollapser {
110
+ transform: rotate(-90deg);
111
+ margin: auto 0;
112
+ height: 16px;
113
+ }
114
+
115
+ .jp-CollaboratorHeader:not(.jp-ClickableCollaborator) .jp-CollaboratorHeaderCollapser {
116
+ visibility: hidden;
117
+ }
118
+
119
+ .jp-CollaboratorHeaderCollapser.jp-mod-expanded {
120
+ transform: rotate(0deg);
107
121
  }
108
122
 
109
123
  .jp-CollaboratorIcon {
@@ -122,7 +136,9 @@
122
136
  color: var(--jp-ui-font-color1);
123
137
  }
124
138
 
125
- .jp-CollaboratorCurrent {
126
- position: absolute;
127
- right: 0;
139
+ .jp-CollaboratorFiles {
140
+ padding-left: 1em;
141
+ margin-top: 0;
142
+ box-shadow: 0 2px 2px -2px rgb(0 0 0 / 24%);
143
+
128
144
  }