[Haik]: and so it begins
@@ -0,0 +1,29 @@
|
||||
.app-container {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
width: 90vw;
|
||||
height: 100%;
|
||||
padding-left: 5vw;
|
||||
padding-right: 5vw;
|
||||
padding-bottom: 5vh;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.app-header {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
|
||||
width: 100%;
|
||||
height: 10vh;
|
||||
|
||||
font-family: 'Consolas', monospace;
|
||||
font-size: 3.45rem;
|
||||
font-weight: bold; /* Changed from 900 to bold */
|
||||
color: #d8d8d8;
|
||||
text-align: center;
|
||||
|
||||
margin-bottom: 2vh;
|
||||
margin-top: 2vh;
|
||||
}
|
||||
@@ -0,0 +1,223 @@
|
||||
import React, { useState } from 'react';
|
||||
import { Container } from '@mui/material';
|
||||
// import PullButton from './components/pull-button/PullButton'; // Import the FetchButton component
|
||||
import SyncSection from './components/sync-section/SyncSection'; // Import the FetchButton component
|
||||
|
||||
import Tree from './components/tree/Tree'; // Import the Tree component
|
||||
import './App.css';
|
||||
|
||||
const App = () => {
|
||||
const [projectStructure, setProjectStructure] = useState(null);
|
||||
const [expanded, setExpanded] = useState({});
|
||||
|
||||
const handleExpandClick = (id) => {
|
||||
setExpanded((prevExpanded) => ({ ...prevExpanded, [id]: !prevExpanded[id] }));
|
||||
};
|
||||
|
||||
const handleCheckboxChange = (nodeId, checked) => {
|
||||
console.log("Handle checkbox change on node: ", nodeId, " with checked: ", checked); // Log the checkbox change
|
||||
|
||||
const updateNode = (nodes, pathParts, checked, forceCheck = false) => {
|
||||
return nodes.map((node) => {
|
||||
if (node.name === pathParts[0]) {
|
||||
// Check if the node should be force-checked due to a child being checked
|
||||
const shouldCheck = checked || forceCheck;
|
||||
|
||||
if (pathParts.length === 1) {
|
||||
console.log("Node found: ", node.name, " With current checked: ", node.is_toggled);
|
||||
return { ...node, is_toggled: shouldCheck, children: updateChildren(node.children, shouldCheck) };
|
||||
}
|
||||
|
||||
// Recursively update children
|
||||
if (node.children) {
|
||||
const updatedChildren = updateNode(node.children, pathParts.slice(1), checked, shouldCheck);
|
||||
return { ...node, is_toggled: shouldCheck, children: updatedChildren };
|
||||
}
|
||||
|
||||
return { ...node, is_toggled: shouldCheck };
|
||||
}
|
||||
return node;
|
||||
});
|
||||
};
|
||||
|
||||
// Function to update the state of child nodes
|
||||
const updateChildren = (children, checked) => {
|
||||
if (!children) return [];
|
||||
return children.map((child) => ({
|
||||
...child,
|
||||
is_toggled: checked,
|
||||
children: updateChildren(child.children, checked)
|
||||
}));
|
||||
};
|
||||
|
||||
setProjectStructure((prevStructure) => {
|
||||
if (!prevStructure) return prevStructure;
|
||||
const pathParts = nodeId.split('/');
|
||||
const updatedStructure = updateNode(prevStructure, pathParts, checked);
|
||||
return updatedStructure;
|
||||
});
|
||||
};
|
||||
|
||||
const handleEmojiChange = (nodeId, emoji) => {
|
||||
console.log("Handle emoji change on node: ", nodeId, " with emoji: ", emoji);
|
||||
|
||||
// Function to recursively propagate the emoji to all children but not update the ancestors
|
||||
const propagateEmojiToChildren = (node) => {
|
||||
if (!node.children) return node; // If no children, return the node as is
|
||||
|
||||
const updatedChildren = node.children.map((child) => ({
|
||||
...child,
|
||||
emoji, // Set the new emoji to the child node
|
||||
children: propagateEmojiToChildren(child).children, // Recursively propagate to deeper children
|
||||
}));
|
||||
|
||||
return { ...node, children: updatedChildren }; // Update the node's children but not the node itself
|
||||
};
|
||||
|
||||
const updateNode = (nodes, pathParts, emoji) => {
|
||||
return nodes.map((node) => {
|
||||
if (node.name === pathParts[0]) {
|
||||
// Update the emoji of the node itself if this is the target node
|
||||
let updatedNode = { ...node };
|
||||
|
||||
if (pathParts.length === 1) {
|
||||
// This is the target node, update its emoji
|
||||
updatedNode.emoji = emoji;
|
||||
}
|
||||
|
||||
// If this node has children and we haven't reached the target node yet
|
||||
if (node.children && pathParts.length > 1) {
|
||||
const updatedChildren = updateNode(node.children, pathParts.slice(1), emoji);
|
||||
updatedNode = { ...updatedNode, children: updatedChildren };
|
||||
}
|
||||
|
||||
// If we've reached the target node, propagate the emoji to its children
|
||||
if (pathParts.length === 1 && node.children) {
|
||||
updatedNode.children = propagateEmojiToChildren(node).children;
|
||||
}
|
||||
|
||||
return updatedNode;
|
||||
}
|
||||
|
||||
return node; // No match, return the node as is
|
||||
});
|
||||
};
|
||||
|
||||
setProjectStructure((prevStructure) => {
|
||||
if (!prevStructure) return prevStructure;
|
||||
|
||||
const pathParts = nodeId.split('/');
|
||||
const updatedStructure = updateNode(prevStructure, pathParts, emoji);
|
||||
|
||||
return updatedStructure;
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
const defaultColor = '#ff0000'; // Define the default color
|
||||
|
||||
const handleColorChange = (nodeId, color) => {
|
||||
console.log("Handle color change on node: ", nodeId, " with color: ", color); // Log the color change
|
||||
const amount = 50; // Define the amount to lighten the color
|
||||
|
||||
const updateNode = (nodes, pathParts, color, isOriginalParent = false) => {
|
||||
console.log("HIT updateNode with color: ", color); // Log the color
|
||||
return nodes.map((node) => {
|
||||
if (node.name === pathParts[0]) {
|
||||
let name = node.name;
|
||||
let newColor = node.color;
|
||||
let is_toggled = node.is_toggled;
|
||||
let set_manually = node.set_manually;
|
||||
|
||||
// If the node is the target node
|
||||
if (pathParts.length === 1) {
|
||||
console.log("Node found: ", node.name, " With current color: ", node.color);
|
||||
console.log("Setting color to: ", color);
|
||||
console.log("Is original parent: ", isOriginalParent, " Set manually: ", set_manually);
|
||||
if (isOriginalParent) {
|
||||
set_manually = true;
|
||||
}
|
||||
newColor = color;
|
||||
}
|
||||
|
||||
if (node.children) {
|
||||
// Update children nodes
|
||||
const updatedChildren = updateNode(node.children, pathParts.slice(1), color, isOriginalParent);
|
||||
|
||||
// Check and propagate the color to children if needed
|
||||
const propagatedChildren = updatedChildren.map((child) => {
|
||||
console.log("Checking child: ", child.name);
|
||||
console.log("Child set manually: ", child.set_manually);
|
||||
if (!child.set_manually) {
|
||||
console.log("Propagating color to child: ", child.name);
|
||||
const newPath = [...pathParts.slice(1), child.name];
|
||||
return updateNode([child], newPath, lightenColor(color, amount), false)[0];
|
||||
}
|
||||
return child;
|
||||
});
|
||||
console.log("RETURN 1 node: ", node.name, " with color: ", newColor);
|
||||
return {
|
||||
...node,
|
||||
name: name,
|
||||
color: newColor,
|
||||
is_toggled: is_toggled,
|
||||
set_manually: set_manually,
|
||||
children: propagatedChildren
|
||||
};
|
||||
}
|
||||
console.log("RETURN 2 node: ", node.name, " with color: ", newColor);
|
||||
return { ...node, name: name, color: newColor, is_toggled, set_manually};
|
||||
}
|
||||
return node;
|
||||
});
|
||||
};
|
||||
|
||||
// Helper function to lighten a color (example implementation)
|
||||
const lightenColor = (color, amount = 50) => {
|
||||
if (!color || typeof color !== 'string' || !color.startsWith('#') || color.length !== 7) {
|
||||
throw new Error('Invalid color format. Expected format is #RRGGBB. But got: ' + color);
|
||||
}
|
||||
|
||||
const colorInt = parseInt(color.slice(1), 16);
|
||||
const r = Math.min(255, (colorInt >> 16) + amount);
|
||||
const g = Math.min(255, ((colorInt >> 8) & 0x00FF) + amount);
|
||||
const b = Math.min(255, (colorInt & 0x0000FF) + amount);
|
||||
const newColorInt = (r << 16) + (g << 8) + b;
|
||||
// Corrected console.log statement
|
||||
console.log(`Old color: ${color}, New color: #${newColorInt.toString(16).padStart(6, '0')}`); // Log the old and new color
|
||||
return `#${newColorInt.toString(16).padStart(6, '0')}`;
|
||||
};
|
||||
|
||||
setProjectStructure((prevStructure) => {
|
||||
if (!prevStructure) return prevStructure;
|
||||
const pathParts = nodeId.split('/');
|
||||
const updatedStructure = updateNode(prevStructure, pathParts, color, true);
|
||||
console.log('Updated structure:', updatedStructure);
|
||||
return updatedStructure;
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
return (
|
||||
<div className='app-container'>
|
||||
<div className='app-header'>
|
||||
Cluster Labs Debugger v0
|
||||
</div>
|
||||
{/* <PullButton setProjectStructure={setProjectStructure} /> */}
|
||||
{/* <SyncSection setProjectStructure={setProjectStructure} /> */}
|
||||
<SyncSection projectStructure={projectStructure} setProjectStructure={setProjectStructure} />
|
||||
<Tree
|
||||
projectStructure={projectStructure}
|
||||
expanded={expanded}
|
||||
handleExpandClick={handleExpandClick}
|
||||
handleCheckboxChange={handleCheckboxChange}
|
||||
handleColorChange={handleColorChange}
|
||||
handleEmojiChange={handleEmojiChange}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default App;
|
||||
|
After Width: | Height: | Size: 7.1 KiB |
|
After Width: | Height: | Size: 17 KiB |
|
After Width: | Height: | Size: 57 KiB |
|
After Width: | Height: | Size: 86 KiB |
@@ -0,0 +1,128 @@
|
||||
// Description: List of emojis for the debugger
|
||||
export const emojiList = {
|
||||
"R": [
|
||||
'👽','👾','🤖','🦠','🚀','🛸',
|
||||
],
|
||||
"S": [
|
||||
'😀','😃','😄','😁','😆','😅','🤣','😂','🙂','🙃','😉','😊',
|
||||
'😇','🥰','😍','🤩','😘','😗','☺','😚','😙','😋','😛','😜',
|
||||
'🤪','😝','🤑','🤗','🤭','🤫','🤔','🤐','🤨','😐',
|
||||
'😑','😶','😏','😒','🙄','😬','🤥',
|
||||
'😌','😔','😪','🤤','😴','😷','🤒','🤕','🤢','🤮','🤧',
|
||||
'🥵','🥶','🥴','😵','🤯','🤠','🥳','😎','🤓','🧐',
|
||||
'😕','😟','🙁','☹','😮','😯','😲','😳','🥺','😦','😧',
|
||||
'😨','😰','😥','😢','😭','😱','😖','😣','😞','😓','😩','😫',
|
||||
'🥱','😤','😡','😠','🤬','😈','👿','💀','☠','💩','🤡','👹',
|
||||
'👺','👻','👋','🤚','🖐','✋','🖖','👌','🤏','✌','🤞','🤟','🤘','🤙','👈','👉',
|
||||
'👆','🖕','👇','☝','👍','👎','✊','👊','🤛','🤜','👏','🙌','👐','🤲','🤝','🙏','✍','💅','🤳',
|
||||
'💪','🦾','🦿','🦵','🦶','👂','🦻','👃','🧠','🦷','🦴','👀','👁',
|
||||
'👅','👄','👶','🧒','👦','👧','🧑','👱','👨','🧔','👨🦰',
|
||||
'👨🦱','👨🦳','👨🦲','👩','👩🦰','👩🦱','👩🦳','👩🦲','👱♀️',
|
||||
'👱♂️','🧓','👴','👵','🙍','🙍♂️','🙍♀️','🙎','🙎♂️','🙎♀️','🙅','🙅♂️','🙅♀️','🙆','🙆♂️',
|
||||
'🙆♀️','💁','💁♂️','💁♀️','🙋','🙋♂️','🙋♀️','🧏','🧏♂️','🧏♀️','🙇','🙇♂️','🙇♀️','🤦','🤦♂️',
|
||||
'🤦♀️','🤷','🤷♂️','🤷♀️','👨⚕️','👩⚕️','👨🎓','👩🎓','👨🏫','👩🏫',
|
||||
'👨⚖️','👩⚖️','👨🌾','👩🌾','👨🍳','👩🍳','👨🔧','👩🔧','👨🏭','👩🏭',
|
||||
'👨💼','👩💼',,'👨🔬','👩🔬','👨💻','👩💻','👨🎤','👩🎤','👨🎨',
|
||||
'👩🎨','👨✈️','👩✈️','👨🚀','👩🚀','👨🚒','👩🚒','👮','👮♂️','👮♀️','🕵','🕵️♂️',
|
||||
'🕵️♀️','💂','💂♂️','💂♀️','👷','👷♂️','👷♀️','🤴','👸','👳','👳♂️','👳♀️','👲','🧕','🤵',
|
||||
'👰','🤰','🤱','👼','🎅',
|
||||
'🤶','🦸','🦸♂️','🦸♀️','🦹','🦹♂️','🦹♀️','🧙','🧙♂️','🧙♀️','🧚','🧚♂️','🧚♀️','🧛',
|
||||
'🧛♂️','🧛♀️','🧜','🧜♂️','🧜♀️','🧝','🧝♂️','🧝♀️','🧞','🧞♂️','🧞♀️','🧟','🧟♂️','🧟♀️','💆',
|
||||
'💆♂️','💆♀️','💇','💇♂️','💇♀️','🚶','🚶♂️','🚶♀️','🧍','🧍♂️','🧍♀️','🧎',
|
||||
'🧎♂️','🧎♀️','👨🦯','👩🦯',
|
||||
'👨🦼','👩🦼','👨🦽','👩🦽','🏃',
|
||||
'🏃♂️','🏃♀️','💃','🕺','🕴','👯','👯♂️','👯♀️','🧖','🧖♂️','🧖♀️','🧗',
|
||||
'🧗♂️','🧗♀️','🤺','🏇','⛷','🏂','🏌','🏌️♂️','🏌️♀️','🏄','🏄♂️','🏄♀️','🚣','🚣♂️','🚣♀️','🏊',
|
||||
'🏊♂️','🏊♀️','⛹','⛹️♂️','⛹️♀️','🏋','🏋️♂️','🏋️♀️','🚴','🚴♂️','🚴♀️','🚵','🚵♂️','🚵♀️','🤸','🤸♂️',
|
||||
'🤸♀️','🤼','🤼♂️','🤼♀️','🤽','🤽♂️','🤽♀️','🤾','🤾♂️','🤾♀️','🤹','🤹♂️','🤹♀️','🧘','🧘♂️','🧘♀️',
|
||||
'🛀','🛌','🗣','👤','👥','👪','👣','🦰','🦱','🦳','🦲',
|
||||
],
|
||||
"A": [
|
||||
'🐕','🦮','🐕🦺','🐩','🐺','🦊','🦝','🐱','😺','😸','😹','😻','😼','😽','🙀','😿','😾','🙈','🙉','🙊','🐵','🐒','🦍','🦧',
|
||||
'🐈','🦁','🐯','🐅','🐆','🐴','🐎','🦄','🦓','🦌',
|
||||
'🐮','🐂','🐃','🐄','🐷','🐖','🐗','🐽','🐏','🐑','🐐','🐪','🐫','🦙',
|
||||
'🦒','🐘','🦏','🦛','🐭','🐁','🐀','🐹','🐰','🐇','🐿','🦔','🦇',
|
||||
'🐻','🐨','🐼','🦥','🦦','🦨','🦘','🦡','🐾','🦃','🐔','🐓','🐣',
|
||||
'🐤','🐥','🐦','🐧','🕊','🦅','🦆','🦢','🦉','🦩','🦚','🦜',
|
||||
'🐸','🐊','🐢','🦎','🐍','🐲','🐉','🦕','🦖','🐳','🐋','🐬','🐟',
|
||||
'🐠','🐡','🦈','🐙','🐚','🐌','🦋','🐛','🐜','🐝','🐞','🦗','🕷',
|
||||
'🕸','🦂','🦟','🍇','🍈',
|
||||
'🛎','💐','🌸','💮','🏵','🌹','🥀','🌺','🌻','🌼','🌷',
|
||||
'🌱','🌲','🌳','🌴','🌵','🌾','🌿','☘','🍀','🍁','🍂','🍃','🍄','🎃','🎄','🎋','🎍',
|
||||
],
|
||||
"F": [
|
||||
'🍉','🍊','🍋','🍌','🍍','🥭','🍎','🍏','🍐','🍑','🍒','🍓','🥝','🍅',
|
||||
'🥥','🥑','🍆','🥔','🥕','🌽','🌶','🥒','🥬','🥦','🧄','🧅','🥜','🌰',
|
||||
'🍞','🥐','🥖','🥨','🥯','🥞','🧇','🧀','🍖','🍗','🥩','🥓','🍔','🍟','🍕',
|
||||
'🌭','🥪','🌮','🌯','🥙','🧆','🥚','🍳','🥘','🍲','🥣','🥗','🍿','🧈','🧂','🥫',
|
||||
'🍱','🍘','🍙','🍚','🍛','🍜','🍝','🍠','🍢','🍣','🍤','🍥','🥮','🍡','🥟','🥠','🥡',
|
||||
'🦀','🦞','🦐','🦑','🦪','🍦','🍧','🍨','🍩','🍪','🎂','🍰','🧁','🥧','🍫','🍬','🍭','🍮',
|
||||
'🍯','🍼','🥛','☕','🍵','🍶','🍾','🍷','🍸','🍹','🍺','🍻','🥂','🥃','🥤','🧃',
|
||||
'🧉','🧊','🥢','🍽','🍴','🥄','🔪',
|
||||
],
|
||||
"G": [
|
||||
'⚽','⚾','🥎','🏀','🏐','🏈','🏉','🎾','🥏',
|
||||
'🎳','🏏','🏑','🏒','🥍','🏓','🏸','🥊','🥋','🥅','⛳','⛸','🎣','🤿','🎽','🎿','🛷','🥌',
|
||||
'♟','🃏','🀄','🎴','🎭','🖼','🎨','🧵','🎖','🏆','🏅','🥇','🥈','🥉',
|
||||
'🎯','🪀','🪁','🔫','🎱','🔮','🎮','🕹','🎰','🎲','🧩','🧸','♠','♥','♦','♣',
|
||||
],
|
||||
"W": [
|
||||
'🌍','🌎','🌏','🌐','🗺','🗾','🧭','🏔','⛰','🌋',
|
||||
'🗻','🏕','🏖','🏜','🏝','🏞','🏟','🏛','🏗','🧱','🏘','🏚','🏠','🏡','🏢','🏣','🏤',
|
||||
'🏥','🏦','🏨','🏩','🏪','🏫','🏬','🏭','🏯','🏰','💒','🗼','🗽','⛪','🕌','🛕','🕍','⛩',
|
||||
'🕋','⛲','⛺','🌁','🌃','🏙','🌄','🌅','🌆','🌇','🌉','♨','🎠','🎡','🎢','💈','🎪','🚂',
|
||||
'🚃','🚄','🚅','🚆','🚇','🚈','🚉','🚊','🚝','🚞','🚋','🚌','🚍','🚎','🚐','🚑','🚒','🚓',
|
||||
'🚔','🚕','🚖','🚗','🚘','🚙','🚚','🚛','🚜','🏎','🏍','🛵','🦽','🦼','🛺','🚲','🛴','🛹',
|
||||
'🚏','🛣','🛤','🛢','⛽','🚨','🚥','🚦','🛑','🚧','⚓','⛵','🛶','🚤','🛳','⛴','🛥',
|
||||
'🚢','✈','🛩','🛫','🛬','🪂','💺','🚁','🚟','🚠','🚡','🛰','🧳','🎎','🎏','🎐','🎑','🧧','🎀','🎁',
|
||||
'🎗','🎟','🎫','🏺',
|
||||
],
|
||||
"T": [
|
||||
'⌛','⏳','⌚',
|
||||
'⏰','⏱','⏲','🕰','🕛','🕧','🕐','🕜','🕑','🕝','🕒','🕞','🕓','🕟','🕔','🕠','🕕','🕡',
|
||||
'🕖','🕢','🕗','🕣','🕘','🕤','🕙','🕥','🕚','🕦','🌑','🌒','🌓','🌔','🌕','🌖','🌗','🌘',
|
||||
'🌙','🌚','🌛','🌜','🌡','☀','🌝','🌞','🪐','⭐','🌟','🌠','🌌','☁','⛅','⛈','🌤','🌥','🌦',
|
||||
'🌧','🌨','🌩','🌪','🌫','🌬','🌀','🌈','🌂','☂','☔','⛱','⚡','❄','☃','⛄','☄','🔥','💧','🌊',
|
||||
'🎆','🎇','🧨','✨','🎈','🎉','🎊',
|
||||
],
|
||||
"X": [
|
||||
'💻','🖥','🖨','⌨','🖱','🖲','💽','💾','💿','📀','🧮','🎥','🎞','📽',
|
||||
'🎬','📺','📷','📸','📹','📼','🔍','🔎','🕯','💡','🔦',
|
||||
'🎙','🎚','🎛','🎤','🎧','📻','🎷','🎸',
|
||||
'🎹','🎺','🎻','🪕','🥁','📱','📲','☎','📞','📟','📠','🔋',
|
||||
'🔌','🏮','🪔','📔','📕','💌',
|
||||
'📖','📗','📘','📙','📚','📓','📒','📃','📜','📄','📰','🗞','📑','🔖','🏷',
|
||||
'💰','💴','💵','💶','💷','💸','💳','🧾','💹','✉','📧','📨','📩','📤',
|
||||
'📥','📦','📫','📪','📬','📭','📮','🗳','✏','✒','🖋','🖊','🖌','🖍','📝',
|
||||
'💼','📁','📂','🗂','📅','📆','🗒','🗓','📇','📈','📉','📊','📋','📌','📍',
|
||||
'📎','🖇','📏','📐','✂','🗃','🗄','🗑','🔒','🔓','🔏','🔐','🔑','🗝','🔨','🪓',
|
||||
'⛏','⚒','🛠','🗡','⚔','💣','🏹','🛡','🔧','🔩','⚙','🗜','⚖','🦯',
|
||||
'🔗','⛓','🧰','🧲','⚗','🧪','🧫','🧬','🔬','🔭','📡','💉','🩸',
|
||||
'💊','🩹','🩺','🚪','🛏','🛋','🪑','🚽','🚿','🛁','🪒',
|
||||
'🧴','🧷','🧹','🧺','🧻','🧼','🧽','🧯','🛒','🚬','⚰','⚱','🧿',
|
||||
],
|
||||
"O": [
|
||||
'💘','💝','💖','💗','💓','💞','💕','💟','❣','💔',
|
||||
'❤','🧡','💛','💚','💙','💜','🤎','🖤','🤍',
|
||||
'💋','💯','💢','💥','💫','💦','💨','🕳','💬','👁️🗨️','🗨',
|
||||
'🗯','💭','💤','🧶','👓','🕶','🥽','🥼','🦺','👔','👕','👖','🧣','🧤','🧥',
|
||||
'🧦','👗','👘','🥻','🩱','🩲','🩳','👙','👚','👛','👜','👝','🛍',
|
||||
'🎒','👞','👟','🥾','🥿','👠','👡','🩰','👢','👑','👒','🎩',
|
||||
'🎓','🧢','⛑','📿','💄','💍','💎','🔇','🔈','🔉','🔊','📢','📣',
|
||||
'📯','🔔','🔕','🎼','🎵','🎶',
|
||||
'🗿','🏧','🚮','🚰','♿','🚹','🚺','🚻','🚼','🚾','🛂','🛃','🛄',
|
||||
'🛅','⚠','🚸','⛔','🚫','🚳','🚭','🚯','🚱','🚷','📵','🔞','☢','☣','⬆','↗',
|
||||
'➡','↘','⬇','↙','⬅','↖','↕','↔','↩','↪','⤴','⤵','🔃','🔄','🔙','🔚','🔛','🔜',
|
||||
'🔝','🛐','⚛','🕉','✡','☸','☯','✝','☦','☪','☮','🕎','🔯','♈','♉','♊',
|
||||
'♋','♌','♍','♎','♏','♐','♑','♒','♓','⛎','🔀','🔁','🔂','▶','⏩','⏭',
|
||||
'⏯','◀','⏪','⏮','🔼','⏫','🔽','⏬','⏸','⏹','⏺','⏏','🎦','🔅','🔆','📶',
|
||||
'📳','📴','♀','♂','⚧','✖','➕','➖','➗','♾','‼','⁉','❓','❔','❕','❗','〰',
|
||||
'💱','💲','⚕','♻','⚜','🔱','📛','🔰','⭕','✅','☑','✔','❌','❎','➰','➿','〽','✳',
|
||||
'✴','❇','©','®','™','#️⃣','*️⃣','0️⃣','1️⃣','2️⃣','3️⃣','4️⃣','5️⃣','6️⃣','7️⃣','8️⃣','9️⃣',
|
||||
'🔟','🔠','🔡','🔢','🔣','🔤','🅰','🆎','🅱','🆑','🆒','🆓','ℹ','🆔','Ⓜ','🆕','🆖',
|
||||
'🅾','🆗','🅿','🆘','🆙','🆚','🈁','🈂','🈷','🈶','🈯','🉐','🈹','🈚','🈲','🉑','🈸',
|
||||
'🈴','🈳','㊗','㊙','🈺','🈵','🔴','🟠','🟡','🟢','🔵','🟣','🟤','⚫','⚪','🟥',
|
||||
'🟧','🟨','🟩','🟦','🟪','🟫','⬛','⬜','◼','◻','◾','◽','▪','▫','🔶','🔷','🔸','🔹',
|
||||
'🔺','🔻','💠','🔘','🔳','🔲','🏁','🚩','🎌','🏴','🏳','🏳️⚧️','🏴☠️',
|
||||
],
|
||||
}
|
||||
|
After Width: | Height: | Size: 7.5 KiB |
|
After Width: | Height: | Size: 19 KiB |
|
After Width: | Height: | Size: 19 KiB |
|
After Width: | Height: | Size: 11 KiB |
|
After Width: | Height: | Size: 10 KiB |
@@ -0,0 +1,57 @@
|
||||
.color-button-container {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
justify-content: flex-start;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.color-button-img {
|
||||
height: 100px;
|
||||
}
|
||||
|
||||
.color-button-content {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.color-button {
|
||||
background-color: rgba(255, 255, 255, 0);
|
||||
color: black;
|
||||
border: none;
|
||||
font-size: 1.45rem;
|
||||
font-weight: bold; /* Changed from 900 to bold */
|
||||
font-family: 'Consolas', monospace;
|
||||
cursor: pointer;
|
||||
border-radius: 400px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
padding: 10px;
|
||||
}
|
||||
|
||||
.color-button:hover {
|
||||
background-color: #9292926c;
|
||||
}
|
||||
|
||||
.color-button:active {
|
||||
background-color: #929292ce;
|
||||
}
|
||||
|
||||
.color-button:focus {
|
||||
outline: none;
|
||||
}
|
||||
|
||||
.color-button-container p {
|
||||
color: #D9D9D9;
|
||||
font-family: 'Consolas', monospace;
|
||||
margin: 0;
|
||||
text-align: center;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
import React from 'react';
|
||||
import axios from 'axios';
|
||||
import pushImg from '../../assets/color-reset.png'; // Adjust the path according to your project structure
|
||||
import './ColorReset.css'; // Import the CSS file
|
||||
|
||||
const ColorReset = ({ projectStructure, setProjectStructure }) => {
|
||||
const pushStructure = async () => {
|
||||
try {
|
||||
console.log("Resetting colors");
|
||||
const response = await axios.post('http://127.0.0.1:6969/reset_color');
|
||||
console.log('Project structure:', response.data);
|
||||
setProjectStructure(response.data);
|
||||
} catch (error) {
|
||||
console.error('Error pushing project structure:', error);
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="color-button-container">
|
||||
<div className="color-button-content">
|
||||
<button className="color-button" onClick={pushStructure}>
|
||||
<img src={pushImg} alt="Project Structure" className="color-button-img" />
|
||||
</button>
|
||||
<p>Wipe colors</p>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default ColorReset;
|
||||
@@ -0,0 +1,129 @@
|
||||
/* EmojiPicker.css */
|
||||
|
||||
.picker-wrapper {
|
||||
position: relative;
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
.picker-button {
|
||||
font-size: 1.5rem;
|
||||
background: none;
|
||||
border: none;
|
||||
cursor: pointer;
|
||||
font-family: "Apple Color Emoji", "Segoe UI Emoji", "Noto Color Emoji", sans-serif;
|
||||
}
|
||||
|
||||
.emoji-popup {
|
||||
position: absolute;
|
||||
top: 2.5rem;
|
||||
left: 0;
|
||||
width: fit-content;
|
||||
padding: 10px;
|
||||
border: 7px solid #cccccc46;
|
||||
border-radius: 35px;
|
||||
background-color: #181818ef;
|
||||
box-shadow: 0 0 10px rgba(0, 0, 0, 0.1);
|
||||
z-index: 1000;
|
||||
}
|
||||
|
||||
.folder-section {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
justify-content: space-around;
|
||||
height: fit-content;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.folder-item {
|
||||
cursor: pointer;
|
||||
padding: 10px;
|
||||
text-align: left;
|
||||
background-color: #00000000;
|
||||
margin: 5px 0;
|
||||
border-radius: 5px;
|
||||
color: #ffffff;
|
||||
}
|
||||
|
||||
.folder-item:hover {
|
||||
background-color: #ffffff35;
|
||||
}
|
||||
|
||||
.folder-item.active {
|
||||
background-color: #ffffff15; /* Active color for the selected folder */
|
||||
}
|
||||
|
||||
.folder-item.active:hover {
|
||||
background-color: #ffffff35; /* Active color for the selected folder */
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
.emoji-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(6, 1fr);
|
||||
gap: 10px;
|
||||
height: 260px;
|
||||
width: 330px;
|
||||
font-family: "Apple Color Emoji", "Segoe UI Emoji", "Noto Color Emoji", sans-serif;
|
||||
}
|
||||
|
||||
.emoji-item {
|
||||
cursor: pointer;
|
||||
padding: 5px;
|
||||
text-align: center;
|
||||
height: fit-content;
|
||||
border-radius: 5px;
|
||||
font-size: 1.5rem;
|
||||
font-family: "Apple Color Emoji", "Segoe UI Emoji", "Noto Color Emoji", sans-serif;
|
||||
}
|
||||
|
||||
.emoji-item:hover {
|
||||
background-color: #ffffff35;
|
||||
}
|
||||
|
||||
.pagination {
|
||||
margin-top: 10px;
|
||||
text-align: center;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
gap: 5px;
|
||||
}
|
||||
|
||||
.pagination button {
|
||||
background-color: #00000000;
|
||||
border: 0;
|
||||
gap: 0;
|
||||
width: 18px;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
cursor: pointer;
|
||||
padding: 0 16px;
|
||||
border-radius: 50px;
|
||||
color: #ffffff;
|
||||
height: fit-content;
|
||||
}
|
||||
|
||||
.pagination button img {
|
||||
height: 100px;
|
||||
width: 30px;
|
||||
object-fit: cover;
|
||||
|
||||
}
|
||||
|
||||
.pagination button:disabled {
|
||||
cursor: default;
|
||||
}
|
||||
|
||||
.pagination button:hover {
|
||||
background-color: #ffffff15;
|
||||
}
|
||||
|
||||
.selected-emoji-display {
|
||||
margin-top: 20px;
|
||||
font-family: "Apple Color Emoji", "Segoe UI Emoji", "Noto Color Emoji", sans-serif;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,141 @@
|
||||
import React, { useState, useEffect } from "react";
|
||||
import { emojiList } from '../../assets/emojis';
|
||||
import PrevPage from '../../assets/prev-page.png';
|
||||
import NextPage from '../../assets/next-page.png';
|
||||
import './EmojiPicker.css'; // Import CSS file
|
||||
|
||||
const EMOJIS_PER_PAGE = 30;
|
||||
|
||||
const EmojiPicker = ({ defaultEmoji, handleEmojiChange }) => {
|
||||
const folderNames = Object.keys(emojiList); // Get all folder names
|
||||
const firstFolder = folderNames[0]; // Get the first folder name
|
||||
const [showPicker, setShowPicker] = useState(false);
|
||||
const [selectedEmoji, setSelectedEmoji] = useState(defaultEmoji || "😀"); // Initialize with the default emoji from props or fallback to smiley
|
||||
const [currentPage, setCurrentPage] = useState(0);
|
||||
const [currentFolder, setCurrentFolder] = useState(firstFolder); // Initialize with first folder
|
||||
|
||||
// When defaultEmoji changes (e.g., from backend data), update selectedEmoji
|
||||
useEffect(() => {
|
||||
if (defaultEmoji) {
|
||||
setSelectedEmoji(defaultEmoji);
|
||||
}
|
||||
}, [defaultEmoji]);
|
||||
|
||||
// Handle folder click
|
||||
const handleFolderClick = (folderName) => {
|
||||
setCurrentFolder(folderName); // Set the current folder
|
||||
setCurrentPage(0); // Reset page to 0 when switching folders
|
||||
};
|
||||
|
||||
// Get the emojis for the selected folder
|
||||
const emojis = currentFolder ? emojiList[currentFolder] : [];
|
||||
const totalPages = Math.ceil(emojis.length / EMOJIS_PER_PAGE);
|
||||
const currentEmojis = emojis.slice(
|
||||
currentPage * EMOJIS_PER_PAGE,
|
||||
(currentPage + 1) * EMOJIS_PER_PAGE
|
||||
);
|
||||
|
||||
const handleEmojiClick = (emoji) => {
|
||||
setSelectedEmoji(emoji);
|
||||
setShowPicker(false); // Close the picker when an emoji is selected
|
||||
handleEmojiChange(emoji); // Call the parent handler with the selected emoji
|
||||
};
|
||||
|
||||
const togglePicker = () => {
|
||||
setShowPicker((prev) => !prev); // Toggle the picker visibility
|
||||
};
|
||||
|
||||
const goToNextFolder = () => {
|
||||
const currentIndex = folderNames.indexOf(currentFolder);
|
||||
const nextFolderIndex = currentIndex + 1;
|
||||
if (nextFolderIndex < folderNames.length) {
|
||||
setCurrentFolder(folderNames[nextFolderIndex]);
|
||||
setCurrentPage(0); // Reset to the first page of the next folder
|
||||
}
|
||||
};
|
||||
|
||||
const goToPreviousFolder = () => {
|
||||
const currentIndex = folderNames.indexOf(currentFolder);
|
||||
const previousFolderIndex = currentIndex - 1;
|
||||
if (previousFolderIndex >= 0) {
|
||||
const previousFolder = folderNames[previousFolderIndex];
|
||||
setCurrentFolder(previousFolder);
|
||||
const lastPageOfPreviousFolder = Math.ceil(emojiList[previousFolder].length / EMOJIS_PER_PAGE) - 1;
|
||||
setCurrentPage(lastPageOfPreviousFolder); // Set to the last page of the previous folder
|
||||
}
|
||||
};
|
||||
|
||||
const goToNextPage = () => {
|
||||
if (currentPage < totalPages - 1) {
|
||||
setCurrentPage(currentPage + 1);
|
||||
} else {
|
||||
goToNextFolder();
|
||||
}
|
||||
};
|
||||
|
||||
const goToPreviousPage = () => {
|
||||
if (currentPage > 0) {
|
||||
setCurrentPage(currentPage - 1);
|
||||
} else {
|
||||
goToPreviousFolder();
|
||||
}
|
||||
};
|
||||
|
||||
const isLastFolder = currentFolder === folderNames[folderNames.length - 1];
|
||||
const isLastPageOfLastFolder = isLastFolder && currentPage === totalPages - 1;
|
||||
const isFirstFolder = currentFolder === folderNames[0];
|
||||
const isFirstPageOfFirstFolder = isFirstFolder && currentPage === 0;
|
||||
|
||||
return (
|
||||
<div className="picker-wrapper">
|
||||
{/* The emoji picker icon that changes when an emoji is selected */}
|
||||
<button onClick={togglePicker} className="picker-button">
|
||||
{selectedEmoji}
|
||||
</button>
|
||||
|
||||
{/* Hoverable emoji picker */}
|
||||
{showPicker && (
|
||||
<div className="emoji-popup">
|
||||
<div className="emoji-section">
|
||||
<div className="pagination">
|
||||
<button onClick={goToPreviousPage} disabled={isFirstPageOfFirstFolder}>
|
||||
<img src={PrevPage} alt="Previous Page" />
|
||||
</button>
|
||||
<div className="emoji-grid">
|
||||
{currentEmojis.map((emoji, index) => (
|
||||
<span
|
||||
key={index}
|
||||
className="emoji-item"
|
||||
onClick={() => handleEmojiClick(emoji)}
|
||||
>
|
||||
{emoji}
|
||||
</span>
|
||||
))}
|
||||
</div>
|
||||
<button
|
||||
onClick={goToNextPage}
|
||||
disabled={isLastPageOfLastFolder}
|
||||
>
|
||||
<img src={NextPage} alt="Next Page" />
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="folder-section">
|
||||
{folderNames.map((folderName, index) => (
|
||||
<div
|
||||
key={index}
|
||||
className={`folder-item ${folderName === currentFolder ? 'active' : ''}`}
|
||||
onClick={() => handleFolderClick(folderName)}
|
||||
>
|
||||
{emojiList[folderName][0]}
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default EmojiPicker;
|
||||
@@ -0,0 +1,49 @@
|
||||
.pull-button-container {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
justify-content: flex-start;
|
||||
width: 40vw;
|
||||
}
|
||||
|
||||
.pull-button-img {
|
||||
margin-right: 20px;
|
||||
height: 100px;
|
||||
}
|
||||
|
||||
.pull-button-content {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: flex-start;
|
||||
}
|
||||
|
||||
.pull-button {
|
||||
background-color: #A3FFA9;
|
||||
color: black;
|
||||
border: none;
|
||||
padding: 10px 20px;
|
||||
font-size: 1.45rem;
|
||||
font-weight: bold; /* Changed from 900 to bold */
|
||||
font-family: 'Consolas', monospace;
|
||||
cursor: pointer;
|
||||
margin: 20px 0;
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
.pull-button:hover {
|
||||
background-color: #84d488;
|
||||
}
|
||||
|
||||
.pull-button:active {
|
||||
background-color: #65a568;
|
||||
}
|
||||
|
||||
.pull-button:focus {
|
||||
outline: none;
|
||||
}
|
||||
|
||||
.pull-button-container p {
|
||||
color: #A3FFA9;
|
||||
font-family: 'Consolas', monospace;
|
||||
margin: 0;
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
import React from 'react';
|
||||
import { Button } from '@mui/material';
|
||||
import axios from 'axios';
|
||||
import pullImg from '../../assets/pull.png'; // Adjust the path according to your project structure
|
||||
import './PullButton.css';
|
||||
|
||||
|
||||
const PullButton = ({ setProjectStructure }) => {
|
||||
const pullStructure = async () => {
|
||||
try {
|
||||
const response = await axios.get('http://127.0.0.1:6969/pull_structure');
|
||||
console.log('Project structure:', response.data);
|
||||
setProjectStructure(response.data);
|
||||
} catch (error) {
|
||||
console.error('Error fetching project structure:', error);
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="pull-button-container">
|
||||
<img src={pullImg} alt="Project Structure" className="pull-button-img" />
|
||||
<div className="pull-button-content">
|
||||
<button className="pull-button" onClick={pullStructure}>
|
||||
Pull
|
||||
</button>
|
||||
<p>Pull debugger config from backend</p>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default PullButton;
|
||||
@@ -0,0 +1,50 @@
|
||||
.push-button-container {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
justify-content: flex-start;
|
||||
width: 40vw;
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
.push-button-img {
|
||||
margin-left: 20px;
|
||||
height: 100px;
|
||||
}
|
||||
|
||||
.push-button-content {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: flex-end;
|
||||
}
|
||||
|
||||
.push-button {
|
||||
background-color: #FFA3A4;
|
||||
color: black;
|
||||
border: none;
|
||||
padding: 10px 20px;
|
||||
font-size: 1.45rem;
|
||||
font-weight: bold; /* Changed from 900 to bold */
|
||||
font-family: 'Consolas', monospace;
|
||||
cursor: pointer;
|
||||
margin: 20px 0;
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
.push-button:hover {
|
||||
background-color: #cc8182;
|
||||
}
|
||||
|
||||
.push-button:active {
|
||||
background-color: #a86768;
|
||||
}
|
||||
|
||||
.push-button:focus {
|
||||
outline: none;
|
||||
}
|
||||
|
||||
.push-button-container p {
|
||||
color: #FFA3A4;
|
||||
font-family: 'Consolas', monospace;
|
||||
margin: 0;
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
import React from 'react';
|
||||
import axios from 'axios';
|
||||
import pushImg from '../../assets/push.png'; // Adjust the path according to your project structure
|
||||
import './PushButton.css'; // Import the CSS file
|
||||
|
||||
const PushButton = ({ projectStructure, setProjectStructure }) => {
|
||||
const pushStructure = async () => {
|
||||
try {
|
||||
console.log("Pushing project structure to backend: ", projectStructure);
|
||||
const response = await axios.post('http://127.0.0.1:6969/push_structure', {
|
||||
projectStructure // Include the projectStructure in the POST request body
|
||||
});
|
||||
console.log('Project structure pushed:', response.data);
|
||||
setProjectStructure(response.data);
|
||||
} catch (error) {
|
||||
console.error('Error pushing project structure:', error);
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="push-button-container">
|
||||
<div className="push-button-content">
|
||||
<button className="push-button" onClick={pushStructure}>
|
||||
Push
|
||||
</button>
|
||||
<p>Push debugger config to backend</p>
|
||||
</div>
|
||||
<img src={pushImg} alt="Project Structure" className="push-button-img" />
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default PushButton;
|
||||
@@ -0,0 +1,16 @@
|
||||
.sync-section-container {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
justify-content: space-between;
|
||||
width: 100vw;
|
||||
align-items: center;
|
||||
padding: 20px;
|
||||
box-sizing: border-box;
|
||||
gap: 0
|
||||
}
|
||||
|
||||
.sync-section-container > div {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
import React from 'react';
|
||||
import PullButton from '../pull-button/PullButton';
|
||||
import PushButton from '../push-button/PushButton';
|
||||
import ColorReset from '../color-reset/ColorReset';
|
||||
import './SyncSection.css'; // Import the CSS file
|
||||
|
||||
const SyncSection = ({ projectStructure, setProjectStructure }) => {
|
||||
return (
|
||||
<div className="sync-section-container">
|
||||
<PullButton setProjectStructure={setProjectStructure} />
|
||||
<ColorReset setProjectStructure={setProjectStructure} />
|
||||
<PushButton projectStructure={projectStructure} setProjectStructure={setProjectStructure} />
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default SyncSection;
|
||||
@@ -0,0 +1,7 @@
|
||||
.tree-container {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
align-items: center;
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
import React from 'react';
|
||||
import TreeNode from '../tree_node/TreeNode';
|
||||
import './Tree.css';
|
||||
|
||||
const Tree = ({ projectStructure, expanded, handleExpandClick, handleCheckboxChange, handleColorChange, handleEmojiChange}) => {
|
||||
const renderTree = (node, parentId = '') => {
|
||||
const nodeId = parentId ? `${parentId}/${node.name}` : node.name;
|
||||
|
||||
return (
|
||||
<TreeNode
|
||||
key={nodeId}
|
||||
node={node}
|
||||
nodeId={nodeId}
|
||||
expanded={expanded}
|
||||
handleExpandClick={handleExpandClick}
|
||||
handleCheckboxChange={handleCheckboxChange}
|
||||
handleColorChange={handleColorChange}
|
||||
handleEmojiChange={handleEmojiChange}
|
||||
renderTree={renderTree}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
if (!Array.isArray(projectStructure)) return null; // Ensure projectStructure is an array
|
||||
|
||||
return (
|
||||
<div className='tree-container'>
|
||||
{projectStructure.map((node) => renderTree(node))}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default Tree;
|
||||
@@ -0,0 +1,88 @@
|
||||
.tree-node {
|
||||
border: 1px solid #ccc;
|
||||
margin-top: 10px;
|
||||
margin-bottom: 10px;
|
||||
padding: 10px;
|
||||
border-radius: 4px;
|
||||
width: 100%;
|
||||
background-color: #181818;
|
||||
}
|
||||
|
||||
.tree-node-content {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.tree-node-content-main {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
width: fit-content;
|
||||
}
|
||||
|
||||
.expand-button {
|
||||
background: none;
|
||||
border: none;
|
||||
cursor: pointer;
|
||||
margin-right: 10px;
|
||||
padding: 0; /* Ensure no padding around the button */
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.expand-button img {
|
||||
width: 16px; /* Adjust the width as needed */
|
||||
height: 16px; /* Adjust the height as needed */
|
||||
max-width: 100%; /* Ensure it doesn't exceed button size */
|
||||
max-height: 100%; /* Ensure it doesn't exceed button size */
|
||||
}
|
||||
|
||||
.tree-node-text {
|
||||
margin-left: 10px;
|
||||
font-size: 1.45rem;
|
||||
font-weight: 600;
|
||||
font-family: 'Consolas', monospace; /* Specify fallback font */
|
||||
color: #ffffff; /* Adjust text color to ensure it's visible on the dark background */
|
||||
}
|
||||
|
||||
.tree-node-children {
|
||||
margin-left: 20px;
|
||||
margin-right: 20px;
|
||||
margin-bottom: 10px;
|
||||
padding-left: 20px;
|
||||
padding-right: 20px;
|
||||
gap: 10px;
|
||||
}
|
||||
|
||||
.tree-node-content-secondary {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.color-picker-wrapper {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.color-picker-icon {
|
||||
display: inline-block;
|
||||
width: 24px;
|
||||
height: 24px;
|
||||
cursor: pointer;
|
||||
border: 1px solid #000;
|
||||
border-radius: 4px;
|
||||
overflow: hidden;
|
||||
background-size: 100% 100%; /* Ensure the background color scales to fit */
|
||||
background-clip: content-box;
|
||||
background-color: transparent; /* Ensure the background is transparent */
|
||||
-webkit-mask: url('../../assets/color-picker.png') center no-repeat;
|
||||
mask: url('../../assets/color-picker.png') center no-repeat;
|
||||
-webkit-mask-size: 20px; /* Adjust this value to fit your icon */
|
||||
mask-size: 20px; /* Adjust this value to fit your icon */
|
||||
}
|
||||
|
||||
.color-picker-icon img {
|
||||
display: none;
|
||||
}
|
||||
@@ -0,0 +1,52 @@
|
||||
import './TreeNode.css';
|
||||
import React from 'react';
|
||||
import plusIcon from '../../assets/collapsed.png';
|
||||
import minusIcon from '../../assets/expanded.png';
|
||||
import colorIcon from '../../assets/color-picker.png'; // Import your custom color icon
|
||||
import EmojiPicker from '../emoji-picker/EmojiPicker';
|
||||
|
||||
const TreeNode = ({ node, nodeId, expanded, handleExpandClick, handleCheckboxChange, handleColorChange, handleEmojiChange, renderTree }) => (
|
||||
<div className="tree-node">
|
||||
<div className="tree-node-content">
|
||||
<div className="tree-node-content-main">
|
||||
{node.children && node.children.length > 0 && (
|
||||
<button className="expand-button" onClick={() => handleExpandClick(nodeId)}>
|
||||
<img src={expanded[nodeId] ? minusIcon : plusIcon} alt="Expand/Collapse Icon" />
|
||||
</button>
|
||||
)}
|
||||
<input
|
||||
type="checkbox"
|
||||
checked={node.is_toggled}
|
||||
onChange={(e) => handleCheckboxChange(nodeId, e.target.checked)}
|
||||
/>
|
||||
{/* Pass node.emoji along with handleEmojiChange */}
|
||||
<EmojiPicker
|
||||
defaultEmoji={node.emoji}
|
||||
handleEmojiChange={(emoji) => handleEmojiChange(nodeId, emoji)}
|
||||
/>
|
||||
<span className="tree-node-text" style={{ color: node.color || '#000000' }}>{node.name}</span>
|
||||
</div>
|
||||
<div className="tree-node-content-secondary">
|
||||
<div className="color-picker-wrapper">
|
||||
<input
|
||||
type="color"
|
||||
value={node.color || '#000000'}
|
||||
onChange={(e) => handleColorChange(nodeId, e.target.value)}
|
||||
style={{ display: 'none' }} // Hide the default color input
|
||||
id={`color-picker-${nodeId}`}
|
||||
/>
|
||||
<label htmlFor={`color-picker-${nodeId}`} className="color-picker-icon" style={{ backgroundColor: node.color || '#000000' }}>
|
||||
<img src={colorIcon} alt="Color Picker Icon" />
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{node.children && expanded[nodeId] && (
|
||||
<div className="tree-node-children">
|
||||
{node.children.map((childNode) => renderTree(childNode, nodeId))}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
|
||||
export default TreeNode;
|
||||
@@ -0,0 +1,14 @@
|
||||
body {
|
||||
margin: 0;
|
||||
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen',
|
||||
'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue',
|
||||
sans-serif;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
-moz-osx-font-smoothing: grayscale;
|
||||
background-color: #242121;
|
||||
}
|
||||
|
||||
code {
|
||||
font-family: source-code-pro, Menlo, Monaco, Consolas, 'Courier New',
|
||||
monospace;
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
import React from 'react';
|
||||
import ReactDOM from 'react-dom';
|
||||
import App from './App';
|
||||
import './index.css';
|
||||
|
||||
ReactDOM.render(
|
||||
<App />,
|
||||
document.getElementById('root')
|
||||
);
|
||||