颜色修复

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> {
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 (floating_toolbar_anchor, set_floating_toolbar_anchor) =
signal(None::<FloatingToolbarAnchor>);
let (locked_toolbar_anchor, set_locked_toolbar_anchor) =
signal(None::<FloatingToolbarAnchor>);
let (slash_open, set_slash_open) = signal(false);
let (slash_index, set_slash_index) = signal(0_usize);
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 text_selection_active = text_selection_active;
@@ -2311,8 +2335,7 @@ fn App() -> impl IntoView {
color_menu_open.get_untracked(),
more_menu_open.get_untracked(),
);
if !editor_focused.get()
|| (!text_selection_active.get() && !toolbar_locked)
if ((!editor_focused.get() || !text_selection_active.get()) && !toolbar_locked)
|| slash_open.get()
|| block_menu_open.get()
{
@@ -2390,7 +2413,7 @@ fn App() -> impl IntoView {
if let Some(target) = event.target() {
if let Some(element) = target_element(target) {
if element
.closest(HANDLE_SHELL_SELECTOR)
.closest(&format!("{HANDLE_SHELL_SELECTOR}, .floating-toolbar"))
.ok()
.flatten()
.is_some()
@@ -2808,14 +2831,20 @@ fn App() -> impl IntoView {
}
>
{move || {
if let Some(anchor) = floating_toolbar_anchor.get() {
let toolbar_locked = toolbar_overlay_locked(
turn_into_open.get(),
color_menu_open.get(),
more_menu_open.get(),
);
if !editor_focused.get()
|| (!text_selection_active.get() && !toolbar_locked)
let toolbar_locked = toolbar_overlay_locked(
turn_into_open.get(),
color_menu_open.get(),
more_menu_open.get(),
);
let toolbar_anchor = if toolbar_locked {
locked_toolbar_anchor
.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()
|| block_menu_open.get()
|| dragging_block_index.get().is_some()
@@ -2829,7 +2858,12 @@ fn App() -> impl IntoView {
style:top=format!("{}px", anchor.top)
style:left=format!("{}px", anchor.left)
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();
}
on:click=move |event: MouseEvent| {
@@ -2844,54 +2878,51 @@ fn App() -> impl IntoView {
data-active=move || turn_into_open.get().to_string()
data-testid="toolbar-turn-into"
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);
}
>
"Text"
</button>
{move || {
if turn_into_open.get() {
<div
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! {
<div
class="turn-into-panel"
data-testid="turn-into-panel"
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);
<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);
}
>
{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! {
<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()
}
}}
{label}
<span>{description}</span>
</button>
}
}).collect_view()}
</div>
</div>
</div>
@@ -2959,113 +2990,110 @@ fn App() -> impl IntoView {
|| color_menu_open.get()).to_string()
data-testid="toolbar-color"
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_turn_into_open.set(false);
}
>
"Color"
</button>
{move || {
if color_menu_open.get() {
view! {
<div
class="toolbar-popover-panel"
data-testid="toolbar-color-panel"
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-color-grid">
{TOOLBAR_TEXT_COLORS.iter().map(|option| {
let value = option.value;
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">
<div
class="toolbar-popover-panel"
data-testid="toolbar-color-panel"
style:display=move || if color_menu_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);
}
>
<div class="toolbar-popover-section">
<div class="toolbar-popover-title">"文字颜色"</div>
<div class="toolbar-color-grid">
{TOOLBAR_TEXT_COLORS.iter().map(|option| {
let value = option.value;
let label = option.label;
let testid = format!("toolbar-text-color-{}", option.id);
view! {
<button
class="toolbar-menu-item"
data-testid="toolbar-clear-color"
class="toolbar-menu-item toolbar-color-item"
data-testid=testid
on:click=move |_| {
apply_feedback_result(
set_command_feedback,
clear_text_color(editor),
apply_text_color(editor, value),
);
let _ = apply_highlight_color(editor, None);
set_color_menu_open.set(false);
}
>
<strong>"清除颜色"</strong>
<span>"移除文字色与背景色"</span>
<span
class="toolbar-color-chip"
style:background=value
></span>
<strong>{label}</strong>
</button>
</div>
</div>
}.into_any()
} else {
().into_any()
}
}}
}
}).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
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>
@@ -3082,69 +3110,66 @@ fn App() -> impl IntoView {
|| more_menu_open.get()).to_string()
data-testid="toolbar-more"
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_turn_into_open.set(false);
}
>
"More"
</button>
{move || {
if more_menu_open.get() {
view! {
<div
class="toolbar-popover-panel"
data-testid="toolbar-more-panel"
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()
<div
class="toolbar-popover-panel"
data-testid="toolbar-more-panel"
style:display=move || if more_menu_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);
}
>
<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>