@jupyterlab/celltags-extension 4.0.0-alpha.8 → 4.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/README.md CHANGED
@@ -1,4 +1,3 @@
1
1
  # @jupyterlab/celltags-extension
2
2
 
3
- A JupyterLab extension which provides an entry point for
4
- [@jupyterlab/celltags](../celltags).
3
+ A JupyterLab extension which provides a form renderer for NotebookTools common tool.
@@ -0,0 +1,24 @@
1
+ import { FieldProps } from '@rjsf/utils';
2
+ import { INotebookTracker } from '@jupyterlab/notebook';
3
+ import { ITranslator } from '@jupyterlab/translation';
4
+ export declare class CellTagField {
5
+ constructor(tracker: INotebookTracker, translator?: ITranslator);
6
+ addTag(props: FieldProps, tag: string): void;
7
+ /**
8
+ * Pull from cell metadata all the tags used in the notebook and update the
9
+ * stored tag list.
10
+ */
11
+ pullTags(): string[];
12
+ private _emptyAddTag;
13
+ private _onAddTagKeyDown;
14
+ private _onAddTagFocus;
15
+ private _onAddTagBlur;
16
+ private _onChange;
17
+ private _onAddTagClick;
18
+ private _onTagClick;
19
+ render(props: FieldProps): JSX.Element;
20
+ private _tracker;
21
+ private _translator;
22
+ private _trans;
23
+ private _editing;
24
+ }
package/lib/celltag.js ADDED
@@ -0,0 +1,153 @@
1
+ /*
2
+ * Copyright (c) Jupyter Development Team.
3
+ * Distributed under the terms of the Modified BSD License.
4
+ */
5
+ import React from 'react';
6
+ import { addIcon, checkIcon, LabIcon } from '@jupyterlab/ui-components';
7
+ import { reduce } from '@lumino/algorithm';
8
+ import { nullTranslator } from '@jupyterlab/translation';
9
+ /**
10
+ * The class name added to the cell-tags field.
11
+ */
12
+ const CELL_TAGS_WIDGET_CLASS = 'jp-CellTags';
13
+ /**
14
+ * The class name added to each tag element.
15
+ */
16
+ const CELL_TAGS_ELEMENT_CLASS = 'jp-CellTags-Tag';
17
+ /**
18
+ * The class name added to each applied tag element.
19
+ */
20
+ const CELL_TAGS_ELEMENT_APPLIED_CLASS = 'jp-CellTags-Applied';
21
+ /**
22
+ * The class name added to each unapplied tag element.
23
+ */
24
+ const CELL_TAGS_ELEMENT_UNAPPLIED_CLASS = 'jp-CellTags-Unapplied';
25
+ /**
26
+ * The class name added to the tag holder.
27
+ */
28
+ const CELL_TAGS_HOLDER_CLASS = 'jp-CellTags-Holder';
29
+ /**
30
+ * The class name added to the add-tag input.
31
+ */
32
+ const CELL_TAGS_ADD_CLASS = 'jp-CellTags-Add';
33
+ /**
34
+ * The class name added to an empty input.
35
+ */
36
+ const CELL_TAGS_EMPTY_CLASS = 'jp-CellTags-Empty';
37
+ export class CellTagField {
38
+ constructor(tracker, translator) {
39
+ this._tracker = tracker;
40
+ this._translator = translator || nullTranslator;
41
+ this._trans = this._translator.load('jupyterlab');
42
+ this._editing = false;
43
+ }
44
+ addTag(props, tag) {
45
+ const data = props.formData;
46
+ if (tag && !data.includes(tag)) {
47
+ data.push(tag);
48
+ props.formContext.updateMetadata({ [props.name]: data }, true);
49
+ }
50
+ }
51
+ /**
52
+ * Pull from cell metadata all the tags used in the notebook and update the
53
+ * stored tag list.
54
+ */
55
+ pullTags() {
56
+ var _a, _b;
57
+ const notebook = (_a = this._tracker) === null || _a === void 0 ? void 0 : _a.currentWidget;
58
+ const cells = (_b = notebook === null || notebook === void 0 ? void 0 : notebook.model) === null || _b === void 0 ? void 0 : _b.cells;
59
+ if (cells === undefined) {
60
+ return [];
61
+ }
62
+ const allTags = reduce(cells, (allTags, cell) => {
63
+ var _a;
64
+ const tags = (_a = cell.getMetadata('tags')) !== null && _a !== void 0 ? _a : [];
65
+ return [...allTags, ...tags];
66
+ }, []);
67
+ return [...new Set(allTags)].filter(tag => tag !== '');
68
+ }
69
+ _emptyAddTag(target) {
70
+ target.value = '';
71
+ target.style.width = '';
72
+ target.classList.add(CELL_TAGS_EMPTY_CLASS);
73
+ }
74
+ _onAddTagKeyDown(props, event) {
75
+ const input = event.target;
76
+ if (event.ctrlKey)
77
+ return;
78
+ if (event.key === 'Enter') {
79
+ this.addTag(props, input.value);
80
+ }
81
+ else if (event.key === 'Escape') {
82
+ this._emptyAddTag(input);
83
+ }
84
+ }
85
+ _onAddTagFocus(event) {
86
+ if (!this._editing) {
87
+ event.target.blur();
88
+ }
89
+ }
90
+ _onAddTagBlur(input) {
91
+ if (this._editing) {
92
+ this._editing = false;
93
+ this._emptyAddTag(input);
94
+ }
95
+ }
96
+ _onChange(event) {
97
+ if (!event.target.value) {
98
+ this._emptyAddTag(event.target);
99
+ }
100
+ else {
101
+ event.target.classList.remove(CELL_TAGS_EMPTY_CLASS);
102
+ const tmp = document.createElement('span');
103
+ tmp.className = CELL_TAGS_ADD_CLASS;
104
+ tmp.textContent = event.target.value;
105
+ // set width to the pixel length of the text
106
+ document.body.appendChild(tmp);
107
+ event.target.style.setProperty('width', `calc(${tmp.getBoundingClientRect().width}px + var(--jp-add-tag-extra-width))`);
108
+ document.body.removeChild(tmp);
109
+ }
110
+ }
111
+ _onAddTagClick(props, event) {
112
+ const elem = event.target.closest('div');
113
+ const input = elem === null || elem === void 0 ? void 0 : elem.childNodes[0];
114
+ if (!this._editing) {
115
+ this._editing = true;
116
+ input.value = '';
117
+ input.focus();
118
+ }
119
+ else if (event.target !== input) {
120
+ this.addTag(props, input.value);
121
+ }
122
+ event.preventDefault();
123
+ }
124
+ _onTagClick(props, tag) {
125
+ const data = props.formData;
126
+ if (data.includes(tag)) {
127
+ data.splice(data.indexOf(tag), 1);
128
+ }
129
+ else {
130
+ data.push(tag);
131
+ }
132
+ props.formContext.updateMetadata({ [props.name]: data }, true);
133
+ }
134
+ render(props) {
135
+ const allTags = this.pullTags();
136
+ return (React.createElement("div", { className: CELL_TAGS_WIDGET_CLASS },
137
+ React.createElement("div", { className: "jp-FormGroup-fieldLabel jp-FormGroup-contentItem" }, "Cell Tags"),
138
+ allTags &&
139
+ allTags.map((tag, i) => (React.createElement("div", { key: i, className: `${CELL_TAGS_ELEMENT_CLASS} ${props.formData.includes(tag)
140
+ ? CELL_TAGS_ELEMENT_APPLIED_CLASS
141
+ : CELL_TAGS_ELEMENT_UNAPPLIED_CLASS}`, onClick: () => this._onTagClick(props, tag) },
142
+ React.createElement("div", { className: CELL_TAGS_HOLDER_CLASS },
143
+ React.createElement("span", null, tag),
144
+ props.formData.includes(tag) && (React.createElement(LabIcon.resolveReact, { icon: checkIcon, tag: "span", elementPosition: "center", height: "18px", width: "18px", marginLeft: "5px", marginRight: "-3px" })))))),
145
+ React.createElement("div", { className: `${CELL_TAGS_ELEMENT_CLASS} ${CELL_TAGS_ELEMENT_UNAPPLIED_CLASS}` },
146
+ React.createElement("div", { className: CELL_TAGS_HOLDER_CLASS, onMouseDown: (e) => this._onAddTagClick(props, e) },
147
+ React.createElement("input", { className: `${CELL_TAGS_ADD_CLASS} ${CELL_TAGS_EMPTY_CLASS}`, type: "text", placeholder: this._trans.__('Add Tag'), onKeyDown: (e) => this._onAddTagKeyDown(props, e), onFocus: (e) => this._onAddTagFocus(e), onBlur: (e) => this._onAddTagBlur(e.target), onChange: (e) => {
148
+ this._onChange(e);
149
+ } }),
150
+ React.createElement(LabIcon.resolveReact, { icon: addIcon, tag: "span", height: "18px", width: "18px", marginLeft: "5px", marginRight: "-3px" })))));
151
+ }
152
+ }
153
+ //# sourceMappingURL=celltag.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"celltag.js","sourceRoot":"","sources":["../src/celltag.tsx"],"names":[],"mappings":"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"}
package/lib/index.d.ts CHANGED
@@ -3,8 +3,5 @@
3
3
  * @module celltags-extension
4
4
  */
5
5
  import { JupyterFrontEndPlugin } from '@jupyterlab/application';
6
- /**
7
- * Initialization data for the celltags extension.
8
- */
9
- declare const celltags: JupyterFrontEndPlugin<void>;
10
- export default celltags;
6
+ declare const _default: JupyterFrontEndPlugin<void>[];
7
+ export default _default;
package/lib/index.js CHANGED
@@ -4,20 +4,28 @@
4
4
  * @packageDocumentation
5
5
  * @module celltags-extension
6
6
  */
7
- import { INotebookTools, INotebookTracker } from '@jupyterlab/notebook';
8
- import { TagTool } from '@jupyterlab/celltags';
9
- import { ITranslator } from '@jupyterlab/translation';
7
+ import { INotebookTracker } from '@jupyterlab/notebook';
8
+ import { CellTagField } from './celltag';
9
+ import { IFormRendererRegistry } from '@jupyterlab/ui-components';
10
10
  /**
11
- * Initialization data for the celltags extension.
11
+ * Registering cell tag field.
12
12
  */
13
- const celltags = {
14
- id: '@jupyterlab/celltags',
13
+ const customCellTag = {
14
+ id: '@jupyterlab/celltags-extension:plugin',
15
15
  autoStart: true,
16
- requires: [INotebookTools, INotebookTracker, ITranslator],
17
- activate: (app, tools, tracker, translator) => {
18
- const tool = new TagTool(tracker, app, translator);
19
- tools.addItem({ tool: tool, rank: 1.6 });
16
+ requires: [INotebookTracker],
17
+ optional: [IFormRendererRegistry],
18
+ activate: (app, tracker, formRegistry) => {
19
+ // Register the custom field
20
+ if (formRegistry) {
21
+ const component = {
22
+ fieldRenderer: (props) => {
23
+ return new CellTagField(tracker).render(props);
24
+ }
25
+ };
26
+ formRegistry.addRenderer('celltags-extension:plugin.renderer', component);
27
+ }
20
28
  }
21
29
  };
22
- export default celltags;
30
+ export default [customCellTag];
23
31
  //# sourceMappingURL=index.js.map
package/lib/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,0CAA0C;AAC1C,2DAA2D;AAC3D;;;GAGG;AAOH,OAAO,EAAE,cAAc,EAAE,gBAAgB,EAAE,MAAM,sBAAsB,CAAC;AAExE,OAAO,EAAE,OAAO,EAAE,MAAM,sBAAsB,CAAC;AAE/C,OAAO,EAAE,WAAW,EAAE,MAAM,yBAAyB,CAAC;AAEtD;;GAEG;AACH,MAAM,QAAQ,GAAgC;IAC5C,EAAE,EAAE,sBAAsB;IAC1B,SAAS,EAAE,IAAI;IACf,QAAQ,EAAE,CAAC,cAAc,EAAE,gBAAgB,EAAE,WAAW,CAAC;IACzD,QAAQ,EAAE,CACR,GAAoB,EACpB,KAAqB,EACrB,OAAyB,EACzB,UAAuB,EACvB,EAAE;QACF,MAAM,IAAI,GAAG,IAAI,OAAO,CAAC,OAAO,EAAE,GAAG,EAAE,UAAU,CAAC,CAAC;QACnD,KAAK,CAAC,OAAO,CAAC,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,GAAG,EAAE,CAAC,CAAC;IAC3C,CAAC;CACF,CAAC;AAEF,eAAe,QAAQ,CAAC"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,0CAA0C;AAC1C,2DAA2D;AAC3D;;;GAGG;AAQH,OAAO,EAAE,gBAAgB,EAAE,MAAM,sBAAsB,CAAC;AAExD,OAAO,EAAE,YAAY,EAAE,MAAM,WAAW,CAAC;AACzC,OAAO,EAEL,qBAAqB,EACtB,MAAM,2BAA2B,CAAC;AAEnC;;GAEG;AACH,MAAM,aAAa,GAAgC;IACjD,EAAE,EAAE,uCAAuC;IAC3C,SAAS,EAAE,IAAI;IACf,QAAQ,EAAE,CAAC,gBAAgB,CAAC;IAC5B,QAAQ,EAAE,CAAC,qBAAqB,CAAC;IACjC,QAAQ,EAAE,CACR,GAAoB,EACpB,OAAyB,EACzB,YAAoC,EACpC,EAAE;QACF,4BAA4B;QAC5B,IAAI,YAAY,EAAE;YAChB,MAAM,SAAS,GAAkB;gBAC/B,aAAa,EAAE,CAAC,KAAiB,EAAE,EAAE;oBACnC,OAAO,IAAI,YAAY,CAAC,OAAO,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;gBACjD,CAAC;aACF,CAAC;YACF,YAAY,CAAC,WAAW,CAAC,oCAAoC,EAAE,SAAS,CAAC,CAAC;SAC3E;IACH,CAAC;CACF,CAAC;AAEF,eAAe,CAAC,aAAa,CAAC,CAAC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@jupyterlab/celltags-extension",
3
- "version": "4.0.0-alpha.8",
3
+ "version": "4.0.0-beta.0",
4
4
  "description": "An extension for manipulating tags in cell metadata",
5
5
  "keywords": [
6
6
  "jupyter",
@@ -30,7 +30,9 @@
30
30
  "files": [
31
31
  "lib/*.{d.ts,js,js.map}",
32
32
  "style/*.css",
33
- "style/index.js"
33
+ "style/index.js",
34
+ "src/**/*.{ts,tsx}",
35
+ "schema/*.json"
34
36
  ],
35
37
  "scripts": {
36
38
  "build": "tsc",
@@ -38,20 +40,24 @@
38
40
  "watch": "tsc -b --watch"
39
41
  },
40
42
  "dependencies": {
41
- "@jupyterlab/application": "^4.0.0-alpha.8",
42
- "@jupyterlab/celltags": "^4.0.0-alpha.8",
43
- "@jupyterlab/notebook": "^4.0.0-alpha.8",
44
- "@jupyterlab/translation": "^4.0.0-alpha.8"
43
+ "@jupyterlab/application": "^4.0.0-beta.0",
44
+ "@jupyterlab/notebook": "^4.0.0-beta.0",
45
+ "@jupyterlab/translation": "^4.0.0-beta.0",
46
+ "@jupyterlab/ui-components": "^4.0.0-beta.0",
47
+ "@lumino/algorithm": "^2.0.0",
48
+ "@rjsf/utils": "^5.1.0",
49
+ "react": "^18.2.0"
45
50
  },
46
51
  "devDependencies": {
47
52
  "rimraf": "~3.0.0",
48
- "typescript": "~4.5.2"
53
+ "typescript": "~5.0.2"
49
54
  },
50
55
  "publishConfig": {
51
56
  "access": "public"
52
57
  },
53
58
  "jupyterlab": {
54
- "extension": true
59
+ "extension": true,
60
+ "schemaDir": "schema"
55
61
  },
56
62
  "styleModule": "style/index.js"
57
63
  }
@@ -0,0 +1,33 @@
1
+ {
2
+ "type": "object",
3
+ "title": "Common tools",
4
+ "description": "Setting for the common tools",
5
+ "jupyter.lab.metadataforms": [
6
+ {
7
+ "id": "commonToolsSection",
8
+ "label": "Common tools",
9
+ "metadataSchema": {
10
+ "type": "object",
11
+ "properties": {
12
+ "/tags": {
13
+ "title": "Cell tag",
14
+ "type": "array",
15
+ "default": [],
16
+ "items": {
17
+ "type": "string"
18
+ }
19
+ }
20
+ }
21
+ },
22
+ "uiSchema": {
23
+ "ui:order": ["_CELL-TOOL", "/tags", "*"]
24
+ },
25
+ "metadataOptions": {
26
+ "/tags": {
27
+ "customRenderer": "celltags-extension:plugin.renderer"
28
+ }
29
+ }
30
+ }
31
+ ],
32
+ "additionalProperties": false
33
+ }
@@ -0,0 +1,243 @@
1
+ /*
2
+ * Copyright (c) Jupyter Development Team.
3
+ * Distributed under the terms of the Modified BSD License.
4
+ */
5
+
6
+ import React from 'react';
7
+ import { FieldProps } from '@rjsf/utils';
8
+ import { INotebookTracker } from '@jupyterlab/notebook';
9
+ import { addIcon, checkIcon, LabIcon } from '@jupyterlab/ui-components';
10
+ import { reduce } from '@lumino/algorithm';
11
+ import {
12
+ ITranslator,
13
+ nullTranslator,
14
+ TranslationBundle
15
+ } from '@jupyterlab/translation';
16
+
17
+ /**
18
+ * The class name added to the cell-tags field.
19
+ */
20
+ const CELL_TAGS_WIDGET_CLASS = 'jp-CellTags';
21
+ /**
22
+ * The class name added to each tag element.
23
+ */
24
+ const CELL_TAGS_ELEMENT_CLASS = 'jp-CellTags-Tag';
25
+ /**
26
+ * The class name added to each applied tag element.
27
+ */
28
+ const CELL_TAGS_ELEMENT_APPLIED_CLASS = 'jp-CellTags-Applied';
29
+ /**
30
+ * The class name added to each unapplied tag element.
31
+ */
32
+ const CELL_TAGS_ELEMENT_UNAPPLIED_CLASS = 'jp-CellTags-Unapplied';
33
+ /**
34
+ * The class name added to the tag holder.
35
+ */
36
+ const CELL_TAGS_HOLDER_CLASS = 'jp-CellTags-Holder';
37
+ /**
38
+ * The class name added to the add-tag input.
39
+ */
40
+ const CELL_TAGS_ADD_CLASS = 'jp-CellTags-Add';
41
+ /**
42
+ * The class name added to an empty input.
43
+ */
44
+ const CELL_TAGS_EMPTY_CLASS = 'jp-CellTags-Empty';
45
+
46
+ export class CellTagField {
47
+ constructor(tracker: INotebookTracker, translator?: ITranslator) {
48
+ this._tracker = tracker;
49
+ this._translator = translator || nullTranslator;
50
+ this._trans = this._translator.load('jupyterlab');
51
+ this._editing = false;
52
+ }
53
+
54
+ addTag(props: FieldProps, tag: string) {
55
+ const data = props.formData;
56
+ if (tag && !data.includes(tag)) {
57
+ data.push(tag);
58
+ props.formContext.updateMetadata({ [props.name]: data }, true);
59
+ }
60
+ }
61
+
62
+ /**
63
+ * Pull from cell metadata all the tags used in the notebook and update the
64
+ * stored tag list.
65
+ */
66
+ pullTags(): string[] {
67
+ const notebook = this._tracker?.currentWidget;
68
+ const cells = notebook?.model?.cells;
69
+ if (cells === undefined) {
70
+ return [];
71
+ }
72
+ const allTags = reduce(
73
+ cells,
74
+ (allTags: string[], cell) => {
75
+ const tags: string[] = (cell.getMetadata('tags') as string[]) ?? [];
76
+ return [...allTags, ...tags];
77
+ },
78
+ []
79
+ );
80
+ return [...new Set(allTags)].filter(tag => tag !== '');
81
+ }
82
+
83
+ private _emptyAddTag(target: HTMLInputElement) {
84
+ target.value = '';
85
+ target.style.width = '';
86
+ target.classList.add(CELL_TAGS_EMPTY_CLASS);
87
+ }
88
+
89
+ private _onAddTagKeyDown(
90
+ props: FieldProps,
91
+ event: React.KeyboardEvent<HTMLInputElement>
92
+ ) {
93
+ const input = event.target as HTMLInputElement;
94
+
95
+ if (event.ctrlKey) return;
96
+
97
+ if (event.key === 'Enter') {
98
+ this.addTag(props, input.value);
99
+ } else if (event.key === 'Escape') {
100
+ this._emptyAddTag(input);
101
+ }
102
+ }
103
+
104
+ private _onAddTagFocus(event: React.FocusEvent<HTMLInputElement>) {
105
+ if (!this._editing) {
106
+ (event.target as HTMLInputElement).blur();
107
+ }
108
+ }
109
+
110
+ private _onAddTagBlur(input: HTMLInputElement) {
111
+ if (this._editing) {
112
+ this._editing = false;
113
+ this._emptyAddTag(input);
114
+ }
115
+ }
116
+
117
+ private _onChange(event: React.ChangeEvent<HTMLInputElement>) {
118
+ if (!event.target.value) {
119
+ this._emptyAddTag(event.target);
120
+ } else {
121
+ event.target.classList.remove(CELL_TAGS_EMPTY_CLASS);
122
+ const tmp = document.createElement('span');
123
+ tmp.className = CELL_TAGS_ADD_CLASS;
124
+ tmp.textContent = event.target.value;
125
+ // set width to the pixel length of the text
126
+ document.body.appendChild(tmp);
127
+ event.target.style.setProperty(
128
+ 'width',
129
+ `calc(${
130
+ tmp.getBoundingClientRect().width
131
+ }px + var(--jp-add-tag-extra-width))`
132
+ );
133
+ document.body.removeChild(tmp);
134
+ }
135
+ }
136
+
137
+ private _onAddTagClick(
138
+ props: FieldProps,
139
+ event: React.MouseEvent<HTMLElement>
140
+ ) {
141
+ const elem = (event.target as HTMLElement).closest('div');
142
+ const input = elem?.childNodes[0] as HTMLInputElement;
143
+ if (!this._editing) {
144
+ this._editing = true;
145
+ input.value = '';
146
+ input.focus();
147
+ } else if (event.target !== input) {
148
+ this.addTag(props, input.value);
149
+ }
150
+ event.preventDefault();
151
+ }
152
+
153
+ private _onTagClick(props: FieldProps, tag: string) {
154
+ const data = props.formData;
155
+ if (data.includes(tag)) {
156
+ data.splice(data.indexOf(tag), 1);
157
+ } else {
158
+ data.push(tag);
159
+ }
160
+
161
+ props.formContext.updateMetadata({ [props.name]: data }, true);
162
+ }
163
+
164
+ render(props: FieldProps): JSX.Element {
165
+ const allTags: string[] = this.pullTags();
166
+
167
+ return (
168
+ <div className={CELL_TAGS_WIDGET_CLASS}>
169
+ <div className="jp-FormGroup-fieldLabel jp-FormGroup-contentItem">
170
+ Cell Tags
171
+ </div>
172
+ {allTags &&
173
+ allTags.map((tag: string, i: number) => (
174
+ <div
175
+ key={i}
176
+ className={`${CELL_TAGS_ELEMENT_CLASS} ${
177
+ props.formData.includes(tag)
178
+ ? CELL_TAGS_ELEMENT_APPLIED_CLASS
179
+ : CELL_TAGS_ELEMENT_UNAPPLIED_CLASS
180
+ }`}
181
+ onClick={() => this._onTagClick(props, tag)}
182
+ >
183
+ <div className={CELL_TAGS_HOLDER_CLASS}>
184
+ <span>{tag}</span>
185
+ {props.formData.includes(tag) && (
186
+ <LabIcon.resolveReact
187
+ icon={checkIcon}
188
+ tag="span"
189
+ elementPosition="center"
190
+ height="18px"
191
+ width="18px"
192
+ marginLeft="5px"
193
+ marginRight="-3px"
194
+ />
195
+ )}
196
+ </div>
197
+ </div>
198
+ ))}
199
+ <div
200
+ className={`${CELL_TAGS_ELEMENT_CLASS} ${CELL_TAGS_ELEMENT_UNAPPLIED_CLASS}`}
201
+ >
202
+ <div
203
+ className={CELL_TAGS_HOLDER_CLASS}
204
+ onMouseDown={(e: React.MouseEvent<HTMLElement>) =>
205
+ this._onAddTagClick(props, e)
206
+ }
207
+ >
208
+ <input
209
+ className={`${CELL_TAGS_ADD_CLASS} ${CELL_TAGS_EMPTY_CLASS}`}
210
+ type="text"
211
+ placeholder={this._trans.__('Add Tag')}
212
+ onKeyDown={(e: React.KeyboardEvent<HTMLInputElement>) =>
213
+ this._onAddTagKeyDown(props, e)
214
+ }
215
+ onFocus={(e: React.FocusEvent<HTMLInputElement>) =>
216
+ this._onAddTagFocus(e)
217
+ }
218
+ onBlur={(e: React.FocusEvent<HTMLInputElement>) =>
219
+ this._onAddTagBlur(e.target)
220
+ }
221
+ onChange={(e: React.ChangeEvent<HTMLInputElement>) => {
222
+ this._onChange(e);
223
+ }}
224
+ />
225
+ <LabIcon.resolveReact
226
+ icon={addIcon}
227
+ tag="span"
228
+ height="18px"
229
+ width="18px"
230
+ marginLeft="5px"
231
+ marginRight="-3px"
232
+ />
233
+ </div>
234
+ </div>
235
+ </div>
236
+ );
237
+ }
238
+
239
+ private _tracker: INotebookTracker;
240
+ private _translator: ITranslator;
241
+ private _trans: TranslationBundle;
242
+ private _editing: boolean;
243
+ }
package/src/index.ts ADDED
@@ -0,0 +1,47 @@
1
+ // Copyright (c) Jupyter Development Team.
2
+ // Distributed under the terms of the Modified BSD License.
3
+ /**
4
+ * @packageDocumentation
5
+ * @module celltags-extension
6
+ */
7
+
8
+ import type { FieldProps } from '@rjsf/utils';
9
+ import {
10
+ JupyterFrontEnd,
11
+ JupyterFrontEndPlugin
12
+ } from '@jupyterlab/application';
13
+
14
+ import { INotebookTracker } from '@jupyterlab/notebook';
15
+
16
+ import { CellTagField } from './celltag';
17
+ import {
18
+ IFormRenderer,
19
+ IFormRendererRegistry
20
+ } from '@jupyterlab/ui-components';
21
+
22
+ /**
23
+ * Registering cell tag field.
24
+ */
25
+ const customCellTag: JupyterFrontEndPlugin<void> = {
26
+ id: '@jupyterlab/celltags-extension:plugin',
27
+ autoStart: true,
28
+ requires: [INotebookTracker],
29
+ optional: [IFormRendererRegistry],
30
+ activate: (
31
+ app: JupyterFrontEnd,
32
+ tracker: INotebookTracker,
33
+ formRegistry?: IFormRendererRegistry
34
+ ) => {
35
+ // Register the custom field
36
+ if (formRegistry) {
37
+ const component: IFormRenderer = {
38
+ fieldRenderer: (props: FieldProps) => {
39
+ return new CellTagField(tracker).render(props);
40
+ }
41
+ };
42
+ formRegistry.addRenderer('celltags-extension:plugin.renderer', component);
43
+ }
44
+ }
45
+ };
46
+
47
+ export default [customCellTag];
package/style/base.css ADDED
@@ -0,0 +1,58 @@
1
+ /*
2
+ * Copyright (c) Jupyter Development Team.
3
+ * Distributed under the terms of the Modified BSD License.
4
+ */
5
+
6
+ :root {
7
+ --jp-add-tag-extra-width: 8px;
8
+ }
9
+
10
+ .jp-CellTags-Tag {
11
+ height: 20px;
12
+ border-radius: 10px;
13
+ margin-right: 5px;
14
+ margin-bottom: 10px;
15
+ padding: 0 8px;
16
+ font-size: var(--jp-ui-font-size1);
17
+ display: inline-flex;
18
+ justify-content: center;
19
+ align-items: center;
20
+ max-width: calc(100% - 25px);
21
+ border: 1px solid var(--jp-border-color1);
22
+ color: var(--jp-ui-font-color1);
23
+ -webkit-touch-callout: none;
24
+ -webkit-user-select: none;
25
+ -khtml-user-select: none;
26
+ -moz-user-select: none;
27
+ -ms-user-select: none;
28
+ user-select: none;
29
+ }
30
+
31
+ .jp-CellTags-Unapplied {
32
+ background-color: var(--jp-layout-color2);
33
+ }
34
+
35
+ .jp-CellTags-Applied {
36
+ background-color: var(--jp-layout-color3);
37
+ }
38
+
39
+ .jp-CellTags-Add {
40
+ white-space: nowrap;
41
+ overflow: hidden;
42
+ border: none;
43
+ outline: none;
44
+ resize: horizontal;
45
+ font-size: var(--jp-ui-font-size1);
46
+ color: var(--jp-ui-font-color1);
47
+ background: var(--jp-layout-color2);
48
+ }
49
+
50
+ .jp-CellTags-Holder {
51
+ display: flex;
52
+ justify-content: center;
53
+ align-items: center;
54
+ }
55
+
56
+ .jp-CellTags-Empty {
57
+ width: 4em;
58
+ }
package/style/index.css CHANGED
@@ -4,6 +4,8 @@
4
4
  |----------------------------------------------------------------------------*/
5
5
 
6
6
  /* This file was auto-generated by ensurePackage() in @jupyterlab/buildutils */
7
+ @import url('~@jupyterlab/ui-components/style/index.css');
7
8
  @import url('~@jupyterlab/application/style/index.css');
8
9
  @import url('~@jupyterlab/notebook/style/index.css');
9
- @import url('~@jupyterlab/celltags/style/index.css');
10
+
11
+ @import url('./base.css');
package/style/index.js CHANGED
@@ -4,6 +4,8 @@
4
4
  |----------------------------------------------------------------------------*/
5
5
 
6
6
  /* This file was auto-generated by ensurePackage() in @jupyterlab/buildutils */
7
+ import '@jupyterlab/ui-components/style/index.js';
7
8
  import '@jupyterlab/application/style/index.js';
8
9
  import '@jupyterlab/notebook/style/index.js';
9
- import '@jupyterlab/celltags/style/index.js';
10
+
11
+ import './base.css';