-
Notifications
You must be signed in to change notification settings - Fork 15
[DAPS-1408] Provenance Visual Management (2/2) #1419
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
AronPerez
merged 15 commits into
feat-DAPS-14xx-provenance-node-label-anchors-lint
from
feat-DAPS-14xx-provenance-node-label-anchors
May 22, 2025
Merged
Changes from all commits
Commits
Show all changes
15 commits
Select commit
Hold shift + click to select a range
d4dfae1
[DAPS-14xx] Base
AronPerez abc42cf
[DAPS-14xx] Add styling and tooltips
AronPerez 841242a
[DAPS-14xx] Add node and label customization
AronPerez d87aba3
[DAPS-14xx] Add editor modal on right-click. Draggable model
AronPerez 7ed703c
[DAPS-14xx] Revert
AronPerez f43a7b4
[DAPS-14xx] Seperate files, update styles
AronPerez 41f7feb
[DAPS-14xx] simplify feat
AronPerez 116993f
[DAPS-14xx] Expand mocha tests, prettier
AronPerez 6835e7c
[DAPS-14xx] Update checkout version
AronPerez 91124ad
[DAPS-14xx] Add todo
AronPerez 7cbd39d
[DAPS-14xx] Update tests, consts, fix tooltip
AronPerez 6ac605f
[DAPS-14xx] Base:
AronPerez cfddbe8
[DAPS-14xx] THEME, address comments
AronPerez fc945c9
[DAPS-14xx] Zoom
AronPerez a3ba93c
[DAPS-14xx] JSDOC
AronPerez File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
237 changes: 237 additions & 0 deletions
237
web/static/components/provenance/customization_modal.js
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,237 @@ | ||
import { DEFAULTS } from "./state.js"; | ||
|
||
function showCustomizationModal(node, x, y, currentCustomizationNode) { | ||
const modal = document.getElementById("customization-modal"); | ||
if (!modal) return; | ||
|
||
// Set the current node being customized | ||
currentCustomizationNode = node; | ||
|
||
// Save original values for reverting if cancelled | ||
if (window.saveOriginalValues) { | ||
window.saveOriginalValues(node); | ||
} | ||
|
||
const nodeColorInput = document.getElementById("node-color-input"); | ||
// Helper function to convert RGB to hex format | ||
const rgbToHex = (rgb) => { | ||
return "#" + rgb.map((x) => parseInt(x).toString(16).padStart(2, "0")).join(""); | ||
}; | ||
|
||
// Helper function to get the default color from CSS | ||
const getDefaultColor = (node) => { | ||
const nodeElement = d3.select(`[id="${node.id}"] circle.obj`).node(); | ||
if (!nodeElement) { | ||
return DEFAULTS.NODE_COLOR; | ||
} | ||
|
||
const computedStyle = window.getComputedStyle(nodeElement); | ||
const fillColor = computedStyle.fill; | ||
|
||
if (fillColor && fillColor !== "none") { | ||
if (fillColor.startsWith("rgb")) { | ||
const rgb = fillColor.match(/\d+/g); | ||
return rgb && rgb.length === 3 ? rgbToHex(rgb) : DEFAULTS.NODE_COLOR; | ||
} | ||
return fillColor; | ||
} | ||
|
||
return DEFAULTS.NODE_COLOR; | ||
}; | ||
|
||
// Get the actual current node color | ||
nodeColorInput.value = node.nodeColor || getDefaultColor(node); | ||
|
||
const labelSizeSlider = document.getElementById("label-size-slider"); | ||
const labelSizeValue = labelSizeSlider.nextElementSibling; | ||
labelSizeSlider.value = node.labelSize || DEFAULTS.LABEL_SIZE; | ||
labelSizeValue.textContent = `${labelSizeSlider.value}px`; | ||
|
||
const labelColorInput = document.getElementById("label-color-input"); | ||
labelColorInput.value = node.labelColor || DEFAULTS.LABEL_COLOR; | ||
|
||
const anchorCheckbox = document.getElementById("anchor-checkbox"); | ||
anchorCheckbox.checked = node.anchored || false; | ||
|
||
// Position and show modal | ||
modal.style.left = `${x}px`; | ||
modal.style.top = `${y}px`; | ||
modal.style.display = "block"; | ||
|
||
// Return the current node for reference | ||
return currentCustomizationNode; | ||
} | ||
|
||
/** | ||
* Makes a modal element draggable by its header | ||
* Optimized to only attach document listeners during drag operations | ||
* @param {HTMLElement} modal - The modal element to make draggable | ||
*/ | ||
function makeModalDraggable(modal) { | ||
let offsetX, offsetY; | ||
const header = modal.querySelector(".modal-header") || modal; | ||
|
||
// Handle mouse movement during drag | ||
function handleMouseMove(e) { | ||
modal.style.left = `${e.clientX - offsetX}px`; | ||
modal.style.top = `${e.clientY - offsetY}px`; | ||
} | ||
|
||
// Handle end of drag operation | ||
function handleMouseUp() { | ||
// Remove event listeners when dragging ends to improve performance | ||
document.removeEventListener("mousemove", handleMouseMove); | ||
document.removeEventListener("mouseup", handleMouseUp); | ||
} | ||
|
||
// Start dragging when mousedown on header | ||
header.addEventListener("mousedown", function (e) { | ||
// Calculate initial offset | ||
offsetX = e.clientX - modal.offsetLeft; | ||
offsetY = e.clientY - modal.offsetTop; | ||
|
||
// Add event listeners for dragging only when needed | ||
document.addEventListener("mousemove", handleMouseMove); | ||
document.addEventListener("mouseup", handleMouseUp); | ||
|
||
e.preventDefault(); | ||
}); | ||
} | ||
|
||
function createCustomizationModal() { | ||
// Remove existing modal if it exists | ||
const existingModal = document.getElementById("customization-modal"); | ||
if (existingModal) { | ||
document.body.removeChild(existingModal); | ||
} | ||
|
||
AronPerez marked this conversation as resolved.
Show resolved
Hide resolved
|
||
const modal = document.createElement("div"); | ||
modal.id = "customization-modal"; | ||
modal.className = "customization-modal"; | ||
modal.style.display = "none"; | ||
|
||
// Add a draggable header | ||
const modalHeader = document.createElement("div"); | ||
modalHeader.className = "modal-header"; | ||
|
||
// Title in the draggable header | ||
const title = document.createElement("h3"); | ||
title.textContent = "Customize Node & Label"; | ||
modalHeader.appendChild(title); | ||
modal.appendChild(modalHeader); | ||
|
||
// Node customization section | ||
const nodeSection = document.createElement("div"); | ||
nodeSection.className = "section"; | ||
|
||
// Node color picker | ||
const nodeColorLabel = document.createElement("label"); | ||
nodeColorLabel.textContent = "Node Color"; | ||
nodeSection.appendChild(nodeColorLabel); | ||
|
||
const nodeColorRow = document.createElement("div"); | ||
nodeColorRow.className = "control-row"; | ||
|
||
const nodeColorInput = document.createElement("input"); | ||
nodeColorInput.type = "color"; | ||
nodeColorInput.id = "node-color-input"; | ||
nodeColorInput.value = DEFAULTS.NODE_COLOR; | ||
|
||
nodeColorRow.appendChild(nodeColorInput); | ||
nodeSection.appendChild(nodeColorRow); | ||
|
||
modal.appendChild(nodeSection); | ||
|
||
// Label customization section | ||
const labelSection = document.createElement("div"); | ||
labelSection.className = "section"; | ||
|
||
const labelSizeLabel = document.createElement("label"); | ||
labelSizeLabel.textContent = "Label Size"; | ||
labelSection.appendChild(labelSizeLabel); | ||
|
||
const labelSizeRow = document.createElement("div"); | ||
labelSizeRow.className = "control-row"; | ||
|
||
const labelSizeSlider = document.createElement("input"); | ||
labelSizeSlider.type = "range"; | ||
labelSizeSlider.min = "8"; | ||
labelSizeSlider.max = "24"; | ||
labelSizeSlider.value = DEFAULTS.LABEL_SIZE; | ||
labelSizeSlider.id = "label-size-slider"; | ||
|
||
const labelSizeValue = document.createElement("span"); | ||
labelSizeValue.className = "value"; | ||
labelSizeValue.textContent = `${DEFAULTS.LABEL_SIZE}px`; | ||
|
||
labelSizeRow.appendChild(labelSizeSlider); | ||
labelSizeRow.appendChild(labelSizeValue); | ||
labelSection.appendChild(labelSizeRow); | ||
|
||
// Label color picker | ||
const labelColorLabel = document.createElement("label"); | ||
labelColorLabel.textContent = "Label Color"; | ||
labelSection.appendChild(labelColorLabel); | ||
|
||
const labelColorRow = document.createElement("div"); | ||
labelColorRow.className = "control-row"; | ||
|
||
const labelColorInput = document.createElement("input"); | ||
labelColorInput.type = "color"; | ||
labelColorInput.id = "label-color-input"; | ||
labelColorInput.value = DEFAULTS.LABEL_COLOR; // Default text color | ||
|
||
labelColorRow.appendChild(labelColorInput); | ||
labelSection.appendChild(labelColorRow); | ||
|
||
modal.appendChild(labelSection); | ||
|
||
// Anchor controls | ||
const anchorSection = document.createElement("div"); | ||
anchorSection.className = "section"; | ||
|
||
const anchorRow = document.createElement("div"); | ||
anchorRow.className = "control-row checkbox-row"; | ||
|
||
const anchorCheckbox = document.createElement("input"); | ||
anchorCheckbox.type = "checkbox"; | ||
anchorCheckbox.id = "anchor-checkbox"; | ||
|
||
const anchorLabel = document.createElement("label"); | ||
anchorLabel.htmlFor = "anchor-checkbox"; | ||
anchorLabel.textContent = "Anchor Node"; | ||
anchorLabel.classList.add("inline-label"); | ||
|
||
anchorRow.appendChild(anchorCheckbox); | ||
anchorRow.appendChild(anchorLabel); | ||
anchorSection.appendChild(anchorRow); | ||
|
||
modal.appendChild(anchorSection); | ||
|
||
// Buttons | ||
const buttonsDiv = document.createElement("div"); | ||
buttonsDiv.className = "buttons"; | ||
|
||
const applyButton = document.createElement("button"); | ||
applyButton.textContent = "Apply"; | ||
applyButton.className = "primary"; | ||
applyButton.id = "apply-customization"; | ||
|
||
const closeButton = document.createElement("button"); | ||
closeButton.textContent = "Close"; | ||
closeButton.id = "close-customization"; | ||
|
||
buttonsDiv.appendChild(closeButton); | ||
buttonsDiv.appendChild(applyButton); | ||
|
||
modal.appendChild(buttonsDiv); | ||
|
||
document.body.appendChild(modal); | ||
|
||
// Make the modal draggable | ||
makeModalDraggable(modal); | ||
|
||
return modal; | ||
} | ||
|
||
export { showCustomizationModal, createCustomizationModal }; |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why was this needed?
Uh oh!
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Original context: #1419 (comment)