@reekon-tools/boldr-utils 1.6.18 → 1.6.20
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/dist/annotation/canvas/AnnotationCanvasInner.js +79 -12
- package/dist/annotation/canvas/AnnotationCanvasInner.native.js +100 -13
- package/dist/annotation/canvas/AnnotationCanvasSkia.d.ts +5 -1
- package/dist/annotation/canvas/AnnotationCanvasSkia.js +53 -4
- package/dist/annotation/canvas/Tool.d.ts +7 -2
- package/dist/annotation/canvas/measurementGeometry.d.ts +1 -1
- package/dist/annotation/canvas/measurementGeometry.js +7 -5
- package/dist/annotation/canvas/stampLayout.d.ts +8 -2
- package/dist/annotation/canvas/stampLayout.js +72 -9
- package/dist/annotation/canvas/tools/measurementLineTool.d.ts +12 -0
- package/dist/annotation/canvas/tools/measurementLineTool.js +95 -0
- package/dist/annotation/canvas/tools/measurementTool.js +8 -2
- package/dist/annotation/canvas/tools/panTool.js +1 -1
- package/dist/annotation/canvas/tools/selectTool.js +116 -13
- package/dist/annotation/canvas/tools/textEditing.d.ts +4 -0
- package/dist/annotation/canvas/tools/textEditing.js +36 -0
- package/dist/annotation/canvas/tools/textTool.js +3 -26
- package/dist/annotation/canvas/useAnnotationCanvasState.d.ts +2 -0
- package/dist/annotation/canvas/useAnnotationCanvasState.js +18 -0
- package/dist/exports.d.ts +1 -0
- package/dist/exports.js +1 -0
- package/dist/types/annotation.d.ts +4 -0
- package/dist/types/annotation.js +6 -0
- package/dist/types/firestore.d.ts +53 -0
- package/dist/types/firestore.js +49 -0
- package/package.json +1 -1
- package/dist/canvas/AnnotationCanvas.d.ts +0 -11
- package/dist/canvas/AnnotationCanvas.js +0 -10
- package/dist/canvas/AnnotationCanvas.native.d.ts +0 -8
- package/dist/canvas/AnnotationCanvas.native.js +0 -6
- package/dist/canvas/AnnotationCanvasInner.d.ts +0 -39
- package/dist/canvas/AnnotationCanvasInner.js +0 -219
- package/dist/canvas/AnnotationCanvasInner.native.d.ts +0 -35
- package/dist/canvas/AnnotationCanvasInner.native.js +0 -138
- package/dist/canvas/AnnotationCanvasSkia.d.ts +0 -27
- package/dist/canvas/AnnotationCanvasSkia.js +0 -20
- package/dist/canvas/Tool.d.ts +0 -38
- package/dist/canvas/Tool.js +0 -1
- package/dist/canvas/elements/BackgroundImageElement.d.ts +0 -9
- package/dist/canvas/elements/BackgroundImageElement.js +0 -37
- package/dist/canvas/elements/MeasurementStampElement.d.ts +0 -13
- package/dist/canvas/elements/MeasurementStampElement.js +0 -30
- package/dist/canvas/elements/ShapeElement.d.ts +0 -7
- package/dist/canvas/elements/ShapeElement.js +0 -62
- package/dist/canvas/elements/StrokeElement.d.ts +0 -7
- package/dist/canvas/elements/StrokeElement.js +0 -18
- package/dist/canvas/measurementPicker.d.ts +0 -10
- package/dist/canvas/measurementPicker.js +0 -1
- package/dist/canvas/measurementStampOverlay.d.ts +0 -11
- package/dist/canvas/measurementStampOverlay.js +0 -1
- package/dist/canvas/pointerAdapter.d.ts +0 -3
- package/dist/canvas/pointerAdapter.js +0 -19
- package/dist/canvas/stampLayout.d.ts +0 -5
- package/dist/canvas/stampLayout.js +0 -14
- package/dist/canvas/tools/measurementStampTool.d.ts +0 -9
- package/dist/canvas/tools/measurementStampTool.js +0 -37
- package/dist/canvas/tools/panTool.d.ts +0 -5
- package/dist/canvas/tools/panTool.js +0 -25
- package/dist/canvas/tools/penTool.d.ts +0 -13
- package/dist/canvas/tools/penTool.js +0 -68
- package/dist/canvas/tools/selectTool.d.ts +0 -2
- package/dist/canvas/tools/selectTool.js +0 -182
- package/dist/canvas/useAnnotationCanvasState.d.ts +0 -54
- package/dist/canvas/useAnnotationCanvasState.js +0 -210
- package/dist/canvas/viewport.d.ts +0 -16
- package/dist/canvas/viewport.js +0 -54
- package/dist/data/AnnotationDataContext.d.ts +0 -8
- package/dist/data/AnnotationDataContext.js +0 -11
- package/dist/data/AnnotationDataProvider.d.ts +0 -65
- package/dist/data/AnnotationDataProvider.js +0 -4
- package/dist/data/InMemoryAnnotationProvider.d.ts +0 -30
- package/dist/data/InMemoryAnnotationProvider.js +0 -197
- package/dist/data/canvasPersistence.d.ts +0 -3
- package/dist/data/canvasPersistence.js +0 -26
- package/dist/data/hooks/useAnnotationCanvasDoc.d.ts +0 -33
- package/dist/data/hooks/useAnnotationCanvasDoc.js +0 -314
- package/dist/data/hooks/useAnnotationDoc.d.ts +0 -7
- package/dist/data/hooks/useAnnotationDoc.js +0 -33
- package/dist/data/hooks/useAnnotationList.d.ts +0 -7
- package/dist/data/hooks/useAnnotationList.js +0 -26
- package/dist/data/hooks/useAnnotationMutations.d.ts +0 -9
- package/dist/data/hooks/useAnnotationMutations.js +0 -11
- package/dist/hooks/useParseMeasurement.d.ts +0 -4
- package/dist/hooks/useParseMeasurement.js +0 -14
- package/dist/utils/evaluateFormula.d.ts +0 -20
- package/dist/utils/evaluateFormula.js +0 -31
|
@@ -103,6 +103,7 @@ export interface AnnotationCanvasState {
|
|
|
103
103
|
strokes: AnnotationStroke[];
|
|
104
104
|
shapes: AnnotationShape[];
|
|
105
105
|
placedMeasurements: PlacedMeasurementRef[];
|
|
106
|
+
tileScaleFactor?: number;
|
|
106
107
|
externalPayloadPath?: string;
|
|
107
108
|
}
|
|
108
109
|
export type AnnotationElement = (AnnotationStroke & {
|
|
@@ -148,6 +149,9 @@ export type AnnotationPatchOp = {
|
|
|
148
149
|
} | {
|
|
149
150
|
op: 'setViewport';
|
|
150
151
|
patch: Partial<AnnotationViewport>;
|
|
152
|
+
} | {
|
|
153
|
+
op: 'setTileScaleFactor';
|
|
154
|
+
value: number;
|
|
151
155
|
} | {
|
|
152
156
|
op: 'setLayers';
|
|
153
157
|
layers: AnnotationLayer[];
|
package/dist/types/annotation.js
CHANGED
|
@@ -67,6 +67,8 @@ const applyOp = (state, op) => {
|
|
|
67
67
|
};
|
|
68
68
|
case 'setViewport':
|
|
69
69
|
return { ...state, viewport: { ...state.viewport, ...op.patch } };
|
|
70
|
+
case 'setTileScaleFactor':
|
|
71
|
+
return { ...state, tileScaleFactor: op.value };
|
|
70
72
|
case 'setLayers':
|
|
71
73
|
return { ...state, layers: op.layers };
|
|
72
74
|
}
|
|
@@ -141,6 +143,10 @@ const invertOp = (before, op) => {
|
|
|
141
143
|
}
|
|
142
144
|
return { op: 'setViewport', patch: inversePatch };
|
|
143
145
|
}
|
|
146
|
+
case 'setTileScaleFactor':
|
|
147
|
+
// Restore the prior factor; absent → an explicit 1 (visually identical to
|
|
148
|
+
// absent), keeping the op's `value: number` contract.
|
|
149
|
+
return { op: 'setTileScaleFactor', value: before.tileScaleFactor ?? 1 };
|
|
144
150
|
case 'setLayers':
|
|
145
151
|
return { op: 'setLayers', layers: before.layers };
|
|
146
152
|
}
|
|
@@ -427,6 +427,53 @@ export declare enum DecimalTolerance {
|
|
|
427
427
|
Hundredth = "0.01",
|
|
428
428
|
Thousandth = "0.001"
|
|
429
429
|
}
|
|
430
|
+
/**
|
|
431
|
+
* Trade/industry a user works in. Collected during registration to personalize
|
|
432
|
+
* the experience. String values are stable identifiers safe for persistence and
|
|
433
|
+
* analytics; they double as i18n key suffixes (e.g. `trades.iron_worker`).
|
|
434
|
+
*/
|
|
435
|
+
export declare enum UserTrades {
|
|
436
|
+
Woodworking = "woodworking",
|
|
437
|
+
Plumbing = "plumbing",
|
|
438
|
+
Electrical = "electrical",
|
|
439
|
+
Structural = "structural",
|
|
440
|
+
Framing = "framing",
|
|
441
|
+
Carpenter = "carpenter",
|
|
442
|
+
IronWorker = "iron_worker",
|
|
443
|
+
Joiners = "joiners",
|
|
444
|
+
HVAC = "hvac",
|
|
445
|
+
Painting = "painting",
|
|
446
|
+
GC = "gc",
|
|
447
|
+
IT = "it",
|
|
448
|
+
Surveying = "surveying",
|
|
449
|
+
Pipefitter = "pipe_fitter",
|
|
450
|
+
Drywall = "drywall",
|
|
451
|
+
Welding = "welding",
|
|
452
|
+
Flooring = "flooring",
|
|
453
|
+
Decking = "decking",
|
|
454
|
+
Tile = "tile",
|
|
455
|
+
DIY = "diy",
|
|
456
|
+
Other = "other"
|
|
457
|
+
}
|
|
458
|
+
/**
|
|
459
|
+
* Role a user holds within their trade. Collected during registration. String
|
|
460
|
+
* values double as i18n key suffixes (e.g. `roles.project_manager`).
|
|
461
|
+
*/
|
|
462
|
+
export declare enum UserRoles {
|
|
463
|
+
Apprentice = "apprentice",
|
|
464
|
+
Journeyman = "journeyman",
|
|
465
|
+
Owner = "owner",
|
|
466
|
+
ProjectManager = "project_manager",
|
|
467
|
+
Supervisor = "supervisor",
|
|
468
|
+
Foreman = "foreman",
|
|
469
|
+
Superintendent = "superintendent",
|
|
470
|
+
Estimator = "estimator",
|
|
471
|
+
OfficeManager = "office_manager",
|
|
472
|
+
Operator = "operator",
|
|
473
|
+
Laborer = "laborer",
|
|
474
|
+
Engineer = "engineer",
|
|
475
|
+
Safety = "safety"
|
|
476
|
+
}
|
|
430
477
|
export interface UserDocument extends FirestoreDoc, Timestamps {
|
|
431
478
|
defaultOrganization: string;
|
|
432
479
|
displayName: string;
|
|
@@ -440,6 +487,12 @@ export interface UserDocument extends FirestoreDoc, Timestamps {
|
|
|
440
487
|
printLabelSize: string;
|
|
441
488
|
printLogoFileId: string;
|
|
442
489
|
devices?: Record<string, string>;
|
|
490
|
+
/** Four-digit birth year. Optional — not required during registration. */
|
|
491
|
+
birthYear?: number;
|
|
492
|
+
/** The trade/industry the user works in. Captured during registration. */
|
|
493
|
+
industry?: UserTrades;
|
|
494
|
+
/** The user's role within their trade. Optional. */
|
|
495
|
+
role?: UserRoles;
|
|
443
496
|
}
|
|
444
497
|
export interface UserComment extends FirestoreDoc, Timestamps {
|
|
445
498
|
comment: string;
|
package/dist/types/firestore.js
CHANGED
|
@@ -123,3 +123,52 @@ export var DecimalTolerance;
|
|
|
123
123
|
DecimalTolerance["Hundredth"] = "0.01";
|
|
124
124
|
DecimalTolerance["Thousandth"] = "0.001";
|
|
125
125
|
})(DecimalTolerance || (DecimalTolerance = {}));
|
|
126
|
+
/**
|
|
127
|
+
* Trade/industry a user works in. Collected during registration to personalize
|
|
128
|
+
* the experience. String values are stable identifiers safe for persistence and
|
|
129
|
+
* analytics; they double as i18n key suffixes (e.g. `trades.iron_worker`).
|
|
130
|
+
*/
|
|
131
|
+
export var UserTrades;
|
|
132
|
+
(function (UserTrades) {
|
|
133
|
+
UserTrades["Woodworking"] = "woodworking";
|
|
134
|
+
UserTrades["Plumbing"] = "plumbing";
|
|
135
|
+
UserTrades["Electrical"] = "electrical";
|
|
136
|
+
UserTrades["Structural"] = "structural";
|
|
137
|
+
UserTrades["Framing"] = "framing";
|
|
138
|
+
UserTrades["Carpenter"] = "carpenter";
|
|
139
|
+
UserTrades["IronWorker"] = "iron_worker";
|
|
140
|
+
UserTrades["Joiners"] = "joiners";
|
|
141
|
+
UserTrades["HVAC"] = "hvac";
|
|
142
|
+
UserTrades["Painting"] = "painting";
|
|
143
|
+
UserTrades["GC"] = "gc";
|
|
144
|
+
UserTrades["IT"] = "it";
|
|
145
|
+
UserTrades["Surveying"] = "surveying";
|
|
146
|
+
UserTrades["Pipefitter"] = "pipe_fitter";
|
|
147
|
+
UserTrades["Drywall"] = "drywall";
|
|
148
|
+
UserTrades["Welding"] = "welding";
|
|
149
|
+
UserTrades["Flooring"] = "flooring";
|
|
150
|
+
UserTrades["Decking"] = "decking";
|
|
151
|
+
UserTrades["Tile"] = "tile";
|
|
152
|
+
UserTrades["DIY"] = "diy";
|
|
153
|
+
UserTrades["Other"] = "other";
|
|
154
|
+
})(UserTrades || (UserTrades = {}));
|
|
155
|
+
/**
|
|
156
|
+
* Role a user holds within their trade. Collected during registration. String
|
|
157
|
+
* values double as i18n key suffixes (e.g. `roles.project_manager`).
|
|
158
|
+
*/
|
|
159
|
+
export var UserRoles;
|
|
160
|
+
(function (UserRoles) {
|
|
161
|
+
UserRoles["Apprentice"] = "apprentice";
|
|
162
|
+
UserRoles["Journeyman"] = "journeyman";
|
|
163
|
+
UserRoles["Owner"] = "owner";
|
|
164
|
+
UserRoles["ProjectManager"] = "project_manager";
|
|
165
|
+
UserRoles["Supervisor"] = "supervisor";
|
|
166
|
+
UserRoles["Foreman"] = "foreman";
|
|
167
|
+
UserRoles["Superintendent"] = "superintendent";
|
|
168
|
+
UserRoles["Estimator"] = "estimator";
|
|
169
|
+
UserRoles["OfficeManager"] = "office_manager";
|
|
170
|
+
UserRoles["Operator"] = "operator";
|
|
171
|
+
UserRoles["Laborer"] = "laborer";
|
|
172
|
+
UserRoles["Engineer"] = "engineer";
|
|
173
|
+
UserRoles["Safety"] = "safety";
|
|
174
|
+
})(UserRoles || (UserRoles = {}));
|
package/package.json
CHANGED
|
@@ -1,11 +0,0 @@
|
|
|
1
|
-
import { type ReactNode } from 'react';
|
|
2
|
-
import type { AnnotationCanvasInnerProps } from './AnnotationCanvasInner.js';
|
|
3
|
-
export type { AnnotationCanvasHandle, GestureConfig, PanTrigger, } from './AnnotationCanvasInner.js';
|
|
4
|
-
export interface CanvasKitOpts {
|
|
5
|
-
locateFile?: (file: string) => string;
|
|
6
|
-
}
|
|
7
|
-
export type AnnotationCanvasProps = AnnotationCanvasInnerProps & {
|
|
8
|
-
fallback?: ReactNode;
|
|
9
|
-
canvasKitOpts?: CanvasKitOpts;
|
|
10
|
-
};
|
|
11
|
-
export declare const AnnotationCanvas: ({ fallback, canvasKitOpts, ...rest }: AnnotationCanvasProps) => import("react/jsx-runtime").JSX.Element;
|
|
@@ -1,10 +0,0 @@
|
|
|
1
|
-
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
-
import { WithSkiaWeb } from '@shopify/react-native-skia/lib/module/web/index.js';
|
|
3
|
-
// Web-only entry point: lazy-loads CanvasKit wasm via WithSkiaWeb. Imperative
|
|
4
|
-
// API (undo/redo/zoom) is exposed through the `imperativeRef` prop rather
|
|
5
|
-
// than React refs, since WithSkiaWeb can't forward refs through its lazy
|
|
6
|
-
// component boundary. The native build sits in `AnnotationCanvas.native.tsx`
|
|
7
|
-
// and skips WithSkiaWeb entirely (no wasm to load).
|
|
8
|
-
export const AnnotationCanvas = ({ fallback, canvasKitOpts, ...rest }) => (_jsx(WithSkiaWeb, { getComponent: () => import('./AnnotationCanvasInner.js').then((m) => ({
|
|
9
|
-
default: m.AnnotationCanvasInner,
|
|
10
|
-
})), fallback: fallback ?? null, componentProps: rest, opts: canvasKitOpts }));
|
|
@@ -1,8 +0,0 @@
|
|
|
1
|
-
import type { AnnotationCanvasInnerProps } from './AnnotationCanvasInner.native.js';
|
|
2
|
-
export type { AnnotationCanvasHandle, } from './useAnnotationCanvasState.js';
|
|
3
|
-
export type { GestureConfig, PanTrigger, } from './AnnotationCanvasInner.js';
|
|
4
|
-
export type AnnotationCanvasProps = AnnotationCanvasInnerProps & {
|
|
5
|
-
fallback?: unknown;
|
|
6
|
-
canvasKitOpts?: unknown;
|
|
7
|
-
};
|
|
8
|
-
export declare const AnnotationCanvas: (props: AnnotationCanvasProps) => import("react/jsx-runtime").JSX.Element;
|
|
@@ -1,6 +0,0 @@
|
|
|
1
|
-
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
-
import { AnnotationCanvasInner } from './AnnotationCanvasInner.native.js';
|
|
3
|
-
export const AnnotationCanvas = (props) => {
|
|
4
|
-
const { fallback: _f, canvasKitOpts: _o, ...rest } = props;
|
|
5
|
-
return _jsx(AnnotationCanvasInner, { ...rest });
|
|
6
|
-
};
|
|
@@ -1,39 +0,0 @@
|
|
|
1
|
-
import { type CSSProperties, type MutableRefObject } from 'react';
|
|
2
|
-
import type { DecimalTolerance, FractionalTolerance, Measurement, Units } from '../types/firestore.js';
|
|
3
|
-
import type { AnnotationCanvasState, AnnotationDocumentPatch, Selection } from '../types/annotation.js';
|
|
4
|
-
import type { MeasurementRef } from './measurementPicker.js';
|
|
5
|
-
import type { Tool } from './Tool.js';
|
|
6
|
-
import { type AnnotationCanvasHandle } from './useAnnotationCanvasState.js';
|
|
7
|
-
import { type ViewportState } from './viewport.js';
|
|
8
|
-
import type { RenderMeasurementStamp } from './measurementStampOverlay.js';
|
|
9
|
-
export type { AnnotationCanvasHandle };
|
|
10
|
-
export interface AnnotationCanvasInnerProps {
|
|
11
|
-
canvas: AnnotationCanvasState;
|
|
12
|
-
onCommit(patch: AnnotationDocumentPatch): void;
|
|
13
|
-
tools: Tool[];
|
|
14
|
-
activeToolId: string;
|
|
15
|
-
selection: Selection | null;
|
|
16
|
-
onSelectionChange(selection: Selection | null): void;
|
|
17
|
-
measurements?: Measurement[];
|
|
18
|
-
fallbackUnit?: Units;
|
|
19
|
-
fractionalTolerance?: FractionalTolerance;
|
|
20
|
-
decimalTolerance?: DecimalTolerance;
|
|
21
|
-
resolveImageUrl?: (storagePath: string) => Promise<string>;
|
|
22
|
-
pickMeasurement?: () => Promise<MeasurementRef | null>;
|
|
23
|
-
renderMeasurementStamp?: RenderMeasurementStamp;
|
|
24
|
-
stampFontSource?: unknown;
|
|
25
|
-
stampValueFontSize?: number;
|
|
26
|
-
stampLabelFontSize?: number;
|
|
27
|
-
gestures?: GestureConfig;
|
|
28
|
-
width: number;
|
|
29
|
-
height: number;
|
|
30
|
-
initialViewport?: ViewportState;
|
|
31
|
-
style?: CSSProperties;
|
|
32
|
-
imperativeRef?: MutableRefObject<AnnotationCanvasHandle | null>;
|
|
33
|
-
}
|
|
34
|
-
export type PanTrigger = 'middleMouse' | 'rightMouse' | 'space';
|
|
35
|
-
export interface GestureConfig {
|
|
36
|
-
wheel?: 'zoom' | 'pan' | 'auto';
|
|
37
|
-
panTriggers?: PanTrigger[];
|
|
38
|
-
}
|
|
39
|
-
export declare const AnnotationCanvasInner: (props: AnnotationCanvasInnerProps) => import("react/jsx-runtime").JSX.Element;
|
|
@@ -1,219 +0,0 @@
|
|
|
1
|
-
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
-
import { useFont } from '@shopify/react-native-skia';
|
|
3
|
-
import { useCallback, useEffect, useRef, } from 'react';
|
|
4
|
-
import { AnnotationCanvasSkia } from './AnnotationCanvasSkia.js';
|
|
5
|
-
import { useAnnotationCanvasState, } from './useAnnotationCanvasState.js';
|
|
6
|
-
import { STAMP_TILE_SIZE } from './stampLayout.js';
|
|
7
|
-
const DEFAULT_PAN_TRIGGERS = ['middleMouse', 'space'];
|
|
8
|
-
export const AnnotationCanvasInner = (props) => {
|
|
9
|
-
const { fallbackUnit, fractionalTolerance, decimalTolerance, resolveImageUrl, stampFontSource, stampValueFontSize = 14, stampLabelFontSize = 11, gestures, width, height, style, activeToolId, tools, } = props;
|
|
10
|
-
const wheelMode = gestures?.wheel ?? 'auto';
|
|
11
|
-
const panTriggers = gestures?.panTriggers ?? DEFAULT_PAN_TRIGGERS;
|
|
12
|
-
const allowSpacePan = panTriggers.includes('space');
|
|
13
|
-
const allowMiddlePan = panTriggers.includes('middleMouse');
|
|
14
|
-
const allowRightPan = panTriggers.includes('rightMouse');
|
|
15
|
-
const valueFont = useFont(stampFontSource, stampValueFontSize);
|
|
16
|
-
const labelFont = useFont(stampFontSource, stampLabelFontSize);
|
|
17
|
-
const state = useAnnotationCanvasState(props);
|
|
18
|
-
const containerRef = useRef(null);
|
|
19
|
-
const panGestureRef = useRef(null);
|
|
20
|
-
const spaceDownRef = useRef(false);
|
|
21
|
-
const activeTool = tools.find((t) => t.id === activeToolId) ?? null;
|
|
22
|
-
const toCanvasPointer = useCallback((event) => {
|
|
23
|
-
const rect = containerRef.current?.getBoundingClientRect();
|
|
24
|
-
const screen = {
|
|
25
|
-
x: event.clientX - (rect?.left ?? 0),
|
|
26
|
-
y: event.clientY - (rect?.top ?? 0),
|
|
27
|
-
};
|
|
28
|
-
return {
|
|
29
|
-
pointerId: event.pointerId,
|
|
30
|
-
screen,
|
|
31
|
-
world: state.ctx.viewport.screenToWorld(screen),
|
|
32
|
-
pressure: event.pressure || undefined,
|
|
33
|
-
shiftKey: event.shiftKey,
|
|
34
|
-
altKey: event.altKey,
|
|
35
|
-
metaKey: event.metaKey,
|
|
36
|
-
ctrlKey: event.ctrlKey,
|
|
37
|
-
};
|
|
38
|
-
}, [state.ctx.viewport]);
|
|
39
|
-
const isPanTriggerDown = useCallback((event) => {
|
|
40
|
-
if (allowMiddlePan && event.button === 1)
|
|
41
|
-
return true;
|
|
42
|
-
if (allowRightPan && event.button === 2)
|
|
43
|
-
return true;
|
|
44
|
-
if (allowSpacePan && event.button === 0 && spaceDownRef.current)
|
|
45
|
-
return true;
|
|
46
|
-
return false;
|
|
47
|
-
}, [allowMiddlePan, allowRightPan, allowSpacePan]);
|
|
48
|
-
const handlePointerDown = useCallback((event) => {
|
|
49
|
-
const rect = containerRef.current?.getBoundingClientRect();
|
|
50
|
-
const screen = {
|
|
51
|
-
x: event.clientX - (rect?.left ?? 0),
|
|
52
|
-
y: event.clientY - (rect?.top ?? 0),
|
|
53
|
-
};
|
|
54
|
-
if (isPanTriggerDown(event)) {
|
|
55
|
-
panGestureRef.current = { pointerId: event.pointerId, lastScreen: screen };
|
|
56
|
-
event.currentTarget.setPointerCapture(event.pointerId);
|
|
57
|
-
event.preventDefault();
|
|
58
|
-
return;
|
|
59
|
-
}
|
|
60
|
-
event.currentTarget.setPointerCapture(event.pointerId);
|
|
61
|
-
state.dispatchPointerDown(toCanvasPointer(event));
|
|
62
|
-
}, [state, toCanvasPointer, isPanTriggerDown]);
|
|
63
|
-
const handlePointerMove = useCallback((event) => {
|
|
64
|
-
const pan = panGestureRef.current;
|
|
65
|
-
if (pan && event.pointerId === pan.pointerId) {
|
|
66
|
-
const rect = containerRef.current?.getBoundingClientRect();
|
|
67
|
-
const screen = {
|
|
68
|
-
x: event.clientX - (rect?.left ?? 0),
|
|
69
|
-
y: event.clientY - (rect?.top ?? 0),
|
|
70
|
-
};
|
|
71
|
-
state.pan({
|
|
72
|
-
x: screen.x - pan.lastScreen.x,
|
|
73
|
-
y: screen.y - pan.lastScreen.y,
|
|
74
|
-
});
|
|
75
|
-
panGestureRef.current = { pointerId: pan.pointerId, lastScreen: screen };
|
|
76
|
-
return;
|
|
77
|
-
}
|
|
78
|
-
state.dispatchPointerMove(toCanvasPointer(event));
|
|
79
|
-
}, [state, toCanvasPointer]);
|
|
80
|
-
const handlePointerUp = useCallback((event) => {
|
|
81
|
-
const pan = panGestureRef.current;
|
|
82
|
-
if (pan && event.pointerId === pan.pointerId) {
|
|
83
|
-
panGestureRef.current = null;
|
|
84
|
-
return;
|
|
85
|
-
}
|
|
86
|
-
state.dispatchPointerUp(toCanvasPointer(event));
|
|
87
|
-
}, [state, toCanvasPointer]);
|
|
88
|
-
const handlePointerCancel = useCallback(() => {
|
|
89
|
-
panGestureRef.current = null;
|
|
90
|
-
state.dispatchPointerCancel();
|
|
91
|
-
}, [state]);
|
|
92
|
-
const handleWheel = useCallback((event) => {
|
|
93
|
-
const rect = containerRef.current?.getBoundingClientRect();
|
|
94
|
-
const focal = {
|
|
95
|
-
x: event.clientX - (rect?.left ?? 0),
|
|
96
|
-
y: event.clientY - (rect?.top ?? 0),
|
|
97
|
-
};
|
|
98
|
-
if (event.ctrlKey || event.metaKey) {
|
|
99
|
-
const factor = Math.exp(-event.deltaY / 200);
|
|
100
|
-
state.zoom(focal, state.ctx.viewport.state.zoom * factor);
|
|
101
|
-
return;
|
|
102
|
-
}
|
|
103
|
-
if (wheelMode === 'zoom') {
|
|
104
|
-
const factor = Math.exp(-event.deltaY / 300);
|
|
105
|
-
state.zoom(focal, state.ctx.viewport.state.zoom * factor);
|
|
106
|
-
return;
|
|
107
|
-
}
|
|
108
|
-
if (event.shiftKey) {
|
|
109
|
-
state.pan({ x: event.deltaY, y: event.deltaX });
|
|
110
|
-
return;
|
|
111
|
-
}
|
|
112
|
-
state.pan({ x: event.deltaX, y: event.deltaY });
|
|
113
|
-
}, [state, wheelMode]);
|
|
114
|
-
const handleContextMenu = useCallback((event) => {
|
|
115
|
-
if (allowRightPan)
|
|
116
|
-
event.preventDefault();
|
|
117
|
-
}, [allowRightPan]);
|
|
118
|
-
useEffect(() => {
|
|
119
|
-
const el = containerRef.current;
|
|
120
|
-
if (!el)
|
|
121
|
-
return;
|
|
122
|
-
const stop = (e) => {
|
|
123
|
-
if (e.target === el || el.contains(e.target)) {
|
|
124
|
-
e.preventDefault();
|
|
125
|
-
}
|
|
126
|
-
};
|
|
127
|
-
el.addEventListener('wheel', stop, { passive: false });
|
|
128
|
-
return () => el.removeEventListener('wheel', stop);
|
|
129
|
-
}, []);
|
|
130
|
-
useEffect(() => {
|
|
131
|
-
if (!allowSpacePan)
|
|
132
|
-
return;
|
|
133
|
-
const onKeyDown = (e) => {
|
|
134
|
-
if (e.code === 'Space' && !e.repeat) {
|
|
135
|
-
spaceDownRef.current = true;
|
|
136
|
-
if (containerRef.current)
|
|
137
|
-
containerRef.current.style.cursor = 'grab';
|
|
138
|
-
}
|
|
139
|
-
};
|
|
140
|
-
const onKeyUp = (e) => {
|
|
141
|
-
if (e.code === 'Space') {
|
|
142
|
-
spaceDownRef.current = false;
|
|
143
|
-
if (containerRef.current)
|
|
144
|
-
containerRef.current.style.cursor = '';
|
|
145
|
-
}
|
|
146
|
-
};
|
|
147
|
-
window.addEventListener('keydown', onKeyDown);
|
|
148
|
-
window.addEventListener('keyup', onKeyUp);
|
|
149
|
-
return () => {
|
|
150
|
-
window.removeEventListener('keydown', onKeyDown);
|
|
151
|
-
window.removeEventListener('keyup', onKeyUp);
|
|
152
|
-
};
|
|
153
|
-
}, [allowSpacePan]);
|
|
154
|
-
const containerStyle = {
|
|
155
|
-
position: 'relative',
|
|
156
|
-
width,
|
|
157
|
-
height,
|
|
158
|
-
overflow: 'hidden',
|
|
159
|
-
touchAction: 'none',
|
|
160
|
-
userSelect: 'none',
|
|
161
|
-
cursor: activeTool?.cursor ?? 'default',
|
|
162
|
-
...style,
|
|
163
|
-
};
|
|
164
|
-
const customPreview = activeTool?.renderPreview?.(state.customPreviewState, state.ctx);
|
|
165
|
-
const { renderMeasurementStamp, selection } = props;
|
|
166
|
-
return (_jsxs("div", { ref: containerRef, style: containerStyle, onPointerDown: handlePointerDown, onPointerMove: handlePointerMove, onPointerUp: handlePointerUp, onPointerCancel: handlePointerCancel, onWheel: handleWheel, onContextMenu: handleContextMenu, children: [AnnotationCanvasSkia({
|
|
167
|
-
width,
|
|
168
|
-
height,
|
|
169
|
-
effectiveCanvas: state.effectiveCanvas,
|
|
170
|
-
worldTransform: state.worldTransform,
|
|
171
|
-
measurementsById: state.measurementsById,
|
|
172
|
-
fallbackUnit,
|
|
173
|
-
fractionalTolerance,
|
|
174
|
-
decimalTolerance,
|
|
175
|
-
resolveImageUrl,
|
|
176
|
-
valueFont,
|
|
177
|
-
labelFont,
|
|
178
|
-
hideMeasurementStamps: !!renderMeasurementStamp,
|
|
179
|
-
penDrawingStroke: state.penDrawingStroke,
|
|
180
|
-
customPreview,
|
|
181
|
-
}), renderMeasurementStamp && (_jsx("div", { style: {
|
|
182
|
-
position: 'absolute',
|
|
183
|
-
inset: 0,
|
|
184
|
-
pointerEvents: 'none',
|
|
185
|
-
}, children: state.effectiveCanvas.placedMeasurements.map((placed) => {
|
|
186
|
-
const size = STAMP_TILE_SIZE * (placed.scale ?? 1);
|
|
187
|
-
const cx = (placed.anchor.x - state.viewport.pan.x) * state.viewport.zoom;
|
|
188
|
-
const cy = (placed.anchor.y - state.viewport.pan.y) * state.viewport.zoom;
|
|
189
|
-
const isSelected = selection?.ids.includes(placed.id) ?? false;
|
|
190
|
-
return (_jsxs("div", { style: {
|
|
191
|
-
position: 'absolute',
|
|
192
|
-
left: 0,
|
|
193
|
-
top: 0,
|
|
194
|
-
width: size,
|
|
195
|
-
height: size,
|
|
196
|
-
transform: `translate(${cx - size / 2}px, ${cy - size / 2}px)`,
|
|
197
|
-
}, children: [renderMeasurementStamp({
|
|
198
|
-
placed,
|
|
199
|
-
measurement: state.measurementsById.get(placed.measurementId) ?? null,
|
|
200
|
-
selected: isSelected,
|
|
201
|
-
size,
|
|
202
|
-
zoom: state.viewport.zoom,
|
|
203
|
-
}), isSelected && (_jsx("div", { role: "button", "aria-label": "Remove measurement", onPointerDown: (e) => {
|
|
204
|
-
e.stopPropagation();
|
|
205
|
-
state.ctx.commit({
|
|
206
|
-
ops: [{ op: 'removeMeasurement', id: placed.id }],
|
|
207
|
-
});
|
|
208
|
-
state.ctx.setSelection(null);
|
|
209
|
-
}, style: {
|
|
210
|
-
position: 'absolute',
|
|
211
|
-
top: -10,
|
|
212
|
-
right: -10,
|
|
213
|
-
width: 40,
|
|
214
|
-
height: 40,
|
|
215
|
-
cursor: 'pointer',
|
|
216
|
-
pointerEvents: 'auto',
|
|
217
|
-
} }))] }, placed.id));
|
|
218
|
-
}) }))] }));
|
|
219
|
-
};
|
|
@@ -1,35 +0,0 @@
|
|
|
1
|
-
import { type MutableRefObject } from 'react';
|
|
2
|
-
import { type ViewStyle } from 'react-native';
|
|
3
|
-
import type { RenderMeasurementStamp } from './measurementStampOverlay.js';
|
|
4
|
-
import type { DecimalTolerance, FractionalTolerance, Measurement, Units } from '../types/firestore.js';
|
|
5
|
-
import type { AnnotationCanvasState, AnnotationDocumentPatch, Selection } from '../types/annotation.js';
|
|
6
|
-
import type { MeasurementRef } from './measurementPicker.js';
|
|
7
|
-
import type { Tool } from './Tool.js';
|
|
8
|
-
import { type AnnotationCanvasHandle } from './useAnnotationCanvasState.js';
|
|
9
|
-
import type { ViewportState } from './viewport.js';
|
|
10
|
-
export type { AnnotationCanvasHandle };
|
|
11
|
-
export interface AnnotationCanvasInnerProps {
|
|
12
|
-
canvas: AnnotationCanvasState;
|
|
13
|
-
onCommit(patch: AnnotationDocumentPatch): void;
|
|
14
|
-
tools: Tool[];
|
|
15
|
-
activeToolId: string;
|
|
16
|
-
selection: Selection | null;
|
|
17
|
-
onSelectionChange(selection: Selection | null): void;
|
|
18
|
-
measurements?: Measurement[];
|
|
19
|
-
fallbackUnit?: Units;
|
|
20
|
-
fractionalTolerance?: FractionalTolerance;
|
|
21
|
-
decimalTolerance?: DecimalTolerance;
|
|
22
|
-
resolveImageUrl?: (storagePath: string) => Promise<string>;
|
|
23
|
-
pickMeasurement?: () => Promise<MeasurementRef | null>;
|
|
24
|
-
renderMeasurementStamp?: RenderMeasurementStamp;
|
|
25
|
-
stampFontSource?: unknown;
|
|
26
|
-
stampValueFontSize?: number;
|
|
27
|
-
stampLabelFontSize?: number;
|
|
28
|
-
gestures?: unknown;
|
|
29
|
-
width: number;
|
|
30
|
-
height: number;
|
|
31
|
-
initialViewport?: ViewportState;
|
|
32
|
-
style?: ViewStyle;
|
|
33
|
-
imperativeRef?: MutableRefObject<AnnotationCanvasHandle | null>;
|
|
34
|
-
}
|
|
35
|
-
export declare const AnnotationCanvasInner: (props: AnnotationCanvasInnerProps) => import("react/jsx-runtime").JSX.Element;
|
|
@@ -1,138 +0,0 @@
|
|
|
1
|
-
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
-
import { useFont } from '@shopify/react-native-skia';
|
|
3
|
-
import { useMemo, useRef } from 'react';
|
|
4
|
-
import { StyleSheet, TouchableOpacity, View } from 'react-native';
|
|
5
|
-
import { Gesture, GestureDetector, GestureHandlerRootView, } from 'react-native-gesture-handler';
|
|
6
|
-
import { STAMP_TILE_SIZE } from './stampLayout.js';
|
|
7
|
-
import { AnnotationCanvasSkia } from './AnnotationCanvasSkia.js';
|
|
8
|
-
import { useAnnotationCanvasState, } from './useAnnotationCanvasState.js';
|
|
9
|
-
// Native fingerprint: one finger drives the active tool, two fingers
|
|
10
|
-
// pan/zoom the viewport. Tap counts as a brief pointer down+up so tools
|
|
11
|
-
// like measurement-stamp (which only listen to onPointerUp) work via tap.
|
|
12
|
-
export const AnnotationCanvasInner = (props) => {
|
|
13
|
-
const { fallbackUnit, fractionalTolerance, decimalTolerance, resolveImageUrl, stampFontSource, stampValueFontSize = 14, stampLabelFontSize = 11, width, height, style, } = props;
|
|
14
|
-
const valueFont = useFont(stampFontSource, stampValueFontSize);
|
|
15
|
-
const labelFont = useFont(stampFontSource, stampLabelFontSize);
|
|
16
|
-
const state = useAnnotationCanvasState(props);
|
|
17
|
-
// Per-gesture refs so we always emit a matching down/move/up sequence.
|
|
18
|
-
const pointerIdRef = useRef(1);
|
|
19
|
-
const inFlightRef = useRef(null);
|
|
20
|
-
const pinchStartZoomRef = useRef(1);
|
|
21
|
-
const buildEvent = (pointerId, screen) => ({
|
|
22
|
-
pointerId,
|
|
23
|
-
screen,
|
|
24
|
-
world: state.ctx.viewport.screenToWorld(screen),
|
|
25
|
-
});
|
|
26
|
-
const gesture = useMemo(() => {
|
|
27
|
-
const toolPan = Gesture.Pan()
|
|
28
|
-
.minPointers(1)
|
|
29
|
-
.maxPointers(1)
|
|
30
|
-
.runOnJS(true)
|
|
31
|
-
.onBegin((e) => {
|
|
32
|
-
const id = pointerIdRef.current++;
|
|
33
|
-
const screen = { x: e.x, y: e.y };
|
|
34
|
-
inFlightRef.current = { id, lastScreen: screen };
|
|
35
|
-
state.dispatchPointerDown(buildEvent(id, screen));
|
|
36
|
-
})
|
|
37
|
-
.onUpdate((e) => {
|
|
38
|
-
const f = inFlightRef.current;
|
|
39
|
-
if (!f)
|
|
40
|
-
return;
|
|
41
|
-
const screen = { x: e.x, y: e.y };
|
|
42
|
-
f.lastScreen = screen;
|
|
43
|
-
state.dispatchPointerMove(buildEvent(f.id, screen));
|
|
44
|
-
})
|
|
45
|
-
.onEnd((e) => {
|
|
46
|
-
const f = inFlightRef.current;
|
|
47
|
-
if (!f)
|
|
48
|
-
return;
|
|
49
|
-
state.dispatchPointerUp(buildEvent(f.id, { x: e.x, y: e.y }));
|
|
50
|
-
inFlightRef.current = null;
|
|
51
|
-
})
|
|
52
|
-
.onFinalize(() => {
|
|
53
|
-
if (inFlightRef.current) {
|
|
54
|
-
state.dispatchPointerCancel();
|
|
55
|
-
inFlightRef.current = null;
|
|
56
|
-
}
|
|
57
|
-
});
|
|
58
|
-
const tap = Gesture.Tap()
|
|
59
|
-
.maxDuration(250)
|
|
60
|
-
.runOnJS(true)
|
|
61
|
-
.onEnd((e) => {
|
|
62
|
-
const id = pointerIdRef.current++;
|
|
63
|
-
const screen = { x: e.x, y: e.y };
|
|
64
|
-
// Synthesize a down+up sequence so tools that only listen to
|
|
65
|
-
// onPointerUp (e.g. measurement stamp) still fire.
|
|
66
|
-
state.dispatchPointerDown(buildEvent(id, screen));
|
|
67
|
-
state.dispatchPointerUp(buildEvent(id, screen));
|
|
68
|
-
});
|
|
69
|
-
const viewportPan = Gesture.Pan()
|
|
70
|
-
.minPointers(2)
|
|
71
|
-
.maxPointers(2)
|
|
72
|
-
.runOnJS(true)
|
|
73
|
-
.onChange((e) => {
|
|
74
|
-
state.pan({ x: e.changeX, y: e.changeY });
|
|
75
|
-
});
|
|
76
|
-
const pinch = Gesture.Pinch()
|
|
77
|
-
.runOnJS(true)
|
|
78
|
-
.onBegin(() => {
|
|
79
|
-
pinchStartZoomRef.current = state.ctx.viewport.state.zoom;
|
|
80
|
-
})
|
|
81
|
-
.onUpdate((e) => {
|
|
82
|
-
state.zoom({ x: e.focalX, y: e.focalY }, pinchStartZoomRef.current * e.scale);
|
|
83
|
-
});
|
|
84
|
-
return Gesture.Race(tap, Gesture.Simultaneous(viewportPan, pinch), toolPan);
|
|
85
|
-
}, [state]);
|
|
86
|
-
const activeTool = props.tools.find((t) => t.id === props.activeToolId) ?? null;
|
|
87
|
-
const customPreview = activeTool?.renderPreview?.(state.customPreviewState, state.ctx);
|
|
88
|
-
const { renderMeasurementStamp, selection } = props;
|
|
89
|
-
return (_jsxs(GestureHandlerRootView, { style: [{ width, height }, style], children: [_jsx(GestureDetector, { gesture: gesture, children: _jsx(View, { style: { width, height }, collapsable: false, children: AnnotationCanvasSkia({
|
|
90
|
-
width,
|
|
91
|
-
height,
|
|
92
|
-
effectiveCanvas: state.effectiveCanvas,
|
|
93
|
-
worldTransform: state.worldTransform,
|
|
94
|
-
measurementsById: state.measurementsById,
|
|
95
|
-
fallbackUnit,
|
|
96
|
-
fractionalTolerance,
|
|
97
|
-
decimalTolerance,
|
|
98
|
-
resolveImageUrl,
|
|
99
|
-
valueFont,
|
|
100
|
-
labelFont,
|
|
101
|
-
hideMeasurementStamps: !!renderMeasurementStamp,
|
|
102
|
-
penDrawingStroke: state.penDrawingStroke,
|
|
103
|
-
customPreview,
|
|
104
|
-
}) }) }), renderMeasurementStamp && (_jsx(View, { pointerEvents: "box-none", style: StyleSheet.absoluteFill, children: state.effectiveCanvas.placedMeasurements.map((placed) => {
|
|
105
|
-
const size = STAMP_TILE_SIZE * (placed.scale ?? 1);
|
|
106
|
-
const cx = (placed.anchor.x - state.viewport.pan.x) * state.viewport.zoom;
|
|
107
|
-
const cy = (placed.anchor.y - state.viewport.pan.y) * state.viewport.zoom;
|
|
108
|
-
const isSelected = selection?.ids.includes(placed.id) ?? false;
|
|
109
|
-
return (_jsxs(View, { pointerEvents: "box-none", style: {
|
|
110
|
-
position: 'absolute',
|
|
111
|
-
left: 0,
|
|
112
|
-
top: 0,
|
|
113
|
-
width: size,
|
|
114
|
-
height: size,
|
|
115
|
-
transform: [
|
|
116
|
-
{ translateX: cx - size / 2 },
|
|
117
|
-
{ translateY: cy - size / 2 },
|
|
118
|
-
],
|
|
119
|
-
}, children: [_jsx(View, { pointerEvents: "none", style: StyleSheet.absoluteFill, children: renderMeasurementStamp({
|
|
120
|
-
placed,
|
|
121
|
-
measurement: state.measurementsById.get(placed.measurementId) ?? null,
|
|
122
|
-
selected: isSelected,
|
|
123
|
-
size,
|
|
124
|
-
zoom: state.viewport.zoom,
|
|
125
|
-
}) }), isSelected && (_jsx(TouchableOpacity, { accessibilityRole: "button", accessibilityLabel: "Remove measurement", hitSlop: 10, onPress: () => {
|
|
126
|
-
state.ctx.commit({
|
|
127
|
-
ops: [{ op: 'removeMeasurement', id: placed.id }],
|
|
128
|
-
});
|
|
129
|
-
state.ctx.setSelection(null);
|
|
130
|
-
}, style: {
|
|
131
|
-
position: 'absolute',
|
|
132
|
-
top: -8,
|
|
133
|
-
right: -8,
|
|
134
|
-
width: 36,
|
|
135
|
-
height: 36,
|
|
136
|
-
} }))] }, placed.id));
|
|
137
|
-
}) }))] }));
|
|
138
|
-
};
|