From c699a55a0bb3936ae10248ed8b195be72da6f237 Mon Sep 17 00:00:00 2001 From: HaudinFlorence Date: Mon, 4 Dec 2023 17:47:57 +0100 Subject: [PATCH 1/2] Add a new side panel and a new drive button in the toolbar. --- package.json | 6 ++- schema/plugin.json | 85 ++++++++++++++++++++++++++++-- src/icons.ts | 6 +++ src/index.ts | 103 +++++++++++++++++++++++++++++++++++- src/svg.d.ts | 4 ++ style/icons/drive.svg | 57 ++++++++++++++++++++ yarn.lock | 118 +++++++++++++++++++++++++++++++++++++++++- 7 files changed, 370 insertions(+), 9 deletions(-) create mode 100644 src/icons.ts create mode 100644 src/svg.d.ts create mode 100644 style/icons/drive.svg diff --git a/package.json b/package.json index 0d2b167..ab67d1c 100644 --- a/package.json +++ b/package.json @@ -22,10 +22,10 @@ } ], "files": [ - "schema/**/*.json", "lib/**/*.{d.ts,eot,gif,html,jpg,js,js.map,json,png,svg,woff2,ttf}", "style/**/*.{css,svg}", - "style/index.js" + "style/index.js", + "schema/*.json" ], "main": "lib/index.js", "types": "lib/index.d.ts", @@ -62,6 +62,8 @@ "watch:src": "tsc -w --sourceMap" }, "dependencies": { + "@jupyter/react-components": "^0.13.3", + "@jupyter/web-components": "^0.13.2", "@jupyterlab/application": "^4.0.5", "@jupyterlab/docmanager": "^4.0.5", "@jupyterlab/filebrowser": "^4.0.5", diff --git a/schema/plugin.json b/schema/plugin.json index bf8473a..1dd86ec 100644 --- a/schema/plugin.json +++ b/schema/plugin.json @@ -1,8 +1,85 @@ { - "jupyter.lab.shortcuts": [], - "title": "jupyterlab-unfold", + "jupyter.lab.toolbars": { + "FileBrowser": [ + { + "name": "new-directory", + "command": "filebrowser:create-new-directory", + "rank": 10 + }, + { "name": "uploader", "rank": 20 }, + { "name": "refresh", "command": "filebrowser:refresh", "rank": 30 }, + { + "name": "drive", + "command": "jupyterlab-unfold:open-drives-dialog", + "rank": 35 + }, + { "name": "fileNameSearcher", "rank": 40 } + ] + }, + "title": "'jupyterlab-unfold", "description": "jupyterlab-unfold settings.", "type": "object", - "properties": {}, - "additionalProperties": false + "jupyter.lab.transform": true, + "properties": { + "toolbar": { + "title": "File browser toolbar items", + "description": "Note: To disable a toolbar item,\ncopy it to User Preferences and add the\n\"disabled\" key. The following example will disable the uploader button:\n{\n \"toolbar\": [\n {\n \"name\": \"uploader\",\n \"disabled\": true\n }\n ]\n}\n\nToolbar description:", + "items": { + "$ref": "#/definitions/toolbarItem" + }, + "type": "array", + "default": [] + } + }, + "additionalProperties": false, + "definitions": { + "toolbarItem": { + "properties": { + "name": { + "title": "Unique name", + "type": "string" + }, + "args": { + "title": "Command arguments", + "type": "object" + }, + "command": { + "title": "Command id", + "type": "string", + "default": "" + }, + "disabled": { + "title": "Whether the item is ignored or not", + "type": "boolean", + "default": false + }, + "icon": { + "title": "Item icon id", + "description": "If defined, it will override the command icon", + "type": "string" + }, + "label": { + "title": "Item label", + "description": "If defined, it will override the command label", + "type": "string" + }, + "caption": { + "title": "Item caption", + "description": "If defined, it will override the command caption", + "type": "string" + }, + "type": { + "title": "Item type", + "type": "string", + "enum": ["command", "spacer"] + }, + "rank": { + "title": "Item rank", + "type": "number", + "minimum": 0, + "default": 50 + } + } + } + } } diff --git a/src/icons.ts b/src/icons.ts new file mode 100644 index 0000000..813178f --- /dev/null +++ b/src/icons.ts @@ -0,0 +1,6 @@ +import { LabIcon } from '@jupyterlab/ui-components'; +import driveSvgstr from '../style/icons/drive.svg'; +export const DriveIcon = new LabIcon({ + name: 'jupyterlab-unfold:drive', + svgstr: driveSvgstr +}); diff --git a/src/index.ts b/src/index.ts index cfafc19..d14ae72 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,4 +1,5 @@ import { + ILayoutRestorer, JupyterFrontEnd, JupyterFrontEndPlugin } from '@jupyterlab/application'; @@ -7,7 +8,12 @@ import { IFileBrowserFactory } from '@jupyterlab/filebrowser'; import { IDocumentManager } from '@jupyterlab/docmanager'; -import { WidgetTracker } from '@jupyterlab/apputils'; +import { + WidgetTracker, + createToolbarFactory, + IToolbarWidgetRegistry, + setToolbar +} from '@jupyterlab/apputils'; import { ISettingRegistry } from '@jupyterlab/settingregistry'; @@ -17,8 +23,26 @@ import { IStateDB } from '@jupyterlab/statedb'; import { FileTreeBrowser, FilterFileTreeBrowserModel } from './unfold'; +import { DriveIcon } from './icons'; + +import { addJupyterLabThemeChangeListener } from '@jupyter/web-components'; + +//import { Dialog, showDialog } from '@jupyterlab/apputils'; + +//import { Drive } from './contents'; + +//import { DriveListModel, DriveListView } from './drivelistmanager'; + const SETTINGS_ID = 'jupyterlab-unfold:jupyterlab-unfold-settings'; +const FILE_BROWSER_FACTORY = 'FileBrowser'; +const FILE_BROWSER_PLUGIN_ID = 'jupyterlab-unfold:plugin'; + +namespace CommandIDs { + export const openDrivesDialog = 'jupyterlab-unfold:open-drives-dialog'; + export const openPath = 'filebrowser:open-path'; +} + /** * The file browser namespace token. */ @@ -80,6 +104,81 @@ const fileBrowserFactory: JupyterFrontEndPlugin = { } }; +const addDrives: JupyterFrontEndPlugin = { + id: 'jupyterlab-unfold:AddDrives', + description: 'Open a dialog to select drives to be added in the filebrowser.', + requires: [ + IDocumentManager, + IToolbarWidgetRegistry, + ITranslator, + ILayoutRestorer, + ISettingRegistry + ], + autoStart: true, + activate: activateAddDrivesPlugin +}; + +export async function activateAddDrivesPlugin( + app: JupyterFrontEnd, + docManager: IDocumentManager, + toolbarRegistry: IToolbarWidgetRegistry, + translator: ITranslator, + restorer: ILayoutRestorer | null, + settings: ISettingRegistry, + state: IStateDB | null +) { + const trans = translator.load('jupyterlab_unfold'); + console.log('AddDrives plugin is activated!'); + const { commands } = app; + + const model = new FilterFileTreeBrowserModel({ + translator: translator, + auto: true, + manager: docManager, + driveName: '', + refreshInterval: 35000, + state: undefined + }); + const panel = new FileTreeBrowser({ + id: 'default', + model, + restore: true, + translator, + app + }); + panel.title.icon = DriveIcon; + panel.title.iconClass = 'jp-SideBar-tabIcon'; + panel.title.caption = 'Browse Drives'; + panel.id = 'panel-file-browser'; + if (restorer) { + restorer.add(panel, 'drive-browser'); + } + app.shell.add(panel, 'left', { rank: 102 }); + setToolbar( + panel, + createToolbarFactory( + toolbarRegistry, + settings, + FILE_BROWSER_FACTORY, + FILE_BROWSER_PLUGIN_ID, + translator + ) + ); + + addJupyterLabThemeChangeListener(); + + commands.addCommand(CommandIDs.openDrivesDialog, { + execute: async args => { + console.log('You have clicked on D button'); + }, + + icon: DriveIcon.bindprops({ stylesheet: 'menuItem' }), + caption: trans.__('Add drives to filebrowser.'), + label: trans.__('Add Drives To Filebrowser') + }); +} + export * from './unfold'; -export default fileBrowserFactory; +const plugins: JupyterFrontEndPlugin[] = [fileBrowserFactory, addDrives]; +export default plugins; diff --git a/src/svg.d.ts b/src/svg.d.ts new file mode 100644 index 0000000..d4627e5 --- /dev/null +++ b/src/svg.d.ts @@ -0,0 +1,4 @@ +declare module '*.svg' { + const value: string; // @ts-ignore + export default value; +} diff --git a/style/icons/drive.svg b/style/icons/drive.svg new file mode 100644 index 0000000..f064d5b --- /dev/null +++ b/style/icons/drive.svg @@ -0,0 +1,57 @@ + + + + + + diff --git a/yarn.lock b/yarn.lock index 4e8a016..069f69b 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2039,6 +2039,30 @@ __metadata: languageName: node linkType: hard +"@jupyter/react-components@npm:^0.13.3": + version: 0.13.3 + resolution: "@jupyter/react-components@npm:0.13.3" + dependencies: + "@jupyter/web-components": ^0.13.3 + "@microsoft/fast-react-wrapper": ^0.3.18 + react: ">=17.0.0 <19.0.0" + checksum: d8912ff6a68833d18bfe44489d71c9e6b4203a29c3c4f65379e630b2b1c1bd887360609d0ee2d03db2e84ee41570de1757cc09a1144288cd0e27a5e9bc0c6e82 + languageName: node + linkType: hard + +"@jupyter/web-components@npm:^0.13.2, @jupyter/web-components@npm:^0.13.3": + version: 0.13.3 + resolution: "@jupyter/web-components@npm:0.13.3" + dependencies: + "@microsoft/fast-colors": ^5.3.1 + "@microsoft/fast-components": ^2.30.6 + "@microsoft/fast-element": ^1.12.0 + "@microsoft/fast-foundation": ^2.49.0 + "@microsoft/fast-web-utilities": ^6.0.0 + checksum: 23a698f4a0cecc0536f8af54c57175fd276d731a8dd978fe52ada02a72679189096f4fff337279a38a75cfdd92c590f7295d3fd12b6e1c5e3241a4691137d214 + languageName: node + linkType: hard + "@jupyter/ydoc@npm:^1.0.2": version: 1.0.2 resolution: "@jupyter/ydoc@npm:1.0.2" @@ -2969,6 +2993,75 @@ __metadata: languageName: node linkType: hard +"@microsoft/fast-colors@npm:^5.3.0, @microsoft/fast-colors@npm:^5.3.1": + version: 5.3.1 + resolution: "@microsoft/fast-colors@npm:5.3.1" + checksum: ff87f402faadb4b5aeee3d27762566c11807f927cd4012b8bbc7f073ca68de0e2197f95330ff5dfd7038f4b4f0e2f51b11feb64c5d570f5c598d37850a5daf60 + languageName: node + linkType: hard + +"@microsoft/fast-components@npm:^2.30.6": + version: 2.30.6 + resolution: "@microsoft/fast-components@npm:2.30.6" + dependencies: + "@microsoft/fast-colors": ^5.3.0 + "@microsoft/fast-element": ^1.10.1 + "@microsoft/fast-foundation": ^2.46.2 + "@microsoft/fast-web-utilities": ^5.4.1 + tslib: ^1.13.0 + checksum: 1fbf3b7c265bcbf6abcae4d2f72430f7f871104a3d8344f16667a4cc7b123698cdf2bab8b760cbed92ef761c4db350a67f570665c76b132d6996990ac93cbd4f + languageName: node + linkType: hard + +"@microsoft/fast-element@npm:^1.10.1, @microsoft/fast-element@npm:^1.12.0": + version: 1.12.0 + resolution: "@microsoft/fast-element@npm:1.12.0" + checksum: bbff4e9c83106d1d74f3eeedc87bf84832429e78fee59c6a4ae8164ee4f42667503f586896bea72341b4d2c76c244a3cb0d4fd0d5d3732755f00357714dd609e + languageName: node + linkType: hard + +"@microsoft/fast-foundation@npm:^2.46.2, @microsoft/fast-foundation@npm:^2.49.0, @microsoft/fast-foundation@npm:^2.49.4": + version: 2.49.4 + resolution: "@microsoft/fast-foundation@npm:2.49.4" + dependencies: + "@microsoft/fast-element": ^1.12.0 + "@microsoft/fast-web-utilities": ^5.4.1 + tabbable: ^5.2.0 + tslib: ^1.13.0 + checksum: e979cd500aaba28090e8d9cdc6192933db01803c13288c11aded89aa54da6f0a70256ff2f249754b1c95d9abad369a18401e1df98d672e2823b83cf4cd88ad55 + languageName: node + linkType: hard + +"@microsoft/fast-react-wrapper@npm:^0.3.18": + version: 0.3.22 + resolution: "@microsoft/fast-react-wrapper@npm:0.3.22" + dependencies: + "@microsoft/fast-element": ^1.12.0 + "@microsoft/fast-foundation": ^2.49.4 + peerDependencies: + react: ">=16.9.0" + checksum: 6c7c0992dbaf91b32bc53b9d7ac21c7c8a89e6f45cc1b015cea1d1f3e766184ac7cea159479e34ddd30c347291cd5939e8d55696712086187deae37687054328 + languageName: node + linkType: hard + +"@microsoft/fast-web-utilities@npm:^5.4.1": + version: 5.4.1 + resolution: "@microsoft/fast-web-utilities@npm:5.4.1" + dependencies: + exenv-es6: ^1.1.1 + checksum: 303e87847f962944f474e3716c3eb305668243916ca9e0719e26bb9a32346144bc958d915c103776b3e552cea0f0f6233f839fad66adfdf96a8436b947288ca7 + languageName: node + linkType: hard + +"@microsoft/fast-web-utilities@npm:^6.0.0": + version: 6.0.0 + resolution: "@microsoft/fast-web-utilities@npm:6.0.0" + dependencies: + exenv-es6: ^1.1.1 + checksum: b4b906dbbf626212446d5952c160b1f7e7ce72dd33087c7ed634cb2745c31767bab7d17fba0e9fc32e42984fc5bc0a9929b4f05cbbcbe52869abe3666b5bfa39 + languageName: node + linkType: hard + "@nodelib/fs.scandir@npm:2.1.5": version: 2.1.5 resolution: "@nodelib/fs.scandir@npm:2.1.5" @@ -5319,6 +5412,13 @@ __metadata: languageName: node linkType: hard +"exenv-es6@npm:^1.1.1": + version: 1.1.1 + resolution: "exenv-es6@npm:1.1.1" + checksum: 7f2aa12025e6f06c48dc286f380cf3183bb19c6017b36d91695034a3e5124a7235c4f8ff24ca2eb88ae801322f0f99605cedfcfd996a5fcbba7669320e2a448e + languageName: node + linkType: hard + "exit@npm:^0.1.2": version: 0.1.2 resolution: "exit@npm:0.1.2" @@ -7073,6 +7173,8 @@ __metadata: version: 0.0.0-use.local resolution: "jupyterlab-unfold@workspace:." dependencies: + "@jupyter/react-components": ^0.13.3 + "@jupyter/web-components": ^0.13.2 "@jupyterlab/application": ^4.0.5 "@jupyterlab/builder": ^4.0.0 "@jupyterlab/docmanager": ^4.0.5 @@ -8443,7 +8545,7 @@ __metadata: languageName: node linkType: hard -"react@npm:^18.2.0": +"react@npm:>=17.0.0 <19.0.0, react@npm:^18.2.0": version: 18.2.0 resolution: "react@npm:18.2.0" dependencies: @@ -9491,6 +9593,13 @@ __metadata: languageName: node linkType: hard +"tabbable@npm:^5.2.0": + version: 5.3.3 + resolution: "tabbable@npm:5.3.3" + checksum: 1aa56e1bb617cc10616c407f4e756f0607f3e2d30f9803664d70b85db037ca27e75918ed1c71443f3dc902e21dc9f991ce4b52d63a538c9b69b3218d3babcd70 + languageName: node + linkType: hard + "table@npm:^6.8.1": version: 6.8.1 resolution: "table@npm:6.8.1" @@ -9702,6 +9811,13 @@ __metadata: languageName: node linkType: hard +"tslib@npm:^1.13.0": + version: 1.14.1 + resolution: "tslib@npm:1.14.1" + checksum: dbe628ef87f66691d5d2959b3e41b9ca0045c3ee3c7c7b906cc1e328b39f199bb1ad9e671c39025bd56122ac57dfbf7385a94843b1cc07c60a4db74795829acd + languageName: node + linkType: hard + "tslib@npm:^2.5.0, tslib@npm:^2.6.0": version: 2.6.2 resolution: "tslib@npm:2.6.2" From c4a0a0150a652bc502153270c1bf82664021770d Mon Sep 17 00:00:00 2001 From: HaudinFlorence Date: Tue, 5 Dec 2023 10:57:11 +0100 Subject: [PATCH 2/2] Implement the OpenDrivesDialog command to open a dialog when clicking on the drive button in the filebrowser toolbar. Add the necessary components to place inside the dialog to be able to pick drive by name or url and display the selected drives list in a table. --- binder/remoteEntry.5fdec407f62437865063.js | 578 +++++++++++++++++++++ src/drivelistmanager.tsx | 303 +++++++++++ src/index.ts | 83 ++- style/base.css | 66 +++ 4 files changed, 1026 insertions(+), 4 deletions(-) create mode 100644 binder/remoteEntry.5fdec407f62437865063.js create mode 100644 src/drivelistmanager.tsx diff --git a/binder/remoteEntry.5fdec407f62437865063.js b/binder/remoteEntry.5fdec407f62437865063.js new file mode 100644 index 0000000..815912c --- /dev/null +++ b/binder/remoteEntry.5fdec407f62437865063.js @@ -0,0 +1,578 @@ +var _JUPYTERLAB; +/******/ (() => { // webpackBootstrap +/******/ "use strict"; +/******/ var __webpack_modules__ = ({ + +/***/ "webpack/container/entry/jupyterlab-unfold": +/*!***********************!*\ + !*** container entry ***! + \***********************/ +/***/ ((__unused_webpack_module, exports, __webpack_require__) => { + +var moduleMap = { + "./index": () => { + return __webpack_require__.e("lib_index_js").then(() => (() => ((__webpack_require__(/*! ./lib/index.js */ "./lib/index.js"))))); + }, + "./extension": () => { + return __webpack_require__.e("lib_index_js").then(() => (() => ((__webpack_require__(/*! ./lib/index.js */ "./lib/index.js"))))); + }, + "./style": () => { + return __webpack_require__.e("style_index_js").then(() => (() => ((__webpack_require__(/*! ./style/index.js */ "./style/index.js"))))); + } +}; +var get = (module, getScope) => { + __webpack_require__.R = getScope; + getScope = ( + __webpack_require__.o(moduleMap, module) + ? moduleMap[module]() + : Promise.resolve().then(() => { + throw new Error('Module "' + module + '" does not exist in container.'); + }) + ); + __webpack_require__.R = undefined; + return getScope; +}; +var init = (shareScope, initScope) => { + if (!__webpack_require__.S) return; + var name = "default" + var oldScope = __webpack_require__.S[name]; + if(oldScope && oldScope !== shareScope) throw new Error("Container initialization failed as it has already been initialized with a different share scope"); + __webpack_require__.S[name] = shareScope; + return __webpack_require__.I(name, initScope); +}; + +// This exports getters to disallow modifications +__webpack_require__.d(exports, { + get: () => (get), + init: () => (init) +}); + +/***/ }) + +/******/ }); +/************************************************************************/ +/******/ // The module cache +/******/ var __webpack_module_cache__ = {}; +/******/ +/******/ // The require function +/******/ function __webpack_require__(moduleId) { +/******/ // Check if module is in cache +/******/ var cachedModule = __webpack_module_cache__[moduleId]; +/******/ if (cachedModule !== undefined) { +/******/ return cachedModule.exports; +/******/ } +/******/ // Create a new module (and put it into the cache) +/******/ var module = __webpack_module_cache__[moduleId] = { +/******/ id: moduleId, +/******/ // no module.loaded needed +/******/ exports: {} +/******/ }; +/******/ +/******/ // Execute the module function +/******/ __webpack_modules__[moduleId](module, module.exports, __webpack_require__); +/******/ +/******/ // Return the exports of the module +/******/ return module.exports; +/******/ } +/******/ +/******/ // expose the modules object (__webpack_modules__) +/******/ __webpack_require__.m = __webpack_modules__; +/******/ +/******/ // expose the module cache +/******/ __webpack_require__.c = __webpack_module_cache__; +/******/ +/************************************************************************/ +/******/ /* webpack/runtime/compat get default export */ +/******/ (() => { +/******/ // getDefaultExport function for compatibility with non-harmony modules +/******/ __webpack_require__.n = (module) => { +/******/ var getter = module && module.__esModule ? +/******/ () => (module['default']) : +/******/ () => (module); +/******/ __webpack_require__.d(getter, { a: getter }); +/******/ return getter; +/******/ }; +/******/ })(); +/******/ +/******/ /* webpack/runtime/define property getters */ +/******/ (() => { +/******/ // define getter functions for harmony exports +/******/ __webpack_require__.d = (exports, definition) => { +/******/ for(var key in definition) { +/******/ if(__webpack_require__.o(definition, key) && !__webpack_require__.o(exports, key)) { +/******/ Object.defineProperty(exports, key, { enumerable: true, get: definition[key] }); +/******/ } +/******/ } +/******/ }; +/******/ })(); +/******/ +/******/ /* webpack/runtime/ensure chunk */ +/******/ (() => { +/******/ __webpack_require__.f = {}; +/******/ // This file contains only the entry chunk. +/******/ // The chunk loading function for additional chunks +/******/ __webpack_require__.e = (chunkId) => { +/******/ return Promise.all(Object.keys(__webpack_require__.f).reduce((promises, key) => { +/******/ __webpack_require__.f[key](chunkId, promises); +/******/ return promises; +/******/ }, [])); +/******/ }; +/******/ })(); +/******/ +/******/ /* webpack/runtime/get javascript chunk filename */ +/******/ (() => { +/******/ // This function allow to reference async chunks +/******/ __webpack_require__.u = (chunkId) => { +/******/ // return url for filenames based on template +/******/ return "" + chunkId + "." + {"lib_index_js":"91b1410ac145136c59be","style_index_js":"e4cc79dc23e0d27e064f"}[chunkId] + ".js"; +/******/ }; +/******/ })(); +/******/ +/******/ /* webpack/runtime/global */ +/******/ (() => { +/******/ __webpack_require__.g = (function() { +/******/ if (typeof globalThis === 'object') return globalThis; +/******/ try { +/******/ return this || new Function('return this')(); +/******/ } catch (e) { +/******/ if (typeof window === 'object') return window; +/******/ } +/******/ })(); +/******/ })(); +/******/ +/******/ /* webpack/runtime/hasOwnProperty shorthand */ +/******/ (() => { +/******/ __webpack_require__.o = (obj, prop) => (Object.prototype.hasOwnProperty.call(obj, prop)) +/******/ })(); +/******/ +/******/ /* webpack/runtime/load script */ +/******/ (() => { +/******/ var inProgress = {}; +/******/ var dataWebpackPrefix = "jupyterlab-unfold:"; +/******/ // loadScript function to load a script via script tag +/******/ __webpack_require__.l = (url, done, key, chunkId) => { +/******/ if(inProgress[url]) { inProgress[url].push(done); return; } +/******/ var script, needAttach; +/******/ if(key !== undefined) { +/******/ var scripts = document.getElementsByTagName("script"); +/******/ for(var i = 0; i < scripts.length; i++) { +/******/ var s = scripts[i]; +/******/ if(s.getAttribute("src") == url || s.getAttribute("data-webpack") == dataWebpackPrefix + key) { script = s; break; } +/******/ } +/******/ } +/******/ if(!script) { +/******/ needAttach = true; +/******/ script = document.createElement('script'); +/******/ +/******/ script.charset = 'utf-8'; +/******/ script.timeout = 120; +/******/ if (__webpack_require__.nc) { +/******/ script.setAttribute("nonce", __webpack_require__.nc); +/******/ } +/******/ script.setAttribute("data-webpack", dataWebpackPrefix + key); +/******/ +/******/ script.src = url; +/******/ } +/******/ inProgress[url] = [done]; +/******/ var onScriptComplete = (prev, event) => { +/******/ // avoid mem leaks in IE. +/******/ script.onerror = script.onload = null; +/******/ clearTimeout(timeout); +/******/ var doneFns = inProgress[url]; +/******/ delete inProgress[url]; +/******/ script.parentNode && script.parentNode.removeChild(script); +/******/ doneFns && doneFns.forEach((fn) => (fn(event))); +/******/ if(prev) return prev(event); +/******/ } +/******/ var timeout = setTimeout(onScriptComplete.bind(null, undefined, { type: 'timeout', target: script }), 120000); +/******/ script.onerror = onScriptComplete.bind(null, script.onerror); +/******/ script.onload = onScriptComplete.bind(null, script.onload); +/******/ needAttach && document.head.appendChild(script); +/******/ }; +/******/ })(); +/******/ +/******/ /* webpack/runtime/make namespace object */ +/******/ (() => { +/******/ // define __esModule on exports +/******/ __webpack_require__.r = (exports) => { +/******/ if(typeof Symbol !== 'undefined' && Symbol.toStringTag) { +/******/ Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' }); +/******/ } +/******/ Object.defineProperty(exports, '__esModule', { value: true }); +/******/ }; +/******/ })(); +/******/ +/******/ /* webpack/runtime/sharing */ +/******/ (() => { +/******/ __webpack_require__.S = {}; +/******/ var initPromises = {}; +/******/ var initTokens = {}; +/******/ __webpack_require__.I = (name, initScope) => { +/******/ if(!initScope) initScope = []; +/******/ // handling circular init calls +/******/ var initToken = initTokens[name]; +/******/ if(!initToken) initToken = initTokens[name] = {}; +/******/ if(initScope.indexOf(initToken) >= 0) return; +/******/ initScope.push(initToken); +/******/ // only runs once +/******/ if(initPromises[name]) return initPromises[name]; +/******/ // creates a new share scope if needed +/******/ if(!__webpack_require__.o(__webpack_require__.S, name)) __webpack_require__.S[name] = {}; +/******/ // runs all init snippets from all modules reachable +/******/ var scope = __webpack_require__.S[name]; +/******/ var warn = (msg) => { +/******/ if (typeof console !== "undefined" && console.warn) console.warn(msg); +/******/ }; +/******/ var uniqueName = "jupyterlab-unfold"; +/******/ var register = (name, version, factory, eager) => { +/******/ var versions = scope[name] = scope[name] || {}; +/******/ var activeVersion = versions[version]; +/******/ if(!activeVersion || (!activeVersion.loaded && (!eager != !activeVersion.eager ? eager : uniqueName > activeVersion.from))) versions[version] = { get: factory, from: uniqueName, eager: !!eager }; +/******/ }; +/******/ var initExternal = (id) => { +/******/ var handleError = (err) => (warn("Initialization of sharing external failed: " + err)); +/******/ try { +/******/ var module = __webpack_require__(id); +/******/ if(!module) return; +/******/ var initFn = (module) => (module && module.init && module.init(__webpack_require__.S[name], initScope)) +/******/ if(module.then) return promises.push(module.then(initFn, handleError)); +/******/ var initResult = initFn(module); +/******/ if(initResult && initResult.then) return promises.push(initResult['catch'](handleError)); +/******/ } catch(err) { handleError(err); } +/******/ } +/******/ var promises = []; +/******/ switch(name) { +/******/ case "default": { +/******/ register("jupyterlab-unfold", "0.3.0", () => (__webpack_require__.e("lib_index_js").then(() => (() => (__webpack_require__(/*! ./lib/index.js */ "./lib/index.js")))))); +/******/ } +/******/ break; +/******/ } +/******/ if(!promises.length) return initPromises[name] = 1; +/******/ return initPromises[name] = Promise.all(promises).then(() => (initPromises[name] = 1)); +/******/ }; +/******/ })(); +/******/ +/******/ /* webpack/runtime/publicPath */ +/******/ (() => { +/******/ var scriptUrl; +/******/ if (__webpack_require__.g.importScripts) scriptUrl = __webpack_require__.g.location + ""; +/******/ var document = __webpack_require__.g.document; +/******/ if (!scriptUrl && document) { +/******/ if (document.currentScript) +/******/ scriptUrl = document.currentScript.src; +/******/ if (!scriptUrl) { +/******/ var scripts = document.getElementsByTagName("script"); +/******/ if(scripts.length) { +/******/ var i = scripts.length - 1; +/******/ while (i > -1 && !scriptUrl) scriptUrl = scripts[i--].src; +/******/ } +/******/ } +/******/ } +/******/ // When supporting browsers where an automatic publicPath is not supported you must specify an output.publicPath manually via configuration +/******/ // or pass an empty string ("") and set the __webpack_public_path__ variable from your code to use your own logic. +/******/ if (!scriptUrl) throw new Error("Automatic publicPath is not supported in this browser"); +/******/ scriptUrl = scriptUrl.replace(/#.*$/, "").replace(/\?.*$/, "").replace(/\/[^\/]+$/, "/"); +/******/ __webpack_require__.p = scriptUrl; +/******/ })(); +/******/ +/******/ /* webpack/runtime/consumes */ +/******/ (() => { +/******/ var parseVersion = (str) => { +/******/ // see webpack/lib/util/semver.js for original code +/******/ var p=p=>{return p.split(".").map((p=>{return+p==p?+p:p}))},n=/^([^-+]+)?(?:-([^+]+))?(?:\+(.+))?$/.exec(str),r=n[1]?p(n[1]):[];return n[2]&&(r.length++,r.push.apply(r,p(n[2]))),n[3]&&(r.push([]),r.push.apply(r,p(n[3]))),r; +/******/ } +/******/ var versionLt = (a, b) => { +/******/ // see webpack/lib/util/semver.js for original code +/******/ a=parseVersion(a),b=parseVersion(b);for(var r=0;;){if(r>=a.length)return r=b.length)return"u"==n;var t=b[r],f=(typeof t)[0];if(n!=f)return"o"==n&&"n"==f||("s"==f||"u"==n);if("o"!=n&&"u"!=n&&e!=t)return e { +/******/ // see webpack/lib/util/semver.js for original code +/******/ var r=range[0],n="";if(1===range.length)return"*";if(r+.5){n+=0==r?">=":-1==r?"<":1==r?"^":2==r?"~":r>0?"=":"!=";for(var e=1,a=1;a0?".":"")+(e=2,t)}return n}var g=[];for(a=1;a { +/******/ // see webpack/lib/util/semver.js for original code +/******/ if(0 in range){version=parseVersion(version);var e=range[0],r=e<0;r&&(e=-e-1);for(var n=0,i=1,a=!0;;i++,n++){var f,s,g=i=version.length||"o"==(s=(typeof(f=version[n]))[0]))return!a||("u"==g?i>e&&!r:""==g!=r);if("u"==s){if(!a||"u"!=g)return!1}else if(a)if(g==s)if(i<=e){if(f!=range[i])return!1}else{if(r?f>range[i]:f { +/******/ var scope = __webpack_require__.S[scopeName]; +/******/ if(!scope || !__webpack_require__.o(scope, key)) throw new Error("Shared module " + key + " doesn't exist in shared scope " + scopeName); +/******/ return scope; +/******/ }; +/******/ var findVersion = (scope, key) => { +/******/ var versions = scope[key]; +/******/ var key = Object.keys(versions).reduce((a, b) => { +/******/ return !a || versionLt(a, b) ? b : a; +/******/ }, 0); +/******/ return key && versions[key] +/******/ }; +/******/ var findSingletonVersionKey = (scope, key) => { +/******/ var versions = scope[key]; +/******/ return Object.keys(versions).reduce((a, b) => { +/******/ return !a || (!versions[a].loaded && versionLt(a, b)) ? b : a; +/******/ }, 0); +/******/ }; +/******/ var getInvalidSingletonVersionMessage = (scope, key, version, requiredVersion) => { +/******/ return "Unsatisfied version " + version + " from " + (version && scope[key][version].from) + " of shared singleton module " + key + " (required " + rangeToString(requiredVersion) + ")" +/******/ }; +/******/ var getSingleton = (scope, scopeName, key, requiredVersion) => { +/******/ var version = findSingletonVersionKey(scope, key); +/******/ return get(scope[key][version]); +/******/ }; +/******/ var getSingletonVersion = (scope, scopeName, key, requiredVersion) => { +/******/ var version = findSingletonVersionKey(scope, key); +/******/ if (!satisfy(requiredVersion, version)) warn(getInvalidSingletonVersionMessage(scope, key, version, requiredVersion)); +/******/ return get(scope[key][version]); +/******/ }; +/******/ var getStrictSingletonVersion = (scope, scopeName, key, requiredVersion) => { +/******/ var version = findSingletonVersionKey(scope, key); +/******/ if (!satisfy(requiredVersion, version)) throw new Error(getInvalidSingletonVersionMessage(scope, key, version, requiredVersion)); +/******/ return get(scope[key][version]); +/******/ }; +/******/ var findValidVersion = (scope, key, requiredVersion) => { +/******/ var versions = scope[key]; +/******/ var key = Object.keys(versions).reduce((a, b) => { +/******/ if (!satisfy(requiredVersion, b)) return a; +/******/ return !a || versionLt(a, b) ? b : a; +/******/ }, 0); +/******/ return key && versions[key] +/******/ }; +/******/ var getInvalidVersionMessage = (scope, scopeName, key, requiredVersion) => { +/******/ var versions = scope[key]; +/******/ return "No satisfying version (" + rangeToString(requiredVersion) + ") of shared module " + key + " found in shared scope " + scopeName + ".\n" + +/******/ "Available versions: " + Object.keys(versions).map((key) => { +/******/ return key + " from " + versions[key].from; +/******/ }).join(", "); +/******/ }; +/******/ var getValidVersion = (scope, scopeName, key, requiredVersion) => { +/******/ var entry = findValidVersion(scope, key, requiredVersion); +/******/ if(entry) return get(entry); +/******/ throw new Error(getInvalidVersionMessage(scope, scopeName, key, requiredVersion)); +/******/ }; +/******/ var warn = (msg) => { +/******/ if (typeof console !== "undefined" && console.warn) console.warn(msg); +/******/ }; +/******/ var warnInvalidVersion = (scope, scopeName, key, requiredVersion) => { +/******/ warn(getInvalidVersionMessage(scope, scopeName, key, requiredVersion)); +/******/ }; +/******/ var get = (entry) => { +/******/ entry.loaded = 1; +/******/ return entry.get() +/******/ }; +/******/ var init = (fn) => (function(scopeName, a, b, c) { +/******/ var promise = __webpack_require__.I(scopeName); +/******/ if (promise && promise.then) return promise.then(fn.bind(fn, scopeName, __webpack_require__.S[scopeName], a, b, c)); +/******/ return fn(scopeName, __webpack_require__.S[scopeName], a, b, c); +/******/ }); +/******/ +/******/ var load = /*#__PURE__*/ init((scopeName, scope, key) => { +/******/ ensureExistence(scopeName, key); +/******/ return get(findVersion(scope, key)); +/******/ }); +/******/ var loadFallback = /*#__PURE__*/ init((scopeName, scope, key, fallback) => { +/******/ return scope && __webpack_require__.o(scope, key) ? get(findVersion(scope, key)) : fallback(); +/******/ }); +/******/ var loadVersionCheck = /*#__PURE__*/ init((scopeName, scope, key, version) => { +/******/ ensureExistence(scopeName, key); +/******/ return get(findValidVersion(scope, key, version) || warnInvalidVersion(scope, scopeName, key, version) || findVersion(scope, key)); +/******/ }); +/******/ var loadSingleton = /*#__PURE__*/ init((scopeName, scope, key) => { +/******/ ensureExistence(scopeName, key); +/******/ return getSingleton(scope, scopeName, key); +/******/ }); +/******/ var loadSingletonVersionCheck = /*#__PURE__*/ init((scopeName, scope, key, version) => { +/******/ ensureExistence(scopeName, key); +/******/ return getSingletonVersion(scope, scopeName, key, version); +/******/ }); +/******/ var loadStrictVersionCheck = /*#__PURE__*/ init((scopeName, scope, key, version) => { +/******/ ensureExistence(scopeName, key); +/******/ return getValidVersion(scope, scopeName, key, version); +/******/ }); +/******/ var loadStrictSingletonVersionCheck = /*#__PURE__*/ init((scopeName, scope, key, version) => { +/******/ ensureExistence(scopeName, key); +/******/ return getStrictSingletonVersion(scope, scopeName, key, version); +/******/ }); +/******/ var loadVersionCheckFallback = /*#__PURE__*/ init((scopeName, scope, key, version, fallback) => { +/******/ if(!scope || !__webpack_require__.o(scope, key)) return fallback(); +/******/ return get(findValidVersion(scope, key, version) || warnInvalidVersion(scope, scopeName, key, version) || findVersion(scope, key)); +/******/ }); +/******/ var loadSingletonFallback = /*#__PURE__*/ init((scopeName, scope, key, fallback) => { +/******/ if(!scope || !__webpack_require__.o(scope, key)) return fallback(); +/******/ return getSingleton(scope, scopeName, key); +/******/ }); +/******/ var loadSingletonVersionCheckFallback = /*#__PURE__*/ init((scopeName, scope, key, version, fallback) => { +/******/ if(!scope || !__webpack_require__.o(scope, key)) return fallback(); +/******/ return getSingletonVersion(scope, scopeName, key, version); +/******/ }); +/******/ var loadStrictVersionCheckFallback = /*#__PURE__*/ init((scopeName, scope, key, version, fallback) => { +/******/ var entry = scope && __webpack_require__.o(scope, key) && findValidVersion(scope, key, version); +/******/ return entry ? get(entry) : fallback(); +/******/ }); +/******/ var loadStrictSingletonVersionCheckFallback = /*#__PURE__*/ init((scopeName, scope, key, version, fallback) => { +/******/ if(!scope || !__webpack_require__.o(scope, key)) return fallback(); +/******/ return getStrictSingletonVersion(scope, scopeName, key, version); +/******/ }); +/******/ var installedModules = {}; +/******/ var moduleToHandlerMapping = { +/******/ "webpack/sharing/consume/default/@jupyterlab/filebrowser": () => (loadSingletonVersionCheck("default", "@jupyterlab/filebrowser", [1,4,0,9])), +/******/ "webpack/sharing/consume/default/@jupyterlab/docmanager": () => (loadSingletonVersionCheck("default", "@jupyterlab/docmanager", [1,4,0,9])), +/******/ "webpack/sharing/consume/default/@jupyterlab/apputils": () => (loadSingletonVersionCheck("default", "@jupyterlab/apputils", [1,4,1,9])), +/******/ "webpack/sharing/consume/default/@jupyterlab/settingregistry": () => (loadSingletonVersionCheck("default", "@jupyterlab/settingregistry", [1,4,0,9])), +/******/ "webpack/sharing/consume/default/@jupyterlab/translation": () => (loadSingletonVersionCheck("default", "@jupyterlab/translation", [1,4,0,9])), +/******/ "webpack/sharing/consume/default/@jupyterlab/statedb": () => (loadSingletonVersionCheck("default", "@jupyterlab/statedb", [1,4,0,9])), +/******/ "webpack/sharing/consume/default/@lumino/algorithm": () => (loadSingletonVersionCheck("default", "@lumino/algorithm", [1,2,0,0])), +/******/ "webpack/sharing/consume/default/@lumino/domutils": () => (loadSingletonVersionCheck("default", "@lumino/domutils", [1,2,0,0])), +/******/ "webpack/sharing/consume/default/@lumino/coreutils": () => (loadSingletonVersionCheck("default", "@lumino/coreutils", [1,2,0,0])), +/******/ "webpack/sharing/consume/default/@jupyterlab/coreutils": () => (loadSingletonVersionCheck("default", "@jupyterlab/coreutils", [1,6,0,9])), +/******/ "webpack/sharing/consume/default/@jupyterlab/ui-components": () => (loadSingletonVersionCheck("default", "@jupyterlab/ui-components", [1,4,0,9])) +/******/ }; +/******/ // no consumes in initial chunks +/******/ var chunkMapping = { +/******/ "lib_index_js": [ +/******/ "webpack/sharing/consume/default/@jupyterlab/filebrowser", +/******/ "webpack/sharing/consume/default/@jupyterlab/docmanager", +/******/ "webpack/sharing/consume/default/@jupyterlab/apputils", +/******/ "webpack/sharing/consume/default/@jupyterlab/settingregistry", +/******/ "webpack/sharing/consume/default/@jupyterlab/translation", +/******/ "webpack/sharing/consume/default/@jupyterlab/statedb", +/******/ "webpack/sharing/consume/default/@lumino/algorithm", +/******/ "webpack/sharing/consume/default/@lumino/domutils", +/******/ "webpack/sharing/consume/default/@lumino/coreutils", +/******/ "webpack/sharing/consume/default/@jupyterlab/coreutils", +/******/ "webpack/sharing/consume/default/@jupyterlab/ui-components" +/******/ ] +/******/ }; +/******/ __webpack_require__.f.consumes = (chunkId, promises) => { +/******/ if(__webpack_require__.o(chunkMapping, chunkId)) { +/******/ chunkMapping[chunkId].forEach((id) => { +/******/ if(__webpack_require__.o(installedModules, id)) return promises.push(installedModules[id]); +/******/ var onFactory = (factory) => { +/******/ installedModules[id] = 0; +/******/ __webpack_require__.m[id] = (module) => { +/******/ delete __webpack_require__.c[id]; +/******/ module.exports = factory(); +/******/ } +/******/ }; +/******/ var onError = (error) => { +/******/ delete installedModules[id]; +/******/ __webpack_require__.m[id] = (module) => { +/******/ delete __webpack_require__.c[id]; +/******/ throw error; +/******/ } +/******/ }; +/******/ try { +/******/ var promise = moduleToHandlerMapping[id](); +/******/ if(promise.then) { +/******/ promises.push(installedModules[id] = promise.then(onFactory)['catch'](onError)); +/******/ } else onFactory(promise); +/******/ } catch(e) { onError(e); } +/******/ }); +/******/ } +/******/ } +/******/ })(); +/******/ +/******/ /* webpack/runtime/jsonp chunk loading */ +/******/ (() => { +/******/ // no baseURI +/******/ +/******/ // object to store loaded and loading chunks +/******/ // undefined = chunk not loaded, null = chunk preloaded/prefetched +/******/ // [resolve, reject, Promise] = chunk loading, 0 = chunk loaded +/******/ var installedChunks = { +/******/ "jupyterlab-unfold": 0 +/******/ }; +/******/ +/******/ __webpack_require__.f.j = (chunkId, promises) => { +/******/ // JSONP chunk loading for javascript +/******/ var installedChunkData = __webpack_require__.o(installedChunks, chunkId) ? installedChunks[chunkId] : undefined; +/******/ if(installedChunkData !== 0) { // 0 means "already installed". +/******/ +/******/ // a Promise means "currently loading". +/******/ if(installedChunkData) { +/******/ promises.push(installedChunkData[2]); +/******/ } else { +/******/ if(true) { // all chunks have JS +/******/ // setup Promise in chunk cache +/******/ var promise = new Promise((resolve, reject) => (installedChunkData = installedChunks[chunkId] = [resolve, reject])); +/******/ promises.push(installedChunkData[2] = promise); +/******/ +/******/ // start chunk loading +/******/ var url = __webpack_require__.p + __webpack_require__.u(chunkId); +/******/ // create error before stack unwound to get useful stacktrace later +/******/ var error = new Error(); +/******/ var loadingEnded = (event) => { +/******/ if(__webpack_require__.o(installedChunks, chunkId)) { +/******/ installedChunkData = installedChunks[chunkId]; +/******/ if(installedChunkData !== 0) installedChunks[chunkId] = undefined; +/******/ if(installedChunkData) { +/******/ var errorType = event && (event.type === 'load' ? 'missing' : event.type); +/******/ var realSrc = event && event.target && event.target.src; +/******/ error.message = 'Loading chunk ' + chunkId + ' failed.\n(' + errorType + ': ' + realSrc + ')'; +/******/ error.name = 'ChunkLoadError'; +/******/ error.type = errorType; +/******/ error.request = realSrc; +/******/ installedChunkData[1](error); +/******/ } +/******/ } +/******/ }; +/******/ __webpack_require__.l(url, loadingEnded, "chunk-" + chunkId, chunkId); +/******/ } +/******/ } +/******/ } +/******/ }; +/******/ +/******/ // no prefetching +/******/ +/******/ // no preloaded +/******/ +/******/ // no HMR +/******/ +/******/ // no HMR manifest +/******/ +/******/ // no on chunks loaded +/******/ +/******/ // install a JSONP callback for chunk loading +/******/ var webpackJsonpCallback = (parentChunkLoadingFunction, data) => { +/******/ var [chunkIds, moreModules, runtime] = data; +/******/ // add "moreModules" to the modules object, +/******/ // then flag all "chunkIds" as loaded and fire callback +/******/ var moduleId, chunkId, i = 0; +/******/ if(chunkIds.some((id) => (installedChunks[id] !== 0))) { +/******/ for(moduleId in moreModules) { +/******/ if(__webpack_require__.o(moreModules, moduleId)) { +/******/ __webpack_require__.m[moduleId] = moreModules[moduleId]; +/******/ } +/******/ } +/******/ if(runtime) var result = runtime(__webpack_require__); +/******/ } +/******/ if(parentChunkLoadingFunction) parentChunkLoadingFunction(data); +/******/ for(;i < chunkIds.length; i++) { +/******/ chunkId = chunkIds[i]; +/******/ if(__webpack_require__.o(installedChunks, chunkId) && installedChunks[chunkId]) { +/******/ installedChunks[chunkId][0](); +/******/ } +/******/ installedChunks[chunkId] = 0; +/******/ } +/******/ +/******/ } +/******/ +/******/ var chunkLoadingGlobal = self["webpackChunkjupyterlab_unfold"] = self["webpackChunkjupyterlab_unfold"] || []; +/******/ chunkLoadingGlobal.forEach(webpackJsonpCallback.bind(null, 0)); +/******/ chunkLoadingGlobal.push = webpackJsonpCallback.bind(null, chunkLoadingGlobal.push.bind(chunkLoadingGlobal)); +/******/ })(); +/******/ +/******/ /* webpack/runtime/nonce */ +/******/ (() => { +/******/ __webpack_require__.nc = undefined; +/******/ })(); +/******/ +/************************************************************************/ +/******/ +/******/ // module cache are used so entry inlining is disabled +/******/ // startup +/******/ // Load entry module and return exports +/******/ var __webpack_exports__ = __webpack_require__("webpack/container/entry/jupyterlab-unfold"); +/******/ (_JUPYTERLAB = typeof _JUPYTERLAB === "undefined" ? {} : _JUPYTERLAB)["jupyterlab-unfold"] = __webpack_exports__; +/******/ +/******/ })() +; +//# sourceMappingURL=remoteEntry.5fdec407f62437865063.js.map \ No newline at end of file diff --git a/src/drivelistmanager.tsx b/src/drivelistmanager.tsx new file mode 100644 index 0000000..16560ea --- /dev/null +++ b/src/drivelistmanager.tsx @@ -0,0 +1,303 @@ +import * as React from 'react'; +//import { requestAPI } from './handler'; +import { VDomModel, VDomRenderer } from '@jupyterlab/ui-components'; +import { + Button, + DataGrid, + DataGridCell, + DataGridRow, + Search +} from '@jupyter/react-components'; +import { useState } from 'react'; +import { Drive } from './contents'; +import { DocumentRegistry } from '@jupyterlab/docregistry'; + +interface IProps { + model: DriveListModel; + docRegistry: DocumentRegistry; +} +export interface IDriveInputProps { + isName: boolean; + value: string; + getValue: (event: any) => void; + updateSelectedDrives: (item: string, isName: boolean) => void; +} +export function DriveInputComponent(props: IDriveInputProps) { + return ( +
+
+
+ +
+
+ +
+
+ ); +} + +interface ISearchListProps { + isName: boolean; + value: string; + filteredList: Array; + filter: (value: any) => void; + updateSelectedDrives: (item: string, isName: boolean) => void; +} + +export function DriveSearchListComponent(props: ISearchListProps) { + return ( +
+
+
+ +
+
+
+ {props.filteredList.map((item, index) => ( +
  • +
    +
    +
    {item}
    +
    +
    + +
    +
    +
  • + ))} +
    + ); +} +interface IDriveDataGridProps { + drives: Drive[]; +} + +export function DriveDataGridComponent(props: IDriveDataGridProps) { + return ( +
    + + + + name + + + url + + + + {props.drives.map((item, index) => ( + + + {item.name} + + + {item.baseUrl} + + + ))} + +
    + ); +} + +export function DriveListManagerComponent(props: IProps) { + const [driveUrl, setDriveUrl] = useState(''); + const [driveName, setDriveName] = useState(''); + let updatedSelectedDrives = [...props.model.selectedDrives]; + const [selectedDrives, setSelectedDrives] = useState(updatedSelectedDrives); + + const nameList: Array = []; + + for (const item of props.model.availableDrives) { + if (item.name !== '') { + nameList.push(item.name); + } + } + const [nameFilteredList, setNameFilteredList] = useState(nameList); + + const isDriveAlreadySelected = (pickedDrive: Drive, driveList: Drive[]) => { + const isbyNameIncluded: boolean[] = []; + const isbyUrlIncluded: boolean[] = []; + let isIncluded: boolean = false; + driveList.forEach(item => { + if (pickedDrive.name !== '' && pickedDrive.name === item.name) { + isbyNameIncluded.push(true); + } else { + isbyNameIncluded.push(false); + } + if (pickedDrive.baseUrl !== '' && pickedDrive.baseUrl === item.baseUrl) { + isbyUrlIncluded.push(true); + } else { + isbyUrlIncluded.push(false); + } + }); + + if (isbyNameIncluded.includes(true) || isbyUrlIncluded.includes(true)) { + isIncluded = true; + } + + return isIncluded; + }; + + const updateSelectedDrives = (item: string, isName: boolean) => { + updatedSelectedDrives = [...props.model.selectedDrives]; + let pickedDrive = new Drive(props.docRegistry); + + props.model.availableDrives.forEach(drive => { + if (isName) { + if (item === drive.name) { + pickedDrive = drive; + } + } else { + if (item !== driveUrl) { + setDriveUrl(item); + } + pickedDrive.baseUrl = driveUrl; + } + }); + + const checkDrive = isDriveAlreadySelected( + pickedDrive, + updatedSelectedDrives + ); + if (checkDrive === false) { + updatedSelectedDrives.push(pickedDrive); + } else { + console.warn('The selected drive is already in the list'); + } + + setSelectedDrives(updatedSelectedDrives); + props.model.setSelectedDrives(updatedSelectedDrives); + props.model.stateChanged.emit(); + }; + + const getValue = (event: any) => { + setDriveUrl(event.target.value); + }; + + const filter = (event: any) => { + const query = event.target.value; + let updatedList: Array; + + updatedList = [...nameList]; + updatedList = updatedList.filter(item => { + return item.toLowerCase().indexOf(query.toLowerCase()) !== -1; + }); + setNameFilteredList(updatedList); + if (nameFilteredList.length === 1 && nameFilteredList[0] !== '') { + setDriveName(nameFilteredList[0]); + setDriveUrl(''); + } + }; + + return ( + <> +
    +
    +

    Select drive(s) to be added to your filebrowser

    +
    +
    +
    +
    Enter a drive URL
    + + updateSelectedDrives(value, isName) + } + /> + +
    Select drive(s) from list
    + + updateSelectedDrives(value, isName) + } + /> +
    + +
    +
    + + + +
    +
    +
    +
    + + ); +} + +export class DriveListModel extends VDomModel { + public availableDrives: Drive[]; + public selectedDrives: Drive[]; + + constructor(availableDrives: Drive[], selectedDrives: Drive[]) { + super(); + + this.availableDrives = availableDrives; + this.selectedDrives = selectedDrives; + } + setSelectedDrives(selectedDrives: Drive[]) { + this.selectedDrives = selectedDrives; + } + async sendConnectionRequest(selectedDrives: Drive[]): Promise { + console.log( + 'Sending a request to connect to drive ', + selectedDrives[selectedDrives.length - 1].name + ); + const response = true; + /*requestAPI('send_connectionRequest', { + method: 'POST' + }) + .then(data => { + console.log('data:', data); + return data; + }) + .catch(reason => { + console.error( + `The jupyter_drive server extension appears to be missing.\n${reason}` + ); + return; + });*/ + return response; + } +} + +export class DriveListView extends VDomRenderer { + constructor(model: DriveListModel, docRegistry: DocumentRegistry) { + super(model); + this.model = model; + this.docRegistry = docRegistry; + } + render() { + return ( + <> + + + ); + } + private docRegistry: DocumentRegistry; +} diff --git a/src/index.ts b/src/index.ts index d14ae72..3808f3d 100644 --- a/src/index.ts +++ b/src/index.ts @@ -27,11 +27,11 @@ import { DriveIcon } from './icons'; import { addJupyterLabThemeChangeListener } from '@jupyter/web-components'; -//import { Dialog, showDialog } from '@jupyterlab/apputils'; +import { Dialog, showDialog } from '@jupyterlab/apputils'; -//import { Drive } from './contents'; +import { Drive } from './contents'; -//import { DriveListModel, DriveListView } from './drivelistmanager'; +import { DriveListModel, DriveListView } from './drivelistmanager'; const SETTINGS_ID = 'jupyterlab-unfold:jupyterlab-unfold-settings'; @@ -130,6 +130,44 @@ export async function activateAddDrivesPlugin( const trans = translator.load('jupyterlab_unfold'); console.log('AddDrives plugin is activated!'); const { commands } = app; + const cocoDrive = new Drive(app.docRegistry); + cocoDrive.name = 'coconutDrive'; + cocoDrive.baseUrl = '/coconut/url'; + cocoDrive.region = ''; + cocoDrive.status = 'active'; + cocoDrive.provider = ''; + const peachDrive = new Drive(app.docRegistry); + peachDrive.baseUrl = '/peach/url'; + peachDrive.name = 'peachDrive'; + const mangoDrive = new Drive(app.docRegistry); + mangoDrive.baseUrl = '/mango/url'; + mangoDrive.name = 'mangoDrive'; + const kiwiDrive = new Drive(app.docRegistry); + kiwiDrive.baseUrl = '/kiwi/url'; + kiwiDrive.name = 'kiwiDrive'; + const pearDrive = new Drive(app.docRegistry); + pearDrive.baseUrl = '/pear/url'; + pearDrive.name = 'pearDrive'; + const customDrive = new Drive(app.docRegistry); + customDrive.baseUrl = '/customDrive/url'; + const tomatoDrive = new Drive(app.docRegistry); + tomatoDrive.baseUrl = '/tomato/url'; + tomatoDrive.name = 'tomatoDrive'; + const avocadoDrive = new Drive(app.docRegistry); + avocadoDrive.baseUrl = '/avocado/url'; + avocadoDrive.name = 'avocadoDrive'; + + const selectedList1: Drive[] = []; + const availableList1: Drive[] = [ + avocadoDrive, + cocoDrive, + customDrive, + kiwiDrive, + mangoDrive, + peachDrive, + pearDrive, + tomatoDrive + ]; const model = new FilterFileTreeBrowserModel({ translator: translator, @@ -166,10 +204,47 @@ export async function activateAddDrivesPlugin( ); addJupyterLabThemeChangeListener(); + const selectedDrivesModelMap = new Map(); + let selectedDrives: Drive[] = selectedList1; + const availableDrives: Drive[] = availableList1; + let driveListModel = selectedDrivesModelMap.get(selectedDrives); + + function addDriveContentsToPanel() { + console.log('A drive is added to the Panel'); + } commands.addCommand(CommandIDs.openDrivesDialog, { execute: async args => { - console.log('You have clicked on D button'); + if (!driveListModel) { + driveListModel = new DriveListModel(availableDrives, selectedDrives); + selectedDrivesModelMap.set(selectedDrives, driveListModel); + } else { + selectedDrives = driveListModel.selectedDrives; + selectedDrivesModelMap.set(selectedDrives, driveListModel); + } + async function onDriveAdded(selectedDrives: Drive[]) { + if (driveListModel) { + const response = driveListModel.sendConnectionRequest(selectedDrives); + if ((await response) === true) { + addDriveContentsToPanel(); + } else { + console.warn('Connection with the drive was not possible'); + } + } + } + + if (driveListModel) { + showDialog({ + body: new DriveListView(driveListModel, app.docRegistry), + buttons: [Dialog.cancelButton()] + }); + } + + driveListModel.stateChanged.connect(async () => { + if (driveListModel) { + onDriveAdded(driveListModel.selectedDrives); + } + }); }, icon: DriveIcon.bindprops({ stylesheet: 'menuItem' }), diff --git a/style/base.css b/style/base.css index 6f4cdc0..fe101cc 100644 --- a/style/base.css +++ b/style/base.css @@ -2,6 +2,72 @@ --indent: 1em; } +li { + list-style-type: none; +} + +.column { + float: left; + width: 50%; +} + +/* Clear floats after the columns */ +.row::after { + content: ''; + display: table; + clear: both; +} + +.drive-search-list { + width: auto; +} + +.drive-list-manager { + width: 800px; + height: 800px; +} + +.search-add-drive-button { + background-color: var(--md-blue-700); + color: white; + + /* width: 8em; */ + height: 1em; + border-radius: 4px; +} + +.input-add-drive-button { + position: relative; + background-color: var(--jp-brand-color1); + text-align: center; + color: white; + height: calc( + (var(--base-height-multiplier) + var(--density)) * var(--design-unit) * 1px + ); + border-radius: 4px; +} + +.drive-search-input { + height: 14px; +} + +.drive-data-grid { + width: 400px; +} + +.data-grid-cell { + text-align: left; + height: 2em; + border-right: 2px; + border-left: 2px; + background-color: var(--jp-layout-color2); +} + +.jp-Dialog-body { + width: 800px; + height: 800px; +} + .jp-DirListing-item { flex-grow: 1; }