рефакторинг

This commit is contained in:
2025-07-07 10:42:50 +03:00
parent 760be42773
commit 04ded483b0
9 changed files with 101 additions and 107 deletions

View File

@@ -1,27 +1 @@
# React + TypeScript + Vite Сайт с калькулятором на mathjs
This template provides a minimal setup to get React working in Vite with HMR and some ESLint rules.
Currently, two official plugins are available:
- [@vitejs/plugin-react](https://github.com/vitejs/vite-plugin-react/blob/main/packages/plugin-react/README.md) uses [Babel](https://babeljs.io/) for Fast Refresh
- [@vitejs/plugin-react-swc](https://github.com/vitejs/vite-plugin-react-swc) uses [SWC](https://swc.rs/) for Fast Refresh
## Expanding the ESLint configuration
If you are developing a production application, we recommend updating the configuration to enable type aware lint rules:
- Configure the top-level `parserOptions` property like this:
```js
parserOptions: {
ecmaVersion: 'latest',
sourceType: 'module',
project: ['./tsconfig.json', './tsconfig.node.json'],
tsconfigRootDir: __dirname,
},
```
- Replace `plugin:@typescript-eslint/recommended` to `plugin:@typescript-eslint/recommended-type-checked` or `plugin:@typescript-eslint/strict-type-checked`
- Optionally add `plugin:@typescript-eslint/stylistic-type-checked`
- Install [eslint-plugin-react](https://github.com/jsx-eslint/eslint-plugin-react) and add `plugin:react/recommended` & `plugin:react/jsx-runtime` to the `extends` list

View File

@@ -6,7 +6,7 @@
<meta name="viewport" content="width=device-width, initial-scale=1.0" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" />
<link rel="icon" type="image/svg" href="./favicon.svg" /> <link rel="icon" type="image/svg" href="./favicon.svg" />
<link rel="manifest" href="./manifest.webmanifest" /> <link rel="manifest" href="./manifest.webmanifest" />
<title>Calculator</title> <title>calculator</title>
</head> </head>
<body> <body>
<div id="root"></div> <div id="root"></div>

View File

@@ -1,53 +0,0 @@
import { noop } from 'lodash'
import { Editor } from './Editor'
export const App = () => {
return <Editor text={testText} onChange={noop} />
}
const testText = `
# Welcome to calculator (написать норм приветствие и описать принцип работы калькулятора)
# Lines starting with # are ignored
# Basic math
2 + 2
6 - 3
10 / 4
5 * 1.5
2 + 2 * 2
(2 + 2) * 2
0.1 + 0.2
# Units
10 inch in cm
2 feet to m
15 degC to degF
3 tbsp to tsp
9 km/h to m/s
# Functions
sqrt(3^2 + 4^2)
cos(pi rad)
sin(30 deg)
# Variables
a = 25
b = a * 2
2 * 2
last + 1
# Function declaration
pow2(x) = x ^ 2
pow2(6)
f(x) = (sin(x) + cos(x/2)) * 5
# Matrices
a = [1, 2, 3; 2+2, 5, 6]
a[2, 3]
a[1:2, 2]
b = [1, 2; 3, 4]
b * a
a[3, 1:3] = [7, 8, 9]
`

View File

@@ -1,33 +1,89 @@
import { defaultKeymap, historyKeymap, history } from '@codemirror/commands' import { defaultKeymap, historyKeymap, history } from '@codemirror/commands'
import { EditorView, drawSelection, dropCursor, keymap } from '@codemirror/view' import { EditorView, drawSelection, dropCursor, keymap, placeholder } from '@codemirror/view'
import { defaultHighlightStyle, indentOnInput, syntaxHighlighting } from '@codemirror/language' import { defaultHighlightStyle, indentOnInput, syntaxHighlighting } from '@codemirror/language'
import { Extension } from '@codemirror/state' import { Extension } from '@codemirror/state'
import { closeBrackets, closeBracketsKeymap } from '@codemirror/autocomplete' import { closeBrackets, closeBracketsKeymap } from '@codemirror/autocomplete'
import { searchKeymap } from '@codemirror/search' import { searchKeymap } from '@codemirror/search'
import { useEffect, useRef } from 'react' import { useEffect, useRef } from 'react'
import { textObserverPlugin } from './textObserverPlugin'
import { mathPlugin } from './mathPlugin' import { mathPlugin } from './mathPlugin'
type Props = { const testText = `# Welcome to calculator
text: string # Lines starting with # are ignored
onChange: (text: string) => void
}
export const Editor = ({ text, onChange }: Props) => { # Basic math
2 + 2
6 - 3
10 / 4
5 * 1.5
2 + 2 * 2
(2 + 2) * 2
0.1 + 0.2
# Units
10 inch in cm
2 feet to m
15 degC to degF
3 tbsp to tsp
9 km/h to m/s
# Functions
sqrt(3^2 + 4^2)
cos(pi rad)
sin(30 deg)
# Variables
a = 25
b = a * 2
2 * 2
last + 1
# Function declaration
pow2(x) = x ^ 2
pow2(6)
f(x) = (sin(x) + cos(x/2)) * 5
# Matrices
a = [1, 2, 3; 2+2, 5, 6]
a[2, 3]
a[1:2, 2]
b = [1, 2; 3, 4]
b * a
a[3, 1:3] = [7, 8, 9]
`
export const Editor = () => {
const ref = useRef<HTMLDivElement>(null) const ref = useRef<HTMLDivElement>(null)
const view = useRef<EditorView>() const view = useRef<EditorView>()
useEffect(() => { useEffect(() => {
view.current = new EditorView({ view.current = new EditorView({
doc: text, doc: '',
extensions: [basicSetup, textObserverPlugin(onChange)], extensions: [basicSetup],
parent: ref.current!, parent: ref.current!,
}) })
return () => view.current?.destroy() return () => view.current?.destroy()
}, []) }, [])
return <div className="cm-editor-mount" ref={ref}></div> return (
<div className="wrapper">
<div className="header">
<button
className="example-button"
onClick={() => {
view.current?.dispatch({
changes: { from: 0, to: view.current?.state.doc.length, insert: testText },
})
}}
>
tutorial
</button>
</div>
<div className="cm-editor-mount" ref={ref}></div>
</div>
)
} }
const basicSetup: Extension = [ const basicSetup: Extension = [
@@ -39,4 +95,5 @@ const basicSetup: Extension = [
closeBrackets(), closeBrackets(),
keymap.of([...closeBracketsKeymap, ...defaultKeymap, ...searchKeymap, ...historyKeymap]), keymap.of([...closeBracketsKeymap, ...defaultKeymap, ...searchKeymap, ...historyKeymap]),
mathPlugin(), mathPlugin(),
placeholder('0.1 + 0.2 = 0.3'),
] ]

View File

@@ -22,6 +22,10 @@ body {
outline: none; outline: none;
} }
.cm-scroller {
margin-right: -14px;
}
.math-result { .math-result {
color: rgb(80, 80, 80); color: rgb(80, 80, 80);
margin-left: 8px; margin-left: 8px;
@@ -52,3 +56,26 @@ body {
border-left: 0; border-left: 0;
margin-left: 6px; margin-left: 6px;
} }
.example-button {
background-color: black;
color: white;
border: none;
border-radius: 4px;
padding: 8px 16px;
font-family: monospace;
font-size: 14px;
cursor: pointer;
margin: 8px 0;
transition: all 0.2s ease;
font-weight: 600;
}
.example-button:hover {
background-color: #333;
transform: translateY(-3px);
}
.example-button:active {
transform: translateY(3px);
}

View File

@@ -1,6 +1,6 @@
import React from 'react' import React from 'react'
import ReactDOM from 'react-dom/client' import ReactDOM from 'react-dom/client'
import { App } from './App.tsx' import { Editor } from './Editor.tsx'
import './index.css' import './index.css'
const root = document.getElementById('root') const root = document.getElementById('root')
@@ -8,7 +8,7 @@ const root = document.getElementById('root')
if (root) { if (root) {
ReactDOM.createRoot(root).render( ReactDOM.createRoot(root).render(
<React.StrictMode> <React.StrictMode>
<App /> <Editor />
</React.StrictMode>, </React.StrictMode>,
) )
} }

View File

@@ -1,3 +0,0 @@
import { create, all } from 'mathjs'
export const math = create(all)

View File

@@ -1,5 +1,7 @@
import { Decoration, DecorationSet, EditorView, ViewPlugin, ViewUpdate, WidgetType } from '@codemirror/view' import { Decoration, DecorationSet, EditorView, ViewPlugin, ViewUpdate, WidgetType } from '@codemirror/view'
import { math } from './math' import { create, all } from 'mathjs'
const math = create(all)
export const mathPlugin = () => export const mathPlugin = () =>
ViewPlugin.fromClass( ViewPlugin.fromClass(

View File

@@ -1,10 +0,0 @@
import { ViewPlugin, ViewUpdate } from '@codemirror/view'
export const textObserverPlugin = (callback: (text: string) => void) =>
ViewPlugin.define(() => ({
update(update: ViewUpdate) {
if (update.docChanged) {
callback(update.state.doc.toString())
}
},
}))