@reekon-tools/boldr-utils 1.6.27 → 1.6.29
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/tools/selectTool.d.ts +4 -1
- package/dist/annotation/canvas/tools/selectTool.js +13 -1
- package/dist/annotation/data/hooks/useAnnotationCanvasDoc.d.ts +1 -0
- package/dist/annotation/data/hooks/useAnnotationCanvasDoc.js +12 -4
- package/dist/exports.d.ts +2 -1
- package/dist/exports.js +2 -1
- package/dist/utils/indexLabels.d.ts +3 -0
- package/dist/utils/indexLabels.js +40 -0
- package/package.json +1 -1
|
@@ -472,7 +472,7 @@ const dragPatch = (s, doc, delta, zoom) => {
|
|
|
472
472
|
const op = translatePatch(s.elementKind, s.id, doc, delta);
|
|
473
473
|
return op ? { ops: [op] } : null;
|
|
474
474
|
};
|
|
475
|
-
export const createSelectTool = () => ({
|
|
475
|
+
export const createSelectTool = (options = {}) => ({
|
|
476
476
|
id: 'select',
|
|
477
477
|
label: 'Select',
|
|
478
478
|
cursor: 'default',
|
|
@@ -582,6 +582,11 @@ export const createSelectTool = () => ({
|
|
|
582
582
|
const hit = findHit(ctx.document, world, zoom, ctx.tileViewportScale);
|
|
583
583
|
if (!hit) {
|
|
584
584
|
ctx.setSelection(null);
|
|
585
|
+
// Nothing under the pointer: with panOnEmptyDrag the gesture pans the
|
|
586
|
+
// viewport instead of dead-ending, so bare drags navigate the canvas.
|
|
587
|
+
if (options.panOnEmptyDrag) {
|
|
588
|
+
return { kind: 'panning', lastScreen: event.screen };
|
|
589
|
+
}
|
|
585
590
|
return { kind: 'idle' };
|
|
586
591
|
}
|
|
587
592
|
// Re-tapping an already-selected text shape (without dragging) opens its
|
|
@@ -610,6 +615,13 @@ export const createSelectTool = () => ({
|
|
|
610
615
|
},
|
|
611
616
|
onPointerMove(event, ctx, state) {
|
|
612
617
|
const s = state;
|
|
618
|
+
if (s?.kind === 'panning') {
|
|
619
|
+
ctx.applyPan({
|
|
620
|
+
x: event.screen.x - s.lastScreen.x,
|
|
621
|
+
y: event.screen.y - s.lastScreen.y,
|
|
622
|
+
});
|
|
623
|
+
return { ...s, lastScreen: event.screen };
|
|
624
|
+
}
|
|
613
625
|
if (s?.kind !== 'dragging')
|
|
614
626
|
return s;
|
|
615
627
|
const delta = {
|
|
@@ -32,7 +32,7 @@ const makeClientId = () => `${Date.now().toString(36)}-${Math.floor(Math.random(
|
|
|
32
32
|
// no `fileId` was supplied. Composes the existing low-level hooks so it stays
|
|
33
33
|
// decoupled from any specific Firebase SDK.
|
|
34
34
|
export const useAnnotationCanvasDoc = (options) => {
|
|
35
|
-
const { scope, fileId, fallbackViewport, debounceMs = 800, createSeed, onFileCreated, onSaveError, captureThumbnail, debugLogging = false, } = options;
|
|
35
|
+
const { scope, fileId, fallbackViewport, debounceMs = 800, autoSave = true, createSeed, onFileCreated, onSaveError, captureThumbnail, debugLogging = false, } = options;
|
|
36
36
|
const { data, loading, error } = useAnnotationDoc(scope, fileId);
|
|
37
37
|
const { create, update, uploadImage, deleteImage } = useAnnotationMutations(scope ?? EMPTY_SCOPE);
|
|
38
38
|
const [working, setWorking] = useState(null);
|
|
@@ -142,8 +142,12 @@ export const useAnnotationCanvasDoc = (options) => {
|
|
|
142
142
|
return;
|
|
143
143
|
}
|
|
144
144
|
// A write of ours is queued or in flight — ignore the snapshot; it is
|
|
145
|
-
// either the echo of our write or about to be superseded by it.
|
|
146
|
-
|
|
145
|
+
// either the echo of our write or about to be superseded by it. 'dirty'
|
|
146
|
+
// matters for the explicit-save mode (autoSave false), where local edits
|
|
147
|
+
// sit unsaved indefinitely and must not be clobbered by a remote write.
|
|
148
|
+
if (statusRef.current === 'saving' ||
|
|
149
|
+
statusRef.current === 'dirty' ||
|
|
150
|
+
timerRef.current !== null)
|
|
147
151
|
return;
|
|
148
152
|
const incoming = data?.fileData.canvas;
|
|
149
153
|
if (!incoming)
|
|
@@ -292,13 +296,17 @@ export const useAnnotationCanvasDoc = (options) => {
|
|
|
292
296
|
return next;
|
|
293
297
|
});
|
|
294
298
|
setStatus('dirty');
|
|
299
|
+
// Explicit-save mode: the edit stays local (status 'dirty') until the
|
|
300
|
+
// caller invokes save() — no debounce, no write.
|
|
301
|
+
if (!autoSave)
|
|
302
|
+
return;
|
|
295
303
|
if (timerRef.current)
|
|
296
304
|
clearTimeout(timerRef.current);
|
|
297
305
|
timerRef.current = setTimeout(() => {
|
|
298
306
|
timerRef.current = null;
|
|
299
307
|
void flush();
|
|
300
308
|
}, debounceMs);
|
|
301
|
-
}, [debounceMs, flush, setStatus]);
|
|
309
|
+
}, [autoSave, debounceMs, flush, setStatus]);
|
|
302
310
|
// Explicit save (Save button, "done" actions): also refreshes the file's
|
|
303
311
|
// thumbnail, which autosaves deliberately skip. Resolves only after the
|
|
304
312
|
// capture+upload too, so a caller may unmount the view right after.
|
package/dist/exports.d.ts
CHANGED
|
@@ -8,6 +8,7 @@ export * from './types/layout.js';
|
|
|
8
8
|
export * from './types/annotation.js';
|
|
9
9
|
export { getToleranceColor, calculateDeviationPercentage, isWithinTolerance, generateToleranceGradient, createDefaultToleranceThresholds, DEFAULT_TOLERANCE_COLORS, type ToleranceThreshold, type ToleranceConfig, } from './utils/tolerance.js';
|
|
10
10
|
export { DEFAULT_GROUP_INDEX, isDefaultGroup, findDefaultGroup, } from './utils/groups.js';
|
|
11
|
+
export { numberToLetterIndex, formGroupInputLabel, listGroupMeasurementLabel, } from './utils/indexLabels.js';
|
|
11
12
|
export { isFieldOp, type AnnotationDataProvider, type AnnotationFile, type AnnotationFileSummary, type FieldOp, type ImageBlob, type JobGroupScope, type JobScope, type Patch, type Unsubscribe, type UploadedImageRef, } from './annotation/data/AnnotationDataProvider.js';
|
|
12
13
|
export { AnnotationDataProviderContext, useAnnotationData, type AnnotationDataProviderProps, } from './annotation/data/AnnotationDataContext.js';
|
|
13
14
|
export { useAnnotationDoc, type UseAnnotationDocResult, } from './annotation/data/hooks/useAnnotationDoc.js';
|
|
@@ -24,7 +25,7 @@ export type { MeasurementStampRenderArgs, RenderMeasurementStamp, } from './anno
|
|
|
24
25
|
export { STAMP_TILE_SIZE, DEFAULT_TILE_SCALE, TILE_SCALE_MIN, TILE_SCALE_MAX, clampTileScale, stampTileSize, isUnassociatedStamp, } from './annotation/canvas/stampLayout.js';
|
|
25
26
|
export { createViewportApi, fitToScreen, fitRectToScreen, imageDocRect, panBy, zoomAt, DEFAULT_VIEWPORT, type ViewportApi, type ViewportState, } from './annotation/canvas/viewport.js';
|
|
26
27
|
export { createPenTool, type PenToolOptions, } from './annotation/canvas/tools/penTool.js';
|
|
27
|
-
export { createSelectTool } from './annotation/canvas/tools/selectTool.js';
|
|
28
|
+
export { createSelectTool, type SelectToolOptions, } from './annotation/canvas/tools/selectTool.js';
|
|
28
29
|
export { createMeasurementStampTool, type MeasurementStampToolOptions, } from './annotation/canvas/tools/measurementStampTool.js';
|
|
29
30
|
export { createPanTool, type PanToolOptions, } from './annotation/canvas/tools/panTool.js';
|
|
30
31
|
export { createTextTool, type TextToolOptions, } from './annotation/canvas/tools/textTool.js';
|
package/dist/exports.js
CHANGED
|
@@ -12,6 +12,7 @@ export * from './types/layout.js';
|
|
|
12
12
|
export * from './types/annotation.js';
|
|
13
13
|
export { getToleranceColor, calculateDeviationPercentage, isWithinTolerance, generateToleranceGradient, createDefaultToleranceThresholds, DEFAULT_TOLERANCE_COLORS, } from './utils/tolerance.js';
|
|
14
14
|
export { DEFAULT_GROUP_INDEX, isDefaultGroup, findDefaultGroup, } from './utils/groups.js';
|
|
15
|
+
export { numberToLetterIndex, formGroupInputLabel, listGroupMeasurementLabel, } from './utils/indexLabels.js';
|
|
15
16
|
// Annotation data layer (SDK-neutral; apps provide their own provider).
|
|
16
17
|
export { isFieldOp, } from './annotation/data/AnnotationDataProvider.js';
|
|
17
18
|
export { AnnotationDataProviderContext, useAnnotationData, } from './annotation/data/AnnotationDataContext.js';
|
|
@@ -24,7 +25,7 @@ export { InMemoryAnnotationProvider } from './annotation/data/InMemoryAnnotation
|
|
|
24
25
|
export { STAMP_TILE_SIZE, DEFAULT_TILE_SCALE, TILE_SCALE_MIN, TILE_SCALE_MAX, clampTileScale, stampTileSize, isUnassociatedStamp, } from './annotation/canvas/stampLayout.js';
|
|
25
26
|
export { createViewportApi, fitToScreen, fitRectToScreen, imageDocRect, panBy, zoomAt, DEFAULT_VIEWPORT, } from './annotation/canvas/viewport.js';
|
|
26
27
|
export { createPenTool, } from './annotation/canvas/tools/penTool.js';
|
|
27
|
-
export { createSelectTool } from './annotation/canvas/tools/selectTool.js';
|
|
28
|
+
export { createSelectTool, } from './annotation/canvas/tools/selectTool.js';
|
|
28
29
|
export { createMeasurementStampTool, } from './annotation/canvas/tools/measurementStampTool.js';
|
|
29
30
|
export { createPanTool, } from './annotation/canvas/tools/panTool.js';
|
|
30
31
|
export { createTextTool, } from './annotation/canvas/tools/textTool.js';
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
export declare const numberToLetterIndex: (index: number) => string;
|
|
2
|
+
export declare const formGroupInputLabel: (columnIndex: number, groupIndex: number) => string;
|
|
3
|
+
export declare const listGroupMeasurementLabel: (deviceMeasurementNumber: number | null | undefined, groupIndex: number) => string;
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
// Canonical measurement / input index labels, shared by rock-desktop and
|
|
2
|
+
// rock-pro-app so every surface (tables, forms, PDF tile overlays, mobile
|
|
3
|
+
// lists) renders the same identifier for the same datum.
|
|
4
|
+
//
|
|
5
|
+
// Two schemes exist:
|
|
6
|
+
//
|
|
7
|
+
// - FORM groups: inputs are identified positionally — the column's index in
|
|
8
|
+
// the section's tableConfig as a letter, plus the group's 1-based number.
|
|
9
|
+
// First column in the second group → "A2"; fifth column in the third
|
|
10
|
+
// group → "E3".
|
|
11
|
+
//
|
|
12
|
+
// - LIST groups: entries are always measurements, identified by their stable
|
|
13
|
+
// deviceMeasurementNumber (capture sequence) as the letter, plus the same
|
|
14
|
+
// group number. deviceMeasurementNumber 4 in the second group → "E2".
|
|
15
|
+
// Legacy measurements without a deviceMeasurementNumber fall back to the
|
|
16
|
+
// bare group number ("2") — positional letters would shift as the list
|
|
17
|
+
// re-sorts, so no letter is shown.
|
|
18
|
+
// 0-based index → spreadsheet-style letters: 0 → "A", 25 → "Z", 26 → "AA".
|
|
19
|
+
// Negative / non-finite input yields ''.
|
|
20
|
+
export const numberToLetterIndex = (index) => {
|
|
21
|
+
if (!Number.isFinite(index) || index < 0)
|
|
22
|
+
return '';
|
|
23
|
+
let n = Math.floor(index) + 1;
|
|
24
|
+
let result = '';
|
|
25
|
+
while (n > 0) {
|
|
26
|
+
n--;
|
|
27
|
+
result = String.fromCharCode((n % 26) + 65) + result;
|
|
28
|
+
n = Math.floor(n / 26);
|
|
29
|
+
}
|
|
30
|
+
return result;
|
|
31
|
+
};
|
|
32
|
+
// Label for a FORM-group input: column position (0-based) + group number.
|
|
33
|
+
// formGroupInputLabel(0, 1) → "A2"
|
|
34
|
+
export const formGroupInputLabel = (columnIndex, groupIndex) => `${numberToLetterIndex(columnIndex)}${groupIndex + 1}`;
|
|
35
|
+
// Label for a LIST-group measurement: deviceMeasurementNumber (0-based) +
|
|
36
|
+
// group number, or the bare group number when the measurement has no device
|
|
37
|
+
// number. listGroupMeasurementLabel(4, 1) → "E2"; (null, 1) → "2".
|
|
38
|
+
export const listGroupMeasurementLabel = (deviceMeasurementNumber, groupIndex) => deviceMeasurementNumber != null && deviceMeasurementNumber >= 0
|
|
39
|
+
? `${numberToLetterIndex(deviceMeasurementNumber)}${groupIndex + 1}`
|
|
40
|
+
: `${groupIndex + 1}`;
|