Make updating code controls async
This commit is contained in:
parent
85dc3028b4
commit
0ee26a8493
27 changed files with 212 additions and 75 deletions
57
__tests__/__snapshots__/code.test.ts.snap
Normal file
57
__tests__/__snapshots__/code.test.ts.snap
Normal file
|
@ -0,0 +1,57 @@
|
|||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`selection creates a code control: generated code controls from code 1`] = `Object {}`;
|
||||
|
||||
exports[`selection generates shapes: generated rectangle from code 1`] = `
|
||||
Array [
|
||||
Object {
|
||||
"childIndex": 1,
|
||||
"id": "test-rectangle",
|
||||
"isAspectRatioLocked": false,
|
||||
"isGenerated": true,
|
||||
"isHidden": false,
|
||||
"isLocked": false,
|
||||
"name": "Test Rectangle",
|
||||
"parentId": "page1",
|
||||
"point": Array [
|
||||
100,
|
||||
100,
|
||||
],
|
||||
"radius": 2,
|
||||
"rotation": 0,
|
||||
"size": Array [
|
||||
200,
|
||||
200,
|
||||
],
|
||||
"style": Object {
|
||||
"color": "Red",
|
||||
"dash": "Dotted",
|
||||
"isFilled": false,
|
||||
"size": "Medium",
|
||||
},
|
||||
"type": "rectangle",
|
||||
},
|
||||
]
|
||||
`;
|
||||
|
||||
exports[`selection updates a code control: data in state after changing control 1`] = `
|
||||
Object {
|
||||
"test-number-control": Object {
|
||||
"id": "test-number-control",
|
||||
"label": "x",
|
||||
"step": 1,
|
||||
"type": "number",
|
||||
"value": 100,
|
||||
},
|
||||
"test-vector-control": Object {
|
||||
"id": "test-vector-control",
|
||||
"isNormalized": false,
|
||||
"label": "size",
|
||||
"type": "vector",
|
||||
"value": Array [
|
||||
0,
|
||||
0,
|
||||
],
|
||||
},
|
||||
}
|
||||
`;
|
|
@ -33,6 +33,7 @@ describe('selection', () => {
|
|||
it('generates shapes', async () => {
|
||||
const code = `
|
||||
const rectangle = new Rectangle({
|
||||
id: "test-rectangle",
|
||||
name: 'Test Rectangle',
|
||||
point: [100, 100],
|
||||
size: [200, 200],
|
||||
|
@ -45,20 +46,72 @@ describe('selection', () => {
|
|||
`
|
||||
|
||||
const { controls, shapes } = await generateFromCode(state.data, code)
|
||||
|
||||
state.send('GENERATED_FROM_CODE', { controls, shapes })
|
||||
expect(getShapes(state.data).length).toBe(1)
|
||||
|
||||
expect(getShapes(state.data)).toMatchSnapshot(
|
||||
'generated rectangle from code'
|
||||
)
|
||||
})
|
||||
|
||||
it('creates a code control', () => {
|
||||
null
|
||||
it('creates a code control', async () => {
|
||||
const code = `
|
||||
const rectangle = new Rectangle({
|
||||
id: "test-rectangle",
|
||||
name: 'Test Rectangle',
|
||||
point: [100, 100],
|
||||
size: [200, 200],
|
||||
style: {
|
||||
size: SizeStyle.Medium,
|
||||
color: ColorStyle.Red,
|
||||
dash: DashStyle.Dotted,
|
||||
},
|
||||
})
|
||||
`
|
||||
|
||||
const { controls, shapes } = await generateFromCode(state.data, code)
|
||||
|
||||
state.send('GENERATED_FROM_CODE', { controls, shapes })
|
||||
|
||||
expect(state.data.codeControls).toMatchSnapshot(
|
||||
'generated code controls from code'
|
||||
)
|
||||
})
|
||||
|
||||
it('updates a code control', () => {
|
||||
null
|
||||
it('updates a code control', async () => {
|
||||
const code = `
|
||||
const rectangle = new Rectangle({
|
||||
id: "test-rectangle",
|
||||
name: 'Test Rectangle',
|
||||
point: [100, 100],
|
||||
size: [200, 200],
|
||||
style: {
|
||||
size: SizeStyle.Medium,
|
||||
color: ColorStyle.Red,
|
||||
dash: DashStyle.Dotted,
|
||||
},
|
||||
})
|
||||
|
||||
it('updates a code control', () => {
|
||||
null
|
||||
new NumberControl({
|
||||
id: "test-number-control",
|
||||
label: "x"
|
||||
})
|
||||
|
||||
new VectorControl({
|
||||
id: "test-vector-control",
|
||||
label: "size"
|
||||
})
|
||||
`
|
||||
|
||||
const { controls, shapes } = await generateFromCode(state.data, code)
|
||||
|
||||
state.send('GENERATED_FROM_CODE', { controls, shapes })
|
||||
|
||||
state.send('CHANGED_CODE_CONTROL', { 'test-number-control': 100 })
|
||||
|
||||
expect(state.data.codeControls).toMatchSnapshot(
|
||||
'data in state after changing control'
|
||||
)
|
||||
})
|
||||
|
||||
/* -------------------- Readonly -------------------- */
|
||||
|
|
|
@ -65,7 +65,6 @@ type ShapeStyles = {
|
|||
|
||||
interface BaseShape {
|
||||
id: string
|
||||
seed: number
|
||||
type: ShapeType
|
||||
parentId: string
|
||||
childIndex: number
|
||||
|
@ -335,27 +334,27 @@ interface BaseCodeControl {
|
|||
|
||||
interface NumberCodeControl extends BaseCodeControl {
|
||||
type: ControlType.Number
|
||||
min: number
|
||||
max: number
|
||||
value: number
|
||||
step: number
|
||||
format: (value: number) => number
|
||||
min?: number
|
||||
max?: number
|
||||
step?: number
|
||||
format?: (value: number) => number
|
||||
}
|
||||
|
||||
interface VectorCodeControl extends BaseCodeControl {
|
||||
type: ControlType.Vector
|
||||
min: number
|
||||
max: number
|
||||
step: number
|
||||
value: number[]
|
||||
isNormalized: boolean
|
||||
format: (value: number[]) => number[]
|
||||
min?: number
|
||||
max?: number
|
||||
step?: number
|
||||
isNormalized?: boolean
|
||||
format?: (value: number[]) => number[]
|
||||
}
|
||||
|
||||
interface TextCodeControl extends BaseCodeControl {
|
||||
type: ControlType.Text
|
||||
value: string
|
||||
format: (value: string) => string
|
||||
format?: (value: string) => string
|
||||
}
|
||||
|
||||
interface SelectCodeControl<T extends string = ''>
|
||||
|
@ -1972,6 +1971,10 @@ interface ShapeUtility<K extends Shape> {
|
|||
return this
|
||||
}
|
||||
|
||||
get id(): string {
|
||||
return this._shape.id
|
||||
}
|
||||
|
||||
/**
|
||||
* The shape's underlying shape.
|
||||
*/
|
||||
|
@ -2057,7 +2060,7 @@ interface ShapeUtility<K extends Shape> {
|
|||
constructor(props = {} as ShapeProps<DotShape>) {
|
||||
super({
|
||||
id: uniqueId(),
|
||||
seed: Math.random(),
|
||||
|
||||
parentId: (window as any).currentPageId,
|
||||
type: ShapeType.Dot,
|
||||
isGenerated: true,
|
||||
|
@ -2085,7 +2088,6 @@ interface ShapeUtility<K extends Shape> {
|
|||
constructor(props = {} as ShapeProps<EllipseShape>) {
|
||||
super({
|
||||
id: uniqueId(),
|
||||
seed: Math.random(),
|
||||
parentId: (window as any).currentPageId,
|
||||
type: ShapeType.Ellipse,
|
||||
isGenerated: true,
|
||||
|
@ -2119,7 +2121,7 @@ interface ShapeUtility<K extends Shape> {
|
|||
constructor(props = {} as ShapeProps<LineShape>) {
|
||||
super({
|
||||
id: uniqueId(),
|
||||
seed: Math.random(),
|
||||
|
||||
parentId: (window as any).currentPageId,
|
||||
type: ShapeType.Line,
|
||||
isGenerated: true,
|
||||
|
@ -2152,7 +2154,7 @@ interface ShapeUtility<K extends Shape> {
|
|||
constructor(props = {} as ShapeProps<PolylineShape>) {
|
||||
super({
|
||||
id: uniqueId(),
|
||||
seed: Math.random(),
|
||||
|
||||
parentId: (window as any).currentPageId,
|
||||
type: ShapeType.Polyline,
|
||||
isGenerated: true,
|
||||
|
@ -2184,7 +2186,7 @@ interface ShapeUtility<K extends Shape> {
|
|||
constructor(props = {} as ShapeProps<RayShape>) {
|
||||
super({
|
||||
id: uniqueId(),
|
||||
seed: Math.random(),
|
||||
|
||||
type: ShapeType.Ray,
|
||||
isGenerated: true,
|
||||
name: 'Ray',
|
||||
|
@ -2242,7 +2244,7 @@ interface ShapeUtility<K extends Shape> {
|
|||
|
||||
super({
|
||||
id: uniqueId(),
|
||||
seed: Math.random(),
|
||||
|
||||
type: ShapeType.Arrow,
|
||||
isGenerated: false,
|
||||
name: 'Arrow',
|
||||
|
@ -2311,7 +2313,7 @@ interface ShapeUtility<K extends Shape> {
|
|||
constructor(props = {} as ShapeProps<DrawShape>) {
|
||||
super({
|
||||
id: uniqueId(),
|
||||
seed: Math.random(),
|
||||
|
||||
type: ShapeType.Draw,
|
||||
isGenerated: false,
|
||||
parentId: (window as any).currentPageId,
|
||||
|
@ -2339,7 +2341,7 @@ interface ShapeUtility<K extends Shape> {
|
|||
constructor(props = {} as ShapeProps<RectangleShape>) {
|
||||
super({
|
||||
id: uniqueId(),
|
||||
seed: Math.random(),
|
||||
|
||||
parentId: (window as any).currentPageId,
|
||||
type: ShapeType.Rectangle,
|
||||
isGenerated: true,
|
||||
|
|
1
next-env.d.ts
vendored
1
next-env.d.ts
vendored
|
@ -1,2 +1,3 @@
|
|||
/// <reference types="next" />
|
||||
/// <reference types="next/types/global" />
|
||||
/// <reference types="next/image-types/global" />
|
||||
|
|
|
@ -36,7 +36,7 @@ export default class Arrow extends CodeShape<ArrowShape> {
|
|||
|
||||
super({
|
||||
id: uniqueId(),
|
||||
seed: Math.random(),
|
||||
|
||||
type: ShapeType.Arrow,
|
||||
isGenerated: false,
|
||||
name: 'Arrow',
|
||||
|
|
|
@ -13,17 +13,17 @@ export const codeControls = new Set<CodeControl>([])
|
|||
/* ----------------- Start Copy Here ---------------- */
|
||||
|
||||
export class Control<T extends CodeControl> {
|
||||
control: T
|
||||
_control: T
|
||||
|
||||
constructor(control: Omit<T, 'id'>) {
|
||||
this.control = { ...control, id: uniqueId() } as T
|
||||
codeControls.add(this.control)
|
||||
constructor(control: T) {
|
||||
this._control = { ...control }
|
||||
codeControls.add(this._control)
|
||||
|
||||
// Could there be a better way to prevent this?
|
||||
// When updating, constructor should just bind to
|
||||
// the existing control rather than creating a new one?
|
||||
if (!(window as any).isUpdatingCode) {
|
||||
controls[this.control.label] = this.control.value
|
||||
controls[this._control.label] = this._control.value
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -32,39 +32,52 @@ export class Control<T extends CodeControl> {
|
|||
delete controls[this.control.label]
|
||||
}
|
||||
|
||||
get control(): T {
|
||||
return this._control
|
||||
}
|
||||
|
||||
get id(): string {
|
||||
return this.control.id
|
||||
}
|
||||
|
||||
get value(): T['value'] {
|
||||
return this.control.value
|
||||
}
|
||||
|
||||
set value(value: T['value']) {
|
||||
this.control.value = value
|
||||
}
|
||||
}
|
||||
|
||||
type ControlProps<T extends CodeControl> = Omit<Partial<T>, 'id' | 'type'>
|
||||
type ControlProps<T extends CodeControl> = Omit<Partial<T>, 'type'>
|
||||
|
||||
export class NumberControl extends Control<NumberCodeControl> {
|
||||
constructor(options: ControlProps<NumberCodeControl>) {
|
||||
const { label = 'Number', value = 0, step = 1 } = options
|
||||
const { id = uniqueId(), label = 'Number', value = 0, step = 1 } = options
|
||||
|
||||
super({
|
||||
type: ControlType.Number,
|
||||
...options,
|
||||
label,
|
||||
value,
|
||||
step,
|
||||
id,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
export class VectorControl extends Control<VectorCodeControl> {
|
||||
constructor(options: ControlProps<VectorCodeControl>) {
|
||||
const { label = 'Vector', value = [0, 0], isNormalized = false } = options
|
||||
const {
|
||||
id = uniqueId(),
|
||||
label = 'Vector',
|
||||
value = [0, 0],
|
||||
isNormalized = false,
|
||||
} = options
|
||||
|
||||
super({
|
||||
type: ControlType.Vector,
|
||||
...options,
|
||||
label,
|
||||
value,
|
||||
isNormalized,
|
||||
id,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
|
@ -9,7 +9,7 @@ export default class Dot extends CodeShape<DotShape> {
|
|||
constructor(props = {} as ShapeProps<DotShape>) {
|
||||
super({
|
||||
id: uniqueId(),
|
||||
seed: Math.random(),
|
||||
|
||||
parentId: (window as any).currentPageId,
|
||||
type: ShapeType.Dot,
|
||||
isGenerated: true,
|
||||
|
|
|
@ -9,7 +9,7 @@ export default class Draw extends CodeShape<DrawShape> {
|
|||
constructor(props = {} as ShapeProps<DrawShape>) {
|
||||
super({
|
||||
id: uniqueId(),
|
||||
seed: Math.random(),
|
||||
|
||||
type: ShapeType.Draw,
|
||||
isGenerated: false,
|
||||
parentId: (window as any).currentPageId,
|
||||
|
|
|
@ -9,7 +9,6 @@ export default class Ellipse extends CodeShape<EllipseShape> {
|
|||
constructor(props = {} as ShapeProps<EllipseShape>) {
|
||||
super({
|
||||
id: uniqueId(),
|
||||
seed: Math.random(),
|
||||
parentId: (window as any).currentPageId,
|
||||
type: ShapeType.Ellipse,
|
||||
isGenerated: true,
|
||||
|
|
|
@ -184,6 +184,10 @@ export default class CodeShape<T extends Shape> {
|
|||
return this
|
||||
}
|
||||
|
||||
get id(): string {
|
||||
return this._shape.id
|
||||
}
|
||||
|
||||
/**
|
||||
* The shape's underlying shape.
|
||||
*/
|
||||
|
|
|
@ -9,7 +9,7 @@ export default class Line extends CodeShape<LineShape> {
|
|||
constructor(props = {} as ShapeProps<LineShape>) {
|
||||
super({
|
||||
id: uniqueId(),
|
||||
seed: Math.random(),
|
||||
|
||||
parentId: (window as any).currentPageId,
|
||||
type: ShapeType.Line,
|
||||
isGenerated: true,
|
||||
|
|
|
@ -9,7 +9,7 @@ export default class Polyline extends CodeShape<PolylineShape> {
|
|||
constructor(props = {} as ShapeProps<PolylineShape>) {
|
||||
super({
|
||||
id: uniqueId(),
|
||||
seed: Math.random(),
|
||||
|
||||
parentId: (window as any).currentPageId,
|
||||
type: ShapeType.Polyline,
|
||||
isGenerated: true,
|
||||
|
|
|
@ -9,7 +9,7 @@ export default class Ray extends CodeShape<RayShape> {
|
|||
constructor(props = {} as ShapeProps<RayShape>) {
|
||||
super({
|
||||
id: uniqueId(),
|
||||
seed: Math.random(),
|
||||
|
||||
type: ShapeType.Ray,
|
||||
isGenerated: true,
|
||||
name: 'Ray',
|
||||
|
|
|
@ -9,7 +9,7 @@ export default class Rectangle extends CodeShape<RectangleShape> {
|
|||
constructor(props = {} as ShapeProps<RectangleShape>) {
|
||||
super({
|
||||
id: uniqueId(),
|
||||
seed: Math.random(),
|
||||
|
||||
parentId: (window as any).currentPageId,
|
||||
type: ShapeType.Rectangle,
|
||||
isGenerated: true,
|
||||
|
|
|
@ -218,7 +218,7 @@ export function getTranslateSnapshot(data: Data) {
|
|||
const clone: Shape = {
|
||||
...shape,
|
||||
id: uniqueId(),
|
||||
seed: Math.random(),
|
||||
|
||||
parentId: shape.parentId,
|
||||
childIndex: getChildIndexAbove(cData, shape.id),
|
||||
isGenerated: false,
|
||||
|
|
|
@ -61,7 +61,7 @@ const arrow = registerShapeUtils<ArrowShape>({
|
|||
|
||||
return {
|
||||
id: uniqueId(),
|
||||
seed: Math.random(),
|
||||
|
||||
type: ShapeType.Arrow,
|
||||
isGenerated: false,
|
||||
name: 'Arrow',
|
||||
|
|
|
@ -11,7 +11,7 @@ const dot = registerShapeUtils<DotShape>({
|
|||
create(props) {
|
||||
return {
|
||||
id: uniqueId(),
|
||||
seed: Math.random(),
|
||||
|
||||
type: ShapeType.Dot,
|
||||
isGenerated: false,
|
||||
name: 'Dot',
|
||||
|
|
|
@ -23,7 +23,7 @@ const draw = registerShapeUtils<DrawShape>({
|
|||
create(props) {
|
||||
return {
|
||||
id: uniqueId(),
|
||||
seed: Math.random(),
|
||||
|
||||
type: ShapeType.Draw,
|
||||
isGenerated: false,
|
||||
name: 'Draw',
|
||||
|
|
|
@ -24,7 +24,7 @@ const ellipse = registerShapeUtils<EllipseShape>({
|
|||
create(props) {
|
||||
return {
|
||||
id: uniqueId(),
|
||||
seed: Math.random(),
|
||||
|
||||
type: ShapeType.Ellipse,
|
||||
isGenerated: false,
|
||||
name: 'Ellipse',
|
||||
|
|
|
@ -15,7 +15,7 @@ const group = registerShapeUtils<GroupShape>({
|
|||
create(props) {
|
||||
return {
|
||||
id: uniqueId(),
|
||||
seed: Math.random(),
|
||||
|
||||
type: ShapeType.Group,
|
||||
isGenerated: false,
|
||||
name: 'Group',
|
||||
|
|
|
@ -13,7 +13,7 @@ const line = registerShapeUtils<LineShape>({
|
|||
create(props) {
|
||||
return {
|
||||
id: uniqueId(),
|
||||
seed: Math.random(),
|
||||
|
||||
type: ShapeType.Line,
|
||||
isGenerated: false,
|
||||
name: 'Line',
|
||||
|
|
|
@ -16,7 +16,7 @@ const polyline = registerShapeUtils<PolylineShape>({
|
|||
create(props) {
|
||||
return {
|
||||
id: uniqueId(),
|
||||
seed: Math.random(),
|
||||
|
||||
type: ShapeType.Polyline,
|
||||
isGenerated: false,
|
||||
name: 'Polyline',
|
||||
|
|
|
@ -13,7 +13,7 @@ const ray = registerShapeUtils<RayShape>({
|
|||
create(props) {
|
||||
return {
|
||||
id: uniqueId(),
|
||||
seed: Math.random(),
|
||||
|
||||
type: ShapeType.Ray,
|
||||
isGenerated: false,
|
||||
name: 'Ray',
|
||||
|
|
|
@ -15,7 +15,7 @@ const rectangle = registerShapeUtils<RectangleShape>({
|
|||
create(props) {
|
||||
return {
|
||||
id: uniqueId(),
|
||||
seed: Math.random(),
|
||||
|
||||
type: ShapeType.Rectangle,
|
||||
isGenerated: false,
|
||||
name: 'Rectangle',
|
||||
|
|
|
@ -53,7 +53,7 @@ const text = registerShapeUtils<TextShape>({
|
|||
create(props) {
|
||||
return {
|
||||
id: uniqueId(),
|
||||
seed: Math.random(),
|
||||
|
||||
type: ShapeType.Text,
|
||||
isGenerated: false,
|
||||
name: 'Text',
|
||||
|
|
|
@ -300,7 +300,7 @@ const state = createState({
|
|||
ZOOMED_CAMERA: 'zoomCamera',
|
||||
INCREASED_CODE_FONT_SIZE: 'increaseCodeFontSize',
|
||||
DECREASED_CODE_FONT_SIZE: 'decreaseCodeFontSize',
|
||||
CHANGED_CODE_CONTROL: 'updateControls',
|
||||
CHANGED_CODE_CONTROL: { to: 'updatingControls' },
|
||||
TOGGLED_TOOL_LOCK: 'toggleToolLock',
|
||||
ZOOMED_TO_SELECTION: {
|
||||
if: 'hasSelection',
|
||||
|
@ -605,6 +605,13 @@ const state = createState({
|
|||
CANCELLED: { do: 'cancelSession', to: 'selecting' },
|
||||
},
|
||||
},
|
||||
updatingControls: {
|
||||
onEnter: 'updateControls',
|
||||
async: {
|
||||
await: 'getUpdatedShapes',
|
||||
onResolve: { do: 'updateGeneratedShapes', to: 'selecting' },
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
editingShape: {
|
||||
|
@ -1742,6 +1749,15 @@ const state = createState({
|
|||
) {
|
||||
commands.generate(data, payload.shapes)
|
||||
},
|
||||
updateGeneratedShapes(data, payload, result: { shapes: Shape[] }) {
|
||||
setSelectedIds(data, [])
|
||||
|
||||
history.disable()
|
||||
|
||||
commands.generate(data, result.shapes)
|
||||
|
||||
history.enable()
|
||||
},
|
||||
setCodeControls(data, payload: { controls: CodeControl[] }) {
|
||||
data.codeControls = Object.fromEntries(
|
||||
payload.controls.map((control) => [control.id, control])
|
||||
|
@ -1757,21 +1773,6 @@ const state = createState({
|
|||
for (const key in payload) {
|
||||
data.codeControls[key].value = payload[key]
|
||||
}
|
||||
|
||||
history.disable()
|
||||
|
||||
setSelectedIds(data, [])
|
||||
|
||||
try {
|
||||
updateFromCode(
|
||||
data,
|
||||
data.document.code[data.currentCodeFileId].code
|
||||
).then(({ shapes }) => commands.generate(data, shapes))
|
||||
} catch (e) {
|
||||
console.error(e)
|
||||
}
|
||||
|
||||
history.enable()
|
||||
},
|
||||
|
||||
/* -------------------- Settings -------------------- */
|
||||
|
@ -1896,6 +1897,14 @@ const state = createState({
|
|||
return commonStyle
|
||||
},
|
||||
},
|
||||
asyncs: {
|
||||
async getUpdatedShapes(data) {
|
||||
return updateFromCode(
|
||||
data,
|
||||
data.document.code[data.currentCodeFileId].code
|
||||
)
|
||||
},
|
||||
},
|
||||
})
|
||||
|
||||
export default state
|
||||
|
|
1
types.ts
1
types.ts
|
@ -115,7 +115,6 @@ export type ShapeStyles = {
|
|||
|
||||
export interface BaseShape {
|
||||
id: string
|
||||
seed: number
|
||||
type: ShapeType
|
||||
parentId: string
|
||||
childIndex: number
|
||||
|
|
Loading…
Reference in a new issue