From 80e74ce4f54fb555f7899ff001274a42a853a207 Mon Sep 17 00:00:00 2001 From: ioedeveloper Date: Mon, 23 Nov 2020 12:32:03 +0100 Subject: [PATCH] File Explorer barebone --- apps/remix-ide/src/app/panels/file-panel.js | 14 +- libs/remix-ui/file-explorer/.eslintrc | 261 ++---------------- .../file-explorer/src/lib/file-explorer.css | 55 ++++ .../file-explorer/src/lib/file-explorer.tsx | 170 +++++++++++- .../file-explorer/src/lib/types/index.ts | 7 + 5 files changed, 254 insertions(+), 253 deletions(-) create mode 100644 libs/remix-ui/file-explorer/src/lib/types/index.ts diff --git a/apps/remix-ide/src/app/panels/file-panel.js b/apps/remix-ide/src/app/panels/file-panel.js index e210ca8ef0..9a5ad3b135 100644 --- a/apps/remix-ide/src/app/panels/file-panel.js +++ b/apps/remix-ide/src/app/panels/file-panel.js @@ -1,9 +1,12 @@ import { ViewPlugin } from '@remixproject/engine-web' import * as packageJson from '../../../../../package.json' +import React from 'react' // eslint-disable-line +import ReactDOM from 'react-dom' +import { FileExplorer } from '@remix-ui/file-explorer' var yo = require('yo-yo') var EventManager = require('../../lib/events') -var FileExplorer = require('../files/file-explorer') +// var FileExplorer = require('../files/file-explorer') var { RemixdHandle } = require('../files/remixd-handle.js') var { GitHandle } = require('../files/git-handle.js') var globalRegistry = require('../../global/registry') @@ -54,13 +57,18 @@ module.exports = class Filepanel extends ViewPlugin { } function createProvider (key, menuItems) { - return new FileExplorer(self._components.registry, self._deps.fileProviders[key], menuItems, self) + return } var fileExplorer = createProvider('browser', ['createNewFile', 'publishToGist', canUpload ? 'uploadFile' : '']) var fileSystemExplorer = createProvider('localhost') - self.remixdHandle = new RemixdHandle(fileSystemExplorer, self._deps.fileProviders.localhost, appManager) + // self.remixdHandle = new RemixdHandle(fileSystemExplorer, self._deps.fileProviders.localhost, appManager) self.gitHandle = new GitHandle() const explorers = yo` diff --git a/libs/remix-ui/file-explorer/.eslintrc b/libs/remix-ui/file-explorer/.eslintrc index 977f139a09..dae5c6feeb 100644 --- a/libs/remix-ui/file-explorer/.eslintrc +++ b/libs/remix-ui/file-explorer/.eslintrc @@ -1,248 +1,19 @@ { - "rules": { - "array-callback-return": "warn", - "dot-location": ["warn", "property"], - "eqeqeq": ["warn", "smart"], - "new-parens": "warn", - "no-caller": "warn", - "no-cond-assign": ["warn", "except-parens"], - "no-const-assign": "warn", - "no-control-regex": "warn", - "no-delete-var": "warn", - "no-dupe-args": "warn", - "no-dupe-keys": "warn", - "no-duplicate-case": "warn", - "no-empty-character-class": "warn", - "no-empty-pattern": "warn", - "no-eval": "warn", - "no-ex-assign": "warn", - "no-extend-native": "warn", - "no-extra-bind": "warn", - "no-extra-label": "warn", - "no-fallthrough": "warn", - "no-func-assign": "warn", - "no-implied-eval": "warn", - "no-invalid-regexp": "warn", - "no-iterator": "warn", - "no-label-var": "warn", - "no-labels": ["warn", { "allowLoop": true, "allowSwitch": false }], - "no-lone-blocks": "warn", - "no-loop-func": "warn", - "no-mixed-operators": [ - "warn", - { - "groups": [ - ["&", "|", "^", "~", "<<", ">>", ">>>"], - ["==", "!=", "===", "!==", ">", ">=", "<", "<="], - ["&&", "||"], - ["in", "instanceof"] - ], - "allowSamePrecedence": false - } - ], - "no-multi-str": "warn", - "no-native-reassign": "warn", - "no-negated-in-lhs": "warn", - "no-new-func": "warn", - "no-new-object": "warn", - "no-new-symbol": "warn", - "no-new-wrappers": "warn", - "no-obj-calls": "warn", - "no-octal": "warn", - "no-octal-escape": "warn", - "no-redeclare": "warn", - "no-regex-spaces": "warn", - "no-restricted-syntax": ["warn", "WithStatement"], - "no-script-url": "warn", - "no-self-assign": "warn", - "no-self-compare": "warn", - "no-sequences": "warn", - "no-shadow-restricted-names": "warn", - "no-sparse-arrays": "warn", - "no-template-curly-in-string": "warn", - "no-this-before-super": "warn", - "no-throw-literal": "warn", - "no-restricted-globals": [ - "error", - "addEventListener", - "blur", - "close", - "closed", - "confirm", - "defaultStatus", - "defaultstatus", - "event", - "external", - "find", - "focus", - "frameElement", - "frames", - "history", - "innerHeight", - "innerWidth", - "length", - "location", - "locationbar", - "menubar", - "moveBy", - "moveTo", - "name", - "onblur", - "onerror", - "onfocus", - "onload", - "onresize", - "onunload", - "open", - "opener", - "opera", - "outerHeight", - "outerWidth", - "pageXOffset", - "pageYOffset", - "parent", - "print", - "removeEventListener", - "resizeBy", - "resizeTo", - "screen", - "screenLeft", - "screenTop", - "screenX", - "screenY", - "scroll", - "scrollbars", - "scrollBy", - "scrollTo", - "scrollX", - "scrollY", - "self", - "status", - "statusbar", - "stop", - "toolbar", - "top" - ], - "no-unexpected-multiline": "warn", - "no-unreachable": "warn", - "no-unused-expressions": [ - "error", - { - "allowShortCircuit": true, - "allowTernary": true, - "allowTaggedTemplates": true - } - ], - "no-unused-labels": "warn", - "no-useless-computed-key": "warn", - "no-useless-concat": "warn", - "no-useless-escape": "warn", - "no-useless-rename": [ - "warn", - { - "ignoreDestructuring": false, - "ignoreImport": false, - "ignoreExport": false - } - ], - "no-with": "warn", - "no-whitespace-before-property": "warn", - "react-hooks/exhaustive-deps": "warn", - "require-yield": "warn", - "rest-spread-spacing": ["warn", "never"], - "strict": ["warn", "never"], - "unicode-bom": ["warn", "never"], - "use-isnan": "warn", - "valid-typeof": "warn", - "no-restricted-properties": [ - "error", - { - "object": "require", - "property": "ensure", - "message": "Please use import() instead. More info: https://facebook.github.io/create-react-app/docs/code-splitting" - }, - { - "object": "System", - "property": "import", - "message": "Please use import() instead. More info: https://facebook.github.io/create-react-app/docs/code-splitting" - } - ], - "getter-return": "warn", - "import/first": "error", - "import/no-amd": "error", - "import/no-webpack-loader-syntax": "error", - "react/forbid-foreign-prop-types": ["warn", { "allowInPropTypes": true }], - "react/jsx-no-comment-textnodes": "warn", - "react/jsx-no-duplicate-props": "warn", - "react/jsx-no-target-blank": "warn", - "react/jsx-no-undef": "error", - "react/jsx-pascal-case": ["warn", { "allowAllCaps": true, "ignore": [] }], - "react/jsx-uses-react": "warn", - "react/jsx-uses-vars": "warn", - "react/no-danger-with-children": "warn", - "react/no-direct-mutation-state": "warn", - "react/no-is-mounted": "warn", - "react/no-typos": "error", - "react/react-in-jsx-scope": "error", - "react/require-render-return": "error", - "react/style-prop-object": "warn", - "react/jsx-no-useless-fragment": "warn", - "jsx-a11y/accessible-emoji": "warn", - "jsx-a11y/alt-text": "warn", - "jsx-a11y/anchor-has-content": "warn", - "jsx-a11y/anchor-is-valid": [ - "warn", - { "aspects": ["noHref", "invalidHref"] } - ], - "jsx-a11y/aria-activedescendant-has-tabindex": "warn", - "jsx-a11y/aria-props": "warn", - "jsx-a11y/aria-proptypes": "warn", - "jsx-a11y/aria-role": "warn", - "jsx-a11y/aria-unsupported-elements": "warn", - "jsx-a11y/heading-has-content": "warn", - "jsx-a11y/iframe-has-title": "warn", - "jsx-a11y/img-redundant-alt": "warn", - "jsx-a11y/no-access-key": "warn", - "jsx-a11y/no-distracting-elements": "warn", - "jsx-a11y/no-redundant-roles": "warn", - "jsx-a11y/role-has-required-aria-props": "warn", - "jsx-a11y/role-supports-aria-props": "warn", - "jsx-a11y/scope": "warn", - "react-hooks/rules-of-hooks": "error", - "default-case": "off", - "no-dupe-class-members": "off", - "no-undef": "off", - "@typescript-eslint/consistent-type-assertions": "warn", - "no-array-constructor": "off", - "@typescript-eslint/no-array-constructor": "warn", - "@typescript-eslint/no-namespace": "error", - "no-use-before-define": "off", - "@typescript-eslint/no-use-before-define": [ - "warn", - { - "functions": false, - "classes": false, - "variables": false, - "typedefs": false - } - ], - "no-unused-vars": "off", - "@typescript-eslint/no-unused-vars": [ - "warn", - { "args": "none", "ignoreRestSiblings": true } - ], - "no-useless-constructor": "off", - "@typescript-eslint/no-useless-constructor": "warn" - }, "env": { - "browser": true, - "commonjs": true, - "es6": true, - "jest": true, - "node": true + "browser": true, + "es6": true + }, + "extends": "../../../.eslintrc", + "globals": { + "Atomics": "readonly", + "SharedArrayBuffer": "readonly" }, - "settings": { "react": { "version": "detect" } }, - "plugins": ["import", "jsx-a11y", "react", "react-hooks"], - "extends": ["../../../.eslintrc"], - "ignorePatterns": ["!**/*"] -} + "parserOptions": { + "ecmaVersion": 11, + "sourceType": "module" + }, + "rules": { + "no-unused-vars": "off", + "@typescript-eslint/no-unused-vars": "error" + } +} diff --git a/libs/remix-ui/file-explorer/src/lib/file-explorer.css b/libs/remix-ui/file-explorer/src/lib/file-explorer.css index e69de29bb2..360834289f 100644 --- a/libs/remix-ui/file-explorer/src/lib/file-explorer.css +++ b/libs/remix-ui/file-explorer/src/lib/file-explorer.css @@ -0,0 +1,55 @@ +.remixui_label { + margin-top : 4px; +} +.remixui_leaf { + overflow : hidden; + text-overflow : ellipsis; + width : 90%; + margin-bottom : 0px; +} +.remixui_fileexplorer { + box-sizing : border-box; +} +input[type="file"] { + display: none; +} +.remixui_folder, +.remixui_file { + font-size : 14px; + cursor : pointer; +} +.remixui_file { + padding : 4px; +} +.remixui_newFile { + padding-right : 10px; +} +.remixui_newFile i { + cursor : pointer; +} +.remixui_newFile:hover { + transform : scale(1.3); +} +.remixui_menu { + margin-left : 20px; +} +.remixui_items { + display : inline +} +.remixui_remove { + margin-left : auto; + padding-left : 5px; + padding-right : 5px; +} +.remixui_activeMode { + display : flex; + width : 100%; + margin-right : 10px; + padding-right : 19px; +} +.remixui_activeMode > div { + min-width : 10px; +} +ul { + padding : 0; +} \ No newline at end of file diff --git a/libs/remix-ui/file-explorer/src/lib/file-explorer.tsx b/libs/remix-ui/file-explorer/src/lib/file-explorer.tsx index 2582096189..5d5e31b9fa 100644 --- a/libs/remix-ui/file-explorer/src/lib/file-explorer.tsx +++ b/libs/remix-ui/file-explorer/src/lib/file-explorer.tsx @@ -1,14 +1,174 @@ -import React from 'react' +import React, { useReducer, useState } from 'react' // eslint-disable-line +import { TreeView, TreeViewItem } from '@remix-ui/tree-view' +import * as helper from '../../../../../apps/remix-ide/src/lib/helper' +import { FileExplorerProps } from './types' -import './remix-ui-file-explorer.css' +import './file-explorer.css' -/* eslint-disable-next-line */ -export interface FileExplorerProps {} +function extractData (value, tree, key) { + const newValue = {} + let isFile = false + + Object.keys(value).filter((x) => { + if (x === '/content') isFile = true + if (x[0] !== '/') return true + }).forEach((x) => { newValue[x] = value[x] }) + + return { + path: (tree || {}).path ? tree.path + '/' + key : key, + children: isFile ? undefined + : value instanceof Array ? value.map((item, index) => ({ + key: index, value: item + })) : value instanceof Object ? Object.keys(value).map(subkey => ({ + key: subkey, value: value[subkey] + })) : undefined + } +} export const FileExplorer = (props: FileExplorerProps) => { + const [state, setState] = useState({ + files: props.files, + focusElement: null, + focusPath: null, + menuItems: [ + { + action: 'createNewFile', + title: 'Create New File', + icon: 'fas fa-plus-circle' + }, + { + action: 'publishToGist', + title: 'Publish all [browser] explorer files to a github gist', + icon: 'fab fa-github' + }, + { + action: 'uploadFile', + title: 'Add Local file to the Browser Storage Explorer', + icon: 'far fa-folder-open' + }, + { + action: 'updateGist', + title: 'Update the current [gist] explorer', + icon: 'fab fa-github' + } + ].filter(item => props.menuItems && props.menuItems.find((name) => { return name === item.action })) + }) + + const formatSelf = (key, data, li) => { + const isRoot = data.path === state.files.type + const isFolder = !!data.children + + return ( +
+ + {key.split('/').pop()} + + {isRoot ? renderMenuItems() : ''} +
+ ) + } + + const remixdDialog = () => { + return
This file has been changed outside of Remix IDE.
+ } + + const fileRenamedError = (error) => { + console.log(error) + // modalDialogCustom.alert(error) + } + + const extractNameFromKey = (key) => { + const keyPath = key.split('/') + + return keyPath[keyPath.length - 1] + } + + const renderMenuItems = () => { + let items + if (state.menuItems) { + items = state.menuItems.map(({ action, title, icon }) => { + if (action === 'uploadFile') { + return ( + + ) + } else { + return ( + { stopPropagation(); this[action]() }} + className={'newFile ' + icon + ' remixui_newFile'} + title={title} + > + + ) + } + }) + } + return {items} + } + + const uploadFile = (target) => { + // TODO The file explorer is merely a view on the current state of + // the files module. Please ask the user here if they want to overwrite + // a file and then just use `files.add`. The file explorer will + // pick that up via the 'fileAdded' event from the files module. + + ;[...target.files].forEach((file) => { + const files = state.files + + function loadFile () { + const fileReader = new FileReader() + + fileReader.onload = async function (event) { + if (helper.checkSpecialChars(file.name)) { + // modalDialogCustom.alert('Special characters are not allowed') + return + } + const success = await files.set(name, event.target.result) + + if (!success) { + // modalDialogCustom.alert('Failed to create file ' + name) + } else { + // self.events.trigger('focus', [name]) + } + } + fileReader.readAsText(file) + } + const name = files.type + '/' + file.name + + files.exists(name, (error, exist) => { + if (error) console.log(error) + if (!exist) { + loadFile() + } else { + // modalDialogCustom.confirm('Confirm overwrite', `The file ${name} already exists! Would you like to overwrite it?`, () => { loadFile() }) + } + }) + }) + } + return (
-

Welcome to file-explorer!

+
) } diff --git a/libs/remix-ui/file-explorer/src/lib/types/index.ts b/libs/remix-ui/file-explorer/src/lib/types/index.ts new file mode 100644 index 0000000000..3886bd86af --- /dev/null +++ b/libs/remix-ui/file-explorer/src/lib/types/index.ts @@ -0,0 +1,7 @@ +/* eslint-disable-next-line */ +export interface FileExplorerProps { + localRegistry: any, + files: any, + menuItems?: string[], + plugin: any +}