mirror of
https://github.com/t8y2/dbx.git
synced 2026-10-19 16:16:45 +00:00
fix(editor): keep table hover open while typing in its search box
保持表格悬浮搜索框在输入法组合期间可用。
This commit is contained in:
@@ -127,6 +127,7 @@ export function createQueryEditorHoverContent(options: QueryEditorHoverContentOp
|
||||
layoutController || handleCopy || searchController
|
||||
? () => {
|
||||
layoutController?.mount();
|
||||
searchController?.pinTooltip(dom.closest<HTMLElement>(".cm-tooltip") ?? dom);
|
||||
if (handleCopy) document.addEventListener("copy", handleCopy);
|
||||
}
|
||||
: undefined,
|
||||
|
||||
@@ -517,4 +517,58 @@ describe("createHoverSearch", () => {
|
||||
input.dispatchEvent(new Event("input"));
|
||||
expect(target.firstChild).toBe(initialFirstChild);
|
||||
});
|
||||
|
||||
describe("pinTooltip", () => {
|
||||
function mountPinnedSearch() {
|
||||
const host = document.createElement("div");
|
||||
const target = document.createElement("div");
|
||||
target.textContent = DDL;
|
||||
const controller = createHoverSearch({ target, originalHtml: target.innerHTML, placeholder: "Search columns…", noResultLabel: "No matching columns" });
|
||||
host.append(controller.element, target);
|
||||
document.body.appendChild(host);
|
||||
controller.pinTooltip(host);
|
||||
const leave = vi.fn();
|
||||
host.addEventListener("mouseleave", leave);
|
||||
const input = controller.element.querySelector<HTMLInputElement>('[data-sql-hover-search-input="true"]')!;
|
||||
return { controller, host, input, leave };
|
||||
}
|
||||
|
||||
it("keeps the tooltip from seeing mouseleave while the search input is focused", () => {
|
||||
const { host, input, leave } = mountPinnedSearch();
|
||||
input.focus();
|
||||
input.dispatchEvent(new CompositionEvent("compositionstart"));
|
||||
|
||||
host.dispatchEvent(new MouseEvent("mouseleave"));
|
||||
|
||||
expect(leave).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it("lets mouseleave through when the search input is not focused", () => {
|
||||
const { host, leave } = mountPinnedSearch();
|
||||
|
||||
host.dispatchEvent(new MouseEvent("mouseleave"));
|
||||
|
||||
expect(leave).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
|
||||
it("replays the missed mouseleave when the search input loses focus off the tooltip", () => {
|
||||
const { host, input, leave } = mountPinnedSearch();
|
||||
input.focus();
|
||||
host.dispatchEvent(new MouseEvent("mouseleave"));
|
||||
|
||||
input.blur();
|
||||
|
||||
expect(leave).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
|
||||
it("stops pinning after destroy", () => {
|
||||
const { controller, host, input, leave } = mountPinnedSearch();
|
||||
input.focus();
|
||||
controller.destroy();
|
||||
|
||||
host.dispatchEvent(new MouseEvent("mouseleave"));
|
||||
|
||||
expect(leave).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -118,6 +118,12 @@ export interface HoverSearchController {
|
||||
status: HTMLElement;
|
||||
/** Focus the search input. */
|
||||
focus(): void;
|
||||
/**
|
||||
* Keep the hover tooltip open while the search input has focus. CodeMirror
|
||||
* closes hover tooltips on `mouseleave`, which Windows IME candidate windows
|
||||
* trigger when they pop up over the pointer mid-composition.
|
||||
*/
|
||||
pinTooltip(host: HTMLElement): void;
|
||||
/** Remove listeners; safe to call once when the tooltip is destroyed. */
|
||||
destroy(): void;
|
||||
}
|
||||
@@ -310,14 +316,32 @@ export function createHoverSearch(options: HoverSearchOptions): HoverSearchContr
|
||||
input.addEventListener("pointerdown", stopPointer);
|
||||
input.addEventListener("mousedown", stopPointer);
|
||||
|
||||
let pinnedHost: HTMLElement | null = null;
|
||||
const keepPinnedTooltipOpen = (event: Event) => {
|
||||
if (event.target === pinnedHost && document.activeElement === input) event.stopImmediatePropagation();
|
||||
};
|
||||
const releasePinnedTooltip = () => {
|
||||
// Hand the leave CodeMirror missed back to it once typing is over.
|
||||
if (pinnedHost && !pinnedHost.matches(":hover")) pinnedHost.dispatchEvent(new MouseEvent("mouseleave"));
|
||||
};
|
||||
|
||||
return {
|
||||
element,
|
||||
status,
|
||||
focus() {
|
||||
input.focus();
|
||||
},
|
||||
pinTooltip(host) {
|
||||
pinnedHost = host;
|
||||
// Capture on the document runs before CodeMirror's listener on the host.
|
||||
document.addEventListener("mouseleave", keepPinnedTooltipOpen, true);
|
||||
input.addEventListener("blur", releasePinnedTooltip);
|
||||
},
|
||||
destroy() {
|
||||
cancelPending();
|
||||
document.removeEventListener("mouseleave", keepPinnedTooltipOpen, true);
|
||||
input.removeEventListener("blur", releasePinnedTooltip);
|
||||
pinnedHost = null;
|
||||
input.removeEventListener("input", scheduleSearch);
|
||||
input.removeEventListener("keydown", stopKeyboard);
|
||||
input.removeEventListener("keyup", stopKeyboard);
|
||||
|
||||
Reference in New Issue
Block a user