@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
package/src/react/Drawer.tsx
CHANGED
|
@@ -1,45 +1,149 @@
|
|
|
1
|
-
import { useEffect, useRef, useState } from 'react';
|
|
2
|
-
import { Pressable, ScrollView, StyleSheet, Switch, Text, TextInput, useWindowDimensions, View } from 'react-native';
|
|
1
|
+
import { useEffect, useMemo, useRef, useState } from 'react';
|
|
3
2
|
import {
|
|
4
|
-
|
|
5
|
-
|
|
3
|
+
Animated, Easing, Keyboard, PanResponder, Platform, Pressable, ScrollView, StyleSheet, Text, TextInput, useWindowDimensions, View,
|
|
4
|
+
} from 'react-native';
|
|
5
|
+
import {
|
|
6
|
+
addPage, deletePage, deletePending, duplicatePage, focusSpace, renamePage, renameSpace, spaceSchedule, switchPage,
|
|
6
7
|
} from '../editor/project';
|
|
8
|
+
import { ed } from '../app/state';
|
|
7
9
|
import type { DrawerTab } from '../editor/ports';
|
|
8
|
-
import { settings } from '../model/settings';
|
|
9
10
|
import { page, store } from '../model/store';
|
|
10
11
|
import { fmtArea, fmtLen } from '../units';
|
|
11
|
-
import { Icon } from './Icon';
|
|
12
|
+
import { Icon, RawSvg } from './Icon';
|
|
13
|
+
import { PLUS_SIZE, PLUS_SVG, TRASH_SIZE, TRASH_SVG } from '../render/figmaIcons';
|
|
12
14
|
import { PageThumb } from './PageThumb';
|
|
13
15
|
import { notify, ui, useEditorVersion } from './store';
|
|
14
16
|
import { C, FONT } from './theme';
|
|
17
|
+
import { EASE, Segmented, Tap } from './motion';
|
|
18
|
+
import { NATIVE } from './easing';
|
|
19
|
+
import { createSheet } from './corners';
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* The Pages frame's sizes, scaled to the panel: Figma draws it 515 wide, the panel's content is about 370 (K). Its
|
|
23
|
+
* tiles are 180 tall there, with the name in a band at their foot below the drawing.
|
|
24
|
+
*/
|
|
25
|
+
const K = 0.72, TILE_H = Math.round(180 * K), NAME_BAND = 34, THUMB_H = TILE_H - NAME_BAND - 2;
|
|
15
26
|
|
|
16
|
-
|
|
27
|
+
/** Pages and Spaces, tabs of one panel (Layers is a menu of its own in the top bar). */
|
|
28
|
+
const TABS: [DrawerTab, string][] = [['pages', 'Pages'], ['rooms', 'Spaces']];
|
|
17
29
|
const close = () => { ui.drawer = null; ui.renameRoom = null; notify(); };
|
|
18
30
|
|
|
19
|
-
/**
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
31
|
+
/** Sliding in it eases to a stop; sliding out it gathers pace as it goes. */
|
|
32
|
+
const SLIDE_IN = 240, SLIDE_OUT = 200, EASE_OUT = Easing.bezier(0.4, 0, 1, 1);
|
|
33
|
+
/** A phone's sheet rises the way iOS's own sheets do: quick off the mark, a long soft landing. */
|
|
34
|
+
const SHEET_IN = 320, SHEET_EASE = Easing.bezier(0.32, 0.72, 0, 1), SHEET_OUT = 220;
|
|
35
|
+
/** Taps on the scrim right after the sheet opens are the opening tap's ghost click (touch screens): ignore them. */
|
|
36
|
+
const GHOST_MS = 450;
|
|
37
|
+
/** A drag down the sheet's top that closes it: this far, or a flick. */
|
|
38
|
+
const CLOSE_DRAG = 80, CLOSE_FLICK = 0.5;
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* The project panel: pages and the spaces schedule. On a tablet or computer it slides in from the right; on a phone
|
|
42
|
+
* it's a bottom sheet the way BOLDR's are (a grey-1 sheet with a grey-4 handle over a dimmed drawing, closed by a drag
|
|
43
|
+
* down or a tap outside). Either way it slides back out when closed.
|
|
44
|
+
*/
|
|
45
|
+
export function Drawer({ phone = false }: { phone?: boolean }) {
|
|
46
|
+
const version = useEditorVersion(), { width, height } = useWindowDimensions(), w = Math.min(400, width);
|
|
47
|
+
const open = !!ui.drawer;
|
|
48
|
+
// what it showed last stays while it slides away
|
|
49
|
+
const last = useRef<DrawerTab>('pages');
|
|
50
|
+
if (ui.drawer) last.current = ui.drawer;
|
|
51
|
+
const [present, setPresent] = useState(open);
|
|
52
|
+
// the phone's sheet slides its own height (measured as it opens), not the screen's: from a screen's height away the
|
|
53
|
+
// first part of the slide happened below the screen, and what showed rushed by in a few frames. While it slides,
|
|
54
|
+
// what's costly in it (the pages' thumbnails) waits: `settled`
|
|
55
|
+
const [travel, setTravel] = useState<number | null>(null), [settled, setSettled] = useState(open);
|
|
56
|
+
const measuring = phone && open && travel == null;
|
|
57
|
+
const x = useRef(new Animated.Value(open ? 0 : 1)).current;
|
|
58
|
+
// made once for its size, which only changes while it's still: a new one each render would detach the old, and a
|
|
59
|
+
// value with nothing attached stops its animation — the slide would end before it began
|
|
60
|
+
const slide = useMemo(() => x.interpolate({ inputRange: [0, 1], outputRange: [0, (phone ? travel ?? height : w) + 40] }), [x, w, height, phone, travel]);
|
|
61
|
+
const fade = useMemo(() => x.interpolate({ inputRange: [0, 1], outputRange: [1, 0] }), [x]);
|
|
62
|
+
// the phone's sheet: how far it's dragged down, when it opened, and how far the keyboard lifts it
|
|
63
|
+
const drag = useRef(new Animated.Value(0)).current, openedAt = useRef(0), sheet = useRef<View>(null), [lift, setLift] = useState(0);
|
|
64
|
+
const dropped = useMemo(() => Animated.add(slide, drag), [slide, drag]);
|
|
65
|
+
useEffect(() => {
|
|
66
|
+
if (open) { setPresent(true); openedAt.current = Date.now(); }
|
|
67
|
+
}, [open]);
|
|
68
|
+
useEffect(() => {
|
|
69
|
+
// (a phone's sheet first lays out below the screen, so it knows how far to come)
|
|
70
|
+
if (measuring) return;
|
|
71
|
+
// started on the next frame: a panel just put on screen is taken off and put back once more by React's strict
|
|
72
|
+
// mode as it mounts, and its animation stops when that leaves nothing attached to it
|
|
73
|
+
const frame = requestAnimationFrame(() => {
|
|
74
|
+
if (open) {
|
|
75
|
+
Animated.timing(x, { toValue: 0, duration: phone ? SHEET_IN : SLIDE_IN, easing: phone ? SHEET_EASE : EASE, useNativeDriver: NATIVE })
|
|
76
|
+
.start(({ finished }) => { if (finished) setSettled(true); });
|
|
77
|
+
} else {
|
|
78
|
+
Animated.timing(x, { toValue: 1, duration: phone ? SHEET_OUT : SLIDE_OUT, easing: EASE_OUT, useNativeDriver: NATIVE })
|
|
79
|
+
// (the thumbnails stay as they slide away; they're held back again the next time it opens)
|
|
80
|
+
.start(({ finished }) => { if (finished) { setPresent(false); setTravel(null); setSettled(false); drag.setValue(0); } });
|
|
81
|
+
}
|
|
82
|
+
});
|
|
83
|
+
return () => cancelAnimationFrame(frame);
|
|
84
|
+
}, [open, measuring, phone, x, drag]);
|
|
85
|
+
const pan = useMemo(() => {
|
|
86
|
+
const back = () => Animated.timing(drag, { toValue: 0, duration: SLIDE_IN, easing: EASE, useNativeDriver: NATIVE }).start();
|
|
87
|
+
return PanResponder.create({
|
|
88
|
+
// a drag down (not a tap on a tab, nor a sideways swipe) moves the sheet with the finger
|
|
89
|
+
onMoveShouldSetPanResponder: (_, g) => g.dy > 4 && Math.abs(g.dy) > Math.abs(g.dx),
|
|
90
|
+
onPanResponderMove: (_, g) => drag.setValue(Math.max(0, g.dy)),
|
|
91
|
+
onPanResponderRelease: (_, g) => { if (g.dy > CLOSE_DRAG || g.vy > CLOSE_FLICK) close(); else back(); },
|
|
92
|
+
onPanResponderTerminate: back,
|
|
93
|
+
});
|
|
94
|
+
}, [drag]);
|
|
95
|
+
// iOS doesn't resize the window for the keyboard (Android does): lift the sheet clear of it to rename a space
|
|
96
|
+
useEffect(() => {
|
|
97
|
+
if (!phone || Platform.OS !== 'ios') return;
|
|
98
|
+
const show = Keyboard.addListener('keyboardWillShow', e => {
|
|
99
|
+
sheet.current?.measureInWindow((_x, y, _w, h) => setLift(l => Math.max(0, y + h + l - e.endCoordinates.screenY)));
|
|
100
|
+
});
|
|
101
|
+
const hide = Keyboard.addListener('keyboardWillHide', () => setLift(0));
|
|
102
|
+
return () => { show.remove(); hide.remove(); };
|
|
103
|
+
}, [phone]);
|
|
104
|
+
if (!open && !present) return null;
|
|
105
|
+
const tab = last.current;
|
|
106
|
+
const body = (
|
|
107
|
+
<ScrollView style={phone && s.sheetScroll} contentContainerStyle={s.body} keyboardShouldPersistTaps="handled">
|
|
108
|
+
{tab === 'pages' && <Pages version={version} thumbs={settled || !phone} />}
|
|
109
|
+
{tab === 'rooms' && <Spaces />}
|
|
110
|
+
</ScrollView>
|
|
111
|
+
);
|
|
112
|
+
const tabs = (
|
|
113
|
+
<Segmented role="tab" value={tab} options={TABS} onPick={t => { ui.drawer = t; notify(); }}
|
|
114
|
+
style={s.tabs} optionStyle={s.tab} textStyle={s.tabText} onText={C.dark} offText={C.lg1} />
|
|
115
|
+
);
|
|
116
|
+
if (phone) {
|
|
117
|
+
return (
|
|
118
|
+
<>
|
|
119
|
+
<Animated.View pointerEvents={open ? 'auto' : 'none'} style={[StyleSheet.absoluteFill, s.scrim, { opacity: fade }]}>
|
|
120
|
+
<Pressable accessibilityRole="button" accessibilityLabel="Close panel" testID="drawerScrim" style={StyleSheet.absoluteFill}
|
|
121
|
+
onPress={() => { if (Date.now() - openedAt.current > GHOST_MS) close(); }} />
|
|
122
|
+
</Animated.View>
|
|
123
|
+
<Animated.View ref={sheet} pointerEvents={open ? 'auto' : 'none'} accessibilityLabel="Project panel" accessibilityViewIsModal
|
|
124
|
+
testID={open ? 'drawer' : undefined} style={[s.sheet, { bottom: lift, transform: [{ translateY: dropped }] }]}
|
|
125
|
+
// its height: how far it slides (taken while it's still — opening, or open and settled — never mid-slide)
|
|
126
|
+
onLayout={e => { const h = e.nativeEvent.layout.height; if (travel == null || (settled && open)) setTravel(t => (t != null && Math.abs(t - h) < 1 ? t : h)); }}>
|
|
127
|
+
{/* the handle and the tabs: a drag down either closes it */}
|
|
128
|
+
<View {...pan.panHandlers}>
|
|
129
|
+
<View style={s.handleWrap}><View style={[s.handle, { width: Math.round(width * 0.075) }]} /></View>
|
|
130
|
+
<View style={[s.head, s.sheetHead]}>{tabs}</View>
|
|
131
|
+
</View>
|
|
132
|
+
{body}
|
|
133
|
+
</Animated.View>
|
|
134
|
+
</>
|
|
135
|
+
);
|
|
136
|
+
}
|
|
24
137
|
return (
|
|
25
|
-
|
|
138
|
+
// (off by its shadow too, so nothing of it shows once it's out)
|
|
139
|
+
<Animated.View pointerEvents={open ? 'auto' : 'none'} accessibilityLabel="Project panel" testID={open ? 'drawer' : undefined}
|
|
140
|
+
style={[s.drawer, { width: w, transform: [{ translateX: slide }] }]}>
|
|
26
141
|
<View style={s.head}>
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
<Pressable key={t} accessibilityRole="tab" accessibilityState={{ selected: tab === t }} onPress={() => { ui.drawer = t; notify(); }}
|
|
30
|
-
style={[s.tab, tab === t && { backgroundColor: '#fff' }]}>
|
|
31
|
-
<Text style={[s.tabText, tab === t && { color: C.dark }]}>{label}</Text>
|
|
32
|
-
</Pressable>
|
|
33
|
-
))}
|
|
34
|
-
</View>
|
|
35
|
-
<Pressable accessibilityRole="button" accessibilityLabel="Close panel" onPress={close} style={s.close}><Icon name="close" /></Pressable>
|
|
142
|
+
{tabs}
|
|
143
|
+
<Tap accessibilityRole="button" accessibilityLabel="Close panel" onPress={close} style={s.close}><Icon name="close" /></Tap>
|
|
36
144
|
</View>
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
{tab === 'layers' && <Layers />}
|
|
40
|
-
{tab === 'rooms' && <Spaces />}
|
|
41
|
-
</ScrollView>
|
|
42
|
-
</View>
|
|
145
|
+
{body}
|
|
146
|
+
</Animated.View>
|
|
43
147
|
);
|
|
44
148
|
}
|
|
45
149
|
|
|
@@ -58,83 +162,58 @@ function NameField({ value, onCommit, style, label, autoFocus, onFocus }: {
|
|
|
58
162
|
);
|
|
59
163
|
}
|
|
60
164
|
|
|
61
|
-
|
|
165
|
+
/**
|
|
166
|
+
* The pages (Figma 2548:101990): the current page's name, then the pages — Duplicate and Delete for the current one
|
|
167
|
+
* (greyed out while it's the only page), + Page, and a tile for each, its drawing above its name, the current one
|
|
168
|
+
* ringed in yellow. A tablet's or a browser's
|
|
169
|
+
* side panel and a phone's bottom sheet show the same.
|
|
170
|
+
*/
|
|
171
|
+
function Pages({ version, thumbs }: { version: number; thumbs: boolean }) {
|
|
172
|
+
const project = store.project, cur = page(), pending = deletePending();
|
|
173
|
+
// a project keeps at least one page: nothing to delete while there's only one
|
|
174
|
+
const lone = project.pages.length <= 1;
|
|
62
175
|
return (
|
|
63
|
-
<
|
|
64
|
-
<
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
function Pages({ version }: { version: number }) {
|
|
70
|
-
const project = store.project, cur = page();
|
|
71
|
-
return (
|
|
72
|
-
<>
|
|
73
|
-
<NameField label="Project name" value={project.name} onCommit={renameProject} style={{ fontSize: 18 }} />
|
|
74
|
-
<Text style={s.sec}>This page</Text>
|
|
75
|
-
<NameField label="Page name" value={cur.name} onCommit={renamePage} style={{ fontSize: 15 }} />
|
|
76
|
-
<View style={s.buttons}>
|
|
77
|
-
<SmallButton label="Duplicate" onPress={duplicatePage} />
|
|
78
|
-
<SmallButton label={deletePending() ? 'Tap again to delete' : 'Delete page'} onPress={deletePage} danger />
|
|
176
|
+
<View style={s.cards}>
|
|
177
|
+
<View style={s.current}>
|
|
178
|
+
<Text style={s.pcardLabel}>Current Page:</Text>
|
|
179
|
+
<NameField label="Page name" value={cur.name} onCommit={renamePage} style={s.curName} />
|
|
79
180
|
</View>
|
|
80
|
-
<View style={s.
|
|
81
|
-
<View style={s.
|
|
82
|
-
<
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
<
|
|
88
|
-
style={[s.
|
|
89
|
-
<
|
|
90
|
-
<Text
|
|
91
|
-
</
|
|
92
|
-
|
|
181
|
+
<View style={s.pcard}>
|
|
182
|
+
<View style={s.pageBar}>
|
|
183
|
+
<View style={s.pageBarLeft}>
|
|
184
|
+
<Tap accessibilityRole="button" accessibilityLabel="Duplicate" onPress={duplicatePage} style={[s.pill, s.pillGrey]}>
|
|
185
|
+
<Text style={[s.pillText, { color: '#fff' }]}>Duplicate</Text>
|
|
186
|
+
</Tap>
|
|
187
|
+
{/* a first tap arms it (it says so), a second deletes the page */}
|
|
188
|
+
<Tap accessibilityRole="button" accessibilityLabel={pending ? 'Tap again to delete' : 'Delete page'} accessibilityState={{ disabled: lone }}
|
|
189
|
+
disabled={lone} onPress={deletePage} style={[s.pill, s.pillRed, pending && s.pillArmed, lone && s.pillOff]}>
|
|
190
|
+
<View style={lone && s.iconOff}><RawSvg xml={TRASH_SVG} width={TRASH_SIZE.width * K} height={TRASH_SIZE.height * K} /></View>
|
|
191
|
+
{pending && <Text style={[s.pillText, { color: '#fff' }]}>Delete?</Text>}
|
|
192
|
+
</Tap>
|
|
193
|
+
</View>
|
|
194
|
+
<Tap accessibilityRole="button" accessibilityLabel="+ Page" onPress={addPage} style={[s.pill, s.pillYellow]}>
|
|
195
|
+
<RawSvg xml={PLUS_SVG} width={PLUS_SIZE.width * K} height={PLUS_SIZE.height * K} />
|
|
196
|
+
<Text style={[s.pillText, { color: C.dark }]}>Page</Text>
|
|
197
|
+
</Tap>
|
|
93
198
|
</View>
|
|
199
|
+
{project.pages.map(p => {
|
|
200
|
+
const on = p.id === cur.id;
|
|
201
|
+
return (
|
|
202
|
+
<Tap key={p.id} accessibilityRole="button" accessibilityLabel={p.name} accessibilityState={{ selected: on }} onPress={() => switchPage(p.id)}
|
|
203
|
+
style={[s.tile, on && s.tileOn]} testID="pageTile">
|
|
204
|
+
{/* (held back while the sheet slides in: recording and mounting them cost the slide its frames; and drawn
|
|
205
|
+
again only for what they show: the page being drawn on, when it's edited — not every tap, toast and
|
|
206
|
+
selection, for every page) */}
|
|
207
|
+
{thumbs ? <PageThumb page={p} version={on ? ed.editSeq : 0} height={THUMB_H} /> : <View style={{ height: THUMB_H }} />}
|
|
208
|
+
<Text numberOfLines={1} style={s.tileName}>{p.name}</Text>
|
|
209
|
+
</Tap>
|
|
210
|
+
);
|
|
211
|
+
})}
|
|
94
212
|
</View>
|
|
95
|
-
</>
|
|
96
|
-
);
|
|
97
|
-
}
|
|
98
|
-
|
|
99
|
-
function SwitchRow({ label, sub, value, onChange }: { label: string; sub?: string; value: boolean; onChange: () => void }) {
|
|
100
|
-
return (
|
|
101
|
-
<View style={s.row}>
|
|
102
|
-
<View style={{ flex: 1 }}>
|
|
103
|
-
<Text style={s.rowText}>{label}</Text>
|
|
104
|
-
{sub ? <Text style={s.rowSub}>{sub}</Text> : null}
|
|
105
|
-
</View>
|
|
106
|
-
<Switch accessibilityLabel={label} value={value} onValueChange={onChange} trackColor={{ true: C.blue, false: C.g3 }} thumbColor="#fff" />
|
|
107
213
|
</View>
|
|
108
214
|
);
|
|
109
215
|
}
|
|
110
216
|
|
|
111
|
-
function Layers() {
|
|
112
|
-
const ul = page().underlay, op = Math.round((ul?.opacity ?? 0.6) * 100);
|
|
113
|
-
return (
|
|
114
|
-
<>
|
|
115
|
-
<SwitchRow label="Photo or plan underlay" sub={ul ? 'Traced image under the drawing' : 'None on this page'} value={settings.layers.underlay} onChange={() => toggleLayer('underlay')} />
|
|
116
|
-
{ul && (
|
|
117
|
-
<>
|
|
118
|
-
<View style={s.row}>
|
|
119
|
-
<Text style={s.rowText}>Image opacity</Text>
|
|
120
|
-
<View style={s.stepper}>
|
|
121
|
-
<SmallButton label="−" onPress={() => setUnderlayOpacity(Math.max(0.1, (op - 10) / 100))} />
|
|
122
|
-
<Text style={[s.rowText, { width: 44, textAlign: 'center' }]}>{op}%</Text>
|
|
123
|
-
<SmallButton label="+" onPress={() => setUnderlayOpacity(Math.min(1, (op + 10) / 100))} />
|
|
124
|
-
</View>
|
|
125
|
-
</View>
|
|
126
|
-
<View style={s.buttons}>
|
|
127
|
-
<SmallButton label={ul.locked ? 'Unlock image' : 'Lock image'} onPress={toggleUnderlayLock} />
|
|
128
|
-
<SmallButton label="Set scale" onPress={() => { close(); calibrateUnderlay(); }} />
|
|
129
|
-
<SmallButton label="Remove image" onPress={removeUnderlay} danger />
|
|
130
|
-
</View>
|
|
131
|
-
</>
|
|
132
|
-
)}
|
|
133
|
-
{LAYER_ROWS.map(([k, label, sub]) => <SwitchRow key={k} label={label} sub={sub} value={layerOn(k)} onChange={() => toggleLayer(k)} />)}
|
|
134
|
-
</>
|
|
135
|
-
);
|
|
136
|
-
}
|
|
137
|
-
|
|
138
217
|
function Spaces() {
|
|
139
218
|
const sched = spaceSchedule();
|
|
140
219
|
if (!sched.rows.length) return <Text style={s.empty}>No spaces on this page yet. Close a loop of walls or lines, or pick the Space tool and tap inside a closed shape.</Text>;
|
|
@@ -160,24 +239,25 @@ function Spaces() {
|
|
|
160
239
|
);
|
|
161
240
|
}
|
|
162
241
|
|
|
163
|
-
const s =
|
|
164
|
-
|
|
242
|
+
const s = createSheet({
|
|
243
|
+
// (a hairline along its top and left: it doesn't run into the page's header, the same grey, above it)
|
|
244
|
+
drawer: { position: 'absolute', top: 0, right: 0, bottom: 0, backgroundColor: C.g1, borderTopWidth: 1, borderLeftWidth: 1, borderColor: C.g2, zIndex: 50, boxShadow: '-8px 0px 30px rgba(0, 0, 0, 0.3)' },
|
|
245
|
+
// BOLDR's bottom sheet (AnimatedBottomSheet, surface="card"): gorhom's 15 pt corners and handle, over a 50% scrim
|
|
246
|
+
scrim: { backgroundColor: 'rgba(0,0,0,0.5)', zIndex: 49 },
|
|
247
|
+
sheet: { position: 'absolute', left: 0, right: 0, maxHeight: '85%', backgroundColor: C.g1, borderTopLeftRadius: 15, borderTopRightRadius: 15, zIndex: 50 },
|
|
248
|
+
handleWrap: { padding: 10 },
|
|
249
|
+
handle: { alignSelf: 'center', height: 4, borderRadius: 4, backgroundColor: C.g4 },
|
|
250
|
+
sheetHead: { paddingTop: 0 },
|
|
251
|
+
// (as tall as what's in it, up to the sheet's limit, then it scrolls)
|
|
252
|
+
sheetScroll: { flexGrow: 0, flexShrink: 1 },
|
|
165
253
|
head: { flexDirection: 'row', alignItems: 'center', justifyContent: 'space-between', paddingTop: 16, paddingBottom: 10, paddingLeft: 16, paddingRight: 14, gap: 10 },
|
|
166
|
-
|
|
167
|
-
|
|
254
|
+
title: { fontFamily: FONT, fontSize: 17, fontWeight: '700', color: '#fff', paddingLeft: 2 },
|
|
255
|
+
tabs: { flexDirection: 'row', backgroundColor: C.dark, borderRadius: 999, padding: 3 },
|
|
256
|
+
tab: { paddingVertical: 8, paddingHorizontal: 14, borderRadius: 999 },
|
|
168
257
|
tabText: { fontFamily: FONT, fontSize: 13, fontWeight: '600', color: C.lg1 },
|
|
169
258
|
close: { width: 38, height: 38, borderRadius: 19, backgroundColor: C.g2, alignItems: 'center', justifyContent: 'center' },
|
|
170
259
|
body: { paddingTop: 6, paddingHorizontal: 16, paddingBottom: 30 },
|
|
171
260
|
field: { fontFamily: FONT, color: '#fff', fontWeight: '600', paddingVertical: 6, paddingHorizontal: 8, marginLeft: -8, borderRadius: 8, borderWidth: 1, borderColor: 'transparent' },
|
|
172
|
-
sec: { fontFamily: FONT, fontSize: 13, fontWeight: '600', color: C.g4, marginTop: 18, marginBottom: 8 },
|
|
173
|
-
buttons: { flexDirection: 'row', flexWrap: 'wrap', gap: 8, marginTop: 10 },
|
|
174
|
-
small: { height: 32, paddingHorizontal: 12, borderRadius: 16, backgroundColor: C.dark, justifyContent: 'center' },
|
|
175
|
-
smallText: { fontFamily: FONT, fontSize: 12.5, fontWeight: '600', color: C.lg2 },
|
|
176
|
-
space: { marginTop: 16, backgroundColor: C.g2, borderWidth: 1, borderColor: C.g3, borderRadius: 12, padding: 10 },
|
|
177
|
-
spaceHead: { flexDirection: 'row', alignItems: 'center', gap: 6, marginBottom: 8 },
|
|
178
|
-
grid: { flexDirection: 'row', flexWrap: 'wrap', gap: 8 },
|
|
179
|
-
card: { width: '48%', flexGrow: 1, backgroundColor: C.dark, borderRadius: 9, padding: 6, borderWidth: 2, borderColor: 'transparent', gap: 5 },
|
|
180
|
-
cardText: { fontFamily: FONT, fontSize: 12.5, fontWeight: '500', color: C.lg2, paddingHorizontal: 2 },
|
|
181
261
|
row: { flexDirection: 'row', alignItems: 'center', justifyContent: 'space-between', gap: 12, paddingVertical: 10, borderBottomWidth: 1, borderBottomColor: C.g2 },
|
|
182
262
|
rowText: { fontFamily: FONT, fontSize: 14, color: '#fff' },
|
|
183
263
|
rowSub: { fontFamily: FONT, fontSize: 12, color: C.g4, marginTop: 2 },
|
|
@@ -186,5 +266,28 @@ const s = StyleSheet.create({
|
|
|
186
266
|
nums: { flexDirection: 'row', gap: 16, marginTop: 2 },
|
|
187
267
|
num: { fontFamily: FONT, fontSize: 13, color: C.lg1 },
|
|
188
268
|
numB: { color: '#fff', fontWeight: '600' },
|
|
269
|
+
// the Pages frame (Figma 2548:101989), at K: two cards of grey-3-ringed #3A3B3C, 20 corners, 15 inside
|
|
270
|
+
cards: { gap: 20 * K },
|
|
271
|
+
pcard: { backgroundColor: C.card, borderWidth: 1, borderColor: C.g3, borderRadius: 20 * K, padding: 15 * K, gap: 15 * K },
|
|
272
|
+
pcardLabel: { fontFamily: FONT, fontSize: 18 * K, fontWeight: '600', color: C.g4 },
|
|
273
|
+
// the current page's name: its label over a dark field, 60 tall, editable in place — straight on the panel, no card
|
|
274
|
+
current: { gap: 15 * K },
|
|
275
|
+
curName: { fontSize: 21 * K, backgroundColor: C.dark, borderRadius: 15 * K, minHeight: 60 * K, paddingVertical: 0, paddingHorizontal: 15 * K, marginLeft: 0 },
|
|
276
|
+
pageBar: { flexDirection: 'row', alignItems: 'center', justifyContent: 'space-between', gap: 10 * K },
|
|
277
|
+
pageBarLeft: { flexDirection: 'row', alignItems: 'center', gap: 10 * K },
|
|
278
|
+
// 50-tall pills: Duplicate 125 wide, Delete 70, + Page 125
|
|
279
|
+
pill: { height: 50 * K, borderRadius: 100, flexDirection: 'row', alignItems: 'center', justifyContent: 'center', gap: 10 * K, paddingHorizontal: 20 * K },
|
|
280
|
+
pillGrey: { backgroundColor: C.g3, minWidth: 125 * K },
|
|
281
|
+
pillRed: { backgroundColor: C.red, minWidth: 70 * K },
|
|
282
|
+
pillArmed: { paddingHorizontal: 15 * K },
|
|
283
|
+
// Delete with nothing to delete (the only page): grey-3 like Duplicate, its can faded
|
|
284
|
+
pillOff: { backgroundColor: C.g3 },
|
|
285
|
+
iconOff: { opacity: 0.45 },
|
|
286
|
+
pillYellow: { backgroundColor: C.yellow, minWidth: 125 * K },
|
|
287
|
+
pillText: { fontFamily: FONT, fontSize: 18 * K, fontWeight: '600' },
|
|
288
|
+
// a page: its drawing, its name at the foot; dark grey, ringed grey-3 (the current one 3 wide in yellow)
|
|
289
|
+
tile: { height: TILE_H, backgroundColor: C.dark, borderRadius: 10 * K, borderWidth: 1, borderColor: C.g3, overflow: 'hidden' },
|
|
290
|
+
tileOn: { borderWidth: 3, borderColor: C.yellow },
|
|
291
|
+
tileName: { fontFamily: FONT, fontSize: 18 * K, fontWeight: '600', color: '#fff', position: 'absolute', left: 15 * K, right: 15 * K, bottom: 15 * K - 2 },
|
|
189
292
|
empty: { fontFamily: FONT, color: C.g4, fontSize: 13, lineHeight: 20, paddingVertical: 8 },
|
|
190
293
|
});
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import { useEffect, useRef, useSyncExternalStore, type ReactNode } from 'react';
|
|
2
|
+
import { Animated, Platform, StyleSheet, type View } from 'react-native';
|
|
3
|
+
|
|
4
|
+
/*
|
|
5
|
+
On a phone the floating chrome — the top bar, the tabs, the rail, the bottom bar and trash, the trays — steps aside
|
|
6
|
+
while a gesture on the drawing is in motion (a stroke, a drag, a pan or pinch that moved; never a tap: see
|
|
7
|
+
editor/gestures.ts), and comes back as the last finger lifts, so the drawing gets the whole of a small screen. As
|
|
8
|
+
BOLDR's annotation screen does it: out in 120 ms, back in 200 ms (a gesture that ends mid-fade turns round from
|
|
9
|
+
where it is), and untouchable while it's away. The fade runs on the native driver, so it stays smooth while the
|
|
10
|
+
JavaScript thread is busy redrawing the plan under a pinch.
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
let moving = false;
|
|
14
|
+
const listeners = new Set<() => void>();
|
|
15
|
+
const subscribe = (fn: () => void) => { listeners.add(fn); return () => { listeners.delete(fn); }; };
|
|
16
|
+
const isMoving = () => moving;
|
|
17
|
+
|
|
18
|
+
/** A gesture on the drawing is in motion, or not (the editor's `interacting` port). */
|
|
19
|
+
export function setInteracting(on: boolean): void {
|
|
20
|
+
if (on === moving) return;
|
|
21
|
+
moving = on;
|
|
22
|
+
listeners.forEach(fn => fn());
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
const OUT_MS = 120, IN_MS = 200;
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* The chrome inside steps aside while a gesture on the drawing moves, when `active` (a phone). `held`: it stays in
|
|
29
|
+
* view but out of reach — a sheet is up over the drawing (a tablet's or a computer's keypad): a tap on it reaches the
|
|
30
|
+
* drawing beneath, which closes the sheet, as a tap outside a sheet does.
|
|
31
|
+
*/
|
|
32
|
+
export function Duck({ active, held = false, children }: { active: boolean; held?: boolean; children: ReactNode }) {
|
|
33
|
+
const away = useSyncExternalStore(subscribe, isMoving) && active, off = away || held;
|
|
34
|
+
const opacity = useRef(new Animated.Value(1)).current, ref = useRef<View>(null);
|
|
35
|
+
useEffect(() => {
|
|
36
|
+
Animated.timing(opacity, { toValue: away ? 0 : 1, duration: away ? OUT_MS : IN_MS, useNativeDriver: Platform.OS !== 'web' }).start();
|
|
37
|
+
}, [away, opacity]);
|
|
38
|
+
useEffect(() => {
|
|
39
|
+
// in a browser the chrome's own pass-through containers (box-none) turn their children back on, whatever is set
|
|
40
|
+
// above them: `inert` takes the whole of it out of reach (a device's pointerEvents="none" already does)
|
|
41
|
+
if (Platform.OS === 'web' && ref.current) (ref.current as unknown as HTMLElement).inert = off;
|
|
42
|
+
}, [off]);
|
|
43
|
+
// (an editor closed mid-gesture mustn't leave the next one's chrome away)
|
|
44
|
+
useEffect(() => () => setInteracting(false), []);
|
|
45
|
+
return (
|
|
46
|
+
<Animated.View ref={ref} pointerEvents={off ? 'none' : 'box-none'} aria-hidden={away || undefined} testID="chrome" style={[StyleSheet.absoluteFill, { opacity }]}>
|
|
47
|
+
{children}
|
|
48
|
+
</Animated.View>
|
|
49
|
+
);
|
|
50
|
+
}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { Text, View } from 'react-native';
|
|
2
|
+
import { planSK } from '../model/store';
|
|
3
|
+
import { useEditorKey } from './store';
|
|
4
|
+
import { C, FONT } from './theme';
|
|
5
|
+
import { createSheet } from './corners';
|
|
6
|
+
|
|
7
|
+
/** Whether the plan has a wall to stand up (lines and guides stay flat on the plan). */
|
|
8
|
+
function hasWalls(): boolean {
|
|
9
|
+
const sk = planSK();
|
|
10
|
+
for (const id in sk.lines) { const l = sk.lines[id]; if (l.kind === 'wall' && !l.constr) return true; }
|
|
11
|
+
for (const id in sk.arcs) { const a = sk.arcs[id]; if (a.kind === 'wall' && !a.constr) return true; }
|
|
12
|
+
for (const id in sk.circles) { const c = sk.circles[id]; if (c.kind === 'wall' && !c.constr) return true; }
|
|
13
|
+
return false;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
/** Over an empty 3D view: what it shows, and how to get walls into it. */
|
|
17
|
+
export function Empty3D() {
|
|
18
|
+
useEditorKey(() => String(hasWalls()));
|
|
19
|
+
if (hasWalls()) return null;
|
|
20
|
+
return (
|
|
21
|
+
<View pointerEvents="none" style={s.wrap} testID="empty3d">
|
|
22
|
+
<Text style={s.title}>No walls to stand up yet</Text>
|
|
23
|
+
<Text style={s.sub}>3D shows walls, with their doors, windows and cabinets. In Draft, draw with Walls on, or select lines and tap To wall.</Text>
|
|
24
|
+
</View>
|
|
25
|
+
);
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
const s = createSheet({
|
|
29
|
+
wrap: { position: 'absolute', left: 24, right: 24, top: 0, bottom: 0, alignItems: 'center', justifyContent: 'center' },
|
|
30
|
+
title: { fontFamily: FONT, fontSize: 16, fontWeight: '600', color: C.lg2, textAlign: 'center' },
|
|
31
|
+
sub: { fontFamily: FONT, fontSize: 13, color: C.g4, textAlign: 'center', marginTop: 6, maxWidth: 360 },
|
|
32
|
+
});
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { useState, type ReactNode } from 'react';
|
|
2
|
+
import { ScrollView, type StyleProp, type ViewStyle } from 'react-native';
|
|
3
|
+
|
|
4
|
+
/*
|
|
5
|
+
A menu's or panel's body: fixed in its box. It neither scrolls nor bounces while everything fits (an iOS ScrollView
|
|
6
|
+
drags and springs back even when there's nothing more to see); only when the screen is too short to hold it all —
|
|
7
|
+
a phone turned on its side — does it scroll, without the bounce.
|
|
8
|
+
*/
|
|
9
|
+
export function FitScroll({ children, contentContainerStyle }: { children: ReactNode; contentContainerStyle?: StyleProp<ViewStyle> }) {
|
|
10
|
+
const [box, setBox] = useState(0), [content, setContent] = useState(0);
|
|
11
|
+
const over = content > box + 1;
|
|
12
|
+
return (
|
|
13
|
+
<ScrollView scrollEnabled={over} bounces={false} alwaysBounceVertical={false} overScrollMode="never"
|
|
14
|
+
showsVerticalScrollIndicator={over} contentContainerStyle={contentContainerStyle}
|
|
15
|
+
onLayout={e => setBox(e.nativeEvent.layout.height)} onContentSizeChange={(_, h) => setContent(h)}>
|
|
16
|
+
{children}
|
|
17
|
+
</ScrollView>
|
|
18
|
+
);
|
|
19
|
+
}
|
|
@@ -5,3 +5,8 @@ import { icon } from '../render/icons';
|
|
|
5
5
|
export function Icon({ name, size = 24, color = '#fff' }: { name: string; size?: number; color?: string }) {
|
|
6
6
|
return <SvgXml xml={icon(name, size)} width={size} height={size} color={color} />;
|
|
7
7
|
}
|
|
8
|
+
|
|
9
|
+
/** An SVG as Figma exported it (render/figmaIcons.ts), shown at `width` × `height`. */
|
|
10
|
+
export function RawSvg({ xml, width, height }: { xml: string; width: number; height: number }) {
|
|
11
|
+
return <SvgXml xml={xml} width={width} height={height} />;
|
|
12
|
+
}
|
package/src/react/Icon.web.tsx
CHANGED
|
@@ -4,3 +4,8 @@ import { icon } from '../render/icons';
|
|
|
4
4
|
export function Icon({ name, size = 24, color = '#fff' }: { name: string; size?: number; color?: string }) {
|
|
5
5
|
return <span aria-hidden style={{ display: 'flex', color, lineHeight: 0 }} dangerouslySetInnerHTML={{ __html: icon(name, size) }} />;
|
|
6
6
|
}
|
|
7
|
+
|
|
8
|
+
/** An SVG as Figma exported it (render/figmaIcons.ts), shown at `width` × `height`. */
|
|
9
|
+
export function RawSvg({ xml, width, height }: { xml: string; width: number; height: number }) {
|
|
10
|
+
return <span aria-hidden style={{ display: 'flex', width, height, lineHeight: 0 }} dangerouslySetInnerHTML={{ __html: xml.replace(/width="[^"]*" height="[^"]*"/, `width="${width}" height="${height}"`) }} />;
|
|
11
|
+
}
|