@teachinglab/omd
Version:
omd
163 lines (135 loc) • 6.03 kB
JavaScript
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;
}
}