diff --git a/apps/remix-ide-e2e/src/tests/defaultLayout.test.ts b/apps/remix-ide-e2e/src/tests/defaultLayout.test.ts index 8f303bf593..ae1f51af60 100644 --- a/apps/remix-ide-e2e/src/tests/defaultLayout.test.ts +++ b/apps/remix-ide-e2e/src/tests/defaultLayout.test.ts @@ -28,7 +28,7 @@ module.exports = { 'Loads Main View': function (browser: NightwatchBrowser) { browser.waitForElementVisible('div[data-id="mainPanelPluginsContainer"]') .waitForElementVisible('div[data-id="landingPageHomeContainer"]') - .waitForElementVisible('div[data-id="landingPageHpSections"]') + .waitForElementVisible('div[data-id="remixUIHTAll"]') .waitForElementVisible('div[data-id="terminalContainer"]') }, diff --git a/apps/remix-ide-e2e/src/tests/workspace.test.ts b/apps/remix-ide-e2e/src/tests/workspace.test.ts index 6b0c78290c..fff723f887 100644 --- a/apps/remix-ide-e2e/src/tests/workspace.test.ts +++ b/apps/remix-ide-e2e/src/tests/workspace.test.ts @@ -130,28 +130,28 @@ module.exports = { // check js and ts files are not transformed .click('*[data-id="treeViewLitreeViewItemscripts/deploy_with_web3.ts"]') .pause(100) - .getEditorValue((content) => { + .getEditorValue((content) => { browser.assert.ok(content.indexOf(`import { deploy } from './web3-lib'`) !== -1, 'Incorrect content') }) .waitForElementVisible('*[data-id="treeViewLitreeViewItemscripts/deploy_with_ethers.ts"]') .click('*[data-id="treeViewLitreeViewItemscripts/deploy_with_ethers.ts"]') .pause(100) - .getEditorValue((content) => { + .getEditorValue((content) => { browser.assert.ok(content.indexOf(`import { deploy } from './ethers-lib'`) !== -1, 'Incorrect content') }) .waitForElementVisible('*[data-id="treeViewLitreeViewItemscripts/web3-lib.ts"]') .click('*[data-id="treeViewLitreeViewItemscripts/web3-lib.ts"]') .pause(100) - .getEditorValue((content) => { + .getEditorValue((content) => { browser.assert.ok(content.indexOf(`export const deploy = async (contractName: string, args: Array, from?: string, gas?: number): Promise => {`) !== -1, 'Incorrect content') }) .waitForElementVisible('*[data-id="treeViewLitreeViewItemscripts/ethers-lib.ts"]') .click('*[data-id="treeViewLitreeViewItemscripts/ethers-lib.ts"]') .pause(100) - .getEditorValue((content) => { + .getEditorValue((content) => { browser.assert.ok(content.indexOf(`export const deploy = async (contractName: string, args: Array, accountIndex?: number): Promise => {`) !== -1, 'Incorrect content') }) @@ -178,28 +178,28 @@ module.exports = { // check js and ts files are not transformed .click('*[data-id="treeViewLitreeViewItemscripts/deploy_with_web3.ts"]') .pause(100) - .getEditorValue((content) => { + .getEditorValue((content) => { browser.assert.ok(content.indexOf(`import { deploy } from './web3-lib'`) !== -1, 'Incorrect content') }) .waitForElementVisible('*[data-id="treeViewLitreeViewItemscripts/deploy_with_ethers.ts"]') .click('*[data-id="treeViewLitreeViewItemscripts/deploy_with_ethers.ts"]') .pause(100) - .getEditorValue((content) => { + .getEditorValue((content) => { browser.assert.ok(content.indexOf(`import { deploy } from './ethers-lib'`) !== -1, 'Incorrect content') }) .waitForElementVisible('*[data-id="treeViewLitreeViewItemscripts/web3-lib.ts"]') .click('*[data-id="treeViewLitreeViewItemscripts/web3-lib.ts"]') .pause(100) - .getEditorValue((content) => { + .getEditorValue((content) => { browser.assert.ok(content.indexOf(`export const deploy = async (contractName: string, args: Array, from?: string, gas?: number): Promise => {`) !== -1, 'Incorrect content') }) .waitForElementVisible('*[data-id="treeViewLitreeViewItemscripts/ethers-lib.ts"]') .click('*[data-id="treeViewLitreeViewItemscripts/ethers-lib.ts"]') .pause(100) - .getEditorValue((content) => { + .getEditorValue((content) => { browser.assert.ok(content.indexOf(`export const deploy = async (contractName: string, args: Array, accountIndex?: number): Promise => {`) !== -1, 'Incorrect content') }) @@ -226,28 +226,28 @@ module.exports = { // check js and ts files are not transformed .click('*[data-id="treeViewLitreeViewItemscripts/deploy_with_web3.ts"]') .pause(1000) - .getEditorValue((content) => { + .getEditorValue((content) => { browser.assert.ok(content.indexOf(`import { deploy } from './web3-lib'`) !== -1, 'Incorrect content') }) .waitForElementVisible('*[data-id="treeViewLitreeViewItemscripts/deploy_with_ethers.ts"]') .click('*[data-id="treeViewLitreeViewItemscripts/deploy_with_ethers.ts"]') .pause(100) - .getEditorValue((content) => { + .getEditorValue((content) => { browser.assert.ok(content.indexOf(`import { deploy } from './ethers-lib'`) !== -1, 'Incorrect content') }) .waitForElementVisible('*[data-id="treeViewLitreeViewItemscripts/web3-lib.ts"]') .click('*[data-id="treeViewLitreeViewItemscripts/web3-lib.ts"]') .pause(100) - .getEditorValue((content) => { + .getEditorValue((content) => { browser.assert.ok(content.indexOf(`export const deploy = async (contractName: string, args: Array, from?: string, gas?: number): Promise => {`) !== -1, 'Incorrect content') }) .waitForElementVisible('*[data-id="treeViewLitreeViewItemscripts/ethers-lib.ts"]') .click('*[data-id="treeViewLitreeViewItemscripts/ethers-lib.ts"]') .pause(100) - .getEditorValue((content) => { + .getEditorValue((content) => { browser.assert.ok(content.indexOf(`export const deploy = async (contractName: string, args: Array, accountIndex?: number): Promise => {`) !== -1, 'Incorrect content') }) @@ -274,7 +274,7 @@ module.exports = { .waitForElementVisible('*[data-id="treeViewLitreeViewItemcontracts/MyToken.sol"]') .click('*[data-id="treeViewLitreeViewItemcontracts/MyToken.sol"]') .pause(1000) - .getEditorValue((content) => { + .getEditorValue((content) => { browser.assert.ok(content.indexOf(`contract MyToken is Initializable, ERC1155Upgradeable, OwnableUpgradeable, PausableUpgradeable, ERC1155BurnableUpgradeable, UUPSUpgradeable {`) !== -1, 'Incorrect content') }) @@ -283,21 +283,21 @@ module.exports = { // check js and ts files are not transformed .click('*[data-id="treeViewLitreeViewItemscripts/deploy_with_web3.ts"]') .pause(100) - .getEditorValue((content) => { + .getEditorValue((content) => { browser.assert.ok(content.indexOf(`import { deploy } from './web3-lib'`) !== -1, 'Incorrect content') }) .waitForElementVisible('*[data-id="treeViewLitreeViewItemscripts/deploy_with_ethers.ts"]') .click('*[data-id="treeViewLitreeViewItemscripts/deploy_with_ethers.ts"]') .pause(100) - .getEditorValue((content) => { + .getEditorValue((content) => { browser.assert.ok(content.indexOf(`import { deploy } from './ethers-lib'`) !== -1, 'Incorrect content') }) .waitForElementVisible('*[data-id="treeViewLitreeViewItemscripts/web3-lib.ts"]') .click('*[data-id="treeViewLitreeViewItemscripts/web3-lib.ts"]') .pause(100) - .getEditorValue((content) => { + .getEditorValue((content) => { browser.assert.ok(content.indexOf(`export const deploy = async (contractName: string, args: Array, from?: string, gas?: number): Promise => {`) !== -1, 'Incorrect content') browser.assert.ok(content.indexOf(`gas: gas || 3600000`) !== -1, @@ -306,7 +306,7 @@ module.exports = { .waitForElementVisible('*[data-id="treeViewLitreeViewItemscripts/ethers-lib.ts"]') .click('*[data-id="treeViewLitreeViewItemscripts/ethers-lib.ts"]') .pause(100) - .getEditorValue((content) => { + .getEditorValue((content) => { browser.assert.ok(content.indexOf(`export const deploy = async (contractName: string, args: Array, accountIndex?: number): Promise => {`) !== -1, 'Incorrect content') }) @@ -345,7 +345,15 @@ module.exports = { 'Should rename a workspace #group1': function (browser: NightwatchBrowser) { browser - .click('*[data-id="workspaceRename"]') // rename workspace_name + .useXpath() + .waitForElementPresent({ + selector: '//i[@data-icon="workspaceDropdownMenuIcon"]', + locateStrategy: 'xpath', + }) + .click('//*[@id="workspacesMenuDropdown"]/span/i') + .waitForElementVisible('//*[@id="workspacesMenuDropdown"]/div/ul') + .click('//*[@id="workspacesMenuDropdown"]/div/ul/a[3]') // rename workspace_name + .useCss() .waitForElementVisible('*[data-id="treeViewLitreeViewItemtests"]') .waitForElementVisible('*[data-id="modalDialogCustomPromptTextRename"]') .click('*[data-id="modalDialogCustomPromptTextRename"]') @@ -365,12 +373,15 @@ module.exports = { 'Should delete a workspace #group1': function (browser: NightwatchBrowser) { browser - .switchWorkspace('workspace_name_1') - .click('*[data-id="workspaceDelete"]') // delete workspace_name_1 - .waitForElementVisible('[data-id="fileSystemModalDialogModalFooter-react"] .modal-ok') - .click('[data-id="fileSystemModalDialogModalFooter-react"] .modal-ok') - .waitForElementVisible('[data-id="workspacesSelect"]') - .click('[data-id="workspacesSelect"]') + .switchWorkspace('workspace_name_1')//*[@id="workspacesMenuDropdown"]/span + .useXpath() + .click('//*[@id="workspacesMenuDropdown"]/span/i') + .click('//*[@id="workspacesMenuDropdown"]/div/ul/a[2]') // delete workspace_name_1 + .waitForElementVisible('//*[@id="fileExplorerView"]/div[2]/div/div/div[2]') + .click('//*[@id="fileExplorerView"]/div[2]/div/div/div[3]/button') + .waitForElementVisible('//*[@id="workspacesSelect"]') + .click('//*[@id="workspacesSelect"]') + .useCss() .waitForElementNotPresent(`[data-id="dropdown-item-workspace_name_1"]`) }, @@ -379,8 +390,11 @@ module.exports = { 'Should clone a repository #group2': function (browser: NightwatchBrowser) { browser .clickLaunchIcon('filePanel') - .waitForElementVisible('[data-id="cloneGitRepository"]') - .click('[data-id="cloneGitRepository"]') + .useXpath() + .click('//*[@id="workspacesMenuDropdown"]/span/i') + .waitForElementVisible('//*[@id="workspacesMenuDropdown"]/div/ul/a[5]') + .click('//*[@id="workspacesMenuDropdown"]/div/ul/a[5]') + .useCss() .waitForElementVisible('[data-id="fileSystemModalDialogModalBody-react"]') .click('[data-id="fileSystemModalDialogModalBody-react"]') .waitForElementVisible('[data-id="modalDialogCustomPromptTextClone"]') @@ -403,8 +417,11 @@ module.exports = { 'Should display non-clashing names for duplicate clone #group2': '' + function (browser: NightwatchBrowser) { browser - .waitForElementVisible('[data-id="cloneGitRepository"]') - .click('[data-id="cloneGitRepository"]') + .useXpath() + .click('//*[@id="workspacesMenuDropdown"]/span/i') + .waitForElementVisible('//*[@id="workspacesMenuDropdown"]/div/ul/a[5]') + .click('//*[@id="workspacesMenuDropdown"]/div/ul/a[5]') + .useCss() .waitForElementVisible('[data-id="fileSystemModalDialogModalBody-react"]') .click('[data-id="fileSystemModalDialogModalBody-react"]') .waitForElementVisible('[data-id="modalDialogCustomPromptTextClone"]') @@ -412,8 +429,11 @@ module.exports = { .click('[data-id="fileSystem-modal-footer-ok-react"]') .pause(5000) .waitForElementContainsText('[data-id="workspacesSelect"]', 'awesome-remix1') - .waitForElementVisible('[data-id="cloneGitRepository"]') - .click('[data-id="cloneGitRepository"]') + .useXpath() + .click('//*[@id="workspacesMenuDropdown"]/span/i') + .waitForElementVisible('//*[@id="workspacesMenuDropdown"]/div/ul/a[5]') + .click('//*[@id="workspacesMenuDropdown"]/div/ul/a[5]') + .useCss() .waitForElementVisible('[data-id="fileSystemModalDialogModalBody-react"]') .click('[data-id="fileSystemModalDialogModalBody-react"]') .waitForElementVisible('[data-id="modalDialogCustomPromptTextClone"]') @@ -421,8 +441,10 @@ module.exports = { .click('[data-id="fileSystem-modal-footer-ok-react"]') .pause(5000) .waitForElementContainsText('[data-id="workspacesSelect"]', 'awesome-remix2') - .waitForElementVisible('[data-id="cloneGitRepository"]') - .click('[data-id="cloneGitRepository"]') + .useXpath() + .click('//*[@id="workspacesMenuDropdown"]/span/i') + .waitForElementVisible('//*[@id="workspacesMenuDropdown"]/div/ul/a[5]') + .useCss() .waitForElementVisible('[data-id="fileSystemModalDialogModalBody-react"]') .click('[data-id="fileSystemModalDialogModalBody-react"]') .waitForElementVisible('[data-id="modalDialogCustomPromptTextClone"]') @@ -438,8 +460,15 @@ module.exports = { 'Should display error message in modal for failed clone #group2': function (browser: NightwatchBrowser) { browser - .waitForElementVisible('[data-id="cloneGitRepository"]') - .click('[data-id="cloneGitRepository"]') + .useXpath() + .waitForElementPresent({ + selector: '//i[@data-icon="workspaceDropdownMenuIcon"]', + locateStrategy: 'xpath', + }) + .click('//*[@id="workspacesMenuDropdown"]/span/i') + .waitForElementVisible('//*[@id="workspacesMenuDropdown"]/div/ul/a[5]') + .click('//*[@id="workspacesMenuDropdown"]/div/ul/a[5]') + .useCss() .waitForElementVisible('[data-id="fileSystemModalDialogModalBody-react"]') .click('[data-id="fileSystemModalDialogModalBody-react"]') .waitForElementVisible('[data-id="modalDialogCustomPromptTextClone"]') @@ -456,3 +485,4 @@ module.exports = { tearDown: sauce } + diff --git a/apps/remix-ide/src/app/ui/landing-page/landing-page.js b/apps/remix-ide/src/app/ui/landing-page/landing-page.js index 978c4fdb1c..81444c5c1e 100644 --- a/apps/remix-ide/src/app/ui/landing-page/landing-page.js +++ b/apps/remix-ide/src/app/ui/landing-page/landing-page.js @@ -10,7 +10,7 @@ const profile = { methods: [], events: [], description: 'Remix home tab ', - icon: 'assets/img/remixLogo.webp', + icon: 'assets/img/home.webp', location: 'mainPanel', version: packageJson.version } @@ -30,9 +30,8 @@ export class LandingPage extends ViewPlugin { } render () { - return
- } - + return
+ +
+ } } diff --git a/apps/remix-ide/src/assets/img/StarkNetLogo.png b/apps/remix-ide/src/assets/img/StarkNetLogo.png new file mode 100644 index 0000000000..74218453e0 Binary files /dev/null and b/apps/remix-ide/src/assets/img/StarkNetLogo.png differ diff --git a/apps/remix-ide/src/assets/img/bgRemi.webp b/apps/remix-ide/src/assets/img/bgRemi.webp new file mode 100644 index 0000000000..80ee32e1de Binary files /dev/null and b/apps/remix-ide/src/assets/img/bgRemi.webp differ diff --git a/apps/remix-ide/src/assets/img/home.webp b/apps/remix-ide/src/assets/img/home.webp new file mode 100644 index 0000000000..6af3393ffd Binary files /dev/null and b/apps/remix-ide/src/assets/img/home.webp differ diff --git a/apps/remix-ide/src/assets/img/logoicon.svg b/apps/remix-ide/src/assets/img/logoicon.svg new file mode 100644 index 0000000000..d064e0a227 --- /dev/null +++ b/apps/remix-ide/src/assets/img/logoicon.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/apps/remix-ide/src/assets/img/remixRewardBetaTester.webp b/apps/remix-ide/src/assets/img/remixRewardBetaTester.webp new file mode 100644 index 0000000000..59a14a9ad2 Binary files /dev/null and b/apps/remix-ide/src/assets/img/remixRewardBetaTester.webp differ diff --git a/apps/remix-ide/src/assets/img/remixRewardUser.webp b/apps/remix-ide/src/assets/img/remixRewardUser.webp new file mode 100644 index 0000000000..4f63b39069 Binary files /dev/null and b/apps/remix-ide/src/assets/img/remixRewardUser.webp differ diff --git a/apps/remix-ide/src/assets/img/remix_logo_light.webp b/apps/remix-ide/src/assets/img/remix_logo_light.webp new file mode 100644 index 0000000000..097a95e50e Binary files /dev/null and b/apps/remix-ide/src/assets/img/remix_logo_light.webp differ diff --git a/libs/remix-ui/helper/src/lib/components/custom-dropdown.tsx b/libs/remix-ui/helper/src/lib/components/custom-dropdown.tsx index 5f0696a554..4ca65cf2d3 100644 --- a/libs/remix-ui/helper/src/lib/components/custom-dropdown.tsx +++ b/libs/remix-ui/helper/src/lib/components/custom-dropdown.tsx @@ -20,6 +20,20 @@ export const CustomToggle = React.forwardRef(({ children, onClick, icon, classNa )) +export const CustomIconsToggle = React.forwardRef(({ onClick, icon, className = '' }: { children?: React.ReactNode, onClick: () => void, icon: string, className: string }, ref: Ref) => ( + { + e.preventDefault() + onClick() + }} + className={`${className.replace('dropdown-toggle', '')} mb-0 pb-0 d-flex justify-content-end align-items-end remixuimenuicon_shadow fs-3`} + data-id="workspaceMenuDropdown" + > + { icon && } + +)) + // forwardRef again here! // Dropdown needs access to the DOM of the Menu to measure it export const CustomMenu = React.forwardRef( diff --git a/libs/remix-ui/home-tab/src/lib/components/customButtonGroupAsArrows.tsx b/libs/remix-ui/home-tab/src/lib/components/customButtonGroupAsArrows.tsx new file mode 100644 index 0000000000..d265e5781d --- /dev/null +++ b/libs/remix-ui/home-tab/src/lib/components/customButtonGroupAsArrows.tsx @@ -0,0 +1,14 @@ +/* eslint-disable @typescript-eslint/no-unused-vars */ +import React from 'react' + +function CustomButtonGroupAsArrows ({ next, previous }) { + return ( +
+

These buttons can be positioned anywhere you want on the screen

+ + +
+ ) +} + +export default CustomButtonGroupAsArrows \ No newline at end of file diff --git a/libs/remix-ui/home-tab/src/lib/components/customNavButtons.tsx b/libs/remix-ui/home-tab/src/lib/components/customNavButtons.tsx new file mode 100644 index 0000000000..f298eba01d --- /dev/null +++ b/libs/remix-ui/home-tab/src/lib/components/customNavButtons.tsx @@ -0,0 +1,20 @@ +/* eslint-disable @typescript-eslint/no-unused-vars */ +import React from 'react' + +const CustomNavButtons = ({ next, previous, goToSlide, ...rest }) => { + const { carouselState: { currentSlide, totalItems } } = rest + return ( +
+ + +
+ ) +} + +export default CustomNavButtons diff --git a/libs/remix-ui/home-tab/src/lib/components/homeTabFeatured.tsx b/libs/remix-ui/home-tab/src/lib/components/homeTabFeatured.tsx new file mode 100644 index 0000000000..8741d7779f --- /dev/null +++ b/libs/remix-ui/home-tab/src/lib/components/homeTabFeatured.tsx @@ -0,0 +1,73 @@ +/* eslint-disable @typescript-eslint/no-unused-vars */ +import React, { useEffect, useState, useRef, useContext } from 'react' +import { ThemeContext, themes } from '../themeContext' +import Carousel from 'react-multi-carousel' +import 'react-multi-carousel/lib/styles.css' +import CustomNavButtons from './customNavButtons' + +function HomeTabFeatured() { + const themeFilter = useContext(ThemeContext) + + useEffect(() => { + return () => { + } + }, []) + + return ( +
+ +
+
+ + } + arrows={false} + swipeable={false} + draggable={true} + showDots={true} + responsive={{ desktop: { breakpoint: { max: 2000, min: 1024 }, items: 1 } }} + renderDotsOutside={true} + ssr={true} // means to render carousel on server-side. + infinite={true} + centerMode={false} + autoPlay={true} + keyBoardControl={true} + containerClass="border carousel-container" + sliderClass="px-2 h-100 justify-content-between" + deviceType={"desktop"} + itemClass="px-2 carousel-item-padding-10-px" + autoPlaySpeed={15000} + dotListClass="position-relative mt-2" + > +
+ +
+
JUMP INTO WEB3
+ The Remix Project is a rich toolset which can be used for the entire journey of contract development by users of any knowledge level, and as a learning lab for teaching and experimenting with Ethereum. +
+
+
+ +
+
REMIX REWARDS
+

NFTs for our users!

+ Remix Project rewards contributors, beta testers, and UX research participants with NFTs deployed on Optimism. Remix Reward holders are able to mint a second “Remixer” user NFT badge to give to any other user of their choice +
+
+
+ +
+
BETA TESTING
+

Our community supports us.

+ You can join Beta Testing before each release of Remix IDE. Help us test now and get a handle on new features! +
+
+
+
+
+
+
+ ) +} + +export default HomeTabFeatured diff --git a/libs/remix-ui/home-tab/src/lib/components/homeTabFeaturedPlugins.tsx b/libs/remix-ui/home-tab/src/lib/components/homeTabFeaturedPlugins.tsx new file mode 100644 index 0000000000..84b2fb4f57 --- /dev/null +++ b/libs/remix-ui/home-tab/src/lib/components/homeTabFeaturedPlugins.tsx @@ -0,0 +1,125 @@ +/* eslint-disable @typescript-eslint/no-unused-vars */ +import React, { useEffect, useState, useRef, useContext } from 'react' +import PluginButton from './pluginButton' +import { ThemeContext, themes } from '../themeContext' +import Carousel from 'react-multi-carousel' +import 'react-multi-carousel/lib/styles.css' +import CustomNavButtons from './customNavButtons' +declare global { + interface Window { + _paq: any + } +} +const _paq = window._paq = window._paq || [] //eslint-disable-line + +interface HomeTabFeaturedPluginsProps { + plugin: any +} + +function HomeTabFeaturedPlugins ({plugin}: HomeTabFeaturedPluginsProps) { + + const themeFilter = useContext(ThemeContext) + const carouselRef = useRef(null) + + // Todo doesn't work + useEffect(() => { + window.addEventListener("scroll", handleScroll) + return () => { + window.removeEventListener("scroll", handleScroll) + } + }, []) + + const handleScroll = (e) => { + } + + const startSolidity = async () => { + await plugin.appManager.activatePlugin(['solidity', 'udapp', 'solidityStaticAnalysis', 'solidityUnitTesting']) + plugin.verticalIcons.select('solidity') + _paq.push(['trackEvent', 'pluginManager', 'userActivate', 'solidity']) + } + const startStarkNet = async () => { + await plugin.appManager.activatePlugin('starkNet_compiler') + plugin.verticalIcons.select('starkNet_compiler') + _paq.push(['trackEvent', 'pluginManager', 'userActivate', 'starkNet_compiler']) + } + const startSolhint = async () => { + await plugin.appManager.activatePlugin(['solidity', 'solhint']) + plugin.verticalIcons.select('solhint') + _paq.push(['trackEvent', 'pluginManager', 'userActivate', 'solhint']) + } + const startSourceVerify = async () => { + await plugin.appManager.activatePlugin(['solidity', 'sourcify']) + plugin.verticalIcons.select('sourcify') + _paq.push(['trackEvent', 'pluginManager', 'userActivate', 'sourcify']) + } + const startSolidityUnitTesting = async () => { + await plugin.appManager.activatePlugin(['solidity', 'solidityUnitTesting']) + plugin.verticalIcons.select('solidityUnitTesting') + _paq.push(['trackEvent', 'pluginManager', 'userActivate', 'solidityUnitTesting']) + } + + return ( +
+ +
+ + } + arrows={false} + swipeable={false} + draggable={true} + showDots={false} + responsive={{ desktop: { breakpoint: { max: 3000, min: 1024 }, items: 5} }} + renderButtonGroupOutside={true} + ssr={true} // means to render carousel on server-side. + keyBoardControl={true} + containerClass="carousel-container" + deviceType={"desktop"} + itemClass="w-100" + > + startSolidity()} + /> + startStarkNet()} + /> + startSolhint()} + /> + startSourceVerify()} + /> + startSolidityUnitTesting()} + /> + + +
+
+ ) +} + +export default HomeTabFeaturedPlugins diff --git a/libs/remix-ui/home-tab/src/lib/components/homeTabFile.tsx b/libs/remix-ui/home-tab/src/lib/components/homeTabFile.tsx new file mode 100644 index 0000000000..baa80619b8 --- /dev/null +++ b/libs/remix-ui/home-tab/src/lib/components/homeTabFile.tsx @@ -0,0 +1,161 @@ +/* eslint-disable @typescript-eslint/no-unused-vars */ +import React, { useState, useRef, useReducer } from 'react' +import { ModalDialog } from '@remix-ui/modal-dialog' // eslint-disable-line +import { Toaster } from '@remix-ui/toaster' // eslint-disable-line + +interface HomeTabFileProps { + plugin: any +} + +const loadingInitialState = { + tooltip: '', + showModalDialog: false, + importSource: '' +} + +const loadingReducer = (state = loadingInitialState, action) => { + return { ...state, tooltip: action.tooltip, showModalDialog: false, importSource: '' } +} + +function HomeTabFile ({plugin}: HomeTabFileProps) { + const [state, setState] = useState<{ + searchInput: string, + showModalDialog: boolean, + modalInfo: { title: string, loadItem: string, examples: Array }, + importSource: string, + toasterMsg: string + }>({ + searchInput: '', + showModalDialog: false, + modalInfo: { title: '', loadItem: '', examples: [] }, + importSource: '', + toasterMsg: '' + }) + + const [, dispatch] = useReducer(loadingReducer, loadingInitialState) + + const inputValue = useRef(null) + + const processLoading = () => { + const contentImport = plugin.contentImport + const workspace = plugin.fileManager.getProvider('workspace') + contentImport.import( + state.importSource, + (loadingMsg) => dispatch({ tooltip: loadingMsg }), + async (error, content, cleanUrl, type, url) => { + if (error) { + toast(error.message || error) + } else { + try { + if (await workspace.exists(type + '/' + cleanUrl)) toast('File already exists in workspace') + else { + workspace.addExternal(type + '/' + cleanUrl, content, url) + plugin.call('menuicons', 'select', 'filePanel') + } + } catch (e) { + toast(e.message) + } + } + } + ) + setState(prevState => { + return { ...prevState, showModalDialog: false, importSource: '' } + }) + } + + const toast = (message: string) => { + setState(prevState => { + return { ...prevState, toasterMsg: message } + }) + } + + const createNewFile = async () => { + plugin.verticalIcons.select('filePanel') + await plugin.call('filePanel', 'createNewFile') + } + + const uploadFile = async (target) => { + await plugin.call('filePanel', 'uploadFile', target) + } + + const connectToLocalhost = () => { + plugin.appManager.activatePlugin('remixd') + } + const importFromGist = () => { + plugin.call('gistHandler', 'load', '') + plugin.verticalIcons.select('filePanel') + } + + const showFullMessage = (title: string, loadItem: string, examples: Array) => { + setState(prevState => { + return { ...prevState, showModalDialog: true, modalInfo: { title: title, loadItem: loadItem, examples: examples } } + }) + } + + const hideFullMessage = () => { //eslint-disable-line + setState(prevState => { + return { ...prevState, showModalDialog: false, importSource: '' } + }) + } + + const examples = state.modalInfo.examples.map((urlEl, key) => ()) + + return ( + <> + hideFullMessage() } + okFn={ () => processLoading() } + > +
+ { state.modalInfo.loadItem !== '' && Enter the { state.modalInfo.loadItem } you would like to load. } + { state.modalInfo.examples.length !== 0 && + <> +
e.g
+
+ { examples } +
+ } + { + setState(prevState => { + return { ...prevState, importSource: inputValue.current.value } + }) + }} + /> +
+
+ +
+ + + + { + event.stopPropagation() + plugin.verticalIcons.select('filePanel') + uploadFile(event.target) + }} multiple /> + + +
+ + + + +
+
+ + ) +} + +export default HomeTabFile \ No newline at end of file diff --git a/libs/remix-ui/home-tab/src/lib/components/homeTabGetStarted.tsx b/libs/remix-ui/home-tab/src/lib/components/homeTabGetStarted.tsx new file mode 100644 index 0000000000..f27f489198 --- /dev/null +++ b/libs/remix-ui/home-tab/src/lib/components/homeTabGetStarted.tsx @@ -0,0 +1,88 @@ +/* eslint-disable @typescript-eslint/no-unused-vars */ +import React, { useEffect, useState, useRef, useContext } from 'react' +import { ThemeContext, themes } from '../themeContext' +import Carousel from 'react-multi-carousel' +import WorkspaceTemplate from './workspaceTemplate' +import 'react-multi-carousel/lib/styles.css' +import CustomNavButtons from './customNavButtons' +declare global { + interface Window { + _paq: any + } +} +const _paq = window._paq = window._paq || [] //eslint-disable-line + +interface HomeTabGetStartedProps { + plugin: any +} + +function HomeTabGetStarted ({plugin}: HomeTabGetStartedProps) { + const themeFilter = useContext(ThemeContext) + + const createWorkspace = async (templateName) => { + await plugin.appManager.activatePlugin('filePanel') + const timeStamp = Date.now() + await plugin.call('filePanel', 'createWorkspace', templateName + "_" + timeStamp, templateName) + await plugin.call('filePanel', 'setWorkspace', templateName + "_" + timeStamp) + plugin.verticalIcons.select('filePanel') + _paq.push(['trackEvent', 'homeGetStarted', templateName]) + } + + return ( +
+ +
+ + } + arrows={false} + swipeable={false} + draggable={true} + showDots={false} + responsive={{ desktop: { breakpoint: { max: 3000, min: 1024 }, items: 5} }} + renderButtonGroupOutside={true} + ssr={true} // means to render carousel on server-side. + keyBoardControl={true} + containerClass="carousel-container" + deviceType={"desktop"} + itemClass="w-100" + > + createWorkspace("blank")} /> + createWorkspace("remixDefault")} /> + createWorkspace("ozerc20")} /> + createWorkspace("ozerc721")} /> + createWorkspace("zeroxErc20")} /> + + +
+
+ ) +} + +export default HomeTabGetStarted diff --git a/libs/remix-ui/home-tab/src/lib/components/homeTabLearn.tsx b/libs/remix-ui/home-tab/src/lib/components/homeTabLearn.tsx new file mode 100644 index 0000000000..0fdc6ab5ff --- /dev/null +++ b/libs/remix-ui/home-tab/src/lib/components/homeTabLearn.tsx @@ -0,0 +1,76 @@ +/* eslint-disable @typescript-eslint/no-unused-vars */ +import React, { useEffect, useState, useContext } from 'react' +import { ThemeContext } from '../themeContext' +declare global { + interface Window { + _paq: any + } +} +const _paq = window._paq = window._paq || [] //eslint-disable-line + +enum VisibleTutorial { + Basics, + Intermediate, + Advanced +} +interface HomeTabLearnProps { + plugin: any +} + +function HomeTabLearn ({plugin}: HomeTabLearnProps) { + const [state, setState] = useState<{ + visibleTutorial: VisibleTutorial + }>({ + visibleTutorial: VisibleTutorial.Basics + }) + + const themeFilter = useContext(ThemeContext) + + const openLink = () => { + window.open("https://remix-ide.readthedocs.io/en/latest/remix_tutorials_learneth.html?highlight=learneth#learneth-tutorial-repos", '_blank') + } + + const startLearnEthTutorial = async (tutorial) => { + await plugin.appManager.activatePlugin(['solidity', 'LearnEth', 'solidityUnitTesting']) + plugin.call('LearnEth', 'startTutorial', 'ethereum/remix-workshops', 'master', tutorial) + plugin.verticalIcons.select('LearnEth') + _paq.push(['trackEvent', 'homeTab', 'startLearnEthTutorial', tutorial]) + } + + return ( +
+
+ + +
+
+ +
} + + +
} + + + } + + + + ) +} + +export default HomeTabLearn \ No newline at end of file diff --git a/libs/remix-ui/home-tab/src/lib/components/homeTabScamAlert.tsx b/libs/remix-ui/home-tab/src/lib/components/homeTabScamAlert.tsx new file mode 100644 index 0000000000..1caa8b3499 --- /dev/null +++ b/libs/remix-ui/home-tab/src/lib/components/homeTabScamAlert.tsx @@ -0,0 +1,28 @@ +/* eslint-disable @typescript-eslint/no-unused-vars */ +import React from 'react' + +function HomeTabScamAlert () { + return ( +
+ +
+ + + Scam Alerts: + + + The only URL Remix uses is remix.ethereum.org + + + Beware of online videos promoting "liquidity front runner bots": + Learn more + + + Additional safety tips:  here + +
+
+ ) +} + +export default HomeTabScamAlert diff --git a/libs/remix-ui/home-tab/src/lib/components/homeTabTitle.tsx b/libs/remix-ui/home-tab/src/lib/components/homeTabTitle.tsx new file mode 100644 index 0000000000..5e461df8a1 --- /dev/null +++ b/libs/remix-ui/home-tab/src/lib/components/homeTabTitle.tsx @@ -0,0 +1,148 @@ +/* eslint-disable @typescript-eslint/no-unused-vars */ +// eslint-disable-next-line @nrwl/nx/enforce-module-boundaries +import BasicLogo from 'libs/remix-ui/vertical-icons-panel/src/lib/components/BasicLogo' +import { ThemeContext } from '../themeContext' +import React, { useEffect, useState, useRef, useContext } from 'react' +import { OverlayTrigger, Tooltip } from 'react-bootstrap'// eslint-disable-line + +function HomeTabTitle() { + useEffect(() => { + document.addEventListener("keyup", (e) => handleSearchKeyDown(e)) + return () => { + document.removeEventListener("keyup", handleSearchKeyDown) + } + }, []) + const [state, setState] = useState<{ + searchDisable: boolean + }>({ + searchDisable: true + }) + + const themeFilter = useContext(ThemeContext) + const searchInputRef = useRef(null) + const remiAudioEl = useRef(null) + + const playRemi = async () => { + remiAudioEl.current.play() + } + const handleSearchKeyDown = (e: KeyboardEvent) => { + if (e.target !== searchInputRef.current) return + if (e.key === "Enter") { + openLink() + searchInputRef.current.value = "" + } else { + setState(prevState => { + return { ...prevState, searchDisable: searchInputRef.current.value === "" } + }) + } + } + + const openLink = (url = "") => { + if (url === "") { + window.open("https://remix-ide.readthedocs.io/en/latest/search.html?q=" + searchInputRef.current.value + "&check_keywords=yes&area=default", '_blank') + } else { + window.open(url, '_blank') + } + } + + return ( +
+
+
playRemi()} style={{ filter: themeFilter.filter}} > + +
+ +
+
+ Remix + + + Remix Youtube Playlist + + }> + + + + + Remix Twitter Profile + + }> + + + + + Remix Linkedin Profile + + }> + + + + + Remix Medium Posts + + }> + + + + + Remix Gitter channel + + }> + + + +
+ The Native IDE for Web3 Development. + +
+ + +
+
+ ) +} + +export default HomeTabTitle diff --git a/libs/remix-ui/home-tab/src/lib/components/pluginButton.tsx b/libs/remix-ui/home-tab/src/lib/components/pluginButton.tsx index 06f108afe8..803c9f7ec4 100644 --- a/libs/remix-ui/home-tab/src/lib/components/pluginButton.tsx +++ b/libs/remix-ui/home-tab/src/lib/components/pluginButton.tsx @@ -1,29 +1,43 @@ /* eslint-disable @typescript-eslint/no-unused-vars */ import React, { useContext } from 'react' import { ThemeContext } from '../themeContext' - +import { OverlayTrigger, Tooltip } from 'react-bootstrap' // eslint-disable-line interface PluginButtonProps { imgPath: string, envID: string, envText: string, callback: any, - l2?: boolean + l2?: boolean, + description: string, + remixMaintained?: boolean } -function PluginButton ({ imgPath, envID, envText, callback, l2 }: PluginButtonProps) { +function PluginButton ({ imgPath, envID, envText, callback, l2, description, remixMaintained }: PluginButtonProps) { const themeFilter = useContext(ThemeContext) return ( -
+
- { l2 && } + { l2 && } + { remixMaintained && + + Maintained by Remix + + }> + + + }
) diff --git a/libs/remix-ui/home-tab/src/lib/components/rssFeed.css b/libs/remix-ui/home-tab/src/lib/components/rssFeed.css deleted file mode 100644 index 22e231c0e0..0000000000 --- a/libs/remix-ui/home-tab/src/lib/components/rssFeed.css +++ /dev/null @@ -1,12 +0,0 @@ -.RSSFeed-item img { - width: 100%; -} - -.RSSFeed-item .truncate { - max-height: 500px; - overflow: hidden; -} - -.RSSFeed-item .more-button { - -} \ No newline at end of file diff --git a/libs/remix-ui/home-tab/src/lib/components/rssFeed.tsx b/libs/remix-ui/home-tab/src/lib/components/rssFeed.tsx deleted file mode 100644 index 71df031d2f..0000000000 --- a/libs/remix-ui/home-tab/src/lib/components/rssFeed.tsx +++ /dev/null @@ -1,42 +0,0 @@ -import React, { useState, useEffect } from "react"; -import Parser from "rss-parser"; -import './rssFeed.css'; - -interface RSSFeedProps { - feedUrl: string, - maxItems: number, -} - -export function RSSFeed({ feedUrl, maxItems }: RSSFeedProps) { - const [feed, setFeed] = useState(null); - - useEffect(() => { - const fetchData = async () => { - const parser = new Parser() - const feed = await parser.parseURL(feedUrl); - for (const item of feed.items) { - item.content = item['content:encoded'] - item.date = new Date(item.pubDate).toLocaleDateString('en-US', { - month: 'short', - day: 'numeric' - }) - } - setFeed(feed); - }; - fetchData(); - }, [feedUrl]); - - - return (<> - {feed && feed.items.slice(0, maxItems).map((item: any, index: any) => ( -
-

{item.title}

-

Author: {item.creator}

-

{item.date}

-
- READ MORE -
-
- ))} - ) -} \ No newline at end of file diff --git a/libs/remix-ui/home-tab/src/lib/components/types/carouselTypes.ts b/libs/remix-ui/home-tab/src/lib/components/types/carouselTypes.ts new file mode 100644 index 0000000000..128815d979 --- /dev/null +++ b/libs/remix-ui/home-tab/src/lib/components/types/carouselTypes.ts @@ -0,0 +1,116 @@ +import * as React from "react"; +export interface ResponsiveType { + [key: string]: { + breakpoint: { max: number; min: number }; + items: number; + partialVisibilityGutter?: number; // back-ward compatible, because previously there has been a typo + paritialVisibilityGutter?: number; + slidesToSlide?: number; + }; +} +export function isMouseMoveEvent( + e: React.MouseEvent | React.TouchEvent +): e is React.MouseEvent { + return "clientX" && "clientY" in e; +} +export interface CarouselProps { + responsive: ResponsiveType; + deviceType?: string; + ssr?: boolean; + slidesToSlide?: number; + draggable?: boolean; + arrows?: boolean; // show or hide arrows. + renderArrowsWhenDisabled?: boolean; // Allow for the arrows to have a disabled attribute instead of not showing them + swipeable?: boolean; + removeArrowOnDeviceType?: string | Array; + children: any; + customLeftArrow?: React.ReactElement | null; + customRightArrow?: React.ReactElement | null; + customDot?: React.ReactElement | null; + customButtonGroup?: React.ReactElement | null; + infinite?: boolean; + minimumTouchDrag?: number; // default 50px. The amount of distance to drag / swipe in order to move to the next slide. + afterChange?: (previousSlide: number, state: StateCallBack) => void; // Change callback after sliding everytime. `(previousSlide, currentState) => ...` + beforeChange?: (nextSlide: number, state: StateCallBack) => void; // Change callback before sliding everytime. `(previousSlide, currentState) => ...` + sliderClass?: string; // Use this to style your own track list. + itemClass?: string; // Use this to style your own Carousel item. For example add padding-left and padding-right + itemAriaLabel?: string; // Use this to add your own Carousel item aria-label.if it is not defined the child aria label will be applied if the child dont have one than a default empty string will be applied + containerClass?: string; // Use this to style the whole container. For example add padding to allow the "dots" or "arrows" to go to other places without being overflown. + className?: string; // Use this to style the whole container with styled-components + dotListClass?: string; // Use this to style the dot list. + keyBoardControl?: boolean; + centerMode?: boolean; // show previous and next set of items partially + autoPlay?: boolean; + autoPlaySpeed?: number; // default 3000ms + showDots?: boolean; + renderDotsOutside?: boolean; // show dots outside of the container for custom styling. + renderButtonGroupOutside?: boolean; // show buttonGroup outside of the container for custom styling. + // Show next/previous item partially + // partialVisible has to be used in conjunction with the responsive props, details are in documentation. + // it shows the next set of items partially, different from centerMode as it shows both. + partialVisible?: boolean; + partialVisbile?: boolean; // old typo - deprecated (will be remove in 3.0) + customTransition?: string; + transitionDuration?: number; + // if you are using customTransition, make sure to put the duration here. + // for example, customTransition="all .5" then put transitionDuration as 500. + // this is needed for the resizing to work. + focusOnSelect?: boolean; + additionalTransfrom?: number; // this is only used if you want to add additional transfrom to the current transform + pauseOnHover?: boolean; + shouldResetAutoplay?: boolean; + rewind?: boolean; + rewindWithAnimation?: boolean; + rtl?: boolean; +} + +export type StateCallBack = CarouselInternalState; + +export type Direction = "left" | "right" | "" | undefined; +export type SkipCallbackOptions = + | boolean + | { skipBeforeChange?: boolean; skipAfterChange?: boolean }; +export interface ButtonGroupProps { + previous?: () => void; + next?: () => void; + goToSlide?: (index: number, skipCallbacks?: SkipCallbackOptions) => void; + carouselState?: StateCallBack; +} +export interface ArrowProps { + onClick?: () => void; + carouselState?: StateCallBack; +} +export interface DotProps { + index?: number; + active?: boolean; + onClick?: () => void; + carouselState?: StateCallBack; +} + +export interface CarouselInternalState { + itemWidth: number; + containerWidth: number; + slidesToShow: number; + currentSlide: number; + totalItems: number; + domLoaded: boolean; + deviceType?: string; + transform: number; +} + +export default class Carousel extends React.Component { + previous: (slidesHavePassed: number) => void; + next: (slidesHavePassed: number) => void; + goToSlide: (slide: number, skipCallbacks?: SkipCallbackOptions) => void; + state: CarouselInternalState; + setClones: ( + slidesToShow: number, + itemWidth?: number, + forResizing?: boolean + ) => void; // reset carousel in infinite mode. + setItemsToShow: (shouldCorrectItemPosition?: boolean) => void; // reset carousel in non-infinite mode. + correctClonesPosition: ({ domLoaded }: { domLoaded: boolean }) => void; + onMove: boolean; + direction: Direction; + containerRef: React.RefObject; +} diff --git a/libs/remix-ui/home-tab/src/lib/components/workspaceTemplate.tsx b/libs/remix-ui/home-tab/src/lib/components/workspaceTemplate.tsx new file mode 100644 index 0000000000..ad079053a8 --- /dev/null +++ b/libs/remix-ui/home-tab/src/lib/components/workspaceTemplate.tsx @@ -0,0 +1,28 @@ +/* eslint-disable @typescript-eslint/no-unused-vars */ +import React, { useContext } from 'react' +interface WorkspaceTemplateProps { + gsID: string, + workspaceTitle: string, + callback: any, + description: string, +} + +function WorkspaceTemplate ({ gsID, workspaceTitle, description, callback }: WorkspaceTemplateProps) { + + return ( +
+ +
+ ) +} + +export default WorkspaceTemplate diff --git a/libs/remix-ui/home-tab/src/lib/remix-ui-home-tab.css b/libs/remix-ui/home-tab/src/lib/remix-ui-home-tab.css index 4464c8d2fa..d6ced30e67 100644 --- a/libs/remix-ui/home-tab/src/lib/remix-ui-home-tab.css +++ b/libs/remix-ui/home-tab/src/lib/remix-ui-home-tab.css @@ -31,7 +31,7 @@ text-align: center; } .remixui_home_logoImg { - height: 10em; + height: 4rem; } .remixui_home_rightPanel { right: 0; @@ -58,19 +58,37 @@ .remixui_home_importFrom p { margin-right: 10px; } -.remixui_home_logoContainer img{ +.remixui_home_logoContainer img { height: 150px; opacity: 0.7; } .remixui_home_envLogo { - height: 16px; + height: 2.5rem; +} +.remixui_home_envLogoDescription { + white-space: pre-wrap; + font-size: small; + line-height: 0.9rem; + text-align: left; +} +.remixui_home_gtDescription { + white-space: pre-wrap; + font-size: small; + line-height: 1.1rem; + text-align: left; } .remixui_home_cursorStyle { cursor: pointer; + font-size: 0.8rem; } .remixui_home_envButton { - width: 120px; - height: 70px; + width: 220px; + cursor: pointer; + height: 130px; +} +.remixui_home_workspaceTemplate { + width: 220px; + height: 80px; } .remixui_home_media { overflow: hidden; @@ -82,5 +100,10 @@ width: 100px; } .remixui_home_l2Label { - bottom: 10px; + top: 120px; + right: 180px; +} +.remixui_home_maintainedLabel { + top: 120px; + right: 180px; } diff --git a/libs/remix-ui/home-tab/src/lib/remix-ui-home-tab.tsx b/libs/remix-ui/home-tab/src/lib/remix-ui-home-tab.tsx index 6fe4d449cc..e7002db1e6 100644 --- a/libs/remix-ui/home-tab/src/lib/remix-ui-home-tab.tsx +++ b/libs/remix-ui/home-tab/src/lib/remix-ui-home-tab.tsx @@ -1,90 +1,34 @@ -import React, { useState, useRef, useEffect, useReducer } from 'react' // eslint-disable-line -import { FormattedMessage } from 'react-intl' +import React, { useState, useEffect } from 'react' // eslint-disable-line + import './remix-ui-home-tab.css' -import { ModalDialog } from '@remix-ui/modal-dialog' // eslint-disable-line -import { Toaster } from '@remix-ui/toaster' // eslint-disable-line -import PluginButton from './components/pluginButton' // eslint-disable-line import { ThemeContext, themes } from './themeContext' -import { RSSFeed } from './components/rssFeed' +import HomeTabTitle from './components/homeTabTitle' +import HomeTabFile from './components/homeTabFile' +import HomeTabLearn from './components/homeTabLearn' +import HomeTabScamAlert from './components/homeTabScamAlert' +import HomeTabGetStarted from './components/homeTabGetStarted' +import HomeTabFeatured from './components/homeTabFeatured' +import HomeTabFeaturedPlugins from './components/homeTabFeaturedPlugins' + declare global { interface Window { _paq: any } } -const _paq = window._paq = window._paq || [] //eslint-disable-line -/* eslint-disable-next-line */ export interface RemixUiHomeTabProps { plugin: any } -const loadingInitialState = { - tooltip: '', - showModalDialog: false, - importSource: '' -} - -const loadingReducer = (state = loadingInitialState, action) => { - return { ...state, tooltip: action.tooltip, showModalDialog: false, importSource: '' } -} - export const RemixUiHomeTab = (props: RemixUiHomeTabProps) => { const { plugin } = props - const fileManager = plugin.fileManager const [state, setState] = useState<{ themeQuality: { filter: string, name: string }, - showMediaPanel: 'none' | 'twitter' | 'medium', - showModalDialog: boolean, - modalInfo: { title: string, loadItem: string, examples: Array }, - importSource: string, - toasterMsg: string }>({ themeQuality: themes.light, - showMediaPanel: 'none', - showModalDialog: false, - modalInfo: { title: '', loadItem: '', examples: [] }, - importSource: '', - toasterMsg: '' }) - const processLoading = () => { - const contentImport = plugin.contentImport - const workspace = fileManager.getProvider('workspace') - contentImport.import( - state.importSource, - (loadingMsg) => dispatch({ tooltip: loadingMsg }), - async (error, content, cleanUrl, type, url) => { - if (error) { - toast(error.message || error) - } else { - try { - if (await workspace.exists(type + '/' + cleanUrl)) toast('File already exists in workspace') - else { - workspace.addExternal(type + '/' + cleanUrl, content, url) - plugin.call('menuicons', 'select', 'filePanel') - } - } catch (e) { - toast(e.message) - } - } - } - ) - setState(prevState => { - return { ...prevState, showModalDialog: false, importSource: '' } - }) - } - - const [, dispatch] = useReducer(loadingReducer, loadingInitialState) - - const playRemi = async () => { - remiAudioEl.current.play() - } - - const remiAudioEl = useRef(null) - const inputValue = useRef(null) - const rightPanel = useRef(null) - useEffect(() => { plugin.call('theme', 'currentTheme').then((theme) => { // update theme quality. To be used for for images @@ -98,276 +42,24 @@ export const RemixUiHomeTab = (props: RemixUiHomeTabProps) => { return { ...prevState, themeQuality: theme.quality === 'dark' ? themes.dark : themes.light } }) }) - window.addEventListener('click', (event) => { - const target = event.target as Element - const id = target.id - if (id !== 'remixIDEHomeTwitterbtn' && id !== 'remixIDEHomeMediumbtn' && !rightPanel.current.contains(event.target)) { - // todo check event.target - setState(prevState => { return { ...prevState, showMediaPanel: 'none' } }) - } - }) - // to retrieve twitter feed - const scriptTwitter = document.createElement('script') - scriptTwitter.src = 'https://platform.twitter.com/widgets.js' - scriptTwitter.async = true - document.body.appendChild(scriptTwitter) - return () => { - document.body.removeChild(scriptTwitter) - } }, []) - const toast = (message: string) => { - setState(prevState => { - return { ...prevState, toasterMsg: message } - }) - } - - const createNewFile = async () => { - plugin.verticalIcons.select('filePanel') - await plugin.call('filePanel', 'createNewFile') - } - - const uploadFile = async (target) => { - await plugin.call('filePanel', 'uploadFile', target) - } - - const connectToLocalhost = () => { - plugin.appManager.activatePlugin('remixd') - } - const importFromGist = () => { - plugin.call('gistHandler', 'load', '') - plugin.verticalIcons.select('filePanel') - } - const startSolidity = async () => { - await plugin.appManager.activatePlugin(['solidity', 'udapp', 'solidityStaticAnalysis', 'solidityUnitTesting']) - plugin.verticalIcons.select('solidity') - _paq.push(['trackEvent', 'pluginManager', 'userActivate', 'solidity']) - } - const startStarkNet = async () => { - await plugin.appManager.activatePlugin('starkNet_compiler') - plugin.verticalIcons.select('starkNet_compiler') - _paq.push(['trackEvent', 'pluginManager', 'userActivate', 'starkNet_compiler']) - } - const startSolhint = async () => { - await plugin.appManager.activatePlugin(['solidity', 'solhint']) - plugin.verticalIcons.select('solhint') - _paq.push(['trackEvent', 'pluginManager', 'userActivate', 'solhint']) - } - const startLearnEth = async () => { - await plugin.appManager.activatePlugin(['solidity', 'LearnEth', 'solidityUnitTesting']) - plugin.verticalIcons.select('LearnEth') - _paq.push(['trackEvent', 'pluginManager', 'userActivate', 'learnEth']) - } - const startSourceVerify = async () => { - await plugin.appManager.activatePlugin(['solidity', 'sourcify']) - plugin.verticalIcons.select('sourcify') - _paq.push(['trackEvent', 'pluginManager', 'userActivate', 'sourcify']) - } - const startPluginManager = async () => { - plugin.verticalIcons.select('pluginManager') - } - - const showFullMessage = (title: string, loadItem: string, examples: Array) => { - setState(prevState => { - return { ...prevState, showModalDialog: true, modalInfo: { title: title, loadItem: loadItem, examples: examples } } - }) - } - - const hideFullMessage = () => { //eslint-disable-line - setState(prevState => { - return { ...prevState, showModalDialog: false, importSource: '' } - }) - } - - const maxHeight = Math.max(window.innerHeight - 150, 250) + 'px' - const examples = state.modalInfo.examples.map((urlEl, key) => ()) - const elHeight = '4000px' return ( - <> - hideFullMessage() } - okFn={ () => processLoading() } - > -
- { state.modalInfo.loadItem !== '' && Enter the { state.modalInfo.loadItem } you would like to load. } - { state.modalInfo.examples.length !== 0 && - <> -
e.g
-
- { examples } -
- } - { - setState(prevState => { - return { ...prevState, importSource: inputValue.current.value } - }) - }} - /> -
-
- -
-
-
-
-
- -
-
- - - : - - - - - - : - - - - :   - -
-
-
- playRemi() } alt=""> - -
-
-
-
-
-
-

-
- - startSolidity()} /> - startStarkNet()} /> - startSolhint()} /> - startLearnEth()} /> - startSourceVerify()} /> - - -
-
-
-
-

-

- - -

-

- - - { - event.stopPropagation() - plugin.verticalIcons.select('filePanel') - uploadFile(event.target) - }} multiple /> -

-

- - -

-

-
- - - - -
-
-
-

-

- - Documentation -

-

- - Gitter channel -

-

- - Featuring website -

-
-
-
+
+ +
+ + +
-
-
- - -
-
-
- -
- -
+
+ + + +
-
- +
+
) } diff --git a/libs/remix-ui/tabs/src/lib/remix-ui-tabs.css b/libs/remix-ui/tabs/src/lib/remix-ui-tabs.css index 7df5e07993..6fd84e1a27 100644 --- a/libs/remix-ui/tabs/src/lib/remix-ui-tabs.css +++ b/libs/remix-ui/tabs/src/lib/remix-ui-tabs.css @@ -33,6 +33,7 @@ .iconImage { width: 1rem; height: 1rem; + align-self: start; } .active { border: 1px solid transparent; diff --git a/libs/remix-ui/vertical-icons-panel/src/lib/components/BasicLogo.tsx b/libs/remix-ui/vertical-icons-panel/src/lib/components/BasicLogo.tsx index a470ce8541..6c0b41331d 100644 --- a/libs/remix-ui/vertical-icons-panel/src/lib/components/BasicLogo.tsx +++ b/libs/remix-ui/vertical-icons-panel/src/lib/components/BasicLogo.tsx @@ -1,12 +1,20 @@ import React from 'react' +interface BasicLogoProps { + classList?: string, + solid?: boolean +} -function BasicLogo () { - return ( - - - - - ) +function BasicLogo ({classList = "", solid = true}: BasicLogoProps) { + if (solid) { + return ( + + + + + ) + } else { + return () + } } -export default BasicLogo +export default BasicLogo \ No newline at end of file diff --git a/libs/remix-ui/vertical-icons-panel/src/lib/remix-ui-vertical-icons-panel.tsx b/libs/remix-ui/vertical-icons-panel/src/lib/remix-ui-vertical-icons-panel.tsx index 3d4413eb4d..9d45b6f335 100644 --- a/libs/remix-ui/vertical-icons-panel/src/lib/remix-ui-vertical-icons-panel.tsx +++ b/libs/remix-ui/vertical-icons-panel/src/lib/remix-ui-vertical-icons-panel.tsx @@ -31,8 +31,6 @@ const RemixUiVerticalIconsPanel = ({ const [activateScroll, dispatchScrollAction] = useReducer(verticalScrollReducer, initialState) const [theme, setTheme] = useState('dark') - - const evaluateScrollability = () => { dispatchScrollAction({ type: 'resize', diff --git a/libs/remix-ui/workspace/src/lib/components/file-explorer.tsx b/libs/remix-ui/workspace/src/lib/components/file-explorer.tsx index 131babab53..64f52d0630 100644 --- a/libs/remix-ui/workspace/src/lib/components/file-explorer.tsx +++ b/libs/remix-ui/workspace/src/lib/components/file-explorer.tsx @@ -460,6 +460,8 @@ export const FileExplorer = (props: FileExplorerProps) => { handleClickFolder={handleClickFolder} handleContextMenu={handleContextMenu} key={index} + showIconsMenu={props.showIconsMenu} + hideIconsMenu={props.hideIconsMenu} />) } diff --git a/libs/remix-ui/workspace/src/lib/components/file-render.tsx b/libs/remix-ui/workspace/src/lib/components/file-render.tsx index 5535290c0a..9d906a34d4 100644 --- a/libs/remix-ui/workspace/src/lib/components/file-render.tsx +++ b/libs/remix-ui/workspace/src/lib/components/file-render.tsx @@ -20,6 +20,8 @@ export interface RenderFileProps { focusContext: { element: string, x: number, y: number, type: string }, ctrlKey: boolean, expandPath: string[], + hideIconsMenu?: React.Dispatch>, + showIconsMenu?: boolean, editModeOff: (content: string) => void, handleClickFolder: (path: string, type: string) => void, handleClickFile: (path: string, type: string) => void, @@ -52,11 +54,13 @@ export const FileRender = (props: RenderFileProps) => { const handleFolderClick = (event: SyntheticEvent) => { event.stopPropagation() if (props.focusEdit.element !== file.path) props.handleClickFolder(file.path, file.type) + if (props.showIconsMenu === true) props.hideIconsMenu(!props.showIconsMenu) } const handleFileClick = (event: SyntheticEvent) => { event.stopPropagation() if (props.focusEdit.element !== file.path) props.handleClickFile(file.path, file.type) + if (props.showIconsMenu === true) props.hideIconsMenu(!props.showIconsMenu) } const handleContextMenu = (event: PointerEvent) => { diff --git a/libs/remix-ui/workspace/src/lib/css/remix-ui-workspace.css b/libs/remix-ui/workspace/src/lib/css/remix-ui-workspace.css index 44e67f30fd..55357dacae 100644 --- a/libs/remix-ui/workspace/src/lib/css/remix-ui-workspace.css +++ b/libs/remix-ui/workspace/src/lib/css/remix-ui-workspace.css @@ -41,7 +41,7 @@ } .remixui_treeview { overflow-y : auto; - } + } .remixui_dialog { display: flex; flex-direction: column; @@ -51,7 +51,7 @@ word-break: break-word; } .remixui_menuicon { - padding-right : 10px; + padding-right : 0px; } .remixui_menuicon:hover { transform: scale(1.3); @@ -61,7 +61,7 @@ align-items: center; height: 32px; } - + .remixui_cloneContainer input { height: 32px; border-top-left-radius: 0 !important; @@ -78,7 +78,7 @@ .remixui_menuicon .bs-popover-auto[x-placement^="bottom"] > .arrow::after, .bs-popover-bottom > .arrow::after { border-bottom-color: var(--dark) !important } - + .custom-dropdown-items { padding: 0.25rem 0.25rem; border-radius: .25rem; @@ -94,4 +94,42 @@ width: auto; color: var(--text); } - \ No newline at end of file + + .remixuimenuicon_shadow { + } + + .remixuimenuicon_shadow:hover { + box-shadow: 0px 0px 14px -7px; + } + + .remixui_topmenu { + padding-bottom: 0.1rem; + } + + .remixui_menuwidth { + width: 8rem; + } + + #workspacesMenuDropdown > div.custom-dropdown-items { + min-width: 8rem; + } + + .remixui_menuhr{ + + } + + #workspacesMenuDropdown>div>ul>a:nth-child(6):hover { + background-color: rgba(0,0,0,0) + } + + #workspacesMenuDropdown>div>ul>a:nth-child(4):hover { + background-color: rgba(0, 0, 0, 0) + } + + #workspacesMenuDropdown > div > ul > a:hover { + background-color: var(--secondary); + /* border: 1px solid var(--secondary); */ + border-radius: 2px; + color: var(--text) + } + diff --git a/libs/remix-ui/workspace/src/lib/remix-ui-workspace.tsx b/libs/remix-ui/workspace/src/lib/remix-ui-workspace.tsx index 88ab906267..3ebd3f5fd3 100644 --- a/libs/remix-ui/workspace/src/lib/remix-ui-workspace.tsx +++ b/libs/remix-ui/workspace/src/lib/remix-ui-workspace.tsx @@ -1,7 +1,7 @@ -import React, { useState, useEffect, useRef, useContext } from 'react' // eslint-disable-line +import React, { useState, useEffect, useRef, useContext, SyntheticEvent } from 'react' // eslint-disable-line import { FormattedMessage, useIntl } from 'react-intl' -import { Dropdown } from 'react-bootstrap' -import { CustomMenu, CustomToggle } from '@remix-ui/helper' +import { Dropdown, OverlayTrigger, Tooltip } from 'react-bootstrap' +import { CustomIconsToggle, CustomMenu, CustomToggle } from '@remix-ui/helper' import { FileExplorer } from './components/file-explorer' // eslint-disable-line import { FileSystemContext } from './contexts' import './css/remix-ui-workspace.css' @@ -16,6 +16,7 @@ export function Workspace () { const [currentWorkspace, setCurrentWorkspace] = useState(NO_WORKSPACE) const [selectedWorkspace, setSelectedWorkspace] = useState<{ name: string, isGitRepo: boolean}>(null) const [showDropdown, setShowDropdown] = useState(false) + const [showIconsMenu, hideIconsMenu] = useState(false) const displayOzCustomRef = useRef() const mintableCheckboxRef = useRef() const burnableCheckboxRef = useRef() @@ -312,91 +313,267 @@ export function Workspace () { ) } + const workspaceMenuIcons = [ + + Create + + } + > +
{ + e.stopPropagation() + createWorkspace() + _paq.push(['trackEvent', 'fileExplorer', 'workspaceMenu', 'workspaceCreate']) + hideIconsMenu(!showIconsMenu) + }} + > + + Create +
+
, + + Delete Workspace + + } + > +
{ + e.stopPropagation() + deleteCurrentWorkspace() + _paq.push(['trackEvent', 'fileExplorer', 'workspaceMenu', 'workspaceDelete']) + hideIconsMenu(!showIconsMenu) + }} + > + + {'Delete'} +
+
, + + Rename Workspace + + } + > +
{ + e.stopPropagation() + renameCurrentWorkspace() + _paq.push(['trackEvent', 'fileExplorer', 'workspaceMenu', 'workspaceRename']) + hideIconsMenu(!showIconsMenu) + }} + data-id='workspaceRename' + > + + {'Rename'} +
+
, + , + + Clone Git Repository + + } + > +
{ + e.stopPropagation() + cloneGitRepository() + _paq.push(['trackEvent', 'fileExplorer', 'workspaceMenu', 'cloneGitRepository']) + hideIconsMenu(!showIconsMenu) + }} + > + + {'Clone'} +
+
, + , + + Download Workspace + + } + > +
{ + e.stopPropagation() + downloadWorkspaces() + _paq.push(['trackEvent', 'fileExplorer', 'workspaceMenu', 'workspacesDownload']) + hideIconsMenu(!showIconsMenu) + }} + > + + {'Download'} +
+
, + + Restore Workspace Backup + + } + > +
{ + e.stopPropagation() + restoreBackup() + _paq.push(['trackEvent', 'fileExplorer', 'workspaceMenu', 'workspacesRestore']) + hideIconsMenu(!showIconsMenu) + }} + > + + {'Restore'} +
+
, + ] + return (
-
- - - - - - -