Adds shape tests (some stubs), fixes bugs
This commit is contained in:
parent
322ce81fb6
commit
70c8c84790
15 changed files with 550 additions and 43 deletions
|
@ -1,18 +1,62 @@
|
||||||
|
import { ArrowShape, ShapeType } from 'types'
|
||||||
import TestState from '../test-utils'
|
import TestState from '../test-utils'
|
||||||
|
|
||||||
describe('arrow shape', () => {
|
describe('arrow shape', () => {
|
||||||
const tt = new TestState()
|
const tt = new TestState()
|
||||||
tt.resetDocumentState().send('SELECTED_ARROW_TOOL').save()
|
tt.resetDocumentState().save()
|
||||||
|
|
||||||
|
describe('creating arrows', () => {
|
||||||
it('creates shape', () => {
|
it('creates shape', () => {
|
||||||
// TODO
|
tt.reset().restore().send('SELECTED_ARROW_TOOL')
|
||||||
null
|
|
||||||
|
expect(tt.state.isIn('arrow.creating')).toBe(true)
|
||||||
|
|
||||||
|
tt.startClick('canvas').movePointerBy([100, 100]).stopClick('canvas')
|
||||||
|
|
||||||
|
const id = tt.getSortedPageShapeIds()[0]
|
||||||
|
|
||||||
|
const shape = tt.getShape<ArrowShape>(id)
|
||||||
|
|
||||||
|
tt.assertShapeType(id, ShapeType.Arrow)
|
||||||
|
|
||||||
|
expect(shape.handles.start.point).toEqual([0, 0])
|
||||||
|
expect(shape.handles.bend.point).toEqual([50.5, 50.5])
|
||||||
|
expect(shape.handles.end.point).toEqual([101, 101])
|
||||||
|
})
|
||||||
|
|
||||||
|
it('creates shapes when pointing a shape', () => {
|
||||||
|
tt.reset().restore().send('SELECTED_ARROW_TOOL').send('TOGGLED_TOOL_LOCK')
|
||||||
|
|
||||||
|
tt.startClick('canvas').movePointerBy([100, 100]).stopClick('canvas')
|
||||||
|
tt.startClick('canvas').movePointerBy([-200, 100]).stopClick('canvas')
|
||||||
|
|
||||||
|
expect(tt.getSortedPageShapeIds()).toHaveLength(2)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('creates shapes when shape locked', () => {
|
||||||
|
tt.reset()
|
||||||
|
.restore()
|
||||||
|
.createShape(
|
||||||
|
{
|
||||||
|
type: ShapeType.Rectangle,
|
||||||
|
point: [0, 0],
|
||||||
|
size: [100, 100],
|
||||||
|
childIndex: 1,
|
||||||
|
},
|
||||||
|
'rect1'
|
||||||
|
)
|
||||||
|
.send('SELECTED_ARROW_TOOL')
|
||||||
|
|
||||||
|
tt.startClick('rect1').movePointerBy([100, 100]).stopClick('canvas')
|
||||||
|
|
||||||
|
expect(tt.getSortedPageShapeIds()).toHaveLength(2)
|
||||||
})
|
})
|
||||||
|
|
||||||
it('cancels shape while creating', () => {
|
it('cancels shape while creating', () => {
|
||||||
// TODO
|
// TODO
|
||||||
null
|
null
|
||||||
})
|
})
|
||||||
|
})
|
||||||
|
|
||||||
it('moves shape', () => {
|
it('moves shape', () => {
|
||||||
// TODO
|
// TODO
|
||||||
|
|
101
__tests__/shapes/draw.test.ts
Normal file
101
__tests__/shapes/draw.test.ts
Normal file
|
@ -0,0 +1,101 @@
|
||||||
|
import { ShapeType } from 'types'
|
||||||
|
import TestState from '../test-utils'
|
||||||
|
|
||||||
|
describe('draw shape', () => {
|
||||||
|
const tt = new TestState()
|
||||||
|
tt.resetDocumentState().save()
|
||||||
|
|
||||||
|
describe('creating draws', () => {
|
||||||
|
it('creates shape', () => {
|
||||||
|
tt.reset().restore().send('SELECTED_DRAW_TOOL')
|
||||||
|
|
||||||
|
expect(tt.state.isIn('draw.creating')).toBe(true)
|
||||||
|
|
||||||
|
tt.startClick('canvas').movePointerBy([100, 100]).stopClick('canvas')
|
||||||
|
|
||||||
|
const id = tt.getSortedPageShapeIds()[0]
|
||||||
|
|
||||||
|
tt.assertShapeType(id, ShapeType.Draw)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('creates shapes when pointing a shape', () => {
|
||||||
|
tt.reset().restore().send('SELECTED_DRAW_TOOL').send('TOGGLED_TOOL_LOCK')
|
||||||
|
|
||||||
|
tt.startClick('canvas').movePointerBy([100, 100]).stopClick('canvas')
|
||||||
|
tt.startClick('canvas').movePointerBy([-200, 100]).stopClick('canvas')
|
||||||
|
|
||||||
|
expect(tt.getSortedPageShapeIds()).toHaveLength(2)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('creates shapes when shape locked', () => {
|
||||||
|
tt.reset()
|
||||||
|
.restore()
|
||||||
|
.createShape(
|
||||||
|
{
|
||||||
|
type: ShapeType.Rectangle,
|
||||||
|
point: [0, 0],
|
||||||
|
size: [100, 100],
|
||||||
|
childIndex: 1,
|
||||||
|
},
|
||||||
|
'rect1'
|
||||||
|
)
|
||||||
|
.send('SELECTED_DRAW_TOOL')
|
||||||
|
|
||||||
|
tt.startClick('rect1').movePointerBy([100, 100]).stopClick('canvas')
|
||||||
|
|
||||||
|
expect(tt.getSortedPageShapeIds()).toHaveLength(2)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('cancels shape while creating', () => {
|
||||||
|
// TODO
|
||||||
|
null
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
it('moves shape', () => {
|
||||||
|
// TODO
|
||||||
|
null
|
||||||
|
})
|
||||||
|
|
||||||
|
it('rotates shape', () => {
|
||||||
|
// TODO
|
||||||
|
null
|
||||||
|
})
|
||||||
|
|
||||||
|
it('rotates shape in a group', () => {
|
||||||
|
// TODO
|
||||||
|
null
|
||||||
|
})
|
||||||
|
|
||||||
|
it('measures shape bounds', () => {
|
||||||
|
// TODO
|
||||||
|
null
|
||||||
|
})
|
||||||
|
|
||||||
|
it('measures shape rotated bounds', () => {
|
||||||
|
// TODO
|
||||||
|
null
|
||||||
|
})
|
||||||
|
|
||||||
|
it('transforms single shape', () => {
|
||||||
|
// TODO
|
||||||
|
null
|
||||||
|
})
|
||||||
|
|
||||||
|
it('transforms in a group', () => {
|
||||||
|
// TODO
|
||||||
|
null
|
||||||
|
})
|
||||||
|
|
||||||
|
/* -------------------- Specific -------------------- */
|
||||||
|
|
||||||
|
it('closes the shape when the start and end points are near enough', () => {
|
||||||
|
// TODO
|
||||||
|
null
|
||||||
|
})
|
||||||
|
|
||||||
|
it('remains closed after resizing up', () => {
|
||||||
|
// TODO
|
||||||
|
null
|
||||||
|
})
|
||||||
|
})
|
104
__tests__/shapes/ellipse.test.ts
Normal file
104
__tests__/shapes/ellipse.test.ts
Normal file
|
@ -0,0 +1,104 @@
|
||||||
|
import { ShapeType } from 'types'
|
||||||
|
import TestState from '../test-utils'
|
||||||
|
|
||||||
|
describe('ellipse shape', () => {
|
||||||
|
const tt = new TestState()
|
||||||
|
tt.resetDocumentState().save()
|
||||||
|
|
||||||
|
describe('creating ellipses', () => {
|
||||||
|
it('creates shape', () => {
|
||||||
|
tt.reset().restore().send('SELECTED_ELLIPSE_TOOL')
|
||||||
|
|
||||||
|
expect(tt.state.isIn('ellipse.creating')).toBe(true)
|
||||||
|
|
||||||
|
tt.startClick('canvas').movePointerBy([100, 100]).stopClick('canvas')
|
||||||
|
|
||||||
|
const id = tt.getSortedPageShapeIds()[0]
|
||||||
|
|
||||||
|
tt.assertShapeType(id, ShapeType.Ellipse)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('creates shapes when pointing a shape', () => {
|
||||||
|
tt.reset()
|
||||||
|
.restore()
|
||||||
|
.send('SELECTED_ELLIPSE_TOOL')
|
||||||
|
.send('TOGGLED_TOOL_LOCK')
|
||||||
|
|
||||||
|
tt.startClick('canvas').movePointerBy([100, 100]).stopClick('canvas')
|
||||||
|
tt.startClick('canvas').movePointerBy([-200, 100]).stopClick('canvas')
|
||||||
|
|
||||||
|
expect(tt.getSortedPageShapeIds()).toHaveLength(2)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('creates shapes when shape locked', () => {
|
||||||
|
tt.reset()
|
||||||
|
.restore()
|
||||||
|
.createShape(
|
||||||
|
{
|
||||||
|
type: ShapeType.Rectangle,
|
||||||
|
point: [0, 0],
|
||||||
|
size: [100, 100],
|
||||||
|
childIndex: 1,
|
||||||
|
},
|
||||||
|
'rect1'
|
||||||
|
)
|
||||||
|
.send('SELECTED_ELLIPSE_TOOL')
|
||||||
|
|
||||||
|
tt.startClick('rect1').movePointerBy([100, 100]).stopClick('canvas')
|
||||||
|
|
||||||
|
expect(tt.getSortedPageShapeIds()).toHaveLength(2)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('cancels shape while creating', () => {
|
||||||
|
// TODO
|
||||||
|
null
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
it('moves shape', () => {
|
||||||
|
// TODO
|
||||||
|
null
|
||||||
|
})
|
||||||
|
|
||||||
|
it('rotates shape', () => {
|
||||||
|
// TODO
|
||||||
|
null
|
||||||
|
})
|
||||||
|
|
||||||
|
it('rotates shape in a group', () => {
|
||||||
|
// TODO
|
||||||
|
null
|
||||||
|
})
|
||||||
|
|
||||||
|
it('measures shape bounds', () => {
|
||||||
|
// TODO
|
||||||
|
null
|
||||||
|
})
|
||||||
|
|
||||||
|
it('measures shape rotated bounds', () => {
|
||||||
|
// TODO
|
||||||
|
null
|
||||||
|
})
|
||||||
|
|
||||||
|
it('transforms single shape', () => {
|
||||||
|
// TODO
|
||||||
|
null
|
||||||
|
})
|
||||||
|
|
||||||
|
it('transforms in a group', () => {
|
||||||
|
// TODO
|
||||||
|
null
|
||||||
|
})
|
||||||
|
|
||||||
|
/* -------------------- Specific -------------------- */
|
||||||
|
|
||||||
|
it('creates aspect-ratio-locked shape with shift key', () => {
|
||||||
|
// TODO
|
||||||
|
null
|
||||||
|
})
|
||||||
|
|
||||||
|
it('resizes aspect-ratio-locked shape with shift key', () => {
|
||||||
|
// TODO
|
||||||
|
null
|
||||||
|
})
|
||||||
|
})
|
104
__tests__/shapes/rectangle.test.ts
Normal file
104
__tests__/shapes/rectangle.test.ts
Normal file
|
@ -0,0 +1,104 @@
|
||||||
|
import { ShapeType } from 'types'
|
||||||
|
import TestState from '../test-utils'
|
||||||
|
|
||||||
|
describe('rectangle shape', () => {
|
||||||
|
const tt = new TestState()
|
||||||
|
tt.resetDocumentState().save()
|
||||||
|
|
||||||
|
describe('creating rectangles', () => {
|
||||||
|
it('creates shape', () => {
|
||||||
|
tt.reset().restore().send('SELECTED_RECTANGLE_TOOL')
|
||||||
|
|
||||||
|
expect(tt.state.isIn('rectangle.creating')).toBe(true)
|
||||||
|
|
||||||
|
tt.startClick('canvas').movePointerBy([100, 100]).stopClick('canvas')
|
||||||
|
|
||||||
|
const id = tt.getSortedPageShapeIds()[0]
|
||||||
|
|
||||||
|
tt.assertShapeType(id, ShapeType.Rectangle)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('creates shapes when pointing a shape', () => {
|
||||||
|
tt.reset()
|
||||||
|
.restore()
|
||||||
|
.send('SELECTED_RECTANGLE_TOOL')
|
||||||
|
.send('TOGGLED_TOOL_LOCK')
|
||||||
|
|
||||||
|
tt.startClick('canvas').movePointerBy([100, 100]).stopClick('canvas')
|
||||||
|
tt.startClick('canvas').movePointerBy([-200, 100]).stopClick('canvas')
|
||||||
|
|
||||||
|
expect(tt.getSortedPageShapeIds()).toHaveLength(2)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('creates shapes when shape locked', () => {
|
||||||
|
tt.reset()
|
||||||
|
.restore()
|
||||||
|
.createShape(
|
||||||
|
{
|
||||||
|
type: ShapeType.Rectangle,
|
||||||
|
point: [0, 0],
|
||||||
|
size: [100, 100],
|
||||||
|
childIndex: 1,
|
||||||
|
},
|
||||||
|
'rect1'
|
||||||
|
)
|
||||||
|
.send('SELECTED_RECTANGLE_TOOL')
|
||||||
|
|
||||||
|
tt.startClick('rect1').movePointerBy([100, 100]).stopClick('canvas')
|
||||||
|
|
||||||
|
expect(tt.getSortedPageShapeIds()).toHaveLength(2)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('cancels shape while creating', () => {
|
||||||
|
// TODO
|
||||||
|
null
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
it('moves shape', () => {
|
||||||
|
// TODO
|
||||||
|
null
|
||||||
|
})
|
||||||
|
|
||||||
|
it('rotates shape', () => {
|
||||||
|
// TODO
|
||||||
|
null
|
||||||
|
})
|
||||||
|
|
||||||
|
it('rotates shape in a group', () => {
|
||||||
|
// TODO
|
||||||
|
null
|
||||||
|
})
|
||||||
|
|
||||||
|
it('measures shape bounds', () => {
|
||||||
|
// TODO
|
||||||
|
null
|
||||||
|
})
|
||||||
|
|
||||||
|
it('measures shape rotated bounds', () => {
|
||||||
|
// TODO
|
||||||
|
null
|
||||||
|
})
|
||||||
|
|
||||||
|
it('transforms single shape', () => {
|
||||||
|
// TODO
|
||||||
|
null
|
||||||
|
})
|
||||||
|
|
||||||
|
it('transforms in a group', () => {
|
||||||
|
// TODO
|
||||||
|
null
|
||||||
|
})
|
||||||
|
|
||||||
|
/* -------------------- Specific -------------------- */
|
||||||
|
|
||||||
|
it('creates aspect-ratio-locked shape with shift key', () => {
|
||||||
|
// TODO
|
||||||
|
null
|
||||||
|
})
|
||||||
|
|
||||||
|
it('resizes aspect-ratio-locked shape with shift key', () => {
|
||||||
|
// TODO
|
||||||
|
null
|
||||||
|
})
|
||||||
|
})
|
79
__tests__/shapes/text.test.ts
Normal file
79
__tests__/shapes/text.test.ts
Normal file
|
@ -0,0 +1,79 @@
|
||||||
|
import TestState from '../test-utils'
|
||||||
|
|
||||||
|
describe('arrow shape', () => {
|
||||||
|
const tt = new TestState()
|
||||||
|
tt.resetDocumentState()
|
||||||
|
|
||||||
|
it('creates shape', () => {
|
||||||
|
tt.send('SELECTED_TEXT_TOOL')
|
||||||
|
|
||||||
|
expect(tt.state.isIn('text.creating')).toBe(true)
|
||||||
|
|
||||||
|
const id = tt.getSortedPageShapeIds()[0]
|
||||||
|
|
||||||
|
tt.clickCanvas()
|
||||||
|
|
||||||
|
expect(tt.state.isIn('editingShape')).toBe(true)
|
||||||
|
|
||||||
|
tt.send('EDITED_SHAPE', {
|
||||||
|
id,
|
||||||
|
change: { text: 'Hello world' },
|
||||||
|
})
|
||||||
|
|
||||||
|
tt.send('BLURRED_EDITING_SHAPE', { id: id })
|
||||||
|
|
||||||
|
expect(tt.state.isIn('selecting')).toBe(true)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('cancels shape while creating', () => {
|
||||||
|
// TODO
|
||||||
|
null
|
||||||
|
})
|
||||||
|
|
||||||
|
it('moves shape', () => {
|
||||||
|
// TODO
|
||||||
|
null
|
||||||
|
})
|
||||||
|
|
||||||
|
it('rotates shape', () => {
|
||||||
|
// TODO
|
||||||
|
null
|
||||||
|
})
|
||||||
|
|
||||||
|
it('rotates shape in a group', () => {
|
||||||
|
// TODO
|
||||||
|
null
|
||||||
|
})
|
||||||
|
|
||||||
|
it('measures shape bounds', () => {
|
||||||
|
// TODO
|
||||||
|
null
|
||||||
|
})
|
||||||
|
|
||||||
|
it('measures shape rotated bounds', () => {
|
||||||
|
// TODO
|
||||||
|
null
|
||||||
|
})
|
||||||
|
|
||||||
|
it('transforms single shape', () => {
|
||||||
|
// TODO
|
||||||
|
null
|
||||||
|
})
|
||||||
|
|
||||||
|
it('transforms in a group', () => {
|
||||||
|
// TODO
|
||||||
|
null
|
||||||
|
})
|
||||||
|
|
||||||
|
/* -------------------- Specific -------------------- */
|
||||||
|
|
||||||
|
it('scales', () => {
|
||||||
|
// TODO
|
||||||
|
null
|
||||||
|
})
|
||||||
|
|
||||||
|
it('selects different text on tap while editing', () => {
|
||||||
|
// TODO
|
||||||
|
null
|
||||||
|
})
|
||||||
|
})
|
|
@ -106,7 +106,7 @@ class TestState {
|
||||||
*/
|
*/
|
||||||
getSortedPageShapeIds(): string[] {
|
getSortedPageShapeIds(): string[] {
|
||||||
return Object.values(
|
return Object.values(
|
||||||
this.data.document.pages[this.data.currentParentId].shapes
|
this.data.document.pages[this.data.currentPageId].shapes
|
||||||
)
|
)
|
||||||
.sort((a, b) => a.childIndex - b.childIndex)
|
.sort((a, b) => a.childIndex - b.childIndex)
|
||||||
.map((shape) => shape.id)
|
.map((shape) => shape.id)
|
||||||
|
@ -257,6 +257,14 @@ class TestState {
|
||||||
const shape = tld.getShape(this.data, id)
|
const shape = tld.getShape(this.data, id)
|
||||||
const [x, y] = shape ? vec.add(shape.point, [1, 1]) : [0, 0]
|
const [x, y] = shape ? vec.add(shape.point, [1, 1]) : [0, 0]
|
||||||
|
|
||||||
|
if (id === 'canvas') {
|
||||||
|
this.state.send(
|
||||||
|
'POINTED_CANVAS',
|
||||||
|
inputs.pointerDown(TestState.point({ x, y, ...options }), id)
|
||||||
|
)
|
||||||
|
return this
|
||||||
|
}
|
||||||
|
|
||||||
this.state.send(
|
this.state.send(
|
||||||
'POINTED_SHAPE',
|
'POINTED_SHAPE',
|
||||||
inputs.pointerDown(TestState.point({ x, y, ...options }), id)
|
inputs.pointerDown(TestState.point({ x, y, ...options }), id)
|
||||||
|
|
38
__tests__/tools.test.ts
Normal file
38
__tests__/tools.test.ts
Normal file
|
@ -0,0 +1,38 @@
|
||||||
|
import TestState from './test-utils'
|
||||||
|
|
||||||
|
const TOOLS = [
|
||||||
|
'draw',
|
||||||
|
'rectangle',
|
||||||
|
'ellipse',
|
||||||
|
'arrow',
|
||||||
|
'text',
|
||||||
|
'line',
|
||||||
|
'ray',
|
||||||
|
'dot',
|
||||||
|
]
|
||||||
|
|
||||||
|
describe('when selecting tools', () => {
|
||||||
|
const tt = new TestState()
|
||||||
|
|
||||||
|
TOOLS.forEach((tool) => {
|
||||||
|
it(`selects ${tool} tool`, () => {
|
||||||
|
tt.reset().send(`SELECTED_${tool.toUpperCase()}_TOOL`)
|
||||||
|
|
||||||
|
expect(tt.data.activeTool).toBe(tool)
|
||||||
|
expect(tt.state.isIn(tool)).toBe(true)
|
||||||
|
})
|
||||||
|
|
||||||
|
TOOLS.forEach((otherTool) => {
|
||||||
|
if (otherTool === tool) return
|
||||||
|
|
||||||
|
it(`selects ${tool} tool from ${otherTool} tool`, () => {
|
||||||
|
tt.reset()
|
||||||
|
.send(`SELECTED_${tool.toUpperCase()}_TOOL`)
|
||||||
|
.send(`SELECTED_${otherTool.toUpperCase()}_TOOL`)
|
||||||
|
|
||||||
|
expect(tt.data.activeTool).toBe(otherTool)
|
||||||
|
expect(tt.state.isIn(otherTool)).toBe(true)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
|
@ -1,9 +0,0 @@
|
||||||
// This is the code library.
|
|
||||||
|
|
||||||
export default `
|
|
||||||
class Circle {
|
|
||||||
greet(): string {
|
|
||||||
return "Hello!"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
`
|
|
|
@ -1,8 +0,0 @@
|
||||||
export default `new Circle({
|
|
||||||
point: [200, 200],
|
|
||||||
})
|
|
||||||
|
|
||||||
new Rectangle({
|
|
||||||
point: [400, 300],
|
|
||||||
})
|
|
||||||
`
|
|
|
@ -62,6 +62,8 @@ enum FontSize {
|
||||||
ExtraLarge = 'ExtraLarge',
|
ExtraLarge = 'ExtraLarge',
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type Theme = 'dark' | 'light'
|
||||||
|
|
||||||
type ShapeStyles = {
|
type ShapeStyles = {
|
||||||
color: ColorStyle
|
color: ColorStyle
|
||||||
size: SizeStyle
|
size: SizeStyle
|
||||||
|
@ -214,6 +216,16 @@ interface CodeResult {
|
||||||
error: CodeError
|
error: CodeError
|
||||||
}
|
}
|
||||||
|
|
||||||
|
interface ShapeTreeNode {
|
||||||
|
shape: Shape
|
||||||
|
children: ShapeTreeNode[]
|
||||||
|
isEditing: boolean
|
||||||
|
isHovered: boolean
|
||||||
|
isSelected: boolean
|
||||||
|
isDarkMode: boolean
|
||||||
|
isCurrentParent: boolean
|
||||||
|
}
|
||||||
|
|
||||||
/* -------------------------------------------------- */
|
/* -------------------------------------------------- */
|
||||||
/* Editor UI */
|
/* Editor UI */
|
||||||
/* -------------------------------------------------- */
|
/* -------------------------------------------------- */
|
||||||
|
@ -545,8 +557,12 @@ interface ShapeUtility<K extends Shape> {
|
||||||
render(
|
render(
|
||||||
this: ShapeUtility<K>,
|
this: ShapeUtility<K>,
|
||||||
shape: K,
|
shape: K,
|
||||||
info: {
|
info?: {
|
||||||
isEditing: boolean
|
isEditing?: boolean
|
||||||
|
isHovered?: boolean
|
||||||
|
isSelected?: boolean
|
||||||
|
isCurrentParent?: boolean
|
||||||
|
isDarkMode?: boolean
|
||||||
ref?: React.MutableRefObject<HTMLTextAreaElement>
|
ref?: React.MutableRefObject<HTMLTextAreaElement>
|
||||||
}
|
}
|
||||||
): JSX.Element
|
): JSX.Element
|
||||||
|
@ -636,6 +652,8 @@ enum FontSize {
|
||||||
ExtraLarge = 'ExtraLarge',
|
ExtraLarge = 'ExtraLarge',
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type Theme = 'dark' | 'light'
|
||||||
|
|
||||||
type ShapeStyles = {
|
type ShapeStyles = {
|
||||||
color: ColorStyle
|
color: ColorStyle
|
||||||
size: SizeStyle
|
size: SizeStyle
|
||||||
|
@ -788,6 +806,16 @@ interface CodeResult {
|
||||||
error: CodeError
|
error: CodeError
|
||||||
}
|
}
|
||||||
|
|
||||||
|
interface ShapeTreeNode {
|
||||||
|
shape: Shape
|
||||||
|
children: ShapeTreeNode[]
|
||||||
|
isEditing: boolean
|
||||||
|
isHovered: boolean
|
||||||
|
isSelected: boolean
|
||||||
|
isDarkMode: boolean
|
||||||
|
isCurrentParent: boolean
|
||||||
|
}
|
||||||
|
|
||||||
/* -------------------------------------------------- */
|
/* -------------------------------------------------- */
|
||||||
/* Editor UI */
|
/* Editor UI */
|
||||||
/* -------------------------------------------------- */
|
/* -------------------------------------------------- */
|
||||||
|
@ -1119,8 +1147,12 @@ interface ShapeUtility<K extends Shape> {
|
||||||
render(
|
render(
|
||||||
this: ShapeUtility<K>,
|
this: ShapeUtility<K>,
|
||||||
shape: K,
|
shape: K,
|
||||||
info: {
|
info?: {
|
||||||
isEditing: boolean
|
isEditing?: boolean
|
||||||
|
isHovered?: boolean
|
||||||
|
isSelected?: boolean
|
||||||
|
isCurrentParent?: boolean
|
||||||
|
isDarkMode?: boolean
|
||||||
ref?: React.MutableRefObject<HTMLTextAreaElement>
|
ref?: React.MutableRefObject<HTMLTextAreaElement>
|
||||||
}
|
}
|
||||||
): JSX.Element
|
): JSX.Element
|
||||||
|
@ -1840,6 +1872,14 @@ type RequiredKeys<T> = {
|
||||||
return Array.from(new Set(items).values())
|
return Array.from(new Set(items).values())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Convert a set to an array.
|
||||||
|
* @param set
|
||||||
|
*/
|
||||||
|
static setToArray<T>(set: Set<T>): T[] {
|
||||||
|
return Array.from(set.values())
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the outer of between a circle and a point.
|
* Get the outer of between a circle and a point.
|
||||||
* @param C The circle's center.
|
* @param C The circle's center.
|
||||||
|
|
|
@ -9,10 +9,13 @@ export default function StatusBar(): JSX.Element {
|
||||||
|
|
||||||
const shapesInView = state.values.shapesToRender.length
|
const shapesInView = state.values.shapesToRender.length
|
||||||
|
|
||||||
const active = local.active.slice(1).map((s) => {
|
const active = local.active
|
||||||
|
.slice(1)
|
||||||
|
.map((s) => {
|
||||||
const states = s.split('.')
|
const states = s.split('.')
|
||||||
return states[states.length - 1]
|
return states[states.length - 1]
|
||||||
})
|
})
|
||||||
|
.join(' | ')
|
||||||
|
|
||||||
const log = local.log[0]
|
const log = local.log[0]
|
||||||
|
|
||||||
|
@ -21,7 +24,7 @@ export default function StatusBar(): JSX.Element {
|
||||||
return (
|
return (
|
||||||
<StatusBarContainer size={size}>
|
<StatusBarContainer size={size}>
|
||||||
<Section>
|
<Section>
|
||||||
{active.join(' | ')} - {log}
|
{active} - {log}
|
||||||
</Section>
|
</Section>
|
||||||
<Section>{shapesInView || '0'} Shapes</Section>
|
<Section>{shapesInView || '0'} Shapes</Section>
|
||||||
</StatusBarContainer>
|
</StatusBarContainer>
|
||||||
|
|
|
@ -21,7 +21,7 @@ export default function IsFilledPicker(): JSX.Element {
|
||||||
|
|
||||||
return (
|
return (
|
||||||
selectedShapes.length === 0 ||
|
selectedShapes.length === 0 ||
|
||||||
selectedShapes.every((shape) => getShapeUtils(shape).canStyleFill)
|
selectedShapes.some((shape) => getShapeUtils(shape).canStyleFill)
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
|
@ -135,7 +135,7 @@ export function PrimaryButton({
|
||||||
children,
|
children,
|
||||||
}: PrimaryToolButtonProps): JSX.Element {
|
}: PrimaryToolButtonProps): JSX.Element {
|
||||||
return (
|
return (
|
||||||
<Tooltip label={label}>
|
<Tooltip label={label[0].toUpperCase() + label.slice(1)}>
|
||||||
<PrimaryToolButton
|
<PrimaryToolButton
|
||||||
name={label}
|
name={label}
|
||||||
bp={{
|
bp={{
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
import isMobile from 'ismobilejs'
|
import { isMobile } from 'utils'
|
||||||
import { useEffect } from 'react'
|
import { useEffect } from 'react'
|
||||||
import state from 'state'
|
import state from 'state'
|
||||||
|
|
||||||
|
@ -11,7 +11,7 @@ function handleFocusOut() {
|
||||||
|
|
||||||
export default function useSafariFocusOutFix(): void {
|
export default function useSafariFocusOutFix(): void {
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (isMobile().apple) {
|
if (isMobile()) {
|
||||||
document.addEventListener('focusout', handleFocusOut)
|
document.addEventListener('focusout', handleFocusOut)
|
||||||
|
|
||||||
return () => {
|
return () => {
|
||||||
|
|
|
@ -93,8 +93,8 @@ const initialData: Data = {
|
||||||
const draw = new Draw({
|
const draw = new Draw({
|
||||||
points: [
|
points: [
|
||||||
...Utils.getPointsBetween([0, 0], [20, 50]),
|
...Utils.getPointsBetween([0, 0], [20, 50]),
|
||||||
...Utils.getPointsBetween([20, 50], [100, 20], 3),
|
...Utils.getPointsBetween([20, 50], [100, 20], { steps: 3 }),
|
||||||
...Utils.getPointsBetween([100, 20], [100, 100], 10),
|
...Utils.getPointsBetween([100, 20], [100, 100], { steps: 10 }),
|
||||||
[100, 100],
|
[100, 100],
|
||||||
],
|
],
|
||||||
})
|
})
|
||||||
|
@ -884,9 +884,9 @@ const state = createState({
|
||||||
},
|
},
|
||||||
arrow: {
|
arrow: {
|
||||||
onEnter: 'setActiveToolArrow',
|
onEnter: 'setActiveToolArrow',
|
||||||
initial: 'idle',
|
initial: 'creating',
|
||||||
states: {
|
states: {
|
||||||
idle: {
|
creating: {
|
||||||
on: {
|
on: {
|
||||||
CANCELLED: { to: 'selecting' },
|
CANCELLED: { to: 'selecting' },
|
||||||
POINTED_SHAPE: {
|
POINTED_SHAPE: {
|
||||||
|
@ -937,6 +937,9 @@ const state = createState({
|
||||||
POINTED_CANVAS: {
|
POINTED_CANVAS: {
|
||||||
to: 'ellipse.editing',
|
to: 'ellipse.editing',
|
||||||
},
|
},
|
||||||
|
POINTED_SHAPE: {
|
||||||
|
to: 'ellipse.editing',
|
||||||
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
editing: {
|
editing: {
|
||||||
|
|
Loading…
Reference in a new issue