@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/BottomBar.tsx
CHANGED
|
@@ -1,84 +1,301 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { memo, useRef, useState } from 'react';
|
|
2
|
+
import { Animated, ScrollView, Text, View } from 'react-native';
|
|
2
3
|
import { ed } from '../app/state';
|
|
3
4
|
import { applyCtxAction, barItems, deleteSelection, plusAction, plusLabel, plusVisible } from '../editor/actions';
|
|
4
|
-
import {
|
|
5
|
+
import { addLinks, LINK_PLUS_LABEL, linkPlusVisible, linkSizeVisible, pageLinkSize, setLinkSize, SIZE_STEPS, sizeToStep, stepToSize } from '../editor/linkActions';
|
|
6
|
+
import { afterEdit } from '../app/edit';
|
|
7
|
+
import { LINK_SIDE_IN } from '../model/links';
|
|
8
|
+
import { fmtLen } from '../units';
|
|
9
|
+
import { bar3d, run3d } from '../editor/view3d';
|
|
10
|
+
import { is3d } from '../editor/view3dState';
|
|
11
|
+
import { OPACITY_MIN, OPACITY_STEP, ROTATE_MAX, ROTATE_STEP, setUnderlayOpacity, setUnderlayRotation } from '../editor/project';
|
|
12
|
+
import { pushHistory } from '../model/history';
|
|
13
|
+
import { imageById } from '../model/images';
|
|
14
|
+
import { Slider } from './Slider';
|
|
15
|
+
import { notify, ui, useEditorKey } from './store';
|
|
16
|
+
import { currentHint } from '../editor/status';
|
|
5
17
|
import { hovered } from './Buttons';
|
|
6
18
|
import { Icon } from './Icon';
|
|
7
19
|
import { RichText } from './RichText';
|
|
8
20
|
import { C, FONT, shadow } from './theme';
|
|
21
|
+
import { Tap, usePopup } from './motion';
|
|
22
|
+
import { createSheet } from './corners';
|
|
9
23
|
|
|
10
|
-
/**
|
|
24
|
+
/**
|
|
25
|
+
* The contextual bar: what fits the tool and selection, the yellow + to measure, and the trash on its own. It's as
|
|
26
|
+
* long as what's in it, centred on the screen — on a phone, unless that would put it under the trash, when it moves
|
|
27
|
+
* just clear of it (and scrolls only if even that leaves too little room). In Annotate on a phone (or a window too
|
|
28
|
+
* short for it under the rail) the + that adds the job's files and measurements has the trash's corner while nothing's
|
|
29
|
+
* selected: selecting anything puts the trash there instead, and letting go brings the + back.
|
|
30
|
+
*/
|
|
11
31
|
export function BottomBar({ phone }: { phone: boolean }) {
|
|
12
|
-
|
|
32
|
+
// what's in the bar, and the selected image's opacity and turn (its sliders)
|
|
33
|
+
useEditorKey(() => {
|
|
34
|
+
const u = ed.sel.length === 1 && ed.sel[0].type === 'underlay' ? imageById(ed.sel[0].id) : undefined;
|
|
35
|
+
return [buttonsKey(), plusVisible(), ed.sel.length, u ? u.opacity ?? 0.6 : '', u ? (u.rot ?? 0) + (u.locked ? 'L' : '') : '', linkPlusVisible(), ui.linkFab, linkSizeVisible(), pageLinkSize() ?? ''].join('|');
|
|
36
|
+
});
|
|
37
|
+
// (on a phone only buttons show: a bar that would hold just a label isn't shown at all. The 3D tab measures and
|
|
38
|
+
// deletes nothing: what's tapped there shows its sizes, and what can be done with it from there)
|
|
39
|
+
const items = shown(phone), plus = !is3d() && plusVisible(), trash = !is3d() && ed.sel.length > 0;
|
|
40
|
+
const fab = !trash && (phone || ui.linkFab) && linkPlusVisible(), corner = trash || fab;
|
|
41
|
+
// it scrolls only when its buttons can't all fit (a narrow phone): otherwise a drag on the bar would move it
|
|
42
|
+
const [room, setRoom] = useState(0), [content, setContent] = useState(0), over = content > room + 1;
|
|
13
43
|
return (
|
|
14
44
|
<>
|
|
15
45
|
{(items.length > 0 || plus) && (
|
|
16
|
-
<View style={[s.bottom, phone ?
|
|
17
|
-
<
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
<Text style={[s.cbText, { color: fg }]}>{it.label}</Text>
|
|
30
|
-
</Pressable>
|
|
31
|
-
);
|
|
32
|
-
})}
|
|
46
|
+
<View style={[s.bottom, phone ? s.phoneRow : s.centered]}>
|
|
47
|
+
<ImagePopover />
|
|
48
|
+
<LinkSizePopover />
|
|
49
|
+
{/* a phone's bar sits between two springs that start as wide as the trash takes and share what's left
|
|
50
|
+
equally, so it's centred; only the left one gives way (first, before the bar), so a long bar slides left
|
|
51
|
+
to clear the trash. (A minimum width on the right one alone did that in a browser, but a device's layout
|
|
52
|
+
shares the room out differently and left a short bar off-centre.) The springs let touches through. */}
|
|
53
|
+
{phone && <View pointerEvents="none" style={[s.springL, { flexBasis: corner ? TRASH_ROOM : 0 }]} />}
|
|
54
|
+
<View testID="ctxBar" style={s.ctx}>
|
|
55
|
+
{/* (a native ScrollView grows to fill its row by default, which stretched the bar across a tablet) */}
|
|
56
|
+
<ScrollView horizontal showsHorizontalScrollIndicator={false} scrollEnabled={over} bounces={false} contentContainerStyle={s.items}
|
|
57
|
+
style={{ flexGrow: 0, flexShrink: 1 }} onLayout={e => setRoom(e.nativeEvent.layout.width)} onContentSizeChange={w => setContent(w)}>
|
|
58
|
+
<BarButtons k={buttonsKey()} phone={phone} />
|
|
33
59
|
</ScrollView>
|
|
34
60
|
{plus && (
|
|
35
|
-
<
|
|
61
|
+
<Tap accessibilityRole="button" accessibilityLabel={plusLabel()} testID="addbtn" onPress={plusAction} style={s.add}>
|
|
36
62
|
<Icon name="plus" color={C.dark} />
|
|
37
|
-
</
|
|
63
|
+
</Tap>
|
|
38
64
|
)}
|
|
39
65
|
</View>
|
|
66
|
+
{phone && <View pointerEvents="none" style={[s.springR, { flexBasis: corner ? TRASH_ROOM : 0 }]} />}
|
|
40
67
|
</View>
|
|
41
68
|
)}
|
|
42
|
-
{
|
|
43
|
-
<
|
|
44
|
-
style={
|
|
45
|
-
<Icon name="trash" />
|
|
46
|
-
</
|
|
69
|
+
{trash && (
|
|
70
|
+
<Tap accessibilityRole="button" accessibilityLabel="Delete selection" testID="btnTrash" onPress={deleteSelection}
|
|
71
|
+
style={s.trash}>
|
|
72
|
+
<Icon name="trash" size={24} color="#fff" />
|
|
73
|
+
</Tap>
|
|
74
|
+
)}
|
|
75
|
+
{fab && (
|
|
76
|
+
<Tap accessibilityRole="button" accessibilityLabel={LINK_PLUS_LABEL} testID="linkFab" onPress={() => { void addLinks(); }} style={[s.trash, s.fab]}>
|
|
77
|
+
<Icon name="plus" size={30} color={C.dark} />
|
|
78
|
+
</Tap>
|
|
47
79
|
)}
|
|
48
80
|
</>
|
|
49
81
|
);
|
|
50
82
|
}
|
|
51
83
|
|
|
52
|
-
/**
|
|
53
|
-
|
|
54
|
-
|
|
84
|
+
/** What's in the bar: for the selection, or for what's tapped in the 3D tab. */
|
|
85
|
+
const entries = () => (is3d() ? bar3d() : barItems());
|
|
86
|
+
|
|
87
|
+
/**
|
|
88
|
+
* What the bar shows: on a phone, its buttons alone (see buttonsOnly) — except in 3D, where the sizes are the point of
|
|
89
|
+
* tapping something, and nothing else shows them.
|
|
90
|
+
*/
|
|
91
|
+
const shown = (phone: boolean) => (phone && !is3d() ? buttonsOnly(entries()) : entries());
|
|
92
|
+
|
|
93
|
+
/** What the bar's buttons show: its items (with their states), and which slider is open above it. */
|
|
94
|
+
function buttonsKey(): string {
|
|
95
|
+
const its = entries();
|
|
96
|
+
return JSON.stringify(its) + '|' + ui.imgPop;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
/**
|
|
100
|
+
* A phone's bar: the buttons alone — the line saying what's selected and its sizes ("Cabinet 24" L × 34 1/2" H…") takes
|
|
101
|
+
* room a phone hasn't got, and the drawing shows the sizes anyway — with no divider left at an end or doubled up.
|
|
102
|
+
*/
|
|
103
|
+
function buttonsOnly(items: ReturnType<typeof barItems>): ReturnType<typeof barItems> {
|
|
104
|
+
const out: ReturnType<typeof barItems> = [];
|
|
105
|
+
for (const it of items) {
|
|
106
|
+
if (it !== 'sep' && 'lbl' in it) continue;
|
|
107
|
+
if (it === 'sep' && (!out.length || out[out.length - 1] === 'sep')) continue;
|
|
108
|
+
out.push(it);
|
|
109
|
+
}
|
|
110
|
+
while (out[out.length - 1] === 'sep') out.pop();
|
|
111
|
+
return out;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
/**
|
|
115
|
+
* The bar's buttons, drawn again only when what they show changes (`k`, see buttonsKey) — not when the bar itself
|
|
116
|
+
* is, as it measures whether they fit.
|
|
117
|
+
*/
|
|
118
|
+
const BarButtons = memo(function BarButtons({ phone }: { k: string; phone: boolean }) {
|
|
55
119
|
return (
|
|
56
120
|
<>
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
121
|
+
{shown(phone).map((it, i) => {
|
|
122
|
+
if (it === 'sep') return <View key={'s' + i} style={s.sep} />;
|
|
123
|
+
if ('lbl' in it) return <Text key={'l' + i} style={s.lbl}>{it.lbl}</Text>;
|
|
124
|
+
const fg = it.on ? C.dark : it.locked ? C.red : '#fff';
|
|
125
|
+
return (
|
|
126
|
+
<Tap key={it.act + i} accessibilityRole="button" accessibilityLabel={it.label}
|
|
127
|
+
accessibilityState={it.on != null || it.locked != null ? { selected: !!(it.on || it.locked) } : undefined}
|
|
128
|
+
onPress={e => { if (!(is3d() && run3d(it.act))) applyCtxAction(it.act, e.currentTarget); }}
|
|
129
|
+
style={st => [s.cb, it.on || (it.act === 'imgOpacity' && ui.imgPop === 'opacity') || (it.act === 'imgRotate' && ui.imgPop === 'rotate') || (it.act === 'linkSize' && ui.imgPop === 'tilesize') ? { backgroundColor: 'rgba(255,255,255,0.14)' } : null,
|
|
130
|
+
it.on ? { backgroundColor: '#fff' } : hovered(st) && { backgroundColor: 'rgba(255,255,255,0.08)' }]}>
|
|
131
|
+
<Icon name={it.ic} color={fg} />
|
|
132
|
+
<Text style={[s.cbText, { color: fg }]}>{it.label}</Text>
|
|
133
|
+
</Tap>
|
|
134
|
+
);
|
|
135
|
+
})}
|
|
64
136
|
</>
|
|
65
137
|
);
|
|
138
|
+
});
|
|
139
|
+
|
|
140
|
+
/** Degrees, as the rotation slider shows them: whole, or to the half degree. */
|
|
141
|
+
const fmtDeg = (v: number): string => (Math.round(v * 2) / 2).toString().replace('-', '−') + '°';
|
|
142
|
+
|
|
143
|
+
/**
|
|
144
|
+
* The selected image's slider, just above the bar: its opacity, or how far it's turned (Opacity or Rotate in the bar
|
|
145
|
+
* opens and closes it), with Reset to stand a turned one upright again. A locked image can't be turned: its slider
|
|
146
|
+
* goes with the lock.
|
|
147
|
+
*
|
|
148
|
+
* Kept quick on a device: as it's dragged the thumb and the number follow the finger from here (`live`), the image is
|
|
149
|
+
* changed at most once a frame, and only the photos are drawn again (requestImageDraw) — the rest of the editor
|
|
150
|
+
* hears of it once, as it's let go.
|
|
151
|
+
*/
|
|
152
|
+
function ImagePopover() {
|
|
153
|
+
const [live, setLive] = useState<number | null>(null);
|
|
154
|
+
const pending = useRef<{ v: number; frame: number } | null>(null);
|
|
155
|
+
const now = ed.sel.length === 1 && ed.sel[0].type === 'underlay' ? imageById(ed.sel[0].id) : undefined;
|
|
156
|
+
const kind = ui.imgPop === 'opacity' || ui.imgPop === 'rotate' ? ui.imgPop : null;
|
|
157
|
+
const open = !!kind && !!now && !(kind === 'rotate' && now.locked);
|
|
158
|
+
// (it rises from the bar; as it goes it keeps showing the image it was for)
|
|
159
|
+
const last = useRef<{ kind: 'opacity' | 'rotate'; sel: NonNullable<typeof now> } | null>(null), pop = usePopup(open, { y: 8 }, 'bottom center');
|
|
160
|
+
if (open) last.current = { kind: kind!, sel: now! };
|
|
161
|
+
if (!pop.present || !last.current) return null;
|
|
162
|
+
const { sel } = last.current, rotate = last.current.kind === 'rotate', id = sel.id;
|
|
163
|
+
const apply = (v: number) => (rotate ? setUnderlayRotation(id, v) : setUnderlayOpacity(id, v / 100));
|
|
164
|
+
const value = live ?? (rotate ? sel.rot ?? 0 : Math.round((sel.opacity ?? 0.6) * 100));
|
|
165
|
+
const change = (v: number) => {
|
|
166
|
+
setLive(v);
|
|
167
|
+
const p = pending.current;
|
|
168
|
+
if (p) { p.v = v; return; }
|
|
169
|
+
pending.current = { v, frame: requestAnimationFrame(() => { const q = pending.current; pending.current = null; if (q) apply(q.v); }) };
|
|
170
|
+
};
|
|
171
|
+
const end = () => {
|
|
172
|
+
const p = pending.current;
|
|
173
|
+
if (p) { cancelAnimationFrame(p.frame); pending.current = null; apply(p.v); }
|
|
174
|
+
setLive(null);
|
|
175
|
+
notify();
|
|
176
|
+
};
|
|
177
|
+
const upright = Math.abs(sel.rot ?? 0) < 1e-9;
|
|
178
|
+
return (
|
|
179
|
+
<View style={s.popWrap} pointerEvents="box-none">
|
|
180
|
+
<Animated.View style={[s.pop, rotate && s.popReset, pop.style]} pointerEvents={open ? 'auto' : 'none'} testID={open ? (rotate ? 'rotatePop' : 'opacityPop') : undefined}>
|
|
181
|
+
<Text style={s.popLabel}>{rotate ? 'Rotate' : 'Opacity'}</Text>
|
|
182
|
+
<View style={{ flex: 1 }}>
|
|
183
|
+
{rotate
|
|
184
|
+
? <Slider label="Image rotation" testID="rotateSlider" value={value} min={-ROTATE_MAX} max={ROTATE_MAX} step={ROTATE_STEP} format={fmtDeg}
|
|
185
|
+
onStart={pushHistory} onChange={change} onEnd={end} />
|
|
186
|
+
: <Slider label="Image opacity" testID="opacitySlider" value={value} min={OPACITY_MIN} max={100} step={OPACITY_STEP}
|
|
187
|
+
onStart={pushHistory} onChange={change} onEnd={end} />}
|
|
188
|
+
</View>
|
|
189
|
+
<Text style={s.popValue}>{rotate ? fmtDeg(value) : value + '%'}</Text>
|
|
190
|
+
{rotate && (
|
|
191
|
+
<Tap feel="dim" accessibilityRole="button" accessibilityLabel="Reset rotation" accessibilityState={{ disabled: upright }} disabled={upright}
|
|
192
|
+
testID="rotateReset" onPress={() => { pushHistory(); setUnderlayRotation(id, 0); notify(); }} style={[s.reset, upright && { opacity: 0.4 }]}>
|
|
193
|
+
<Text style={s.resetText}>Reset</Text>
|
|
194
|
+
</Tap>
|
|
195
|
+
)}
|
|
196
|
+
</Animated.View>
|
|
197
|
+
</View>
|
|
198
|
+
);
|
|
66
199
|
}
|
|
67
200
|
|
|
68
|
-
|
|
201
|
+
/** A tile size from the slider, as it's kept: to the quarter inch. */
|
|
202
|
+
const tileSize = (step: number): number => Math.round(stepToSize(step) * 4) / 4;
|
|
203
|
+
|
|
204
|
+
/**
|
|
205
|
+
* The tiles' size, just above the bar (Size in it opens and closes it): one size for every tile on the page, on the drawing
|
|
206
|
+
* (they zoom with the plan and print at it). Kept quick on a device, as ImagePopover is: the thumb and the number follow
|
|
207
|
+
* the finger from here, the tiles are sized at most once a frame, and the rest of the editor hears of it as it's let go.
|
|
208
|
+
*/
|
|
209
|
+
function LinkSizePopover() {
|
|
210
|
+
const [live, setLive] = useState<number | null>(null);
|
|
211
|
+
// the number's width as it was laid out, held while a drag goes on (the track ends at it: as long as the number is,
|
|
212
|
+
// no longer — but never narrower mid-drag, which would move the track under the finger)
|
|
213
|
+
const [numW, setNumW] = useState(0);
|
|
214
|
+
const pending = useRef<{ v: number; frame: number } | null>(null);
|
|
215
|
+
// (opened from the bar's Size, as an image's sliders are; a press on the drawing closes it)
|
|
216
|
+
const size = ui.imgPop === 'tilesize' && linkSizeVisible() ? pageLinkSize() : null, open = size != null;
|
|
217
|
+
// (as it goes it keeps showing the size it had)
|
|
218
|
+
const last = useRef(size ?? LINK_SIDE_IN), pop = usePopup(open, { y: 8 }, 'bottom center');
|
|
219
|
+
if (size != null) last.current = size;
|
|
220
|
+
if (!pop.present) return null;
|
|
221
|
+
const value = live ?? sizeToStep(last.current);
|
|
222
|
+
const change = (v: number) => {
|
|
223
|
+
setLive(v);
|
|
224
|
+
const p = pending.current;
|
|
225
|
+
if (p) { p.v = v; return; }
|
|
226
|
+
pending.current = { v, frame: requestAnimationFrame(() => { const q = pending.current; pending.current = null; if (q) setLinkSize(tileSize(q.v)); }) };
|
|
227
|
+
};
|
|
228
|
+
const end = () => {
|
|
229
|
+
const p = pending.current;
|
|
230
|
+
if (p) { cancelAnimationFrame(p.frame); pending.current = null; setLinkSize(tileSize(p.v)); }
|
|
231
|
+
setLive(null);
|
|
232
|
+
afterEdit();
|
|
233
|
+
notify();
|
|
234
|
+
};
|
|
235
|
+
return (
|
|
236
|
+
<View style={s.popWrap} pointerEvents="box-none">
|
|
237
|
+
<Animated.View style={[s.pop, pop.style]} pointerEvents={open ? 'auto' : 'none'} testID={open ? 'tileSizePop' : undefined}>
|
|
238
|
+
<Text style={s.popLabel}>Tile size</Text>
|
|
239
|
+
<View style={{ flex: 1 }}>
|
|
240
|
+
<Slider label="Tile size" testID="tileSizeSlider" value={value} min={0} max={SIZE_STEPS} step={1} format={v => fmtLen(tileSize(v))}
|
|
241
|
+
onStart={pushHistory} onChange={change} onEnd={end} />
|
|
242
|
+
</View>
|
|
243
|
+
<Text onLayout={e => { if (live == null) setNumW(e.nativeEvent.layout.width); }}
|
|
244
|
+
style={[s.popValue, s.sizeValue, live != null && numW ? { minWidth: numW } : null]}>{fmtLen(live != null ? tileSize(live) : last.current)}</Text>
|
|
245
|
+
</Animated.View>
|
|
246
|
+
</View>
|
|
247
|
+
);
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
/** The hint for screen readers: what to do next with the tool or what's picked, read out as it changes. */
|
|
251
|
+
export function SpokenHint() {
|
|
252
|
+
useEditorKey(currentHint);
|
|
253
|
+
return (
|
|
254
|
+
<View style={s.srOnly} accessibilityLiveRegion="polite" nativeID="hint">
|
|
255
|
+
<RichText html={currentHint()} />
|
|
256
|
+
</View>
|
|
257
|
+
);
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
/** What the trash (or Annotate's +, in its place) takes at the right of a phone's bar row: its 60 pt, and the 12 between them. */
|
|
261
|
+
const TRASH_ROOM = 72;
|
|
262
|
+
|
|
263
|
+
const s = createSheet({
|
|
69
264
|
bottom: { pointerEvents: 'box-none', position: 'absolute', bottom: 14, zIndex: 24, flexDirection: 'row', alignItems: 'center' },
|
|
70
265
|
centered: { left: 90, right: 90, justifyContent: 'center' },
|
|
71
|
-
|
|
266
|
+
phoneRow: { left: 12, right: 12 },
|
|
267
|
+
springL: { flexGrow: 1, flexShrink: 1000 },
|
|
268
|
+
springR: { flexGrow: 1, flexShrink: 0 },
|
|
269
|
+
// the same 6 all round, and corners of the buttons' radius plus 6: the end buttons sit concentric in the bar
|
|
270
|
+
// (it gives way when its buttons don't fit — React Native doesn't shrink a view unless told — and then scrolls)
|
|
271
|
+
ctx: { flexDirection: 'row', alignItems: 'center', backgroundColor: C.g2, borderRadius: 27 + 6, minHeight: 54 + 12, padding: 6, maxWidth: '100%', flexShrink: 1, ...shadow },
|
|
72
272
|
items: { flexDirection: 'row', alignItems: 'center', gap: 2 },
|
|
73
273
|
cb: { minWidth: 58, height: 54, borderRadius: 27, alignItems: 'center', justifyContent: 'center', gap: 3, paddingHorizontal: 8 },
|
|
74
274
|
cbText: { fontFamily: FONT, fontSize: 10.5, fontWeight: '500' },
|
|
75
275
|
sep: { width: 1, height: 32, backgroundColor: C.g3, marginHorizontal: 4 },
|
|
76
276
|
lbl: { fontFamily: FONT, fontSize: 12, color: C.lg1, paddingHorizontal: 10 },
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
277
|
+
// the slider sits just above the bar, centred on it
|
|
278
|
+
popWrap: { position: 'absolute', left: 0, right: 0, bottom: 76, alignItems: 'center' },
|
|
279
|
+
pop: { width: 400, maxWidth: '100%', flexDirection: 'row', alignItems: 'center', gap: 10, backgroundColor: C.g2, borderRadius: 999, paddingHorizontal: 16, paddingVertical: 4, ...shadow },
|
|
280
|
+
popLabel: { fontFamily: FONT, fontSize: 12.5, fontWeight: '600', color: C.lg2 },
|
|
281
|
+
popValue: { fontFamily: FONT, fontSize: 12.5, fontWeight: '600', color: '#fff', width: 48, textAlign: 'right' },
|
|
282
|
+
// (a length: as long as it is, the track running up to it)
|
|
283
|
+
sizeValue: { width: 'auto', minWidth: 0 },
|
|
284
|
+
// back to upright: a pill at the end of the rotation slider, concentric with it — the same 4 above, below and beside
|
|
285
|
+
// it, and the slider's radius (22, half its 44) less that 4 for its own
|
|
286
|
+
popReset: { paddingRight: 4 },
|
|
287
|
+
reset: { height: 36, paddingHorizontal: 16, borderRadius: 18, backgroundColor: C.g3, justifyContent: 'center' },
|
|
288
|
+
resetText: { fontFamily: FONT, fontSize: 12.5, fontWeight: '600', color: '#fff' },
|
|
289
|
+
// (smaller than a button, so 3 in from the bar's end as well as from its top and bottom: concentric too)
|
|
290
|
+
add: { width: 48, height: 48, borderRadius: 24, backgroundColor: C.yellow, alignItems: 'center', justifyContent: 'center', marginLeft: 4, marginRight: 3 },
|
|
291
|
+
// BOLDR's delete FAB (the annotation editors' DeleteFab): a 60 pt solid red circle 12 from the right and 14 from the
|
|
292
|
+
// bottom, level with the bar beside it, on every screen size
|
|
293
|
+
trash: {
|
|
294
|
+
position: 'absolute', right: 12, bottom: 14, zIndex: 24, width: 60, height: 60, borderRadius: 30,
|
|
295
|
+
backgroundColor: '#FD2525', alignItems: 'center', justifyContent: 'center',
|
|
296
|
+
boxShadow: '0 2.5px 2.5px rgba(0,0,0,0.15)', elevation: 4,
|
|
297
|
+
},
|
|
83
298
|
srOnly: { position: 'absolute', width: 1, height: 1, overflow: 'hidden', opacity: 0 },
|
|
299
|
+
// BOLDR's add FAB, in the trash's place: yellow, its + dark (white on the yellow is too faint)
|
|
300
|
+
fab: { backgroundColor: C.yellow },
|
|
84
301
|
});
|
package/src/react/Buttons.tsx
CHANGED
|
@@ -1,42 +1,83 @@
|
|
|
1
|
-
import type
|
|
2
|
-
import {
|
|
1
|
+
import { useMemo, type ReactNode } from 'react';
|
|
2
|
+
import { Animated, Pressable, Text, View, type PressableStateCallbackType, type StyleProp, type ViewStyle } from 'react-native';
|
|
3
3
|
import { Icon } from './Icon';
|
|
4
|
+
import { Tap, TOGGLE_EASE, TOGGLE_MS, TOGGLE_PRESS_MS, usePress, useToggleEased } from './motion';
|
|
4
5
|
import { C, FONT, shadow } from './theme';
|
|
6
|
+
import { createSheet } from './corners';
|
|
5
7
|
|
|
6
8
|
/** A round icon button (the top bar's circles). */
|
|
7
9
|
export function CircleButton({ icon, label, onPress, color = C.g3, iconColor = '#fff', size = 46, testID }: {
|
|
8
10
|
icon: string; label: string; onPress: () => void; color?: string; iconColor?: string; size?: number; testID?: string;
|
|
9
11
|
}) {
|
|
10
12
|
return (
|
|
11
|
-
<
|
|
12
|
-
style={
|
|
13
|
+
<Tap accessibilityRole="button" accessibilityLabel={label} testID={testID} onPress={onPress}
|
|
14
|
+
style={[s.circle, { width: size, height: size, backgroundColor: color }]}>
|
|
13
15
|
<Icon name={icon} color={iconColor} />
|
|
14
|
-
</
|
|
16
|
+
</Tap>
|
|
15
17
|
);
|
|
16
18
|
}
|
|
17
19
|
|
|
18
20
|
/** A rounded pill button: yellow for the main action, grey otherwise. */
|
|
19
|
-
export function Pill({ label, onPress, children, kind = 'yellow', style, testID, accessibilityLabel }: {
|
|
21
|
+
export function Pill({ label, onPress, children, kind = 'yellow', style, testID, accessibilityLabel, disabled }: {
|
|
20
22
|
label?: string; onPress: () => void; children?: ReactNode; kind?: 'yellow' | 'grey' | 'dark'; style?: StyleProp<ViewStyle>; testID?: string; accessibilityLabel?: string;
|
|
23
|
+
disabled?: boolean;
|
|
21
24
|
}) {
|
|
22
25
|
const bg = kind === 'yellow' ? C.yellow : kind === 'grey' ? C.g2 : C.g1, fg = kind === 'yellow' ? C.dark : '#fff';
|
|
23
26
|
return (
|
|
24
|
-
<
|
|
25
|
-
|
|
27
|
+
<Tap accessibilityRole="button" accessibilityLabel={accessibilityLabel ?? label} accessibilityState={{ disabled: !!disabled }}
|
|
28
|
+
testID={testID} onPress={onPress} disabled={disabled}
|
|
29
|
+
style={[s.pill, { backgroundColor: bg }, disabled && { opacity: 0.7 }, style]}>
|
|
26
30
|
{children}
|
|
27
31
|
{label ? <Text style={[s.pillText, { color: fg }]}>{label}</Text> : null}
|
|
32
|
+
</Tap>
|
|
33
|
+
);
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* BOLDR's on/off toggle: a grey track with its knob on the left when off, green with the knob on the right when on.
|
|
38
|
+
* As on Apple's switches, pressing it in stretches the knob toward the side it's about to go (to the right when off,
|
|
39
|
+
* to the left when on); letting go, the knob glides across, rounding up again, as the track fades (react/motion.tsx).
|
|
40
|
+
*/
|
|
41
|
+
export function Toggle({ label, value, onChange }: { label: string; value: boolean; onChange: () => void }) {
|
|
42
|
+
// pressed in it stretches (unhurried, so a quick tap still shows it); let go, it rounds up again over the whole glide
|
|
43
|
+
const { v: t, go } = useToggleEased(value), press = usePress(TOGGLE_PRESS_MS, TOGGLE_MS, TOGGLE_EASE, true);
|
|
44
|
+
// the knob sets off at once; the setting changes a frame later, once the glide is with the native driver (what it
|
|
45
|
+
// sets off — the menu drawn again, the drawing for a layer or a snap — then can't hold it up)
|
|
46
|
+
const flip = () => go(!value, onChange);
|
|
47
|
+
const travel = TOGGLE.w - TOGGLE.k - 2 * TOGGLE.pad;
|
|
48
|
+
// the stretch grows from the knob's outer edge: rightward from the left, leftward from the right. Drawn as a scale
|
|
49
|
+
// and a slide (what the native driver can play): the knob, k wide, scaled about its middle to k + stretch, its left
|
|
50
|
+
// edge at t × (travel − stretch) — so moved there plus half the stretch
|
|
51
|
+
const { fill, x, sx } = useMemo(() => {
|
|
52
|
+
const stretch = Animated.multiply(press.v, TOGGLE.stretch);
|
|
53
|
+
return {
|
|
54
|
+
fill: t.interpolate({ inputRange: [0, 1], outputRange: [C.g4, C.on] }),
|
|
55
|
+
x: Animated.add(Animated.subtract(Animated.multiply(t, travel), Animated.multiply(t, stretch)), Animated.multiply(stretch, 0.5)),
|
|
56
|
+
sx: Animated.add(1, Animated.multiply(press.v, TOGGLE.stretch / TOGGLE.k)),
|
|
57
|
+
};
|
|
58
|
+
}, [t, press.v, travel]);
|
|
59
|
+
return (
|
|
60
|
+
<Pressable accessibilityRole="switch" accessibilityLabel={label} accessibilityState={{ checked: value }} aria-checked={value} hitSlop={6}
|
|
61
|
+
onPressIn={press.onPressIn} onPressOut={press.onPressOut} onPress={flip}>
|
|
62
|
+
<Animated.View style={[s.track, { backgroundColor: fill }]}>
|
|
63
|
+
<Animated.View style={[s.knob, { transform: [{ translateX: x }, { scaleX: sx }] }]} />
|
|
64
|
+
</Animated.View>
|
|
28
65
|
</Pressable>
|
|
29
66
|
);
|
|
30
67
|
}
|
|
68
|
+
/** The Figma's 69 × 36 toggle (30 knob), scaled to the menus */
|
|
69
|
+
const TOGGLE = { w: 48, h: 25, k: 21, pad: 2, stretch: 6 };
|
|
31
70
|
|
|
32
71
|
export function Row({ children, gap = 8, style }: { children: ReactNode; gap?: number; style?: StyleProp<ViewStyle> }) {
|
|
33
72
|
return <View style={[{ flexDirection: 'row', alignItems: 'center', gap }, style]}>{children}</View>;
|
|
34
73
|
}
|
|
35
74
|
|
|
36
|
-
const s =
|
|
75
|
+
const s = createSheet({
|
|
37
76
|
circle: { borderRadius: 999, alignItems: 'center', justifyContent: 'center', borderWidth: 1, borderColor: 'rgba(255,255,255,0.16)', ...shadow },
|
|
38
77
|
pill: { height: 46, minWidth: 64, paddingHorizontal: 18, borderRadius: 24, flexDirection: 'row', alignItems: 'center', justifyContent: 'center', gap: 8, ...shadow },
|
|
39
78
|
pillText: { fontFamily: FONT, fontWeight: '600', fontSize: 15 },
|
|
79
|
+
track: { width: TOGGLE.w, height: TOGGLE.h, borderRadius: 100, padding: TOGGLE.pad, justifyContent: 'center', alignItems: 'flex-start', flexShrink: 0 },
|
|
80
|
+
knob: { width: TOGGLE.k, height: TOGGLE.k, borderRadius: 100, backgroundColor: '#fff', boxShadow: '0px 2px 4px rgba(0, 0, 0, 0.2)' },
|
|
40
81
|
});
|
|
41
82
|
|
|
42
83
|
/** Whether a Pressable is under the mouse (react-native-web adds `hovered`; devices without a mouse never are). */
|