@weasel-js/core 1.12.0 → 1.13.0-rc.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/CHANGELOG.md +91 -0
- package/dist/animation/poseTarget.js +46 -15
- package/dist/animation/poseTarget.js.map +1 -1
- package/dist/animation/poseTransition.js +3 -3
- package/dist/animation/poseTransition.js.map +1 -1
- package/dist/animation/reflow.js +3 -6
- package/dist/animation/reflow.js.map +1 -1
- package/dist/animation/rig/bindRig.js +2 -2
- package/dist/animation/rig/bindRig.js.map +1 -1
- package/dist/animation/rig/skinBinding.js +2 -2
- package/dist/animation/rig/skinBinding.js.map +1 -1
- package/dist/barrel/canvas.js +5 -3
- package/dist/barrel/chrome.js +3 -1
- package/dist/barrel/paths.js +3 -2
- package/dist/barrel/routing.js +2 -2
- package/dist/barrel/scene.js +6 -1
- package/dist/canvas/NodeShape.js +1 -0
- package/dist/canvas/NodeShape.js.map +1 -1
- package/dist/canvas/SceneCanvas/SceneCanvasInput.js +2 -0
- package/dist/canvas/SceneCanvas/SceneCanvasInput.js.map +1 -1
- package/dist/canvas/SceneCanvas/canvasPassThrough.js +1 -1
- package/dist/canvas/SceneCanvas/canvasPassThrough.js.map +1 -1
- package/dist/canvas/SceneCanvas/defaultNodeRouting.js +2 -2
- package/dist/canvas/SceneCanvas/defaultNodeRouting.js.map +1 -1
- package/dist/canvas/SceneCanvas/scenePointQuery.js +20 -2
- package/dist/canvas/SceneCanvas/scenePointQuery.js.map +1 -1
- package/dist/canvas/SceneCanvas/useCanvasTools.js +3 -0
- package/dist/canvas/SceneCanvas/useCanvasTools.js.map +1 -1
- package/dist/canvas/SceneCanvas/usePreviewGhostLayer.js +25 -19
- package/dist/canvas/SceneCanvas/usePreviewGhostLayer.js.map +1 -1
- package/dist/canvas/SceneCanvas/useSceneCanvasLayers.js +7 -0
- package/dist/canvas/SceneCanvas/useSceneCanvasLayers.js.map +1 -1
- package/dist/canvas/SceneCanvas/useSceneCanvasTools.js +1 -0
- package/dist/canvas/SceneCanvas/useSceneCanvasTools.js.map +1 -1
- package/dist/canvas/SceneCanvas/useScenePicking.js +31 -6
- package/dist/canvas/SceneCanvas/useScenePicking.js.map +1 -1
- package/dist/canvas/SceneCanvas/useSceneSelectTool.js +4 -5
- package/dist/canvas/SceneCanvas/useSceneSelectTool.js.map +1 -1
- package/dist/canvas/SceneCanvas/useSceneStandardActions.js +5 -3
- package/dist/canvas/SceneCanvas/useSceneStandardActions.js.map +1 -1
- package/dist/canvas/SceneCanvas.js +19 -2
- package/dist/canvas/SceneCanvas.js.map +1 -1
- package/dist/canvas/accentColor.js +26 -3
- package/dist/canvas/accentColor.js.map +1 -1
- package/dist/canvas/buildSceneTree.js +14 -20
- package/dist/canvas/buildSceneTree.js.map +1 -1
- package/dist/canvas/copiesAbove.js +1 -1
- package/dist/canvas/copiesAbove.js.map +1 -1
- package/dist/canvas/defaultDrawOne.js +10 -7
- package/dist/canvas/defaultDrawOne.js.map +1 -1
- package/dist/canvas/deps/areaSelect.js +5 -2
- package/dist/canvas/deps/areaSelect.js.map +1 -1
- package/dist/canvas/deps/booleans.js +1 -1
- package/dist/canvas/deps/booleans.js.map +1 -1
- package/dist/canvas/deps/enteredGroup.js +81 -0
- package/dist/canvas/deps/enteredGroup.js.map +1 -0
- package/dist/canvas/deps/hitTestArea.js +27 -15
- package/dist/canvas/deps/hitTestArea.js.map +1 -1
- package/dist/canvas/deps/insert.js +4 -36
- package/dist/canvas/deps/insert.js.map +1 -1
- package/dist/canvas/deps/insertSite.js +39 -0
- package/dist/canvas/deps/insertSite.js.map +1 -0
- package/dist/canvas/deps/lassoSelect.js +6 -3
- package/dist/canvas/deps/lassoSelect.js.map +1 -1
- package/dist/canvas/deps/pathLeaf.js +1 -1
- package/dist/canvas/deps/pathLeaf.js.map +1 -1
- package/dist/canvas/deps/slice.js +2 -1
- package/dist/canvas/deps/slice.js.map +1 -1
- package/dist/canvas/derivedPath.js +14 -6
- package/dist/canvas/derivedPath.js.map +1 -1
- package/dist/canvas/doubleClickTarget.js +9 -11
- package/dist/canvas/doubleClickTarget.js.map +1 -1
- package/dist/canvas/drawnBounds.js +16 -7
- package/dist/canvas/drawnBounds.js.map +1 -1
- package/dist/canvas/inheritedPaints.js +22 -11
- package/dist/canvas/inheritedPaints.js.map +1 -1
- package/dist/canvas/nodeShapes/nodePaint.js +1 -7
- package/dist/canvas/nodeShapes/nodePaint.js.map +1 -1
- package/dist/canvas/nodeShapes/pathPainter.js +2 -3
- package/dist/canvas/nodeShapes/pathPainter.js.map +1 -1
- package/dist/canvas/nodeShapes/rectFallbackPainter.js +1 -1
- package/dist/canvas/nodeShapes/rectFallbackPainter.js.map +1 -1
- package/dist/canvas/nodeShapes/shapePainter.js +2 -3
- package/dist/canvas/nodeShapes/shapePainter.js.map +1 -1
- package/dist/canvas/ownClip.js +24 -0
- package/dist/canvas/ownClip.js.map +1 -0
- package/dist/canvas/paintCull.js.map +1 -1
- package/dist/canvas/pickIndex/nodeBroadBox.js +86 -0
- package/dist/canvas/pickIndex/nodeBroadBox.js.map +1 -0
- package/dist/canvas/pickIndex/scenePickIndex.js +208 -0
- package/dist/canvas/pickIndex/scenePickIndex.js.map +1 -0
- package/dist/canvas/pickWalk.js +14 -90
- package/dist/canvas/pickWalk.js.map +1 -1
- package/dist/canvas/poseCascade.js +1 -1
- package/dist/canvas/poseCascade.js.map +1 -1
- package/dist/canvas/publishPreviews.js +97 -0
- package/dist/canvas/publishPreviews.js.map +1 -0
- package/dist/canvas/sceneAdapter.js +7 -5
- package/dist/canvas/sceneAdapter.js.map +1 -1
- package/dist/canvas/sceneClipboard.js +17 -10
- package/dist/canvas/sceneClipboard.js.map +1 -1
- package/dist/canvas/scenePickReads.js +37 -0
- package/dist/canvas/scenePickReads.js.map +1 -0
- package/dist/canvas/scenePickSource.js +74 -0
- package/dist/canvas/scenePickSource.js.map +1 -0
- package/dist/canvas/sceneSlotWiring.js +9 -4
- package/dist/canvas/sceneSlotWiring.js.map +1 -1
- package/dist/canvas/sceneViewRender.js +9 -5
- package/dist/canvas/sceneViewRender.js.map +1 -1
- package/dist/canvas/shownNodes.js +15 -0
- package/dist/canvas/shownNodes.js.map +1 -0
- package/dist/canvas/useSceneAdapter.js +5 -3
- package/dist/canvas/useSceneAdapter.js.map +1 -1
- package/dist/clipboard.d.ts +2 -2
- package/dist/{clone-C3e0OTuF.d.ts → clone-qb2e1F5p.d.ts} +2 -2
- package/dist/clone.d.ts +3 -3
- package/dist/core/isPlainObject.js +2 -2
- package/dist/core/layers/layerGroup.js.map +1 -1
- package/dist/core/scene/NodeRouting.js.map +1 -1
- package/dist/core/scene/arrivalWindow.js +1 -1
- package/dist/core/scene/arrivalWindow.js.map +1 -1
- package/dist/core/scene/clipNothing.js +2 -5
- package/dist/core/scene/clipNothing.js.map +1 -1
- package/dist/core/scene/declaredFieldSetters.js +44 -0
- package/dist/core/scene/declaredFieldSetters.js.map +1 -0
- package/dist/core/scene/depMemo.js +3 -0
- package/dist/core/scene/depMemo.js.map +1 -1
- package/dist/core/scene/dependents.js +3 -1
- package/dist/core/scene/dependents.js.map +1 -1
- package/dist/core/scene/documentOf.js +43 -0
- package/dist/core/scene/documentOf.js.map +1 -0
- package/dist/core/scene/effectivePose.js +37 -13
- package/dist/core/scene/effectivePose.js.map +1 -1
- package/dist/core/scene/fieldEdits.js +17 -14
- package/dist/core/scene/fieldEdits.js.map +1 -1
- package/dist/core/scene/historyForm.js +4 -4
- package/dist/core/scene/historyForm.js.map +1 -1
- package/dist/core/scene/inheritedPaint.js +27 -5
- package/dist/core/scene/inheritedPaint.js.map +1 -1
- package/dist/core/scene/kitOps.js +3 -23
- package/dist/core/scene/kitOps.js.map +1 -1
- package/dist/core/scene/layoutPass.js +1 -2
- package/dist/core/scene/layoutPass.js.map +1 -1
- package/dist/core/scene/nodeAsShown.js +72 -0
- package/dist/core/scene/nodeAsShown.js.map +1 -0
- package/dist/core/scene/nodeFnFields.js +2 -10
- package/dist/core/scene/nodeFnFields.js.map +1 -1
- package/dist/core/scene/nodeMemo.js +6 -1
- package/dist/core/scene/nodeMemo.js.map +1 -1
- package/dist/core/scene/nodeStore.js +2 -4
- package/dist/core/scene/nodeStore.js.map +1 -1
- package/dist/core/scene/pathEffects/derive.js +3 -2
- package/dist/core/scene/pathEffects/derive.js.map +1 -1
- package/dist/core/scene/pathEffects/foundHidden.js +35 -0
- package/dist/core/scene/pathEffects/foundHidden.js.map +1 -0
- package/dist/core/scene/plyStacks.js +89 -0
- package/dist/core/scene/plyStacks.js.map +1 -0
- package/dist/core/scene/poseHold.js +42 -24
- package/dist/core/scene/poseHold.js.map +1 -1
- package/dist/core/scene/poseOverrides.js +35 -38
- package/dist/core/scene/poseOverrides.js.map +1 -1
- package/dist/core/scene/posePlies.js +59 -0
- package/dist/core/scene/posePlies.js.map +1 -0
- package/dist/core/scene/reconcileSpecs.js +6 -15
- package/dist/core/scene/reconcileSpecs.js.map +1 -1
- package/dist/core/scene/renderOrder.js +10 -4
- package/dist/core/scene/renderOrder.js.map +1 -1
- package/dist/core/scene/sameValue.js +17 -0
- package/dist/core/scene/sameValue.js.map +1 -0
- package/dist/core/scene/scene.js +3 -3
- package/dist/core/scene/scene.js.map +1 -1
- package/dist/core/scene/sceneEdits.js +8 -0
- package/dist/core/scene/sceneEdits.js.map +1 -1
- package/dist/core/scene/sceneState.js +1 -2
- package/dist/core/scene/sceneState.js.map +1 -1
- package/dist/core/scene/serialization.js +45 -9
- package/dist/core/scene/serialization.js.map +1 -1
- package/dist/core/scene/staleReplay.js +5 -2
- package/dist/core/scene/staleReplay.js.map +1 -1
- package/dist/core/scene/statedFields.js +4 -2
- package/dist/core/scene/statedFields.js.map +1 -1
- package/dist/core/scene/treeEdits.js +2 -4
- package/dist/core/scene/treeEdits.js.map +1 -1
- package/dist/core/scene/treeOrder.js +3 -1
- package/dist/core/scene/treeOrder.js.map +1 -1
- package/dist/core/scene/treeReads.js +1 -2
- package/dist/core/scene/treeReads.js.map +1 -1
- package/dist/core/scene/treeShape.js +13 -0
- package/dist/core/scene/treeShape.js.map +1 -0
- package/dist/core/scene/types.js.map +1 -1
- package/dist/core/scene/useScene.js +1 -1
- package/dist/core/scene/useScene.js.map +1 -1
- package/dist/{cornerHandles-p8fDIAKt.d.ts → cornerHandles-CNCb7aTY.d.ts} +1 -1
- package/dist/{depSchema-mG19aEdD.d.ts → depSchema-B1RS-IJx.d.ts} +27 -54
- package/dist/devtools/handlers.js +1 -1
- package/dist/devtools/handlers.js.map +1 -1
- package/dist/devtools/protocol.js.map +1 -1
- package/dist/devtools-protocol.d.ts +4 -3
- package/dist/devtools.d.ts +3 -2
- package/dist/features/measure/measureHit.js +5 -3
- package/dist/features/measure/measureHit.js.map +1 -1
- package/dist/features/measure/measurement.js +1 -1
- package/dist/features/measure/measurement.js.map +1 -1
- package/dist/features/modes/modeLayers.js +14 -7
- package/dist/features/modes/modeLayers.js.map +1 -1
- package/dist/features/modes/modeTriggers.js +3 -3
- package/dist/features/modes/modeTriggers.js.map +1 -1
- package/dist/features/pathEffects/effectSources.js +25 -16
- package/dist/features/pathEffects/effectSources.js.map +1 -1
- package/dist/features/pathEffects/readSource.js +1 -1
- package/dist/features/pathEffects/readSource.js.map +1 -1
- package/dist/features/pathEffects/useEffectSourcesLayer.js +15 -19
- package/dist/features/pathEffects/useEffectSourcesLayer.js.map +1 -1
- package/dist/features/paths/editablePath.js +1 -1
- package/dist/features/paths/editablePath.js.map +1 -1
- package/dist/features/paths/pathSnap/sceneIndex.js +1 -1
- package/dist/features/paths/pathSnap/sceneIndex.js.map +1 -1
- package/dist/features/selection/enteredGroupFrame.js +65 -0
- package/dist/features/selection/enteredGroupFrame.js.map +1 -0
- package/dist/features/selection/enteredGroupScope.js +44 -0
- package/dist/features/selection/enteredGroupScope.js.map +1 -0
- package/dist/features/text/sceneTextDrafts.js +26 -27
- package/dist/features/text/sceneTextDrafts.js.map +1 -1
- package/dist/features/text/textHitAt.js +4 -2
- package/dist/features/text/textHitAt.js.map +1 -1
- package/dist/features/text/useSceneTextEdit.js +3 -3
- package/dist/features/text/useSceneTextEdit.js.map +1 -1
- package/dist/index.d.ts +225 -73
- package/dist/index.js +15 -5
- package/dist/interactions/actions/admitsChild.js +11 -0
- package/dist/interactions/actions/admitsChild.js.map +1 -0
- package/dist/interactions/actions/defaults/contextMenu.js +16 -3
- package/dist/interactions/actions/defaults/contextMenu.js.map +1 -1
- package/dist/interactions/actions/defaults/createPaintAction.js +4 -1
- package/dist/interactions/actions/defaults/createPaintAction.js.map +1 -1
- package/dist/interactions/actions/defaults/enterGroup.js +88 -0
- package/dist/interactions/actions/defaults/enterGroup.js.map +1 -0
- package/dist/interactions/actions/defaults/enterPathEdit.js +1 -1
- package/dist/interactions/actions/defaults/enterPathEdit.js.map +1 -1
- package/dist/interactions/actions/defaults/enterTextEdit.js +1 -1
- package/dist/interactions/actions/defaults/enterTextEdit.js.map +1 -1
- package/dist/interactions/actions/defaults/group.js +7 -7
- package/dist/interactions/actions/defaults/group.js.map +1 -1
- package/dist/interactions/actions/defaults/selectAll.js +7 -3
- package/dist/interactions/actions/defaults/selectAll.js.map +1 -1
- package/dist/interactions/actions/defaults/selectDeep.js +35 -0
- package/dist/interactions/actions/defaults/selectDeep.js.map +1 -0
- package/dist/interactions/actions/drawnBoxes.js +2 -1
- package/dist/interactions/actions/drawnBoxes.js.map +1 -1
- package/dist/interactions/actions/insert/action.js +1 -0
- package/dist/interactions/actions/insert/action.js.map +1 -1
- package/dist/interactions/actions/insert/parent.js +7 -5
- package/dist/interactions/actions/insert/parent.js.map +1 -1
- package/dist/interactions/actions/insert/placeAction.js +3 -2
- package/dist/interactions/actions/insert/placeAction.js.map +1 -1
- package/dist/interactions/actions/insert/session.js +2 -2
- package/dist/interactions/actions/insert/session.js.map +1 -1
- package/dist/interactions/actions/move/commit/reparent.js +1 -1
- package/dist/interactions/actions/move/commit/reparent.js.map +1 -1
- package/dist/interactions/actions/move/gestureAdapter.js +6 -7
- package/dist/interactions/actions/move/gestureAdapter.js.map +1 -1
- package/dist/interactions/actions/move/layout/exits.js +1 -1
- package/dist/interactions/actions/move/layout/exits.js.map +1 -1
- package/dist/interactions/actions/move/session.js +4 -4
- package/dist/interactions/actions/move/session.js.map +1 -1
- package/dist/interactions/actions/pathCommands/pathNodes.js +14 -5
- package/dist/interactions/actions/pathCommands/pathNodes.js.map +1 -1
- package/dist/interactions/gestures/thresholdDrag/thresholdDrag.js +14 -2
- package/dist/interactions/gestures/thresholdDrag/thresholdDrag.js.map +1 -1
- package/dist/{math-B56zxVs5.d.ts → math-C_elShP_.d.ts} +2 -5
- package/dist/math.d.ts +1 -1
- package/dist/move.d.ts +4 -4
- package/dist/{options-DBxEryYq.d.ts → options-ClEm3SEl.d.ts} +1 -1
- package/dist/resize.d.ts +4 -4
- package/dist/test-seams.d.ts +6 -6
- package/dist/tools/builtin/pressTarget.js +22 -0
- package/dist/tools/builtin/pressTarget.js.map +1 -0
- package/dist/tools/builtin/select/adapterPointQuery.js +3 -3
- package/dist/tools/builtin/select/adapterPointQuery.js.map +1 -1
- package/dist/tools/builtin/select/collapseDeferred.js +40 -0
- package/dist/tools/builtin/select/collapseDeferred.js.map +1 -0
- package/dist/tools/builtin/select/groupEntry.js +82 -0
- package/dist/tools/builtin/select/groupEntry.js.map +1 -0
- package/dist/tools/builtin/select/pressDecision.js +1 -1
- package/dist/tools/builtin/select/pressDecision.js.map +1 -1
- package/dist/tools/builtin/select/useSelectTool.js +61 -45
- package/dist/tools/builtin/select/useSelectTool.js.map +1 -1
- package/dist/{types-7puAuVD2.d.ts → types-49uYJN3j.d.ts} +1 -1
- package/dist/{types-CniVctmk.d.ts → types-BEdRIhNc.d.ts} +272 -109
- package/dist/{types-oFeQ5VjV.d.ts → types-p2alu-R1.d.ts} +1 -1
- package/dist/version.js +1 -1
- package/package.json +25 -25
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { admitsChild } from "../admitsChild.js";
|
|
1
2
|
import { nodeDescriptors, poseDescriptorOf } from "../poseDescriptorDep.js";
|
|
2
3
|
import { transformFinisher } from "../transformFinish.js";
|
|
3
4
|
import { commitGestureOps, readGestureLifecycle } from "../gestureLifecycle.js";
|
|
@@ -11,10 +12,9 @@ import { lazySnapSession } from "../../../features/snap/lazySession.js";
|
|
|
11
12
|
import { DRAG_THRESHOLD_PX, isPathEditPart, resolveParams } from "@weasel-js/routing";
|
|
12
13
|
|
|
13
14
|
//#region src/interactions/actions/move/session.ts
|
|
14
|
-
function admitsOf(canDropInto, ids) {
|
|
15
|
-
if (canDropInto === void 0) return () => true;
|
|
15
|
+
function admitsOf(canDropInto, ids, scene) {
|
|
16
16
|
const roots = ids;
|
|
17
|
-
return (parent) =>
|
|
17
|
+
return (parent) => admitsChild(scene, parent, roots, canDropInto);
|
|
18
18
|
}
|
|
19
19
|
/** Capture a drag at press. Null when there is nothing to move. Runs each
|
|
20
20
|
* behavior's `onStart`. `frame` reads the drag's latest frame, for the
|
|
@@ -81,7 +81,7 @@ function beginMove(ctx, opts, frame) {
|
|
|
81
81
|
geometryProjection,
|
|
82
82
|
lifecycle,
|
|
83
83
|
nodeAtPoint: ctx.deps.nodeAtPoint,
|
|
84
|
-
admits: admitsOf(ctx.deps.canDropInto, ids),
|
|
84
|
+
admits: admitsOf(ctx.deps.canDropInto, ids, scene),
|
|
85
85
|
reparentOnDrop: () => resolveParams(opts?.params)?.["reparentOnDrop"] ?? "off",
|
|
86
86
|
dragThresholdPx,
|
|
87
87
|
snap: lazySnapSession(ctx.deps.snap, {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"session.js","names":[],"sources":["../../../../src/interactions/actions/move/session.ts"],"sourcesContent":["import type { BindingOpts, InvocationCtx } from '@weasel-js/routing';\nimport { DRAG_THRESHOLD_PX, isPathEditPart, resolveParams } from '@weasel-js/routing';\nimport type { Op } from 'core/ops/types';\nimport type { NodeId, Scene } from 'core/scene/types';\nimport type { PoseDescriptor } from 'core/geometry/poseDescriptor';\nimport type { PoseComposition } from 'core/scene/composePose';\nimport { gestureReaders, type GestureReaders } from '../../gestures/shared/screenTolerance';\nimport type { GestureContext, MoveBehavior } from '../../gestures/types';\nimport type { CanDropIntoDep, LayoutDep, NodeAtPointDep } from '../depSchema';\nimport type { GeometryProjection } from '../geometryProjection';\nimport { commitGestureOps, readGestureLifecycle, type GestureLifecycle } from '../gestureLifecycle';\nimport { transformFinisher, type TransformFinisher } from '../transformFinish';\nimport { nodeDescriptors, poseDescriptorOf } from '../poseDescriptorDep';\nimport { moveSources } from '../selectionSubtree';\nimport { layoutDrop } from './behaviors/layoutDrop';\nimport { reparentOnDrop } from './behaviors/reparentOnDrop';\nimport { buildGestureContext, captureFrames, capturePlaneRefs, captureStartPoses } from './capture';\nimport { moveGestureAdapter, type MoveGestureAdapter } from './gestureAdapter';\nimport type { SnapSession } from '../snapDep';\nimport { lazySnapSession } from 'features/snap/lazySession';\n\n/** Allowed values for the `reparentOnDrop` binding param. `'off'` (the\n * default) preserves the legacy translate-only commit. `'top'` lands the\n * moved node at the top of the container under the cursor. `'above'`\n * lands it immediately above the hit sibling in z-order (falls back to\n * `'top'` semantics when the hit is itself a container or when the hit\n * and moved node share no parent). */\nexport type ReparentOnDrop = 'off' | 'top' | 'above';\n\n/** What a move captured at press. Nothing here changes during the drag. */\nexport interface MoveSession {\n readonly scene: Scene<unknown, string, unknown>;\n /** The drag roots. */\n readonly ids: readonly NodeId[];\n /** Descendants of the roots, previewed along with them. */\n readonly cascadeIds: readonly NodeId[];\n /** Start pose of every root and descendant. */\n readonly startPoses: ReadonlyMap<NodeId, unknown>;\n /** Ids whose frame is itself moving, so they ride it and are not translated. */\n readonly carried: ReadonlySet<NodeId>;\n /** For each moved id stored under a frame: its world pose and the frame's, at press. */\n readonly frames: ReadonlyMap<NodeId, { world: unknown; frameWorld: unknown }>;\n /** Ids grabbed mid-glide, with how far the pose shown stood from the document's. */\n readonly caught: ReadonlyMap<NodeId, { dx: number; dy: number }>;\n /** Per layer on another plane than the edited one: a point of its first root, in both. */\n readonly planeRefs: ReadonlyMap<string, { own: { x: number; y: number }; edited: { x: number; y: number } }>;\n readonly view: unknown;\n readonly projection: PoseDescriptor<unknown>;\n readonly descriptorOf: (id: NodeId | string) => PoseDescriptor<unknown>;\n readonly pc: PoseComposition<unknown>;\n /** The binding's behaviors, then the ones the action adds: a layout drop\n * when the `layout` dep is present, and reparent-on-drop. */\n readonly behaviors: readonly MoveBehavior<unknown>[];\n /** Reused across the drag; behaviors mutate its `scratch`. */\n readonly gestureCtx: GestureContext<unknown>;\n readonly adapter: MoveGestureAdapter<unknown>;\n readonly layout: LayoutDep | undefined;\n readonly applyOps?: (ops: Op[], label: string) => void;\n /** Finishes the committed batch with the other nodes that change with it. */\n readonly finish?: TransformFinisher;\n readonly geometryProjection?: GeometryProjection;\n readonly lifecycle: GestureLifecycle;\n readonly nodeAtPoint: NodeAtPointDep | undefined;\n /** Whether a container admits the dragged roots; every one does when absent. */\n readonly admits: (parent: NodeId) => boolean;\n /** Read at release: a binding's params may be a thunk. */\n readonly reparentOnDrop: () => ReparentOnDrop;\n readonly dragThresholdPx: number;\n /** The drag's snapping, begun once the drag passes its threshold; null\n * with no `snap` dep. */\n readonly snap: SnapSession | null;\n /** The press, in the coordinates the drag's delta is measured in. */\n readonly press: { x: number; y: number };\n}\n\n/** What one `onMove` produced. Replaced, not mutated, by the next. */\nexport interface MoveFrame {\n readonly delta: { dx: number; dy: number };\n /** Stored-frame pose of every displaced id, roots and descendants, and of\n * any other id a behavior posed. */\n readonly previews: Map<NodeId, unknown>;\n /** Ids the behaviors asked to have painted at full opacity. */\n readonly opaque: ReadonlySet<NodeId>;\n}\n\n/** What the behaviors the action builds itself read of the drag they belong\n * to. Neither is readable from `onStart`, which runs before the session\n * exists. */\nexport interface MoveGesture {\n session(): MoveSession;\n /** The latest frame: the previous one while an `onMove` is running. */\n frame(): MoveFrame;\n}\n\nfunction admitsOf(\n canDropInto: CanDropIntoDep | undefined,\n ids: readonly NodeId[],\n): (parent: NodeId) => boolean {\n if (canDropInto === undefined) return () => true;\n const roots = ids as readonly string[];\n return (parent) => canDropInto(parent as string, roots);\n}\n\n/** Capture a drag at press. Null when there is nothing to move. Runs each\n * behavior's `onStart`. `frame` reads the drag's latest frame, for the\n * behaviors appended to the binding's own. */\nexport function beginMove(\n ctx: InvocationCtx,\n opts: BindingOpts | undefined,\n frame: () => MoveFrame,\n): { session: MoveSession; readers: GestureReaders } | null {\n const selection = ctx.deps.selection;\n const scene = ctx.deps.scene;\n const projection = poseDescriptorOf(ctx.deps.poseDescriptor);\n const layout = ctx.deps.layout;\n const applyOps = ctx.deps.applyOps;\n const geometryProjection = ctx.deps.geometryProjection;\n const finish = transformFinisher(ctx.deps);\n\n if (!selection || !scene) return null;\n const pc = scene.composition;\n // Anchors, segments, and balance grips sit on the selected body, and this binding ties the\n // path-edit drags' on specificity; with no mode registry to filter move out, declining is what\n // lets them through.\n if (isPathEditPart(ctx.drag?.affordance)) return null;\n\n const params = resolveParams(opts?.params);\n const expandIds = params?.['expandIds'] as ((ids: string[]) => string[]) | undefined;\n const selected = [...selection.get()] as string[];\n const ids = moveSources(scene, expandIds ? expandIds(selected) : selected) as NodeId[];\n if (ids.length === 0) return null;\n\n const reflow = layout?.reflow ?? null;\n const { startPoses, cascadeIds, caught } = captureStartPoses(scene, ids, reflow, projection);\n const { carried, frames } = captureFrames(scene, ids, startPoses);\n\n // Everything a move does beyond translating is a behavior: the binding's\n // own, then the layout drop, then reparent-on-drop. On release the first to\n // answer owns the commit, so each preempts the ones after it.\n const gesture: MoveGesture = { session: () => session, frame };\n const behaviors: MoveBehavior<unknown>[] = [\n ...((opts?.behaviors ?? []) as MoveBehavior<unknown>[]),\n ...(layout ? [layoutDrop(gesture)] : []),\n reparentOnDrop(gesture),\n ];\n const dragThresholdPx = (params?.['dragThresholdPx'] as number | undefined)\n ?? ctx.deps.kitSettings?.get('gestures.dragPx') ?? DRAG_THRESHOLD_PX;\n const lifecycle = readGestureLifecycle(params, 'Move', behaviors);\n // A behavior that commits after the gesture has ended, a coast coming to\n // rest, lands its batch the way the gesture's own commit does.\n const adapter = moveGestureAdapter<unknown>(scene, (ops, _label, through) =>\n commitGestureOps({ scene, applyOps, adapter: through, finish }, lifecycle, ops), selection.adapterMethods);\n const readers = gestureReaders(ctx.deps);\n const gestureCtx = buildGestureContext(ctx, scene, ids, startPoses, adapter, readers);\n for (const b of behaviors) b.onStart?.(gestureCtx);\n\n const press = ctx.drag?.start ?? ctx.world;\n const session: MoveSession = {\n startPoses,\n ids,\n cascadeIds,\n carried,\n frames,\n scene,\n view: ctx.deps.view,\n planeRefs: capturePlaneRefs(scene, ids, ctx.deps.view, frames, startPoses, projection),\n caught,\n projection,\n descriptorOf: nodeDescriptors(scene, projection),\n behaviors,\n gestureCtx,\n adapter,\n layout,\n pc,\n applyOps,\n finish,\n geometryProjection,\n lifecycle,\n nodeAtPoint: ctx.deps.nodeAtPoint,\n admits: admitsOf(ctx.deps.canDropInto, ids),\n reparentOnDrop: () => (resolveParams(opts?.params)?.['reparentOnDrop'] as ReparentOnDrop | undefined) ?? 'off',\n dragThresholdPx,\n snap: lazySnapSession(ctx.deps.snap, { gesture: 'move', ids, press, constrain: 'axis' }),\n press,\n };\n return { session, readers };\n}\n"],"mappings":";;;;;;;;;;;;;AA8FA,SAAS,SACP,aACA,KAC6B;CAC7B,IAAI,gBAAgB,QAAW,aAAa;CAC5C,MAAM,QAAQ;CACd,QAAQ,WAAW,YAAY,QAAkB,KAAK;AACxD;;;;AAKA,SAAgB,UACd,KACA,MACA,OAC0D;CAC1D,MAAM,YAAY,IAAI,KAAK;CAC3B,MAAM,QAAQ,IAAI,KAAK;CACvB,MAAM,aAAa,iBAAiB,IAAI,KAAK,cAAc;CAC3D,MAAM,SAAS,IAAI,KAAK;CACxB,MAAM,WAAW,IAAI,KAAK;CAC1B,MAAM,qBAAqB,IAAI,KAAK;CACpC,MAAM,SAAS,kBAAkB,IAAI,IAAI;CAEzC,IAAI,CAAC,aAAa,CAAC,OAAO,OAAO;CACjC,MAAM,KAAK,MAAM;CAIjB,IAAI,eAAe,IAAI,MAAM,UAAU,GAAG,OAAO;CAEjD,MAAM,SAAS,cAAc,MAAM,MAAM;CACzC,MAAM,YAAY,SAAS;CAC3B,MAAM,WAAW,CAAC,GAAG,UAAU,IAAI,CAAC;CACpC,MAAM,MAAM,YAAY,OAAO,YAAY,UAAU,QAAQ,IAAI,QAAQ;CACzE,IAAI,IAAI,WAAW,GAAG,OAAO;CAE7B,MAAM,SAAS,QAAQ,UAAU;CACjC,MAAM,EAAE,YAAY,YAAY,WAAW,kBAAkB,OAAO,KAAK,QAAQ,UAAU;CAC3F,MAAM,EAAE,SAAS,WAAW,cAAc,OAAO,KAAK,UAAU;CAKhE,MAAM,UAAuB;EAAE,eAAe;EAAS;CAAM;CAC7D,MAAM,YAAqC;EACzC,GAAK,MAAM,aAAa,CAAC;EACzB,GAAI,SAAS,CAAC,WAAW,OAAO,CAAC,IAAI,CAAC;EACtC,eAAe,OAAO;CACxB;CACA,MAAM,kBAAmB,SAAS,sBAC7B,IAAI,KAAK,aAAa,IAAI,iBAAiB,KAAK;CACrD,MAAM,YAAY,qBAAqB,QAAQ,QAAQ,SAAS;CAGhE,MAAM,UAAU,mBAA4B,QAAQ,KAAK,QAAQ,YAC/D,iBAAiB;EAAE;EAAO;EAAU,SAAS;EAAS;CAAO,GAAG,WAAW,GAAG,GAAG,UAAU,cAAc;CAC3G,MAAM,UAAU,eAAe,IAAI,IAAI;CACvC,MAAM,aAAa,oBAAoB,KAAK,OAAO,KAAK,YAAY,SAAS,OAAO;CACpF,KAAK,MAAM,KAAK,WAAW,EAAE,UAAU,UAAU;CAEjD,MAAM,QAAQ,IAAI,MAAM,SAAS,IAAI;CACrC,MAAM,UAAuB;EAC3B;EACA;EACA;EACA;EACA;EACA;EACA,MAAM,IAAI,KAAK;EACf,WAAW,iBAAiB,OAAO,KAAK,IAAI,KAAK,MAAM,QAAQ,YAAY,UAAU;EACrF;EACA;EACA,cAAc,gBAAgB,OAAO,UAAU;EAC/C;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA,aAAa,IAAI,KAAK;EACtB,QAAQ,SAAS,IAAI,KAAK,aAAa,GAAG;EAC1C,sBAAuB,cAAc,MAAM,MAAM,CAAC,GAAG,qBAAoD;EACzG;EACA,MAAM,gBAAgB,IAAI,KAAK,MAAM;GAAE,SAAS;GAAQ;GAAK;GAAO,WAAW;EAAO,CAAC;EACvF;CACF;CACA,OAAO;EAAE;EAAS;CAAQ;AAC5B"}
|
|
1
|
+
{"version":3,"file":"session.js","names":[],"sources":["../../../../src/interactions/actions/move/session.ts"],"sourcesContent":["import type { BindingOpts, InvocationCtx } from '@weasel-js/routing';\nimport { DRAG_THRESHOLD_PX, isPathEditPart, resolveParams } from '@weasel-js/routing';\nimport type { Op } from 'core/ops/types';\nimport type { NodeId, Scene } from 'core/scene/types';\nimport type { PoseDescriptor } from 'core/geometry/poseDescriptor';\nimport type { PoseComposition } from 'core/scene/composePose';\nimport { gestureReaders, type GestureReaders } from '../../gestures/shared/screenTolerance';\nimport type { GestureContext, MoveBehavior } from '../../gestures/types';\nimport type { CanDropIntoDep, LayoutDep, NodeAtPointDep } from '../depSchema';\nimport { admitsChild } from '../admitsChild';\nimport type { GeometryProjection } from '../geometryProjection';\nimport { commitGestureOps, readGestureLifecycle, type GestureLifecycle } from '../gestureLifecycle';\nimport { transformFinisher, type TransformFinisher } from '../transformFinish';\nimport { nodeDescriptors, poseDescriptorOf } from '../poseDescriptorDep';\nimport { moveSources } from '../selectionSubtree';\nimport { layoutDrop } from './behaviors/layoutDrop';\nimport { reparentOnDrop } from './behaviors/reparentOnDrop';\nimport { buildGestureContext, captureFrames, capturePlaneRefs, captureStartPoses } from './capture';\nimport { moveGestureAdapter, type MoveGestureAdapter } from './gestureAdapter';\nimport type { SnapSession } from '../snapDep';\nimport { lazySnapSession } from 'features/snap/lazySession';\n\n/** Allowed values for the `reparentOnDrop` binding param. `'off'` (the\n * default) preserves the legacy translate-only commit. `'top'` lands the\n * moved node at the top of the container under the cursor. `'above'`\n * lands it immediately above the hit sibling in z-order (falls back to\n * `'top'` semantics when the hit is itself a container or when the hit\n * and moved node share no parent). */\nexport type ReparentOnDrop = 'off' | 'top' | 'above';\n\n/** What a move captured at press. Nothing here changes during the drag. */\nexport interface MoveSession {\n readonly scene: Scene<unknown, string, unknown>;\n /** The drag roots. */\n readonly ids: readonly NodeId[];\n /** Descendants of the roots, previewed along with them. */\n readonly cascadeIds: readonly NodeId[];\n /** Start pose of every root and descendant. */\n readonly startPoses: ReadonlyMap<NodeId, unknown>;\n /** Ids whose frame is itself moving, so they ride it and are not translated. */\n readonly carried: ReadonlySet<NodeId>;\n /** For each moved id stored under a frame: its world pose and the frame's, at press. */\n readonly frames: ReadonlyMap<NodeId, { world: unknown; frameWorld: unknown }>;\n /** Ids grabbed mid-glide, with how far the pose shown stood from the document's. */\n readonly caught: ReadonlyMap<NodeId, { dx: number; dy: number }>;\n /** Per layer on another plane than the edited one: a point of its first root, in both. */\n readonly planeRefs: ReadonlyMap<string, { own: { x: number; y: number }; edited: { x: number; y: number } }>;\n readonly view: unknown;\n readonly projection: PoseDescriptor<unknown>;\n readonly descriptorOf: (id: NodeId | string) => PoseDescriptor<unknown>;\n readonly pc: PoseComposition<unknown>;\n /** The binding's behaviors, then the ones the action adds: a layout drop\n * when the `layout` dep is present, and reparent-on-drop. */\n readonly behaviors: readonly MoveBehavior<unknown>[];\n /** Reused across the drag; behaviors mutate its `scratch`. */\n readonly gestureCtx: GestureContext<unknown>;\n readonly adapter: MoveGestureAdapter<unknown>;\n readonly layout: LayoutDep | undefined;\n readonly applyOps?: (ops: Op[], label: string) => void;\n /** Finishes the committed batch with the other nodes that change with it. */\n readonly finish?: TransformFinisher;\n readonly geometryProjection?: GeometryProjection;\n readonly lifecycle: GestureLifecycle;\n readonly nodeAtPoint: NodeAtPointDep | undefined;\n /** Whether a node admits the dragged roots; with no `canDropInto`, a group does and a mark does not. */\n readonly admits: (parent: NodeId) => boolean;\n /** Read at release: a binding's params may be a thunk. */\n readonly reparentOnDrop: () => ReparentOnDrop;\n readonly dragThresholdPx: number;\n /** The drag's snapping, begun once the drag passes its threshold; null\n * with no `snap` dep. */\n readonly snap: SnapSession | null;\n /** The press, in the coordinates the drag's delta is measured in. */\n readonly press: { x: number; y: number };\n}\n\n/** What one `onMove` produced. Replaced, not mutated, by the next. */\nexport interface MoveFrame {\n readonly delta: { dx: number; dy: number };\n /** Stored-frame pose of every displaced id, roots and descendants, and of\n * any other id a behavior posed. */\n readonly previews: Map<NodeId, unknown>;\n /** Ids the behaviors asked to have painted at full opacity. */\n readonly opaque: ReadonlySet<NodeId>;\n}\n\n/** What the behaviors the action builds itself read of the drag they belong\n * to. Neither is readable from `onStart`, which runs before the session\n * exists. */\nexport interface MoveGesture {\n session(): MoveSession;\n /** The latest frame: the previous one while an `onMove` is running. */\n frame(): MoveFrame;\n}\n\nfunction admitsOf(\n canDropInto: CanDropIntoDep | undefined,\n ids: readonly NodeId[],\n scene: { get(id: NodeId): { kind: string } | undefined },\n): (parent: NodeId) => boolean {\n const roots = ids as readonly string[];\n return (parent) => admitsChild(scene, parent, roots, canDropInto);\n}\n\n/** Capture a drag at press. Null when there is nothing to move. Runs each\n * behavior's `onStart`. `frame` reads the drag's latest frame, for the\n * behaviors appended to the binding's own. */\nexport function beginMove(\n ctx: InvocationCtx,\n opts: BindingOpts | undefined,\n frame: () => MoveFrame,\n): { session: MoveSession; readers: GestureReaders } | null {\n const selection = ctx.deps.selection;\n const scene = ctx.deps.scene;\n const projection = poseDescriptorOf(ctx.deps.poseDescriptor);\n const layout = ctx.deps.layout;\n const applyOps = ctx.deps.applyOps;\n const geometryProjection = ctx.deps.geometryProjection;\n const finish = transformFinisher(ctx.deps);\n\n if (!selection || !scene) return null;\n const pc = scene.composition;\n // Anchors, segments, and balance grips sit on the selected body, and this binding ties the\n // path-edit drags' on specificity; with no mode registry to filter move out, declining is what\n // lets them through.\n if (isPathEditPart(ctx.drag?.affordance)) return null;\n\n const params = resolveParams(opts?.params);\n const expandIds = params?.['expandIds'] as ((ids: string[]) => string[]) | undefined;\n const selected = [...selection.get()] as string[];\n const ids = moveSources(scene, expandIds ? expandIds(selected) : selected) as NodeId[];\n if (ids.length === 0) return null;\n\n const reflow = layout?.reflow ?? null;\n const { startPoses, cascadeIds, caught } = captureStartPoses(scene, ids, reflow, projection);\n const { carried, frames } = captureFrames(scene, ids, startPoses);\n\n // Everything a move does beyond translating is a behavior: the binding's\n // own, then the layout drop, then reparent-on-drop. On release the first to\n // answer owns the commit, so each preempts the ones after it.\n const gesture: MoveGesture = { session: () => session, frame };\n const behaviors: MoveBehavior<unknown>[] = [\n ...((opts?.behaviors ?? []) as MoveBehavior<unknown>[]),\n ...(layout ? [layoutDrop(gesture)] : []),\n reparentOnDrop(gesture),\n ];\n const dragThresholdPx = (params?.['dragThresholdPx'] as number | undefined)\n ?? ctx.deps.kitSettings?.get('gestures.dragPx') ?? DRAG_THRESHOLD_PX;\n const lifecycle = readGestureLifecycle(params, 'Move', behaviors);\n // A behavior that commits after the gesture has ended, a coast coming to\n // rest, lands its batch the way the gesture's own commit does.\n const adapter = moveGestureAdapter<unknown>(scene, (ops, _label, through) =>\n commitGestureOps({ scene, applyOps, adapter: through, finish }, lifecycle, ops), selection.adapterMethods);\n const readers = gestureReaders(ctx.deps);\n const gestureCtx = buildGestureContext(ctx, scene, ids, startPoses, adapter, readers);\n for (const b of behaviors) b.onStart?.(gestureCtx);\n\n const press = ctx.drag?.start ?? ctx.world;\n const session: MoveSession = {\n startPoses,\n ids,\n cascadeIds,\n carried,\n frames,\n scene,\n view: ctx.deps.view,\n planeRefs: capturePlaneRefs(scene, ids, ctx.deps.view, frames, startPoses, projection),\n caught,\n projection,\n descriptorOf: nodeDescriptors(scene, projection),\n behaviors,\n gestureCtx,\n adapter,\n layout,\n pc,\n applyOps,\n finish,\n geometryProjection,\n lifecycle,\n nodeAtPoint: ctx.deps.nodeAtPoint,\n admits: admitsOf(ctx.deps.canDropInto, ids, scene),\n reparentOnDrop: () => (resolveParams(opts?.params)?.['reparentOnDrop'] as ReparentOnDrop | undefined) ?? 'off',\n dragThresholdPx,\n snap: lazySnapSession(ctx.deps.snap, { gesture: 'move', ids, press, constrain: 'axis' }),\n press,\n };\n return { session, readers };\n}\n"],"mappings":";;;;;;;;;;;;;;AA+FA,SAAS,SACP,aACA,KACA,OAC6B;CAC7B,MAAM,QAAQ;CACd,QAAQ,WAAW,YAAY,OAAO,QAAQ,OAAO,WAAW;AAClE;;;;AAKA,SAAgB,UACd,KACA,MACA,OAC0D;CAC1D,MAAM,YAAY,IAAI,KAAK;CAC3B,MAAM,QAAQ,IAAI,KAAK;CACvB,MAAM,aAAa,iBAAiB,IAAI,KAAK,cAAc;CAC3D,MAAM,SAAS,IAAI,KAAK;CACxB,MAAM,WAAW,IAAI,KAAK;CAC1B,MAAM,qBAAqB,IAAI,KAAK;CACpC,MAAM,SAAS,kBAAkB,IAAI,IAAI;CAEzC,IAAI,CAAC,aAAa,CAAC,OAAO,OAAO;CACjC,MAAM,KAAK,MAAM;CAIjB,IAAI,eAAe,IAAI,MAAM,UAAU,GAAG,OAAO;CAEjD,MAAM,SAAS,cAAc,MAAM,MAAM;CACzC,MAAM,YAAY,SAAS;CAC3B,MAAM,WAAW,CAAC,GAAG,UAAU,IAAI,CAAC;CACpC,MAAM,MAAM,YAAY,OAAO,YAAY,UAAU,QAAQ,IAAI,QAAQ;CACzE,IAAI,IAAI,WAAW,GAAG,OAAO;CAE7B,MAAM,SAAS,QAAQ,UAAU;CACjC,MAAM,EAAE,YAAY,YAAY,WAAW,kBAAkB,OAAO,KAAK,QAAQ,UAAU;CAC3F,MAAM,EAAE,SAAS,WAAW,cAAc,OAAO,KAAK,UAAU;CAKhE,MAAM,UAAuB;EAAE,eAAe;EAAS;CAAM;CAC7D,MAAM,YAAqC;EACzC,GAAK,MAAM,aAAa,CAAC;EACzB,GAAI,SAAS,CAAC,WAAW,OAAO,CAAC,IAAI,CAAC;EACtC,eAAe,OAAO;CACxB;CACA,MAAM,kBAAmB,SAAS,sBAC7B,IAAI,KAAK,aAAa,IAAI,iBAAiB,KAAK;CACrD,MAAM,YAAY,qBAAqB,QAAQ,QAAQ,SAAS;CAGhE,MAAM,UAAU,mBAA4B,QAAQ,KAAK,QAAQ,YAC/D,iBAAiB;EAAE;EAAO;EAAU,SAAS;EAAS;CAAO,GAAG,WAAW,GAAG,GAAG,UAAU,cAAc;CAC3G,MAAM,UAAU,eAAe,IAAI,IAAI;CACvC,MAAM,aAAa,oBAAoB,KAAK,OAAO,KAAK,YAAY,SAAS,OAAO;CACpF,KAAK,MAAM,KAAK,WAAW,EAAE,UAAU,UAAU;CAEjD,MAAM,QAAQ,IAAI,MAAM,SAAS,IAAI;CACrC,MAAM,UAAuB;EAC3B;EACA;EACA;EACA;EACA;EACA;EACA,MAAM,IAAI,KAAK;EACf,WAAW,iBAAiB,OAAO,KAAK,IAAI,KAAK,MAAM,QAAQ,YAAY,UAAU;EACrF;EACA;EACA,cAAc,gBAAgB,OAAO,UAAU;EAC/C;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA,aAAa,IAAI,KAAK;EACtB,QAAQ,SAAS,IAAI,KAAK,aAAa,KAAK,KAAK;EACjD,sBAAuB,cAAc,MAAM,MAAM,CAAC,GAAG,qBAAoD;EACzG;EACA,MAAM,gBAAgB,IAAI,KAAK,MAAM;GAAE,SAAS;GAAQ;GAAK;GAAO,WAAW;EAAO,CAAC;EACvF;CACF;CACA,OAAO;EAAE;EAAS;CAAQ;AAC5B"}
|
|
@@ -19,7 +19,7 @@ function pathNodesOf(scene, ids, shapeOf) {
|
|
|
19
19
|
const out = [];
|
|
20
20
|
for (const id of ids) {
|
|
21
21
|
const found = scene.get(id);
|
|
22
|
-
if (!found || found.kind !== "
|
|
22
|
+
if (!found || found.kind !== "mark") continue;
|
|
23
23
|
const shape = shapeOf?.(id);
|
|
24
24
|
const node = shape ? {
|
|
25
25
|
...found,
|
|
@@ -35,15 +35,24 @@ function pathNodesOf(scene, ids, shapeOf) {
|
|
|
35
35
|
}
|
|
36
36
|
return out;
|
|
37
37
|
}
|
|
38
|
+
/** Per node, the last path `withPath` stored and what it made of it. */
|
|
39
|
+
var lastStored = /* @__PURE__ */ new WeakMap();
|
|
38
40
|
/** `node` storing `worldPath` as plain geometry, or null when it holds no
|
|
39
|
-
* editable polygon.
|
|
41
|
+
* editable polygon. The same object while the node and the path are, so a
|
|
42
|
+
* gesture previewing through it keeps its preview's identity between frames. */
|
|
40
43
|
function withPath(node, worldPath) {
|
|
44
|
+
const held = lastStored.get(node);
|
|
45
|
+
if (held?.path === worldPath) return held.shape;
|
|
41
46
|
const stored = storeEditablePath(node, worldPath);
|
|
42
|
-
|
|
43
|
-
return {
|
|
47
|
+
const shape = stored ? {
|
|
44
48
|
pose: stored.pose,
|
|
45
49
|
data: withoutGeometryBoundData(withoutShapeGenerator(stored.data))
|
|
46
|
-
};
|
|
50
|
+
} : null;
|
|
51
|
+
lastStored.set(node, {
|
|
52
|
+
path: worldPath,
|
|
53
|
+
shape
|
|
54
|
+
});
|
|
55
|
+
return shape;
|
|
47
56
|
}
|
|
48
57
|
/** The ops that give `node` the geometry `worldPath`, labeled `label`. */
|
|
49
58
|
function reshapeOps(node, worldPath, label) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"pathNodes.js","names":[],"sources":["../../../../src/interactions/actions/pathCommands/pathNodes.ts"],"sourcesContent":["/**\n * What the path commands share: reading the selected path nodes, and writing\n * new geometry back into a node or into a copy of one. A node's geometry is\n * stored where it already lives (`storeEditablePath`); a shape generator, and\n * the data in `GEOMETRY_BOUND_DATA`, which no longer make it or name anchors\n * it no longer has, are dropped.\n */\nimport type { ActionDeps } from '@weasel-js/routing';\nimport type { PolygonPath } from '@weasel-js/geom';\nimport type { Node, NodeId, Scene } from 'core/scene/types';\nimport type { Op } from 'core/ops/types';\nimport { createTransformOp } from 'core/ops/transform';\nimport { createSetDataOp } from 'core/ops/setData';\nimport { createInsertOp } from 'core/ops/create';\nimport { createDeleteOp } from 'core/ops/delete';\nimport { resolveEditablePathOf, storeEditablePath } from 'features/paths/editablePath';\nimport { withoutShapeGenerator } from 'features/paths/shapeGenerator';\nimport { withoutGeometryBoundData } from 'features/paths/pathEditExtension';\n\n\n/** A selected node with an editable polygon, and that polygon in world space. */\nexport interface PathNode {\n id: NodeId;\n node: Node<unknown, string, unknown>;\n path: PolygonPath;\n}\n\n/** The selected leaves that hold an editable polygon, in selection order. */\nexport function selectedPathNodes(deps: ActionDeps | undefined): PathNode[] {\n const scene = deps?.scene as Scene<unknown, string, unknown> | undefined;\n return scene ? pathNodesOf(scene, deps?.selection?.get() ?? []) : [];\n}\n\n/** The leaves among `ids` that hold an editable polygon, in the order given.\n * `shapeOf` stands in a node's pose and data, as an in-flight gesture would\n * leave them, for its own. */\nexport function pathNodesOf(\n scene: Pick<Scene<unknown, string, unknown>, 'get'>,\n ids: readonly string[],\n shapeOf?: (id: string) => { pose: unknown; data: unknown } | undefined,\n): PathNode[] {\n const out: PathNode[] = [];\n for (const id of ids) {\n const found = scene.get(id as NodeId);\n if (!found || found.kind !== '
|
|
1
|
+
{"version":3,"file":"pathNodes.js","names":[],"sources":["../../../../src/interactions/actions/pathCommands/pathNodes.ts"],"sourcesContent":["/**\n * What the path commands share: reading the selected path nodes, and writing\n * new geometry back into a node or into a copy of one. A node's geometry is\n * stored where it already lives (`storeEditablePath`); a shape generator, and\n * the data in `GEOMETRY_BOUND_DATA`, which no longer make it or name anchors\n * it no longer has, are dropped.\n */\nimport type { ActionDeps } from '@weasel-js/routing';\nimport type { PolygonPath } from '@weasel-js/geom';\nimport type { Node, NodeId, Scene } from 'core/scene/types';\nimport type { Op } from 'core/ops/types';\nimport { createTransformOp } from 'core/ops/transform';\nimport { createSetDataOp } from 'core/ops/setData';\nimport { createInsertOp } from 'core/ops/create';\nimport { createDeleteOp } from 'core/ops/delete';\nimport { resolveEditablePathOf, storeEditablePath } from 'features/paths/editablePath';\nimport { withoutShapeGenerator } from 'features/paths/shapeGenerator';\nimport { withoutGeometryBoundData } from 'features/paths/pathEditExtension';\n\n\n/** A selected node with an editable polygon, and that polygon in world space. */\nexport interface PathNode {\n id: NodeId;\n node: Node<unknown, string, unknown>;\n path: PolygonPath;\n}\n\n/** The selected leaves that hold an editable polygon, in selection order. */\nexport function selectedPathNodes(deps: ActionDeps | undefined): PathNode[] {\n const scene = deps?.scene as Scene<unknown, string, unknown> | undefined;\n return scene ? pathNodesOf(scene, deps?.selection?.get() ?? []) : [];\n}\n\n/** The leaves among `ids` that hold an editable polygon, in the order given.\n * `shapeOf` stands in a node's pose and data, as an in-flight gesture would\n * leave them, for its own. */\nexport function pathNodesOf(\n scene: Pick<Scene<unknown, string, unknown>, 'get'>,\n ids: readonly string[],\n shapeOf?: (id: string) => { pose: unknown; data: unknown } | undefined,\n): PathNode[] {\n const out: PathNode[] = [];\n for (const id of ids) {\n const found = scene.get(id as NodeId);\n if (!found || found.kind !== 'mark') continue;\n const shape = shapeOf?.(id);\n const node = shape ? ({ ...found, pose: shape.pose, data: shape.data } as Node<unknown, string, unknown>) : found;\n const path = resolveEditablePathOf(node);\n if (path) out.push({ id: id as NodeId, node, path });\n }\n return out;\n}\n\n/** Per node, the last path `withPath` stored and what it made of it. */\nconst lastStored = new WeakMap<object, { path: PolygonPath; shape: { pose: unknown; data: unknown } | null }>();\n\n/** `node` storing `worldPath` as plain geometry, or null when it holds no\n * editable polygon. The same object while the node and the path are, so a\n * gesture previewing through it keeps its preview's identity between frames. */\nexport function withPath(node: Node<unknown, string, unknown>, worldPath: PolygonPath): { pose: unknown; data: unknown } | null {\n const held = lastStored.get(node);\n if (held?.path === worldPath) return held.shape;\n const stored = storeEditablePath(node, worldPath);\n const shape = stored ? { pose: stored.pose, data: withoutGeometryBoundData(withoutShapeGenerator(stored.data)) } : null;\n lastStored.set(node, { path: worldPath, shape });\n return shape;\n}\n\n/** The ops that give `node` the geometry `worldPath`, labeled `label`. */\nexport function reshapeOps(node: Node<unknown, string, unknown>, worldPath: PolygonPath, label: string): Op[] {\n const next = withPath(node, worldPath);\n return next ? reshapeTo(node, next, label) : [];\n}\n\n/** The ops taking `node` from what it is to `next`, labeled `label`. */\nexport function reshapeTo(node: Node<unknown, string, unknown>, next: { pose: unknown; data: unknown }, label: string): Op[] {\n const ops: Op[] = [];\n if (next.pose !== node.pose) ops.push(createTransformOp({ id: node.id, from: node.pose, to: next.pose, label }));\n if (next.data !== node.data) ops.push(createSetDataOp({ id: node.id, from: node.data, to: next.data, label }));\n return ops;\n}\n\n/** A copy of `node` under a fresh id, holding `worldPath`. */\nexport function pathCopy(scene: Scene<unknown, string, unknown>, node: Node<unknown, string, unknown>, worldPath: PolygonPath): Node<unknown, string, unknown> | null {\n const next = withPath(node, worldPath);\n if (!next) return null;\n return { ...node, id: scene.mintId(), pose: next.pose, data: next.data } as Node<unknown, string, unknown>;\n}\n\n/** Where `id` sits among its siblings, back to front. */\nexport function slotOf(scene: Scene<unknown, string, unknown>, id: NodeId): number {\n const parent = scene.get(id)?.parent ?? null;\n const siblings = parent === null ? scene.roots : scene.childrenOf(parent);\n return siblings.indexOf(id);\n}\n\n/** Insert `node` at `index` among its parent's children. */\nexport function insertAt(node: Node<unknown, string, unknown>, index: number, label: string): Op {\n return createInsertOp(index >= 0 ? { node, index, label } : { node, label });\n}\n\n/** Delete `node`, remembering its slot for undo. */\nexport function deleteNode(scene: Scene<unknown, string, unknown>, node: Node<unknown, string, unknown>, label: string): Op {\n return createDeleteOp({ node, index: slotOf(scene, node.id), label });\n}\n"],"mappings":";;;;;;;;;;AA4BA,SAAgB,kBAAkB,MAA0C;CAC1E,MAAM,QAAQ,MAAM;CACpB,OAAO,QAAQ,YAAY,OAAO,MAAM,WAAW,IAAI,KAAK,CAAC,CAAC,IAAI,CAAC;AACrE;;;;AAKA,SAAgB,YACd,OACA,KACA,SACY;CACZ,MAAM,MAAkB,CAAC;CACzB,KAAK,MAAM,MAAM,KAAK;EACpB,MAAM,QAAQ,MAAM,IAAI,EAAY;EACpC,IAAI,CAAC,SAAS,MAAM,SAAS,QAAQ;EACrC,MAAM,QAAQ,UAAU,EAAE;EAC1B,MAAM,OAAO,QAAS;GAAE,GAAG;GAAO,MAAM,MAAM;GAAM,MAAM,MAAM;EAAK,IAAuC;EAC5G,MAAM,OAAO,sBAAsB,IAAI;EACvC,IAAI,MAAM,IAAI,KAAK;GAAM;GAAc;GAAM;EAAK,CAAC;CACrD;CACA,OAAO;AACT;;AAGA,IAAM,6BAAa,IAAI,QAAuF;;;;AAK9G,SAAgB,SAAS,MAAsC,WAAiE;CAC9H,MAAM,OAAO,WAAW,IAAI,IAAI;CAChC,IAAI,MAAM,SAAS,WAAW,OAAO,KAAK;CAC1C,MAAM,SAAS,kBAAkB,MAAM,SAAS;CAChD,MAAM,QAAQ,SAAS;EAAE,MAAM,OAAO;EAAM,MAAM,yBAAyB,sBAAsB,OAAO,IAAI,CAAC;CAAE,IAAI;CACnH,WAAW,IAAI,MAAM;EAAE,MAAM;EAAW;CAAM,CAAC;CAC/C,OAAO;AACT;;AAGA,SAAgB,WAAW,MAAsC,WAAwB,OAAqB;CAC5G,MAAM,OAAO,SAAS,MAAM,SAAS;CACrC,OAAO,OAAO,UAAU,MAAM,MAAM,KAAK,IAAI,CAAC;AAChD;;AAGA,SAAgB,UAAU,MAAsC,MAAwC,OAAqB;CAC3H,MAAM,MAAY,CAAC;CACnB,IAAI,KAAK,SAAS,KAAK,MAAM,IAAI,KAAK,kBAAkB;EAAE,IAAI,KAAK;EAAI,MAAM,KAAK;EAAM,IAAI,KAAK;EAAM;CAAM,CAAC,CAAC;CAC/G,IAAI,KAAK,SAAS,KAAK,MAAM,IAAI,KAAK,gBAAgB;EAAE,IAAI,KAAK;EAAI,MAAM,KAAK;EAAM,IAAI,KAAK;EAAM;CAAM,CAAC,CAAC;CAC7G,OAAO;AACT;;AAGA,SAAgB,SAAS,OAAwC,MAAsC,WAA+D;CACpK,MAAM,OAAO,SAAS,MAAM,SAAS;CACrC,IAAI,CAAC,MAAM,OAAO;CAClB,OAAO;EAAE,GAAG;EAAM,IAAI,MAAM,OAAO;EAAG,MAAM,KAAK;EAAM,MAAM,KAAK;CAAK;AACzE;;AAGA,SAAgB,OAAO,OAAwC,IAAoB;CACjF,MAAM,SAAS,MAAM,IAAI,EAAE,CAAC,EAAE,UAAU;CAExC,QADiB,WAAW,OAAO,MAAM,QAAQ,MAAM,WAAW,MAAM,EACjE,CAAS,QAAQ,EAAE;AAC5B;;AAGA,SAAgB,SAAS,MAAsC,OAAe,OAAmB;CAC/F,OAAO,eAAe,SAAS,IAAI;EAAE;EAAM;EAAO;CAAM,IAAI;EAAE;EAAM;CAAM,CAAC;AAC7E;;AAGA,SAAgB,WAAW,OAAwC,MAAsC,OAAmB;CAC1H,OAAO,eAAe;EAAE;EAAM,OAAO,OAAO,OAAO,KAAK,EAAE;EAAG;CAAM,CAAC;AACtE"}
|
|
@@ -8,13 +8,23 @@ function startThresholdDrag(e, opts) {
|
|
|
8
8
|
clientY: e.clientY
|
|
9
9
|
};
|
|
10
10
|
let activated = false;
|
|
11
|
+
const origin = opts.origin ?? e.currentTarget;
|
|
12
|
+
const late = opts.captureAt === "threshold";
|
|
13
|
+
const releaseLate = () => {
|
|
14
|
+
if (!late || !activated) return;
|
|
15
|
+
try {
|
|
16
|
+
origin.releasePointerCapture?.(e.pointerId);
|
|
17
|
+
} catch {}
|
|
18
|
+
};
|
|
11
19
|
const maybeActivate = (ev) => {
|
|
12
20
|
if (activated) return;
|
|
13
21
|
if (!pastDragThreshold(start, ev, opts.threshold)) return;
|
|
14
22
|
activated = true;
|
|
23
|
+
if (late) try {
|
|
24
|
+
origin.setPointerCapture?.(ev.pointerId);
|
|
25
|
+
} catch {}
|
|
15
26
|
opts.onActivate?.(ev);
|
|
16
27
|
};
|
|
17
|
-
const origin = opts.origin ?? e.currentTarget;
|
|
18
28
|
const keys = origin.ownerDocument.defaultView ?? origin.ownerDocument;
|
|
19
29
|
const onKey = (ev) => {
|
|
20
30
|
if (!activated || ev.key !== "Escape") return;
|
|
@@ -31,14 +41,16 @@ function startThresholdDrag(e, opts) {
|
|
|
31
41
|
},
|
|
32
42
|
onEnd: (ev) => {
|
|
33
43
|
unlisten();
|
|
44
|
+
releaseLate();
|
|
34
45
|
if (activated) opts.onCommit(ev);
|
|
35
46
|
else opts.onClick?.(ev);
|
|
36
47
|
},
|
|
37
48
|
onCancel: () => {
|
|
38
49
|
unlisten();
|
|
50
|
+
releaseLate();
|
|
39
51
|
opts.onCancel?.();
|
|
40
52
|
}
|
|
41
|
-
});
|
|
53
|
+
}, { capture: !late });
|
|
42
54
|
return {
|
|
43
55
|
isDragging: () => activated,
|
|
44
56
|
cancel: () => {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"thresholdDrag.js","names":[],"sources":["../../../../src/interactions/gestures/thresholdDrag/thresholdDrag.ts"],"sourcesContent":["import { openPointerSession, pastDragThreshold } from '@weasel-js/routing';\n\n/**\n * A drag that does not start until the pointer has moved far enough to mean\n * it. Below the threshold the gesture is still a click; above it, `onActivate`\n * fires once and every later move is a drag.\n *\n * The pointer lifecycle underneath — capture, pointer identity, lost-capture\n * and missed-release recovery, teardown — belongs to `openPointerSession`.\n */\nexport interface ThresholdDragOptions {\n /** Travel, in CSS pixels, that makes the press a drag. Default `DRAG_THRESHOLD_PX`. A component\n * passes `useKitSetting('gestures.dragPx')` here, which falls through to that default when unset. */\n threshold?: number;\n /** Element the session opens on. Defaults to `e.currentTarget`; a list must\n * pass its container, since a grabbed row unmounting drops capture. */\n origin?: Element;\n onActivate?: (e: PointerEvent) => void;\n onMove: (e: PointerEvent) => void;\n onCommit: (e: PointerEvent) => void;\n /** Released below the threshold — the click the press turned out to be. */\n onClick?: (e: PointerEvent) => void;\n /** Ended without a release: pointercancel, lost capture, Escape during the drag, or `cancel()`. */\n onCancel?: () => void;\n}\n\n/** Handle returned by `startThresholdDrag` exposing live gesture state. */\nexport interface ThresholdDragHandle {\n /** True after the pointer has moved past `threshold` and the drag is live. */\n isDragging: () => boolean;\n /** End the gesture now, as a cancel. For an unmount, or any other rule the caller owns. */\n cancel: () => void;\n}\n\n/** Begin a threshold-gated drag from a React PointerDown event; returns a handle exposing live state. */\nexport function startThresholdDrag(\n e: React.PointerEvent,\n opts: ThresholdDragOptions,\n): ThresholdDragHandle {\n const start = { clientX: e.clientX, clientY: e.clientY };\n let activated = false;\n\n const maybeActivate = (ev: PointerEvent) => {\n if (activated) return;\n if (!pastDragThreshold(start, ev, opts.threshold)) return;\n activated = true;\n
|
|
1
|
+
{"version":3,"file":"thresholdDrag.js","names":[],"sources":["../../../../src/interactions/gestures/thresholdDrag/thresholdDrag.ts"],"sourcesContent":["import { openPointerSession, pastDragThreshold } from '@weasel-js/routing';\n\n/**\n * A drag that does not start until the pointer has moved far enough to mean\n * it. Below the threshold the gesture is still a click; above it, `onActivate`\n * fires once and every later move is a drag.\n *\n * The pointer lifecycle underneath — capture, pointer identity, lost-capture\n * and missed-release recovery, teardown — belongs to `openPointerSession`.\n */\nexport interface ThresholdDragOptions {\n /** Travel, in CSS pixels, that makes the press a drag. Default `DRAG_THRESHOLD_PX`. A component\n * passes `useKitSetting('gestures.dragPx')` here, which falls through to that default when unset. */\n threshold?: number;\n /** Element the session opens on. Defaults to `e.currentTarget`; a list must\n * pass its container, since a grabbed row unmounting drops capture. */\n origin?: Element;\n /** When the origin captures the pointer: at the press (the default), or only once the press is a drag. Capture at\n * the press retargets the release, so the click a press below the threshold means lands off what was pressed. */\n captureAt?: 'press' | 'threshold';\n onActivate?: (e: PointerEvent) => void;\n onMove: (e: PointerEvent) => void;\n onCommit: (e: PointerEvent) => void;\n /** Released below the threshold — the click the press turned out to be. */\n onClick?: (e: PointerEvent) => void;\n /** Ended without a release: pointercancel, lost capture, Escape during the drag, or `cancel()`. */\n onCancel?: () => void;\n}\n\n/** Handle returned by `startThresholdDrag` exposing live gesture state. */\nexport interface ThresholdDragHandle {\n /** True after the pointer has moved past `threshold` and the drag is live. */\n isDragging: () => boolean;\n /** End the gesture now, as a cancel. For an unmount, or any other rule the caller owns. */\n cancel: () => void;\n}\n\n/** Begin a threshold-gated drag from a React PointerDown event; returns a handle exposing live state. */\nexport function startThresholdDrag(\n e: React.PointerEvent,\n opts: ThresholdDragOptions,\n): ThresholdDragHandle {\n const start = { clientX: e.clientX, clientY: e.clientY };\n let activated = false;\n const origin = opts.origin ?? (e.currentTarget as Element);\n const late = opts.captureAt === 'threshold';\n // The session releases only a capture it took itself, so one taken at the threshold is released here.\n const releaseLate = () => {\n if (!late || !activated) return;\n try {\n origin.releasePointerCapture?.(e.pointerId);\n } catch {\n /* already gone */\n }\n };\n\n const maybeActivate = (ev: PointerEvent) => {\n if (activated) return;\n if (!pastDragThreshold(start, ev, opts.threshold)) return;\n activated = true;\n if (late) {\n try {\n origin.setPointerCapture?.(ev.pointerId);\n } catch {\n /* a pointer already gone: nothing to retarget */\n }\n }\n opts.onActivate?.(ev);\n };\n\n const keys: EventTarget = origin.ownerDocument.defaultView ?? origin.ownerDocument;\n // Window capture, and stopped there: the Escape that ends a drag must not also close the dialog the drag is in.\n const onKey = (ev: Event) => {\n if (!activated || (ev as KeyboardEvent).key !== 'Escape') return;\n ev.preventDefault();\n ev.stopPropagation();\n session.cancel();\n };\n const unlisten = () => keys.removeEventListener('keydown', onKey, true);\n keys.addEventListener('keydown', onKey, true);\n\n const session = openPointerSession(origin, e, {\n onMove: (ev) => {\n maybeActivate(ev);\n if (activated) opts.onMove(ev);\n },\n onEnd: (ev) => {\n unlisten();\n releaseLate();\n if (activated) opts.onCommit(ev);\n else opts.onClick?.(ev);\n },\n onCancel: () => {\n unlisten();\n releaseLate();\n opts.onCancel?.();\n },\n }, { capture: !late });\n\n return {\n isDragging: () => activated,\n cancel: () => { session.cancel(); },\n };\n}\n"],"mappings":";;;;AAsCA,SAAgB,mBACd,GACA,MACqB;CACrB,MAAM,QAAQ;EAAE,SAAS,EAAE;EAAS,SAAS,EAAE;CAAQ;CACvD,IAAI,YAAY;CAChB,MAAM,SAAS,KAAK,UAAW,EAAE;CACjC,MAAM,OAAO,KAAK,cAAc;CAEhC,MAAM,oBAAoB;EACxB,IAAI,CAAC,QAAQ,CAAC,WAAW;EACzB,IAAI;GACF,OAAO,wBAAwB,EAAE,SAAS;EAC5C,QAAQ,CAER;CACF;CAEA,MAAM,iBAAiB,OAAqB;EAC1C,IAAI,WAAW;EACf,IAAI,CAAC,kBAAkB,OAAO,IAAI,KAAK,SAAS,GAAG;EACnD,YAAY;EACZ,IAAI,MACF,IAAI;GACF,OAAO,oBAAoB,GAAG,SAAS;EACzC,QAAQ,CAER;EAEF,KAAK,aAAa,EAAE;CACtB;CAEA,MAAM,OAAoB,OAAO,cAAc,eAAe,OAAO;CAErE,MAAM,SAAS,OAAc;EAC3B,IAAI,CAAC,aAAc,GAAqB,QAAQ,UAAU;EAC1D,GAAG,eAAe;EAClB,GAAG,gBAAgB;EACnB,QAAQ,OAAO;CACjB;CACA,MAAM,iBAAiB,KAAK,oBAAoB,WAAW,OAAO,IAAI;CACtE,KAAK,iBAAiB,WAAW,OAAO,IAAI;CAE5C,MAAM,UAAU,mBAAmB,QAAQ,GAAG;EAC5C,SAAS,OAAO;GACd,cAAc,EAAE;GAChB,IAAI,WAAW,KAAK,OAAO,EAAE;EAC/B;EACA,QAAQ,OAAO;GACb,SAAS;GACT,YAAY;GACZ,IAAI,WAAW,KAAK,SAAS,EAAE;QAC1B,KAAK,UAAU,EAAE;EACxB;EACA,gBAAgB;GACd,SAAS;GACT,YAAY;GACZ,KAAK,WAAW;EAClB;CACF,GAAG,EAAE,SAAS,CAAC,KAAK,CAAC;CAErB,OAAO;EACL,kBAAkB;EAClB,cAAc;GAAE,QAAQ,OAAO;EAAG;CACpC;AACF"}
|
|
@@ -31,11 +31,8 @@ declare function pointInRotatedRect(pose: RotatedPose, worldX: number, worldY: n
|
|
|
31
31
|
|
|
32
32
|
/** Registry key for {@link clipNothing}; every scene's registry carries it. */
|
|
33
33
|
declare const CLIP_NOTHING = "kit:clipNothing";
|
|
34
|
-
/**
|
|
35
|
-
*
|
|
36
|
-
* children may draw outside it. A container without one clips its children to
|
|
37
|
-
* its painter's silhouette.
|
|
38
|
-
*/
|
|
34
|
+
/** A `clipFromPose` that clips nothing, registered in every scene so a save
|
|
35
|
+
* naming {@link CLIP_NOTHING} loads. */
|
|
39
36
|
declare const clipNothing: () => null;
|
|
40
37
|
|
|
41
38
|
export { CLIP_NOTHING as C, aabbCenter as a, rotatePoint as b, clipNothing as c, rotatedRectCorners as d, pointInRotatedRect as p, rectCorners as r };
|
package/dist/math.d.ts
CHANGED
|
@@ -2,5 +2,5 @@ export { DEFAULT_FLATTEN_TOLERANCE, PATH_C, PATH_CMD_LENGTHS, PATH_L, PATH_M, PA
|
|
|
2
2
|
export { P as PoseDescriptor, t as translatePoseViaDescriptor } from './poseDescriptor-DN2yfnq_.js';
|
|
3
3
|
export { Simulation, SimulationCore, SimulationForce, SimulationNode, SimulationOptions, createSimulation } from '@weasel-js/simulation';
|
|
4
4
|
export { A as AUTO_POSE_DESCRIPTOR } from './autoPoseDescriptor-CZ1eXbzY.js';
|
|
5
|
-
export { C as CLIP_NOTHING, c as clipNothing, b as rotatePoint } from './math-
|
|
5
|
+
export { C as CLIP_NOTHING, c as clipNothing, b as rotatePoint } from './math-C_elShP_.js';
|
|
6
6
|
import '@weasel-js/animator';
|
package/dist/move.d.ts
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
export { U as UseMoveOptions } from './options-
|
|
2
|
-
import { S as SnapTarget } from './types-
|
|
3
|
-
import { M as MoveBehavior } from './clone-
|
|
1
|
+
export { U as UseMoveOptions } from './options-ClEm3SEl.js';
|
|
2
|
+
import { S as SnapTarget } from './types-p2alu-R1.js';
|
|
3
|
+
import { M as MoveBehavior } from './clone-qb2e1F5p.js';
|
|
4
4
|
import { O as OriginProjection } from './originProjection-gY0jb4Wb.js';
|
|
5
|
-
import './types-
|
|
5
|
+
import './types-49uYJN3j.js';
|
|
6
6
|
import '@weasel-js/geom';
|
|
7
7
|
import '@weasel-js/history';
|
|
8
8
|
import './poseDescriptor-DN2yfnq_.js';
|
package/dist/resize.d.ts
CHANGED
|
@@ -1,13 +1,13 @@
|
|
|
1
|
-
export { C as CORNER_ANCHORS, a as CornerAnchor, b as CornerHandle, E as EDGE_HANDLE_MIN_RADII, H as HandleEdge, R as RESIZE_HANDLE_ANCHORS, c as ResizeHandleAnchor, d as ResizeHandleLayout, U as UseResizeOptions, e as cornerPoint, f as cornerResizeHandles, g as fixedCornerOf, h as handlePoint, i as hitCornerHandle, j as isCornerHandle, r as resizeHandlesFor } from './cornerHandles-
|
|
1
|
+
export { C as CORNER_ANCHORS, a as CornerAnchor, b as CornerHandle, E as EDGE_HANDLE_MIN_RADII, H as HandleEdge, R as RESIZE_HANDLE_ANCHORS, c as ResizeHandleAnchor, d as ResizeHandleLayout, U as UseResizeOptions, e as cornerPoint, f as cornerResizeHandles, g as fixedCornerOf, h as handlePoint, i as hitCornerHandle, j as isCornerHandle, r as resizeHandlesFor } from './cornerHandles-CNCb7aTY.js';
|
|
2
2
|
export { P as PoseDescriptor, R as RECT_POSE_DESCRIPTOR, a as ROTATED_POSE_DESCRIPTOR, p as poseDescriptorForNode } from './poseDescriptor-DN2yfnq_.js';
|
|
3
|
-
import { B as BoundsConstraint } from './clone-
|
|
3
|
+
import { B as BoundsConstraint } from './clone-qb2e1F5p.js';
|
|
4
4
|
import { FramedBox } from '@weasel-js/geom';
|
|
5
5
|
export { A as AUTO_POSE_DESCRIPTOR, i as isPathLike, a as isRectPose } from './autoPoseDescriptor-CZ1eXbzY.js';
|
|
6
6
|
import '@weasel-js/routing';
|
|
7
7
|
import '@weasel-js/animator';
|
|
8
|
-
import './types-
|
|
8
|
+
import './types-49uYJN3j.js';
|
|
9
9
|
import '@weasel-js/history';
|
|
10
|
-
import './types-
|
|
10
|
+
import './types-p2alu-R1.js';
|
|
11
11
|
import '@weasel-js/react';
|
|
12
12
|
|
|
13
13
|
/** Resize constraint that stops a node shrinking past a minimum. The anchored
|
package/dist/test-seams.d.ts
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
|
-
import { E as EditAnchorsDep, P as PathEditMark, a as PathEditExtension, H as HandleLocks, S as SnapSource, b as SnapGesture, c as SnapConstrain, d as SnapResizeBox, e as SnapGeometry, f as SnapOutcomeStore, g as SnapSession } from './depSchema-
|
|
2
|
-
export { _ as _resetPaintKindsForTests } from './depSchema-
|
|
1
|
+
import { E as EditAnchorsDep, P as PathEditMark, a as PathEditExtension, H as HandleLocks, S as SnapSource, b as SnapGesture, c as SnapConstrain, d as SnapResizeBox, e as SnapGeometry, f as SnapOutcomeStore, g as SnapSession } from './depSchema-B1RS-IJx.js';
|
|
2
|
+
export { _ as _resetPaintKindsForTests } from './depSchema-B1RS-IJx.js';
|
|
3
3
|
export { _resetMarkersForTests } from '@weasel-js/stroke/test-seams';
|
|
4
|
-
import { S as Scene } from './types-
|
|
4
|
+
import { S as Scene } from './types-BEdRIhNc.js';
|
|
5
5
|
import { SelectionApi, ActionTunings, ResizeAnchor } from '@weasel-js/routing';
|
|
6
6
|
import { Op } from '@weasel-js/history';
|
|
7
7
|
import { View } from '@weasel-js/geom';
|
|
8
8
|
import { P as PoseDescriptor } from './poseDescriptor-DN2yfnq_.js';
|
|
9
|
-
import { a as PlaneMap } from './types-
|
|
9
|
+
import { a as PlaneMap } from './types-49uYJN3j.js';
|
|
10
10
|
import { O as OriginProjection } from './originProjection-gY0jb4Wb.js';
|
|
11
11
|
import '@weasel-js/paint';
|
|
12
12
|
import '@weasel-js/registry';
|
|
@@ -19,8 +19,8 @@ import '@weasel-js/cursor';
|
|
|
19
19
|
import '@weasel-js/gestures';
|
|
20
20
|
import '@weasel-js/geom/nd';
|
|
21
21
|
import '@weasel-js/animator';
|
|
22
|
-
import './types-
|
|
23
|
-
import './clone-
|
|
22
|
+
import './types-p2alu-R1.js';
|
|
23
|
+
import './clone-qb2e1F5p.js';
|
|
24
24
|
import '@weasel-js/react';
|
|
25
25
|
import '@weasel-js/quantity';
|
|
26
26
|
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
//#region src/tools/builtin/pressTarget.ts
|
|
2
|
+
/**
|
|
3
|
+
* The node a press on `hitId` selects: the outermost group above it that is
|
|
4
|
+
* below `entered`, or `hitId` itself when there is none. With nothing entered
|
|
5
|
+
* the walk runs to the root, and it does too when `entered` is not an
|
|
6
|
+
* ancestor of the hit; a press inside the entered group stops at it, so
|
|
7
|
+
* its direct members are what gets selected.
|
|
8
|
+
*/
|
|
9
|
+
function pressTarget(tree, hitId, entered) {
|
|
10
|
+
let target = hitId;
|
|
11
|
+
let id = tree.getParent(hitId);
|
|
12
|
+
let steps = 0;
|
|
13
|
+
while (id !== null && id !== entered && steps++ < 1e4) {
|
|
14
|
+
if (tree.getNode(id)?.kind === "group") target = id;
|
|
15
|
+
id = tree.getParent(id);
|
|
16
|
+
}
|
|
17
|
+
return target;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
//#endregion
|
|
21
|
+
export { pressTarget };
|
|
22
|
+
//# sourceMappingURL=pressTarget.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"pressTarget.js","names":[],"sources":["../../../src/tools/builtin/pressTarget.ts"],"sourcesContent":["/** The reads `pressTarget` makes: the tree, and each node's kind. */\nexport interface PressTargetTree {\n getParent(id: string): string | null;\n getNode(id: string): unknown;\n}\n\n/**\n * The node a press on `hitId` selects: the outermost group above it that is\n * below `entered`, or `hitId` itself when there is none. With nothing entered\n * the walk runs to the root, and it does too when `entered` is not an\n * ancestor of the hit; a press inside the entered group stops at it, so\n * its direct members are what gets selected.\n */\nexport function pressTarget(\n tree: PressTargetTree,\n hitId: string,\n entered: string | null,\n): string {\n let target = hitId;\n let id: string | null = tree.getParent(hitId);\n let steps = 0;\n while (id !== null && id !== entered && steps++ < 10_000) {\n if ((tree.getNode(id) as { kind?: string } | undefined)?.kind === 'group') target = id;\n id = tree.getParent(id);\n }\n return target;\n}\n"],"mappings":";;;;;;;;AAaA,SAAgB,YACd,MACA,OACA,SACQ;CACR,IAAI,SAAS;CACb,IAAI,KAAoB,KAAK,UAAU,KAAK;CAC5C,IAAI,QAAQ;CACZ,OAAO,OAAO,QAAQ,OAAO,WAAW,UAAU,KAAQ;EACxD,IAAK,KAAK,QAAQ,EAAE,CAAC,EAAoC,SAAS,SAAS,SAAS;EACpF,KAAK,KAAK,UAAU,EAAE;CACxB;CACA,OAAO;AACT"}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { toPlane } from "../../../core/viewport/parallax.js";
|
|
2
2
|
import { poseDescriptorForNode } from "../../../core/geometry/poseDescriptor.js";
|
|
3
3
|
import { shapeCoversPoint } from "../../../canvas/NodeShape.js";
|
|
4
|
-
import { ownClipOf } from "../../../canvas/
|
|
4
|
+
import { ownClipOf } from "../../../canvas/ownClip.js";
|
|
5
5
|
import { applyToPoint, boundsOfPath, meanScale, pathContainsPoint } from "@weasel-js/geom";
|
|
6
6
|
|
|
7
7
|
//#region src/tools/builtin/select/adapterPointQuery.ts
|
|
@@ -23,7 +23,7 @@ function adapterPointQuery(d, preciseLeaves) {
|
|
|
23
23
|
const pointQuery = (px, py, t) => ({
|
|
24
24
|
hits: (node, pose, derived, inherited) => {
|
|
25
25
|
const b = derived ? boundsOfPath(derived) : poseDescriptorForNode(d, node).getBounds(pose);
|
|
26
|
-
if (node.kind !== "
|
|
26
|
+
if (node.kind !== "group") {
|
|
27
27
|
const slop = preciseLeaves || derived ? t : 0;
|
|
28
28
|
if (!(px >= b.x - slop && px <= b.x + b.width + slop && py >= b.y - slop && py <= b.y + b.height + slop)) return false;
|
|
29
29
|
if (!preciseLeaves) return true;
|
|
@@ -34,7 +34,7 @@ function adapterPointQuery(d, preciseLeaves) {
|
|
|
34
34
|
});
|
|
35
35
|
}
|
|
36
36
|
if (!(px >= b.x && px <= b.x + b.width && py >= b.y && py <= b.y + b.height)) return false;
|
|
37
|
-
const own = ownClipOf(node, pose, derived);
|
|
37
|
+
const own = ownClipOf(node, pose, () => derived);
|
|
38
38
|
return own === null || pathContainsPoint(own, px, py);
|
|
39
39
|
},
|
|
40
40
|
clipAdmits: (clip) => pathContainsPoint(clip, px, py),
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"adapterPointQuery.js","names":[],"sources":["../../../../src/tools/builtin/select/adapterPointQuery.ts"],"sourcesContent":["import { applyToPoint, boundsOfPath, meanScale, pathContainsPoint } from '@weasel-js/geom';\nimport { shapeCoversPoint } from 'canvas/NodeShape';\nimport { ownClipOf
|
|
1
|
+
{"version":3,"file":"adapterPointQuery.js","names":[],"sources":["../../../../src/tools/builtin/select/adapterPointQuery.ts"],"sourcesContent":["import { applyToPoint, boundsOfPath, meanScale, pathContainsPoint } from '@weasel-js/geom';\nimport { shapeCoversPoint } from 'canvas/NodeShape';\nimport { ownClipOf } from 'canvas/ownClip';\nimport type { PickQuery } from 'canvas/pickWalk';\nimport { poseDescriptorForNode, type PoseDescriptor } from 'core/geometry/poseDescriptor';\nimport type { Node } from 'core/scene/types';\nimport { toPlane } from 'core/viewport/parallax';\n\n/**\n * The point query `useSelectTool`'s default `pickEvery` asks of a bare adapter:\n * a point at (px, py) with `t` world units of slop. AABB first (cheap), then\n * the painter's ink when `preciseLeaves` asks for it and it is available. A\n * painter with no `silhouette` — or one that returns null, as `kit:text` does\n * for an empty node — keeps the AABB answer.\n *\n * The AABB pre-filter is grown by the slop: a shape's outline, and the slop\n * around it, reach outside the shape's own bounds.\n *\n * A node that derives its path is tested against that path's box: its own pose\n * is a placeholder, and the box of a straight route has no area to land in\n * without the slop.\n */\nexport function adapterPointQuery<TPose>(\n d: PoseDescriptor<TPose>,\n preciseLeaves: boolean,\n): (px: number, py: number, t: number) => PickQuery<TPose> {\n const pointQuery = (px: number, py: number, t: number): PickQuery<TPose> => ({\n hits: (node, pose, derived, inherited) => {\n const b = derived ? boundsOfPath(derived) : poseDescriptorForNode(d, node).getBounds(pose);\n if (node.kind !== 'group') {\n const slop = preciseLeaves || derived ? t : 0;\n if (!(px >= b.x - slop && px <= b.x + b.width + slop\n && py >= b.y - slop && py <= b.y + b.height + slop)) {\n return false;\n }\n if (!preciseLeaves) return true;\n return shapeCoversPoint(\n node as Node<unknown, string, TPose>, pose, px, py, { tolerance: t, derivedPath: derived, inherited },\n );\n }\n // A group paints nothing and is hit across its box, within its own clip\n // when it declares one, since only what shows there is its. Ancestor\n // clips are the walk's business.\n const inAabb = px >= b.x && px <= b.x + b.width\n && py >= b.y && py <= b.y + b.height;\n if (!inAabb) return false;\n const own = ownClipOf(node, pose, () => derived);\n return own === null || pathContainsPoint(own, px, py);\n },\n clipAdmits: (clip) => pathContainsPoint(clip, px, py),\n inPlane: (m) => {\n const p = toPlane(m, { x: px, y: py });\n return pointQuery(p.x, p.y, t * meanScale(m.scale));\n },\n inCopy: (m) => {\n const [x, y] = applyToPoint(m, px, py);\n return pointQuery(x, y, t * Math.sqrt(Math.abs(m[0] * m[3] - m[1] * m[2])));\n },\n });\n return pointQuery;\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;AAsBA,SAAgB,kBACd,GACA,eACyD;CACzD,MAAM,cAAc,IAAY,IAAY,OAAiC;EAC3E,OAAO,MAAM,MAAM,SAAS,cAAc;GACxC,MAAM,IAAI,UAAU,aAAa,OAAO,IAAI,sBAAsB,GAAG,IAAI,CAAC,CAAC,UAAU,IAAI;GACzF,IAAI,KAAK,SAAS,SAAS;IACzB,MAAM,OAAO,iBAAiB,UAAU,IAAI;IAC5C,IAAI,EAAE,MAAM,EAAE,IAAI,QAAQ,MAAM,EAAE,IAAI,EAAE,QAAQ,QACvC,MAAM,EAAE,IAAI,QAAQ,MAAM,EAAE,IAAI,EAAE,SAAS,OAClD,OAAO;IAET,IAAI,CAAC,eAAe,OAAO;IAC3B,OAAO,iBACL,MAAsC,MAAM,IAAI,IAAI;KAAE,WAAW;KAAG,aAAa;KAAS;IAAU,CACtG;GACF;GAMA,IAAI,EAFW,MAAM,EAAE,KAAK,MAAM,EAAE,IAAI,EAAE,SACrC,MAAM,EAAE,KAAK,MAAM,EAAE,IAAI,EAAE,SACnB,OAAO;GACpB,MAAM,MAAM,UAAU,MAAM,YAAY,OAAO;GAC/C,OAAO,QAAQ,QAAQ,kBAAkB,KAAK,IAAI,EAAE;EACtD;EACA,aAAa,SAAS,kBAAkB,MAAM,IAAI,EAAE;EACpD,UAAU,MAAM;GACd,MAAM,IAAI,QAAQ,GAAG;IAAE,GAAG;IAAI,GAAG;GAAG,CAAC;GACrC,OAAO,WAAW,EAAE,GAAG,EAAE,GAAG,IAAI,UAAU,EAAE,KAAK,CAAC;EACpD;EACA,SAAS,MAAM;GACb,MAAM,CAAC,GAAG,KAAK,aAAa,GAAG,IAAI,EAAE;GACrC,OAAO,WAAW,GAAG,GAAG,IAAI,KAAK,KAAK,KAAK,IAAI,EAAE,KAAK,EAAE,KAAK,EAAE,KAAK,EAAE,EAAE,CAAC,CAAC;EAC5E;CACF;CACA,OAAO;AACT"}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import { ActionDisabledReason } from "@weasel-js/routing";
|
|
2
|
+
|
|
3
|
+
//#region src/tools/builtin/select/collapseDeferred.ts
|
|
4
|
+
/**
|
|
5
|
+
* `select.collapseDeferred` — the release half of the deferred multi-click.
|
|
6
|
+
*
|
|
7
|
+
* Pressing an already-selected node inside a multi-selection leaves the
|
|
8
|
+
* selection alone so the press can start a drag of the whole set. If the
|
|
9
|
+
* gesture ends without a drag, this collapses to the clicked node.
|
|
10
|
+
*/
|
|
11
|
+
function createCollapseDeferredAction(pressRef) {
|
|
12
|
+
return {
|
|
13
|
+
id: "select.collapseDeferred",
|
|
14
|
+
label: "Select — collapse deferred click",
|
|
15
|
+
eligible: { capability: "creates-selection" },
|
|
16
|
+
requires: ["selection"],
|
|
17
|
+
enabled: () => pressRef.current.deferredClickId !== null ? true : ActionDisabledReason.NotApplicable,
|
|
18
|
+
invoker: {
|
|
19
|
+
timing: "immediate",
|
|
20
|
+
run: (deps, params) => {
|
|
21
|
+
const deferred = pressRef.current.deferredClickId;
|
|
22
|
+
pressRef.current = { deferredClickId: null };
|
|
23
|
+
if (deferred === null) return;
|
|
24
|
+
const selection = deps.selection;
|
|
25
|
+
if (!selection) return;
|
|
26
|
+
const p = params;
|
|
27
|
+
selection.applyClick(deferred, p?.mods ?? {
|
|
28
|
+
alt: false,
|
|
29
|
+
ctrl: false,
|
|
30
|
+
meta: false,
|
|
31
|
+
shift: false
|
|
32
|
+
});
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
};
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
//#endregion
|
|
39
|
+
export { createCollapseDeferredAction };
|
|
40
|
+
//# sourceMappingURL=collapseDeferred.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"collapseDeferred.js","names":[],"sources":["../../../../src/tools/builtin/select/collapseDeferred.ts"],"sourcesContent":["import type { RefObject } from 'react';\nimport type { Action } from '@weasel-js/routing';\nimport { ActionDisabledReason } from '@weasel-js/routing';\nimport type { NodeId } from 'core/scene/types';\n\n/**\n * `select.collapseDeferred` — the release half of the deferred multi-click.\n *\n * Pressing an already-selected node inside a multi-selection leaves the\n * selection alone so the press can start a drag of the whole set. If the\n * gesture ends without a drag, this collapses to the clicked node.\n */\nexport function createCollapseDeferredAction(\n pressRef: RefObject<{ deferredClickId: string | null }>,\n): Action {\n return {\n id: 'select.collapseDeferred',\n label: 'Select — collapse deferred click',\n eligible: { capability: 'creates-selection' },\n requires: ['selection'],\n // Declining when there is nothing deferred is what keeps this binding\n // from swallowing every click on a node. It is bound at ACTIVE scope, so\n // without the gate it would outrank ambient click bindings like\n // `selectAnchor` and silently break anchor selection.\n enabled: () =>\n pressRef.current.deferredClickId !== null\n ? true\n : ActionDisabledReason.NotApplicable,\n invoker: {\n timing: 'immediate' as const,\n run: (deps, params) => {\n const deferred = pressRef.current.deferredClickId;\n pressRef.current = { deferredClickId: null };\n if (deferred === null) return;\n const selection = deps.selection;\n if (!selection) return;\n const p = params as {\n mods?: { alt: boolean; ctrl: boolean; meta: boolean; shift: boolean };\n } | undefined;\n selection.applyClick(\n deferred as NodeId,\n p?.mods ?? { alt: false, ctrl: false, meta: false, shift: false },\n );\n },\n },\n };\n}\n"],"mappings":";;;;;;;;;;AAYA,SAAgB,6BACd,UACQ;CACR,OAAO;EACL,IAAI;EACJ,OAAO;EACP,UAAU,EAAE,YAAY,oBAAoB;EAC5C,UAAU,CAAC,WAAW;EAKtB,eACE,SAAS,QAAQ,oBAAoB,OACjC,OACA,qBAAqB;EAC3B,SAAS;GACP,QAAQ;GACR,MAAM,MAAM,WAAW;IACrB,MAAM,WAAW,SAAS,QAAQ;IAClC,SAAS,UAAU,EAAE,iBAAiB,KAAK;IAC3C,IAAI,aAAa,MAAM;IACvB,MAAM,YAAY,KAAK;IACvB,IAAI,CAAC,WAAW;IAChB,MAAM,IAAI;IAGV,UAAU,WACR,UACA,GAAG,QAAQ;KAAE,KAAK;KAAO,MAAM;KAAO,MAAM;KAAO,OAAO;IAAM,CAClE;GACF;EACF;CACF;AACF"}
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
import { onBody, onEmptyCanvas } from "./selectionContributions.js";
|
|
2
|
+
import { createEnterGroupAction, createLeaveGroupAction } from "../../../interactions/actions/defaults/enterGroup.js";
|
|
3
|
+
import { useMemo } from "react";
|
|
4
|
+
import { useLatest } from "@weasel-js/react";
|
|
5
|
+
|
|
6
|
+
//#region src/tools/builtin/select/groupEntry.ts
|
|
7
|
+
/** Id of the entry {@link createGroupEntryContribution} builds. */
|
|
8
|
+
var GROUP_ENTRY_ID = "group.entry";
|
|
9
|
+
var INITIAL = [{
|
|
10
|
+
channel: "*",
|
|
11
|
+
phase: "initial"
|
|
12
|
+
}];
|
|
13
|
+
var ANY_MODS = {
|
|
14
|
+
shift: "optional",
|
|
15
|
+
alt: "optional",
|
|
16
|
+
meta: "optional",
|
|
17
|
+
ctrl: "optional"
|
|
18
|
+
};
|
|
19
|
+
/**
|
|
20
|
+
* `group.enter` and `group.leave`, bound under any tool that leaves the gesture unclaimed. A
|
|
21
|
+
* double-click on a body enters the group a press there would select, and declines where that is
|
|
22
|
+
* no group, so text and path editing keep their double-click. Escape leaves one level, carrying
|
|
23
|
+
* the phase `escape` does and declining with nothing entered, which leaves Escape to `escape`.
|
|
24
|
+
* Leaving on a press is the select tool's ({@link GROUP_LEAVE_PRESS_BINDING}): under a drawing
|
|
25
|
+
* tool a press outside the group draws into it.
|
|
26
|
+
*/
|
|
27
|
+
function createGroupEntryContribution(opts) {
|
|
28
|
+
return {
|
|
29
|
+
id: GROUP_ENTRY_ID,
|
|
30
|
+
eligibility: { always: true },
|
|
31
|
+
actions: [createEnterGroupAction(opts), createLeaveGroupAction(opts)],
|
|
32
|
+
bindings: [{
|
|
33
|
+
spec: {
|
|
34
|
+
kind: "doubleClick",
|
|
35
|
+
target: { kindOf: onBody },
|
|
36
|
+
phase: INITIAL
|
|
37
|
+
},
|
|
38
|
+
actionId: "group.enter",
|
|
39
|
+
opts: { id: "group.enter" }
|
|
40
|
+
}, {
|
|
41
|
+
spec: {
|
|
42
|
+
kind: "key",
|
|
43
|
+
key: "Escape",
|
|
44
|
+
phase: INITIAL
|
|
45
|
+
},
|
|
46
|
+
actionId: "group.leave",
|
|
47
|
+
opts: {
|
|
48
|
+
id: "group.leave.key",
|
|
49
|
+
yields: true
|
|
50
|
+
}
|
|
51
|
+
}]
|
|
52
|
+
};
|
|
53
|
+
}
|
|
54
|
+
/** {@link createGroupEntryContribution} for the life of a canvas, reading its pickers live. */
|
|
55
|
+
function useGroupEntryContribution(opts) {
|
|
56
|
+
const live = useLatest(opts);
|
|
57
|
+
return useMemo(() => createGroupEntryContribution({
|
|
58
|
+
pickHit: (x, y) => live.current.pickHit(x, y),
|
|
59
|
+
tree: {
|
|
60
|
+
getParent: (id) => live.current.tree.getParent(id),
|
|
61
|
+
getNode: (id) => live.current.tree.getNode(id)
|
|
62
|
+
},
|
|
63
|
+
boxOf: (id) => live.current.boxOf(id)
|
|
64
|
+
}), [live]);
|
|
65
|
+
}
|
|
66
|
+
/**
|
|
67
|
+
* A press outside the entered group's box leaves it, whatever modifiers are held. Nodes outside
|
|
68
|
+
* the group take no hits, so every such press lands on empty canvas.
|
|
69
|
+
*/
|
|
70
|
+
var GROUP_LEAVE_PRESS_BINDING = {
|
|
71
|
+
spec: {
|
|
72
|
+
kind: "pointerDown",
|
|
73
|
+
target: { kindOf: onEmptyCanvas },
|
|
74
|
+
mods: ANY_MODS
|
|
75
|
+
},
|
|
76
|
+
actionId: "group.leave",
|
|
77
|
+
opts: { id: "group.leave.press" }
|
|
78
|
+
};
|
|
79
|
+
|
|
80
|
+
//#endregion
|
|
81
|
+
export { GROUP_ENTRY_ID, GROUP_LEAVE_PRESS_BINDING, createGroupEntryContribution, useGroupEntryContribution };
|
|
82
|
+
//# sourceMappingURL=groupEntry.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"groupEntry.js","names":[],"sources":["../../../../src/tools/builtin/select/groupEntry.ts"],"sourcesContent":["import { useMemo } from 'react';\nimport { useLatest } from '@weasel-js/react';\nimport {\n createEnterGroupAction, createLeaveGroupAction, type EnterGroupOptions, type LeaveGroupOptions,\n} from '../../../interactions/actions/defaults/enterGroup';\nimport type { Contribution } from '../../overlayBinding';\nimport { onBody, onEmptyCanvas } from './selectionContributions';\n\n/** Id of the entry {@link createGroupEntryContribution} builds. */\nexport const GROUP_ENTRY_ID = 'group.entry';\n\nconst INITIAL = [{ channel: '*', phase: 'initial' as const }];\nconst ANY_MODS = { shift: 'optional', alt: 'optional', meta: 'optional', ctrl: 'optional' } as const;\n\n/**\n * `group.enter` and `group.leave`, bound under any tool that leaves the gesture unclaimed. A\n * double-click on a body enters the group a press there would select, and declines where that is\n * no group, so text and path editing keep their double-click. Escape leaves one level, carrying\n * the phase `escape` does and declining with nothing entered, which leaves Escape to `escape`.\n * Leaving on a press is the select tool's ({@link GROUP_LEAVE_PRESS_BINDING}): under a drawing\n * tool a press outside the group draws into it.\n */\nexport function createGroupEntryContribution(opts: EnterGroupOptions & LeaveGroupOptions): Contribution {\n return {\n id: GROUP_ENTRY_ID,\n eligibility: { always: true },\n actions: [createEnterGroupAction(opts), createLeaveGroupAction(opts)],\n bindings: [\n {\n spec: { kind: 'doubleClick', target: { kindOf: onBody }, phase: INITIAL },\n actionId: 'group.enter',\n opts: { id: 'group.enter' },\n },\n {\n spec: { kind: 'key', key: 'Escape', phase: INITIAL },\n actionId: 'group.leave',\n opts: { id: 'group.leave.key', yields: true },\n },\n ],\n };\n}\n\n/** {@link createGroupEntryContribution} for the life of a canvas, reading its pickers live. */\nexport function useGroupEntryContribution(opts: EnterGroupOptions & LeaveGroupOptions): Contribution {\n const live = useLatest(opts);\n return useMemo(() => createGroupEntryContribution({\n pickHit: (x, y) => live.current.pickHit(x, y),\n tree: {\n getParent: (id) => live.current.tree.getParent(id),\n getNode: (id) => live.current.tree.getNode(id),\n },\n boxOf: (id) => live.current.boxOf(id),\n }), [live]);\n}\n\n/**\n * A press outside the entered group's box leaves it, whatever modifiers are held. Nodes outside\n * the group take no hits, so every such press lands on empty canvas.\n */\nexport const GROUP_LEAVE_PRESS_BINDING = {\n spec: { kind: 'pointerDown' as const, target: { kindOf: onEmptyCanvas }, mods: ANY_MODS },\n actionId: 'group.leave',\n opts: { id: 'group.leave.press' },\n};\n"],"mappings":";;;;;;;AASA,IAAa,iBAAiB;AAE9B,IAAM,UAAU,CAAC;CAAE,SAAS;CAAK,OAAO;AAAmB,CAAC;AAC5D,IAAM,WAAW;CAAE,OAAO;CAAY,KAAK;CAAY,MAAM;CAAY,MAAM;AAAW;;;;;;;;;AAU1F,SAAgB,6BAA6B,MAA2D;CACtG,OAAO;EACL,IAAI;EACJ,aAAa,EAAE,QAAQ,KAAK;EAC5B,SAAS,CAAC,uBAAuB,IAAI,GAAG,uBAAuB,IAAI,CAAC;EACpE,UAAU,CACR;GACE,MAAM;IAAE,MAAM;IAAe,QAAQ,EAAE,QAAQ,OAAO;IAAG,OAAO;GAAQ;GACxE,UAAU;GACV,MAAM,EAAE,IAAI,cAAc;EAC5B,GACA;GACE,MAAM;IAAE,MAAM;IAAO,KAAK;IAAU,OAAO;GAAQ;GACnD,UAAU;GACV,MAAM;IAAE,IAAI;IAAmB,QAAQ;GAAK;EAC9C,CACF;CACF;AACF;;AAGA,SAAgB,0BAA0B,MAA2D;CACnG,MAAM,OAAO,UAAU,IAAI;CAC3B,OAAO,cAAc,6BAA6B;EAChD,UAAU,GAAG,MAAM,KAAK,QAAQ,QAAQ,GAAG,CAAC;EAC5C,MAAM;GACJ,YAAY,OAAO,KAAK,QAAQ,KAAK,UAAU,EAAE;GACjD,UAAU,OAAO,KAAK,QAAQ,KAAK,QAAQ,EAAE;EAC/C;EACA,QAAQ,OAAO,KAAK,QAAQ,MAAM,EAAE;CACtC,CAAC,GAAG,CAAC,IAAI,CAAC;AACZ;;;;;AAMA,IAAa,4BAA4B;CACvC,MAAM;EAAE,MAAM;EAAwB,QAAQ,EAAE,QAAQ,cAAc;EAAG,MAAM;CAAS;CACxF,UAAU;CACV,MAAM,EAAE,IAAI,oBAAoB;AAClC"}
|
|
@@ -12,7 +12,7 @@ import { selectedAncestor } from "../../../interactions/actions/selectionSubtree
|
|
|
12
12
|
* picks the member out of it.
|
|
13
13
|
*/
|
|
14
14
|
function pressDecision(top, sel, mods, extendLocked, tree) {
|
|
15
|
-
const isExtend = mods.shift
|
|
15
|
+
const isExtend = mods.shift;
|
|
16
16
|
const inMultiSelection = sel.includes(top) && sel.length > 1;
|
|
17
17
|
const defer = !isExtend && (inMultiSelection || insideSelectedGroup(top, sel, tree));
|
|
18
18
|
return {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"pressDecision.js","names":[],"sources":["../../../../src/tools/builtin/select/pressDecision.ts"],"sourcesContent":["import { isEnvelope } from 'core/scene/effectivePose';\nimport { selectedAncestor } from '../../../interactions/actions/selectionSubtree';\n\n/** The tree a press reads: who a node's parent is, and the node itself. */\nexport interface PressTree {\n getParent(id: string): string | null;\n getNode(id: string): unknown;\n}\n\n/** What a press on `top` does to the selection. */\nexport interface PressDecision {\n /** Apply the click to the selection on the press. */\n applyNow: boolean;\n /** The id the click is held back for until release, so the press can drag\n * what is already selected instead; `null` when nothing is held back. */\n deferred: string | null;\n}\n\n/**\n * Decide a press with no extend modifier on `top`, and one with it.\n *\n * Pressing a member of a multi-selection, or a node inside a selected group,\n * leaves the selection alone: the press may be the start of a drag of the\n * set, or of the group. A group here is a container whose pose derives from\n * its children; one with its own pose is a place its members sit, and a press\n * picks the member out of it.\n */\nexport function pressDecision(\n top: string,\n sel: readonly string[],\n mods: { shift: boolean; meta: boolean },\n extendLocked: () => boolean,\n tree: PressTree,\n): PressDecision {\n const isExtend = mods.shift
|
|
1
|
+
{"version":3,"file":"pressDecision.js","names":[],"sources":["../../../../src/tools/builtin/select/pressDecision.ts"],"sourcesContent":["import { isEnvelope } from 'core/scene/effectivePose';\nimport { selectedAncestor } from '../../../interactions/actions/selectionSubtree';\n\n/** The tree a press reads: who a node's parent is, and the node itself. */\nexport interface PressTree {\n getParent(id: string): string | null;\n getNode(id: string): unknown;\n}\n\n/** What a press on `top` does to the selection. */\nexport interface PressDecision {\n /** Apply the click to the selection on the press. */\n applyNow: boolean;\n /** The id the click is held back for until release, so the press can drag\n * what is already selected instead; `null` when nothing is held back. */\n deferred: string | null;\n}\n\n/**\n * Decide a press with no extend modifier on `top`, and one with it.\n *\n * Pressing a member of a multi-selection, or a node inside a selected group,\n * leaves the selection alone: the press may be the start of a drag of the\n * set, or of the group. A group here is a container whose pose derives from\n * its children; one with its own pose is a place its members sit, and a press\n * picks the member out of it.\n */\nexport function pressDecision(\n top: string,\n sel: readonly string[],\n mods: { shift: boolean; meta: boolean },\n extendLocked: () => boolean,\n tree: PressTree,\n): PressDecision {\n const isExtend = mods.shift;\n const inMultiSelection = sel.includes(top) && sel.length > 1;\n const defer = !isExtend && (inMultiSelection || insideSelectedGroup(top, sel, tree));\n return { applyNow: !defer && !(isExtend && extendLocked()), deferred: defer ? top : null };\n}\n\nfunction insideSelectedGroup(top: string, sel: readonly string[], tree: PressTree): boolean {\n if (sel.includes(top)) return false;\n const ancestor = selectedAncestor((id) => tree.getParent(id), top, new Set(sel));\n const node = ancestor === null ? undefined : tree.getNode(ancestor) as { dependsOn?: unknown; derivePose?: unknown } | undefined;\n return node !== undefined && isEnvelope(node);\n}\n"],"mappings":";;;;;;;;;;;;;AA2BA,SAAgB,cACd,KACA,KACA,MACA,cACA,MACe;CACf,MAAM,WAAW,KAAK;CACtB,MAAM,mBAAmB,IAAI,SAAS,GAAG,KAAK,IAAI,SAAS;CAC3D,MAAM,QAAQ,CAAC,aAAa,oBAAoB,oBAAoB,KAAK,KAAK,IAAI;CAClF,OAAO;EAAE,UAAU,CAAC,SAAS,EAAE,YAAY,aAAa;EAAI,UAAU,QAAQ,MAAM;CAAK;AAC3F;AAEA,SAAS,oBAAoB,KAAa,KAAwB,MAA0B;CAC1F,IAAI,IAAI,SAAS,GAAG,GAAG,OAAO;CAC9B,MAAM,WAAW,kBAAkB,OAAO,KAAK,UAAU,EAAE,GAAG,KAAK,IAAI,IAAI,GAAG,CAAC;CAC/E,MAAM,OAAO,aAAa,OAAO,SAAY,KAAK,QAAQ,QAAQ;CAClE,OAAO,SAAS,UAAa,WAAW,IAAI;AAC9C"}
|