refactor: extract block menu overlay layout
This commit is contained in:
@@ -0,0 +1,100 @@
|
||||
//! Block menu hover anchor / layout helper。
|
||||
//!
|
||||
//! 本模块承接 block menu 的锚点与面板布局计算,保持几何计算与 DOM 查询分离。
|
||||
//! 调用方(lib.rs)保留 DOM 查询(editor_stage_element),本模块只做纯几何计算。
|
||||
//!
|
||||
//! 迁入自 lib.rs:
|
||||
//! - `hover_anchor_top`
|
||||
//! - `hover_anchor_transform`
|
||||
//! - `block_menu_layout`(参数化版)
|
||||
|
||||
use crate::editor_runtime::block_hover_state::{BlockMenuLayout, HoveredBlockState};
|
||||
|
||||
/// 多行块的最小高度阈值,超过此值视作多行。
|
||||
const HANDLE_MULTILINE_HEIGHT: f64 = 40.0;
|
||||
|
||||
/// handle shell 菜单左侧最小限制。
|
||||
const HANDLE_MENU_LEFT_LIMIT: f64 = -320.0;
|
||||
|
||||
/// 内容列水平内边距(与 lib.rs 中同名常量同值)。
|
||||
const CONTENT_COLUMN_HORIZONTAL_PADDING: f64 = 48.0;
|
||||
|
||||
/// 内容列最大宽度(与 lib.rs 中同名常量同值)。
|
||||
const CONTENT_COLUMN_MAX_WIDTH: f64 = 708.0;
|
||||
|
||||
/// 根据 stage 宽度计算实际内容列宽。
|
||||
fn content_column_width(stage_rect_width: f64) -> f64 {
|
||||
let available = (stage_rect_width - (CONTENT_COLUMN_HORIZONTAL_PADDING * 2.0)).max(0.0);
|
||||
available.min(CONTENT_COLUMN_MAX_WIDTH)
|
||||
}
|
||||
|
||||
/// 内容列左侧相对于 stage 的偏移。
|
||||
fn content_column_left(stage_rect_width: f64) -> f64 {
|
||||
let column_width = content_column_width(stage_rect_width);
|
||||
((stage_rect_width - column_width) / 2.0).max(0.0)
|
||||
}
|
||||
|
||||
/// 内容文本区左侧相对于 stage 的偏移(列左内边距后)。
|
||||
fn content_text_left(stage_rect_width: f64) -> f64 {
|
||||
content_column_left(stage_rect_width) + CONTENT_COLUMN_HORIZONTAL_PADDING
|
||||
}
|
||||
|
||||
/// 计算 handle shell 锚点在 stage 内的垂直位置。
|
||||
pub(crate) fn hover_anchor_top(block: &HoveredBlockState) -> f64 {
|
||||
if block.height > HANDLE_MULTILINE_HEIGHT {
|
||||
block.top + 12.0
|
||||
} else {
|
||||
block.top + (block.height / 2.0) - 32.0
|
||||
}
|
||||
}
|
||||
|
||||
/// 计算 handle shell 锚点 transform 值(当前固定返回 "none")。
|
||||
pub(crate) fn hover_anchor_transform(_block: &HoveredBlockState) -> &'static str {
|
||||
"none"
|
||||
}
|
||||
|
||||
/// 计算 block menu 面板布局。
|
||||
///
|
||||
/// 参数化版本:调用方提供 stage 的 bounding client rect 尺寸和位置,
|
||||
/// 本函数做纯几何计算,不查询 DOM。
|
||||
pub(crate) fn block_menu_layout(
|
||||
block: &HoveredBlockState,
|
||||
stage_width: f64,
|
||||
stage_height: f64,
|
||||
stage_top: f64,
|
||||
stage_left: f64,
|
||||
) -> Option<BlockMenuLayout> {
|
||||
let shell_top = hover_anchor_top(block);
|
||||
let stage_padding = 18.0;
|
||||
let space_above = (shell_top - stage_padding).max(0.0);
|
||||
let space_below = (stage_height - shell_top - stage_padding).max(0.0);
|
||||
let open_upward = space_below < 340.0 && space_above > space_below;
|
||||
let available_height = if open_upward {
|
||||
space_above
|
||||
} else {
|
||||
space_below
|
||||
};
|
||||
let max_height = if available_height >= 220.0 {
|
||||
available_height
|
||||
} else {
|
||||
available_height.max(160.0)
|
||||
};
|
||||
let menu_width = 242.0;
|
||||
let menu_stage_left =
|
||||
(content_text_left(stage_width) - menu_width - 132.0).max(HANDLE_MENU_LEFT_LIMIT);
|
||||
let viewport_shell_top = stage_top + shell_top;
|
||||
let top = if open_upward {
|
||||
viewport_shell_top + 64.0 - max_height
|
||||
} else {
|
||||
viewport_shell_top
|
||||
}
|
||||
.max(8.0);
|
||||
let left = (stage_left + menu_stage_left).max(8.0);
|
||||
|
||||
Some(BlockMenuLayout {
|
||||
max_height,
|
||||
open_upward,
|
||||
top,
|
||||
left,
|
||||
})
|
||||
}
|
||||
@@ -2,6 +2,7 @@ pub(crate) mod attachment_links;
|
||||
pub(crate) mod attachment_upload;
|
||||
pub(crate) mod block_hover_state;
|
||||
pub(crate) mod block_menu_document;
|
||||
pub(crate) mod block_menu_overlay;
|
||||
pub(crate) mod block_menu_legacy_html;
|
||||
pub(crate) mod command_sync;
|
||||
pub(crate) mod dom_selection;
|
||||
|
||||
Reference in New Issue
Block a user