颜色修复

This commit is contained in:
lix-2026
2026-04-19 22:09:13 +08:00
parent 394e2a155c
commit 1e686bfa3c
+225 -200
View File
@@ -1209,6 +1209,16 @@ fn target_element(target: web_sys::EventTarget) -> Option<Element> {
}) })
} }
fn event_target_matches_selector(
target: Option<web_sys::EventTarget>,
selector: &str,
) -> bool {
target
.and_then(target_element)
.and_then(|element| element.closest(selector).ok().flatten())
.is_some()
}
fn current_target_html_element(event: &WheelEvent) -> Option<HtmlElement> { fn current_target_html_element(event: &WheelEvent) -> Option<HtmlElement> {
event.current_target()?.dyn_into::<HtmlElement>().ok() event.current_target()?.dyn_into::<HtmlElement>().ok()
} }
@@ -2250,6 +2260,8 @@ fn App() -> impl IntoView {
let (text_selection_active, set_text_selection_active) = signal(false); let (text_selection_active, set_text_selection_active) = signal(false);
let (floating_toolbar_anchor, set_floating_toolbar_anchor) = let (floating_toolbar_anchor, set_floating_toolbar_anchor) =
signal(None::<FloatingToolbarAnchor>); signal(None::<FloatingToolbarAnchor>);
let (locked_toolbar_anchor, set_locked_toolbar_anchor) =
signal(None::<FloatingToolbarAnchor>);
let (slash_open, set_slash_open) = signal(false); let (slash_open, set_slash_open) = signal(false);
let (slash_index, set_slash_index) = signal(0_usize); let (slash_index, set_slash_index) = signal(0_usize);
let (turn_into_open, set_turn_into_open) = signal(false); let (turn_into_open, set_turn_into_open) = signal(false);
@@ -2294,6 +2306,18 @@ fn App() -> impl IntoView {
}); });
} }
{
let turn_into_open = turn_into_open;
let color_menu_open = color_menu_open;
let more_menu_open = more_menu_open;
let set_locked_toolbar_anchor = set_locked_toolbar_anchor;
Effect::new(move |_| {
if !(turn_into_open.get() || color_menu_open.get() || more_menu_open.get()) {
set_locked_toolbar_anchor.set(None);
}
});
}
{ {
let editor_focused = editor_focused; let editor_focused = editor_focused;
let text_selection_active = text_selection_active; let text_selection_active = text_selection_active;
@@ -2311,8 +2335,7 @@ fn App() -> impl IntoView {
color_menu_open.get_untracked(), color_menu_open.get_untracked(),
more_menu_open.get_untracked(), more_menu_open.get_untracked(),
); );
if !editor_focused.get() if ((!editor_focused.get() || !text_selection_active.get()) && !toolbar_locked)
|| (!text_selection_active.get() && !toolbar_locked)
|| slash_open.get() || slash_open.get()
|| block_menu_open.get() || block_menu_open.get()
{ {
@@ -2390,7 +2413,7 @@ fn App() -> impl IntoView {
if let Some(target) = event.target() { if let Some(target) = event.target() {
if let Some(element) = target_element(target) { if let Some(element) = target_element(target) {
if element if element
.closest(HANDLE_SHELL_SELECTOR) .closest(&format!("{HANDLE_SHELL_SELECTOR}, .floating-toolbar"))
.ok() .ok()
.flatten() .flatten()
.is_some() .is_some()
@@ -2808,14 +2831,20 @@ fn App() -> impl IntoView {
} }
> >
{move || { {move || {
if let Some(anchor) = floating_toolbar_anchor.get() { let toolbar_locked = toolbar_overlay_locked(
let toolbar_locked = toolbar_overlay_locked( turn_into_open.get(),
turn_into_open.get(), color_menu_open.get(),
color_menu_open.get(), more_menu_open.get(),
more_menu_open.get(), );
); let toolbar_anchor = if toolbar_locked {
if !editor_focused.get() locked_toolbar_anchor
|| (!text_selection_active.get() && !toolbar_locked) .get()
.or_else(|| floating_toolbar_anchor.get())
} else {
floating_toolbar_anchor.get()
};
if let Some(anchor) = toolbar_anchor {
if ((!editor_focused.get() || !text_selection_active.get()) && !toolbar_locked)
|| slash_open.get() || slash_open.get()
|| block_menu_open.get() || block_menu_open.get()
|| dragging_block_index.get().is_some() || dragging_block_index.get().is_some()
@@ -2829,7 +2858,12 @@ fn App() -> impl IntoView {
style:top=format!("{}px", anchor.top) style:top=format!("{}px", anchor.top)
style:left=format!("{}px", anchor.left) style:left=format!("{}px", anchor.left)
on:mousedown=move |event: MouseEvent| { on:mousedown=move |event: MouseEvent| {
event.prevent_default(); if event_target_matches_selector(
event.target(),
".toolbar-btn, .toolbar-menu-item, .turn-into-item",
) {
event.prevent_default();
}
event.stop_propagation(); event.stop_propagation();
} }
on:click=move |event: MouseEvent| { on:click=move |event: MouseEvent| {
@@ -2844,54 +2878,51 @@ fn App() -> impl IntoView {
data-active=move || turn_into_open.get().to_string() data-active=move || turn_into_open.get().to_string()
data-testid="toolbar-turn-into" data-testid="toolbar-turn-into"
on:click=move |_| { on:click=move |_| {
set_turn_into_open.update(|open| *open = !*open); let next_open = !turn_into_open.get_untracked();
if next_open {
set_locked_toolbar_anchor
.set(floating_toolbar_anchor.get_untracked());
}
set_turn_into_open.set(next_open);
set_block_menu_open.set(false); set_block_menu_open.set(false);
} }
> >
"Text" "Text"
</button> </button>
{move || { <div
if turn_into_open.get() { class="turn-into-panel"
data-testid="turn-into-panel"
style:display=move || if turn_into_open.get() { "grid" } else { "none" }
on:mousedown=move |event: MouseEvent| {
event.stop_propagation();
}
on:click=move |event: MouseEvent| {
event.stop_propagation();
}
on:wheel=move |event: WheelEvent| {
trap_scroll_inside_menu(&event);
}
>
{SLASH_ACTIONS.iter().map(|action| {
let kind = action.kind;
let label = action.label;
let description = action.description;
let testid = format!("turn-into-{}", action.id);
view! { view! {
<div <button
class="turn-into-panel" class="turn-into-item"
data-testid="turn-into-panel" data-testid=testid
on:mousedown=move |event: MouseEvent| { on:click=move |_| {
event.prevent_default(); apply_feedback_result(set_command_feedback, run_slash_action(editor, kind));
event.stop_propagation(); set_turn_into_open.set(false);
}
on:click=move |event: MouseEvent| {
event.stop_propagation();
}
on:wheel=move |event: WheelEvent| {
trap_scroll_inside_menu(&event);
} }
> >
{SLASH_ACTIONS.iter().map(|action| { {label}
let kind = action.kind; <span>{description}</span>
let label = action.label; </button>
let description = action.description; }
let testid = format!("turn-into-{}", action.id); }).collect_view()}
view! { </div>
<button
class="turn-into-item"
data-testid=testid
on:click=move |_| {
apply_feedback_result(set_command_feedback, run_slash_action(editor, kind));
set_turn_into_open.set(false);
}
>
{label}
<span>{description}</span>
</button>
}
}).collect_view()}
</div>
}.into_any()
} else {
().into_any()
}
}}
</div> </div>
</div> </div>
@@ -2959,113 +2990,110 @@ fn App() -> impl IntoView {
|| color_menu_open.get()).to_string() || color_menu_open.get()).to_string()
data-testid="toolbar-color" data-testid="toolbar-color"
on:click=move |_| { on:click=move |_| {
set_color_menu_open.update(|open| *open = !*open); let next_open = !color_menu_open.get_untracked();
if next_open {
set_locked_toolbar_anchor
.set(floating_toolbar_anchor.get_untracked());
}
set_color_menu_open.set(next_open);
set_more_menu_open.set(false); set_more_menu_open.set(false);
set_turn_into_open.set(false); set_turn_into_open.set(false);
} }
> >
"Color" "Color"
</button> </button>
{move || { <div
if color_menu_open.get() { class="toolbar-popover-panel"
view! { data-testid="toolbar-color-panel"
<div style:display=move || if color_menu_open.get() { "grid" } else { "none" }
class="toolbar-popover-panel" on:mousedown=move |event: MouseEvent| {
data-testid="toolbar-color-panel" event.stop_propagation();
on:mousedown=move |event: MouseEvent| { }
event.prevent_default(); on:click=move |event: MouseEvent| {
event.stop_propagation(); event.stop_propagation();
} }
on:click=move |event: MouseEvent| { on:wheel=move |event: WheelEvent| {
event.stop_propagation(); trap_scroll_inside_menu(&event);
} }
on:wheel=move |event: WheelEvent| { >
trap_scroll_inside_menu(&event); <div class="toolbar-popover-section">
} <div class="toolbar-popover-title">"文字颜色"</div>
> <div class="toolbar-color-grid">
<div class="toolbar-popover-section"> {TOOLBAR_TEXT_COLORS.iter().map(|option| {
<div class="toolbar-popover-title">"文字颜色"</div> let value = option.value;
<div class="toolbar-color-grid"> let label = option.label;
{TOOLBAR_TEXT_COLORS.iter().map(|option| { let testid = format!("toolbar-text-color-{}", option.id);
let value = option.value; view! {
let label = option.label;
let testid = format!("toolbar-text-color-{}", option.id);
view! {
<button
class="toolbar-menu-item toolbar-color-item"
data-testid=testid
on:click=move |_| {
apply_feedback_result(
set_command_feedback,
apply_text_color(editor, value),
);
set_color_menu_open.set(false);
}
>
<span
class="toolbar-color-chip"
style:background=value
></span>
<strong>{label}</strong>
</button>
}
}).collect_view()}
</div>
</div>
<div class="toolbar-popover-section">
<div class="toolbar-popover-title">"背景颜色"</div>
<div class="toolbar-color-grid">
{TOOLBAR_HIGHLIGHT_COLORS.iter().map(|option| {
let value = option.value;
let label = option.label;
let testid = format!("toolbar-highlight-color-{}", option.id);
view! {
<button
class="toolbar-menu-item toolbar-color-item"
data-testid=testid
on:click=move |_| {
apply_feedback_result(
set_command_feedback,
apply_highlight_color(editor, Some(value)),
);
set_color_menu_open.set(false);
}
>
<span
class="toolbar-color-chip"
style:background=value
></span>
<strong>{label}</strong>
</button>
}
}).collect_view()}
</div>
</div>
<div class="toolbar-popover-section">
<button <button
class="toolbar-menu-item" class="toolbar-menu-item toolbar-color-item"
data-testid="toolbar-clear-color" data-testid=testid
on:click=move |_| { on:click=move |_| {
apply_feedback_result( apply_feedback_result(
set_command_feedback, set_command_feedback,
clear_text_color(editor), apply_text_color(editor, value),
); );
let _ = apply_highlight_color(editor, None);
set_color_menu_open.set(false); set_color_menu_open.set(false);
} }
> >
<strong>"清除颜色"</strong> <span
<span>"移除文字色与背景色"</span> class="toolbar-color-chip"
style:background=value
></span>
<strong>{label}</strong>
</button> </button>
</div> }
</div> }).collect_view()}
}.into_any() </div>
} else { </div>
().into_any()
} <div class="toolbar-popover-section">
}} <div class="toolbar-popover-title">"背景颜色"</div>
<div class="toolbar-color-grid">
{TOOLBAR_HIGHLIGHT_COLORS.iter().map(|option| {
let value = option.value;
let label = option.label;
let testid = format!("toolbar-highlight-color-{}", option.id);
view! {
<button
class="toolbar-menu-item toolbar-color-item"
data-testid=testid
on:click=move |_| {
apply_feedback_result(
set_command_feedback,
apply_highlight_color(editor, Some(value)),
);
set_color_menu_open.set(false);
}
>
<span
class="toolbar-color-chip"
style:background=value
></span>
<strong>{label}</strong>
</button>
}
}).collect_view()}
</div>
</div>
<div class="toolbar-popover-section">
<button
class="toolbar-menu-item"
data-testid="toolbar-clear-color"
on:click=move |_| {
apply_feedback_result(
set_command_feedback,
clear_text_color(editor),
);
let _ = apply_highlight_color(editor, None);
set_color_menu_open.set(false);
}
>
<strong>"清除颜色"</strong>
<span>"移除文字色与背景色"</span>
</button>
</div>
</div>
</div> </div>
</div> </div>
@@ -3082,69 +3110,66 @@ fn App() -> impl IntoView {
|| more_menu_open.get()).to_string() || more_menu_open.get()).to_string()
data-testid="toolbar-more" data-testid="toolbar-more"
on:click=move |_| { on:click=move |_| {
set_more_menu_open.update(|open| *open = !*open); let next_open = !more_menu_open.get_untracked();
if next_open {
set_locked_toolbar_anchor
.set(floating_toolbar_anchor.get_untracked());
}
set_more_menu_open.set(next_open);
set_color_menu_open.set(false); set_color_menu_open.set(false);
set_turn_into_open.set(false); set_turn_into_open.set(false);
} }
> >
"More" "More"
</button> </button>
{move || { <div
if more_menu_open.get() { class="toolbar-popover-panel"
view! { data-testid="toolbar-more-panel"
<div style:display=move || if more_menu_open.get() { "grid" } else { "none" }
class="toolbar-popover-panel" on:mousedown=move |event: MouseEvent| {
data-testid="toolbar-more-panel" event.stop_propagation();
on:mousedown=move |event: MouseEvent| {
event.prevent_default();
event.stop_propagation();
}
on:click=move |event: MouseEvent| {
event.stop_propagation();
}
on:wheel=move |event: WheelEvent| {
trap_scroll_inside_menu(&event);
}
>
<div class="toolbar-popover-section">
<div class="toolbar-popover-title">"对齐方式"</div>
<div class="toolbar-more-list">
{TOOLBAR_ALIGN_OPTIONS.iter().map(|option| {
let label = option.label;
let alignment = option.alignment;
let testid = format!("toolbar-align-{}", option.id);
let active = match option.alignment {
TiptapTextAlign::Left => selection_state.get().align_left,
TiptapTextAlign::Center => selection_state.get().align_center,
TiptapTextAlign::Right => selection_state.get().align_right,
TiptapTextAlign::Justify => selection_state.get().align_justify,
};
view! {
<button
class="toolbar-menu-item toolbar-more-item"
data-active=active.to_string()
data-testid=testid
on:click=move |_| {
apply_feedback_result(
set_command_feedback,
apply_text_align(editor, alignment),
);
set_more_menu_open.set(false);
}
>
<strong>{label}</strong>
<span>"按官方浮动工具条对当前块对齐"</span>
</button>
}
}).collect_view()}
</div>
</div>
</div>
}.into_any()
} else {
().into_any()
} }
}} on:click=move |event: MouseEvent| {
event.stop_propagation();
}
on:wheel=move |event: WheelEvent| {
trap_scroll_inside_menu(&event);
}
>
<div class="toolbar-popover-section">
<div class="toolbar-popover-title">"对齐方式"</div>
<div class="toolbar-more-list">
{TOOLBAR_ALIGN_OPTIONS.iter().map(|option| {
let label = option.label;
let alignment = option.alignment;
let testid = format!("toolbar-align-{}", option.id);
let active = match option.alignment {
TiptapTextAlign::Left => selection_state.get().align_left,
TiptapTextAlign::Center => selection_state.get().align_center,
TiptapTextAlign::Right => selection_state.get().align_right,
TiptapTextAlign::Justify => selection_state.get().align_justify,
};
view! {
<button
class="toolbar-menu-item toolbar-more-item"
data-active=active.to_string()
data-testid=testid
on:click=move |_| {
apply_feedback_result(
set_command_feedback,
apply_text_align(editor, alignment),
);
set_more_menu_open.set(false);
}
>
<strong>{label}</strong>
<span>"按官方浮动工具条对当前块对齐"</span>
</button>
}
}).collect_view()}
</div>
</div>
</div>
</div> </div>
</div> </div>
</div> </div>