chore: use monaco editor

This commit is contained in:
Ibrahima G. Coulibaly
2025-07-18 03:26:42 +01:00
parent 64d6628270
commit db87355274
6 changed files with 163 additions and 212 deletions

View File

@@ -1,127 +0,0 @@
import { Box, styled, TextField } from '@mui/material';
import React, { useContext, useRef } from 'react';
import { CustomSnackBarContext } from '../../contexts/CustomSnackBarContext';
import InputHeader from '../InputHeader';
import InputFooter from './InputFooter';
import { useTranslation } from 'react-i18next';
const LineNumberWrapper = styled(Box)(({ theme }) => ({
position: 'relative',
display: 'flex',
backgroundColor: theme.palette.background.paper,
'.line-numbers': {
whiteSpace: 'pre',
position: 'absolute',
left: 0,
top: 0,
bottom: 0,
width: '40px',
backgroundColor: theme.palette.action.hover,
borderRight: `1px solid ${theme.palette.divider}`,
textAlign: 'right',
paddingRight: '8px',
paddingTop: '16px', // Align with TextField content
paddingBottom: '8px',
color: theme.palette.text.secondary,
userSelect: 'none',
fontSize: '14px',
lineHeight: '1.5em',
fontFamily: 'monospace',
zIndex: 1,
overflow: 'hidden'
},
'.MuiTextField-root': {
position: 'relative',
'& .MuiInputBase-root': {
paddingLeft: '48px',
fontFamily: 'monospace',
fontSize: '14px',
lineHeight: '1.5em'
},
'& .MuiInputBase-input': {
lineHeight: '1.5em'
}
}
}));
export default function LineNumberInput({
value,
onChange,
title = 'Input text',
placeholder
}: {
title?: string;
value: string;
onChange: (value: string) => void;
placeholder?: string;
}) {
const { t } = useTranslation();
const { showSnackBar } = useContext(CustomSnackBarContext);
const fileInputRef = useRef<HTMLInputElement>(null);
const handleCopy = () => {
navigator.clipboard
.writeText(value)
.then(() => showSnackBar(t('toolTextInput.copied'), 'success'))
.catch((err) => {
showSnackBar(t('toolTextInput.copyFailed', { error: err }), 'error');
});
};
const handleFileChange = (event: React.ChangeEvent<HTMLInputElement>) => {
const file = event.target.files?.[0];
if (file) {
const reader = new FileReader();
reader.onload = (e) => {
const text = e.target?.result;
if (typeof text === 'string') {
onChange(text);
}
};
reader.readAsText(file);
}
};
const handleImportClick = () => {
fileInputRef.current?.click();
};
// Generate line numbers based on the content
const lineCount = value.split('\n').length;
const lineNumbers = Array.from({ length: lineCount }, (_, i) => i + 1).join(
'\n'
);
return (
<Box>
<InputHeader title={title || t('toolTextInput.input')} />
<LineNumberWrapper>
<pre className="line-numbers">{lineNumbers}</pre>
<TextField
value={value}
onChange={(event) => onChange(event.target.value)}
fullWidth
multiline
rows={10}
placeholder={placeholder || t('toolTextInput.placeholder')}
sx={{
'&.MuiTextField-root': {
backgroundColor: 'background.paper'
}
}}
inputProps={{
'data-testid': 'text-input'
}}
/>
</LineNumberWrapper>
<InputFooter handleCopy={handleCopy} handleImport={handleImportClick} />
<input
type="file"
accept="*"
ref={fileInputRef}
style={{ display: 'none' }}
onChange={handleFileChange}
/>
</Box>
);
}

View File

@@ -0,0 +1,73 @@
import { Box } from '@mui/material';
import React, { useContext, useRef } from 'react';
import { CustomSnackBarContext } from '../../contexts/CustomSnackBarContext';
import InputHeader from '../InputHeader';
import InputFooter from './InputFooter';
import { useTranslation } from 'react-i18next';
import Editor from '@monaco-editor/react';
import { globalInputHeight } from '../../config/uiConfig';
export default function ToolCodeInput({
value,
onChange,
title = 'Input text',
language
}: {
title?: string;
value: string;
language: string;
onChange: (value: string) => void;
}) {
const { t } = useTranslation();
const { showSnackBar } = useContext(CustomSnackBarContext);
const fileInputRef = useRef<HTMLInputElement>(null);
const handleCopy = () => {
navigator.clipboard
.writeText(value)
.then(() => showSnackBar(t('toolTextInput.copied'), 'success'))
.catch((err) => {
showSnackBar(t('toolTextInput.copyFailed', { error: err }), 'error');
});
};
const handleFileChange = (event: React.ChangeEvent<HTMLInputElement>) => {
const file = event.target.files?.[0];
if (file) {
const reader = new FileReader();
reader.onload = (e) => {
const text = e.target?.result;
if (typeof text === 'string') {
onChange(text);
}
};
reader.readAsText(file);
}
};
const handleImportClick = () => {
fileInputRef.current?.click();
};
return (
<Box>
<InputHeader title={title || t('toolTextInput.input')} />
<Box height={globalInputHeight}>
<Editor
height={'87%'}
language={language}
value={value}
onChange={(value) => onChange(value ?? '')}
/>
<InputFooter handleCopy={handleCopy} handleImport={handleImportClick} />
<input
type="file"
accept="*"
ref={fileInputRef}
style={{ display: 'none' }}
onChange={handleFileChange}
/>
</Box>
</Box>
);
}

View File

@@ -1,42 +1,10 @@
import { useState, useEffect } from 'react';
import { useEffect, useState } from 'react';
import ToolContent from '@components/ToolContent';
import LineNumberInput from '@components/input/LineNumberInput';
import ToolCodeInput from '@components/input/ToolCodeInput';
import ToolTextResult from '@components/result/ToolTextResult';
import { compareJson } from './service';
import { ToolComponentProps } from '@tools/defineTool';
import { Box, Grid, styled } from '@mui/material';
const StyledContainer = styled(Box)({
position: 'relative',
width: '100%',
height: '100%',
display: 'flex',
flexDirection: 'column',
minHeight: '500px',
marginBottom: '20px'
});
const StyledGrid = styled(Grid)({
flex: 1,
'& .MuiGrid-item': {
height: '100%'
}
});
const StyledInputWrapper = styled(Box)({
height: '100%',
'& > div': {
height: '100%',
'& textarea': {
height: '100% !important',
minHeight: '450px',
resize: 'none',
fontSize: '14px',
lineHeight: '1.5',
padding: '12px'
}
}
});
import { Grid } from '@mui/material';
type InitialValuesType = {};
@@ -73,8 +41,8 @@ export default function JsonComparison({ title }: ToolComponentProps) {
compareInputs();
}, [input1, input2]);
const handleInput1Change = (value: string) => {
setInput1(value);
const handleInput1Change = (value: string | undefined) => {
setInput1(value ?? '');
};
const handleInput2Change = (value: string) => {
@@ -90,39 +58,31 @@ export default function JsonComparison({ title }: ToolComponentProps) {
getGroups={null}
compute={() => {}}
inputComponent={
<StyledContainer>
<StyledGrid container spacing={2}>
<Grid item xs={4}>
<StyledInputWrapper>
<LineNumberInput
title="First JSON"
value={input1}
onChange={handleInput1Change}
placeholder="Paste your first JSON here..."
/>
</StyledInputWrapper>
</Grid>
<Grid item xs={4}>
<StyledInputWrapper>
<LineNumberInput
title="Second JSON"
value={input2}
onChange={handleInput2Change}
placeholder="Paste your second JSON here..."
/>
</StyledInputWrapper>
</Grid>
<Grid item xs={4}>
<StyledInputWrapper>
<ToolTextResult
title="Differences"
value={result}
extension={'txt'}
/>
</StyledInputWrapper>
</Grid>
</StyledGrid>
</StyledContainer>
<Grid container spacing={2}>
<Grid item xs={12} md={6} lg={4}>
<ToolCodeInput
title="First JSON"
value={input1}
onChange={handleInput1Change}
language={'json'}
/>
</Grid>
<Grid item xs={12} md={6} lg={4}>
<ToolCodeInput
title="Second JSON"
language={'json'}
value={input2}
onChange={handleInput2Change}
/>
</Grid>
<Grid item xs={12} md={12} lg={4}>
<ToolTextResult
title="Differences"
value={result}
extension={'txt'}
/>
</Grid>
</Grid>
}
/>
);