@jupyter/collaboration 3.0.0-alpha.1 → 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.
- package/lib/collaboratorspanel.d.ts +18 -19
- package/lib/collaboratorspanel.js +86 -54
- package/lib/tokens.d.ts +5 -1
- package/package.json +5 -3
- package/style/sidepanel.css +22 -6
|
@@ -1,27 +1,26 @@
|
|
|
1
|
-
import
|
|
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
|
-
|
|
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
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
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 {
|
|
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
|
|
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.
|
|
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.
|
|
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
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
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-
|
|
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-
|
|
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": "
|
|
59
|
+
"@types/react": "~18.3.1",
|
|
58
60
|
"rimraf": "^4.1.2",
|
|
59
61
|
"typescript": "~5.0.4"
|
|
60
62
|
},
|
package/style/sidepanel.css
CHANGED
|
@@ -86,7 +86,7 @@
|
|
|
86
86
|
z-index: 1000;
|
|
87
87
|
}
|
|
88
88
|
|
|
89
|
-
.jp-
|
|
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-
|
|
106
|
-
|
|
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-
|
|
126
|
-
|
|
127
|
-
|
|
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
|
}
|