complete custom drop-down

pull/2554/head
David Disu 2 years ago
parent 3ec818ba51
commit d6fedba4bb
  1. 20
      libs/remix-ui/workspace/src/lib/actions/index.ts
  2. 4
      libs/remix-ui/workspace/src/lib/actions/payload.ts
  3. 6
      libs/remix-ui/workspace/src/lib/actions/workspace.ts
  4. 42
      libs/remix-ui/workspace/src/lib/components/custom-dropdown.tsx
  5. 15
      libs/remix-ui/workspace/src/lib/css/remix-ui-workspace.css
  6. 21
      libs/remix-ui/workspace/src/lib/reducers/workspace.ts
  7. 66
      libs/remix-ui/workspace/src/lib/remix-ui-workspace.tsx

@ -24,17 +24,19 @@ export type UrlParametersType = {
url: string url: string
} }
const basicWorkspaceInit = async (workspaces, workspaceProvider) => { const basicWorkspaceInit = async (workspaces: { name: string; isGitRepo: boolean; }[], workspaceProvider) => {
if (workspaces.length === 0) { if (workspaces.length === 0) {
await createWorkspaceTemplate('default_workspace', 'remixDefault') await createWorkspaceTemplate('default_workspace', 'remixDefault')
plugin.setWorkspace({ name: 'default_workspace', isLocalhost: false }) plugin.setWorkspace({ name: 'default_workspace', isLocalhost: false })
dispatch(setCurrentWorkspace('default_workspace')) dispatch(setCurrentWorkspace({ name: 'default_workspace', isGitRepo: false }))
await loadWorkspacePreset('remixDefault') await loadWorkspacePreset('remixDefault')
} else { } else {
if (workspaces.length > 0) { if (workspaces.length > 0) {
workspaceProvider.setWorkspace(workspaces[workspaces.length - 1]) const workspace = workspaces[workspaces.length - 1]
plugin.setWorkspace({ name: workspaces[workspaces.length - 1], isLocalhost: false })
dispatch(setCurrentWorkspace(workspaces[workspaces.length - 1])) workspaceProvider.setWorkspace(workspace.name)
plugin.setWorkspace({ name: (workspace || {}).name, isLocalhost: false })
dispatch(setCurrentWorkspace(workspace))
} }
} }
} }
@ -52,12 +54,12 @@ export const initWorkspace = (filePanelPlugin) => async (reducerDispatch: React.
if (params.gist) { if (params.gist) {
await createWorkspaceTemplate('gist-sample', 'gist-template') await createWorkspaceTemplate('gist-sample', 'gist-template')
plugin.setWorkspace({ name: 'gist-sample', isLocalhost: false }) plugin.setWorkspace({ name: 'gist-sample', isLocalhost: false })
dispatch(setCurrentWorkspace('gist-sample')) dispatch(setCurrentWorkspace({ name: 'gist-sample', isGitRepo: false }))
await loadWorkspacePreset('gist-template') await loadWorkspacePreset('gist-template')
} else if (params.code || params.url) { } else if (params.code || params.url) {
await createWorkspaceTemplate('code-sample', 'code-template') await createWorkspaceTemplate('code-sample', 'code-template')
plugin.setWorkspace({ name: 'code-sample', isLocalhost: false }) plugin.setWorkspace({ name: 'code-sample', isLocalhost: false })
dispatch(setCurrentWorkspace('code-sample')) dispatch(setCurrentWorkspace({ name: 'code-sample', isGitRepo: false }))
const filePath = await loadWorkspacePreset('code-template') const filePath = await loadWorkspacePreset('code-template')
plugin.on('editor', 'editorMounted', async () => await plugin.fileManager.openFile(filePath)) plugin.on('editor', 'editorMounted', async () => await plugin.fileManager.openFile(filePath))
} else if (window.location.pathname && window.location.pathname !== '/') { } else if (window.location.pathname && window.location.pathname !== '/') {
@ -95,7 +97,7 @@ export const initWorkspace = (filePanelPlugin) => async (reducerDispatch: React.
foundOnNetworks.push(network.name) foundOnNetworks.push(network.name)
await createWorkspaceTemplate('etherscan-code-sample', 'code-template') await createWorkspaceTemplate('etherscan-code-sample', 'code-template')
plugin.setWorkspace({ name: 'etherscan-code-sample', isLocalhost: false }) plugin.setWorkspace({ name: 'etherscan-code-sample', isLocalhost: false })
dispatch(setCurrentWorkspace('etherscan-code-sample')) dispatch(setCurrentWorkspace({ name: 'etherscan-code-sample', isGitRepo: false }))
let filePath let filePath
count = count + (Object.keys(data.compilationTargets)).length count = count + (Object.keys(data.compilationTargets)).length
for (filePath in data.compilationTargets) for (filePath in data.compilationTargets)
@ -119,7 +121,7 @@ export const initWorkspace = (filePanelPlugin) => async (reducerDispatch: React.
const content = response.data const content = response.data
await createWorkspaceTemplate('github-code-sample', 'code-template') await createWorkspaceTemplate('github-code-sample', 'code-template')
plugin.setWorkspace({ name: 'github-code-sample', isLocalhost: false }) plugin.setWorkspace({ name: 'github-code-sample', isLocalhost: false })
dispatch(setCurrentWorkspace('github-code-sample')) dispatch(setCurrentWorkspace({ name: 'github-code-sample', isGitRepo: false }))
await workspaceProvider.set(route, content) await workspaceProvider.set(route, content)
plugin.on('editor', 'editorMounted', async () => await plugin.fileManager.openFile(route)) plugin.on('editor', 'editorMounted', async () => await plugin.fileManager.openFile(route))
} else await basicWorkspaceInit(workspaces, workspaceProvider) } else await basicWorkspaceInit(workspaces, workspaceProvider)

@ -1,6 +1,6 @@
import { action } from '../types' import { action } from '../types'
export const setCurrentWorkspace = (workspace: string) => { export const setCurrentWorkspace = (workspace: { name: string; isGitRepo: boolean; }) => {
return { return {
type: 'SET_CURRENT_WORKSPACE', type: 'SET_CURRENT_WORKSPACE',
payload: workspace payload: workspace
@ -125,7 +125,7 @@ export const createWorkspaceRequest = (promise: Promise<any>) => {
} }
} }
export const createWorkspaceSuccess = (workspaceName: string) => { export const createWorkspaceSuccess = (workspaceName: { name: string; isGitRepo: boolean; }) => {
return { return {
type: 'CREATE_WORKSPACE_SUCCESS', type: 'CREATE_WORKSPACE_SUCCESS',
payload: workspaceName payload: workspaceName

@ -48,7 +48,7 @@ export const createWorkspace = async (workspaceName: string, workspaceTemplateNa
dispatch(createWorkspaceRequest(promise)) dispatch(createWorkspaceRequest(promise))
promise.then(async () => { promise.then(async () => {
dispatch(createWorkspaceSuccess(workspaceName)) dispatch(createWorkspaceSuccess({ name: workspaceName, isGitRepo: false }))
await plugin.setWorkspace({ name: workspaceName, isLocalhost: false }) await plugin.setWorkspace({ name: workspaceName, isLocalhost: false })
await plugin.setWorkspaces(await getWorkspaces()) await plugin.setWorkspaces(await getWorkspaces())
await plugin.workspaceCreated(workspaceName) await plugin.workspaceCreated(workspaceName)
@ -254,8 +254,10 @@ export const switchToWorkspace = async (name: string) => {
if (isActive) await plugin.call('manager', 'deactivatePlugin', 'remixd') if (isActive) await plugin.call('manager', 'deactivatePlugin', 'remixd')
await plugin.fileProviders.workspace.setWorkspace(name) await plugin.fileProviders.workspace.setWorkspace(name)
await plugin.setWorkspace({ name, isLocalhost: false }) await plugin.setWorkspace({ name, isLocalhost: false })
const isGitRepo = await plugin.fileManager.isGitRepo()
dispatch(setMode('browser')) dispatch(setMode('browser'))
dispatch(setCurrentWorkspace(name)) dispatch(setCurrentWorkspace({ name, isGitRepo }))
dispatch(setReadOnlyMode(false)) dispatch(setReadOnlyMode(false))
} }
} }

@ -0,0 +1,42 @@
// The forwardRef is important!!
import React, { Ref } from "react"
// Dropdown needs access to the DOM node in order to position the Menu
export const CustomToggle = React.forwardRef(({ children, onClick, icon, className = '' }: { children: React.ReactNode, onClick: (e) => void, icon: string, className: string }, ref: Ref<HTMLButtonElement>) => (
<button
ref={ref}
onClick={(e) => {
e.preventDefault()
onClick(e)
}}
className={className.replace('dropdown-toggle', '')}
>
<div className="d-flex">
<div className="mr-auto">{ children }</div>
{ icon && <div className="pr-1"><i className={`${icon} pr-1`}></i></div> }
<div><i className="fad fa-sort-circle"></i></div>
</div>
</button>
))
// forwardRef again here!
// Dropdown needs access to the DOM of the Menu to measure it
export const CustomMenu = React.forwardRef(
({ children, style, className, 'aria-labelledby': labeledBy }: { children: React.ReactNode, style?: React.CSSProperties, className: string, 'aria-labelledby'?: string }, ref: Ref<HTMLDivElement>) => {
return (
<div
ref={ref}
style={style}
className={className}
aria-labelledby={labeledBy}
>
<ul className="list-unstyled mb-0">
{
children
}
</ul>
</div>
)
},
)

@ -84,3 +84,18 @@
.remixui_menuicon .bs-popover-auto[x-placement^="bottom"] > .arrow::after, .bs-popover-bottom > .arrow::after { .remixui_menuicon .bs-popover-auto[x-placement^="bottom"] > .arrow::after, .bs-popover-bottom > .arrow::after {
border-bottom-color: var(--dark) !important border-bottom-color: var(--dark) !important
} }
.custom-dropdown-items {
padding: 0.25rem 0.25rem;
border-radius: .25rem;
}
.custom-dropdown-items a {
border-radius: .25rem;
text-transform: none;
text-decoration: none;
font-weight: normal;
font-size: 0.875rem;
padding: 0.25rem 0.25rem;
width: auto;
}

@ -8,7 +8,10 @@ interface Action {
export interface BrowserState { export interface BrowserState {
browser: { browser: {
currentWorkspace: string, currentWorkspace: string,
workspaces: string[], workspaces: {
name: string;
isGitRepo: boolean;
}[],
files: { [x: string]: Record<string, FileType> }, files: { [x: string]: Record<string, FileType> },
expandPath: string[] expandPath: string[]
isRequestingDirectory: boolean, isRequestingDirectory: boolean,
@ -106,14 +109,14 @@ export const browserInitialState: BrowserState = {
export const browserReducer = (state = browserInitialState, action: Action) => { export const browserReducer = (state = browserInitialState, action: Action) => {
switch (action.type) { switch (action.type) {
case 'SET_CURRENT_WORKSPACE': { case 'SET_CURRENT_WORKSPACE': {
const payload = action.payload as string const payload = action.payload as { name: string; isGitRepo: boolean; }
const workspaces = state.browser.workspaces.includes(payload) ? state.browser.workspaces : [...state.browser.workspaces, action.payload] const workspaces = state.browser.workspaces.find(({ name }) => name === payload.name) ? state.browser.workspaces : [...state.browser.workspaces, action.payload]
return { return {
...state, ...state,
browser: { browser: {
...state.browser, ...state.browser,
currentWorkspace: payload, currentWorkspace: payload.name,
workspaces: workspaces.filter(workspace => workspace) workspaces: workspaces.filter(workspace => workspace)
} }
} }
@ -418,14 +421,14 @@ export const browserReducer = (state = browserInitialState, action: Action) => {
} }
case 'CREATE_WORKSPACE_SUCCESS': { case 'CREATE_WORKSPACE_SUCCESS': {
const payload = action.payload as string const payload = action.payload as { name: string; isGitRepo: boolean; }
const workspaces = state.browser.workspaces.includes(payload) ? state.browser.workspaces : [...state.browser.workspaces, action.payload] const workspaces = state.browser.workspaces.find(({ name }) => name === payload.name) ? state.browser.workspaces : [...state.browser.workspaces, action.payload]
return { return {
...state, ...state,
browser: { browser: {
...state.browser, ...state.browser,
currentWorkspace: payload, currentWorkspace: payload.name,
workspaces: workspaces.filter(workspace => workspace), workspaces: workspaces.filter(workspace => workspace),
isRequestingWorkspace: false, isRequestingWorkspace: false,
isSuccessfulWorkspace: true, isSuccessfulWorkspace: true,
@ -448,7 +451,7 @@ export const browserReducer = (state = browserInitialState, action: Action) => {
case 'RENAME_WORKSPACE': { case 'RENAME_WORKSPACE': {
const payload = action.payload as { oldName: string, workspaceName: string } const payload = action.payload as { oldName: string, workspaceName: string }
const workspaces = state.browser.workspaces.filter(name => name && (name !== payload.oldName)) const workspaces = state.browser.workspaces.filter(({ name }) => name && (name !== payload.oldName))
return { return {
...state, ...state,
@ -463,7 +466,7 @@ export const browserReducer = (state = browserInitialState, action: Action) => {
case 'DELETE_WORKSPACE': { case 'DELETE_WORKSPACE': {
const payload = action.payload as string const payload = action.payload as string
const workspaces = state.browser.workspaces.filter(name => name && (name !== payload)) const workspaces = state.browser.workspaces.filter(({ name }) => name && (name !== payload))
return { return {
...state, ...state,

@ -1,5 +1,6 @@
import React, { useState, useEffect, useRef, useContext } from 'react' // eslint-disable-line import React, { useState, useEffect, useRef, useContext } from 'react' // eslint-disable-line
import { Dropdown } from 'react-bootstrap' import { Dropdown } from 'react-bootstrap'
import { CustomMenu, CustomToggle } from './components/custom-dropdown'
import { FileExplorer } from './components/file-explorer' // eslint-disable-line import { FileExplorer } from './components/file-explorer' // eslint-disable-line
import { FileSystemContext } from './contexts' import { FileSystemContext } from './contexts'
import './css/remix-ui-workspace.css' import './css/remix-ui-workspace.css'
@ -10,8 +11,7 @@ export function Workspace () {
const LOCALHOST = ' - connect to localhost - ' const LOCALHOST = ' - connect to localhost - '
const NO_WORKSPACE = ' - none - ' const NO_WORKSPACE = ' - none - '
const [currentWorkspace, setCurrentWorkspace] = useState<string>(NO_WORKSPACE) const [currentWorkspace, setCurrentWorkspace] = useState<string>(NO_WORKSPACE)
// const [toggleSelectWorkspace, setToggleSelectWorkspace] = useState<string | JSX.Element>("") const [selectedWorkspace, setSelectedWorkspace] = useState<{ name: string, isGitRepo: boolean}>(null)
const [dropdownWorkspacesList, setDropdownWorkspacesList] = useState<{string: string | JSX.Element}[]>([])
const global = useContext(FileSystemContext) const global = useContext(FileSystemContext)
const workspaceRenameInput = useRef() const workspaceRenameInput = useRef()
const workspaceCreateInput = useRef() const workspaceCreateInput = useRef()
@ -34,19 +34,21 @@ export function Workspace () {
}, [global.fs.browser.currentWorkspace, global.fs.localhost.sharedFolder, global.fs.mode]) }, [global.fs.browser.currentWorkspace, global.fs.localhost.sharedFolder, global.fs.mode])
useEffect(() => { useEffect(() => {
if (global.fs.browser.currentWorkspace && !global.fs.browser.workspaces.includes(global.fs.browser.currentWorkspace)) { if (global.fs.browser.currentWorkspace && !global.fs.browser.workspaces.find(({ name }) => name === global.fs.browser.currentWorkspace)) {
if (global.fs.browser.workspaces.length > 0) { if (global.fs.browser.workspaces.length > 0) {
switchWorkspace(global.fs.browser.workspaces[global.fs.browser.workspaces.length - 1]) switchWorkspace(global.fs.browser.workspaces[global.fs.browser.workspaces.length - 1].name)
} else { } else {
switchWorkspace(NO_WORKSPACE) switchWorkspace(NO_WORKSPACE)
} }
} }
const dropdownList = global.fs.browser.workspaces.map((workspace) => {
})
setDropdownWorkspacesList(dropdownList)
}, [global.fs.browser.workspaces]) }, [global.fs.browser.workspaces])
useEffect(() => {
const workspace = global.fs.browser.workspaces.find(workspace => workspace.name === currentWorkspace)
setSelectedWorkspace(workspace)
}, [currentWorkspace])
const renameCurrentWorkspace = () => { const renameCurrentWorkspace = () => {
global.modal('Rename Current Workspace', renameModalMessage(), 'OK', onFinishRenameWorkspace, '') global.modal('Rename Current Workspace', renameModalMessage(), 'OK', onFinishRenameWorkspace, '')
} }
@ -255,34 +257,34 @@ export function Workspace () {
title='Clone Git Repository'> title='Clone Git Repository'>
</span> </span>
</span> </span>
{/* <Dropdown <Dropdown id="workspacesSelect" data-id="workspacesSelect">
onSelect={value => { <Dropdown.Toggle as={CustomToggle} id="dropdown-custom-components" className="btn btn-light btn-block w-100 d-inline-block border border-dark form-control" icon={selectedWorkspace && selectedWorkspace.isGitRepo ? 'far fa-code-branch' : null}>
const { code, title } = [{ code: 'NG', title: 'Nigeria' }].find(({ code }) => value === code) { selectedWorkspace ? selectedWorkspace.name : currentWorkspace === LOCALHOST ? 'localhost' : NO_WORKSPACE }
setSelectedCountry(value)
setToggleContents(<><i className="fas fa-code-branch"></i> {title}</>)
}}
>
<Dropdown.Toggle variant="secondary" id="dropdown-flags" className="text-left" style={{ width: 300 }}>
{toggleContents}
</Dropdown.Toggle> </Dropdown.Toggle>
<Dropdown.Menu> <Dropdown.Menu as={CustomMenu} className='w-100 custom-dropdown-items' >
{[{ code: 'NG', title: 'Nigeria' }].map(({ code, title }) => (
<Dropdown.Item key={code} eventKey={code}><i className="fas fa-code-branch"></i> {title}</Dropdown.Item>
))}
</Dropdown.Menu>
</Dropdown> */}
<select id="workspacesSelect" value={currentWorkspace} data-id="workspacesSelect" onChange={(e) => switchWorkspace(e.target.value)} className="form-select">
{ {
global.fs.browser.workspaces global.fs.browser.workspaces.map(({ name, isGitRepo }, index) => (
.map((folder, index) => { <Dropdown.Item
return <option key={index} value={folder}>{folder}</option> key={index}
}) onClick={() => {
switchWorkspace(name)
}}
>
{ isGitRepo ?
<div className='d-flex justify-content-between'>
<span>{ currentWorkspace === name ? <span>&#10003; { name } </span> : <span className="pl-3">{ name }</span> }</span>
<i className='fas fa-code-branch pt-1'></i>
</div> :
<span>{ currentWorkspace === name ? <span>&#10003; { name } </span> : <span className="pl-3">{ name }</span> }</span>
} }
<option value={LOCALHOST}>{currentWorkspace === LOCALHOST ? 'localhost' : LOCALHOST}</option> </Dropdown.Item>
{ global.fs.browser.workspaces.length <= 0 && <option value={NO_WORKSPACE}>{NO_WORKSPACE}</option> } ))
</select> }
<Dropdown.Item onClick={() => { switchWorkspace(LOCALHOST) }}>{currentWorkspace === LOCALHOST ? <span>&#10003; localhost </span> : <span className="pl-3"> { LOCALHOST } </span>}</Dropdown.Item>
{ ((global.fs.browser.workspaces.length <= 0) || currentWorkspace === NO_WORKSPACE) && <Dropdown.Item onClick={() => { switchWorkspace(NO_WORKSPACE) }}>{ <span className="pl-3">NO_WORKSPACE</span> }</Dropdown.Item> }
</Dropdown.Menu>
</Dropdown>
</div> </div>
</header> </header>
</div> </div>

Loading…
Cancel
Save