@reekon-tools/boldr-cad 0.1.0 → 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +81 -9
- package/dist/app/edit.d.ts +12 -1
- package/dist/app/edit.js +96 -11
- package/dist/app/edit.js.map +1 -1
- package/dist/app/state.d.ts +31 -4
- package/dist/app/state.js +31 -2
- package/dist/app/state.js.map +1 -1
- package/dist/data/memory.d.ts +5 -0
- package/dist/data/memory.js +2 -2
- package/dist/data/memory.js.map +1 -1
- package/dist/data/provider.d.ts +19 -7
- package/dist/data/sync.d.ts +7 -2
- package/dist/data/sync.js +115 -64
- package/dist/data/sync.js.map +1 -1
- package/dist/editor/actions.d.ts +1 -2
- package/dist/editor/actions.js +391 -113
- package/dist/editor/actions.js.map +1 -1
- package/dist/editor/confirm.d.ts +23 -0
- package/dist/editor/confirm.js +35 -0
- package/dist/editor/confirm.js.map +1 -0
- package/dist/editor/elevation.d.ts +191 -90
- package/dist/editor/elevation.js +812 -616
- package/dist/editor/elevation.js.map +1 -1
- package/dist/editor/face.d.ts +73 -0
- package/dist/editor/face.js +269 -0
- package/dist/editor/face.js.map +1 -0
- package/dist/editor/files.d.ts +6 -0
- package/dist/editor/files.js +50 -4
- package/dist/editor/files.js.map +1 -1
- package/dist/editor/gestures.d.ts +18 -3
- package/dist/editor/gestures.js +145 -30
- package/dist/editor/gestures.js.map +1 -1
- package/dist/editor/keypad.d.ts +19 -4
- package/dist/editor/keypad.js +39 -9
- package/dist/editor/keypad.js.map +1 -1
- package/dist/editor/keys.js +36 -4
- package/dist/editor/keys.js.map +1 -1
- package/dist/editor/laser.d.ts +20 -4
- package/dist/editor/laser.js +104 -21
- package/dist/editor/laser.js.map +1 -1
- package/dist/editor/linkActions.d.ts +49 -0
- package/dist/editor/linkActions.js +210 -0
- package/dist/editor/linkActions.js.map +1 -0
- package/dist/editor/linkFollow.d.ts +2 -0
- package/dist/editor/linkFollow.js +53 -0
- package/dist/editor/linkFollow.js.map +1 -0
- package/dist/editor/linkWrite.d.ts +13 -0
- package/dist/editor/linkWrite.js +84 -0
- package/dist/editor/linkWrite.js.map +1 -0
- package/dist/editor/links.d.ts +84 -0
- package/dist/editor/links.js +130 -0
- package/dist/editor/links.js.map +1 -0
- package/dist/editor/markup.d.ts +29 -3
- package/dist/editor/markup.js +95 -10
- package/dist/editor/markup.js.map +1 -1
- package/dist/editor/mode.d.ts +40 -2
- package/dist/editor/mode.js +71 -4
- package/dist/editor/mode.js.map +1 -1
- package/dist/editor/ports.d.ts +36 -6
- package/dist/editor/ports.js +10 -1
- package/dist/editor/ports.js.map +1 -1
- package/dist/editor/project.d.ts +38 -6
- package/dist/editor/project.js +167 -27
- package/dist/editor/project.js.map +1 -1
- package/dist/editor/selection.d.ts +10 -0
- package/dist/editor/selection.js +62 -8
- package/dist/editor/selection.js.map +1 -1
- package/dist/editor/status.js +3 -0
- package/dist/editor/status.js.map +1 -1
- package/dist/editor/tooltips.d.ts +5 -0
- package/dist/editor/tooltips.js +48 -0
- package/dist/editor/tooltips.js.map +1 -0
- package/dist/editor/view3d.d.ts +17 -0
- package/dist/editor/view3d.js +135 -0
- package/dist/editor/view3d.js.map +1 -0
- package/dist/editor/view3dState.d.ts +39 -0
- package/dist/editor/view3dState.js +44 -0
- package/dist/editor/view3dState.js.map +1 -0
- package/dist/export/dxf.js +1 -1
- package/dist/export/dxf.js.map +1 -1
- package/dist/export/pdf.js +17 -12
- package/dist/export/pdf.js.map +1 -1
- package/dist/export/plan.d.ts +23 -6
- package/dist/export/plan.js +127 -44
- package/dist/export/plan.js.map +1 -1
- package/dist/index.d.ts +5 -0
- package/dist/index.js +2 -0
- package/dist/index.js.map +1 -1
- package/dist/model/constraints.js +15 -4
- package/dist/model/constraints.js.map +1 -1
- package/dist/model/demo.js +3 -3
- package/dist/model/demo.js.map +1 -1
- package/dist/model/elevation.d.ts +6 -9
- package/dist/model/elevation.js +5 -13
- package/dist/model/elevation.js.map +1 -1
- package/dist/model/faces.d.ts +58 -0
- package/dist/model/faces.js +197 -0
- package/dist/model/faces.js.map +1 -0
- package/dist/model/facesolve.d.ts +72 -0
- package/dist/model/facesolve.js +369 -0
- package/dist/model/facesolve.js.map +1 -0
- package/dist/model/history.d.ts +3 -2
- package/dist/model/history.js +17 -11
- package/dist/model/history.js.map +1 -1
- package/dist/model/images.d.ts +18 -0
- package/dist/model/images.js +26 -0
- package/dist/model/images.js.map +1 -0
- package/dist/model/links.d.ts +46 -0
- package/dist/model/links.js +73 -0
- package/dist/model/links.js.map +1 -0
- package/dist/model/markup.d.ts +46 -0
- package/dist/model/markup.js +71 -0
- package/dist/model/markup.js.map +1 -0
- package/dist/model/measurement.d.ts +11 -0
- package/dist/model/measurement.js +24 -0
- package/dist/model/measurement.js.map +1 -0
- package/dist/model/openings.d.ts +4 -0
- package/dist/model/openings.js +4 -0
- package/dist/model/openings.js.map +1 -1
- package/dist/model/planGeom.d.ts +72 -2
- package/dist/model/planGeom.js +184 -10
- package/dist/model/planGeom.js.map +1 -1
- package/dist/model/project.js +3 -2
- package/dist/model/project.js.map +1 -1
- package/dist/model/projectfile.js +2 -1
- package/dist/model/projectfile.js.map +1 -1
- package/dist/model/rooms.js +2 -2
- package/dist/model/rooms.js.map +1 -1
- package/dist/model/settings.d.ts +6 -0
- package/dist/model/settings.js +2 -2
- package/dist/model/settings.js.map +1 -1
- package/dist/model/sketch.d.ts +14 -0
- package/dist/model/sketch.js +143 -24
- package/dist/model/sketch.js.map +1 -1
- package/dist/model/smooth.d.ts +6 -0
- package/dist/model/smooth.js +52 -0
- package/dist/model/smooth.js.map +1 -0
- package/dist/model/store.d.ts +12 -0
- package/dist/model/store.js +35 -3
- package/dist/model/store.js.map +1 -1
- package/dist/model/types.d.ts +173 -8
- package/dist/model/wallfaces.d.ts +32 -0
- package/dist/model/wallfaces.js +133 -20
- package/dist/model/wallfaces.js.map +1 -1
- package/dist/model/wallshape.d.ts +15 -0
- package/dist/model/wallshape.js +89 -0
- package/dist/model/wallshape.js.map +1 -0
- package/dist/react/BottomBar.d.ts +9 -5
- package/dist/react/BottomBar.js +213 -24
- package/dist/react/BottomBar.js.map +1 -1
- package/dist/react/Buttons.d.ts +13 -2
- package/dist/react/Buttons.js +37 -5
- package/dist/react/Buttons.js.map +1 -1
- package/dist/react/CadEditor.d.ts +46 -3
- package/dist/react/CadEditor.js +77 -20
- package/dist/react/CadEditor.js.map +1 -1
- package/dist/react/ConfirmSheet.d.ts +4 -0
- package/dist/react/ConfirmSheet.js +31 -0
- package/dist/react/ConfirmSheet.js.map +1 -0
- package/dist/react/ConflictSheet.d.ts +2 -1
- package/dist/react/ConflictSheet.js +16 -8
- package/dist/react/ConflictSheet.js.map +1 -1
- package/dist/react/Drawer.d.ts +8 -2
- package/dist/react/Drawer.js +167 -38
- package/dist/react/Drawer.js.map +1 -1
- package/dist/react/Duck.d.ts +13 -0
- package/dist/react/Duck.js +45 -0
- package/dist/react/Duck.js.map +1 -0
- package/dist/react/Empty3D.d.ts +2 -0
- package/dist/react/Empty3D.js +39 -0
- package/dist/react/Empty3D.js.map +1 -0
- package/dist/react/FitScroll.d.ts +6 -0
- package/dist/react/FitScroll.js +14 -0
- package/dist/react/FitScroll.js.map +1 -0
- package/dist/react/Icon.native.d.ts +6 -0
- package/dist/react/Icon.native.js +4 -0
- package/dist/react/Icon.native.js.map +1 -1
- package/dist/react/Icon.web.d.ts +6 -0
- package/dist/react/Icon.web.js +4 -0
- package/dist/react/Icon.web.js.map +1 -1
- package/dist/react/KeypadSheet.d.ts +9 -2
- package/dist/react/KeypadSheet.js +174 -42
- package/dist/react/KeypadSheet.js.map +1 -1
- package/dist/react/LaserTray.js +8 -5
- package/dist/react/LaserTray.js.map +1 -1
- package/dist/react/MarkupPanel.d.ts +8 -0
- package/dist/react/MarkupPanel.js +67 -0
- package/dist/react/MarkupPanel.js.map +1 -0
- package/dist/react/PageThumb.native.d.ts +7 -2
- package/dist/react/PageThumb.native.js +19 -7
- package/dist/react/PageThumb.native.js.map +1 -1
- package/dist/react/PageThumb.web.d.ts +3 -2
- package/dist/react/PageThumb.web.js +4 -4
- package/dist/react/PageThumb.web.js.map +1 -1
- package/dist/react/PlanCanvas.native.d.ts +1 -1
- package/dist/react/PlanCanvas.native.js +134 -36
- package/dist/react/PlanCanvas.native.js.map +1 -1
- package/dist/react/PlanCanvas.web.js +16 -5
- package/dist/react/PlanCanvas.web.js.map +1 -1
- package/dist/react/Popovers.d.ts +12 -6
- package/dist/react/Popovers.js +53 -26
- package/dist/react/Popovers.js.map +1 -1
- package/dist/react/Rail.d.ts +24 -0
- package/dist/react/Rail.js +324 -34
- package/dist/react/Rail.js.map +1 -1
- package/dist/react/ShapeMenu.d.ts +7 -0
- package/dist/react/ShapeMenu.js +58 -0
- package/dist/react/ShapeMenu.js.map +1 -0
- package/dist/react/Sheet.d.ts +14 -4
- package/dist/react/Sheet.js +54 -9
- package/dist/react/Sheet.js.map +1 -1
- package/dist/react/Slider.d.ts +18 -0
- package/dist/react/Slider.js +27 -0
- package/dist/react/Slider.js.map +1 -0
- package/dist/react/Toast.js +14 -6
- package/dist/react/Toast.js.map +1 -1
- package/dist/react/ToolTip.d.ts +7 -0
- package/dist/react/ToolTip.js +43 -0
- package/dist/react/ToolTip.js.map +1 -0
- package/dist/react/TopBar.d.ts +6 -3
- package/dist/react/TopBar.js +48 -17
- package/dist/react/TopBar.js.map +1 -1
- package/dist/react/Tween.d.ts +7 -0
- package/dist/react/Tween.js +26 -0
- package/dist/react/Tween.js.map +1 -0
- package/dist/react/Tween.native.d.ts +6 -0
- package/dist/react/Tween.native.js +43 -0
- package/dist/react/Tween.native.js.map +1 -0
- package/dist/react/View3D.native.d.ts +4 -0
- package/dist/react/View3D.native.js +103 -0
- package/dist/react/View3D.native.js.map +1 -0
- package/dist/react/View3D.web.d.ts +4 -0
- package/dist/react/View3D.web.js +151 -0
- package/dist/react/View3D.web.js.map +1 -0
- package/dist/react/View3DHost.d.ts +143 -0
- package/dist/react/View3DHost.js +377 -0
- package/dist/react/View3DHost.js.map +1 -0
- package/dist/react/corners.d.ts +7 -0
- package/dist/react/corners.js +51 -0
- package/dist/react/corners.js.map +1 -0
- package/dist/react/easing.d.ts +15 -0
- package/dist/react/easing.js +10 -0
- package/dist/react/easing.js.map +1 -0
- package/dist/react/host.d.ts +7 -2
- package/dist/react/host.js +8 -3
- package/dist/react/host.js.map +1 -1
- package/dist/react/hostKeypad.d.ts +56 -0
- package/dist/react/hostKeypad.js +5 -0
- package/dist/react/hostKeypad.js.map +1 -0
- package/dist/react/motion.d.ts +77 -0
- package/dist/react/motion.js +173 -0
- package/dist/react/motion.js.map +1 -0
- package/dist/react/ports.js +90 -8
- package/dist/react/ports.js.map +1 -1
- package/dist/react/saving.d.ts +15 -0
- package/dist/react/saving.js +76 -0
- package/dist/react/saving.js.map +1 -0
- package/dist/react/store.d.ts +38 -1
- package/dist/react/store.js +31 -1
- package/dist/react/store.js.map +1 -1
- package/dist/react/theme.d.ts +10 -0
- package/dist/react/theme.js +8 -0
- package/dist/react/theme.js.map +1 -1
- package/dist/react/topRow.d.ts +22 -0
- package/dist/react/topRow.js +61 -0
- package/dist/react/topRow.js.map +1 -0
- package/dist/react/touch.d.ts +6 -0
- package/dist/react/touch.js +9 -0
- package/dist/react/touch.js.map +1 -0
- package/dist/render/ctx.d.ts +8 -0
- package/dist/render/draw.d.ts +33 -1
- package/dist/render/draw.js +593 -215
- package/dist/render/draw.js.map +1 -1
- package/dist/render/elevation.d.ts +65 -43
- package/dist/render/elevation.js +414 -140
- package/dist/render/elevation.js.map +1 -1
- package/dist/render/figmaIcons.d.ts +12 -0
- package/dist/render/figmaIcons.js +21 -0
- package/dist/render/figmaIcons.js.map +1 -0
- package/dist/render/hit.d.ts +22 -2
- package/dist/render/hit.js +168 -25
- package/dist/render/hit.js.map +1 -1
- package/dist/render/icons.js +69 -10
- package/dist/render/icons.js.map +1 -1
- package/dist/render/linkTile.d.ts +15 -0
- package/dist/render/linkTile.js +369 -0
- package/dist/render/linkTile.js.map +1 -0
- package/dist/render/markup.d.ts +11 -0
- package/dist/render/markup.js +112 -0
- package/dist/render/markup.js.map +1 -0
- package/dist/render/navigate.d.ts +88 -0
- package/dist/render/navigate.js +100 -0
- package/dist/render/navigate.js.map +1 -0
- package/dist/render/snap.d.ts +20 -1
- package/dist/render/snap.js +118 -25
- package/dist/render/snap.js.map +1 -1
- package/dist/render/squircle.d.ts +8 -0
- package/dist/render/squircle.js +71 -0
- package/dist/render/squircle.js.map +1 -0
- package/dist/render/view.d.ts +115 -2
- package/dist/render/view.js +244 -19
- package/dist/render/view.js.map +1 -1
- package/dist/skia/SkiaDrawCtx.d.ts +30 -2
- package/dist/skia/SkiaDrawCtx.js +198 -43
- package/dist/skia/SkiaDrawCtx.js.map +1 -1
- package/dist/skia/host.d.ts +18 -2
- package/dist/skia/host.js +46 -1
- package/dist/skia/host.js.map +1 -1
- package/dist/solver/equations.js +37 -2
- package/dist/solver/equations.js.map +1 -1
- package/dist/solver/gauss-newton.d.ts +17 -0
- package/dist/solver/gauss-newton.js +144 -36
- package/dist/solver/gauss-newton.js.map +1 -1
- package/dist/solver/linalg.d.ts +45 -2
- package/dist/solver/linalg.js +164 -10
- package/dist/solver/linalg.js.map +1 -1
- package/dist/solver/types.d.ts +5 -0
- package/dist/tools/annotate.d.ts +57 -1
- package/dist/tools/annotate.js +64 -43
- package/dist/tools/annotate.js.map +1 -1
- package/dist/tools/chamfer.d.ts +12 -0
- package/dist/tools/chamfer.js +114 -0
- package/dist/tools/chamfer.js.map +1 -0
- package/dist/tools/corner.d.ts +38 -0
- package/dist/tools/corner.js +99 -0
- package/dist/tools/corner.js.map +1 -0
- package/dist/tools/dim.d.ts +86 -9
- package/dist/tools/dim.js +581 -96
- package/dist/tools/dim.js.map +1 -1
- package/dist/tools/elevation.js +1 -1
- package/dist/tools/elevation.js.map +1 -1
- package/dist/tools/fillet.d.ts +9 -0
- package/dist/tools/fillet.js +86 -0
- package/dist/tools/fillet.js.map +1 -0
- package/dist/tools/freehand.d.ts +3 -2
- package/dist/tools/freehand.js +19 -12
- package/dist/tools/freehand.js.map +1 -1
- package/dist/tools/index.js +14 -4
- package/dist/tools/index.js.map +1 -1
- package/dist/tools/line.d.ts +8 -3
- package/dist/tools/line.js +66 -30
- package/dist/tools/line.js.map +1 -1
- package/dist/tools/modify.d.ts +13 -0
- package/dist/tools/modify.js +105 -25
- package/dist/tools/modify.js.map +1 -1
- package/dist/tools/openings.js +22 -15
- package/dist/tools/openings.js.map +1 -1
- package/dist/tools/registry.d.ts +8 -0
- package/dist/tools/registry.js +42 -2
- package/dist/tools/registry.js.map +1 -1
- package/dist/tools/select.d.ts +77 -3
- package/dist/tools/select.js +560 -74
- package/dist/tools/select.js.map +1 -1
- package/dist/tools/shapes.d.ts +14 -0
- package/dist/tools/shapes.js +154 -75
- package/dist/tools/shapes.js.map +1 -1
- package/dist/units/format.d.ts +10 -2
- package/dist/units/format.js +18 -3
- package/dist/units/format.js.map +1 -1
- package/dist/view3d/build.d.ts +35 -0
- package/dist/view3d/build.js +630 -0
- package/dist/view3d/build.js.map +1 -0
- package/dist/view3d/camera.d.ts +60 -0
- package/dist/view3d/camera.js +84 -0
- package/dist/view3d/camera.js.map +1 -0
- package/dist/view3d/fatLines.d.ts +11 -0
- package/dist/view3d/fatLines.js +83 -0
- package/dist/view3d/fatLines.js.map +1 -0
- package/dist/view3d/lines.d.ts +17 -0
- package/dist/view3d/lines.js +271 -0
- package/dist/view3d/lines.js.map +1 -0
- package/dist/view3d/mesh.d.ts +51 -0
- package/dist/view3d/mesh.js +101 -0
- package/dist/view3d/mesh.js.map +1 -0
- package/dist/view3d/poly.d.ts +16 -0
- package/dist/view3d/poly.js +109 -0
- package/dist/view3d/poly.js.map +1 -0
- package/dist/view3d/viewer.d.ts +86 -0
- package/dist/view3d/viewer.js +350 -0
- package/dist/view3d/viewer.js.map +1 -0
- package/package.json +15 -2
- package/src/app/edit.ts +83 -12
- package/src/app/main.ts +11 -5
- package/src/app/state.ts +38 -5
- package/src/data/memory.ts +2 -2
- package/src/data/provider.ts +15 -7
- package/src/data/sync.ts +87 -50
- package/src/editor/actions.ts +292 -83
- package/src/editor/confirm.ts +58 -0
- package/src/editor/elevation.ts +698 -478
- package/src/editor/face.ts +227 -0
- package/src/editor/files.ts +40 -4
- package/src/editor/gestures.ts +147 -31
- package/src/editor/keypad.ts +43 -11
- package/src/editor/keys.ts +25 -5
- package/src/editor/laser.ts +98 -20
- package/src/editor/linkActions.ts +194 -0
- package/src/editor/linkFollow.ts +43 -0
- package/src/editor/linkWrite.ts +77 -0
- package/src/editor/links.ts +181 -0
- package/src/editor/markup.ts +92 -8
- package/src/editor/mode.ts +83 -7
- package/src/editor/ports.ts +37 -6
- package/src/editor/project.ts +155 -26
- package/src/editor/selection.ts +54 -8
- package/src/editor/status.ts +2 -0
- package/src/editor/tooltips.ts +47 -0
- package/src/editor/view3d.ts +119 -0
- package/src/editor/view3dState.ts +61 -0
- package/src/export/dxf.ts +1 -1
- package/src/export/pdf.ts +15 -7
- package/src/export/plan.ts +119 -35
- package/src/index.ts +5 -0
- package/src/input/keyboard.ts +2 -4
- package/src/model/constraints.ts +13 -4
- package/src/model/demo.ts +3 -3
- package/src/model/elevation.ts +6 -16
- package/src/model/faces.ts +153 -0
- package/src/model/facesolve.ts +322 -0
- package/src/model/history.ts +19 -11
- package/src/model/images.ts +35 -0
- package/src/model/links.ts +77 -0
- package/src/model/markup.ts +80 -0
- package/src/model/measurement.ts +26 -0
- package/src/model/openings.ts +6 -0
- package/src/model/planGeom.ts +174 -8
- package/src/model/project.ts +3 -2
- package/src/model/projectfile.ts +2 -1
- package/src/model/rooms.ts +1 -1
- package/src/model/settings.ts +8 -2
- package/src/model/sketch.ts +101 -14
- package/src/model/smooth.ts +51 -0
- package/src/model/store.ts +35 -4
- package/src/model/types.ts +170 -8
- package/src/model/wallfaces.ts +126 -17
- package/src/model/wallshape.ts +97 -0
- package/src/react/BottomBar.tsx +263 -46
- package/src/react/Buttons.tsx +50 -9
- package/src/react/CadEditor.tsx +133 -30
- package/src/react/ConfirmSheet.tsx +38 -0
- package/src/react/ConflictSheet.tsx +19 -12
- package/src/react/Drawer.tsx +213 -110
- package/src/react/Duck.tsx +50 -0
- package/src/react/Empty3D.tsx +32 -0
- package/src/react/FitScroll.tsx +19 -0
- package/src/react/Icon.native.tsx +5 -0
- package/src/react/Icon.web.tsx +5 -0
- package/src/react/KeypadSheet.tsx +253 -60
- package/src/react/LaserTray.tsx +12 -8
- package/src/react/MarkupPanel.tsx +150 -0
- package/src/react/PageThumb.native.tsx +18 -7
- package/src/react/PageThumb.web.tsx +4 -4
- package/src/react/PlanCanvas.native.tsx +151 -34
- package/src/react/PlanCanvas.web.tsx +15 -5
- package/src/react/Popovers.tsx +116 -51
- package/src/react/Rail.tsx +360 -27
- package/src/react/ShapeMenu.tsx +66 -0
- package/src/react/Sheet.tsx +56 -11
- package/src/react/Slider.tsx +48 -0
- package/src/react/Toast.tsx +16 -9
- package/src/react/ToolTip.tsx +49 -0
- package/src/react/TopBar.tsx +97 -39
- package/src/react/Tween.native.tsx +36 -0
- package/src/react/Tween.tsx +24 -0
- package/src/react/View3D.native.tsx +131 -0
- package/src/react/View3D.web.tsx +131 -0
- package/src/react/View3DHost.ts +367 -0
- package/src/react/corners.ts +52 -0
- package/src/react/easing.ts +14 -0
- package/src/react/host.ts +8 -3
- package/src/react/hostKeypad.ts +66 -0
- package/src/react/motion.tsx +239 -0
- package/src/react/ports.ts +78 -9
- package/src/react/saving.ts +80 -0
- package/src/react/store.ts +35 -3
- package/src/react/theme.ts +8 -0
- package/src/react/topRow.ts +71 -0
- package/src/react/touch.ts +10 -0
- package/src/render/ctx.ts +8 -0
- package/src/render/draw.ts +437 -175
- package/src/render/elevation.ts +349 -143
- package/src/render/figmaIcons.ts +22 -0
- package/src/render/hit.ts +140 -21
- package/src/render/icons.ts +69 -10
- package/src/render/linkTile.ts +297 -0
- package/src/render/markup.ts +91 -0
- package/src/render/navigate.ts +114 -0
- package/src/render/snap.ts +105 -22
- package/src/render/squircle.ts +73 -0
- package/src/render/view.ts +255 -19
- package/src/skia/SkiaDrawCtx.ts +163 -41
- package/src/skia/host.ts +46 -1
- package/src/solver/equations.ts +37 -2
- package/src/solver/gauss-newton.ts +125 -28
- package/src/solver/linalg.ts +141 -5
- package/src/solver/types.ts +5 -0
- package/src/styles.css +91 -42
- package/src/tools/annotate.ts +58 -29
- package/src/tools/chamfer.ts +103 -0
- package/src/tools/corner.ts +99 -0
- package/src/tools/dim.ts +524 -92
- package/src/tools/elevation.ts +1 -1
- package/src/tools/fillet.ts +82 -0
- package/src/tools/freehand.ts +20 -16
- package/src/tools/index.ts +17 -4
- package/src/tools/line.ts +59 -23
- package/src/tools/modify.ts +86 -24
- package/src/tools/openings.ts +8 -13
- package/src/tools/registry.ts +27 -2
- package/src/tools/select.ts +424 -75
- package/src/tools/shapes.ts +119 -58
- package/src/ui/ctx.ts +2 -1
- package/src/ui/domPorts.ts +23 -4
- package/src/ui/drawer.ts +6 -26
- package/src/ui/elevation.ts +10 -101
- package/src/ui/keypad.ts +8 -14
- package/src/ui/laser.ts +2 -2
- package/src/ui/menu.ts +63 -17
- package/src/ui/noteinput.ts +3 -3
- package/src/ui/opacity.ts +108 -0
- package/src/ui/palette.ts +67 -12
- package/src/ui/rail.ts +21 -7
- package/src/ui/topbar.ts +4 -14
- package/src/units/format.ts +18 -3
- package/src/view3d/build.ts +607 -0
- package/src/view3d/camera.ts +113 -0
- package/src/view3d/fatLines.ts +83 -0
- package/src/view3d/lines.ts +258 -0
- package/src/view3d/mesh.ts +111 -0
- package/src/view3d/poly.ts +104 -0
- package/src/view3d/viewer.ts +348 -0
- package/dist/react/ElevCanvas.native.d.ts +0 -3
- package/dist/react/ElevCanvas.native.js +0 -58
- package/dist/react/ElevCanvas.native.js.map +0 -1
- package/dist/react/ElevCanvas.web.d.ts +0 -4
- package/dist/react/ElevCanvas.web.js +0 -44
- package/dist/react/ElevCanvas.web.js.map +0 -1
- package/dist/react/ElevationSheet.d.ts +0 -2
- package/dist/react/ElevationSheet.js +0 -38
- package/dist/react/ElevationSheet.js.map +0 -1
- package/dist/render/elevationView.d.ts +0 -3
- package/dist/render/elevationView.js +0 -100
- package/dist/render/elevationView.js.map +0 -1
- package/src/react/ElevCanvas.native.tsx +0 -68
- package/src/react/ElevCanvas.web.tsx +0 -42
- package/src/react/ElevationSheet.tsx +0 -88
- package/src/render/elevationView.ts +0 -73
|
@@ -1,38 +1,141 @@
|
|
|
1
|
-
import { Canvas, Picture, type SkPicture } from '@shopify/react-native-skia';
|
|
2
|
-
import { useEffect, useMemo, useRef
|
|
1
|
+
import { Canvas, Group, Picture, Skia, type SkPicture, type Transforms3d } from '@shopify/react-native-skia';
|
|
2
|
+
import { memo, useEffect, useMemo, useRef } from 'react';
|
|
3
3
|
import { View, type LayoutChangeEvent } from 'react-native';
|
|
4
4
|
import { Gesture, GestureDetector, PointerType, type GestureTouchEvent, type TouchData } from 'react-native-gesture-handler';
|
|
5
|
+
import { runOnJS, useDerivedValue, useSharedValue } from 'react-native-reanimated';
|
|
5
6
|
import { ed } from '../app/state';
|
|
6
|
-
import { pointerCancel, pointerDown, pointerLeave, pointerMove, pointerUp,
|
|
7
|
-
import {
|
|
8
|
-
import {
|
|
7
|
+
import { navigationOnly, pointerCancel, pointerDown, pointerLeave, pointerMove, pointersStill, pointerUp, setHostNavigation } from '../editor/gestures';
|
|
8
|
+
import { drawPlanOverlay, drawPlanStatic, drawPlanUnder } from '../render/draw';
|
|
9
|
+
import {
|
|
10
|
+
carryTo, NAV_CANCEL, NAV_DOWN, NAV_MOVE, NAV_UP, navTouch, type NavState, type NavView, type UiView,
|
|
11
|
+
} from '../render/navigate';
|
|
12
|
+
import {
|
|
13
|
+
cvs, drawingChanged, fitView, imagesChanged, mainT, markDrawingKept, markImagesKept, paintPending, setFrameRenderer, setImageLoader, setTextMeasure, setViewFromHost, setViewSize, V, type Xform,
|
|
14
|
+
} from '../render/view';
|
|
9
15
|
import { page } from '../model/store';
|
|
10
|
-
import {
|
|
16
|
+
import { recordLayers, skiaImageLoader, skiaTextMeasure } from '../skia/host';
|
|
11
17
|
|
|
12
18
|
/*
|
|
13
|
-
The plan on a device
|
|
14
|
-
a
|
|
15
|
-
|
|
19
|
+
The plan on a device. Pan and zoom never wait for the JavaScript thread: the fingers are followed on the UI thread
|
|
20
|
+
(a worklet), which moves the view and redraws at the screen's own rate (120 Hz on ProMotion), as a native app does.
|
|
21
|
+
|
|
22
|
+
The drawing itself is drawn on the JavaScript thread by the shared renderer, recorded as Skia pictures, and handed
|
|
23
|
+
to the canvas through shared values — so no frame goes through React. While a pinch runs, that thread draws the
|
|
24
|
+
plan again at the newest view as fast as it can, and until each new picture arrives the UI thread moves and scales
|
|
25
|
+
the last one to where the fingers have taken the view. Pictures reach past the screen's edges (OVERSCAN), so one
|
|
26
|
+
that has fallen a little behind a pinch out or a fling shows drawing, not an edge; what stays put on the screen (the
|
|
27
|
+
wall view's key plan, the conflict border) is drawn apart and never moves.
|
|
28
|
+
|
|
29
|
+
As a tool follows the pointer, only the moving parts (preview, snap marker, selection box) are drawn again; the
|
|
30
|
+
drawing is kept until it changes. Touches still reach editor/gestures.ts for the tools, Apple Pencil, and two- and
|
|
31
|
+
three-finger taps (undo, redo); it leaves the view to this canvas (setHostNavigation).
|
|
16
32
|
*/
|
|
17
33
|
|
|
18
|
-
|
|
19
|
-
const
|
|
34
|
+
/** How far past each edge of the screen a picture of the drawing reaches, as a share of the screen's longer side. */
|
|
35
|
+
const OVERSCAN = 0.5;
|
|
36
|
+
const EMPTY = Skia.Picture.MakePicture(null)!;
|
|
37
|
+
|
|
38
|
+
/** What the JavaScript thread drew last, and the view it drew it at. */
|
|
39
|
+
interface Frame {
|
|
40
|
+
/** the photos and plans under the drawing, the drawing, its moving parts, and what stays put on the screen (from each) */
|
|
41
|
+
under: SkPicture; base: SkPicture; overlay: SkPicture; baseFixed: SkPicture; overlayFixed: SkPicture;
|
|
42
|
+
/** the view it was drawn at, as a transform (s, ox, oy) on a W × H screen and as a view */
|
|
43
|
+
s: number; ox: number; oy: number; W: number; H: number; view: NavView;
|
|
44
|
+
/** the latest of the UI thread's views it was drawn at (see UiView) */
|
|
45
|
+
seq: number;
|
|
46
|
+
}
|
|
47
|
+
/** A touch as the UI thread hands it over: id, position on the canvas, position in the window. */
|
|
48
|
+
type Packed = [id: number, x: number, y: number, ax: number, ay: number];
|
|
49
|
+
|
|
50
|
+
/** The latest of the UI thread's views this thread has taken; each frame says which it includes. */
|
|
51
|
+
let hostSeq = 0;
|
|
20
52
|
|
|
21
|
-
|
|
22
|
-
|
|
53
|
+
/**
|
|
54
|
+
* Touches from the UI thread: the view first, if the fingers moved it, then each touch to the editor's gestures (as a
|
|
55
|
+
* pointer lands, `down` is every one still down: any other's lift was lost).
|
|
56
|
+
*/
|
|
57
|
+
function touchesOnJS(type: number, kind: string, ts: Packed[], view: UiView | null, down: number[] | null): void {
|
|
58
|
+
if (view) { hostSeq = view.seq; setViewFromHost(view.s, view.cx, view.cy); }
|
|
59
|
+
if (down) pointersStill(down);
|
|
60
|
+
const fn = type === NAV_DOWN ? pointerDown : type === NAV_MOVE ? pointerMove : type === NAV_UP ? pointerUp : pointerCancel, t = Date.now();
|
|
61
|
+
for (const [id, x, y, ax, ay] of ts) fn({ id, kind, x, y, t, cx: ax, cy: ay });
|
|
62
|
+
// what a tool shows following the finger (a shape's outline, its marks, the snap), drawn now: left for the next frame
|
|
63
|
+
// it reached the screen a frame behind the finger. Only when nothing but those moving parts changed — the drawing
|
|
64
|
+
// itself, and the view, are drawn on the frame as before.
|
|
65
|
+
if (!view && !drawingChanged() && !imagesChanged()) paintPending();
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
const kindOf = (t: PointerType): string => {
|
|
69
|
+
'worklet';
|
|
70
|
+
return t === PointerType.STYLUS ? 'pen' : t === PointerType.MOUSE ? 'mouse' : 'touch';
|
|
71
|
+
};
|
|
72
|
+
const pack = (ts: TouchData[]): Packed[] => {
|
|
73
|
+
'worklet';
|
|
74
|
+
return ts.map(t => [t.id, t.x, t.y, t.absoluteX, t.absoluteY] as Packed);
|
|
75
|
+
};
|
|
76
|
+
|
|
77
|
+
export const PlanCanvas = memo(function PlanCanvas() {
|
|
23
78
|
const first = useRef(true);
|
|
79
|
+
const frame = useSharedValue<Frame | null>(null);
|
|
80
|
+
const view = useSharedValue<UiView>({ s: 1, cx: 0, cy: 0, seq: 0 });
|
|
81
|
+
// the UI thread's own: the fingers down, whether they're navigating and where they were last, a pen seen
|
|
82
|
+
const nav = useSharedValue<NavState>({ fingers: {}, navigating: false, last: null, pen: false });
|
|
83
|
+
const readOnly = useSharedValue(false);
|
|
84
|
+
|
|
85
|
+
// the last pictures, carried from the view they were drawn at to the view the fingers have reached since
|
|
86
|
+
const shift = useDerivedValue<Transforms3d>(() => {
|
|
87
|
+
const f = frame.value, c = f && carryTo(f, view.value);
|
|
88
|
+
return c ? [{ translateX: c.tx }, { translateY: c.ty }, { scale: c.k }] : [];
|
|
89
|
+
});
|
|
90
|
+
const under = useDerivedValue(() => frame.value?.under ?? EMPTY);
|
|
91
|
+
const base = useDerivedValue(() => frame.value?.base ?? EMPTY);
|
|
92
|
+
const overlay = useDerivedValue(() => frame.value?.overlay ?? EMPTY);
|
|
93
|
+
const baseFixed = useDerivedValue(() => frame.value?.baseFixed ?? EMPTY);
|
|
94
|
+
const overlayFixed = useDerivedValue(() => frame.value?.overlayFixed ?? EMPTY);
|
|
24
95
|
|
|
25
96
|
useEffect(() => {
|
|
26
97
|
setImageLoader(skiaImageLoader);
|
|
98
|
+
// (a device has no browser canvas to measure a note's text with)
|
|
99
|
+
setTextMeasure(skiaTextMeasure);
|
|
100
|
+
setHostNavigation(true);
|
|
101
|
+
/**
|
|
102
|
+
* The drawing as last kept (and what of it stays put), the photos and plans under it, and the view and size they
|
|
103
|
+
* were drawn at. Each is drawn again only when it changed: turning or moving a photo redraws the photos alone,
|
|
104
|
+
* not every wall and dimension over them (a frame's worth of the JavaScript thread on a big plan).
|
|
105
|
+
*/
|
|
106
|
+
let kept: { under: SkPicture; base: SkPicture; fixed: SkPicture; T: Xform; W: number; H: number } | null = null;
|
|
107
|
+
let wasReadOnly: boolean | null = null;
|
|
27
108
|
setFrameRenderer(() => {
|
|
28
109
|
const { W, H } = cvs;
|
|
29
110
|
if (!W || !H) return;
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
111
|
+
// (fingers only move the view: a drawing that can't be edited, or a sheet up over it — the keypad)
|
|
112
|
+
const navOnly = navigationOnly();
|
|
113
|
+
if (navOnly !== wasReadOnly) { wasReadOnly = navOnly; readOnly.value = navOnly; }
|
|
114
|
+
const T = mainT(), v = V();
|
|
115
|
+
// the drawing changed, the screen changed size, or the view moved (each step of a pinch: the UI thread carries
|
|
116
|
+
// the last picture along until this one arrives)
|
|
117
|
+
const moved = !kept || kept.W !== W || kept.H !== H || kept.T.s !== T.s || kept.T.ox !== T.ox || kept.T.oy !== T.oy;
|
|
118
|
+
const margin = Math.round(Math.max(W, H) * OVERSCAN);
|
|
119
|
+
let under = kept?.under, base = kept?.base, fixed = kept?.fixed;
|
|
120
|
+
if (moved || imagesChanged() || !under) {
|
|
121
|
+
under = recordLayers(W, H, margin, ctx => drawPlanUnder(ctx, W, H, { margin })).moving;
|
|
122
|
+
markImagesKept();
|
|
123
|
+
}
|
|
124
|
+
if (moved || drawingChanged() || !base || !fixed) {
|
|
125
|
+
const r = recordLayers(W, H, margin, (ctx, fx) => drawPlanStatic(ctx, W, H, { margin, fixed: fx, layer: 'above' }));
|
|
126
|
+
base = r.moving; fixed = r.fixed;
|
|
127
|
+
ed.hitChips = r.result;
|
|
128
|
+
markDrawingKept();
|
|
129
|
+
}
|
|
130
|
+
kept = { under, base, fixed, T, W, H };
|
|
131
|
+
const over = recordLayers(W, H, 0, (ctx, fx) => drawPlanOverlay(ctx, W, H, { fixed: fx }));
|
|
132
|
+
frame.value = {
|
|
133
|
+
under: kept.under, base: kept.base, overlay: over.moving, baseFixed: kept.fixed, overlayFixed: over.fixed,
|
|
134
|
+
s: T.s, ox: T.ox, oy: T.oy, W, H, view: { s: v.s, cx: v.cx, cy: v.cy }, seq: hostSeq,
|
|
135
|
+
};
|
|
33
136
|
});
|
|
34
|
-
return () => { setFrameRenderer(null); setImageLoader(null); };
|
|
35
|
-
}, []);
|
|
137
|
+
return () => { setFrameRenderer(null); setImageLoader(null); setTextMeasure(null); setHostNavigation(false); };
|
|
138
|
+
}, [frame, readOnly]);
|
|
36
139
|
|
|
37
140
|
const onLayout = (e: LayoutChangeEvent) => {
|
|
38
141
|
const { width, height } = e.nativeEvent.layout;
|
|
@@ -42,25 +145,33 @@ export function PlanCanvas() {
|
|
|
42
145
|
};
|
|
43
146
|
|
|
44
147
|
const gesture = useMemo(() => {
|
|
148
|
+
/** A touch event on the UI thread: follow the fingers (render/navigate.ts), then hand the touches to this thread. */
|
|
149
|
+
const touch = (type: number, e: GestureTouchEvent) => {
|
|
150
|
+
'worklet';
|
|
151
|
+
const kind = kindOf(e.pointerType), down = type === NAV_DOWN ? e.allTouches.map(t => t.id) : null;
|
|
152
|
+
const r = navTouch(nav.value, type, kind, e.changedTouches, down, readOnly.value, view.value, frame.value);
|
|
153
|
+
nav.value = r.st;
|
|
154
|
+
if (r.view !== view.value) view.value = r.view;
|
|
155
|
+
runOnJS(touchesOnJS)(type, kind, pack(e.changedTouches), r.moved ? r.view : null, down);
|
|
156
|
+
};
|
|
45
157
|
const touches = Gesture.Manual()
|
|
46
|
-
.
|
|
47
|
-
|
|
158
|
+
.onTouchesDown((e, manager) => {
|
|
159
|
+
'worklet';
|
|
48
160
|
manager.activate();
|
|
49
|
-
|
|
50
|
-
for (const t of e.changedTouches) pointerDown(pin(t, kind));
|
|
161
|
+
touch(NAV_DOWN, e);
|
|
51
162
|
})
|
|
52
|
-
.onTouchesMove(
|
|
53
|
-
|
|
54
|
-
|
|
163
|
+
.onTouchesMove(e => {
|
|
164
|
+
'worklet';
|
|
165
|
+
touch(NAV_MOVE, e);
|
|
55
166
|
})
|
|
56
|
-
.onTouchesUp((e
|
|
57
|
-
|
|
58
|
-
|
|
167
|
+
.onTouchesUp((e, manager) => {
|
|
168
|
+
'worklet';
|
|
169
|
+
touch(NAV_UP, e);
|
|
59
170
|
if (e.numberOfTouches === 0) manager.end();
|
|
60
171
|
})
|
|
61
|
-
.onTouchesCancelled((e
|
|
62
|
-
|
|
63
|
-
|
|
172
|
+
.onTouchesCancelled((e, manager) => {
|
|
173
|
+
'worklet';
|
|
174
|
+
touch(NAV_CANCEL, e);
|
|
64
175
|
manager.fail();
|
|
65
176
|
});
|
|
66
177
|
// a hovering Pencil (iPad Pro) or trackpad pointer shows snaps and highlights before it touches down
|
|
@@ -69,15 +180,21 @@ export function PlanCanvas() {
|
|
|
69
180
|
.onUpdate(e => pointerMove({ id: -1, kind: kindOf(e.pointerType), x: e.x, y: e.y, t: Date.now() }))
|
|
70
181
|
.onEnd(() => pointerLeave('mouse')); // drops hover highlights, whichever pointer hovered
|
|
71
182
|
return Gesture.Simultaneous(touches, hover);
|
|
72
|
-
}, []);
|
|
183
|
+
}, [frame, nav, readOnly, view]);
|
|
73
184
|
|
|
74
185
|
return (
|
|
75
186
|
<GestureDetector gesture={gesture}>
|
|
76
187
|
<View style={{ position: 'absolute', top: 0, left: 0, right: 0, bottom: 0 }} onLayout={onLayout} accessibilityLabel="Drawing canvas" testID="cv">
|
|
77
188
|
<Canvas style={{ flex: 1 }}>
|
|
78
|
-
|
|
189
|
+
<Group transform={shift}>
|
|
190
|
+
<Picture picture={under} />
|
|
191
|
+
<Picture picture={base} />
|
|
192
|
+
<Picture picture={overlay} />
|
|
193
|
+
</Group>
|
|
194
|
+
<Picture picture={baseFixed} />
|
|
195
|
+
<Picture picture={overlayFixed} />
|
|
79
196
|
</Canvas>
|
|
80
197
|
</View>
|
|
81
198
|
</GestureDetector>
|
|
82
199
|
);
|
|
83
|
-
}
|
|
200
|
+
});
|
|
@@ -1,10 +1,9 @@
|
|
|
1
1
|
import { useEffect, useRef } from 'react';
|
|
2
|
-
import { elevKeyDown, elevOpen } from '../editor/elevation';
|
|
3
2
|
import { pointerCancel, pointerDown, pointerLeave, pointerMove, pointerUp, wheelZoom, type PointerIn } from '../editor/gestures';
|
|
4
|
-
import { keypadOpen } from '../editor/keypad';
|
|
5
3
|
import { keyDown, keyUp, type KeyIn } from '../editor/keys';
|
|
6
4
|
import { page } from '../model/store';
|
|
7
5
|
import { fitView, initCanvas, resize, setFrameRenderer } from '../render/view';
|
|
6
|
+
import { PAGE_LOCK_ROOT } from './pageLock';
|
|
8
7
|
import { setCursorTarget } from './ports';
|
|
9
8
|
|
|
10
9
|
/*
|
|
@@ -15,6 +14,18 @@ import { setCursorTarget } from './ports';
|
|
|
15
14
|
|
|
16
15
|
const keyIn = (e: KeyboardEvent): KeyIn => ({ key: e.key, shift: e.shiftKey, mod: e.metaKey || e.ctrlKey, alt: e.altKey });
|
|
17
16
|
|
|
17
|
+
/**
|
|
18
|
+
* Whether a key is the editor's: pressed in it, or with nothing focused — not typed into a field, nor pressed in the
|
|
19
|
+
* host's own UI over the drawing (a picker, a measurement's detail), which is outside the editor's root. Delete there
|
|
20
|
+
* would otherwise delete what's selected on the drawing.
|
|
21
|
+
*/
|
|
22
|
+
function forEditor(e: KeyboardEvent): boolean {
|
|
23
|
+
const t = e.target instanceof HTMLElement ? e.target : null;
|
|
24
|
+
if (!t || t === document.body || t === document.documentElement) return true;
|
|
25
|
+
if (t.tagName === 'INPUT' || t.tagName === 'TEXTAREA' || t.tagName === 'SELECT' || t.isContentEditable) return false;
|
|
26
|
+
return !!t.closest('#' + PAGE_LOCK_ROOT);
|
|
27
|
+
}
|
|
28
|
+
|
|
18
29
|
export function PlanCanvas() {
|
|
19
30
|
const ref = useRef<HTMLCanvasElement>(null);
|
|
20
31
|
useEffect(() => {
|
|
@@ -52,10 +63,9 @@ export function PlanCanvas() {
|
|
|
52
63
|
// capture phase: a key the editor uses (Enter on the keypad, Space to pan, …) must not also press whatever button
|
|
53
64
|
// last took focus
|
|
54
65
|
document.addEventListener('keydown', e => {
|
|
55
|
-
|
|
56
|
-
if (tag === 'INPUT' || tag === 'TEXTAREA') return;
|
|
66
|
+
if (!forEditor(e)) return;
|
|
57
67
|
const k = keyIn(e);
|
|
58
|
-
if (
|
|
68
|
+
if (keyDown(k)) { e.preventDefault(); e.stopPropagation(); }
|
|
59
69
|
}, { capture: true, signal: ac.signal });
|
|
60
70
|
document.addEventListener('keyup', e => keyUp(keyIn(e)), { capture: true, signal: ac.signal });
|
|
61
71
|
return () => { ac.abort(); ro.disconnect(); setCursorTarget('plan', null); };
|
package/src/react/Popovers.tsx
CHANGED
|
@@ -1,48 +1,85 @@
|
|
|
1
1
|
import { useEffect, useRef, useState, type ReactNode } from 'react';
|
|
2
|
-
import {
|
|
3
|
-
import { disarmReset, exportDxf, exportPdf, openProjectText, resetArmed, resetToDemo, saveProjectFile } from '../editor/files';
|
|
4
|
-
import {
|
|
5
|
-
|
|
6
|
-
|
|
2
|
+
import { Animated, Text, TextInput, View, type ViewStyle } from 'react-native';
|
|
3
|
+
import { disarmReset, exportDxf, exportPdf, importPagesText, openProjectText, resetArmed, resetToDemo, saveProjectFile } from '../editor/files';
|
|
4
|
+
import {
|
|
5
|
+
editDefaultWallHeight, LAYER_ROWS, layerOn, setDefaultWallHeight, setPrecision, setPreference, setUnits,
|
|
6
|
+
toggleLayer, togglePreference, WALL_PRESETS, type UnitChoice,
|
|
7
|
+
} from '../editor/project';
|
|
8
|
+
import { page } from '../model/store';
|
|
9
|
+
import { fitView } from '../render/view';
|
|
10
|
+
import { command3d, is3d } from '../editor/view3dState';
|
|
11
|
+
import { fmtLen, followingHostUnits } from '../units';
|
|
7
12
|
import { settings } from '../model/settings';
|
|
13
|
+
import { Toggle } from './Buttons';
|
|
14
|
+
import { FitScroll } from './FitScroll';
|
|
8
15
|
import { pickTextFile } from './files';
|
|
9
|
-
import { notify, ui } from './store';
|
|
16
|
+
import { editorVersion, notify, ui, useEditorKey } from './store';
|
|
10
17
|
import { C, FONT, shadow } from './theme';
|
|
18
|
+
import { Segmented, Tap, usePopup } from './motion';
|
|
19
|
+
import { createSheet } from './corners';
|
|
11
20
|
|
|
12
21
|
const closeMenu = () => { ui.menu = false; disarmReset(); notify(); };
|
|
13
22
|
|
|
14
23
|
function MenuLink({ label, onPress, danger }: { label: string; onPress: () => void; danger?: boolean }) {
|
|
15
24
|
return (
|
|
16
|
-
<
|
|
25
|
+
<Tap feel="dim" accessibilityRole="button" accessibilityLabel={label} onPress={onPress} style={s.link}>
|
|
17
26
|
<Text style={[s.linkText, danger && { color: C.softRed }]}>{label}</Text>
|
|
18
|
-
</
|
|
27
|
+
</Tap>
|
|
19
28
|
);
|
|
20
29
|
}
|
|
21
30
|
|
|
22
31
|
function Seg<T extends string | number>({ value, opts, onPick }: { value: T; opts: [T, string][]; onPick: (v: T) => void }) {
|
|
23
|
-
return
|
|
24
|
-
<View style={s.seg}>
|
|
25
|
-
{opts.map(([v, l]) => (
|
|
26
|
-
<Pressable key={String(v)} accessibilityRole="button" accessibilityLabel={l} accessibilityState={{ selected: v === value }} onPress={() => onPick(v)}
|
|
27
|
-
style={[s.segBtn, v === value && { backgroundColor: '#fff' }]}>
|
|
28
|
-
<Text style={[s.segText, v === value && { color: C.dark }]}>{l}</Text>
|
|
29
|
-
</Pressable>
|
|
30
|
-
))}
|
|
31
|
-
</View>
|
|
32
|
-
);
|
|
32
|
+
return <Segmented value={value} options={opts} onPick={onPick} style={s.seg} optionStyle={s.segBtn} textStyle={s.segText} onText={C.dark} offText={C.lg1} />;
|
|
33
33
|
}
|
|
34
34
|
|
|
35
35
|
const MRow = ({ label, children }: { label: string; children: ReactNode }) => (
|
|
36
36
|
<View style={s.mrow}><Text style={s.rowText}>{label}</Text>{children}</View>
|
|
37
37
|
);
|
|
38
38
|
|
|
39
|
-
/**
|
|
40
|
-
|
|
41
|
-
|
|
39
|
+
/**
|
|
40
|
+
* The top bar's menus: Layers, or ••• (on a phone Fit to screen, then exports, opening a file, settings). `accountUnits`: the host formats
|
|
41
|
+
* lengths (BOLDR: the person's account settings), offered as Units → Account beside the editor's own units.
|
|
42
|
+
*/
|
|
43
|
+
export function Menu({ accountUnits = false, devTools = true, themeChoice = true, pickText = pickTextFile, readOnly = false, phone = false }: {
|
|
44
|
+
accountUnits?: boolean; devTools?: boolean; themeChoice?: boolean;
|
|
45
|
+
/** how to pick a .boldr.json file's text (a device's host passes its own); the browser's file picker by default */
|
|
46
|
+
pickText?: () => Promise<string | null>;
|
|
47
|
+
readOnly?: boolean;
|
|
48
|
+
/** a phone's top bar has no room for Fit to screen: it's the menu's first item there */
|
|
49
|
+
phone?: boolean;
|
|
50
|
+
}) {
|
|
51
|
+
// (open only briefly: while it is, it follows every change)
|
|
52
|
+
useEditorKey(() => (ui.menu ? ui.menu + editorVersion() : ''));
|
|
53
|
+
// two menus in the one spot, each dropping down from its button (top right) and kept built once opened, so it opens
|
|
54
|
+
// at once the next time; one swapped for the other fades across
|
|
55
|
+
const more = usePopup(ui.menu === 'more', { y: -6 }, 'top right', true), layers = usePopup(ui.menu === 'layers', { y: -6 }, 'top right', true);
|
|
56
|
+
return (
|
|
57
|
+
<>
|
|
58
|
+
{more.mounted && <MoreMenu open={ui.menu === 'more'} style={more.style} {...{ accountUnits, devTools, themeChoice, pickText, readOnly, phone }} />}
|
|
59
|
+
{layers.mounted && <LayersMenu open={ui.menu === 'layers'} style={layers.style} />}
|
|
60
|
+
</>
|
|
61
|
+
);
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
/** ••• : the exports, opening a file, and the settings. */
|
|
65
|
+
function MoreMenu({ open, style, accountUnits, devTools, themeChoice, pickText, readOnly, phone }: {
|
|
66
|
+
open: boolean; style: Animated.WithAnimatedObject<ViewStyle>;
|
|
67
|
+
accountUnits: boolean; devTools: boolean; themeChoice: boolean; pickText: () => Promise<string | null>; readOnly: boolean; phone: boolean;
|
|
68
|
+
}) {
|
|
42
69
|
const run = (fn: () => void) => () => { closeMenu(); fn(); };
|
|
43
70
|
return (
|
|
44
|
-
<View style={s.menu} accessibilityLabel="Settings" testID=
|
|
45
|
-
<
|
|
71
|
+
<Animated.View style={[s.menu, style]} pointerEvents={open ? 'auto' : 'none'} accessibilityLabel="Settings" testID={open ? 'menu' : undefined}>
|
|
72
|
+
<FitScroll>
|
|
73
|
+
{phone && <MenuLink label="Fit to screen" onPress={run(() => (is3d() ? command3d('fit') : fitView()))} />}
|
|
74
|
+
<MenuLink label="Export page as PDF (.pdf)" onPress={run(exportPdf)} />
|
|
75
|
+
<MenuLink label="Export page as DXF (.dxf)" onPress={run(exportDxf)} />
|
|
76
|
+
<View style={s.hr} />
|
|
77
|
+
{!readOnly && (
|
|
78
|
+
<>
|
|
79
|
+
<MenuLink label="Open file… (adds its pages)" onPress={run(() => { pickText().then(t => { if (t != null) importPagesText(t); }, () => {}); })} />
|
|
80
|
+
<View style={s.hr} />
|
|
81
|
+
</>
|
|
82
|
+
)}
|
|
46
83
|
{devTools && (
|
|
47
84
|
<>
|
|
48
85
|
<MenuLink label="Save project to file…" onPress={run(saveProjectFile)} />
|
|
@@ -50,21 +87,31 @@ export function Menu({ units = true, devTools = true, themeChoice = true }: { un
|
|
|
50
87
|
<View style={s.hr} />
|
|
51
88
|
</>
|
|
52
89
|
)}
|
|
53
|
-
<
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
90
|
+
<MRow label="Units">
|
|
91
|
+
<Seg<UnitChoice>
|
|
92
|
+
value={accountUnits && followingHostUnits() ? 'account' : settings.unit}
|
|
93
|
+
opts={[...(accountUnits ? [['account', 'Account'] as [UnitChoice, string]] : []), ['ftin', 'ft-in'], ['in', 'in'], ['mm', 'mm']]}
|
|
94
|
+
onPick={setUnits}
|
|
95
|
+
/>
|
|
96
|
+
</MRow>
|
|
97
|
+
{!(accountUnits && followingHostUnits()) && settings.unit !== 'mm' && (
|
|
98
|
+
<MRow label="Precision"><Seg value={settings.denom} opts={[[16, '1/16"'], [8, '1/8"'], [4, '1/4"']]} onPick={setPrecision} /></MRow>
|
|
61
99
|
)}
|
|
62
|
-
|
|
63
|
-
<MRow label="
|
|
100
|
+
{/* 8', 9', 10' in a tap; Custom (showing the height when it's one) opens the keypad */}
|
|
101
|
+
<MRow label="Wall height">
|
|
102
|
+
<Seg value={WALL_PRESETS.includes(settings.wallH) ? settings.wallH : -1}
|
|
103
|
+
opts={[[96, "8'"], [108, "9'"], [120, "10'"], [-1, WALL_PRESETS.includes(settings.wallH) ? 'Custom' : fmtLen(settings.wallH)]]}
|
|
104
|
+
onPick={v => { if (v === -1) { closeMenu(); editDefaultWallHeight(); } else if (setDefaultWallHeight(v)) notify(); }} />
|
|
105
|
+
</MRow>
|
|
106
|
+
<MRow label="Snap to grid"><Toggle label="Snap to grid" value={settings.snapGrid} onChange={() => togglePreference('snapGrid')} /></MRow>
|
|
107
|
+
{/* cabinets, doors and windows snapping to corners and each other */}
|
|
108
|
+
<MRow label="Object snapping"><Toggle label="Object snapping" value={layerOn('objSnap')} onChange={() => { toggleLayer('objSnap'); notify(); }} /></MRow>
|
|
64
109
|
{themeChoice && (
|
|
65
110
|
<MRow label="Theme"><Seg value={settings.theme} opts={[['system', 'Auto'], ['light', 'Light'], ['dark', 'Dark']]} onPick={v => setPreference('theme', v)} /></MRow>
|
|
66
111
|
)}
|
|
67
|
-
<MRow label="Auto-constrain while drawing"><
|
|
112
|
+
<MRow label="Auto-constrain while drawing"><Toggle label="Auto-constrain while drawing" value={settings.autoCons} onChange={() => togglePreference('autoCons')} /></MRow>
|
|
113
|
+
{/* picking a tool says what it does, for a moment, under the tabs */}
|
|
114
|
+
<MRow label="Tool tips"><Toggle label="Tool tips" value={settings.toolTips !== false} onChange={() => { togglePreference('toolTips'); notify(); }} /></MRow>
|
|
68
115
|
{devTools && (
|
|
69
116
|
<>
|
|
70
117
|
<View style={s.hr} />
|
|
@@ -72,28 +119,46 @@ export function Menu({ units = true, devTools = true, themeChoice = true }: { un
|
|
|
72
119
|
onPress={() => { if (resetToDemo()) closeMenu(); else notify(); }} />
|
|
73
120
|
</>
|
|
74
121
|
)}
|
|
75
|
-
</
|
|
76
|
-
</View>
|
|
122
|
+
</FitScroll>
|
|
123
|
+
</Animated.View>
|
|
77
124
|
);
|
|
78
125
|
}
|
|
79
126
|
|
|
80
|
-
/**
|
|
81
|
-
|
|
82
|
-
if (!ui.palette) return null;
|
|
127
|
+
/** A layer's switch: its name, what it shows beneath, the toggle. */
|
|
128
|
+
function LayerRow({ label, sub, value, onChange }: { label: string; sub?: string; value: boolean; onChange: () => void }) {
|
|
83
129
|
return (
|
|
84
|
-
<View style={s.
|
|
85
|
-
{
|
|
86
|
-
<
|
|
87
|
-
|
|
88
|
-
|
|
130
|
+
<View style={s.mrow}>
|
|
131
|
+
<View style={{ flexShrink: 1 }}>
|
|
132
|
+
<Text style={s.rowText}>{label}</Text>
|
|
133
|
+
{sub ? <Text style={s.rowSub}>{sub}</Text> : null}
|
|
134
|
+
</View>
|
|
135
|
+
<Toggle label={label} value={value} onChange={onChange} />
|
|
89
136
|
</View>
|
|
90
137
|
);
|
|
91
138
|
}
|
|
92
139
|
|
|
140
|
+
/** The Layers menu: what's shown — the photos and plans under the drawing, then each layer. (An image's own lock, scale and
|
|
141
|
+
* opacity are in its bar when it's selected.) */
|
|
142
|
+
function LayersMenu({ open, style }: { open: boolean; style: Animated.WithAnimatedObject<ViewStyle> }) {
|
|
143
|
+
const ims = page().underlays, n = ims.length;
|
|
144
|
+
return (
|
|
145
|
+
<Animated.View style={[s.menu, s.layers, style]} pointerEvents={open ? 'auto' : 'none'} accessibilityLabel="Layers" testID={open ? 'layersMenu' : undefined}>
|
|
146
|
+
<FitScroll>
|
|
147
|
+
<LayerRow label={n > 1 ? 'Photos and plans' : 'Photo or plan underlay'}
|
|
148
|
+
sub={n > 1 ? n + ' images under the drawing' : n ? 'Traced image under the drawing' : 'None on this page'} value={settings.layers.underlay}
|
|
149
|
+
onChange={() => { toggleLayer('underlay'); notify(); }} />
|
|
150
|
+
<View style={s.hr} />
|
|
151
|
+
{LAYER_ROWS.map(([k, label, sub]) => <LayerRow key={k} label={label} sub={sub} value={layerOn(k)} onChange={() => { toggleLayer(k); notify(); }} />)}
|
|
152
|
+
</FitScroll>
|
|
153
|
+
</Animated.View>
|
|
154
|
+
);
|
|
155
|
+
}
|
|
156
|
+
|
|
93
157
|
/** Typing a note where the text tool was tapped: Enter places it, Esc cancels, leaving the field places it too. */
|
|
94
158
|
export function NoteInput() {
|
|
159
|
+
useEditorKey(() => (ui.note ? String(editorVersion()) : ''));
|
|
95
160
|
const n = ui.note, [text, setText] = useState(''), ref = useRef<TextInput>(null), done = useRef(false);
|
|
96
|
-
useEffect(() => { setText(''); done.current = false; if (n) setTimeout(() => ref.current?.focus(), 30); }, [n]);
|
|
161
|
+
useEffect(() => { setText(n?.initial ?? ''); done.current = false; if (n) setTimeout(() => ref.current?.focus(), 30); }, [n]);
|
|
97
162
|
if (!n) return null;
|
|
98
163
|
const finish = (commit: boolean) => {
|
|
99
164
|
if (done.current) return;
|
|
@@ -103,24 +168,24 @@ export function NoteInput() {
|
|
|
103
168
|
if (commit && text.trim()) cb(text.trim());
|
|
104
169
|
};
|
|
105
170
|
return (
|
|
106
|
-
<TextInput ref={ref} accessibilityLabel="Note text" placeholder="Type a note, then Enter" placeholderTextColor={C.g4} value={text} onChangeText={setText}
|
|
171
|
+
<TextInput ref={ref} accessibilityLabel="Note text" selectTextOnFocus={!!n.initial} placeholder="Type a note, then Enter" placeholderTextColor={C.g4} value={text} onChangeText={setText}
|
|
107
172
|
onSubmitEditing={() => finish(true)} onBlur={() => finish(true)}
|
|
108
173
|
onKeyPress={e => { if (e.nativeEvent.key === 'Escape') finish(false); }}
|
|
109
174
|
style={[s.note, { left: n.x, top: n.y - 18 }]} />
|
|
110
175
|
);
|
|
111
176
|
}
|
|
112
177
|
|
|
113
|
-
const s =
|
|
178
|
+
const s = createSheet({
|
|
114
179
|
menu: { position: 'absolute', top: 66, right: 12, width: 320, maxHeight: '80%', backgroundColor: C.g1, borderRadius: 15, paddingVertical: 8, zIndex: 45, ...shadow },
|
|
115
180
|
link: { paddingVertical: 10, paddingHorizontal: 16 },
|
|
116
181
|
linkText: { fontFamily: FONT, fontSize: 14, color: '#fff' },
|
|
117
182
|
hr: { height: 1, backgroundColor: C.g2, marginVertical: 6 },
|
|
118
183
|
mrow: { flexDirection: 'row', alignItems: 'center', justifyContent: 'space-between', paddingVertical: 8, paddingHorizontal: 16, gap: 10 },
|
|
184
|
+
layers: { width: 340 },
|
|
185
|
+
rowSub: { fontFamily: FONT, fontSize: 12, color: C.g4, marginTop: 2 },
|
|
119
186
|
rowText: { fontFamily: FONT, fontSize: 14, color: '#fff', flexShrink: 1 },
|
|
120
|
-
seg: { flexDirection: 'row', backgroundColor: C.dark, borderRadius:
|
|
121
|
-
segBtn: { paddingVertical: 5, paddingHorizontal: 10, borderRadius:
|
|
187
|
+
seg: { flexDirection: 'row', backgroundColor: C.dark, borderRadius: 999, padding: 3 },
|
|
188
|
+
segBtn: { paddingVertical: 5, paddingHorizontal: 10, borderRadius: 999 },
|
|
122
189
|
segText: { fontFamily: FONT, fontSize: 12.5, fontWeight: '600', color: C.lg1 },
|
|
123
|
-
palette: { position: 'absolute', left: 70, top: 300, flexDirection: 'row', gap: 8, backgroundColor: C.g1, borderRadius: 24, padding: 8, zIndex: 46, ...shadow },
|
|
124
|
-
swatch: { width: 32, height: 32, borderRadius: 16, borderWidth: 2, borderColor: 'rgba(255,255,255,0.4)' },
|
|
125
190
|
note: { position: 'absolute', zIndex: 40, minWidth: 220, height: 36, paddingHorizontal: 10, borderRadius: 8, backgroundColor: '#fff', color: C.dark, fontFamily: FONT, fontSize: 14, borderWidth: 2, borderColor: C.purple },
|
|
126
191
|
});
|