@jupyter/collaboration 1.0.0-alpha.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/README.md +3 -0
- package/lib/awarenessmock.d.ts +23 -0
- package/lib/awarenessmock.js +40 -0
- package/lib/collaboratorspanel.d.ts +27 -0
- package/lib/collaboratorspanel.js +93 -0
- package/lib/components.d.ts +12 -0
- package/lib/components.js +15 -0
- package/lib/index.d.ts +9 -0
- package/lib/index.js +11 -0
- package/lib/menu.d.ts +58 -0
- package/lib/menu.js +75 -0
- package/lib/tokens.d.ts +79 -0
- package/lib/tokens.js +14 -0
- package/lib/userinfopanel.d.ts +21 -0
- package/lib/userinfopanel.js +50 -0
- package/lib/utils.d.ts +8 -0
- package/lib/utils.js +35 -0
- package/package.json +68 -0
- package/style/base.css +7 -0
- package/style/index.css +6 -0
- package/style/index.js +6 -0
- package/style/menu.css +41 -0
- package/style/sidepanel.css +124 -0
package/README.md
ADDED
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import * as Y from 'yjs';
|
|
2
|
+
import { IAwareness } from './tokens';
|
|
3
|
+
/**
|
|
4
|
+
* Default user implementation.
|
|
5
|
+
*/
|
|
6
|
+
export declare class AwarenessMock implements IAwareness {
|
|
7
|
+
constructor(doc: Y.Doc);
|
|
8
|
+
setLocalState(state: any): void;
|
|
9
|
+
setLocalStateField(field: string, value: any): void;
|
|
10
|
+
getLocalState(): any;
|
|
11
|
+
getStates(): any;
|
|
12
|
+
on(name: string, f: any): void;
|
|
13
|
+
off(name: string, f: any): void;
|
|
14
|
+
once(name: string, f: any): void;
|
|
15
|
+
emit(name: string, args: any): void;
|
|
16
|
+
destroy(): void;
|
|
17
|
+
doc: Y.Doc;
|
|
18
|
+
clientID: number;
|
|
19
|
+
states: Map<any, any>;
|
|
20
|
+
meta: Map<any, any>;
|
|
21
|
+
_checkInterval: any;
|
|
22
|
+
_observers: any;
|
|
23
|
+
}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
// Copyright (c) Jupyter Development Team.
|
|
2
|
+
// Distributed under the terms of the Modified BSD License.
|
|
3
|
+
/**
|
|
4
|
+
* Default user implementation.
|
|
5
|
+
*/
|
|
6
|
+
export class AwarenessMock {
|
|
7
|
+
constructor(doc) {
|
|
8
|
+
this.states = new Map();
|
|
9
|
+
this.meta = new Map();
|
|
10
|
+
this.doc = doc;
|
|
11
|
+
this.clientID = doc.clientID;
|
|
12
|
+
}
|
|
13
|
+
setLocalState(state) {
|
|
14
|
+
return;
|
|
15
|
+
}
|
|
16
|
+
setLocalStateField(field, value) {
|
|
17
|
+
return;
|
|
18
|
+
}
|
|
19
|
+
getLocalState() {
|
|
20
|
+
return null;
|
|
21
|
+
}
|
|
22
|
+
getStates() {
|
|
23
|
+
return this.states;
|
|
24
|
+
}
|
|
25
|
+
on(name, f) {
|
|
26
|
+
return;
|
|
27
|
+
}
|
|
28
|
+
off(name, f) {
|
|
29
|
+
return;
|
|
30
|
+
}
|
|
31
|
+
once(name, f) {
|
|
32
|
+
return;
|
|
33
|
+
}
|
|
34
|
+
emit(name, args) {
|
|
35
|
+
return;
|
|
36
|
+
}
|
|
37
|
+
destroy() {
|
|
38
|
+
return;
|
|
39
|
+
}
|
|
40
|
+
}
|
|
@@ -0,0 +1,27 @@
|
|
|
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';
|
|
5
|
+
import { User } from '@jupyterlab/services';
|
|
6
|
+
import { ICollaboratorAwareness } from './tokens';
|
|
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);
|
|
12
|
+
/**
|
|
13
|
+
* Handle collaborator change.
|
|
14
|
+
*/
|
|
15
|
+
private _onAwarenessChanged;
|
|
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
|
+
}
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
// Copyright (c) Jupyter Development Team.
|
|
2
|
+
// Distributed under the terms of the Modified BSD License.
|
|
3
|
+
import * as React from 'react';
|
|
4
|
+
import { Panel } from '@lumino/widgets';
|
|
5
|
+
import { ReactWidget } from '@jupyterlab/apputils';
|
|
6
|
+
import { PathExt } from '@jupyterlab/coreutils';
|
|
7
|
+
/**
|
|
8
|
+
* The CSS class added to collaborators list container.
|
|
9
|
+
*/
|
|
10
|
+
const COLLABORATORS_LIST_CLASS = 'jp-CollaboratorsList';
|
|
11
|
+
/**
|
|
12
|
+
* The CSS class added to each collaborator element.
|
|
13
|
+
*/
|
|
14
|
+
const COLLABORATOR_CLASS = 'jp-Collaborator';
|
|
15
|
+
/**
|
|
16
|
+
* The CSS class added to each collaborator element.
|
|
17
|
+
*/
|
|
18
|
+
const CLICKABLE_COLLABORATOR_CLASS = 'jp-ClickableCollaborator';
|
|
19
|
+
/**
|
|
20
|
+
* The CSS class added to each collaborator icon.
|
|
21
|
+
*/
|
|
22
|
+
const COLLABORATOR_ICON_CLASS = 'jp-CollaboratorIcon';
|
|
23
|
+
export class CollaboratorsPanel extends Panel {
|
|
24
|
+
constructor(currentUser, awareness, fileopener) {
|
|
25
|
+
super({});
|
|
26
|
+
/**
|
|
27
|
+
* Handle collaborator change.
|
|
28
|
+
*/
|
|
29
|
+
this._onAwarenessChanged = () => {
|
|
30
|
+
const state = this._awareness.getStates();
|
|
31
|
+
const collaborators = [];
|
|
32
|
+
state.forEach((value, key) => {
|
|
33
|
+
if (this._currentUser.isReady &&
|
|
34
|
+
value.user.name !== this._currentUser.identity.name) {
|
|
35
|
+
collaborators.push(value);
|
|
36
|
+
}
|
|
37
|
+
});
|
|
38
|
+
this._body.collaborators = collaborators;
|
|
39
|
+
};
|
|
40
|
+
this._awareness = awareness;
|
|
41
|
+
this._currentUser = currentUser;
|
|
42
|
+
this._body = new CollaboratorsBody(fileopener);
|
|
43
|
+
this.addWidget(this._body);
|
|
44
|
+
this.update();
|
|
45
|
+
this._awareness.on('change', this._onAwarenessChanged);
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* The collaborators list.
|
|
50
|
+
*/
|
|
51
|
+
export class CollaboratorsBody extends ReactWidget {
|
|
52
|
+
constructor(fileopener) {
|
|
53
|
+
super();
|
|
54
|
+
this._collaborators = [];
|
|
55
|
+
this._fileopener = fileopener;
|
|
56
|
+
this.addClass(COLLABORATORS_LIST_CLASS);
|
|
57
|
+
}
|
|
58
|
+
get collaborators() {
|
|
59
|
+
return this._collaborators;
|
|
60
|
+
}
|
|
61
|
+
set collaborators(value) {
|
|
62
|
+
this._collaborators = value;
|
|
63
|
+
this.update();
|
|
64
|
+
}
|
|
65
|
+
render() {
|
|
66
|
+
return this._collaborators.map((value, i) => {
|
|
67
|
+
let canOpenCurrent = false;
|
|
68
|
+
let current = '';
|
|
69
|
+
let separator = '';
|
|
70
|
+
let currentFileLocation = '';
|
|
71
|
+
if (value.current) {
|
|
72
|
+
canOpenCurrent = true;
|
|
73
|
+
currentFileLocation = value.current.split(':')[1];
|
|
74
|
+
current = PathExt.basename(currentFileLocation);
|
|
75
|
+
current =
|
|
76
|
+
current.length > 25 ? current.slice(0, 12).concat('…') : current;
|
|
77
|
+
separator = '•';
|
|
78
|
+
}
|
|
79
|
+
const onClick = () => {
|
|
80
|
+
if (canOpenCurrent) {
|
|
81
|
+
this._fileopener(currentFileLocation);
|
|
82
|
+
}
|
|
83
|
+
};
|
|
84
|
+
const displayName = `${value.user.display_name} ${separator} ${current}`;
|
|
85
|
+
return (React.createElement("div", { className: canOpenCurrent
|
|
86
|
+
? `${CLICKABLE_COLLABORATOR_CLASS} ${COLLABORATOR_CLASS}`
|
|
87
|
+
: COLLABORATOR_CLASS, key: i, onClick: onClick },
|
|
88
|
+
React.createElement("div", { className: COLLABORATOR_ICON_CLASS, style: { backgroundColor: value.user.color } },
|
|
89
|
+
React.createElement("span", null, value.user.initials)),
|
|
90
|
+
React.createElement("span", null, displayName)));
|
|
91
|
+
});
|
|
92
|
+
}
|
|
93
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { User } from '@jupyterlab/services';
|
|
2
|
+
import * as React from 'react';
|
|
3
|
+
declare type Props = {
|
|
4
|
+
user: User.IIdentity;
|
|
5
|
+
};
|
|
6
|
+
/**
|
|
7
|
+
* React component for the user icon.
|
|
8
|
+
*
|
|
9
|
+
* @returns The React component
|
|
10
|
+
*/
|
|
11
|
+
export declare const UserIconComponent: React.FC<Props>;
|
|
12
|
+
export {};
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
// Copyright (c) Jupyter Development Team.
|
|
2
|
+
// Distributed under the terms of the Modified BSD License.
|
|
3
|
+
import * as React from 'react';
|
|
4
|
+
/**
|
|
5
|
+
* React component for the user icon.
|
|
6
|
+
*
|
|
7
|
+
* @returns The React component
|
|
8
|
+
*/
|
|
9
|
+
export const UserIconComponent = props => {
|
|
10
|
+
const { user } = props;
|
|
11
|
+
return (React.createElement("div", { className: "jp-UserInfo-Container" },
|
|
12
|
+
React.createElement("div", { title: user.display_name, className: "jp-UserInfo-Icon", style: { backgroundColor: user.color } },
|
|
13
|
+
React.createElement("span", null, user.initials)),
|
|
14
|
+
React.createElement("h3", null, user.display_name)));
|
|
15
|
+
};
|
package/lib/index.d.ts
ADDED
package/lib/index.js
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
// Copyright (c) Jupyter Development Team.
|
|
2
|
+
// Distributed under the terms of the Modified BSD License.
|
|
3
|
+
/**
|
|
4
|
+
* @packageDocumentation
|
|
5
|
+
* @module collaboration
|
|
6
|
+
*/
|
|
7
|
+
export * from './tokens';
|
|
8
|
+
export * from './awarenessmock';
|
|
9
|
+
export * from './menu';
|
|
10
|
+
export * from './userinfopanel';
|
|
11
|
+
export * from './collaboratorspanel';
|
package/lib/menu.d.ts
ADDED
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import { User } from '@jupyterlab/services';
|
|
2
|
+
import { Menu, MenuBar } from '@lumino/widgets';
|
|
3
|
+
import { VirtualElement } from '@lumino/virtualdom';
|
|
4
|
+
/**
|
|
5
|
+
* Custom renderer for the user menu.
|
|
6
|
+
*/
|
|
7
|
+
export declare class RendererUserMenu extends MenuBar.Renderer {
|
|
8
|
+
private _user;
|
|
9
|
+
/**
|
|
10
|
+
* Constructor of the class RendererUserMenu.
|
|
11
|
+
*
|
|
12
|
+
* @argument user Current user object.
|
|
13
|
+
*/
|
|
14
|
+
constructor(user: User.IManager);
|
|
15
|
+
/**
|
|
16
|
+
* Render the virtual element for a menu bar item.
|
|
17
|
+
*
|
|
18
|
+
* @param data - The data to use for rendering the item.
|
|
19
|
+
*
|
|
20
|
+
* @returns A virtual element representing the item.
|
|
21
|
+
*/
|
|
22
|
+
renderItem(data: MenuBar.IRenderData): VirtualElement;
|
|
23
|
+
/**
|
|
24
|
+
* Render the label element for a menu item.
|
|
25
|
+
*
|
|
26
|
+
* @param data - The data to use for rendering the label.
|
|
27
|
+
*
|
|
28
|
+
* @returns A virtual element representing the item label.
|
|
29
|
+
*/
|
|
30
|
+
renderLabel(data: MenuBar.IRenderData): VirtualElement;
|
|
31
|
+
/**
|
|
32
|
+
* Render the user icon element for a menu item.
|
|
33
|
+
*
|
|
34
|
+
* @returns A virtual element representing the item label.
|
|
35
|
+
*/
|
|
36
|
+
private _createUserIcon;
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* This menu does not contain anything but we keep it around in case someone uses it.
|
|
40
|
+
* Custom lumino Menu for the user menu.
|
|
41
|
+
*/
|
|
42
|
+
export declare class UserMenu extends Menu {
|
|
43
|
+
constructor(options: UserMenu.IOptions);
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* Namespace of the UserMenu class.
|
|
47
|
+
*/
|
|
48
|
+
export declare namespace UserMenu {
|
|
49
|
+
/**
|
|
50
|
+
* User menu options interface
|
|
51
|
+
*/
|
|
52
|
+
interface IOptions extends Menu.IOptions {
|
|
53
|
+
/**
|
|
54
|
+
* Current user manager.
|
|
55
|
+
*/
|
|
56
|
+
user: User.IManager;
|
|
57
|
+
}
|
|
58
|
+
}
|
package/lib/menu.js
ADDED
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
// Copyright (c) Jupyter Development Team.
|
|
2
|
+
// Distributed under the terms of the Modified BSD License.
|
|
3
|
+
import { userIcon } from '@jupyterlab/ui-components';
|
|
4
|
+
import { Menu, MenuBar } from '@lumino/widgets';
|
|
5
|
+
import { h } from '@lumino/virtualdom';
|
|
6
|
+
/**
|
|
7
|
+
* Custom renderer for the user menu.
|
|
8
|
+
*/
|
|
9
|
+
export class RendererUserMenu extends MenuBar.Renderer {
|
|
10
|
+
/**
|
|
11
|
+
* Constructor of the class RendererUserMenu.
|
|
12
|
+
*
|
|
13
|
+
* @argument user Current user object.
|
|
14
|
+
*/
|
|
15
|
+
constructor(user) {
|
|
16
|
+
super();
|
|
17
|
+
this._user = user;
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* Render the virtual element for a menu bar item.
|
|
21
|
+
*
|
|
22
|
+
* @param data - The data to use for rendering the item.
|
|
23
|
+
*
|
|
24
|
+
* @returns A virtual element representing the item.
|
|
25
|
+
*/
|
|
26
|
+
renderItem(data) {
|
|
27
|
+
const className = this.createItemClass(data);
|
|
28
|
+
const dataset = this.createItemDataset(data);
|
|
29
|
+
const aria = this.createItemARIA(data);
|
|
30
|
+
return h.li({ className, dataset, tabindex: '0', onfocus: data.onfocus, ...aria }, this._createUserIcon(), this.renderLabel(data), this.renderIcon(data));
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* Render the label element for a menu item.
|
|
34
|
+
*
|
|
35
|
+
* @param data - The data to use for rendering the label.
|
|
36
|
+
*
|
|
37
|
+
* @returns A virtual element representing the item label.
|
|
38
|
+
*/
|
|
39
|
+
renderLabel(data) {
|
|
40
|
+
const content = this.formatLabel(data);
|
|
41
|
+
return h.div({ className: 'lm-MenuBar-itemLabel jp-MenuBar-label' }, content);
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* Render the user icon element for a menu item.
|
|
45
|
+
*
|
|
46
|
+
* @returns A virtual element representing the item label.
|
|
47
|
+
*/
|
|
48
|
+
_createUserIcon() {
|
|
49
|
+
if (this._user.isReady && this._user.identity.avatar_url) {
|
|
50
|
+
return h.div({
|
|
51
|
+
className: 'lm-MenuBar-itemIcon jp-MenuBar-imageIcon'
|
|
52
|
+
}, h.img({ src: this._user.identity.avatar_url }));
|
|
53
|
+
}
|
|
54
|
+
else if (this._user.isReady) {
|
|
55
|
+
return h.div({
|
|
56
|
+
className: 'lm-MenuBar-itemIcon jp-MenuBar-anonymousIcon',
|
|
57
|
+
style: { backgroundColor: this._user.identity.color }
|
|
58
|
+
}, h.span({}, this._user.identity.initials));
|
|
59
|
+
}
|
|
60
|
+
else {
|
|
61
|
+
return h.div({
|
|
62
|
+
className: 'lm-MenuBar-itemIcon jp-MenuBar-anonymousIcon'
|
|
63
|
+
}, userIcon);
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
/**
|
|
68
|
+
* This menu does not contain anything but we keep it around in case someone uses it.
|
|
69
|
+
* Custom lumino Menu for the user menu.
|
|
70
|
+
*/
|
|
71
|
+
export class UserMenu extends Menu {
|
|
72
|
+
constructor(options) {
|
|
73
|
+
super(options);
|
|
74
|
+
}
|
|
75
|
+
}
|
package/lib/tokens.d.ts
ADDED
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
import type { Menu } from '@lumino/widgets';
|
|
2
|
+
import { Token } from '@lumino/coreutils';
|
|
3
|
+
import type { Awareness } from 'y-protocols/awareness';
|
|
4
|
+
import type { User } from '@jupyterlab/services';
|
|
5
|
+
/**
|
|
6
|
+
* The user menu token.
|
|
7
|
+
*
|
|
8
|
+
* NOTE: Require this token in your extension to access the user menu
|
|
9
|
+
* (top-right menu in JupyterLab's interface).
|
|
10
|
+
*/
|
|
11
|
+
export declare const IUserMenu: Token<IUserMenu>;
|
|
12
|
+
/**
|
|
13
|
+
* The global awareness token.
|
|
14
|
+
*/
|
|
15
|
+
export declare const IGlobalAwareness: Token<Awareness>;
|
|
16
|
+
/**
|
|
17
|
+
* The awareness interface.
|
|
18
|
+
*/
|
|
19
|
+
export declare type IAwareness = Awareness;
|
|
20
|
+
/**
|
|
21
|
+
* An interface describing the user menu.
|
|
22
|
+
*/
|
|
23
|
+
export interface IUserMenu {
|
|
24
|
+
/**
|
|
25
|
+
* Dispose of the resources held by the menu.
|
|
26
|
+
*/
|
|
27
|
+
dispose(): void;
|
|
28
|
+
/**
|
|
29
|
+
* Test whether the widget has been disposed.
|
|
30
|
+
*/
|
|
31
|
+
readonly isDisposed: boolean;
|
|
32
|
+
/**
|
|
33
|
+
* A read-only array of the menu items in the menu.
|
|
34
|
+
*/
|
|
35
|
+
readonly items: ReadonlyArray<Menu.IItem>;
|
|
36
|
+
/**
|
|
37
|
+
* Add a menu item to the end of the menu.
|
|
38
|
+
*
|
|
39
|
+
* @param options - The options for creating the menu item.
|
|
40
|
+
*
|
|
41
|
+
* @returns The menu item added to the menu.
|
|
42
|
+
*/
|
|
43
|
+
addItem(options: Menu.IItemOptions): Menu.IItem;
|
|
44
|
+
/**
|
|
45
|
+
* Insert a menu item into the menu at the specified index.
|
|
46
|
+
*
|
|
47
|
+
* @param index - The index at which to insert the item.
|
|
48
|
+
*
|
|
49
|
+
* @param options - The options for creating the menu item.
|
|
50
|
+
*
|
|
51
|
+
* @returns The menu item added to the menu.
|
|
52
|
+
*
|
|
53
|
+
* #### Notes
|
|
54
|
+
* The index will be clamped to the bounds of the items.
|
|
55
|
+
*/
|
|
56
|
+
insertItem(index: number, options: Menu.IItemOptions): Menu.IItem;
|
|
57
|
+
/**
|
|
58
|
+
* Remove an item from the menu.
|
|
59
|
+
*
|
|
60
|
+
* @param item - The item to remove from the menu.
|
|
61
|
+
*
|
|
62
|
+
* #### Notes
|
|
63
|
+
* This is a no-op if the item is not in the menu.
|
|
64
|
+
*/
|
|
65
|
+
removeItem(item: Menu.IItem): void;
|
|
66
|
+
}
|
|
67
|
+
/**
|
|
68
|
+
* Global awareness for JupyterLab scopped shared data.
|
|
69
|
+
*/
|
|
70
|
+
export interface ICollaboratorAwareness {
|
|
71
|
+
/**
|
|
72
|
+
* The User owning theses data.
|
|
73
|
+
*/
|
|
74
|
+
user: User.IIdentity;
|
|
75
|
+
/**
|
|
76
|
+
* The current file/context the user is working on.
|
|
77
|
+
*/
|
|
78
|
+
current?: string;
|
|
79
|
+
}
|
package/lib/tokens.js
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
// Copyright (c) Jupyter Development Team.
|
|
2
|
+
// Distributed under the terms of the Modified BSD License.
|
|
3
|
+
import { Token } from '@lumino/coreutils';
|
|
4
|
+
/**
|
|
5
|
+
* The user menu token.
|
|
6
|
+
*
|
|
7
|
+
* NOTE: Require this token in your extension to access the user menu
|
|
8
|
+
* (top-right menu in JupyterLab's interface).
|
|
9
|
+
*/
|
|
10
|
+
export const IUserMenu = new Token('@jupyter/collaboration:IUserMenu');
|
|
11
|
+
/**
|
|
12
|
+
* The global awareness token.
|
|
13
|
+
*/
|
|
14
|
+
export const IGlobalAwareness = new Token('@jupyter/collaboration:IGlobalAwareness');
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { ReactWidget } from '@jupyterlab/apputils';
|
|
2
|
+
import { User } from '@jupyterlab/services';
|
|
3
|
+
import { Panel } from '@lumino/widgets';
|
|
4
|
+
export declare class UserInfoPanel extends Panel {
|
|
5
|
+
private _profile;
|
|
6
|
+
private _body;
|
|
7
|
+
constructor(user: User.IManager);
|
|
8
|
+
}
|
|
9
|
+
/**
|
|
10
|
+
* A SettingsWidget for the user.
|
|
11
|
+
*/
|
|
12
|
+
export declare class UserInfoBody extends ReactWidget {
|
|
13
|
+
private _user;
|
|
14
|
+
/**
|
|
15
|
+
* Constructs a new settings widget.
|
|
16
|
+
*/
|
|
17
|
+
constructor(user: User.IIdentity);
|
|
18
|
+
get user(): User.IIdentity;
|
|
19
|
+
set user(user: User.IIdentity);
|
|
20
|
+
render(): JSX.Element;
|
|
21
|
+
}
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
// Copyright (c) Jupyter Development Team.
|
|
2
|
+
// Distributed under the terms of the Modified BSD License.
|
|
3
|
+
import { ReactWidget } from '@jupyterlab/apputils';
|
|
4
|
+
import { Panel } from '@lumino/widgets';
|
|
5
|
+
import * as React from 'react';
|
|
6
|
+
import { UserIconComponent } from './components';
|
|
7
|
+
export class UserInfoPanel extends Panel {
|
|
8
|
+
constructor(user) {
|
|
9
|
+
super({});
|
|
10
|
+
this.addClass('jp-UserInfoPanel');
|
|
11
|
+
this._profile = user;
|
|
12
|
+
this._body = null;
|
|
13
|
+
if (this._profile.isReady) {
|
|
14
|
+
this._body = new UserInfoBody(this._profile.identity);
|
|
15
|
+
this.addWidget(this._body);
|
|
16
|
+
this.update();
|
|
17
|
+
}
|
|
18
|
+
else {
|
|
19
|
+
this._profile.ready
|
|
20
|
+
.then(() => {
|
|
21
|
+
this._body = new UserInfoBody(this._profile.identity);
|
|
22
|
+
this.addWidget(this._body);
|
|
23
|
+
this.update();
|
|
24
|
+
})
|
|
25
|
+
.catch(e => console.error(e));
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* A SettingsWidget for the user.
|
|
31
|
+
*/
|
|
32
|
+
export class UserInfoBody extends ReactWidget {
|
|
33
|
+
/**
|
|
34
|
+
* Constructs a new settings widget.
|
|
35
|
+
*/
|
|
36
|
+
constructor(user) {
|
|
37
|
+
super();
|
|
38
|
+
this._user = user;
|
|
39
|
+
}
|
|
40
|
+
get user() {
|
|
41
|
+
return this._user;
|
|
42
|
+
}
|
|
43
|
+
set user(user) {
|
|
44
|
+
this._user = user;
|
|
45
|
+
this.update();
|
|
46
|
+
}
|
|
47
|
+
render() {
|
|
48
|
+
return React.createElement(UserIconComponent, { user: this._user });
|
|
49
|
+
}
|
|
50
|
+
}
|
package/lib/utils.d.ts
ADDED
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Call the API extension
|
|
3
|
+
*
|
|
4
|
+
* @param endPoint API REST end point for the extension
|
|
5
|
+
* @param init Initial values for the request
|
|
6
|
+
* @returns The response body interpreted as JSON
|
|
7
|
+
*/
|
|
8
|
+
export declare function requestAPI<T>(endPoint?: string, init?: RequestInit): Promise<T>;
|
package/lib/utils.js
ADDED
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
// Copyright (c) Jupyter Development Team.
|
|
2
|
+
// Distributed under the terms of the Modified BSD License.
|
|
3
|
+
import { URLExt } from '@jupyterlab/coreutils';
|
|
4
|
+
import { ServerConnection } from '@jupyterlab/services';
|
|
5
|
+
/**
|
|
6
|
+
* Call the API extension
|
|
7
|
+
*
|
|
8
|
+
* @param endPoint API REST end point for the extension
|
|
9
|
+
* @param init Initial values for the request
|
|
10
|
+
* @returns The response body interpreted as JSON
|
|
11
|
+
*/
|
|
12
|
+
export async function requestAPI(endPoint = '', init = {}) {
|
|
13
|
+
const settings = ServerConnection.makeSettings();
|
|
14
|
+
const requestUrl = URLExt.join(settings.baseUrl, endPoint);
|
|
15
|
+
let response;
|
|
16
|
+
try {
|
|
17
|
+
response = await ServerConnection.makeRequest(requestUrl, init, settings);
|
|
18
|
+
}
|
|
19
|
+
catch (error) {
|
|
20
|
+
throw new ServerConnection.NetworkError(error);
|
|
21
|
+
}
|
|
22
|
+
let data = await response.text();
|
|
23
|
+
if (data.length > 0) {
|
|
24
|
+
try {
|
|
25
|
+
data = JSON.parse(data);
|
|
26
|
+
}
|
|
27
|
+
catch (error) {
|
|
28
|
+
console.log('Not a JSON response body.', response);
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
if (!response.ok) {
|
|
32
|
+
throw new ServerConnection.ResponseError(response, data.message || data);
|
|
33
|
+
}
|
|
34
|
+
return data;
|
|
35
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@jupyter/collaboration",
|
|
3
|
+
"version": "1.0.0-alpha.0",
|
|
4
|
+
"description": "JupyterLab - Real-Time Collaboration Widgets",
|
|
5
|
+
"homepage": "https://github.com/jupyterlab/jupyter_collaboration",
|
|
6
|
+
"bugs": {
|
|
7
|
+
"url": "https://github.com/jupyterlab/jupyter_collaboration/issues"
|
|
8
|
+
},
|
|
9
|
+
"repository": {
|
|
10
|
+
"type": "git",
|
|
11
|
+
"url": "https://github.com/jupyterlab/jupyter_collaboration.git"
|
|
12
|
+
},
|
|
13
|
+
"license": "BSD-3-Clause",
|
|
14
|
+
"author": "Project Jupyter",
|
|
15
|
+
"sideEffects": [
|
|
16
|
+
"style/*.css",
|
|
17
|
+
"style/index.js"
|
|
18
|
+
],
|
|
19
|
+
"main": "lib/index.js",
|
|
20
|
+
"types": "lib/index.d.ts",
|
|
21
|
+
"style": "style/index.css",
|
|
22
|
+
"directories": {
|
|
23
|
+
"lib": "lib/"
|
|
24
|
+
},
|
|
25
|
+
"files": [
|
|
26
|
+
"lib/*.d.ts",
|
|
27
|
+
"lib/*.js.map",
|
|
28
|
+
"lib/*.js",
|
|
29
|
+
"style/*.css",
|
|
30
|
+
"style/index.js"
|
|
31
|
+
],
|
|
32
|
+
"scripts": {
|
|
33
|
+
"build": "tsc -b",
|
|
34
|
+
"build:prod": "jlpm run build",
|
|
35
|
+
"clean": "rimraf lib tsconfig.tsbuildinfo",
|
|
36
|
+
"clean:lib": "jlpm run clean:all",
|
|
37
|
+
"clean:all": "rimraf lib tsconfig.tsbuildinfo node_modules",
|
|
38
|
+
"install:extension": "jlpm run build",
|
|
39
|
+
"watch": "tsc -b --watch"
|
|
40
|
+
},
|
|
41
|
+
"dependencies": {
|
|
42
|
+
"@jupyterlab/apputils": "^4.0.0-alpha.18",
|
|
43
|
+
"@jupyterlab/coreutils": "^6.0.0-alpha.18",
|
|
44
|
+
"@jupyterlab/services": "^7.0.0-alpha.18",
|
|
45
|
+
"@jupyterlab/ui-components": "^4.0.0-alpha.33",
|
|
46
|
+
"@lumino/coreutils": "^2.0.0-alpha.6",
|
|
47
|
+
"@lumino/virtualdom": "^2.0.0-alpha.6",
|
|
48
|
+
"@lumino/widgets": "^2.0.0-alpha.6",
|
|
49
|
+
"react": "^18.2.0",
|
|
50
|
+
"y-protocols": "^1.0.5",
|
|
51
|
+
"yjs": "^13.5.40"
|
|
52
|
+
},
|
|
53
|
+
"devDependencies": {
|
|
54
|
+
"rimraf": "~3.0.0",
|
|
55
|
+
"typescript": "~4.7.3"
|
|
56
|
+
},
|
|
57
|
+
"publishConfig": {
|
|
58
|
+
"access": "public"
|
|
59
|
+
},
|
|
60
|
+
"jupyterlab": {},
|
|
61
|
+
"typedoc": {
|
|
62
|
+
"entryPoint": "./src/index.ts",
|
|
63
|
+
"readmeFile": "./README.md",
|
|
64
|
+
"displayName": "@jupyter/collaboration",
|
|
65
|
+
"tsconfig": "./tsconfig.json"
|
|
66
|
+
},
|
|
67
|
+
"styleModule": "style/index.js"
|
|
68
|
+
}
|
package/style/base.css
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
/* -----------------------------------------------------------------------------
|
|
2
|
+
| Copyright (c) Jupyter Development Team.
|
|
3
|
+
| Distributed under the terms of the Modified BSD License.
|
|
4
|
+
|---------------------------------------------------------------------------- */
|
|
5
|
+
|
|
6
|
+
@import url("./menu.css");
|
|
7
|
+
@import url("./sidepanel.css");
|
package/style/index.css
ADDED
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
/* -----------------------------------------------------------------------------
|
|
2
|
+
| Copyright (c) Jupyter Development Team.
|
|
3
|
+
| Distributed under the terms of the Modified BSD License.
|
|
4
|
+
|---------------------------------------------------------------------------- */
|
|
5
|
+
|
|
6
|
+
@import url('./base.css');
|
package/style/index.js
ADDED
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
/*-----------------------------------------------------------------------------
|
|
2
|
+
| Copyright (c) Jupyter Development Team.
|
|
3
|
+
| Distributed under the terms of the Modified BSD License.
|
|
4
|
+
|----------------------------------------------------------------------------*/
|
|
5
|
+
|
|
6
|
+
import './base.css';
|
package/style/menu.css
ADDED
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
/* -----------------------------------------------------------------------------
|
|
2
|
+
| Copyright (c) Jupyter Development Team.
|
|
3
|
+
| Distributed under the terms of the Modified BSD License.
|
|
4
|
+
|---------------------------------------------------------------------------- */
|
|
5
|
+
|
|
6
|
+
.jp-MenuBar-label {
|
|
7
|
+
margin-left: 25px;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
.jp-MenuBar-anonymousIcon span {
|
|
11
|
+
width: 24px;
|
|
12
|
+
text-align: center;
|
|
13
|
+
fill: var(--jp-ui-font-color1);
|
|
14
|
+
color: var(--jp-ui-font-color1);
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
.jp-MenuBar-anonymousIcon,
|
|
18
|
+
.jp-MenuBar-imageIcon {
|
|
19
|
+
position: absolute;
|
|
20
|
+
top: 1px;
|
|
21
|
+
left: 8px;
|
|
22
|
+
width: 24px;
|
|
23
|
+
height: 24px;
|
|
24
|
+
display: flex;
|
|
25
|
+
align-items: center;
|
|
26
|
+
vertical-align: middle;
|
|
27
|
+
border-radius: 100%;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
.jp-MenuBar-imageIcon img {
|
|
31
|
+
width: 24px;
|
|
32
|
+
border-radius: 100%;
|
|
33
|
+
fill: var(--jp-ui-font-color1);
|
|
34
|
+
color: var(--jp-ui-font-color1);
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
.jp-UserMenu-caretDownIcon {
|
|
38
|
+
height: 22px;
|
|
39
|
+
position: relative;
|
|
40
|
+
top: 15%;
|
|
41
|
+
}
|
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Copyright (c) Jupyter Development Team.
|
|
3
|
+
* Distributed under the terms of the Modified BSD License.
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
/************************************************************
|
|
7
|
+
Main Panel
|
|
8
|
+
*************************************************************/
|
|
9
|
+
|
|
10
|
+
.jp-RTCPanel {
|
|
11
|
+
min-width: var(--jp-sidebar-min-width) !important;
|
|
12
|
+
color: var(--jp-ui-font-color1);
|
|
13
|
+
background: var(--jp-layout-color1);
|
|
14
|
+
font-size: var(--jp-ui-font-size1);
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
/************************************************************
|
|
18
|
+
User Info Panel
|
|
19
|
+
*************************************************************/
|
|
20
|
+
.jp-UserInfoPanel {
|
|
21
|
+
display: flex;
|
|
22
|
+
flex-direction: column;
|
|
23
|
+
max-height: 140px;
|
|
24
|
+
padding-top: 3px;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
.jp-UserInfo-Container {
|
|
28
|
+
margin: 20px;
|
|
29
|
+
display: flex;
|
|
30
|
+
flex-direction: column;
|
|
31
|
+
align-items: center;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
.jp-UserInfo-Icon {
|
|
35
|
+
margin: auto;
|
|
36
|
+
width: 50px;
|
|
37
|
+
height: 50px;
|
|
38
|
+
border-radius: 50px;
|
|
39
|
+
display: inline-flex;
|
|
40
|
+
align-items: center;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
.jp-UserInfo-Icon span {
|
|
44
|
+
margin: auto;
|
|
45
|
+
text-align: center;
|
|
46
|
+
font-size: 25px;
|
|
47
|
+
fill: var(--jp-ui-font-color1);
|
|
48
|
+
color: var(--jp-ui-font-color1);
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
.jp-UserInfo-Info {
|
|
52
|
+
margin: 20px;
|
|
53
|
+
display: inline-flex;
|
|
54
|
+
flex-direction: column;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
.jp-UserInfo-Info label {
|
|
58
|
+
font-weight: bold;
|
|
59
|
+
fill: var(--jp-ui-font-color1);
|
|
60
|
+
color: var(--jp-ui-font-color1);
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
.jp-UserInfo-Info input {
|
|
64
|
+
text-decoration: none;
|
|
65
|
+
border-top: none;
|
|
66
|
+
border-left: none;
|
|
67
|
+
border-right: none;
|
|
68
|
+
border-color: var(--jp-ui-font-color1);
|
|
69
|
+
border-width: 0.5px;
|
|
70
|
+
background-color: transparent;
|
|
71
|
+
fill: var(--jp-ui-font-color1);
|
|
72
|
+
color: var(--jp-ui-font-color1);
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
/************************************************************
|
|
76
|
+
Collaborators Info Panel
|
|
77
|
+
*************************************************************/
|
|
78
|
+
|
|
79
|
+
.jp-CollaboratorsList {
|
|
80
|
+
flex-direction: column;
|
|
81
|
+
display: flex;
|
|
82
|
+
z-index: 1000;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
.jp-Collaborator {
|
|
86
|
+
padding: 10px;
|
|
87
|
+
display: flex;
|
|
88
|
+
align-items: center;
|
|
89
|
+
font-size: var(--jp-ui-font-size0);
|
|
90
|
+
fill: var(--jp-ui-font-color1);
|
|
91
|
+
color: var(--jp-ui-font-color1);
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
.jp-ClickableCollaborator:hover {
|
|
95
|
+
cursor: pointer;
|
|
96
|
+
background-color: var(--jp-layout-color2);
|
|
97
|
+
fill: var(--jp-ui-font-color0);
|
|
98
|
+
color: var(--jp-ui-font-color0);
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
.jp-Collaborator > span {
|
|
102
|
+
padding-left: 7px;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
.jp-CollaboratorIcon {
|
|
106
|
+
border-radius: 100%;
|
|
107
|
+
padding: 2px;
|
|
108
|
+
width: 24px;
|
|
109
|
+
height: 24px;
|
|
110
|
+
display: flex;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
.jp-CollaboratorIcon > span {
|
|
114
|
+
text-align: center;
|
|
115
|
+
margin: auto;
|
|
116
|
+
font-size: 12px;
|
|
117
|
+
fill: var(--jp-ui-font-color1);
|
|
118
|
+
color: var(--jp-ui-font-color1);
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
.jp-CollaboratorCurrent {
|
|
122
|
+
position: absolute;
|
|
123
|
+
right: 0;
|
|
124
|
+
}
|