This commit is contained in:
2023-11-15 23:14:17 +03:00
commit 6f147ca8b7
16 changed files with 3465 additions and 0 deletions

18
.eslintrc.cjs Normal file
View File

@@ -0,0 +1,18 @@
module.exports = {
root: true,
env: { browser: true, es2020: true },
extends: [
'eslint:recommended',
'plugin:@typescript-eslint/recommended',
'plugin:react-hooks/recommended',
],
ignorePatterns: ['dist', '.eslintrc.cjs'],
parser: '@typescript-eslint/parser',
plugins: ['react-refresh'],
rules: {
'react-refresh/only-export-components': [
'warn',
{ allowConstantExport: true },
],
},
}

24
.gitignore vendored Normal file
View File

@@ -0,0 +1,24 @@
# Logs
logs
*.log
npm-debug.log*
yarn-debug.log*
yarn-error.log*
pnpm-debug.log*
lerna-debug.log*
node_modules
dist
dist-ssr
*.local
# Editor directories and files
.vscode/*
!.vscode/extensions.json
.idea
.DS_Store
*.suo
*.ntvs*
*.njsproj
*.sln
*.sw?

13
.prettierrc.cjs Normal file
View File

@@ -0,0 +1,13 @@
module.exports = {
tabWidth: 4,
printWidth: 120,
useTabs: false,
semi: false,
singleQuote: true,
quoteProps: 'as-needed',
jsxSingleQuote: false,
trailingComma: 'all',
bracketSpacing: true,
arrowParens: 'always',
endOfLine: 'auto',
}

27
README.md Normal file
View File

@@ -0,0 +1,27 @@
# React + TypeScript + Vite
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

13
index.html Normal file
View File

@@ -0,0 +1,13 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<link rel="icon" type="image/svg+xml" href="/vite.svg" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Vite + React + TS</title>
</head>
<body>
<div id="root"></div>
<script type="module" src="/src/main.tsx"></script>
</body>
</html>

47
package.json Normal file
View File

@@ -0,0 +1,47 @@
{
"name": "calc",
"private": true,
"version": "0.0.0",
"type": "module",
"scripts": {
"start": "vite",
"build": "tsc && vite build",
"lint": "eslint . --ext ts,tsx --report-unused-disable-directives --max-warnings 0",
"preview": "vite preview"
},
"dependencies": {
"@codemirror/autocomplete": "^6.9.0",
"@codemirror/commands": "^6.2.4",
"@codemirror/lang-html": "^6.4.5",
"@codemirror/lang-markdown": "^6.2.0",
"@codemirror/language": "^6.8.0",
"@codemirror/language-data": "^6.3.1",
"@codemirror/legacy-modes": "^6.3.3",
"@codemirror/search": "^6.5.0",
"@codemirror/view": "^6.16.0",
"@emotion/react": "^11.11.1",
"@mantine/core": "^6.0.17",
"@mantine/hooks": "^6.0.17",
"codemirror": "^6.0.1",
"lodash": "^4.17.21",
"mathjs": "^12.0.0",
"react": "^18.2.0",
"react-dom": "^18.2.0"
},
"devDependencies": {
"@types/lodash": "^4.14.196",
"@types/mathjs": "^9.4.2",
"@types/react": "^18.2.15",
"@types/react-dom": "^18.2.7",
"@typescript-eslint/eslint-plugin": "^6.0.0",
"@typescript-eslint/parser": "^6.0.0",
"@vitejs/plugin-react": "^4.0.4",
"eslint": "^8.45.0",
"eslint-plugin-react-hooks": "^4.6.0",
"eslint-plugin-react-refresh": "^0.4.3",
"prettier": "^3.0.0",
"typescript": "^5.0.2",
"unocss": "^0.54.1",
"vite": "^4.4.5"
}
}

56
src/App.tsx Normal file
View File

@@ -0,0 +1,56 @@
import { defaultKeymap, historyKeymap, history } from '@codemirror/commands'
import { EditorView, drawSelection, dropCursor, keymap } from '@codemirror/view'
import { defaultHighlightStyle, indentOnInput, syntaxHighlighting } from '@codemirror/language'
import { Extension } from '@codemirror/state'
import { closeBrackets, closeBracketsKeymap } from '@codemirror/autocomplete'
import { searchKeymap } from '@codemirror/search'
import { useEffect, useRef, useState } from 'react'
import { getTextPlugin } from './getTextPlugin'
import { mathPlugin } from './mathPlugin'
const basicSetup: Extension = [
history(),
drawSelection(),
dropCursor(),
indentOnInput(),
syntaxHighlighting(defaultHighlightStyle, { fallback: true }),
closeBrackets(),
keymap.of([...closeBracketsKeymap, ...defaultKeymap, ...searchKeymap, ...historyKeymap]),
mathPlugin(),
]
export const App = () => {
const ref = useRef<HTMLDivElement>(null)
const view = useRef<EditorView>()
const [text, setText] = useState(testText)
useEffect(() => {
view.current = new EditorView({
doc: text,
extensions: [basicSetup, getTextPlugin(setText)],
parent: ref.current!,
})
return () => view.current?.destroy()
}, [])
return <div className="cm-editor-mount" ref={ref}></div>
}
export default App
const testText = `
2 + 2 * 2
sqrt(3^2 + 4^2)
2 inch to cm
cos(45 deg)
a = 25
b = a * 2
pow2(x) = x ^ 2
pow2(6)
2 * 2
last + 1
`

10
src/getTextPlugin.ts Normal file
View File

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

22
src/index.css Normal file
View File

@@ -0,0 +1,22 @@
html {
line-height: 1.5;
tab-size: 4;
font-feature-settings: normal;
font-variation-settings: normal;
}
body {
margin: 0;
height: 100vh;
}
#root,
.cm-editor-mount,
.cm-editor {
height: 100%;
}
.math-result {
color: grey;
margin-left: 8px;
}

14
src/main.tsx Normal file
View File

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

79
src/mathPlugin.ts Normal file
View File

@@ -0,0 +1,79 @@
import { Decoration, DecorationSet, EditorView, ViewPlugin, ViewUpdate, WidgetType } from '@codemirror/view'
import { create, all } from 'mathjs'
export const math = create(all, { number: 'BigNumber' })
export const mathPlugin = () =>
ViewPlugin.fromClass(
class {
decorations: DecorationSet
constructor(view: EditorView) {
this.decorations = createDecorations(view)
}
update(update: ViewUpdate) {
if (update.docChanged) {
this.decorations = createDecorations(update.view)
}
}
},
{
decorations: (v) => v.decorations,
},
)
const createDecorations = (view: EditorView) => {
let widgets: Range<Decoration>[] = []
const parser = math.parser()
for (let i = 1; i <= view.state.doc.lines; i++) {
const { text, to } = view.state.doc.line(i)
try {
const result = parser.evaluate(text)
if (result?.isBigNumber) {
parser.set('last', result)
const decoration = Decoration.widget({
widget: new MathResult(result.toPrecision()),
side: 1,
})
widgets.push(decoration.range(to))
}
} catch (e) {}
}
return Decoration.set(widgets)
}
class MathResult extends WidgetType {
constructor(readonly result: string) {
super()
}
eq(other: MathResult) {
return other.result == this.result
}
toDOM() {
let element = document.createElement('span')
element.className = 'math-result'
element.innerText = `= ${this.result}`
return element
}
ignoreEvent() {
return false
}
}
type Range<T> = {
readonly from: number
readonly to: number
readonly value: T
}

1
src/vite-env.d.ts vendored Normal file
View File

@@ -0,0 +1 @@
/// <reference types="vite/client" />

25
tsconfig.json Normal file
View File

@@ -0,0 +1,25 @@
{
"compilerOptions": {
"target": "ES2020",
"useDefineForClassFields": true,
"lib": ["ES2020", "DOM", "DOM.Iterable"],
"module": "ESNext",
"skipLibCheck": true,
/* Bundler mode */
"moduleResolution": "bundler",
"allowImportingTsExtensions": true,
"resolveJsonModule": true,
"isolatedModules": true,
"noEmit": true,
"jsx": "react-jsx",
/* Linting */
"strict": true,
"noUnusedLocals": true,
"noUnusedParameters": true,
"noFallthroughCasesInSwitch": true
},
"include": ["src"],
"references": [{ "path": "./tsconfig.node.json" }]
}

10
tsconfig.node.json Normal file
View File

@@ -0,0 +1,10 @@
{
"compilerOptions": {
"composite": true,
"skipLibCheck": true,
"module": "ESNext",
"moduleResolution": "bundler",
"allowSyntheticDefaultImports": true
},
"include": ["vite.config.ts"]
}

7
vite.config.ts Normal file
View File

@@ -0,0 +1,7 @@
import { defineConfig } from 'vite'
import react from '@vitejs/plugin-react'
// https://vitejs.dev/config/
export default defineConfig({
plugins: [react()],
})

3099
yarn.lock Normal file

File diff suppressed because it is too large Load Diff