refactor: extract editor dnd helpers

This commit is contained in:
lix-2026
2026-05-25 03:05:57 +08:00
parent fa3dc76d53
commit 3361a5e1b1
8 changed files with 173 additions and 38 deletions
@@ -0,0 +1,92 @@
//! Block handle 拖拽命中的纯几何 helper。
//!
//! 本模块不查询 DOM、不处理 Leptos signal,也不分发 editor command
//! 调用方负责传入已经从页面中读取的坐标。
use crate::editor_runtime::block_hover_state::DropPlacement;
pub(crate) struct HandleCorridorGeometry {
pub(crate) stage_left: f64,
pub(crate) stage_top: f64,
pub(crate) block_left: f64,
pub(crate) block_top: f64,
pub(crate) block_bottom: f64,
pub(crate) handle_left: f64,
pub(crate) handle_right: f64,
}
pub(crate) fn pointer_in_handle_corridor_geometry(
client_x: i32,
client_y: i32,
geometry: &HandleCorridorGeometry,
) -> bool {
let x = f64::from(client_x) - geometry.stage_left;
let y = f64::from(client_y) - geometry.stage_top;
let corridor_left = (geometry.handle_left - 12.0).min(geometry.block_left);
let corridor_right = (geometry.block_left + 18.0).max(geometry.handle_right + 12.0);
x >= corridor_left
&& x <= corridor_right
&& y >= geometry.block_top - 18.0
&& y <= geometry.block_bottom + 18.0
}
pub(crate) fn drop_placement_from_block_point(
client_y: i32,
block_top: f64,
block_height: f64,
) -> DropPlacement {
let midpoint = block_top + (block_height / 2.0);
if f64::from(client_y) <= midpoint {
DropPlacement::Before
} else {
DropPlacement::After
}
}
pub(crate) fn drop_indicator_top(
stage_top: f64,
block_top: f64,
block_bottom: f64,
placement: DropPlacement,
) -> f64 {
match placement {
DropPlacement::Before => block_top - stage_top,
DropPlacement::After => block_bottom - stage_top,
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn pointer_corridor_accepts_handle_lane_and_block_edge() {
let geometry = HandleCorridorGeometry {
stage_left: 100.0,
stage_top: 20.0,
block_left: 140.0,
block_top: 40.0,
block_bottom: 80.0,
handle_left: 108.0,
handle_right: 130.0,
};
assert!(pointer_in_handle_corridor_geometry(118, 80, &geometry));
assert!(pointer_in_handle_corridor_geometry(250, 80, &geometry));
assert!(!pointer_in_handle_corridor_geometry(90, 80, &geometry));
assert!(!pointer_in_handle_corridor_geometry(118, 28, &geometry));
}
#[test]
fn drop_placement_splits_block_at_midpoint() {
assert_eq!(
drop_placement_from_block_point(50, 40.0, 20.0),
DropPlacement::Before
);
assert_eq!(
drop_placement_from_block_point(51, 40.0, 20.0),
DropPlacement::After
);
}
}
@@ -1,5 +1,6 @@
pub(crate) mod attachment_links;
pub(crate) mod attachment_upload;
pub(crate) mod block_dnd;
pub(crate) mod block_hover_state;
pub(crate) mod block_menu_document;
pub(crate) mod block_menu_overlay;
+20 -16
View File
@@ -21,12 +21,16 @@ use web_sys::{
};
use editor_runtime::attachment_upload::dispatch_editor_upload_request;
use editor_runtime::block_dnd::{
drop_indicator_top, drop_placement_from_block_point, pointer_in_handle_corridor_geometry,
HandleCorridorGeometry,
};
use editor_runtime::block_menu_overlay;
use editor_runtime::block_menu_legacy_html::{
duplicate_top_level_block_html, reorder_top_level_block_html,
};
use editor_runtime::block_hover_state::{
BlockMenuLayout, DropIndicatorState, DropPlacement, HoveredBlockState, PendingDragState,
BlockMenuLayout, DropIndicatorState, HoveredBlockState, PendingDragState,
};
use editor_runtime::command_sync::{
read_editor_snapshot, sync_editor_outputs, sync_persisted_editor_command,
@@ -5416,8 +5420,6 @@ fn pointer_in_handle_corridor(client_x: i32, client_y: i32, block: &HoveredBlock
let block_left = block_rect.left() - stage_rect.left();
let block_top = block_rect.top() - stage_rect.top();
let block_bottom = block_rect.bottom() - stage_rect.top();
let x = f64::from(client_x) - stage_rect.left();
let y = f64::from(client_y) - stage_rect.top();
let handle_left = handle_rect
.as_ref()
.map(|rect| rect.left() - stage_rect.left())
@@ -5426,10 +5428,20 @@ fn pointer_in_handle_corridor(client_x: i32, client_y: i32, block: &HoveredBlock
.as_ref()
.map(|rect| rect.right() - stage_rect.left())
.unwrap_or_else(|| handle_left + HANDLE_TRIGGER_WIDTH);
let corridor_left = (handle_left - 12.0).min(block_left);
let corridor_right = (block_left + 18.0).max(handle_right + 12.0);
x >= corridor_left && x <= corridor_right && y >= block_top - 18.0 && y <= block_bottom + 18.0
pointer_in_handle_corridor_geometry(
client_x,
client_y,
&HandleCorridorGeometry {
stage_left: stage_rect.left(),
stage_top: stage_rect.top(),
block_left,
block_top,
block_bottom,
handle_left,
handle_right,
},
)
}
fn drop_indicator_from_target(
@@ -5442,16 +5454,8 @@ fn drop_indicator_from_target(
let stage = editor_stage_element()?;
let block_rect = block.get_bounding_client_rect();
let stage_rect = stage.get_bounding_client_rect();
let midpoint = block_rect.top() + (block_rect.height() / 2.0);
let placement = if f64::from(client_y) <= midpoint {
DropPlacement::Before
} else {
DropPlacement::After
};
let top = match placement {
DropPlacement::Before => block_rect.top() - stage_rect.top(),
DropPlacement::After => block_rect.bottom() - stage_rect.top(),
};
let placement = drop_placement_from_block_point(client_y, block_rect.top(), block_rect.height());
let top = drop_indicator_top(stage_rect.top(), block_rect.top(), block_rect.bottom(), placement);
Some(DropIndicatorState {
index: hovered.index,