diff --git a/src/components/AvailableVarsHelpers.tsx b/src/components/AvailableVarsHelpers.tsx new file mode 100644 index 000000000..e9c017d0f --- /dev/null +++ b/src/components/AvailableVarsHelpers.tsx @@ -0,0 +1,236 @@ +import { useEffect, useState, useRef } from 'react' +import { abstractSyntaxTree, Value } from '../lang/abstractSyntaxTree' +import { executor } from '../lang/executor' +import { findUniqueName } from '../lang/modifyAst' +import { findAllPreviousVariables, PrevVariable } from '../lang/queryAst' +import { lexer } from '../lang/tokeniser' +import { useStore } from '../useStore' + +export const AvailableVars = ({ + onVarClick, + prevVariables, +}: { + onVarClick: (a: string) => void + prevVariables: PrevVariable[] +}) => { + return ( + + ) +} + +export const addToInputHelper = + ( + inputRef: React.RefObject, + setValue: (a: string) => void + ) => + (varName: string) => { + const selectionStart = inputRef.current?.selectionStart + let selectionEnd = inputRef.current?.selectionEnd + let newValue = '' + if ( + typeof selectionStart === 'number' && + typeof selectionEnd === 'number' + ) { + newValue = stringSplice( + inputRef.current?.value || '', + selectionStart, + selectionEnd, + varName + ) + selectionEnd = selectionStart + varName.length + } else { + newValue = inputRef.current?.value + varName + } + setValue(newValue) + inputRef.current?.focus() + setTimeout(() => { + // run in the next render cycle + const _selectionEnd = + typeof selectionEnd === 'number' ? selectionEnd : newValue.length + inputRef.current?.setSelectionRange(_selectionEnd, _selectionEnd) + }) + } + +function stringSplice(str: string, index: number, count: number, add: string) { + return str.slice(0, index) + (add || '') + str.slice(index + count) +} + +export function useCalc({ + value, + initialVariableName: valueName = '', +}: { + value: string + initialVariableName?: string +}): { + inputRef: React.RefObject + valueNode: Value | null + calcResult: string + prevVariables: PrevVariable[] + newVariableName: string + isNewVariableNameUnique: boolean + newVariableInsertIndex: number + setNewVariableName: (a: string) => void +} { + const { ast, programMemory, selectionRange } = useStore((s) => ({ + ast: s.ast, + programMemory: s.programMemory, + selectionRange: s.selectionRanges[0], + })) + const inputRef = useRef(null) + const [availableVarInfo, setAvailableVarInfo] = useState< + ReturnType + >({ + variables: [], + insertIndex: 0, + bodyPath: [], + }) + const [valueNode, setValueNode] = useState(null) + const [calcResult, setCalcResult] = useState('NAN') + const [newVariableName, setNewVariableName] = useState('') + const [isNewVariableNameUnique, setIsNewVariableNameUnique] = useState(true) + + useEffect(() => { + setTimeout(() => { + inputRef.current && inputRef.current.focus() + inputRef.current && + inputRef.current.setSelectionRange(0, String(value).length) + }, 100) + if (ast) { + setNewVariableName(findUniqueName(ast, valueName)) + } + }, []) + + useEffect(() => { + const allVarNames = Object.keys(programMemory.root) + if (allVarNames.includes(newVariableName)) { + setIsNewVariableNameUnique(false) + } else { + setIsNewVariableNameUnique(true) + } + }, [newVariableName]) + + useEffect(() => { + if (!ast || !programMemory || !selectionRange) return + const varInfo = findAllPreviousVariables(ast, programMemory, selectionRange) + setAvailableVarInfo(varInfo) + }, [ast, programMemory, selectionRange]) + + useEffect(() => { + try { + const code = `const __result__ = ${value}\nshow(__result__)` + const ast = abstractSyntaxTree(lexer(code)) + const _programMem: any = { root: {} } + availableVarInfo.variables.forEach(({ key, value }) => { + _programMem.root[key] = { type: 'userVal', value, __meta: [] } + }) + const programMemory = executor(ast, _programMem) + const resultDeclaration = ast.body.find( + (a) => + a.type === 'VariableDeclaration' && + a.declarations?.[0]?.id?.name === '__result__' + ) + const init = + resultDeclaration?.type === 'VariableDeclaration' && + resultDeclaration?.declarations?.[0]?.init + const result = programMemory?.root?.__result__?.value + setCalcResult(typeof result === 'number' ? String(result) : 'NAN') + init && setValueNode(init) + } catch (e) { + setCalcResult('NAN') + setValueNode(null) + } + }, [value]) + + return { + valueNode, + calcResult, + prevVariables: availableVarInfo.variables, + newVariableInsertIndex: availableVarInfo.insertIndex, + newVariableName, + isNewVariableNameUnique, + setNewVariableName, + inputRef, + } +} + +export const CalcResult = ({ calcResult }: { calcResult: string }) => { + return ( +
+ + = {calcResult} + +
+ ) +} + +export const CreateNewVariable = ({ + newVariableName, + isNewVariableNameUnique, + setNewVariableName, + shouldCreateVariable, + setShouldCreateVariable, +}: { + isNewVariableNameUnique: boolean + newVariableName: string + setNewVariableName: (a: string) => void + shouldCreateVariable: boolean + setShouldCreateVariable: (a: boolean) => void +}) => { + return ( + <> + +
+ { + setShouldCreateVariable(e.target.checked) + }} + /> + { + setNewVariableName(e.target.value) + }} + /> +
+ {!isNewVariableNameUnique && ( +
+ Sorry, that's not a unique variable name. Please try something else +
+ )} + + ) +} diff --git a/src/components/SetAngleLengthModal.tsx b/src/components/SetAngleLengthModal.tsx new file mode 100644 index 000000000..72748fc7f --- /dev/null +++ b/src/components/SetAngleLengthModal.tsx @@ -0,0 +1,142 @@ +import { Dialog, Transition } from '@headlessui/react' +import { Fragment, useState } from 'react' +import { Value } from '../lang/abstractSyntaxTree' +import { + AvailableVars, + addToInputHelper, + useCalc, + CalcResult, + CreateNewVariable, +} from './AvailableVarsHelpers' + +export const SetAngleLengthModal = ({ + isOpen, + onResolve, + onReject, + value: initialValue, + valueName, +}: { + isOpen: boolean + onResolve: (a: { + value: string + valueNode: Value + variableName?: string + newVariableInsertIndex: number + }) => void + onReject: (a: any) => void + value: number + valueName: string +}) => { + const [value, setValue] = useState(String(initialValue)) + const [shouldCreateVariable, setShouldCreateVariable] = useState(false) + + const { + prevVariables, + calcResult, + valueNode, + isNewVariableNameUnique, + newVariableName, + setNewVariableName, + inputRef, + newVariableInsertIndex, + } = useCalc({ value, initialVariableName: valueName }) + + return ( + + + +
+ + +
+
+ + + + Set {valueName} + +
+ Available Variables +
+ + +
+ { + setValue(e.target.value) + }} + /> +
+ + +
+ +
+
+
+
+
+
+
+ ) +} diff --git a/src/components/SetAngleModal.tsx b/src/components/SetAngleModal.tsx deleted file mode 100644 index f1ac3678d..000000000 --- a/src/components/SetAngleModal.tsx +++ /dev/null @@ -1,272 +0,0 @@ -import { Dialog, Transition } from '@headlessui/react' -import { Fragment, useState, useRef, useEffect } from 'react' -import { abstractSyntaxTree, Value } from '../lang/abstractSyntaxTree' -import { executor } from '../lang/executor' -import { findUniqueName } from '../lang/modifyAst' -import { PrevVariable } from '../lang/queryAst' -import { lexer } from '../lang/tokeniser' -import { useStore } from '../useStore' - -export const SetAngleLengthModal = ({ - isOpen, - onResolve, - onReject, - prevVariables, - value: initialValue, - valueName, -}: { - isOpen: boolean - onResolve: (a: { - value: string - valueNode: Value - variableName?: string - }) => void - onReject: (a: any) => void - prevVariables: PrevVariable[] - value: number - valueName: string -}) => { - const { ast, programMemory } = useStore((s) => ({ - ast: s.ast, - programMemory: s.programMemory, - })) - const [value, setValue] = useState(String(initialValue)) - const [calcResult, setCalcResult] = useState('NAN') - const [shouldCreateVariable, setShouldCreateVariable] = useState(false) - const [newVariableName, setNewVariableName] = useState('') - const [isNewVariableNameUnique, setIsNewVariableNameUnique] = useState(true) - const [valueNode, setValueNode] = useState(null) - const inputRef = useRef(null) - useEffect(() => { - setTimeout(() => { - inputRef.current && inputRef.current.focus() - inputRef.current && - inputRef.current.setSelectionRange(0, String(value).length) - }, 100) - if (ast) { - setNewVariableName(findUniqueName(ast, valueName)) - } - }, []) - - useEffect(() => { - const allVarNames = Object.keys(programMemory.root) - if (allVarNames.includes(newVariableName)) { - setIsNewVariableNameUnique(false) - } else { - setIsNewVariableNameUnique(true) - } - }, [newVariableName]) - - useEffect(() => { - try { - const code = `const __result__ = ${value}\nshow(__result__)` - const ast = abstractSyntaxTree(lexer(code)) - const _programMem: any = { root: {} } - prevVariables.forEach(({ key, value }) => { - _programMem.root[key] = { type: 'userVal', value, __meta: [] } - }) - const programMemory = executor(ast, _programMem) - const resultDeclaration = ast.body.find( - (a) => - a.type === 'VariableDeclaration' && - a.declarations?.[0]?.id?.name === '__result__' - ) - const init = - resultDeclaration?.type === 'VariableDeclaration' && - resultDeclaration?.declarations?.[0]?.init - console.log(init) - setCalcResult(programMemory?.root?.__result__?.value || 'NAN') - setValueNode(init) - } catch (e) { - setCalcResult('NAN') - setValueNode(null) - } - }, [value]) - - return ( - - - -
- - -
-
- - - - Set {valueName} - -
- Available Variables -
-
    - {prevVariables.length && - prevVariables.map(({ key, value }) => ( -
  • - -
  • - ))} -
- -
- { - setValue(e.target.value) - }} - /> -
-
- - = {calcResult} - -
- - -
- { - setShouldCreateVariable(e.target.checked) - }} - /> - { - setNewVariableName(e.target.value) - }} - /> -
- {!isNewVariableNameUnique && ( -
- Sorry, that's not a unique variable name. Please try - something else -
- )} - -
- -
-
-
-
-
-
-
- ) -} - -function stringSplice(str: string, index: number, count: number, add: string) { - return str.slice(0, index) + (add || '') + str.slice(index + count) -} diff --git a/src/components/GetInfoModal.tsx b/src/components/SetHorVertDistanceModal.tsx similarity index 63% rename from src/components/GetInfoModal.tsx rename to src/components/SetHorVertDistanceModal.tsx index e140b88c4..ff492c77c 100644 --- a/src/components/GetInfoModal.tsx +++ b/src/components/SetHorVertDistanceModal.tsx @@ -1,28 +1,55 @@ import { Dialog, Transition } from '@headlessui/react' import { Fragment, useState } from 'react' +import { Value } from '../lang/abstractSyntaxTree' +import { + AvailableVars, + addToInputHelper, + useCalc, + CalcResult, + CreateNewVariable, +} from './AvailableVarsHelpers' export const GetInfoModal = ({ isOpen, onResolve, onReject, - // fields: initialFields, segName: initialSegName, isSegNameEditable, value: initialValue, + initialVariableName, }: { isOpen: boolean - onResolve: (a: any) => void + onResolve: (a: { + value: string + segName: string + valueNode: Value + variableName?: string + newVariableInsertIndex: number + }) => void onReject: (a: any) => void segName: string isSegNameEditable: boolean value: number + initialVariableName: string }) => { const [segName, setSegName] = useState(initialSegName) - const [value, setValue] = useState(initialValue) + const [value, setValue] = useState(String(initialValue)) + const [shouldCreateVariable, setShouldCreateVariable] = useState(false) + + const { + prevVariables, + inputRef, + calcResult, + valueNode, + setNewVariableName, + newVariableName, + isNewVariableNameUnique, + newVariableInsertIndex, + } = useCalc({ value, initialVariableName }) return ( - + Constraint details +
+ Available Variables +
+