UNPKG

@teachinglab/omd

Version:

omd

163 lines (135 loc) 6.03 kB
import { omdEquationNode } from '../nodes/omdEquationNode.js'; import { omdStepVisualizerInteractiveSteps } from '../utils/omdStepVisualizerInteractiveSteps.js'; /** * Manages interactive step text boxes that appear when dots are clicked * Handles creation, positioning, and cleanup of explanation popups */ export class omdStepVisualizerTextBoxes { constructor(stepVisualizer, highlighting) { this.stepVisualizer = stepVisualizer; this.highlighting = highlighting; this.stepTextBoxes = []; } /** * Creates an interactive steps popup for a clicked dot * @param {number} dotIndex - Index of the dot to create text box for */ createTextBoxForDot(dotIndex) { try { this.removeTextBoxForDot(dotIndex); const targetDot = this._findDotAboveForPositioning(dotIndex); if (!targetDot) { console.error('Target dot not found for positioning text box for dot index:', dotIndex); return; } const simplificationData = this._getSimplificationDataForDot(dotIndex); this._createInteractiveStepsForDot(dotIndex, targetDot, simplificationData); } catch (error) { console.error('Error creating text box for dot', dotIndex, ':', error); } } /** * Creates interactive steps for a dot with simplification data * @param {number} dotIndex - Index of the dot * @param {Object} targetDot - The dot to position relative to * @param {Object} simplificationData - Full simplification data including rule names * @private */ _createInteractiveStepsForDot(dotIndex, targetDot, simplificationData) { const interactiveSteps = new omdStepVisualizerInteractiveSteps(this.stepVisualizer, simplificationData); // Position relative to the target dot const x = targetDot.xpos + this.stepVisualizer.dotRadius * 2 + 10; const y = targetDot.ypos - this.stepVisualizer.dotRadius; interactiveSteps.setPosition(x, y); // Set up hover interactions interactiveSteps.setOnStepHover((stepIndex, message, isEntering) => { }); // Set up click interactions interactiveSteps.setOnStepClick((stepIndex, message) => { }); // Add to visual container and track const layoutGroup = interactiveSteps.getLayoutGroup(); layoutGroup.dotIndex = dotIndex; this.stepVisualizer.visualContainer.addChild(layoutGroup); this.stepTextBoxes.push({ dotIndex: dotIndex, interactiveSteps: interactiveSteps, layoutGroup: layoutGroup }); // Update layout to prevent clipping after adding text box this.stepVisualizer.layoutManager.updateVisualLayout(); } /** * Removes the text box for a specific dot * @param {number} dotIndex - Index of the dot to remove text box for */ removeTextBoxForDot(dotIndex) { const textBoxIndex = this.stepTextBoxes.findIndex(tb => tb.dotIndex === dotIndex); if (textBoxIndex >= 0) { const item = this.stepTextBoxes[textBoxIndex]; this.stepVisualizer.visualContainer.removeChild(item.layoutGroup); item.interactiveSteps.destroy(); this.stepTextBoxes.splice(textBoxIndex, 1); // Update layout after removing text box to adjust container size this.stepVisualizer.layoutManager.updateVisualLayout(); } } /** * Removes all text boxes */ clearAllTextBoxes() { this.stepTextBoxes.forEach(item => { this.stepVisualizer.visualContainer.removeChild(item.layoutGroup); item.interactiveSteps.destroy(); }); this.stepTextBoxes = []; // Update layout after clearing all text boxes this.stepVisualizer.layoutManager.updateVisualLayout(); } /** * Finds the appropriate dot above the clicked one for text box positioning * @param {number} dotIndex - Index of the clicked dot * @returns {Object|null} The dot to align with, or null if none found * @private */ _findDotAboveForPositioning(dotIndex) { const currentDot = this.stepVisualizer.stepDots[dotIndex]; if (!currentDot || !currentDot.equationRef) { return null; } const currentEquation = currentDot.equationRef; const currentEquationIndex = this.stepVisualizer.steps.indexOf(currentEquation); // Find the nearest visible equation above for (let i = currentEquationIndex - 1; i >= 0; i--) { const step = this.stepVisualizer.steps[i]; if (step instanceof omdEquationNode && step.visible !== false) { // Find the corresponding dot for (let dotIdx = dotIndex - 1; dotIdx >= 0; dotIdx--) { const dot = this.stepVisualizer.stepDots[dotIdx]; if (dot && dot.equationRef === step) { return dot; } } break; } } // If no visible equation above, use the clicked dot itself return currentDot; } /** * Gets the simplification data for a specific dot * @param {number} dotIndex - Index of the dot * @returns {Object} The simplification data for this step * @private */ _getSimplificationDataForDot(dotIndex) { return this.stepVisualizer._getSimplificationDataForDot(dotIndex); } /** * Gets all text boxes * @returns {Array} Array of text box objects */ getStepTextBoxes() { return this.stepTextBoxes; } }