restores code editor

This commit is contained in:
Steve Ruiz 2021-06-23 23:32:21 +01:00
parent badda0bb1c
commit 059d704404
23 changed files with 1944 additions and 129 deletions

View file

@ -1,11 +1,9 @@
// This is the code library.
export default `
new Circle({
point: [200, 200],
})
new Rectangle({
point: [400, 300],
})
class Circle {
greet(): string {
return "Hello!"
}
}
`

View file

@ -2,10 +2,15 @@ import Editor, { Monaco } from '@monaco-editor/react'
import useTheme from 'hooks/useTheme'
import prettier from 'prettier/standalone'
import parserTypeScript from 'prettier/parser-typescript'
import codeAsString from './code-as-string'
// import codeAsString from './code-as-string'
import typesImport from './types-import'
import React, { useCallback, useEffect, useRef } from 'react'
import styled from 'styles'
import { IMonaco, IMonacoEditor } from 'types'
import * as monaco from 'monaco-editor/esm/vs/editor/editor.api'
export type IMonaco = typeof monaco
export type IMonacoEditor = monaco.editor.IStandaloneCodeEditor
interface Props {
value: string
@ -54,22 +59,27 @@ export default function CodeEditor({
})
monaco.languages.typescript.typescriptDefaults.setEagerModelSync(true)
monaco.languages.typescript.javascriptDefaults.setEagerModelSync(true)
monaco.languages.typescript.javascriptDefaults.setDiagnosticsOptions({
noSemanticValidation: true,
noSyntaxValidation: true,
// noSemanticValidation: true,
// noSyntaxValidation: true,
})
monaco.languages.typescript.typescriptDefaults.setDiagnosticsOptions({
noSemanticValidation: true,
noSyntaxValidation: true,
// noSemanticValidation: true,
// noSyntaxValidation: true,
})
monaco.languages.typescript.javascriptDefaults.addExtraLib(codeAsString)
monaco.languages.typescript.typescriptDefaults.addExtraLib(
typesImport.content
)
monaco.languages.registerDocumentFormattingEditProvider('javascript', {
monaco.languages.typescript.javascriptDefaults.addExtraLib(
typesImport.content
)
monaco.languages.registerDocumentFormattingEditProvider('typescript', {
async provideDocumentFormattingEdits(model) {
const text = prettier.format(model.getValue(), {
parser: 'typescript',
@ -192,7 +202,7 @@ export default function CodeEditor({
<EditorContainer onKeyDown={handleKeydown} onKeyUp={handleKeyUp}>
<Editor
height="100%"
language="javascript"
language="typescript"
value={value}
theme={theme === 'dark' ? 'vs-dark' : 'light'}
beforeMount={handleBeforeMount}

File diff suppressed because it is too large Load diff

View file

@ -6,22 +6,15 @@ import ToolsPanel from './tools-panel/tools-panel'
import StylePanel from './style-panel/style-panel'
import styled from 'styles'
import PagePanel from './page-panel/page-panel'
// import dynamic from 'next/dynamic'
// import ControlsPanel from './controls-panel/controls-panel'
// import { useSelector } from 'state'
// const CodePanel = dynamic(() => import('./code-panel/code-panel'))
import CodePanel from './code-panel/code-panel'
export default function Editor(): JSX.Element {
useKeyboardEvents()
useLoadOnMount()
// const hasControls = useSelector(
// (s) => Object.keys(s.data.codeControls).length > 0
// )
return (
<Layout>
{/* <CodePanel /> */}
<CodePanel />
<PagePanel />
<Spacer />
<StylePanel />

View file

@ -38,7 +38,7 @@ export const Root = styled('div', {
left: 8,
right: 8,
bottom: 48,
maxWidth: 720,
maxWidth: 680,
zIndex: 1000,
},
},

View file

@ -11,6 +11,7 @@
"format": "prettier --write .",
"lint": "eslint . --ext ts --ext tsx --ext js",
"test": "jest",
"scripts": "node scripts/type-gen",
"test:watch": "jest --watchAll",
"test:update": "jest --updateSnapshot",
"test-all": "yarn lint && yarn type-check && yarn test"

56
scripts/type-gen.js Normal file
View file

@ -0,0 +1,56 @@
// @ts-check
/*
Type gen script
This script will generate TypeScript declarations for the code editor. It reads
the global types, as well as all of the code classes, and writes them into a
single file as a string. This string is fed into the Monaco editor as an extraLib.
*/
const fs = require("fs/promises")
async function copyTypesToFile() {
const types = await fs.readFile(__dirname + "/../types.ts", 'utf8')
const codeIndex = await fs.readFile(__dirname + "/../state/code/index.ts", 'utf8')
const codeDot = await fs.readFile(__dirname + "/../state/code/dot.ts", 'utf8')
const codeEllipse = await fs.readFile(__dirname + "/../state/code/ellipse.ts", 'utf8')
const codeLine = await fs.readFile(__dirname + "/../state/code/line.ts", 'utf8')
const codePolyline = await fs.readFile(__dirname + "/../state/code/polyline.ts", 'utf8')
const codeRay = await fs.readFile(__dirname + "/../state/code/ray.ts", 'utf8')
const codeArrow = await fs.readFile(__dirname + "/../state/code/arrow.ts", 'utf8')
const codeDraw = await fs.readFile(__dirname + "/../state/code/draw.ts", 'utf8')
const codeRectangle = await fs.readFile(__dirname + "/../state/code/rectangle.ts", 'utf8')
const codeVector = await fs.readFile(__dirname + "/../utils/vec.ts", 'utf8')
const codeUtils = await fs.readFile(__dirname + "/../state/code/utils.ts", 'utf8')
const content = `
// HEY! DO NOT MODIFY THIS FILE. THE CONTENTS OF THIS FILE
// ARE AUTO-GENERATED BY A SCRIPT AT: /scripts/type-gen.js
// ANY CHANGES WILL BE LOST WHEN THE SCRIPT RUNS AGAIN!
export default {` + `
name: "types.ts",
content: \`
${types}
${codeIndex.match(/export default(.|\n)*$/g)[0]}
${codeDot.match(/\/\*\*(.|\n)*$/g)[0]}
${codeEllipse.match(/\/\*\*(.|\n)*$/g)[0]}
${codeLine.match(/\/\*\*(.|\n)*$/g)[0]}
${codePolyline.match(/\/\*\*(.|\n)*$/g)[0]}
${codeRay.match(/\/\*\*(.|\n)*$/g)[0]}
${codeRectangle.match(/\/\*\*(.|\n)*$/g)[0]}
${codeArrow.match(/\/\*\*(.|\n)*$/g)[0]}
${codeDraw.match(/\/\*\*(.|\n)*$/g)[0]}
${codeUtils.match(/\/\*\*(.|\n)*$/g)[0]}
${codeVector}
\`
}`.replaceAll("export default", "").replaceAll("export ", "")
await fs.writeFile(__dirname + "/../components/code-panel/types-import.ts", content)
}
// Kickoff
copyTypesToFile()

101
state/code/arrow.ts Normal file
View file

@ -0,0 +1,101 @@
import CodeShape from './index'
import { uniqueId } from 'utils/utils'
import { ArrowShape, Decoration, ShapeStyles, ShapeType } from 'types'
import { defaultStyle } from 'state/shape-styles'
import { getShapeUtils } from 'state/shape-utils'
import Vec from 'utils/vec'
/**
* ## Draw
*/
export default class Arrow extends CodeShape<ArrowShape> {
constructor(
props = {} as Partial<ArrowShape> &
Partial<ShapeStyles> & { start?: number[]; end?: number[] }
) {
const { start = [0, 0], end = [0, 0] } = props
const {
point = [0, 0],
handles = {
start: {
id: 'start',
index: 0,
point: start,
},
end: {
id: 'end',
index: 1,
point: end,
},
bend: {
id: 'bend',
index: 2,
point: Vec.med(start, end),
},
},
} = props
super({
id: uniqueId(),
seed: Math.random(),
type: ShapeType.Arrow,
isGenerated: false,
name: 'Arrow',
parentId: 'page1',
childIndex: 0,
point,
rotation: 0,
isAspectRatioLocked: false,
isLocked: false,
isHidden: false,
bend: 0,
handles,
decorations: {
start: null,
middle: null,
end: Decoration.Arrow,
},
...props,
style: {
...defaultStyle,
...props.style,
isFilled: false,
},
})
}
get start(): number[] {
return this.shape.handles.start.point
}
set start(point: number[]) {
getShapeUtils(this.shape).onHandleChange(this.shape, {
start: { ...this.shape.handles.start, point },
})
}
get middle(): number[] {
return this.shape.handles.bend.point
}
set middle(point: number[]) {
getShapeUtils(this.shape).onHandleChange(this.shape, {
bend: { ...this.shape.handles.bend, point },
})
}
get end(): number[] {
return this.shape.handles.end.point
}
set end(point: number[]) {
getShapeUtils(this.shape).onHandleChange(this.shape, {
end: { ...this.shape.handles.end, point },
})
}
get bend(): number {
return this.shape.bend
}
}

View file

@ -1,13 +1,13 @@
import CodeShape from './index'
import { uniqueId } from 'utils/utils'
import { DotShape, ShapeType } from 'types'
import { DotShape, ShapeStyles, ShapeType } from 'types'
import { defaultStyle } from 'state/shape-styles'
import Utils from './utils'
/**
* ## Dot
*/
export default class Dot extends CodeShape<DotShape> {
constructor(props = {} as Partial<DotShape>) {
props.point = Utils.vectorToPoint(props.point)
constructor(props = {} as Partial<DotShape> & Partial<ShapeStyles>) {
super({
id: uniqueId(),
seed: Math.random(),
@ -29,12 +29,4 @@ export default class Dot extends CodeShape<DotShape> {
},
})
}
export(): DotShape {
const shape = { ...this.shape }
shape.point = Utils.vectorToPoint(shape.point)
return shape
}
}

32
state/code/draw.ts Normal file
View file

@ -0,0 +1,32 @@
import CodeShape from './index'
import { uniqueId } from 'utils/utils'
import { DrawShape, ShapeType } from 'types'
import { defaultStyle } from 'state/shape-styles'
/**
* ## Draw
*/
export default class Draw extends CodeShape<DrawShape> {
constructor(props = {} as Partial<DrawShape>) {
super({
id: uniqueId(),
seed: Math.random(),
type: ShapeType.Draw,
isGenerated: false,
name: 'Draw',
parentId: 'page1',
childIndex: 0,
point: [0, 0],
points: [],
rotation: 0,
isAspectRatioLocked: false,
isLocked: false,
isHidden: false,
...props,
style: {
...defaultStyle,
...props.style,
},
})
}
}

View file

@ -1,13 +1,13 @@
import CodeShape from './index'
import { uniqueId } from 'utils/utils'
import { EllipseShape, ShapeType } from 'types'
import Utils from './utils'
import { EllipseShape, ShapeStyles, ShapeType } from 'types'
import { defaultStyle } from 'state/shape-styles'
/**
* ## Ellipse
*/
export default class Ellipse extends CodeShape<EllipseShape> {
constructor(props = {} as Partial<EllipseShape>) {
props.point = Utils.vectorToPoint(props.point)
constructor(props = {} as Partial<EllipseShape> & Partial<ShapeStyles>) {
super({
id: uniqueId(),
seed: Math.random(),
@ -17,8 +17,8 @@ export default class Ellipse extends CodeShape<EllipseShape> {
name: 'Ellipse',
childIndex: 0,
point: [0, 0],
radiusX: 20,
radiusY: 20,
radiusX: 50,
radiusY: 50,
rotation: 0,
isAspectRatioLocked: false,
isLocked: false,
@ -28,14 +28,6 @@ export default class Ellipse extends CodeShape<EllipseShape> {
})
}
export(): EllipseShape {
const shape = { ...this.shape }
shape.point = Utils.vectorToPoint(shape.point)
return shape
}
get radiusX(): number {
return this.shape.radiusX
}

View file

@ -4,11 +4,14 @@ import Polyline from './polyline'
import Dot from './dot'
import Ray from './ray'
import Line from './line'
import Vector from './vector'
import Arrow from './arrow'
import Draw from './draw'
import Utils from './utils'
import Vec from 'utils/vec'
import { NumberControl, VectorControl, codeControls, controls } from './control'
import { codeShapes } from './index'
import { CodeControl, Data, Shape } from 'types'
import { getPage } from 'utils/utils'
const baseScope = {
Dot,
@ -17,8 +20,10 @@ const baseScope = {
Line,
Polyline,
Rectangle,
Vector,
Vec,
Utils,
Arrow,
Draw,
VectorControl,
NumberControl,
}
@ -45,9 +50,11 @@ export function generateFromCode(
new Function(...Object.keys(scope), `${code}`)(...Object.values(scope))
const generatedShapes = Array.from(codeShapes.values()).map(
(instance) => instance.shape
)
const generatedShapes = Array.from(codeShapes.values()).map((instance) => ({
...instance.shape,
isGenerated: true,
parentId: getPage(data).id,
}))
const generatedControls = Array.from(codeControls.values())

View file

@ -1,8 +1,6 @@
import { Mutable, Shape, ShapeUtility } from 'types'
import { createShape, getShapeUtils } from 'state/shape-utils'
import vec from 'utils/vec'
import Vector from './vector'
import Utils from './utils'
export const codeShapes = new Set<CodeShape<Shape>>([])
@ -20,20 +18,24 @@ export default class CodeShape<T extends Shape> {
codeShapes.add(this)
}
export(): Mutable<T> {
return { ...this._shape }
}
destroy(): void {
codeShapes.delete(this)
}
moveTo(point: Vector): CodeShape<T> {
this.utils.setProperty(this._shape, 'point', Utils.vectorToPoint(point))
moveTo(point: number[]): CodeShape<T> {
this.utils.setProperty(this._shape, 'point', point)
return this
}
translate(delta: Vector): CodeShape<T> {
translate(delta: number[]): CodeShape<T> {
this.utils.setProperty(
this._shape,
'point',
vec.add(this._shape.point, Utils.vectorToPoint(delta))
vec.add(this._shape.point, delta)
)
return this
}
@ -48,8 +50,8 @@ export default class CodeShape<T extends Shape> {
return this
}
hitTest(point: Vector): CodeShape<T> {
this.utils.hitTest(this.shape, Utils.vectorToPoint(point))
hitTest(point: number[]): CodeShape<T> {
this.utils.hitTest(this.shape, point)
return this
}

View file

@ -1,14 +1,13 @@
import CodeShape from './index'
import { uniqueId } from 'utils/utils'
import { LineShape, ShapeType } from 'types'
import { LineShape, ShapeStyles, ShapeType } from 'types'
import { defaultStyle } from 'state/shape-styles'
import Utils from './utils'
/**
* ## Line
*/
export default class Line extends CodeShape<LineShape> {
constructor(props = {} as Partial<LineShape>) {
props.point = Utils.vectorToPoint(props.point)
props.direction = Utils.vectorToPoint(props.direction)
constructor(props = {} as Partial<LineShape> & Partial<ShapeStyles>) {
super({
id: uniqueId(),
seed: Math.random(),
@ -32,15 +31,6 @@ export default class Line extends CodeShape<LineShape> {
})
}
export(): LineShape {
const shape = { ...this.shape }
shape.point = Utils.vectorToPoint(shape.point)
shape.direction = Utils.vectorToPoint(shape.direction)
return shape
}
get direction(): number[] {
return this.shape.direction
}

View file

@ -1,14 +1,13 @@
import CodeShape from './index'
import { uniqueId } from 'utils/utils'
import { PolylineShape, ShapeType } from 'types'
import { PolylineShape, ShapeStyles, ShapeType } from 'types'
import { defaultStyle } from 'state/shape-styles'
import Utils from './utils'
/**
* ## Polyline
*/
export default class Polyline extends CodeShape<PolylineShape> {
constructor(props = {} as Partial<PolylineShape>) {
props.point = Utils.vectorToPoint(props.point)
props.points = props.points.map(Utils.vectorToPoint)
constructor(props = {} as Partial<PolylineShape> & Partial<ShapeStyles>) {
super({
id: uniqueId(),
seed: Math.random(),
@ -28,15 +27,6 @@ export default class Polyline extends CodeShape<PolylineShape> {
})
}
export(): PolylineShape {
const shape = { ...this.shape }
shape.point = Utils.vectorToPoint(shape.point)
shape.points = shape.points.map(Utils.vectorToPoint)
return shape
}
get points(): number[][] {
return this.shape.points
}

View file

@ -1,14 +1,13 @@
import CodeShape from './index'
import { uniqueId } from 'utils/utils'
import { RayShape, ShapeType } from 'types'
import { RayShape, ShapeStyles, ShapeType } from 'types'
import { defaultStyle } from 'state/shape-styles'
import Utils from './utils'
/**
* ## Ray
*/
export default class Ray extends CodeShape<RayShape> {
constructor(props = {} as Partial<RayShape>) {
props.point = Utils.vectorToPoint(props.point)
props.direction = Utils.vectorToPoint(props.direction)
constructor(props = {} as Partial<RayShape> & Partial<ShapeStyles>) {
super({
id: uniqueId(),
seed: Math.random(),
@ -32,15 +31,6 @@ export default class Ray extends CodeShape<RayShape> {
})
}
export(): RayShape {
const shape = { ...this.shape }
shape.point = Utils.vectorToPoint(shape.point)
shape.direction = Utils.vectorToPoint(shape.direction)
return shape
}
get direction(): number[] {
return this.shape.direction
}

View file

@ -1,14 +1,13 @@
import CodeShape from './index'
import { uniqueId } from 'utils/utils'
import { RectangleShape, ShapeType } from 'types'
import Utils from './utils'
import { RectangleShape, ShapeStyles, ShapeType } from 'types'
import { defaultStyle } from 'state/shape-styles'
/**
* ## Rectangle
*/
export default class Rectangle extends CodeShape<RectangleShape> {
constructor(props = {} as Partial<RectangleShape>) {
props.point = Utils.vectorToPoint(props.point)
props.size = Utils.vectorToPoint(props.size)
constructor(props = {} as Partial<RectangleShape> & Partial<ShapeStyles>) {
super({
id: uniqueId(),
seed: Math.random(),

View file

@ -1,6 +1,9 @@
import { Bounds } from 'types'
import Vector, { Point } from './vector'
/**
* ## Utils
*/
export default class Utils {
static vectorToPoint(point: number[] | Vector | undefined): number[] {
if (typeof point === 'undefined') {

View file

@ -8,6 +8,9 @@ export interface Point {
y: number
}
/**
* ## Vector
*/
export default class Vector {
x = 0
y = 0

View file

@ -60,6 +60,7 @@ export default function pasteCommand(data: Data, initialShapes: Shape[]): void {
parentId: oldSelectedIds[shape.parentId] || data.currentPageId,
childIndex: childIndex++,
point: vec.add(topLeft, topLeftOffset),
isGenerated: false,
}
}

View file

@ -1,4 +1,4 @@
import { Data, GroupShape, ShapeType } from 'types'
import { Data, GroupShape, Shape, ShapeType } from 'types'
import vec from 'utils/vec'
import BaseSession from './base-session'
import commands from 'state/commands'
@ -217,12 +217,13 @@ export function getTranslateSnapshot(data: Data) {
clones: selectedShapes
.filter((shape) => shape.type !== ShapeType.Group)
.flatMap((shape) => {
const clone = {
const clone: Shape = {
...shape,
id: uniqueId(),
seed: Math.random(),
parentId: shape.parentId,
childIndex: getChildIndexAbove(cData, shape.id),
isGenerated: false,
}
return clone

View file

@ -93,7 +93,52 @@ const initialData: Data = {
file0: {
id: 'file0',
name: 'index.ts',
code: ``,
code: `
const draw = new Draw({
points: [
[0, 0],
[0, 50],
[20, 80],
[56, 56],
[52, 52],
[80, 20],
[90, 90],
[100, 100],
],
})
const rectangle = new Rectangle({
point: [200, 0],
style: {
color: ColorStyle.Blue,
},
})
const ellipse = new Ellipse({
point: [400, 0],
})
const arrow = new Arrow({
start: [600, 0],
end: [700, 100],
})
const radius = 1000
const count = 100
const center = [350, 50]
for (let i = 0; i < count; i++) {
const point = Vec.rotWith(
Vec.add(center, [radius, 0]),
center,
(Math.PI * 2 * i) / count
)
const dot = new Dot({
point,
})
}
`,
},
},
},

View file

@ -1,5 +1,3 @@
import * as monaco from 'monaco-editor/esm/vs/editor/editor.api'
/* -------------------------------------------------- */
/* Client State */
/* -------------------------------------------------- */
@ -355,10 +353,6 @@ export enum DistributeType {
/* Code Editor */
/* -------------------------------------------------- */
export type IMonaco = typeof monaco
export type IMonacoEditor = monaco.editor.IStandaloneCodeEditor
export enum ControlType {
Number = 'number',
Vector = 'vector',