Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feat/module explorer redesign pt. II #1023

Merged
merged 23 commits into from
Oct 13, 2023
Merged
Show file tree
Hide file tree
Changes from 22 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 9 additions & 1 deletion packages/apps/tools/locales/en/common.json
Original file line number Diff line number Diff line change
Expand Up @@ -166,5 +166,13 @@
"Save Network": "Save Network",
"Network label": "Network label",
"Network ID": "Network ID",
"Network api": "Network api"
"Network api": "Network api",
"No code to be shown yet": "No code to be shown yet",
"Click on a module from the left panel to see its code in this panel.": "Click on a module from the left panel to see its code in this panel.",
"Select/open a module to see the outline of the contract/module": "Select/open a module to see the outline of the contract/module",
"Faucet": "Faucet",
"Select Network": "Select Network",
"Finalize Cross Chain": "Finalize Cross Chain",
"Module Explorer": "Module Explorer",
"Module name": "Module name"
}
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import React from 'react';
const WalletConnectButton: FC = () => {
const { connect, isInitializing, disconnect, session } =
useWalletConnectClient();
const { t } = useTranslation();
const { t } = useTranslation('common');

const handleClick = async (): Promise<void> => {
if (session) {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,24 +1,39 @@
import { Text, TextField } from '@kadena/react-ui';

import type { IChainModule } from '../types';

import type { IOutlineProps } from './outline';
import Outline from './outline';
import type { IResultsProps } from './results';
import Results from './results';
import { containerStyle, modulesContainerStyle } from './styles.css';
import {
containerStyle,
modulesContainerStyle,
outlineStyle,
} from './styles.css';

import useTranslation from 'next-translate/useTranslation';
import React, { useState, useTransition } from 'react';

export interface ISidePanelProps {
results: IResultsProps['data'];
onResultClick: IResultsProps['onItemClick'];
onModuleExpand: IResultsProps['onModuleExpand'];
onInterfaceClick: IOutlineProps['onInterfaceClick'];
selectedModule?: IChainModule;
}

const SidePanel = ({
results,
onResultClick,
onInterfaceClick,
onModuleExpand,
selectedModule,
}: ISidePanelProps): React.JSX.Element => {
const [text, setText] = useState('');
const [searchQuery, setSearchQuery] = useState<string>();
const [isPending, startTransition] = useTransition();
const { t } = useTranslation('common');

const onChange: React.ChangeEventHandler<HTMLInputElement> = (e) => {
setText(e.target.value);
Expand All @@ -33,8 +48,8 @@ const SidePanel = ({
<TextField
label="Search"
inputProps={{
id: 'something',
placeholder: 'Module name',
id: 'module-explorer-search',
placeholder: t('Module name'),
onChange,
value: text,
}}
Expand All @@ -45,9 +60,14 @@ const SidePanel = ({
data={results}
filter={searchQuery}
onItemClick={onResultClick}
onModuleExpand={onModuleExpand}
className={modulesContainerStyle}
/>
<Outline style={{ minHeight: '10rem' }} />
<Outline
selectedModule={selectedModule}
className={outlineStyle}
onInterfaceClick={onInterfaceClick}
/>
</div>
);
};
Expand Down
Original file line number Diff line number Diff line change
@@ -1,14 +1,100 @@
import { Heading, Text } from '@kadena/react-ui';
import { contractParser } from '@kadena/pactjs-generator';
import type { ITreeProps } from '@kadena/react-ui';
import { Button, Heading, Text, Tree } from '@kadena/react-ui';

import type { IChainModule } from '../types';

import useTranslation from 'next-translate/useTranslation';
import React from 'react';

interface IOutlineProps extends React.HTMLAttributes<HTMLDivElement> {}
export interface IOutlineProps extends React.HTMLAttributes<HTMLDivElement> {
selectedModule?: IChainModule;
onInterfaceClick: (module: IChainModule) => void;
}

type Contract = ReturnType<typeof contractParser>[0][0]; // TODO: Should we improve this because it's a bit hacky?

const contractToTreeItems = (
sanderlooijenga marked this conversation as resolved.
Show resolved Hide resolved
contract: Contract,
onInterfaceClick: IOutlineProps['onInterfaceClick'],
module: IOutlineProps['selectedModule'],
): ITreeProps['items'] => {
const { usedInterface: interfaces, capabilities, functions } = contract;
const items: ITreeProps['items'] = [];

if (interfaces?.length) {
items.push({
title: 'Interfaces',
isOpen: true,
items: interfaces.map((i) => ({
title: (
<Button
onClick={() =>
onInterfaceClick({ chainId: module!.chainId, moduleName: i.name })
}
variant="compact"
icon="ExitToApp"
>
{i.name}
</Button>
),
})),
});
}

if (capabilities?.length) {
items.push({
title: 'Capabilities',
items: capabilities.map((c) => ({
title: c.name,
})),
});
}

if (functions?.length) {
items.push({
title: 'Functions',
items: functions.map((f) => ({
title: f.name,
})),
});
}

return items;
};

const Outline = (props: IOutlineProps): React.JSX.Element => {
const { selectedModule, onInterfaceClick } = props;
const { t } = useTranslation('common');

let parsedContract: Contract;
if (selectedModule) {
const [, namespace] = selectedModule.moduleName.split('.');
[[parsedContract]] = contractParser(selectedModule.code!, namespace);
}

return (
<div {...props}>
<Heading as="h4">Outline</Heading>
<Text>To be implemented…</Text>
<Heading as="h4">
Outline
{selectedModule
? ` ${selectedModule.moduleName} @ ${selectedModule.chainId}`
: null}
</Heading>
{selectedModule ? (
<Tree
items={contractToTreeItems(
parsedContract!,
onInterfaceClick,
selectedModule,
)}
isOpen
/>
) : (
<Text>
{t('Select/open a module to see the outline of the contract/module')}
</Text>
)}
</div>
);
};
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
import type { ChainwebChainId } from '@kadena/chainweb-node-client';
import type { ITreeProps } from '@kadena/react-ui';
import { Button, Tree } from '@kadena/react-ui';

import type { IModule } from '..';
import type { IChainModule } from '../types';
import type { getModulesMap } from '../utils';

import { moduleTitle } from './styles.css';
Expand All @@ -10,28 +11,44 @@ import React, { useMemo } from 'react';

export interface IResultsProps extends React.HTMLAttributes<HTMLDivElement> {
data: ReturnType<typeof getModulesMap>;
onItemClick: (result: IModule) => void;
onItemClick: (result: IChainModule) => void;
onModuleExpand: (module: {
moduleName: string;
chains: ChainwebChainId[];
}) => void;
filter?: string;
}

const truncateString = (str: string, maxChars: number): string => {
if (str.length <= maxChars) return str;
return `${str.slice(0, maxChars)}...`;
};

const CHARCOUNT_BREAKING_POINT = 15;

const resultsMapToTreeItems = (
data: IResultsProps['data'],
onItemClick: IResultsProps['onItemClick'],
onModuleExpand: IResultsProps['onModuleExpand'],
): ITreeProps['items'] => {
return Array.from(data, ([moduleName, chains]) => ({
return Array.from(data, ([moduleName, chainsInfo]) => ({
title: (
<p className={moduleTitle} title={moduleName}>
{moduleName}
</p>
),
items: chains.map((chainId) => ({
onOpen: () =>
onModuleExpand({ moduleName, chains: chainsInfo.map((x) => x.chainId) }),
items: chainsInfo.map(({ chainId, hash }) => ({
title: (
<Button
onClick={() => onItemClick({ chainId, moduleName })}
variant="compact"
icon="ExitToApp"
title={chainId + (hash ? ` - ${hash}` : '')}
>
{chainId}
{hash ? ` - ${truncateString(hash, CHARCOUNT_BREAKING_POINT)}` : null}
</Button>
),
})),
Expand All @@ -41,6 +58,7 @@ const resultsMapToTreeItems = (
const Results = ({
data,
onItemClick,
onModuleExpand,
filter,
...rest
}: IResultsProps): React.JSX.Element => {
Expand All @@ -49,12 +67,12 @@ const Results = ({
if (filter) {
filteredData = new Map(
[...filteredData].filter(([moduleName]) => {
return moduleName.includes(filter);
return moduleName.toLowerCase().includes(filter.toLowerCase());
}),
);
}
return resultsMapToTreeItems(filteredData, onItemClick);
}, [data, filter, onItemClick]);
return resultsMapToTreeItems(filteredData, onItemClick, onModuleExpand);
}, [data, filter, onItemClick, onModuleExpand]);

return (
<div {...rest}>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { vars } from '@kadena/react-ui/theme';
import { sprinkles, vars } from '@kadena/react-ui/theme';

import { style } from '@vanilla-extract/css';

Expand All @@ -18,3 +18,12 @@ export const moduleTitle = style({
textOverflow: 'ellipsis',
maxWidth: `calc(${vars.sizes.$64} + ${vars.sizes.$8})`, // 2rem less than the width of the column
});

export const outlineStyle = style([
sprinkles({
height: '$64',
}),
{
overflow: 'scroll',
},
]);
Original file line number Diff line number Diff line change
@@ -1,31 +1,40 @@
import { Tabs } from '@kadena/react-ui';
import { Heading, Tabs } from '@kadena/react-ui';

import type { IModule } from '.';
import type { IChainModule } from './types';

import dynamic from 'next/dynamic';
import useTranslation from 'next-translate/useTranslation';
import React from 'react';

const AceViewer = dynamic(import('@/components/Global/Ace'), {
ssr: false,
});

export interface IEditorProps {
openedModules: Array<IModule & { code: string }>;
openedModules: IChainModule[];
}

const moduleToTabId = ({ moduleName, chainId }: IModule): string => {
const moduleToTabId = ({ moduleName, chainId }: IChainModule): string => {
return `${moduleName}-${chainId}`;
};

const Editor = ({ openedModules }: IEditorProps): React.JSX.Element => {
const { t } = useTranslation('common');

if (!openedModules.length) {
return <div>Nothing loaded</div>;
return (
<section>
<Heading variant="h4">{t('No code to be shown yet')}</Heading>
<p>
{t(
'Click on a module from the left panel to see its code in this panel.',
)}
</p>
</section>
);
}
return (
<Tabs.Root
initialTab={moduleToTabId(openedModules[0])}
currentTab={moduleToTabId(openedModules[0])}
>
<Tabs.Root initialTab={moduleToTabId(openedModules[0])}>
{openedModules.map(({ moduleName, chainId }) => {
return (
<Tabs.Tab
Expand Down
24 changes: 12 additions & 12 deletions packages/apps/tools/src/components/Global/ModuleExplorer/index.tsx
Original file line number Diff line number Diff line change
@@ -1,37 +1,37 @@
import type { ChainwebChainId } from '@kadena/chainweb-node-client';

import type { IEditorProps } from './editor';
import Editor from './editor';
import type { ISidePanelProps } from './SidePanel';
import SidePanel from './SidePanel';
import { containerStyle } from './styles.css';
import type { IChainModule } from './types';
import { getModulesMap } from './utils';

import React from 'react';

export interface IModule {
chainId: ChainwebChainId;
moduleName: string;
}

export interface IModuleExplorerProps {
modules: IModule[];
modules: IChainModule[];
openedModules: IEditorProps['openedModules'];
onModuleClick: (module: IModule) => void;
onModuleClick: ISidePanelProps['onResultClick'];
onInterfaceClick: ISidePanelProps['onInterfaceClick'];
onModuleExpand: ISidePanelProps['onModuleExpand'];
}

const ModuleExplorer = ({
modules,
openedModules,
onModuleClick,
onInterfaceClick,
onModuleExpand,
}: IModuleExplorerProps): React.JSX.Element => {
const results = getModulesMap(modules);
return (
<div className={containerStyle}>
<SidePanel
results={results}
onResultClick={(result) => {
onModuleClick(result);
}}
onResultClick={onModuleClick}
onInterfaceClick={onInterfaceClick}
onModuleExpand={onModuleExpand}
selectedModule={openedModules[0]}
/>
<Editor openedModules={openedModules} />
</div>
Expand Down
Loading