Skip to content

Commit b5e0146

Browse files
authoredJul 16, 2024··
Merge pull request #3 from NuclMe/TASK-4_code-display
added codeMirrorEditor
2 parents 7b6ec93 + 6453e7e commit b5e0146

File tree

5 files changed

+548
-21
lines changed

5 files changed

+548
-21
lines changed
 

‎package-lock.json

+287-20
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

‎package.json

+5
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,11 @@
1010
"preview": "vite preview"
1111
},
1212
"dependencies": {
13+
"@codemirror/lang-javascript": "^6.2.2",
14+
"@typescript/vfs": "^1.5.3",
15+
"@uiw/codemirror-theme-bbedit": "^4.23.0",
16+
"@uiw/react-codemirror": "^4.23.0",
17+
"@valtown/codemirror-ts": "^2.1.2",
1318
"antd": "^5.19.1",
1419
"react": "^18.3.1",
1520
"react-dom": "^18.3.1",

‎src/CodeMirrorEditor.tsx

+38
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
import { useEffect, useRef } from 'react';
2+
import { Typography, Flex } from 'antd';
3+
4+
import { EditorView, basicSetup } from 'codemirror';
5+
import { javascript } from '@codemirror/lang-javascript';
6+
import { appCodeString } from './appCodeString';
7+
const CodeMirrorEditor = () => {
8+
const editorRef = useRef<EditorView | null>(null);
9+
10+
useEffect(() => {
11+
editorRef.current = new EditorView({
12+
doc: appCodeString,
13+
extensions: [
14+
basicSetup,
15+
javascript({
16+
typescript: true,
17+
jsx: true,
18+
}),
19+
],
20+
parent: document.querySelector('#editor')!,
21+
});
22+
23+
return () => {
24+
editorRef.current?.destroy();
25+
};
26+
}, []);
27+
28+
return (
29+
<Flex vertical gap="middle">
30+
<Typography.Title style={{ fontSize: '24px', fontWeight: '400' }}>
31+
App source code
32+
</Typography.Title>
33+
<div id="editor" />
34+
</Flex>
35+
);
36+
};
37+
38+
export default CodeMirrorEditor;

‎src/appCodeString.ts

+212
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,212 @@
1+
export const appCodeString: string = `import React, { useState, useEffect, useCallback } from 'react';
2+
import { Typography, Flex, Select, Table } from 'antd';
3+
import styled from 'styled-components';
4+
5+
interface ICrypto {
6+
id: string;
7+
symbol: string;
8+
name: string;
9+
image: string;
10+
current_price: number;
11+
market_cap: number;
12+
market_cap_rank: number;
13+
fully_diluted_valuation: number | null;
14+
total_volume: number;
15+
high_24h: number;
16+
low_24h: number;
17+
price_change_24h: number;
18+
price_change_percentage_24h: number;
19+
market_cap_change_24h: number;
20+
market_cap_change_percentage_24h: number;
21+
circulating_supply: number;
22+
total_supply: number;
23+
max_supply: number | null;
24+
ath: number;
25+
ath_change_percentage: number;
26+
ath_date: string;
27+
atl: number;
28+
atl_change_percentage: number;
29+
atl_date: string;
30+
roi: {
31+
times: number;
32+
currency: string;
33+
percentage: number;
34+
} | null;
35+
last_updated: string;
36+
}
37+
interface ColumnType {
38+
title: string;
39+
dataIndex: string;
40+
key?: string;
41+
render?: (value: any, record: ICrypto) => JSX.Element;
42+
}
43+
const StyledImage = styled.img\`
44+
width: 32px;
45+
46+
@media (max-width: 767px) {
47+
width: 24px;
48+
}
49+
\`;
50+
const StyledText = styled(Typography.Text)\`
51+
font-size: 14px;
52+
53+
@media (max-width: 767px) {
54+
font-size: 12px;
55+
}
56+
\`;
57+
const App: React.FC = () => {
58+
const [cryptos, setCryptos] = useState<ICrypto[]>([]);
59+
const [isLoading, setIsLoading] = useState<boolean>(false);
60+
const [isError, setIsError] = useState<string | null>(null);
61+
const [page, setPage] = useState<number>(1);
62+
const [pageSize, setPageSize] = useState<number>(10);
63+
const [currency, setCurrency] = useState<string>('usd');
64+
const [sortOrder, setSortOrder] = useState<string>('desc');
65+
const [columns, setColumns] = useState<ColumnType[]>([
66+
{
67+
title: 'Name',
68+
dataIndex: 'image',
69+
render: (theImageURL: string, data: ICrypto) => (
70+
<Flex gap="middle" align="center">
71+
<StyledImage src={theImageURL} alt={data.name} />
72+
<StyledText>{data.name}</StyledText>
73+
</Flex>
74+
),
75+
},
76+
{
77+
title: 'Current Price',
78+
dataIndex: 'current_price',
79+
key: 'current_price',
80+
render: (price: number) => (
81+
<StyledText>
82+
{price} {currency}
83+
</StyledText>
84+
),
85+
},
86+
{
87+
title: 'Circulating Supply',
88+
dataIndex: 'circulating_supply',
89+
key: 'circulating_supply',
90+
render: (circulating_supply: string) => (
91+
<StyledText>{circulating_supply}</StyledText>
92+
),
93+
},
94+
]);
95+
96+
const fetchCryptos = useCallback(
97+
async (
98+
page: number,
99+
pageSize: number,
100+
currency: string,
101+
sortOrder: string
102+
) => {
103+
setIsLoading(true);
104+
try {
105+
const response = await fetch(
106+
\`https://api.coingecko.com/api/v3/coins/markets?vs_currency=\${currency}&order=market_cap_\${sortOrder}&per_page=\${pageSize}&page=\${page}&sparkline=false\`
107+
);
108+
const data = await response.json();
109+
if (Array.isArray(data)) {
110+
const dataWithKeys = data.map((item: ICrypto) => ({
111+
...item,
112+
key: item.id,
113+
}));
114+
setCryptos(dataWithKeys);
115+
} else {
116+
console.error('Expected an array but got:', data);
117+
setIsError('Unexpected response format');
118+
}
119+
} catch (error) {
120+
const errorMessage = (error as Error).message;
121+
setIsError(errorMessage);
122+
console.error('Error fetching data:', errorMessage);
123+
} finally {
124+
setIsLoading(false);
125+
}
126+
},
127+
[isError]
128+
);
129+
130+
useEffect(() => {
131+
fetchCryptos(page, pageSize, currency, sortOrder);
132+
}, [page, pageSize, currency, sortOrder]);
133+
134+
useEffect(() => {
135+
setColumns((prevColumns) => [
136+
prevColumns[0],
137+
{
138+
...prevColumns[1],
139+
render: (price: number) => (
140+
<StyledText>
141+
{price} {currency}
142+
</StyledText>
143+
),
144+
},
145+
prevColumns[2],
146+
]);
147+
}, [currency]);
148+
149+
const handleCurrencyChange = (value: string) => {
150+
setCurrency(value);
151+
setPage(1);
152+
fetchCryptos(1, pageSize, value, sortOrder);
153+
};
154+
155+
const handleSortOrderChange = (value: string) => {
156+
setSortOrder(value);
157+
setPage(1);
158+
fetchCryptos(page, pageSize, currency, value);
159+
};
160+
161+
return (
162+
<Flex gap="large" vertical>
163+
<Typography.Title style={{ fontSize: '24px', fontWeight: '400' }}>
164+
Coins & Markets
165+
</Typography.Title>
166+
<Flex gap="large">
167+
<Select
168+
defaultValue="usd"
169+
style={{ width: 200 }}
170+
onChange={handleCurrencyChange}
171+
options={[
172+
{ value: 'usd', label: 'USD' },
173+
{ value: 'eur', label: 'EUR' },
174+
]}
175+
/>
176+
<Select
177+
defaultValue="desc"
178+
style={{ width: 200 }}
179+
onChange={handleSortOrderChange}
180+
options={[
181+
{ value: 'desc', label: 'Market cap descending' },
182+
{ value: 'asc', label: 'Market cap ascending' },
183+
]}
184+
/>
185+
</Flex>
186+
<Table
187+
loading={isLoading}
188+
dataSource={cryptos}
189+
columns={columns}
190+
pagination={{
191+
showSizeChanger: true,
192+
pageSize: pageSize,
193+
total: 10000,
194+
onChange: (page, pageSize) => {
195+
setPageSize(pageSize);
196+
setPage(page);
197+
fetchCryptos(page, pageSize, currency, sortOrder);
198+
},
199+
onShowSizeChange: (page, pageSize) => {
200+
setPageSize(pageSize);
201+
setPage(page);
202+
fetchCryptos(page, pageSize, currency, sortOrder);
203+
},
204+
pageSizeOptions: ['5', '10', '20', '50', '100'],
205+
}}
206+
/>
207+
</Flex>
208+
);
209+
};
210+
211+
export default App;
212+
`;

‎src/main.tsx

+6-1
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,14 @@ import React from 'react';
22
import ReactDOM from 'react-dom/client';
33
import App from './App.tsx';
44
import './index.css';
5+
import CodeMirrorEditor from './CodeMirrorEditor';
6+
import { Flex } from 'antd';
57

68
ReactDOM.createRoot(document.getElementById('root')!).render(
79
<React.StrictMode>
8-
<App />
10+
<Flex gap="middle" vertical>
11+
<App />
12+
<CodeMirrorEditor />
13+
</Flex>
914
</React.StrictMode>
1015
);

0 commit comments

Comments
 (0)
Please sign in to comment.