Page MenuHomec4science

ConceptBoxes.js
No OneTemporary

File Metadata

Created
Tue, Jan 28, 15:37

ConceptBoxes.js

import React, {useEffect, useRef, useState} from 'react';
import {FontAwesomeIcon} from '@fortawesome/react-fontawesome';
import {faTimes, faArrowUp, faArrowDown, faArrowRight} from '@fortawesome/free-solid-svg-icons';
import {callbackAddConceptsToBoxes} from "@/app/utils/utils";
function ConceptBox({
concept,
onRemove,
handleMagicClick,
setToolTipVisible,
setToolTipContent,
setSearchData,
searchData,
}) {
const [showPopup, setShowPopup] = useState(false);
const [popupContent, setPopupContent] = useState(null);
const [conceptClicked, setConceptClicked] = useState(false);
const popupRef = useRef(null);
const buttonRefs = useRef([]);
const handleMouseEnter = (title, event, setToolTipContent, setToolTipVisible) => {
setToolTipContent(title);
setToolTipVisible(true);
};
const handleMouseLeave = (setToolTipVisible) => {
setShowPopup(false);
setToolTipVisible(false);
};
const handleMouseClickConcept = (concept, event) => {
event.stopPropagation();
console.log('concept', concept)
if (JSON.stringify(concept) === JSON.stringify(conceptClicked)) {
setConceptClicked(false);
setShowPopup(false);
return;
}
setConceptClicked(concept);
setPopupContent(concept);
setShowPopup(true);
}
useEffect(() => {
const handleClickOutside = (event) => {
if (
showPopup &&
popupRef.current &&
!popupRef.current.contains(event.target) &&
// Check all the button refs
!buttonRefs.current.some(ref => ref.contains(event.target))
) {
setShowPopup(false);
setConceptClicked(null);
}
}
document.addEventListener("mousedown", handleClickOutside);
return () => {
document.removeEventListener("mousedown", handleClickOutside);
}
}, [showPopup]);
let boxWidth = 140
if (!concept.siblings) {
boxWidth += 30
}
if (!concept.parent) {
boxWidth += 30
}
if (!concept.children || concept.children.length === 0) {
boxWidth += 30
}
return (
<div className="block concept" style={{position: 'relative'}}>
<div className="input-group input-group-sm" style={{width: '260px'}}>
<span
id={`text${concept.id}`}
data-pageid={concept.title}
className="input-group-text"
style={{overflow: 'hidden', width: boxWidth + 'px'}}
onMouseEnter={(e) => handleMouseEnter(
concept.title || concept,
e,
setToolTipContent,
setToolTipVisible,
)}
onMouseLeave={e => handleMouseLeave(setToolTipVisible)}
>
{concept.title || concept}
</span>
{concept.parent && (
<button
ref={(el) => buttonRefs.current[0] = el}
className="btn btn-primary form-control"
style={{flex: '0 1 30px'}}
onClick={(e) => {
e.stopPropagation();
handleMouseClickConcept([concept.parent], e)
}}
>
<FontAwesomeIcon icon={faArrowUp}/>
</button>
)}
{concept.children && concept.children.length > 0 && (
<button
ref={(el) => buttonRefs.current[1] = el}
className="btn btn-primary form-control"
style={{flex: '0 1 30px'}}
onClick={(e) => {
e.stopPropagation();
handleMouseClickConcept(concept.children, e)
}
}
>
<FontAwesomeIcon icon={faArrowDown}/>
</button>
)}
{concept.siblings && concept.siblings.length > 0 && (
<button
ref={(el) => buttonRefs.current[2] = el}
className="btn btn-primary form-control"
style={{flex: '0 1 30px'}}
onClick={(e) => {
e.stopPropagation();
handleMouseClickConcept(concept.siblings, e)
}
}>
<FontAwesomeIcon icon={faArrowRight}/>
</button>
)}
<button
ref={(el) => buttonRefs.current[3] = el}
className="btn btn-danger form-control"
style={{flex: '0 1 30px', backgroundColor: 'red'}}
onClick={() => onRemove(concept)}
>
<FontAwesomeIcon icon={faTimes}/>
</button>
</div>
{showPopup && (
<div
ref={popupRef}
style={{
position: 'absolute',
top: '-70px',
left: '260px',
background: '#fff',
border: '1px solid #ccc',
padding: '10px',
borderRadius: '5px', // Added rounded corners
boxShadow: '0px 0px 10px rgba(0,0,0,0.1)', // Added a shadow for a "lifted" effect
zIndex: 1000
}}>
{popupContent.map((sibling, index) => (
<div key={index} className="d-flex justify-content-between align-items-right mb-2" style={{
padding: '5px',
borderRadius: '3px',
width: '100%'
}}>
<div style={{flexGrow: 1, padding: '5px'}}>{sibling.title}</div>
<button className="btn btn-primary" style={{
flex: '0 0 30px',
backgroundColor: 'green',
marginLeft: '10px',
borderRadius: '3px'
}} onClick={() => handleMagicClick(sibling, searchData, setSearchData)}>+
</button>
</div>
))}
</div>
)}
</div>
);
}
function ConceptBoxes({
setToolTipContent,
setToolTipVisible,
freeTextSearch,
searchData,
setSearchData,
searchEntity,
setRenderForceGraph,
removeNodeInGraph,
}) {
console.log('render ConceptBoxes');
// console.log('conceptsToAddBoxes', conceptsToAddBoxes)
useEffect(() => {
console.log("searchData", searchData)
}, [searchData])
const handleRemoveClick = (conceptToRemove) => {
setRenderForceGraph(false)
console.log('conceptToRemove', conceptToRemove)
// setTableColumnsInRightOrder(tableColumnsInRightOrder.filter((concept) => concept !== conceptToRemove.title));
// setConceptsToAddBoxes(conceptsToAddBoxes.filter((concept) => concept !== conceptToRemove));
let conceptsToAddBoxes = searchData['conceptsToAddBoxes']
conceptsToAddBoxes = conceptsToAddBoxes.filter((concept) => concept !== conceptToRemove);
let tableColumnsInRightOrder = searchData['tableColumnsInRightOrder']
tableColumnsInRightOrder = tableColumnsInRightOrder.filter((concept) => concept !== conceptToRemove.title);
console.log('conceptsToAddBoxes', conceptsToAddBoxes)
console.log('concepttoRemove', conceptToRemove)
// remove rows from searchData['table'] if they have only zeros left
let table = searchData['table']
let tableWithoutConceptToRemove = table.filter(
// check if any or row[column] is not 0 for column in tableColumnsInRightOrder
(row) => tableColumnsInRightOrder.some((column) => row[column] !== 0)
)
console.log('tableWithoutConceptToRemove', tableWithoutConceptToRemove)
setSearchData({
...searchData,
'conceptsToAddBoxes': conceptsToAddBoxes,
'tableColumnsInRightOrder': tableColumnsInRightOrder,
'table': tableWithoutConceptToRemove
})
removeNodeInGraph(conceptToRemove.title)
};
const handleMagicClick = (concept, searchData, setSearchData) => {
console.log('concept', concept)
console.log('handleMagicClick searchdata', searchData)
if (concept.id[0] === 'p') {
console.log('concept is a page')
} else if (concept.id[0] === 'c') {
console.log('concept is a category')
}
let callback = (data) => {
console.log('received data', data)
if(data['table'].length === 0){
console.log('received data is empty')
return
}
let myNewConceptsToAddBoxes_with_duplicates = [
...searchData['conceptsToAddBoxes'],
...data['conceptsToAddBoxes']
]
let myNewConceptsToAddBoxes = []
const seen = new Set();
for (const item of myNewConceptsToAddBoxes_with_duplicates) {
if (!seen.has(item.id)) {
seen.add(item.id);
myNewConceptsToAddBoxes.push(item);
}
}
let myNewConceptsOfPeople = {
...searchData['conceptsOfPeople'],
...data['conceptsOfPeople']
}
let myNewTableColumnsInRightOrder = [
...new Set([
...searchData['tableColumnsInRightOrder'],
...data['tableColumnsInRightOrder']
])
]
// create new table with all rows from both tables
// Create lookup objects.
let lookupData = {};
let lookupSearchData = {};
data['table'].forEach(item => lookupData[item.ID] = item);
searchData['table'].forEach(item => lookupSearchData[item.ID] = item);
// Get the unique keys from both data sets
let setOfKeys1 = new Set(data['table'].flatMap(item => Object.keys(item)));
let setOfKeys2 = new Set(searchData['table'].flatMap(item => Object.keys(item)));
// Create a merged array
let mergedArray = [];
// Get the union of keys from both datasets
let allKeys = [...new Set([...setOfKeys1, ...setOfKeys2])];
// Iterate through the IDs in both datasets
let allIDs = [...new Set([...data['table'].map(item => item.ID), ...searchData['table'].map(item => item.ID)])];
allIDs.forEach(id => {
let baseData = lookupData[id] || {};
let searchItem = lookupSearchData[id] || {};
let mergedItem = {ID: id}; // Initialize with ID
allKeys.forEach(key => {
mergedItem[key] = baseData[key] || searchItem[key] || 0;
});
mergedArray.push(mergedItem);
});
// Now mergedArray will have your required data
let myNewData = {
...searchData,
'conceptsToAddBoxes': myNewConceptsToAddBoxes,
'conceptsOfPeople': myNewConceptsOfPeople,
'tableColumnsInRightOrder': myNewTableColumnsInRightOrder,
'table': mergedArray
}
setSearchData(myNewData)
}
if (concept.id[0] === 'p') {
freeTextSearch('#' + concept.title,
30,
searchEntity,
callbackAddConceptsToBoxes,
'concepts',
searchData,
setSearchData);
}
if (concept.id[0] === 'c') {
freeTextSearch('+' + concept.title,
30,
searchEntity,
callbackAddConceptsToBoxes,
'concepts',
searchData,
setSearchData);
}
};
return (
<div id="selected-id">
{(searchData['conceptsToAddBoxes'] || searchData['tableColumnsInRightOrder']).map((concept, index) => (
<ConceptBox key={index} concept={concept} onRemove={handleRemoveClick}
handleMagicClick={handleMagicClick}
setToolTipVisible={setToolTipVisible}
setToolTipContent={setToolTipContent}
searchData={searchData}
setSearchData={setSearchData}
/>
))}
</div>
);
}
export default React.memo(ConceptBoxes);

Event Timeline