@weasel-js/labkit 1.4.0 → 1.4.1

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.
Files changed (77) hide show
  1. package/dist/_dts/{CanvasStackContext-D6M0o8cQ.d.ts → CanvasStackContext-BIRUVVGH.d.ts} +1 -1
  2. package/dist/_dts/PrefsForm.d-CgxUequc.d.ts +20 -0
  3. package/dist/_dts/{frac-QS7XzvpG.d.ts → frac-X7mWgd7y.d.ts} +9 -3
  4. package/dist/_dts/{index-B2X21G1c.d.ts → index-FEa1MudK.d.ts} +1 -1
  5. package/dist/_dts/{types-C1Zw7s5P.d.ts → types-lg4TSCb2.d.ts} +2 -1
  6. package/dist/_dts/weasel-canvas-FJTFi3ZZ.d.ts +5041 -0
  7. package/dist/canvas/index.d.ts +15 -15
  8. package/dist/canvas/index.js +2 -2
  9. package/dist/chrome/index.d.ts +5 -5
  10. package/dist/chrome/index.js +5 -5
  11. package/dist/{chunk-W3KSUXXR.js → chunk-53AZ2O2N.js} +4 -4
  12. package/dist/{chunk-W3KSUXXR.js.map → chunk-53AZ2O2N.js.map} +1 -1
  13. package/dist/{chunk-4AMN4MMB.js → chunk-7TRFMIWX.js} +53 -39
  14. package/dist/chunk-7TRFMIWX.js.map +1 -0
  15. package/dist/{chunk-UD7TEHZT.js → chunk-BMQWLKOL.js} +6 -6
  16. package/dist/{chunk-UD7TEHZT.js.map → chunk-BMQWLKOL.js.map} +1 -1
  17. package/dist/{chunk-YQGQUBKA.js → chunk-D7QIPGEB.js} +3 -3
  18. package/dist/{chunk-YQGQUBKA.js.map → chunk-D7QIPGEB.js.map} +1 -1
  19. package/dist/{chunk-7VW3S44V.js → chunk-DG2IFMLA.js} +802 -222
  20. package/dist/chunk-DG2IFMLA.js.map +1 -0
  21. package/dist/{chunk-LN6JDUGB.js → chunk-HXXTULDN.js} +2 -2
  22. package/dist/{chunk-LN6JDUGB.js.map → chunk-HXXTULDN.js.map} +1 -1
  23. package/dist/{chunk-L5WBGUVR.js → chunk-J6SBFZYT.js} +2131 -2174
  24. package/dist/chunk-J6SBFZYT.js.map +1 -0
  25. package/dist/{chunk-XCBCZEBO.js → chunk-MIT3ISW4.js} +37 -31
  26. package/dist/chunk-MIT3ISW4.js.map +1 -0
  27. package/dist/{chunk-QR5V3AFG.js → chunk-MSR7EVS6.js} +5 -5
  28. package/dist/{chunk-QR5V3AFG.js.map → chunk-MSR7EVS6.js.map} +1 -1
  29. package/dist/{chunk-B5VWJBWM.js → chunk-OFCBEZMU.js} +3 -3
  30. package/dist/{chunk-B5VWJBWM.js.map → chunk-OFCBEZMU.js.map} +1 -1
  31. package/dist/{chunk-VHFQKAUL.js → chunk-PP7ZWAMV.js} +83 -75
  32. package/dist/chunk-PP7ZWAMV.js.map +1 -0
  33. package/dist/{chunk-Y2HK44TG.js → chunk-RLOXQZW3.js} +3 -3
  34. package/dist/{chunk-Y2HK44TG.js.map → chunk-RLOXQZW3.js.map} +1 -1
  35. package/dist/controls/index.d.ts +4 -3
  36. package/dist/controls/index.js +3 -3
  37. package/dist/dragdrop/index.d.ts +4 -4
  38. package/dist/dragdrop/index.js +2 -2
  39. package/dist/index.d.ts +8 -71
  40. package/dist/index.js +21 -21
  41. package/dist/index.js.map +1 -1
  42. package/dist/job/index.js +1 -1
  43. package/dist/layers/index.d.ts +5 -5
  44. package/dist/layers/index.js +3 -3
  45. package/dist/loupe/index.d.ts +6 -12
  46. package/dist/loupe/index.js +2 -2
  47. package/dist/passthrough/weasel-canvas.d.ts +3 -343
  48. package/dist/passthrough/weasel-canvas.js +1 -1
  49. package/dist/passthrough/weasel-ui.d.ts +40 -3086
  50. package/dist/passthrough/weasel-ui.js +2 -2
  51. package/dist/primitives/index.js +4 -4
  52. package/dist/styles.css +10 -0
  53. package/dist/surface/index.js +2 -2
  54. package/dist/ui/layers/index.js +2 -2
  55. package/dist/undo/index.d.ts +5 -4
  56. package/package.json +8 -8
  57. package/src/canvas/CanvasStack.tsx +8 -13
  58. package/src/canvas/useOrbit.test.ts +93 -1
  59. package/src/canvas/useOrbit.ts +41 -34
  60. package/src/canvas/usePanZoom.test.ts +102 -1
  61. package/src/canvas/usePanZoom.ts +54 -36
  62. package/src/job/useJob.ts +1 -1
  63. package/src/lab/Workspace.tsx +1 -1
  64. package/src/layers/LayerList.test.tsx +72 -1
  65. package/src/layers/LayerList.tsx +40 -32
  66. package/src/primitives/FloatingPanel.test.tsx +82 -0
  67. package/src/primitives/FloatingPanel.tsx +59 -41
  68. package/dist/_dts/DrawCommand-B3bskUsC.d.ts +0 -564
  69. package/dist/_dts/PrefsForm-BkUJZx0A.d.ts +0 -204
  70. package/dist/_dts/fitViewToBounds-dZ2UDB6e.d.ts +0 -21
  71. package/dist/_dts/shapeKinds-Cx_rxwsa.d.ts +0 -87
  72. package/dist/_dts/types-C-gh9Ap-.d.ts +0 -695
  73. package/dist/chunk-4AMN4MMB.js.map +0 -1
  74. package/dist/chunk-7VW3S44V.js.map +0 -1
  75. package/dist/chunk-L5WBGUVR.js.map +0 -1
  76. package/dist/chunk-VHFQKAUL.js.map +0 -1
  77. package/dist/chunk-XCBCZEBO.js.map +0 -1
@@ -1,3 +1,4 @@
1
+ import { openPointerSession, type PointerSession } from '@weasel-js/core';
1
2
  import type React from 'react';
2
3
  import { type CSSProperties, type ReactNode, useEffect, useMemo, useRef, useState } from 'react';
3
4
  import {
@@ -130,67 +131,84 @@ export function FloatingPanel({
130
131
  .placements.get(ITEM_ID);
131
132
 
132
133
  const dragging = useRef<{ x: number; y: number } | null>(null);
133
- /** Pressed but not yet moved far enough to be a drag. */
134
- const pending = useRef<{ x: number; y: number; pointerId: number } | null>(null);
134
+ const session = useRef<PointerSession | null>(null);
135
135
  const [isDragging, setDragging] = useState(false);
136
+ // A drag outlives the render that started it, so the reducer's inputs are
137
+ // read here and not from that render's closure — a container resized
138
+ // mid-drag would otherwise place the panel against its old box.
139
+ const layout = useRef({ container, options, item });
140
+ layout.current = { container, options, item };
141
+
142
+ useEffect(() => () => session.current?.cancel(), []);
136
143
 
137
144
  const onPointerDown = (e: React.PointerEvent<HTMLDivElement>) => {
138
145
  if (isInteractive(e.target)) return;
139
146
  // The canvas stack underneath owns pan/zoom on the same pointer events.
140
147
  e.stopPropagation();
148
+ const origin = e.currentTarget;
149
+ const pointerId = e.pointerId;
150
+ const start = { x: e.clientX, y: e.clientY };
151
+
152
+ const end = () => {
153
+ session.current = null;
154
+ if (!dragging.current) return;
155
+ dragging.current = null;
156
+ setDragging(false);
157
+ try {
158
+ origin.releasePointerCapture?.(pointerId);
159
+ } catch {
160
+ // Capture is already gone whenever the session ended by losing it.
161
+ }
162
+ };
163
+
141
164
  // Armed, not capturing. Capture retargets mouseup to this element, and the
142
165
  // browser then synthesizes no `click` on the child under the cursor — so
143
166
  // capturing on press makes every non-native control in the panel dead to a
144
167
  // real mouse, silently. `isInteractive` only exempts the names it lists; a
145
168
  // `role="button"` span, a react-aria control or a canvas is not among them.
146
- pending.current = { x: e.clientX, y: e.clientY, pointerId: e.pointerId };
147
- };
148
-
149
- const onPointerMove = (e: React.PointerEvent<HTMLDivElement>) => {
150
- const armed = pending.current;
151
- if (armed && armed.pointerId === e.pointerId && !dragging.current) {
152
- if (Math.hypot(e.clientX - armed.x, e.clientY - armed.y) < DRAG_THRESHOLD) return;
153
- dragging.current = { x: armed.x, y: armed.y };
154
- setDragging(true);
155
- e.currentTarget.setPointerCapture?.(e.pointerId);
156
- }
157
- const from = dragging.current;
158
- if (!from) return;
159
- const dx = e.clientX - from.x;
160
- const dy = e.clientY - from.y;
161
- if (dx === 0 && dy === 0) return;
162
- dragging.current = { x: e.clientX, y: e.clientY };
163
- setPlace(
164
- (prev) =>
165
- strategy.reduce?.(
166
- { at: { [ITEM_ID]: prev }, inner: undefined },
167
- {
168
- affordanceId: `${FLOATING_DRAG_PREFIX}${ITEM_ID}`,
169
- kind: 'drag',
170
- payload: { dx, dy },
171
- },
172
- { container, options, items: [item] },
173
- ).at[ITEM_ID] ?? prev,
169
+ session.current = openPointerSession(
170
+ origin,
171
+ e,
172
+ {
173
+ onMove: (ev) => {
174
+ if (!dragging.current) {
175
+ if (Math.hypot(ev.clientX - start.x, ev.clientY - start.y) < DRAG_THRESHOLD) return;
176
+ dragging.current = { ...start };
177
+ setDragging(true);
178
+ origin.setPointerCapture?.(pointerId);
179
+ }
180
+ const from = dragging.current;
181
+ const dx = ev.clientX - from.x;
182
+ const dy = ev.clientY - from.y;
183
+ if (dx === 0 && dy === 0) return;
184
+ dragging.current = { x: ev.clientX, y: ev.clientY };
185
+ const { container: box, options: opts, item: only } = layout.current;
186
+ setPlace(
187
+ (prev) =>
188
+ strategy.reduce?.(
189
+ { at: { [ITEM_ID]: prev }, inner: undefined },
190
+ {
191
+ affordanceId: `${FLOATING_DRAG_PREFIX}${ITEM_ID}`,
192
+ kind: 'drag',
193
+ payload: { dx, dy },
194
+ },
195
+ { container: box, options: opts, items: [only] },
196
+ ).at[ITEM_ID] ?? prev,
197
+ );
198
+ },
199
+ onEnd: end,
200
+ onCancel: end,
201
+ },
202
+ { capture: false },
174
203
  );
175
204
  };
176
205
 
177
- const endDrag = (e: React.PointerEvent<HTMLDivElement>) => {
178
- pending.current = null;
179
- if (!dragging.current) return;
180
- dragging.current = null;
181
- setDragging(false);
182
- e.currentTarget.releasePointerCapture?.(e.pointerId);
183
- };
184
-
185
206
  return (
186
207
  <div
187
208
  className={className ? `lk-floating-panel ${className}` : 'lk-floating-panel'}
188
209
  data-dragging={isDragging ? 'true' : undefined}
189
210
  data-placed={rect ? 'true' : undefined}
190
- onPointerCancel={endDrag}
191
211
  onPointerDown={onPointerDown}
192
- onPointerMove={onPointerMove}
193
- onPointerUp={endDrag}
194
212
  ref={ref}
195
213
  style={
196
214
  {