Merge pull request #4721 from ethereum/gridView

Grid view + Remix Guide Plugin
pull/4754/head
yann300 7 months ago committed by GitHub
commit 878a875ff1
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
  1. 6
      apps/remix-ide/src/app.js
  2. 244
      apps/remix-ide/src/app/plugins/remixGuide.tsx
  3. 13
      apps/remix-ide/src/assets/css/themes/bootstrap-cerulean.min.css
  4. 15
      apps/remix-ide/src/assets/css/themes/bootstrap-cyborg.min.css
  5. 13
      apps/remix-ide/src/assets/css/themes/bootstrap-flatly.min.css
  6. 13
      apps/remix-ide/src/assets/css/themes/bootstrap-spacelab.min.css
  7. 1
      apps/remix-ide/src/assets/css/themes/remix-black_undtds.css
  8. 14
      apps/remix-ide/src/assets/css/themes/remix-candy_ikhg4m.css
  9. 1
      apps/remix-ide/src/assets/css/themes/remix-dark_tvx1s2.css
  10. 1
      apps/remix-ide/src/assets/css/themes/remix-hacker_owl.css
  11. 12
      apps/remix-ide/src/assets/css/themes/remix-light_powaqg.css
  12. 12
      apps/remix-ide/src/assets/css/themes/remix-midcentury_hrzph3.css
  13. 12
      apps/remix-ide/src/assets/css/themes/remix-unicorn.css
  14. 12
      apps/remix-ide/src/assets/css/themes/remix-violet.css
  15. 3
      apps/remix-ide/src/remixAppManager.js
  16. 4
      libs/remix-ui/grid-view/src/index.ts
  17. 42
      libs/remix-ui/grid-view/src/lib/components/customCheckbox.tsx
  18. 18
      libs/remix-ui/grid-view/src/lib/filtersContext.tsx
  19. 46
      libs/remix-ui/grid-view/src/lib/remix-ui-grid-cell.css
  20. 86
      libs/remix-ui/grid-view/src/lib/remix-ui-grid-cell.tsx
  21. 26
      libs/remix-ui/grid-view/src/lib/remix-ui-grid-section.css
  22. 38
      libs/remix-ui/grid-view/src/lib/remix-ui-grid-section.tsx
  23. 25
      libs/remix-ui/grid-view/src/lib/remix-ui-grid-view.css
  24. 147
      libs/remix-ui/grid-view/src/lib/remix-ui-grid-view.tsx
  25. 16
      libs/remix-ui/grid-view/src/lib/themeContext.tsx
  26. 2
      libs/remix-ui/home-tab/src/lib/remix-ui-home-tab.tsx
  27. 2
      libs/remix-ui/static-analyser/src/staticanalyser.d.ts
  28. 9
      tsconfig.paths.json

@ -42,6 +42,7 @@ import { CodeFormat } from './app/plugins/code-format'
import { SolidityUmlGen } from './app/plugins/solidity-umlgen'
import { CompilationDetailsPlugin } from './app/plugins/compile-details'
import { VyperCompilationDetailsPlugin } from './app/plugins/vyper-compilation-details'
import { RemixGuidePlugin } from './app/plugins/remixGuide'
import { ContractFlattener } from './app/plugins/contractFlattener'
import { TemplatesPlugin } from './app/plugins/remix-templates'
import { fsPlugin } from './app/plugins/electron/fsPlugin'
@ -222,6 +223,10 @@ class AppComponent {
// ----------------- Compilation Details ----------------------------
const compilationDetails = new CompilationDetailsPlugin(appManager)
const vyperCompilationDetails = new VyperCompilationDetailsPlugin(appManager)
// ----------------- Remix Guide ----------------------------
const remixGuide = new RemixGuidePlugin(appManager)
// ----------------- ContractFlattener ----------------------------
const contractFlattener = new ContractFlattener()
@ -340,6 +345,7 @@ class AppComponent {
solidityumlgen,
compilationDetails,
vyperCompilationDetails,
remixGuide,
contractFlattener,
solidityScript,
templates,

@ -0,0 +1,244 @@
import React from 'react'
import { ViewPlugin } from '@remixproject/engine-web'
import { PluginViewWrapper } from '@remix-ui/helper'
import { RemixAppManager } from '../../remixAppManager'
import { RemixUIGridView } from '@remix-ui/remix-ui-grid-view'
import { RemixUIGridSection } from '@remix-ui/remix-ui-grid-section'
import { RemixUIGridCell } from '@remix-ui/remix-ui-grid-cell'
import { ThemeKeys, ThemeObject } from '@microlink/react-json-view'
//@ts-ignore
const _paq = (window._paq = window._paq || [])
const profile = {
name: 'remixGuide',
displayName: 'Remix Guide',
description: 'Learn remix with videos',
location: 'mainPanel',
methods: ['showDetails'],
events: []
}
export class RemixGuidePlugin extends ViewPlugin {
dispatch: React.Dispatch<any> = () => { }
appManager: RemixAppManager
element: HTMLDivElement
payload: any
themeStyle: any
theme: ThemeKeys | ThemeObject
constructor(appManager: RemixAppManager) {
super(profile)
this.appManager = appManager
this.element = document.createElement('div')
this.element.setAttribute('id', 'remixGuideEl')
}
async onActivation() {
this.handleThemeChange()
await this.call('tabs', 'focus', 'remixGuide')
this.renderComponent()
_paq.push(['trackEvent', 'plugin', 'activated', 'remixGuide'])
}
onDeactivation(): void {
}
async showDetails(sentPayload: any) {
const contractName = Object.entries(sentPayload).find(([key, value]) => key)
await this.call('tabs', 'focus', 'remixGuide')
this.profile.displayName = `${contractName[0]}`
this.payload = sentPayload
const active = await this.call('theme', 'currentTheme')
this.renderComponent()
}
private handleThemeChange() {
this.on('theme', 'themeChanged', (theme: any) => {
this.renderComponent()
})
}
setDispatch(dispatch: React.Dispatch<any>): void {
this.dispatch = dispatch
this.renderComponent()
}
render() {
return (
<div className="bg-dark" id="remixGuide">
<PluginViewWrapper plugin={this} />
</div>
)
}
renderComponent() {
this.dispatch({
...this,
...this.payload,
themeStyle: this.themeStyle,
theme: this.theme
})
}
updateComponent(state: any) {
return (
<RemixUIGridView
plugin={this}
styleList={""}
logo='/assets/img/YouTubeLogo.webp'
enableFilter={true}
showUntagged={true}
showPin={true}
tagList={[
['beginner', 'danger'],
['advanced', 'warning'],
['AI', 'success'],
['plugins', 'secondary'],
['solidity', 'primary'],
['vyper', 'info'],
['L2', 'danger']
]}
title='Remix Guide'
description="Streamlined access to categorized video tutorials for mastering Remix IDE. From fundamentals to advanced techniques, level up your development skills with ease."
//themeStyle={state.themeStyle}
>
<RemixUIGridSection
plugin={this}
title='Basics'
hScrollable= {true}
>
<RemixUIGridCell
plugin={this}
title="first item"
tagList={['L2', 'AI']}
logo='/assets/img/soliditySurvey2023.webp'
>
<img src={'/assets/img/soliditySurvey2023.webp'} style={{height: '70px', width: '70px'}} alt=""></img>
</RemixUIGridCell>
<RemixUIGridCell
plugin={this}
title="next"
pinned={true}
tagList={['L2', 'plugins']}
>
<img src={'/assets/img/soliditySurvey2023.webp'} style={{height: '70px', width: '70px'}} alt=""></img>
</RemixUIGridCell> <RemixUIGridCell
plugin={this}
title="something"
pinned={false}
tagList={['solidity', 'plugins']}
>
<img src={'/assets/img/soliditySurvey2023.webp'} style={{height: '70px', width: '70px'}} alt=""></img>
</RemixUIGridCell>
<RemixUIGridCell
plugin={this}
title="1"
tagList={['solidity']}
>
<img src={'/assets/img/soliditySurvey2023.webp'} style={{height: '70px', width: '70px'}} alt=""></img>
</RemixUIGridCell> <RemixUIGridCell
plugin={this}
title="1"
>
<img src={'/assets/img/soliditySurvey2023.webp'} style={{height: '70px', width: '70px'}} alt=""></img>
</RemixUIGridCell>
<RemixUIGridCell
plugin={this}
title="Something very very long"
>
<img src={'/assets/img/soliditySurvey2023.webp'} style={{height: '70px', width: '70px'}} alt=""></img>
</RemixUIGridCell> <RemixUIGridCell
plugin={this}
title="1"
>
<img src={'/assets/img/soliditySurvey2023.webp'} style={{height: '70px', width: '70px'}} alt=""></img>
</RemixUIGridCell>
<RemixUIGridCell
plugin={this}
title="1"
>
<img src={'/assets/img/soliditySurvey2023.webp'} style={{height: '70px', width: '70px'}} alt=""></img>
</RemixUIGridCell>
<RemixUIGridCell
plugin={this}
title="1"
>
<img src={'/assets/img/soliditySurvey2023.webp'} style={{height: '70px', width: '70px'}} alt=""></img>
</RemixUIGridCell>
<RemixUIGridCell
plugin={this}
title="1"
>
<img src={'/assets/img/soliditySurvey2023.webp'} style={{height: '70px', width: '70px'}} alt=""></img>
</RemixUIGridCell>
</RemixUIGridSection>
<RemixUIGridSection
plugin={this}
title='Basics not scrollable'
hScrollable= {false}
>
<RemixUIGridCell
plugin={this}
title="first item"
logo='/assets/img/soliditySurvey2023.webp'
>
<img src={'/assets/img/soliditySurvey2023.webp'} style={{height: '70px', width: '70px'}} alt=""></img>
</RemixUIGridCell>
<RemixUIGridCell
plugin={this}
title="next"
>
<img src={'/assets/img/soliditySurvey2023.webp'} style={{height: '70px', width: '70px'}} alt=""></img>
</RemixUIGridCell> <RemixUIGridCell
plugin={this}
title="something"
>
<img src={'/assets/img/soliditySurvey2023.webp'} style={{height: '70px', width: '70px'}} alt=""></img>
</RemixUIGridCell>
<RemixUIGridCell
plugin={this}
title="1"
>
<img src={'/assets/img/soliditySurvey2023.webp'} style={{height: '70px', width: '70px'}} alt=""></img>
</RemixUIGridCell> <RemixUIGridCell
plugin={this}
title="1"
>
<img src={'/assets/img/soliditySurvey2023.webp'} style={{height: '70px', width: '70px'}} alt=""></img>
</RemixUIGridCell>
<RemixUIGridCell
plugin={this}
title="1"
>
<img src={'/assets/img/soliditySurvey2023.webp'} style={{height: '70px', width: '70px'}} alt=""></img>
</RemixUIGridCell> <RemixUIGridCell
plugin={this}
title="1"
>
<img src={'/assets/img/soliditySurvey2023.webp'} style={{height: '70px', width: '70px'}} alt=""></img>
</RemixUIGridCell>
<RemixUIGridCell
plugin={this}
title="1"
>
<img src={'/assets/img/soliditySurvey2023.webp'} style={{height: '70px', width: '70px'}} alt=""></img>
</RemixUIGridCell>
<RemixUIGridCell
plugin={this}
title="1"
>
<img src={'/assets/img/soliditySurvey2023.webp'} style={{height: '70px', width: '70px'}} alt=""></img>
</RemixUIGridCell>
<RemixUIGridCell
plugin={this}
title="1"
>
<img src={'/assets/img/soliditySurvey2023.webp'} style={{height: '70px', width: '70px'}} alt=""></img>
</RemixUIGridCell>
</RemixUIGridSection>
</RemixUIGridView>
)
}
}

@ -66,6 +66,19 @@ body {
text-align:left;
background-color:#fff
}
*::-webkit-scrollbar {
width: 8px;
height: 6px;
background-color: var(--body-bg);
}
*::-webkit-scrollbar-thumb {
background-color: var(--secondary);
opacity: 0.3;
border-radius: 30px;
}
[tabindex="-1"]:focus:not(:focus-visible) {
outline:0!important
}

@ -67,6 +67,19 @@ body {
text-align:left;
background-color:#060606
}
*::-webkit-scrollbar {
width: 8px;
height: 6px;
background-color: var(--body-bg);
}
*::-webkit-scrollbar-thumb {
background-color: var(--secondary);
opacity: 0.3;
border-radius: 30px;
}
[tabindex="-1"]:focus:not(:focus-visible) {
outline:0!important
}
@ -5635,7 +5648,7 @@ a.bg-light:focus,a.bg-light:hover,button.bg-light:focus,button.bg-light:hover {
background-color:#090909!important
}
.bg-dark {
background-color:#adafae!important
background-color:#2f3130!important
}
a.bg-dark:focus,a.bg-dark:hover,button.bg-dark:focus,button.bg-dark:hover {
background-color:#939695!important

@ -65,6 +65,19 @@ body {
text-align:left;
background-color:#fff
}
*::-webkit-scrollbar {
width: 8px;
height: 6px;
background-color: var(--body-bg);
}
*::-webkit-scrollbar-thumb {
background-color: var(--secondary);
opacity: 0.3;
border-radius: 30px;
}
[tabindex="-1"]:focus:not(:focus-visible) {
outline:0!important
}

@ -69,6 +69,19 @@ body {
text-align:left;
background-color:#fff
}
*::-webkit-scrollbar {
width: 8px;
height: 6px;
background-color: var(--body-bg);
}
*::-webkit-scrollbar-thumb {
background-color: var(--secondary);
opacity: 0.3;
border-radius: 30px;
}
[tabindex="-1"]:focus:not(:focus-visible) {
outline:0!important
}

@ -74,6 +74,7 @@ body {
}
*::-webkit-scrollbar {
width: 8px;
height: 6px;
}
*::-webkit-scrollbar-thumb {
background-color: #37373b;

@ -14,7 +14,7 @@
--gray: #6c757d;
--gray-dark: #343a40;
--primary: #fc58a3;
--secondary: #e2f5f2;
--secondary: #c7e3de;
--success: #24b882;
--info: #00bbff;
--warning: #fabe33;
@ -76,6 +76,18 @@ body {
background-color: var(--body-bg);
}
*::-webkit-scrollbar {
width: 8px;
height: 6px;
background-color: var(--body-bg);
}
*::-webkit-scrollbar-thumb {
background-color: var(--secondary);
opacity: 0.3;
border-radius: 30px;
}
[tabindex="-1"]:focus {
outline: 0 !important;
}

@ -74,6 +74,7 @@ body {
}
*::-webkit-scrollbar {
width: 8px;
height: 6px;
}
*::-webkit-scrollbar-thumb {
background-color: #41455b;

@ -91,6 +91,7 @@ body {
}
*::-webkit-scrollbar {
width: 8px;
height: 6px;
}
*::-webkit-scrollbar-thumb {
background-color: #41455b;

@ -76,6 +76,18 @@ body {
background-color: var(--body-bg);
}
*::-webkit-scrollbar {
width: 8px;
height: 6px;
background-color: var(--body-bg);
}
*::-webkit-scrollbar-thumb {
background-color: var(--secondary);
opacity: 0.3;
border-radius: 30px;
}
[tabindex="-1"]:focus {
outline: 0 !important;
}

@ -76,6 +76,18 @@ body {
background-color: var(--body-bg);
}
*::-webkit-scrollbar {
width: 8px;
height: 6px;
background-color: var(--body-bg);
}
*::-webkit-scrollbar-thumb {
background-color: var(--secondary);
opacity: 0.3;
border-radius: 30px;
}
[tabindex="-1"]:focus {
outline: 0 !important;
}

@ -76,6 +76,18 @@ body {
background-color: var(--body-bg);
}
*::-webkit-scrollbar {
width: 8px;
height: 6px;
background-color: var(--body-bg);
}
*::-webkit-scrollbar-thumb {
background-color: var(--secondary);
opacity: 0.3;
border-radius: 30px;
}
[tabindex="-1"]:focus {
outline: 0 !important;
}

@ -76,6 +76,18 @@ body {
background-color: var(--body-bg);
}
*::-webkit-scrollbar {
width: 8px;
height: 6px;
background-color: var(--body-bg);
}
*::-webkit-scrollbar-thumb {
background-color: var(--secondary);
opacity: 0.3;
border-radius: 30px;
}
[tabindex="-1"]:focus {
outline: 0 !important;
}

@ -127,7 +127,8 @@ export function isNative(name) {
'doc-viewer',
'circuit-compiler',
'compilationDetails',
'vyperCompilationDetails'
'vyperCompilationDetails',
'remixGuide',
]
return nativePlugins.includes(name) || requiredModules.includes(name)
}

@ -0,0 +1,4 @@
export * from './lib/remix-ui-grid-view'
export * from './lib/remix-ui-grid-section'
export * from './lib/remix-ui-grid-cell'

@ -0,0 +1,42 @@
/* eslint-disable @typescript-eslint/no-unused-vars */
import React from 'react'
import { useContext } from 'react'
import FiltersContext from ".././filtersContext"
interface CustomCheckboxProps {
label: string
color?: string
}
export const CustomCheckbox = (props: CustomCheckboxProps) => {
const filterCon = useContext(FiltersContext)
let textColor = props.color
let defChecked = true
if (filterCon.keyValueMap[props.label]) defChecked = filterCon.keyValueMap[props.label].enabled
if (!textColor || textColor == '') textColor = filterCon.keyValueMap[props.label].color
return (
<div id={textColor + props.label} className="h-80 mx-1 align-items-center custom-control custom-checkbox" style={{minWidth: '4rem'}}>
<input
className="custom-control-input"
id={"GVCheckbox" + props.label}
defaultChecked={defChecked}
onChange={e => {
if (props.label == 'no tag')
filterCon.showUntagged = ! filterCon.showUntagged
else filterCon.updateValue(props.label, e.target.checked, textColor)}}
type="checkbox"
/>
<label
className={"form-check-label custom-control-label text-nowrap text-" + textColor}
style={{ paddingTop: '0.125rem' }}
htmlFor={"GVCheckbox" + props.label}
data-id={"GVCheckboxLabel" + props.label}
>
{ props.label }
</label>
</div>
)
}
export default CustomCheckbox

@ -0,0 +1,18 @@
import React, { createContext, useState, useContext } from 'react';
interface FilterContextType {
showUntagged: boolean
showPin: boolean
keyValueMap: Record<string, { enabled: boolean; color: string; }>;
updateValue: (key: string, enabled: boolean, color: string) => void
addValue: (key: string, enabled: boolean, color: string) => void
}
const FiltersContext = createContext<FilterContextType>({
showUntagged: false,
showPin: false,
keyValueMap: {},
updateValue: () => {},
addValue: () => {},
});
export default FiltersContext

@ -0,0 +1,46 @@
.remixui_grid_cell {
font-weight: normal;
}
.remixui_grid_cell_container {
width: fit-content;
}
.remixui_grid_cell_title{
font-size: 0.8rem;
font-style: italic;
}
.remixui_grid_cell_btn {
width: 32px;
}
.remixui_grid_cell_logo {
width: 3rem;
height: 3rem;
}
.remixui_grid_cell_pin:focus {
outline: none;
}
.remixui_grid_cell_pin {
width: 1rem;
height: 1rem;
position: relative;
right: 1rem;
top: -0.5rem;
background: transparent;
}
.remixui_grid_cell_tags {
position: relative;
right: 1rem;
top: 0.1rem;
}
.remixui_grid_cell_tag {
font-size: x-small;
font-weight: bolder;
width: 0.4rem;
height: 1.2rem;
}

@ -0,0 +1,86 @@
import React, {useState, useEffect, useContext, useRef, ReactNode} from 'react' // eslint-disable-line
import './remix-ui-grid-cell.css'
import FiltersContext from "./filtersContext"
import { CustomTooltip } from '@remix-ui/helper'
declare global {
interface Window {
_paq: any
}
}
const _paq = window._paq = window._paq || []
interface RemixUIGridCellProps {
plugin: any
pinned?: boolean
pinStateCallback?: any
logo?: string
title?: string
tagList?: string[] // max 8, others will be ignored
classList?: string
styleList?: any
children?: ReactNode
}
export const RemixUIGridCell = (props: RemixUIGridCellProps) => {
const filterCon = useContext(FiltersContext)
const [anyEnabled, setAnyEnabled] = useState(false)
const [pinned, setPinned] = useState<boolean>(props.pinned)
useEffect(() => {
if (props.tagList) setAnyEnabled(props.tagList.some((key) => filterCon.keyValueMap[key]?.enabled))
else setAnyEnabled(filterCon.showUntagged)
}, [filterCon, props.tagList])
return (
<div className='mr-2 mt-3'>
{ anyEnabled && <div className='d-flex flex-grid'>
<div className={"d-flex mx-0 p-2 bg-light border border-secondary remixui_grid_cell_container " + props.classList || ''} data-id={"remixUIGS" + props.title}>
<div className="d-flex remixui_grid_cell flex-column">
<div className='d-flex flex-row pb-1 align-items-end' style={{width: '8rem', height: '1rem'}}>
{ props.logo && <img className='remixui_grid_view_logo mr-1' src={props.logo} style={{width: '1rem', height: '1rem'}}/> }
{ props.title && <label
className='m-0 p-0 align-items-left'
style={{overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap', fontSize: 'xx-small'}}
>
{ props.title }
</label> }
</div>
{ props.children }
</div>
</div>
{ filterCon.showPin && <button
className={`${pinned ? 'fa-duotone' : 'fa-light'}` + ` fa-map-pin text-info border-0 mb-0 remixui_grid_cell_pin`}
onClick={() => {
setPinned(!pinned)
props.pinStateCallback()
}}
></button>}
{ props.tagList && <div className='d-flex flex-column align-items-begin remixui_grid_cell_tags'>
{ Object.keys(props.tagList).map((key) => (
filterCon.keyValueMap[props.tagList[key]].enabled && (
<CustomTooltip
placement="right"
tooltipId="pluginManagerInactiveTitleLinkToDoc"
tooltipClasses="text-nowrap"
tooltipText={props.tagList[key]}
>
<span key={props.tagList[key]}
className={'remixui_grid_cell_tag bg-' + filterCon.keyValueMap[props.tagList[key]].color}
>
</span>
</CustomTooltip>
)
)) }
</div> }
{ !props.tagList && <span
className={'remixui_grid_cell_tags'}>
</span> }
</div> }
</div>
)
}
export default RemixUIGridCell

@ -0,0 +1,26 @@
.remixui_grid_section {
font-weight: normal;
}
.remixui_grid_section_container {
width: fit-content;
}
.remixui_grid_section_title{
font-size: 0.8rem;
font-style: italic;
}
.remixui_grid_section_btn {
width: 32px;
}
.remixui_grid_section_logo {
width: 3rem;
height: 3rem;
}
* {
scrollbar-width: thin;
scrollbar-color: var(--bg-dark) var(--bg-light);
}

@ -0,0 +1,38 @@
import React, {useState, useEffect, useContext, useRef, ReactNode} from 'react' // eslint-disable-line
import './remix-ui-grid-section.css'
declare global {
interface Window {
_paq: any
}
}
const _paq = window._paq = window._paq || []
interface RemixUIGridSectionProps {
plugin: any
title?: string
hScrollable: boolean
classList?: string
styleList?: any
children?: ReactNode
}
export const RemixUIGridSection = (props: RemixUIGridSectionProps) => {
return (
<div
className={`d-flex px-4 py-2 flex-column w-100 remixui_grid_section_container ${props.classList}`}
data-id={"remixUIGS" + props.title}
style={{ overflowX: 'auto' }}
>
<div className="d-flex flex-column w-100 remixui_grid_section">
{ props.title && <h6 className='mt-1 mb-0 align-items-left '>{ props.title }</h6> }
<div className={(props.hScrollable) ? `d-flex flex-row pb-2 overflow-auto` : `d-flex flex-wrap`}>
{ props.children }
</div>
</div>
</div>
)
}
export default RemixUIGridSection

@ -0,0 +1,25 @@
.remixui_grid_view {
font-weight: normal;
}
.remixui_grid_view_container {
overflow: auto;
}
.remixui_grid_view_title{
font-size: 0.8rem;
font-style: italic;
}
.remixui_grid_view_btn {
width: 32px;
}
.remixui_grid_view_logo {
width: 3rem;
height: 3rem;
}
.remixui_grid_view_titlebar {
min-width: max-content;
}

@ -0,0 +1,147 @@
import React, {useState, useEffect, useContext, useRef, ReactNode} from 'react' // eslint-disable-line
import './remix-ui-grid-view.css'
import {ThemeContext, themes} from './themeContext'
import CustomCheckbox from './components/customCheckbox'
import FiltersContext from "./filtersContext"
declare global {
interface Window {
_paq: any
}
}
const _paq = window._paq = window._paq || []
interface RemixUIGridViewProps {
plugin: any
logo?: string
title?: string
enableFilter?: boolean
tagList?: [string, string][] // max 8, others will be ignored
showUntagged?: boolean
showPin?: boolean
classList?: string
styleList?: any
description?: string
children?: ReactNode
}
export const RemixUIGridView = (props: RemixUIGridViewProps) => {
const [keyValueMap, setKeyValueMap] = useState<Record<string, { enabled: boolean; color: string; }>>({});
const showUntagged = props.showUntagged || false
const showPin = props.showPin || false
const updateValue = (key: string, enabled: boolean, color?: string) => {
if (!color || color === '') color = setKeyValueMap[key].color
setKeyValueMap((prevMap) => ({
...prevMap,
[key]: {color, enabled},
}))
}
const addValue = (key: string, enabled: boolean, color: string) => {
// Check if the key already exists, if so, do not add
if (key in keyValueMap) {
return
}
// Add the new key-value pair
setKeyValueMap((prevMap) => ({
...prevMap,
[key]: { enabled, color },
}))
}
const {plugin} = props.plugin
const searchInputRef = useRef(null)
const [state, setState] = useState<{
themeQuality: {filter: string; name: string}
}>({
themeQuality: themes.light
})
// Initialize filters context with data from props
useEffect(() => {
if (props.tagList && Array.isArray(props.tagList)) {
const initialKeyValueMap: Record<string, { enabled: boolean; color: string; }> = {};
// Limit to first 8 elements, ignoring the rest
for (let i = 0; i < props.tagList.length; i++) {
const [key, color] = props.tagList[i]
initialKeyValueMap[key] = { enabled: true, color }
}
if (showUntagged) initialKeyValueMap['no tag'] = { enabled: true, color: 'primary' }
setKeyValueMap(initialKeyValueMap)
}
}, [])
useEffect(() => {
plugin?.call('theme', 'currentTheme').then((theme) => {
// update theme quality. To be used for for images
setState((prevState) => {
return {
...prevState,
themeQuality: theme.quality === 'dark' ? themes.dark : themes.light
}
})
})
plugin?.on('theme', 'themeChanged', (theme) => {
// update theme quality. To be used for for images
setState((prevState) => {
return {
...prevState,
themeQuality: theme.quality === 'dark' ? themes.dark : themes.light
}
})
})
}, [plugin])
return (
<FiltersContext.Provider value={{ showUntagged, showPin, keyValueMap, updateValue, addValue }}>
<div className={"d-flex flex-column bg-dark w-100 h-100 remixui_grid_view_container " + props.classList || ''} data-id="remixUIGV">
<ThemeContext.Provider value={state.themeQuality}>
<div className="d-flex flex-column w-100 remixui_grid_view">
<div className='d-flex p-4 bg-light flex-column remixui_grid_view_titlebar'>
<div className='d-flex flex-row align-items-center mb-2'>
{ props.logo && <img className='remixui_grid_view_logo mr-2' src={props.logo} /> }
{ props.title && <h3 className='mb-0'>{ props.title }</h3> }
</div>
{ props.description && <div className='pb-3 remixui_grid_view_title'>{ props.description }</div> }
{ props.enableFilter && <div className='d-flex flex-row'>
<div className="d-flex flex-row pr-2 pb-1 align-items-center justify-content-between">
<div className='d-flex' id="GVFilter">
<button
className="remixui_grid_view_btn text-secondary form-control bg-light border d-flex align-items-center p-2 justify-content-center fas fa-filter bg-light"
onClick={(e) => {
_paq.push(['trackEvent', 'GridView' + props.title ? props.title : '', 'filter', searchInputRef.current.value])
//setstate
}}
></button>
<input
ref={searchInputRef}
type="text"
style={{minWidth: '100px'}}
className="border form-control border-right-0 mr-4"
id="GVFilterInput"
placeholder={"Filter the list"}
data-id="RemixGVFilterInput"
/>
</div>
<div className='d-flex flex-row'>
{ Object.keys(keyValueMap).map((key) => (
<CustomCheckbox label={key} />
)) }
</div>
</div>
</div> }
</div>
{ props.children }
</div>
</ThemeContext.Provider>
</div>
</FiltersContext.Provider>
)
}
export default RemixUIGridView

@ -0,0 +1,16 @@
import React from 'react' // eslint-disable-line
export const themes = {
light: {
filter: 'invert(0)',
name: 'light'
},
dark: {
filter: 'invert(1)',
name: 'dark'
}
}
export const ThemeContext = React.createContext(
themes.dark // default value
)

@ -13,8 +13,6 @@ import { appPlatformTypes, platformContext } from '@remix-ui/app'
import { HomeTabFileElectron } from './components/homeTabFileElectron'
import { LanguageOptions } from './components/homeTablangOptions'
declare global {
interface Window {
_paq: any

@ -2,7 +2,7 @@ import { CompilationResult, SourceWithTarget } from '@remixproject/plugin-api'
import { ViewPlugin } from '@remixproject/engine-web';
import { EventEmitter } from 'events';
import {Registry} from '@remix-project/remix-lib';
import { Registry } from '@remix-project/remix-lib';
export declare class AnalysisTab extends ViewPlugin {
event: EventManager;
events: EventEmitter;

@ -103,6 +103,15 @@
"@remix-ui/vyper-compile-details": [
"libs/remix-ui/vyper-compile-details/src/index.ts"
],
"@remix-ui/remix-ui-grid-view": [
"libs/remix-ui/grid-view/src/index.ts"
],
"@remix-ui/remix-ui-grid-section": [
"libs/remix-ui/grid-view/src/index.ts"
],
"@remix-ui/remix-ui-grid-cell": [
"libs/remix-ui/grid-view/src/index.ts"
],
"@remix-ui/solidity-compiler": [
"libs/remix-ui/solidity-compiler/src/index.ts"
],

Loading…
Cancel
Save