import * as d3 from 'd3'; import type { GLTR_Text_Box } from '../vis/GLTR_Text_Box'; import type { Histogram } from '../vis/Histogram'; import type { HistogramBinClickEvent } from '../vis/Histogram'; import type { FrontendAnalyzeResult } from '../api/GLTR_API'; import { calculateHighlights } from '../utils/highlightUtils'; export type HighlightControllerOptions = { stats_frac: Histogram; lmf: GLTR_Text_Box; currentData: { result: FrontendAnalyzeResult } | null; }; export class HighlightController { private options: HighlightControllerOptions; constructor(options: HighlightControllerOptions) { this.options = options; } /** * 清除所有高亮(文本与直方图) */ public clearHighlights(): void { this.options.stats_frac.clearSelection(); this.options.lmf.clearHighlight(); } /** * 处理直方图 bin 点击事件 */ public handleHistogramBinClick(ev: HistogramBinClickEvent): void { const { currentData } = this.options; if (!currentData) return; // 如果 binIndex 为 -1,表示用户取消选择,清除所有高亮 if (ev.binIndex === -1) { this.clearHighlights(); return; } const { x0, x1, binIndex, no_bins, source } = ev; const data = currentData.result; // 仅处理 token 直方图 const { indices, style } = calculateHighlights('token', x0, x1, binIndex, no_bins, data); // 高亮这些 token this.options.lmf.setHighlightedIndices(indices, style); } /** * 更新当前数据(当数据变化时调用) */ public updateCurrentData(currentData: { result: FrontendAnalyzeResult } | null): void { // 创建一个新对象来更新,保持 options 对象的引用不变 (this.options as any).currentData = currentData; } } /** * 初始化高亮清除事件监听(点击空白处和 ESC 键) */ export const initHighlightClearListeners = ( clearHighlights: () => void ): void => { // 点击页面空白处清除高亮(通用解决方案) // 监听整个文档的点击事件,但排除可交互元素 d3.select('body').on('click.clearHighlight', (event: MouseEvent) => { const target = event.target; if (!target) return; // 排除可交互元素:token、按钮、输入框、直方图bin等 const isInteractive = target.closest('.token') || // token元素 target.closest('button') || // 按钮 target.closest('input') || // 输入框 target.closest('textarea') || // 文本域 target.closest('select') || // 下拉框 target.closest('.bar') || // 直方图bar target.closest('.hover-area') || // 直方图悬停区域 target.closest('a') || // 链接 target.closest('[role="button"]') || // 有button角色的元素 target.closest('[onclick]'); // 有onclick属性的元素 // 如果点击的不是可交互元素,则清除高亮 if (!isInteractive) { clearHighlights(); } }); // 按下 ESC 键清除高亮 d3.select(window).on('keydown.clearHighlight', (event: KeyboardEvent) => { if (event.key === 'Escape') { clearHighlights(); } }); };