@jupyter/collaboration 4.0.1 → 4.1.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.
@@ -1,12 +1,53 @@
1
1
  import { User } from '@jupyterlab/services';
2
- import * as React from 'react';
3
- type Props = {
4
- user: User.IIdentity;
2
+ import { ReactWidget } from '@jupyterlab/ui-components';
3
+ import React from 'react';
4
+ type UserIconProps = {
5
+ /**
6
+ * The user manager instance.
7
+ */
8
+ userManager: User.IManager;
9
+ /**
10
+ * An optional onclick handler for the icon.
11
+ *
12
+ */
13
+ onClick?: () => void;
5
14
  };
6
15
  /**
7
16
  * React component for the user icon.
8
17
  *
9
18
  * @returns The React component
10
19
  */
11
- export declare const UserIconComponent: React.FC<Props>;
20
+ export declare function UserIconComponent(props: UserIconProps): JSX.Element;
21
+ type UserDetailsBodyProps = {
22
+ /**
23
+ * The user manager instance.
24
+ **/
25
+ userManager: User.IManager;
26
+ };
27
+ /**
28
+ * React widget for the user details.
29
+ **/
30
+ export declare class UserDetailsBody extends ReactWidget {
31
+ /**
32
+ * Constructs a new user details widget.
33
+ */
34
+ constructor(props: UserDetailsBodyProps);
35
+ /**
36
+ * Get the user modified fields.
37
+ */
38
+ getValue(): UserUpdate;
39
+ /**
40
+ * Handle change on a field, by updating the user object.
41
+ */
42
+ private _onChange;
43
+ render(): React.JSX.Element;
44
+ private _userManager;
45
+ private _userUpdate;
46
+ }
47
+ /**
48
+ * Type for the user update object.
49
+ */
50
+ export type UserUpdate = {
51
+ [field in keyof Omit<User.IIdentity, 'username'>]: string;
52
+ };
12
53
  export {};
package/lib/components.js CHANGED
@@ -1,15 +1,69 @@
1
1
  // Copyright (c) Jupyter Development Team.
2
2
  // Distributed under the terms of the Modified BSD License.
3
- import * as React from 'react';
3
+ import { ReactWidget } from '@jupyterlab/ui-components';
4
+ import React, { useEffect, useState } from 'react';
4
5
  /**
5
6
  * React component for the user icon.
6
7
  *
7
8
  * @returns The React component
8
9
  */
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
- };
10
+ export function UserIconComponent(props) {
11
+ const { userManager, onClick } = props;
12
+ const [user, setUser] = useState(userManager.identity);
13
+ useEffect(() => {
14
+ const updateUser = () => {
15
+ setUser(userManager.identity);
16
+ };
17
+ userManager.userChanged.connect(updateUser);
18
+ return () => {
19
+ userManager.userChanged.disconnect(updateUser);
20
+ };
21
+ }, [userManager]);
22
+ return (React.createElement("div", { title: user.display_name, className: "jp-UserInfo-Icon", style: { backgroundColor: user.color }, onClick: onClick },
23
+ React.createElement("span", null, user.initials)));
24
+ }
25
+ /**
26
+ * React widget for the user details.
27
+ **/
28
+ export class UserDetailsBody extends ReactWidget {
29
+ /**
30
+ * Constructs a new user details widget.
31
+ */
32
+ constructor(props) {
33
+ super();
34
+ /**
35
+ * Handle change on a field, by updating the user object.
36
+ */
37
+ this._onChange = (event, field) => {
38
+ var _a;
39
+ const updatableFields = (((_a = this._userManager.permissions) === null || _a === void 0 ? void 0 : _a['updatable_fields']) || []);
40
+ if (!(updatableFields === null || updatableFields === void 0 ? void 0 : updatableFields.includes(field))) {
41
+ return;
42
+ }
43
+ this._userUpdate[field] =
44
+ event.target.value;
45
+ };
46
+ this._userUpdate = {};
47
+ this._userManager = props.userManager;
48
+ }
49
+ /**
50
+ * Get the user modified fields.
51
+ */
52
+ getValue() {
53
+ return this._userUpdate;
54
+ }
55
+ render() {
56
+ var _a;
57
+ const identity = this._userManager.identity;
58
+ if (!identity) {
59
+ return React.createElement("div", { className: "jp-UserInfo-Details" }, "Error loading user info");
60
+ }
61
+ const updatableFields = (((_a = this._userManager.permissions) === null || _a === void 0 ? void 0 : _a['updatable_fields']) || []);
62
+ return (React.createElement("div", { className: "jp-UserInfo-Details" }, Object.keys(identity).map((field) => {
63
+ const id = `jp-UserInfo-Value-${field}`;
64
+ return (React.createElement("div", { key: field, className: "jp-UserInfo-Field" },
65
+ React.createElement("label", { htmlFor: id }, field),
66
+ React.createElement("input", { type: 'text', name: field, id: id, onInput: (event) => this._onChange(event, field), defaultValue: identity[field], disabled: !(updatableFields === null || updatableFields === void 0 ? void 0 : updatableFields.includes(field)) })));
67
+ })));
68
+ }
69
+ }
@@ -1,21 +1,32 @@
1
- import { ReactWidget } from '@jupyterlab/apputils';
1
+ import { Dialog, ReactWidget } from '@jupyterlab/apputils';
2
2
  import { User } from '@jupyterlab/services';
3
+ import { IRenderMime } from '@jupyterlab/rendermime-interfaces';
3
4
  import { Panel } from '@lumino/widgets';
5
+ /**
6
+ * The properties for the UserInfoBody.
7
+ */
8
+ type UserInfoProps = {
9
+ userManager: User.IManager;
10
+ trans: IRenderMime.TranslationBundle;
11
+ };
4
12
  export declare class UserInfoPanel extends Panel {
5
13
  private _profile;
6
14
  private _body;
7
- constructor(user: User.IManager);
15
+ constructor(options: UserInfoProps);
8
16
  }
9
17
  /**
10
18
  * A SettingsWidget for the user.
11
19
  */
12
- export declare class UserInfoBody extends ReactWidget {
13
- private _user;
20
+ export declare class UserInfoBody extends ReactWidget implements Dialog.IBodyWidget<User.IManager> {
21
+ private _userManager;
22
+ private _trans;
14
23
  /**
15
24
  * Constructs a new settings widget.
16
25
  */
17
- constructor(user: User.IIdentity);
18
- get user(): User.IIdentity;
19
- set user(user: User.IIdentity);
26
+ constructor(props: UserInfoProps);
27
+ get user(): User.IManager;
28
+ set user(user: User.IManager);
29
+ private onClick;
20
30
  render(): JSX.Element;
21
31
  }
32
+ export {};
@@ -1,24 +1,32 @@
1
1
  // Copyright (c) Jupyter Development Team.
2
2
  // Distributed under the terms of the Modified BSD License.
3
- import { ReactWidget } from '@jupyterlab/apputils';
3
+ import { ReactWidget, showDialog } from '@jupyterlab/apputils';
4
+ import { ServerConnection } from '@jupyterlab/services';
5
+ import { URLExt } from '@jupyterlab/coreutils';
4
6
  import { Panel } from '@lumino/widgets';
5
7
  import * as React from 'react';
6
- import { UserIconComponent } from './components';
8
+ import { UserDetailsBody, UserIconComponent } from './components';
7
9
  export class UserInfoPanel extends Panel {
8
- constructor(user) {
10
+ constructor(options) {
9
11
  super({});
10
12
  this.addClass('jp-UserInfoPanel');
11
- this._profile = user;
13
+ this._profile = options.userManager;
12
14
  this._body = null;
13
15
  if (this._profile.isReady) {
14
- this._body = new UserInfoBody(this._profile.identity);
16
+ this._body = new UserInfoBody({
17
+ userManager: this._profile,
18
+ trans: options.trans
19
+ });
15
20
  this.addWidget(this._body);
16
21
  this.update();
17
22
  }
18
23
  else {
19
24
  this._profile.ready
20
25
  .then(() => {
21
- this._body = new UserInfoBody(this._profile.identity);
26
+ this._body = new UserInfoBody({
27
+ userManager: this._profile,
28
+ trans: options.trans
29
+ });
22
30
  this.addWidget(this._body);
23
31
  this.update();
24
32
  })
@@ -33,18 +41,59 @@ export class UserInfoBody extends ReactWidget {
33
41
  /**
34
42
  * Constructs a new settings widget.
35
43
  */
36
- constructor(user) {
44
+ constructor(props) {
37
45
  super();
38
- this._user = user;
46
+ this.onClick = () => {
47
+ if (!this._userManager.identity) {
48
+ return;
49
+ }
50
+ showDialog({
51
+ body: new UserDetailsBody({
52
+ userManager: this._userManager
53
+ }),
54
+ title: this._trans.__('User Details')
55
+ }).then(async (result) => {
56
+ if (result.button.accept) {
57
+ // Call the Jupyter Server API to update the user field
58
+ try {
59
+ const settings = ServerConnection.makeSettings();
60
+ const url = URLExt.join(settings.baseUrl, '/api/me');
61
+ const body = {
62
+ method: 'PATCH',
63
+ body: JSON.stringify(result.value)
64
+ };
65
+ let response;
66
+ try {
67
+ response = await ServerConnection.makeRequest(url, body, settings);
68
+ }
69
+ catch (error) {
70
+ throw new ServerConnection.NetworkError(error);
71
+ }
72
+ if (!response.ok) {
73
+ const errorMsg = this._trans.__('Failed to update user data');
74
+ throw new Error(errorMsg);
75
+ }
76
+ // Refresh user information
77
+ this._userManager.refreshUser();
78
+ }
79
+ catch (error) {
80
+ console.error(error);
81
+ }
82
+ }
83
+ });
84
+ };
85
+ this._userManager = props.userManager;
86
+ this._trans = props.trans;
39
87
  }
40
88
  get user() {
41
- return this._user;
89
+ return this._userManager;
42
90
  }
43
91
  set user(user) {
44
- this._user = user;
92
+ this._userManager = user;
45
93
  this.update();
46
94
  }
47
95
  render() {
48
- return React.createElement(UserIconComponent, { user: this._user });
96
+ return (React.createElement("div", { className: "jp-UserInfo-Container" },
97
+ React.createElement(UserIconComponent, { userManager: this._userManager, onClick: this.onClick })));
49
98
  }
50
99
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@jupyter/collaboration",
3
- "version": "4.0.1",
3
+ "version": "4.1.0-alpha.0",
4
4
  "description": "JupyterLab - Real-Time Collaboration Widgets",
5
5
  "homepage": "https://github.com/jupyterlab/jupyter-collaboration",
6
6
  "bugs": {
@@ -44,6 +44,7 @@
44
44
  "@jupyterlab/apputils": "^4.4.0",
45
45
  "@jupyterlab/coreutils": "^6.4.0",
46
46
  "@jupyterlab/docregistry": "^4.4.0",
47
+ "@jupyterlab/rendermime-interfaces": "^3.12.0",
47
48
  "@jupyterlab/services": "^7.4.0",
48
49
  "@jupyterlab/ui-components": "^4.4.0",
49
50
  "@lumino/coreutils": "^2.2.1",
@@ -142,3 +142,38 @@
142
142
  box-shadow: 0 2px 2px -2px rgb(0 0 0 / 24%);
143
143
 
144
144
  }
145
+
146
+ /************************************************************
147
+ User Info Details
148
+ *************************************************************/
149
+ .jp-UserInfo-Field {
150
+ display: flex;
151
+ justify-content: space-between;
152
+ }
153
+
154
+ .jp-UserInfo-Field > label,
155
+ .jp-UserInfo-Field > input {
156
+ padding: 0.5em 1em;
157
+ margin: 0.25em 0;
158
+ }
159
+
160
+ .jp-UserInfo-Field > label {
161
+ font-weight: bold;
162
+ }
163
+
164
+ .jp-UserInfo-Field > input {
165
+ border: none;
166
+ }
167
+
168
+ .jp-UserInfo-Field > input:not(:disabled) {
169
+ cursor: pointer;
170
+ background-color: var(--jp-input-background);
171
+ }
172
+
173
+ .jp-UserInfo-Field > input:focus {
174
+ border: solid 1px var(--jp-cell-editor-active-border-color);
175
+ }
176
+
177
+ .jp-UserInfo-Field > input:focus-visible {
178
+ outline: none;
179
+ }