Closed Javiondox closed 1 year ago
Suggested edit:
diff --git a/components/Aside.js b/components/Aside.js index 3ce1b39..f45448d 100644 --- a/components/Aside.js +++ b/components/Aside.js @@ -18,8 +18,9 @@ import { VersionJsonContext, SettingsContext, BlocksDataContext, + ReactFlowInstanceContext, } from "../pages/_app.js"; -import { getUpdatedBlocksData } from "./Utils.js"; +import { getUpdatedArrayById } from "./Utils.js"; export default function Aside({ className, closeBtn, svgExists }) { const [expandedContent, setExpandedContent] = useState(true); @@ -61,6 +62,9 @@ export default function Aside({ className, closeBtn, svgExists }) { const { versionJson, setVersionJson } = useContext(VersionJsonContext); const { currentBlocksData, setCurrentBlocksData } = useContext(BlocksDataContext); + const { reactFlowInstance, setReactFlowInstance } = useContext( + ReactFlowInstanceContext + ); const { expandedAside, setExpandedAside } = useContext(ExpandedAsideContext); @@ -263,7 +267,27 @@ export default function Aside({ className, closeBtn, svgExists }) { * Updates the selected block with the values from the specified DOM elements. */ const updateBlock = () => { + console.log(blockSelected); setCurrentBlocksData( + getUpdatedArrayById( + { + ...blockSelected, + ...{ + id: blockSelected.id, + type: typeDOM.current.value, + data: { + label: titleDOM.current.value, + resource: optionsDOM.current.value, + children: blockSelected.children, + identation: blockSelected.identation, + }, + }, + }, + currentBlocksData + ) + ); + + /*setCurrentBlocksData( getUpdatedBlocksData( { id: blockSelected.id, @@ -277,7 +301,7 @@ export default function Aside({ className, closeBtn, svgExists }) { }, currentBlocksData ) - ); + );*/ if (autoHideAside) { setExpandedAside(false); } @@ -553,9 +577,9 @@ export default function Aside({ className, closeBtn, svgExists }) { {blockSelected.children && blockSelected.children.map((childId) => { - const selectedChild = currentBlocksData.find( - (child) => child.id === childId - ); + const selectedChild = reactFlowInstance + .getNodes() + .find((child) => child.id === childId); return ( <option diff --git a/components/BlockCanvas.js b/components/BlockCanvas.js index f0f1117..9bf649d 100644 --- a/components/BlockCanvas.js +++ b/components/BlockCanvas.js @@ -28,7 +28,7 @@ import ConditionModal from "./flow/conditions/ConditionModal.js"; import BlockFlow from "./BlockFlow.js"; import { toast } from "react-toastify"; import { useHotkeys } from "react-hotkeys-hook"; -import { uniqueId, getBlockByNode } from "./Utils.js"; +import { uniqueId, getBlockByNodeDOM, getBlocksByNodesDOM } from "./Utils.js"; export const BlockOriginContext = createContext(); export const PaneContextMenuPositionContext = createContext(); @@ -45,7 +45,7 @@ function addEventListeners(element, events) { }); } -const reservedBlocksTypes = ["start", "end", "fragment"]; +const reservedBlocksTypes = ["start", "end"]; /** * Checks if there are any reserved blocks in an array of DOM elements @@ -115,7 +115,7 @@ export default function BlockCanvas() { //This will be given by the back - const currentBlocksDataRef = useRef(currentBlocksData); + //const currentBlocksDataRef = useRef(reactFlowInstance.getNodes()); /** Client-side */ @@ -143,19 +143,21 @@ export default function BlockCanvas() { }); useEffect(() => { - if (cMBlockData) + if (cMBlockData && reactFlowInstance) if (!Array.isArray(cMBlockData)) { - let newcurrentBlocksData = [...currentBlocksData]; + let newcurrentBlocksData = [...reactFlowInstance.getNodes()]; newcurrentBlocksData[ - currentBlocksData.findIndex((b) => b.id == cMBlockData.id) + reactFlowInstance.getNodes().findIndex((b) => b.id == cMBlockData.id) ] = cMBlockData; - setCurrentBlocksData(newcurrentBlocksData); + reactFlowInstance.setNodes(newcurrentBlocksData); } else { - const newcurrentBlocksData = currentBlocksData.map((block) => { - const newBlock = cMBlockData.find((b) => b.id === block.id); - return newBlock ? { ...block, ...newBlock } : block; - }); - setCurrentBlocksData(newcurrentBlocksData); + const newcurrentBlocksData = reactFlowInstance + .getNodes() + .map((block) => { + const newBlock = cMBlockData.find((b) => b.id === block.id); + return newBlock ? { ...block, ...newBlock } : block; + }); + reactFlowInstance.setNodes(newcurrentBlocksData); } }, [cMBlockData]); @@ -163,7 +165,7 @@ export default function BlockCanvas() { useEffect(() => { if (deletedEdge.id) { - let updatedBlocksArray = currentBlocksData.slice(); + let updatedBlocksArray = reactFlowInstance.getNodes().slice(); const blockNodeDelete = updatedBlocksArray.find( (obj) => obj.id === deletedEdge.source @@ -191,15 +193,15 @@ export default function BlockCanvas() { } } - setCurrentBlocksData(updatedBlocksArray); + reactFlowInstance.setNodes(updatedBlocksArray); } }, [deletedEdge]); const deleteBlocks = (blocks) => { if (!Array.isArray(blocks)) { - const deletedBlockArray = currentBlocksData.filter( - (b) => b.id !== blocks.id - ); + const deletedBlockArray = reactFlowInstance + .getNodes() + .filter((b) => b.id !== blocks.id); const deletedRelatedChildrenArray = deleteRelatedChildrenById( blocks.id, deletedBlockArray @@ -210,10 +212,10 @@ export default function BlockCanvas() { deletedRelatedChildrenArray ); - setCurrentBlocksData(deleteRelatedConditionsArray); + reactFlowInstance.setNodes(deleteRelatedConditionsArray); } else { if (blocks.length > 0) { - let updatedBlocksArray = currentBlocksData.slice(); + let updatedBlocksArray = reactFlowInstance.getNodes().slice(); blocks.forEach((b) => { const id = b.id; @@ -229,7 +231,7 @@ export default function BlockCanvas() { ); }); - setCurrentBlocksData(updatedBlocksArray); + reactFlowInstance.setNodes(updatedBlocksArray); } } }; @@ -320,10 +322,6 @@ export default function BlockCanvas() { ]); }, []); - useLayoutEffect(() => { - currentBlocksDataRef.current = currentBlocksData; - }, [expandedAside, settings, currentBlocksData]); - /** * Handles the context menu position, positioning it. * @param {Event} e @@ -344,9 +342,7 @@ export default function BlockCanvas() { e.preventDefault(); setCMX(e.clientX - bounds.left); setCMY(e.clientY - bounds.top); - let block = currentBlocksDataRef.current.find( - (e) => e.id == selectedBlock.id - ); + let block = currentBlocksData.find((e) => e.id == selectedBlock.id); setCMBlockData(block); if (block.type == "start" || block.type == "end") { setCMContainsReservedNodes(true); @@ -367,13 +363,19 @@ export default function BlockCanvas() { document.getElementsByClassName("selected").length > 1) ) { e.preventDefault(); + const selectedNodes = [ + ...document.querySelectorAll(".react-flow__node.selected"), + ]; setCMX(e.clientX - bounds.left); setCMY(e.clientY - bounds.top); setContextMenuOrigin("nodesselection"); + + console.log(selectedNodes); + setCMBlockData( + getBlocksByNodesDOM(selectedNodes, reactFlowInstance.getNodes()) + ); setCMContainsReservedNodes( - thereIsReservedBlocksInArray([ - ...document.querySelectorAll(".react-flow__node.selected"), - ]) + thereIsReservedBlocksInArray(selectedNodes) ); setShowContextualMenu(true); } @@ -388,22 +390,23 @@ export default function BlockCanvas() { const handleBlockCopy = (blockData = []) => { setShowContextualMenu(false); - let blockDataSet = new Set(blockData); + let blockDataSet; if (blockData.length > 0) { blockDataSet = new Set(blockData); } else { blockDataSet = new Set(); } - const selectedNodes = document.querySelectorAll( - ".react-flow__node.selected" - ); + const selectedNodes = [ + ...document.querySelectorAll(".react-flow__node.selected"), + ]; for (const node of selectedNodes) { const id = node.dataset?.id; + console.log(id); if (id) { const blockDataMap = new Map( - currentBlocksData.map((block) => [block.id, block]) + reactFlowInstance.getNodes().map((block) => [block.id, block]) ); const block = blockDataMap.get(id); if (block) { @@ -469,7 +472,10 @@ export default function BlockCanvas() { handleDeleteBlockSelection(); } else { if (selectedNodes.length == 1) { - blockData = getBlockByNode(selectedNodes[0], currentBlocksData); + blockData = getBlockByNodeDOM( + selectedNodes[0], + reactFlowInstance.getNodes() + ); } handleDeleteBlock(blockData); } @@ -492,7 +498,7 @@ export default function BlockCanvas() { y: preferredPosition.y - reactFlowBounds.top, }); - const asideOffset = expanded + const asideOffset = expandedAside ? Math.floor(asideBounds.width / 125) * 125 : 0; @@ -536,9 +542,9 @@ export default function BlockCanvas() { setShowContextualMenu(false); if (Object.keys(newBlockCreated).length !== 0) { - let newcurrentBlocksData = [...currentBlocksData]; + let newcurrentBlocksData = [...reactFlowInstance.getNodes()]; newcurrentBlocksData.push(newBlockCreated); - setCurrentBlocksData(newcurrentBlocksData); + reactFlowInstance.setNodes(newcurrentBlocksData); } }; @@ -554,7 +560,7 @@ export default function BlockCanvas() { y: preferredPosition.y - reactFlowBounds.top, }); - const asideOffset = expanded + const asideOffset = expandedAside ? Math.floor(asideBounds.width / 125) * 125 : 0; @@ -568,8 +574,8 @@ export default function BlockCanvas() { }); setShowContextualMenu(false); - let newcurrentBlocksData = [...currentBlocksData, ...newBlocks]; - setCurrentBlocksData(newcurrentBlocksData); + let newcurrentBlocksData = [...reactFlowInstance.getNodes(), ...newBlocks]; + reactFlowInstance.setNodes(newcurrentBlocksData); }; const handleDeleteBlock = (blockData) => { @@ -585,7 +591,9 @@ export default function BlockCanvas() { ); const blockDataArray = []; for (let node of selectedNodes) { - blockDataArray.push(getBlockByNode(node, currentBlocksData)); + blockDataArray.push( + getBlockByNodeDOM(node, reactFlowInstance.getNodes()) + ); } deleteBlocks(blockDataArray); }; @@ -596,7 +604,10 @@ export default function BlockCanvas() { undefined; end = - end || currentBlocksData.find((block) => block.id === currentSelectionId); + end || + reactFlowInstance + .getNodes() + .find((block) => block.id === currentSelectionId); setShowContextualMenu(false); @@ -613,7 +624,7 @@ export default function BlockCanvas() { return; } - const newBlocksData = [...currentBlocksData]; + const newBlocksData = [...reactFlowInstance.getNodes()]; const bI = newBlocksData.findIndex((block) => block.id == origin.id); if (newBlocksData[bI].children) { const alreadyAChildren = newBlocksData[bI].children.includes(end.id); @@ -636,11 +647,11 @@ export default function BlockCanvas() { newBlocksData[bI].children = [end.id]; } setBlockOrigin(); - setCurrentBlocksData(newBlocksData); + reactFlowInstance.setNodes(newBlocksData); } else { - const starterBlock = currentBlocksData.find( - (block) => block.id == currentSelectionId - ); + const starterBlock = reactFlowInstance + .getNodes() + .find((block) => block.id == currentSelectionId); setBlockOrigin(starterBlock); @@ -663,7 +674,10 @@ export default function BlockCanvas() { let newCMBlockData = undefined; if (selectedNodes.length == 1) { - newCMBlockData = getBlockByNode(selectedNodes[0], currentBlocksData); + newCMBlockData = getBlockByNodeDOM( + selectedNodes[0], + reactFlowInstance.getNodes() + ); } if (newCMBlockData || cMBlockData) { @@ -723,7 +737,7 @@ export default function BlockCanvas() { <ConditionModal blockData={cMBlockData} setBlockData={setCMBlockData} - blocksData={currentBlocksData} + blocksData={reactFlowInstance.getNodes()} showConditionsModal={showConditionsModal} setShowConditionsModal={setShowConditionsModal} /> diff --git a/components/BlockFlow.js b/components/BlockFlow.js index 39e2a54..118bbb8 100644 --- a/components/BlockFlow.js +++ b/components/BlockFlow.js @@ -42,7 +42,7 @@ import { } from "@fortawesome/free-solid-svg-icons"; import { PaneContextMenuPositionContext } from "./BlockCanvas.js"; import { Button } from "react-bootstrap"; -import { getBlockById, getNodeById, getUpdatedBlocksData } from "./Utils.js"; +import { getBlockById, getNodeById, getUpdatedArrayById } from "./Utils.js"; const minimapStyle = { height: 120, @@ -286,10 +286,10 @@ const OverviewFlow = ({ map, deleteBlocks, setShowContextualMenu }, ref) => { const handleNodeDragStart = (event, node) => { setShowContextualMenu(false); - let inFragment = true; - getBlockById(node.id, currentBlocksData).parent - ? null - : (inFragment = false); + let inFragment = false; + if (node.parentNode != undefined) { + inFragment = true; + } setSnapToGrid(!inFragment); draggedNodePosition.current = node.position; }; @@ -305,25 +305,12 @@ const OverviewFlow = ({ map, deleteBlocks, setShowContextualMenu }, ref) => { (draggedNodesPosition.current.x !== nodes[0].position.x || draggedNodesPosition.current.y !== nodes[0].position.y) ) { + //FIXME:LOOK IF NECESSARY const selectionBlock = nodes.map((b) => ({ - id: b.id, - x: b.position.x, - y: b.position.y, - type: b.type, - title: b.data.label, - parent: b.parentNode, - style: b.data.style, - innerNodes: b.data.innerNodes, - expanded: b.data.expanded, - draggable: b.draggable, - children: b.data.children, - identation: b.data.identation, - conditions: b.data.conditions, - order: b.data.order, - unit: b.data.unit, + b, })); - setCurrentBlocksData( - getUpdatedBlocksData(selectionBlock, currentBlocksData) + reactFlowInstance.setNodes( + getUpdatedArrayById(selectionBlock, reactFlowInstance.getNodes()) ); } }; @@ -334,29 +321,9 @@ const OverviewFlow = ({ map, deleteBlocks, setShowContextualMenu }, ref) => { draggedNodePosition.current.x !== node.position.x || draggedNodePosition.current.y !== node.position.y ) { - setCurrentBlocksData( - getUpdatedBlocksData( - { - id: node.id, - x: node.position.x, - y: node.position.y, - type: node.type, - title: node.data.label, - parent: node.parentNode, - style: node.data.style, - innerNodes: node.data.innerNodes, - expanded: node.data.expanded, - draggable: node.draggable, - children: node.data.children, - identation: node.data.identation, - conditions: node.data.conditions, - order: node.data.order, - unit: node.data.unit, - }, - currentBlocksData - ) + reactFlowInstance.setNodes( + getUpdatedArrayById(node, reactFlowInstance.getNodes()) ); - console.log(node); setSnapToGrid(true); } } @@ -384,8 +351,20 @@ const OverviewFlow = ({ map, deleteBlocks, setShowContextualMenu }, ref) => { } setCurrentBlocksData( - getUpdatedBlocksData({ ...sourceNode }, currentBlocksData) + getUpdatedArrayById({ ...sourceNode }, currentBlocksData) ); + + //FIXME: + setEdges([ + ...edges, + { + id: sourceNodeId + "-" + targetNodeId, + source: sourceNodeId, + target: targetNodeId, + }, + ]); + + console.log(sourceNode); }; useEffect(() => { @@ -412,30 +391,16 @@ const OverviewFlow = ({ map, deleteBlocks, setShowContextualMenu }, ref) => { useEffect(() => { setNewInitialNodes( map?.map((block) => ({ - id: block.id, - type: block.type, - parentNode: block.parent, + ...block, extent: block.parent ? "parent" : undefined, //draggable: block.parent ? false : true, - data: { - label: block.title, - identation: block.identation, - children: block.children, - conditions: block.conditions, - style: block.style, - innerNodes: block.innerNodes, - expanded: block.expanded, - order: block.order, - unit: block.unit, - }, - position: { x: block.x, y: block.y }, })) ); setNewInitialEdges( map?.flatMap((parent) => { - if (parent.children) { - return parent.children.map((child) => { + if (parent.data.children) { + return parent.data.children.map((child) => { return { id: `${parent.id}-${child}`, source: parent.id, @@ -456,22 +421,24 @@ const OverviewFlow = ({ map, deleteBlocks, setShowContextualMenu }, ref) => { // we are using a bit of a shortcut here to adjust the edge type // this could also be done with a custom edge for example - const edgesWithUpdatedTypes = edges.map((edge) => { - if (edge.sourceHandle) { - const edgeType = nodes.find((node) => node.type === "custom").data - .selects[edge.sourceHandle]; - edge.type = edgeType; - } + const edgesWithUpdatedTypes = edges + ? edges.map((edge) => { + if (edge.sourceHandle) { + const edgeType = nodes.find((node) => node.type === "custom").data + .selects[edge.sourceHandle]; + edge.type = edgeType; + } - //FIXME: Does nothing - if (edge.conditions != undefined) { - for (let condition of cedge.conditions) { - edge.label = "" + condition.operand + condition.objective; - } - } + //FIXME: Does nothing + if (edge.conditions != undefined) { + for (let condition of cedge.conditions) { + edge.label = "" + condition.operand + condition.objective; + } + } - return edge; - }); + return edge; + }) + : edges; return ( <div ref={reactFlowWrapper} style={{ height: "100%", width: "100%" }}> @@ -502,6 +469,7 @@ const OverviewFlow = ({ map, deleteBlocks, setShowContextualMenu }, ref) => { deleteKeyCode={["Backspace", "Delete", "d"]} multiSelectionKeyCode={["Shift"]} selectionKeyCode={["Shift"]} + zoomOnDoubleClick={false} > {minimap && ( <MiniMap diff --git a/components/ContextualMenu.js b/components/ContextualMenu.js index 77aad4d..0bf818d 100644 --- a/components/ContextualMenu.js +++ b/components/ContextualMenu.js @@ -1,10 +1,14 @@ import styles from "@components/styles/BlockContextualMenu.module.css"; -import { forwardRef, useContext } from "react"; -import { ExpandedAsideContext } from "@components/pages/_app"; +import { forwardRef, useContext, useState, useLayoutEffect } from "react"; +import { + BlocksDataContext, + ExpandedAsideContext, +} from "@components/pages/_app"; import { BlockOriginContext } from "./BlockCanvas"; import CMBlockMenu from "./flow/contextualmenu/CMBlockMenu"; import CMPaneMenu from "./flow/contextualmenu/CMPaneMenu"; import CMSelectionMenu from "./flow/contextualmenu/CMSelectionMenu"; +import { getBlocksByNodesDOM } from "./Utils"; export default forwardRef(function ContextualMenu( { @@ -13,7 +17,6 @@ export default forwardRef(function ContextualMenu( showContextualMenu, blockData, setShowContextualMenu, - setShowConditionsModal, contextMenuOrigin, containsReservedNodes, handleBlockCopy, @@ -28,104 +31,103 @@ export default forwardRef(function ContextualMenu( ref ) { const { blockOrigin, setBlockOrigin } = useContext(BlockOriginContext); - + const { currentBlocksData, setCurrentBlocksData } = + useContext(BlocksDataContext); const { expanded: expandedAside } = useContext(ExpandedAsideContext); const asideBounds = expandedAside ? document.getElementsByTagName("aside")[0]?.getBoundingClientRect() : 0; + const [enableEditPreconditions, setEnableEditPreconditions] = useState(true); + const [enableCreateRelation, setEnableCreateRelation] = useState(true); + const [enableCreateFragment, setEnableCreateFragment] = useState(true); + const [enableCut, setEnableCut] = useState(true); + const [enableCopy, setEnableCopy] = useState(true); + const [enablePaste, setEnablePaste] = useState(true); + const [enableCreate, setEnableCreate] = useState(true); + const [enableDelete, setEnableDelete] = useState(true); + + useLayoutEffect(() => { + if (containsReservedNodes) { + setEnableEditPreconditions(false); + setEnableCreateFragment(false); + setEnableDelete(false); + setEnableCut(false); + setEnableCopy(false); + } else { + setEnableDelete(true); + setEnableCut(true); + setEnableCopy(true); + + if (blockData) { + if (Array.isArray(blockData)) { + const blocks = getBlocksByNodesDOM( + blockData, + reactFlowInstance.getNodes() + ); + const fragment = blocks.find((block) => block.type == "fragment"); + if (fragment) { + setEnableCreateFragment(false); + } else { + setEnableCreateFragment(true); + } + } + } + } + }, [blockData]); + return ( <> {showContextualMenu && ( <> - {containsReservedNodes ? ( - <div - ref={ref} - style={{ - top: `${y}px`, - left: `${x + (asideBounds && asideBounds.width)}px`, - }} - className={styles.cM + " "} - > - {contextMenuOrigin == "block" && ( - <CMBlockMenu - handleShow={handleShow} - blockOrigin={blockOrigin} - blockData={blockData} - setBlockOrigin={setBlockOrigin} - setShowContextualMenu={setShowContextualMenu} - handleDeleteBlock={handleDeleteBlock} - handleNewRelation={handleNewRelation} - handleBlockCopy={handleBlockCopy} - handleBlockCut={handleBlockCut} - EnableEditPreconditions={false} - EnableCreateRelation={true} - EnableCut={false} - EnableCopy={false} - EnableDelete={false} - /> - )} - {contextMenuOrigin == "nodesselection" && ( - <CMSelectionMenu - handleDeleteBlockSelection={handleDeleteBlockSelection} - handleBlockCut={handleBlockCut} - handleBlockCopy={handleBlockCopy} - EnableCreateFragment={false} - EnableCut={false} - EnableCopy={false} - EnableDelete={false} - /> - )} - </div> - ) : ( - <div - ref={ref} - style={{ - top: `${y}px`, - left: `${x + (asideBounds && asideBounds.width)}px`, - }} - className={styles.cM + " "} - > - {contextMenuOrigin == "pane" && ( - <CMPaneMenu - createBlock={createBlock} - handleBlockPaste={handleBlockPaste} - EnableCreate={true} - EnablePaste={true} - /> - )} - {contextMenuOrigin == "block" && ( - <CMBlockMenu - handleShow={handleShow} - blockOrigin={blockOrigin} - blockData={blockData} - setBlockOrigin={setBlockOrigin} - setShowContextualMenu={setShowContextualMenu} - handleDeleteBlock={handleDeleteBlock} - handleNewRelation={handleNewRelation} - handleBlockCopy={handleBlockCopy} - handleBlockCut={handleBlockCut} - EnableEditPreconditions={true} - EnableCreateRelation={true} - EnableCut={true} - EnableCopy={true} - EnableDelete={true} - /> - )} - {contextMenuOrigin == "nodesselection" && ( - <CMSelectionMenu - handleDeleteBlockSelection={handleDeleteBlockSelection} - handleBlockCut={handleBlockCut} - handleBlockCopy={handleBlockCopy} - EnableCreateFragment={true} - EnableCut={true} - EnableCopy={true} - EnableDelete={true} - /> - )} - </div> - )} + <div + ref={ref} + style={{ + top: `${y}px`, + left: `${x + (asideBounds && asideBounds.width)}px`, + }} + className={styles.cM + " "} + > + {contextMenuOrigin == "pane" && ( + <CMPaneMenu + createBlock={createBlock} + handleBlockPaste={handleBlockPaste} + EnableCreate={enableCreate} + EnablePaste={enablePaste} + /> + )} + {contextMenuOrigin == "block" && ( + <CMBlockMenu + handleShow={handleShow} + blockOrigin={blockOrigin} + blockData={blockData} + setBlockOrigin={setBlockOrigin} + setShowContextualMenu={setShowContextualMenu} + handleDeleteBlock={handleDeleteBlock} + handleNewRelation={handleNewRelation} + handleBlockCopy={handleBlockCopy} + handleBlockCut={handleBlockCut} + EnableEditPreconditions={enableEditPreconditions} + EnableCreateRelation={enableCreateRelation} + EnableCut={enableCut} + EnableCopy={enableCopy} + EnableDelete={enableDelete} + /> + )} + {contextMenuOrigin == "nodesselection" && ( + <CMSelectionMenu + handleDeleteBlockSelection={handleDeleteBlockSelection} + handleBlockCut={handleBlockCut} + handleBlockCopy={handleBlockCopy} + blocksData={blockData} + EnableCreateFragment={enableCreateFragment} + EnableCut={enableCut} + EnableCopy={enableCopy} + EnableDelete={enableDelete} + /> + )} + </div> </> )} </> diff --git a/components/Header.js b/components/Header.js index 46efa89..732f2c4 100644 --- a/components/Header.js +++ b/components/Header.js @@ -36,6 +36,7 @@ import { MSGContext, SettingsContext, OnlineContext, + ReactFlowInstanceContext, } from "@components/pages/_app"; import { toast } from "react-toastify"; import { notImplemented } from "@components/pages/_app"; @@ -81,6 +82,9 @@ function Header({ closeBtn }, ref) { const { expandedAside, setExpandedAside } = useContext(ExpandedAsideContext); + const { reactFlowInstance, setReactFlowInstance } = useContext( + ReactFlowInstanceContext + ); const { currentBlocksData, setCurrentBlocksData } = useContext(BlocksDataContext); const { isOffline } = useContext(OnlineContext); @@ -147,6 +151,7 @@ function Header({ closeBtn }, ref) { if (selectedMap.versions) { setSelectedVersion(selectedMap.versions[0]); setCurrentBlocksData(selectedMap.versions[0].blocksData); + console.log(selectedMap.versions[0].blocksData); } if (selectedMap.id == -1) { setCurrentBlocksData(); @@ -304,6 +309,7 @@ function Header({ closeBtn }, ref) { setCurrentBlocksData( modifiedMap.versions[selectedMap.versions.length - 1].blocksData ); + toast( `Versión: "Nueva Versión ${modifiedMap.versions.length - 1}" creada`, defaultToastSuccess @@ -392,7 +398,7 @@ function Header({ closeBtn }, ref) { newMaps[mapIndex] = modifiedMap; setMaps(newMaps); setVersions(modifiedMap.versions); - setCurrentBlocksData(firstVersion?.blocksData || versions[0]?.blocksData); + currentBlocksData(firstVersion?.blocksData || versions[0]?.blocksData); toast(`Versión eliminada con éxito.`, defaultToastSuccess); } else { toast(`No puedes eliminar esta versión.`, defaultToastError); @@ -401,7 +407,7 @@ function Header({ closeBtn }, ref) { const handleBlockDataExport = () => { download( - encodeURIComponent(JSON.stringify(currentBlocksData)), + encodeURIComponent(JSON.stringify(reactFlowInstance.getNodes())), `${mapSelected.name}-${selectedVersion.name}-${new Date() .toLocaleDateString() .replaceAll("/", "-")}.json`, @@ -456,14 +462,14 @@ function Header({ closeBtn }, ref) { }, [mapSelected]); useEffect(() => { - if (selectedVersion) { + if (selectedVersion && reactFlowInstance) { if (selectedVersion.id != versionJson.id) { resetEdit(); setCurrentBlocksData(selectedVersion.blocksData); resetMapSesion(); } } - }, [selectedVersion]); + }, [selectedVersion, reactFlowInstance]); useEffect(() => { if (versions) { @@ -844,7 +850,7 @@ function Header({ closeBtn }, ref) { fontFamily: "monospace", }} > - {JSON.stringify(currentBlocksData, null, "\t")} + {JSON.stringify(reactFlowInstance.getNodes(), null, "\t")} </code> </details> </div> diff --git a/components/Utils.js b/components/Utils.js index 249e712..1dd2057 100644 --- a/components/Utils.js +++ b/components/Utils.js @@ -10,14 +10,14 @@ export const getBlockById = (id, blocksData) => { return blocksData.find((block) => block.id == id); }; -export const getBlockByNode = (node, blocksData) => { +export const getBlockByNodeDOM = (node, blocksData) => { return blocksData.find((block) => block.id == node.dataset.id); }; -export const getBlocksByNodes = (nodes, blocksData) => { +export const getBlocksByNodesDOM = (nodes, blocksData) => { const blockArray = []; nodes.forEach((node) => { - const block = getBlockByNode(node, blocksData); + const block = getBlockByNodeDOM(node, blocksData); if (block) { blockArray.push(block); } @@ -25,17 +25,8 @@ export const getBlocksByNodes = (nodes, blocksData) => { return blockArray; }; -export const getUpdatedBlocksData = (newBlock, blocksData) => { - const newBlocks = Array.isArray(newBlock) ? newBlock : [newBlock]; - - return blocksData.map((oldBlock) => { - const newBlock = newBlocks.find((block) => block.id === oldBlock.id); - return newBlock ? { ...oldBlock, ...newBlock } : oldBlock; - }); -}; - export const getUpdatedBlocksDataFromFlow = (blocksData, reactflowInstance) => { - return getUpdatedBlocksData(reactflowInstance.getNodes(), blocksData); + return getUpdatedArrayById(reactflowInstance.getNodes(), blocksData); }; //Nodes @@ -50,7 +41,7 @@ export const getNodeByBlock = (block, reactflowInstance) => { export const getNodesByBlocks = (blocks, reactflowInstance) => { const nodeArray = []; blocks.forEach((block) => { - const node = getBlockByNode(block, reactflowInstance); + const node = getBlockByNodeDOM(block, reactflowInstance); if (node) { nodeArray.push(node); } @@ -73,3 +64,20 @@ export const getNodesByProperty = ( export const getEdgeBetweenNodeIds = (id1, id2, reactflowInstance) => { return reactflowInstance.getEdge(`${id1}-${id2}`); }; + +//Generic + +/** + * Returns a new array with updated entries from the original array. + * @param {Object|Object[]} updatedEntry - The entry or entries to be updated in the original array. Each entry must have an id property. + * @param {Object[]} originalArray - The original array of entries. Each entry must have an id property. + * @returns {Object[]} A new array with the updated entries. If an entry in the original array does not have a matching id in the updatedEntry, it is returned unchanged. + */ +export const getUpdatedArrayById = (updatedEntry, originalArray) => { + const newBlocks = Array.isArray(updatedEntry) ? updatedEntry : [updatedEntry]; + + return originalArray.map((oldEntry) => { + const newBlock = newBlocks.find((entry) => entry.id === oldEntry.id); + return newBlock ? { ...oldEntry, ...newBlock } : oldEntry; + }); +}; diff --git a/components/flow/contextualmenu/CMBlockMenu.js b/components/flow/contextualmenu/CMBlockMenu.js index 7457d2b..7a76900 100644 --- a/components/flow/contextualmenu/CMBlockMenu.js +++ b/components/flow/contextualmenu/CMBlockMenu.js @@ -34,21 +34,23 @@ const Menu = ( ) => { return ( <div ref={ref} className={styles.cM + " "}> - <li> - <Button - variant="light" - onClick={handleShow} - disabled={!EnableEditPreconditions} - > - <div> - <FontAwesomeIcon icon={faEdit} /> + {["fragment"].includes(blockData.type) == false && ( + <li> + <Button + variant="light" + onClick={handleShow} + disabled={!EnableEditPreconditions} + > <div> - Editar precondiciones - <span>SHIFT+E</span> + <FontAwesomeIcon icon={faEdit} /> + <div> + Editar precondiciones + <span>SHIFT+E</span> + </div> </div> - </div> - </Button> - </li> + </Button> + </li> + )} {blockOrigin ? ( blockOrigin.id == blockData.id ? ( <li> @@ -58,6 +60,7 @@ const Menu = ( setBlockOrigin(); setShowContextualMenu(false); }} + disabled={!EnableCreateRelation} > <div> <FontAwesomeIcon icon={faDiagramNext} /> @@ -69,23 +72,27 @@ const Menu = ( </Button> </li> ) : ( - <li> - <Button - variant="light" - onClick={() => handleNewRelation(blockOrigin, blockData)} - > - <div> - <FontAwesomeIcon icon={faDiagramNext} /> + ["fragment"].includes(blockData.type) == false && ( + <li> + <Button + variant="light" + onClick={() => handleNewRelation(blockOrigin, blockData)} + disabled={!EnableCreateRelation} + > <div> - Terminar relación - <span>Unir a "{blockOrigin.title}"</span> + <FontAwesomeIcon icon={faDiagramNext} /> + <div> + Terminar relación + <span>Unir a "{blockOrigin.title}"</span> + </div> </div> - </div> - </Button> - </li> + </Button> + </li> + ) ) ) : ( - [...ActionBlocks, "end"].includes(blockData.type) == false && ( + [...ActionBlocks, "end", "fragment"].includes(blockData.type) == + false && ( <li> <Button variant="light" diff --git a/components/flow/contextualmenu/CMSelectionMenu.js b/components/flow/contextualmenu/CMSelectionMenu.js index b079917..2783470 100644 --- a/components/flow/contextualmenu/CMSelectionMenu.js +++ b/components/flow/contextualmenu/CMSelectionMenu.js @@ -16,6 +16,7 @@ const Menu = ( handleDeleteBlockSelection, handleBlockCopy, handleBlockCut, + blocksData, EnableCreateFragment, EnableCopy, EnableCut, @@ -43,7 +44,7 @@ const Menu = ( <li> <Button variant="light" - onClick={() => handleBlockCopy()} + onClick={() => handleBlockCopy(blocksData)} disabled={!EnableCopy} > <div> @@ -58,7 +59,7 @@ const Menu = ( <li> <Button variant="light" - onClick={() => handleBlockCut()} + onClick={() => handleBlockCut(blocksData)} disabled={!EnableCut} > <div> @@ -73,7 +74,7 @@ const Menu = ( <li> <Button variant="light" - onClick={handleDeleteBlockSelection} + onClick={() => handleDeleteBlockSelection(blocksData)} disabled={!EnableDelete} > <div> @@ -94,6 +95,7 @@ export default function CMSelectionMenu({ handleDeleteBlockSelection, handleBlockCopy, handleBlockCut, + blocksData, EnableCreateFragment = false, EnableCopy = false, EnableCut = false, @@ -114,6 +116,7 @@ export default function CMSelectionMenu({ handleDeleteBlockSelection={handleDeleteBlockSelection} handleBlockCopy={handleBlockCopy} handleBlockCut={handleBlockCut} + blocksData={blocksData} EnableCreateFragment={EnableCreateFragment} EnableCut={EnableCut} EnableCopy={EnableCopy} @@ -126,6 +129,7 @@ export default function CMSelectionMenu({ handleDeleteBlockSelection={handleDeleteBlockSelection} handleBlockCopy={handleBlockCopy} handleBlockCut={handleBlockCut} + blocksData={blocksData} EnableCreateFragment={EnableCreateFragment} EnableCut={EnableCut} EnableCopy={EnableCopy} diff --git a/components/flow/nodes/ActionNode.js b/components/flow/nodes/ActionNode.js index 3cbcd03..ffbc3ef 100644 --- a/components/flow/nodes/ActionNode.js +++ b/components/flow/nodes/ActionNode.js @@ -96,7 +96,7 @@ const getAriaLabel = () => { return ( getHumanDesc() + ", " + - blockData.title + + blockData.label + ", posición en el eje X: " + blockData.x + ", posición en el eje Y: " + diff --git a/components/flow/nodes/ElementNode.js b/components/flow/nodes/ElementNode.js index cd8ec1d..b2dcb5f 100644 --- a/components/flow/nodes/ElementNode.js +++ b/components/flow/nodes/ElementNode.js @@ -229,7 +229,7 @@ function ElementNode({ id, xPos, yPos, type, data, isConnectable }) { return ( getHumanDesc() + ", " + - data.title + + data.label + ", posición en el eje X: " + xPos + ", posición en el eje Y: " + diff --git a/components/flow/nodes/FragmentNode.js b/components/flow/nodes/FragmentNode.js index 12bbdc7..f8ee16c 100644 --- a/components/flow/nodes/FragmentNode.js +++ b/components/flow/nodes/FragmentNode.js @@ -25,11 +25,11 @@ import { import { useEffect } from "react"; import { getBlockById, - getBlockByNode, + getBlockByNodeDOM, getEdgeBetweenNodeIds, getNodeById, getNodesByProperty, - getUpdatedBlocksData, + getUpdatedArrayById, getUpdatedBlocksDataFromFlow, } from "@components/components/Utils"; import FocusTrap from "focus-trap-react"; @@ -92,22 +92,28 @@ function FragmentNode({ id, xPos, yPos, type, data }) { if (!expanded) { nodeDOM.classList.add("insideContractedFragment"); - const newBlock = getBlockByNode(nodeDOM, currentBlocksData); + const newBlock = getBlockByNodeDOM( + nodeDOM, + reactFlowInstance.getNodes() + ); newBlock.x = 0; newBlock.y = 0; - setCurrentBlocksData( - getUpdatedBlocksData(newBlock, currentBlocksData) + reactFlowInstance.setNodes( + getUpdatedArrayById(newBlock, reactFlowInstance.getNodes()) ); } else { nodeDOM.classList.remove("insideContractedFragment"); - const newBlock = getBlockByNode(nodeDOM, currentBlocksData); + const newBlock = getBlockByNodeDOM( + nodeDOM, + reactFlowInstance.getNodes() + ); const matchingInnerNode = originalChildrenStatus.find( (innerNode) => innerNode.id == newBlock.id ); newBlock.x = matchingInnerNode.x; newBlock.y = matchingInnerNode.y; - setCurrentBlocksData( - getUpdatedBlocksData(newBlock, currentBlocksData) + reactFlowInstance.setNodes( + getUpdatedArrayById(newBlock, reactFlowInstance.getNodes()) ); } } @@ -186,10 +192,10 @@ function FragmentNode({ id, xPos, yPos, type, data }) { setOriginalChildrenStatus(updatedChildrenBlockData); - setCurrentBlocksData( - getUpdatedBlocksData( + reactFlowInstance.setNodes( + getUpdatedArrayById( [updatedInfo, ...updatedChildrenBlockData], - currentBlocksData + reactFlowInstance.getNodes() ) ); }; @@ -202,7 +208,10 @@ function FragmentNode({ id, xPos, yPos, type, data }) { const nodeDOM = document.querySelector( "[data-id=" + childNode.id + "]" ); - const newBlock = getBlockByNode(nodeDOM, currentBlocksData); + const newBlock = getBlockByNodeDOM( + nodeDOM, + reactFlowInstance.getNodes() + ); const matchingInnerNode = getNodesByProperty( reactFlowInstance, "parentNode", @@ -234,7 +243,7 @@ function FragmentNode({ id, xPos, yPos, type, data }) { } return ( "Fragmento, " + - data.title + + data.label + ", posición en el eje X: " + xPos + ", posición en el eje Y: " + diff --git a/public/resources/dev.json b/public/resources/dev.json index 7153e7a..bb85a6b 100644 --- a/public/resources/dev.json +++ b/public/resources/dev.json @@ -11,357 +11,439 @@ "blocksData": [ { "id": "dev-2A1", - "x": 0, - "y": 0, + "position": { + "x": 0, + "y": 0 + }, "type": "start", - "title": "Inicio", - "children": ["dev0A1"], - "identation": 1 + "data": { + "label": "Inicio", + "children": ["dev0A1"] + } }, { "id": "dev-1A1", - "x": 2000, - "y": 0, + "position": { + "x": 2000, + "y": 0 + }, "type": "end", - "title": "Final", - "identation": 1 + "data": { + "label": "Final" + } }, { "id": "dev0A1", - "x": 125, - "y": 0, + "position": { + "x": 125, + "y": 0 + }, "type": "resource", - "title": "Objetivos del curso", - "res": "url/objetivos.pdf", - "children": ["dev1A1"], - "identation": 1, - "order": 1, - "unit": 1 + "data": { + "label": "Objetivos del curso", + "res": "url/objetivos.pdf", + "children": ["dev1A1"], + "identation": 1, + "order": 1, + "unit": 1 + } }, { "id": "dev1A1", - "x": 250, - "y": 0, + "position": { + "x": 250, + "y": 0 + }, "type": "quiz", - "title": "Aerodinámica", - "children": ["dev2A1", "dev3A1", "dev9A1"], - "identation": 2, - "order": 2, - "unit": 1, - "conditions": { - "type": "conditionsGroup", - "id": "3123551512354", - "op": "&", - "conditions": [ - { - "type": "qualification", - "id": "1326689252709", - "op": "fullCourse", - "objective": "2" - }, - { - "type": "qualification", - "id": "7841278214890", - "op": "fullCourse", - "objective": "3" - }, - { - "type": "qualification", - "id": "8795742484563", - "op": "fullCourse", - "objective": "10" - } - ] + "data": { + "label": "Aerodinámica", + "children": ["dev2A1", "dev3A1", "dev9A1"], + "identation": 2, + "order": 2, + "unit": 1, + "conditions": { + "type": "conditionsGroup", + "id": "3123551512354", + "op": "&", + "conditions": [ + { + "type": "qualification", + "id": "1326689252709", + "op": "fullCourse", + "objective": "2" + }, + { + "type": "qualification", + "id": "7841278214890", + "op": "fullCourse", + "objective": "3" + }, + { + "type": "qualification", + "id": "8795742484563", + "op": "fullCourse", + "objective": "10" + } + ] + } } }, { "id": "dev2A1", - "x": 375, - "y": -175, + "position": { + "x": 375, + "y": -175 + }, "type": "badge", - "title": "Conocimiento" + "data": { + "label": "Conocimiento" + } }, { "id": "dev3A1", - "x": 500, - "y": 0, + "position": { + "x": 500, + "y": 0 + }, "type": "url", - "title": "Web de Aerodinámica Avanzada", - "children": ["dev4A1"], - "identation": 2, - "order": 3, - "unit": 1 + "data": { + "label": "Web de Aerodinámica Avanzada", + "children": ["dev4A1"], + "identation": 2, + "order": 3, + "unit": 1 + } }, { "id": "dev4A1", - "x": 625, - "y": 0, + "position": { + "x": 625, + "y": 0 + }, "type": "assign", - "title": "Ejercicios de la Web", - "children": ["dev5A1", "dev6A1"], - "identation": 2, - "order": 4, - "unit": 1, - "conditions": { - "type": "conditionsGroup", - "id": "784378163420978", - "op": "&", - "conditions": [ - { - "type": "qualification", - "id": "89674351236523", - "op": "fullCourse", - "objective": "2" - }, - { - "type": "qualification", - "id": "1532657364123", - "op": "fullCourse", - "objective": "2" - } - ] + "data": { + "label": "Ejercicios de la Web", + "children": ["dev5A1", "dev6A1"], + "identation": 2, + "order": 4, + "unit": 1, + "conditions": { + "type": "conditionsGroup", + "id": "784378163420978", + "op": "&", + "conditions": [ + { + "type": "qualification", + "id": "89674351236523", + "op": "fullCourse", + "objective": "2" + }, + { + "type": "qualification", + "id": "1532657364123", + "op": "fullCourse", + "objective": "2" + } + ] + } } }, { "id": "dev5A1", - "x": 1000, - "y": 0, + "position": { + "x": 1000, + "y": 0 + }, "type": "quiz", - "title": "Física de fluidos", - "children": ["dev7A1", "dev8A1", "dev13A1"], - "identation": 3, - "order": 8, - "unit": 1, - "conditions": { - "type": "conditionsGroup", - "id": "100236670156", - "op": "|", - "conditions": [ - { - "type": "qualification", - "id": "1326689252709", - "op": "Ejercicios de la Web", - "objective2": "5" - }, - { - "type": "conditionsGroup", - "id": "100236670128", - "op": "&", - "conditions": [ - { - "type": "qualification", - "id": "1321231241559", - "op": "fullCourse", - "objective": "10" - } - ] - }, - { - "type": "conditionsGroup", - "id": "324236670128", - "op": "&", - "conditions": [ - { - "type": "qualification", - "id": "131234681559", - "op": "fullCourse", - "objective": "2" - } - ] - } - ] + "data": { + "label": "Física de fluidos", + "children": ["dev7A1", "dev8A1", "dev13A1"], + "identation": 3, + "order": 8, + "unit": 1, + "conditions": { + "type": "conditionsGroup", + "id": "100236670156", + "op": "|", + "conditions": [ + { + "type": "qualification", + "id": "1326689252709", + "op": "Ejercicios de la Web", + "objective2": "5" + }, + { + "type": "conditionsGroup", + "id": "100236670128", + "op": "&", + "conditions": [ + { + "type": "qualification", + "id": "1321231241559", + "op": "fullCourse", + "objective": "10" + } + ] + }, + { + "type": "conditionsGroup", + "id": "324236670128", + "op": "&", + "conditions": [ + { + "type": "qualification", + "id": "131234681559", + "op": "fullCourse", + "objective": "2" + } + ] + } + ] + } } }, { "id": "dev6A1", - "x": 875, - "y": 175, + "position": { + "x": 875, + "y": 175 + }, "type": "assign", - "title": "Ejercicios de ampliación", - "children": ["dev5A1"], - "identation": 3, - "order": 8, - "unit": 1 + "data": { + "label": "Ejercicios de ampliación", + "children": ["dev5A1"], + "identation": 3, + "order": 8, + "unit": 1 + } }, { "id": "dev7A1", - "x": 1750, - "y": -175, + "position": { + "x": 1750, + "y": -175 + }, "type": "badge", - "title": "Mecánica de fluidos", - "identation": 1 + "data": { + "label": "Mecánica de fluidos" + } }, { "id": "dev8A1", - "x": 1875, - "y": 0, + "position": { + "x": 1875, + "y": 0 + }, "type": "page", - "title": "Física cuantica", - "children": ["dev-1A1"], - "identation": 1, - "order": 14, - "unit": 3 + "data": { + "label": "Física cuantica", + "children": ["dev-1A1"], + "identation": 1, + "order": 14, + "unit": 3 + } }, { "id": "dev9A1", - "x": 375, - "y": 350, + "position": { + "x": 375, + "y": 350 + }, "type": "folder", - "title": "Aerodínamica, refuerzo", - "children": ["dev10A1"], - "identation": 2, - "order": 5, - "unit": 1 + "data": { + "label": "Aerodínamica, refuerzo", + "children": ["dev10A1"], + "identation": 2, + "order": 5, + "unit": 1 + } }, { "id": "dev10A1", - "x": 500, - "y": 350, + "position": { + "x": 500, + "y": 350 + }, "type": "quiz", - "title": "Aerodínamica, refuerzo", - "children": ["dev11A1", "dev12A1", "dev5A1"], - "identation": 2, - "order": 6, - "unit": 1, - "conditions": { - "type": "conditionsGroup", - "id": "98437432194690", - "op": "&", - "conditions": [ - { - "type": "qualification", - "id": "64819193482345", - "op": "fullCourse", - "objective": "2" - }, - { - "type": "qualification", - "id": "1245689934223", - "op": "fullCourse", - "objective": "2" - } - ] + "data": { + "label": "Aerodínamica, refuerzo", + "children": ["dev11A1", "dev12A1", "dev5A1"], + "identation": 2, + "order": 6, + "unit": 1, + "conditions": { + "type": "conditionsGroup", + "id": "98437432194690", + "op": "&", + "conditions": [ + { + "type": "qualification", + "id": "64819193482345", + "op": "fullCourse", + "objective": "2" + }, + { + "type": "qualification", + "id": "1245689934223", + "op": "fullCourse", + "objective": "2" + } + ] + } } }, { "id": "dev11A1", - "x": 625, - "y": 175, + "position": { + "x": 625, + "y": 175 + }, "type": "badge", - "title": "Recuperación", - "identation": 3 + "data": { + "label": "Recuperación" + } }, { "id": "dev12A1", - "x": 625, - "y": 525, + "position": { + "x": 625, + "y": 525 + }, "type": "assign", - "title": "Trabajo de recuperación", - "children": ["dev6A1"], - "identation": 3, - "order": 7, - "unit": 1 + "data": { + "label": "Trabajo de recuperación", + "children": ["dev6A1"], + "identation": 3, + "order": 7, + "unit": 1 + } }, { "id": "devFragment", - "x": 1125, - "y": 175, + "position": { + "x": 1125, + "y": 175 + }, "type": "fragment", - "title": "Fragmento", - "style": { - "width": 318, - "height": 68 - }, - "expanded": false, - "innerNodes": [ - { - "id": "dev13A1", - "x": 32, - "y": 64 + "data": { + "label": "Fragmento", + "style": { + "width": 318, + "height": 68 }, - { - "id": "dev14A1", - "x": 157, - "y": 64 - }, - { - "id": "dev15A1", - "x": 282, - "y": 64 - } - ] + "expanded": false, + "innerNodes": [ + { + "id": "dev13A1", + "position": { + "x": 32, + "y": 64 + } + }, + { + "id": "dev14A1", + "position": { + "x": 157, + "y": 64 + } + }, + { + "id": "dev15A1", + "position": { + "x": 282, + "y": 64 + } + } + ] + } }, { "id": "dev13A1", - "x": 32, - "y": 64, + "position": { + "x": 32, + "y": 64 + }, "type": "page", - "title": "Ayuda física de fluidos", - "parent": "devFragment", - "children": ["dev14A1"], - "identation": 3, - "order": 10, - "unit": 2 + "parentNode": "devFragment", + "data": { + "label": "Ayuda física de fluidos", + "children": ["dev14A1"], + "identation": 3, + "order": 10, + "unit": 2 + } }, { "id": "dev14A1", - "x": 157, - "y": 64, + "position": { + "x": 157, + "y": 64 + }, "type": "forum", - "title": "Preguntas fluidos", - "parent": "devFragment", - "children": ["dev15A1"], - "identation": 3, - "order": 11, - "unit": 2 + "parentNode": "devFragment", + "data": { + "label": "Preguntas fluidos", + "children": ["dev15A1"], + "identation": 3, + "order": 11, + "unit": 2 + } }, { "id": "dev15A1", - "x": 282, - "y": 64, + "position": { + "x": 282, + "y": 64 + }, "type": "quiz", - "title": "Recuperación fluidos", - "parent": "devFragment", - "children": ["dev7A1", "dev8A1", "dev16A1"], - "identation": 3, - "order": 12, - "unit": 2, - "conditions": { - "type": "conditionsGroup", - "id": "66789554263476", - "op": "&", - "conditions": [ - { - "type": "22374819276399", - "id": "33211882379017", - "op": "fullCourse", - "objective": "2" - }, - { - "type": "qualification", - "id": "45566778891238", - "op": "fullCourse", - "objective": "2" - }, - { - "type": "qualification", - "id": "92172368816234", - "op": "fullCourse", - "objective": "2" - } - ] + "parentNode": "devFragment", + "data": { + "label": "Recuperación fluidos", + "children": ["dev7A1", "dev8A1", "dev16A1"], + "identation": 3, + "order": 12, + "unit": 2, + "conditions": { + "type": "conditionsGroup", + "id": "66789554263476", + "op": "&", + "conditions": [ + { + "type": "22374819276399", + "id": "33211882379017", + "op": "fullCourse", + "objective": "2" + }, + { + "type": "qualification", + "id": "45566778891238", + "op": "fullCourse", + "objective": "2" + }, + { + "type": "qualification", + "id": "92172368816234", + "op": "fullCourse", + "objective": "2" + } + ] + } } }, { "id": "dev16A1", - "x": 1500, - "y": 350, + "position": { + "x": 1500, + "y": 350 + }, "type": "assign", - "title": "Trabajo de recuperación", - "children": ["dev7A1", "dev8A1"], - "identation": 4, - "order": 13, - "unit": 2 + "data": { + "label": "Trabajo de recuperación", + "children": ["dev7A1", "dev8A1"], + "identation": 4, + "order": 13, + "unit": 2 + } } ] }, @@ -373,19 +455,25 @@ "blocksData": [ { "id": "dev-2A2", - "x": 0, - "y": 0, + "position": { + "x": 0, + "y": 0 + }, "type": "start", - "title": "Inicio", - "identation": 1 + "data": { + "label": "Inicio" + } }, { "id": "dev-1A2", - "x": 125, - "y": 0, + "position": { + "x": 125, + "y": 0 + }, "type": "end", - "title": "Final", - "identation": 1 + "data": { + "label": "Final" + } } ] } @@ -403,145 +491,190 @@ "blocksData": [ { "id": "dev-2B1", - "x": 0, - "y": 175, + "position": { + "x": 0, + "y": 175 + }, "type": "start", - "title": "Inicio", - "children": ["dev0B1"], - "identation": 1 + "data": { + "label": "Inicio", + "children": ["dev0B1"] + } }, { "id": "dev-1B1", - "x": 1000, - "y": 525, + "position": { + "x": 1000, + "y": 525 + }, "type": "end", - "title": "Final", - "identation": 1 + "data": { + "label": "Final" + } }, { "id": "dev0B1", - "x": 125, - "y": 175, + "position": { + "x": 125, + "y": 175 + }, "type": "resource", - "title": "Ecuaciones", - "children": ["dev1B1"], - "identation": 1, - "order": 1, - "unit": 1 + "data": { + "label": "Ecuaciones", + "children": ["dev1B1"], + "identation": 1, + "order": 1, + "unit": 1 + } }, { "id": "dev1B1", - "x": 250, - "y": 175, + "position": { + "x": 250, + "y": 175 + }, "type": "quiz", - "title": "Examen Tema 1", - "children": ["dev2B1", "dev4B1"], - "identation": 2, - "order": 2, - "unit": 1, - "conditions": { - "type": "conditionsGroup", - "id": "781267123670427", - "op": "&", - "conditions": [ - { - "type": "qualification", - "id": "984325782581123", - "op": "fullCourse", - "objective": "2" - }, - { - "type": "qualification", - "id": "123658623618641", - "op": "fullCourse", - "objective": "2" - } - ] + "data": { + "label": "Examen Tema 1", + "children": ["dev2B1", "dev4B1"], + "identation": 2, + "order": 2, + "unit": 1, + "conditions": { + "type": "conditionsGroup", + "id": "781267123670427", + "op": "&", + "conditions": [ + { + "type": "qualification", + "id": "984325782581123", + "op": "fullCourse", + "objective": "2" + }, + { + "type": "qualification", + "id": "123658623618641", + "op": "fullCourse", + "objective": "2" + } + ] + } } }, { "id": "dev2B1", - "x": 375, - "y": 0, + "position": { + "x": 375, + "y": 0 + }, "type": "folder", - "title": "Ecuaciones", - "children": ["dev3B1"], - "identation": 2, - "order": 3, - "unit": 1 + "data": { + "label": "Ecuaciones", + "children": ["dev3B1"], + "identation": 2, + "order": 3, + "unit": 1 + } }, { "id": "dev3B1", - "x": 500, - "y": 0, + "position": { + "x": 500, + "y": 0 + }, "type": "badge", - "title": "Insignia Ecuaciones", - "identation": 2 + "data": { + "label": "Insignia Ecuaciones" + } }, { "id": "dev4B1", - "x": 375, - "y": 350, + "position": { + "x": 375, + "y": 350 + }, "type": "url", - "title": "Web raices cuadradas", - "children": ["dev5B1"], - "identation": 1, - "order": 4, - "unit": 1 + "data": { + "label": "Web raices cuadradas", + "children": ["dev5B1"], + "identation": 1, + "order": 4, + "unit": 1 + } }, { "id": "dev5B1", - "x": 500, - "y": 350, + "position": { + "x": 500, + "y": 350 + }, "type": "forum", - "title": "Foro de discusión", - "children": ["dev6B1"], - "identation": 2, - "order": 5, - "unit": 1 + "data": { + "label": "Foro de discusión", + "children": ["dev6B1"], + "identation": 2, + "order": 5, + "unit": 1 + } }, { "id": "dev6B1", - "x": 625, - "y": 350, + "position": { + "x": 625, + "y": 350 + }, "type": "quiz", - "title": "Cuestionario de raices", - "children": ["dev7B1", "dev8B1"], - "identation": 1, - "order": 6, - "unit": 2 + "data": { + "label": "Cuestionario de raices", + "children": ["dev7B1", "dev8B1"], + "identation": 1, + "order": 6, + "unit": 2 + } }, { "id": "dev7B1", - "x": 750, - "y": 175, + "position": { + "x": 750, + "y": 175 + }, "type": "assign", - "title": "Ejercicio de raices", - "identation": 1, - "order": 7, - "unit": 2 + "data": { + "label": "Ejercicio de raices", + "identation": 1, + "order": 7, + "unit": 2 + } }, { "id": "dev8B1", - "x": 750, - "y": 525, + "position": { + "x": 750, + "y": 525 + }, "type": "choice", - "title": "Preguntas sobre raices", - "children": ["dev9B1"], - "identation": 1, - "order": 8, - "unit": 2 + "data": { + "label": "Preguntas sobre raices", + "children": ["dev9B1"], + "identation": 1, + "order": 8, + "unit": 2 + } }, { "id": "dev9B1", - "x": 875, - "y": 525, + "position": { + "x": 875, + "y": 525 + }, "type": "page", - "title": "Web informativa", - "children": ["dev-1B1"], - "identation": 2, - "order": 9, - "unit": 3 + "data": { + "label": "Web informativa", + "children": ["dev-1B1"], + "identation": 2, + "order": 9, + "unit": 3 + } } ] }, @@ -553,166 +686,222 @@ "blocksData": [ { "id": "dev-2B2", - "x": 0, - "y": 700, + "position": { + "x": 0, + "y": 700 + }, "type": "start", - "title": "Inicio", - "children": ["dev0B2"], - "identation": 1 + "data": { + "label": "Inicio", + "children": ["dev0B2"] + } }, { "id": "dev-1B2", - "x": 1000, - "y": 700, + "position": { + "x": 1000, + "y": 700 + }, "type": "end", - "title": "Final", - "identation": 1 + "data": { + "label": "Final" + } }, { "id": "dev0B2", - "x": 125, - "y": 700, + "position": { + "x": 125, + "y": 700 + }, "type": "resource", - "title": "Ecuaciones", - "children": ["dev1B2"], - "identation": 1, - "order": 1, - "unit": 1 + "data": { + "label": "Ecuaciones", + "children": ["dev1B2"], + "identation": 1, + "order": 1, + "unit": 1 + } }, { "id": "dev1B2", - "x": 250, - "y": 700, + "position": { + "x": 250, + "y": 700 + }, "type": "quiz", - "title": "Examen Tema 1", - "children": ["dev2B2", "dev5B2"], - "identation": 2, - "order": 2, - "unit": 1, - "conditions": { - "type": "conditionsGroup", - "id": "77665544331122", - "op": "&", - "conditions": [ - { - "type": "qualification", - "id": "22885507753719", - "op": "fullCourse", - "objective": "2" - }, - { - "type": "qualification", - "id": "228855077531234", - "op": "fullCourse", - "objective": "2" - } - ] + "data": { + "label": "Examen Tema 1", + "children": ["dev2B2", "dev5B2"], + "identation": 2, + "order": 2, + "unit": 1, + "conditions": { + "type": "conditionsGroup", + "id": "77665544331122", + "op": "&", + "conditions": [ + { + "type": "qualification", + "id": "22885507753719", + "op": "fullCourse", + "objective": "2" + }, + { + "type": "qualification", + "id": "228855077531234", + "op": "fullCourse", + "objective": "2" + } + ] + } } }, { "id": "dev2B2", - "x": 375, - "y": 525, + "position": { + "x": 375, + "y": 525 + }, "type": "folder", - "title": "Carpeta Ecuaciones", - "children": ["dev10B2", "dev11B2"], - "identation": 2, - "order": 3, - "unit": 1 + "data": { + "label": "Carpeta Ecuaciones", + "children": ["dev10B2", "dev11B2"], + "identation": 2, + "order": 3, + "unit": 1 + } }, { "id": "dev5B2", - "x": 375, - "y": 1050, + "position": { + "x": 375, + "y": 1050 + }, "type": "quiz", - "title": "Cuestionario de raices", - "children": ["dev6B2", "dev7B2"], - "identation": 1, - "order": 8, - "unit": 2 + "data": { + "label": "Cuestionario de raices", + "children": ["dev6B2", "dev7B2"], + "identation": 1, + "order": 8, + "unit": 2 + } }, { "id": "dev6B2", - "x": 625, - "y": 875, + "position": { + "x": 625, + "y": 875 + }, "type": "assign", - "title": "Ejercicio de raices", - "children": ["dev-1B2"], - "identation": 1, - "order": 9, - "unit": 2 + "data": { + "label": "Ejercicio de raices", + "children": ["dev-1B2"], + "identation": 1, + "order": 9, + "unit": 2 + } }, { "id": "dev7B2", "x": 625, "y": 1225, + "position": { + "x": 625, + "y": 1225 + }, "type": "choice", - "title": "Preguntas y respuestas", - "children": ["dev40B2", "dev41B2"], - "identation": 1, - "order": 10, - "unit": 2 + "data": { + "label": "Preguntas y respuestas", + "children": ["dev40B2", "dev41B2"], + "identation": 1, + "order": 10, + "unit": 2 + } }, { "id": "dev10B2", - "x": 500, - "y": 350, + "position": { + "x": 500, + "y": 350 + }, "type": "folder", - "title": "Carpeta Ecuaciones 2", - "children": ["dev12B2", "dev13B2"], - "identation": 2, - "order": 4, - "unit": 1 + "data": { + "label": "Carpeta Ecuaciones 2", + "children": ["dev12B2", "dev13B2"], + "identation": 2, + "order": 4, + "unit": 1 + } }, { "id": "dev11B2", - "x": 500, - "y": 700, + "position": { + "x": 500, + "y": 700 + }, "type": "folder", - "title": "Carpeta Ecuaciones 3", - "identation": 2, - "order": 5, - "unit": 1 + "data": { + "label": "Carpeta Ecuaciones 3", + "identation": 2, + "order": 5, + "unit": 1 + } }, { "id": "dev12B2", - "x": 625, - "y": 175, + "position": { + "x": 625, + "y": 175 + }, "type": "folder", - "title": "Insignia Ecuaciones 4", - "identation": 2, - "order": 6, - "unit": 1 + "data": { + "label": "Insignia Ecuaciones 4", + "identation": 2, + "order": 6, + "unit": 1 + } }, { "id": "dev13B2", - "x": 625, - "y": 525, + "position": { + "x": 625, + "y": 525 + }, "type": "folder", - "title": "Carpeta Ecuaciones 5", - "identation": 2, - "order": 7, - "unit": 1 + "data": { + "label": "Carpeta Ecuaciones 5", + "identation": 2, + "order": 7, + "unit": 1 + } }, { "id": "dev40B2", - "x": 750, - "y": 1050, + "position": { + "x": 750, + "y": 1050 + }, "type": "page", - "title": "Web informativa 2", - "identation": 2, - "order": 11, - "unit": 2 + "data": { + "label": "Web informativa 2", + "identation": 2, + "order": 11, + "unit": 2 + } }, { "id": "dev41B2", - "x": 750, - "y": 1400, + "position": { + "x": 750, + "y": 1400 + }, "type": "page", - "title": "Web informativa 3", - "identation": 2, - "order": 12, - "unit": 2 + "data": { + "label": "Web informativa 3", + "identation": 2, + "order": 12, + "unit": 2 + } } ] }, @@ -724,220 +913,294 @@ "blocksData": [ { "id": "dev-2B3", - "x": 0, - "y": 175, + "position": { + "x": 0, + "y": 175 + }, "type": "start", - "title": "Inicio", - "children": ["dev0B3"], - "identation": 1 + "data": { + "label": "Inicio", + "children": ["dev0B3"] + } }, { "id": "dev-1B3", - "x": 1500, - "y": 525, + "position": { + "x": 1500, + "y": 525 + }, "type": "end", - "title": "Final", - "identation": 1 + "data": { + "label": "Final" + } }, { "id": "dev0B3", - "x": 125, - "y": 175, + "position": { + "x": 125, + "y": 175 + }, "type": "resource", - "title": "Ecuaciones", - "children": ["dev1B3"], - "identation": 1, - "order": 1, - "unit": 1 + "data": { + "label": "Ecuaciones", + "children": ["dev1B3"], + "identation": 1, + "order": 1, + "unit": 1 + } }, { "id": "dev1B3", - "x": 250, - "y": 175, + "position": { + "x": 250, + "y": 175 + }, "type": "quiz", - "title": "Examen Tema 1", - "children": ["dev2B3", "dev3B3"], - "identation": 2, - "order": 2, - "unit": 1, - "conditions": { - "type": "conditionsGroup", - "id": "44885577221100", - "op": "&", - "conditions": [ - { - "type": "qualification", - "id": "0987886579865", - "op": "fullCourse", - "objective": "2" - }, - { - "type": "qualification", - "id": "3678543556977", - "op": "fullCourse", - "objective": "2" - } - ] + "data": { + "label": "Examen Tema 1", + "children": ["dev2B3", "dev3B3"], + "identation": 2, + "order": 2, + "unit": 1, + "conditions": { + "type": "conditionsGroup", + "id": "44885577221100", + "op": "&", + "conditions": [ + { + "type": "qualification", + "id": "0987886579865", + "op": "fullCourse", + "objective": "2" + }, + { + "type": "qualification", + "id": "3678543556977", + "op": "fullCourse", + "objective": "2" + } + ] + } } }, { "id": "dev2B3", - "x": 375, - "y": 0, + "position": { + "x": 375, + "y": 0 + }, "type": "folder", - "title": "Carpeta Ecuaciones", - "identation": 2, - "order": 3, - "unit": 1 + "data": { + "label": "Carpeta Ecuaciones", + "identation": 2, + "order": 3, + "unit": 1 + } }, { "id": "dev3B3", - "x": 375, - "y": 350, + "position": { + "x": 375, + "y": 350 + }, "type": "url", - "title": "Web raices cuadradas", - "children": ["dev4B3"], - "identation": 1, - "order": 4, - "unit": 1 + "data": { + "label": "Web raices cuadradas", + "children": ["dev4B3"], + "identation": 1, + "order": 4, + "unit": 1 + } }, { "id": "dev4B3", - "x": 500, - "y": 350, + "position": { + "x": 500, + "y": 350 + }, "type": "forum", - "title": "Foro de discusión", - "children": ["dev5B3"], - "identation": 2, - "order": 5, - "unit": 1 + "data": { + "label": "Foro de discusión", + "children": ["dev5B3"], + "identation": 2, + "order": 5, + "unit": 1 + } }, { "id": "dev5B3", - "x": 625, - "y": 350, + "position": { + "x": 625, + "y": 350 + }, "type": "quiz", - "title": "Cuestionario de raices", - "children": ["dev6B3", "dev7B3"], - "identation": 1, - "order": 6, - "unit": 1 + "data": { + "label": "Cuestionario de raices", + "children": ["dev6B3", "dev7B3"], + "identation": 1, + "order": 6, + "unit": 1 + } }, { "id": "dev6B3", - "x": 750, - "y": 0, + "position": { + "x": 750, + "y": 0 + }, "type": "assign", - "title": "Ejercicio de raices", - "identation": 1, - "order": 7, - "unit": 1 + "data": { + "label": "Ejercicio de raices", + "identation": 1, + "order": 7, + "unit": 1 + } }, { "id": "dev7B3", - "x": 750, - "y": 350, + "position": { + "x": 750, + "y": 350 + }, "type": "choice", - "title": "Preguntas de Matemáticas", - "children": ["dev8B3"], - "identation": 1, - "order": 8, - "unit": 1 + "data": { + "label": "Preguntas de Matemáticas", + "children": ["dev8B3"], + "identation": 1, + "order": 8, + "unit": 1 + } }, { "id": "dev8B3", - "x": 875, - "y": 525, + "position": { + "x": 875, + "y": 525 + }, "type": "page", - "title": "Web informativa", - "children": ["dev40B3", "dev41B3"], - "identation": 2, - "order": 9, - "unit": 1 + "data": { + "label": "Web informativa", + "children": ["dev40B3", "dev41B3"], + "identation": 2, + "order": 9, + "unit": 1 + } }, { "id": "dev40B3", - "x": 1000, - "y": 875, + "position": { + "x": 1000, + "y": 875 + }, "type": "page", - "title": "Web informativa 2", - "identation": 2, - "order": 10, - "unit": 2 + "data": { + "label": "Web informativa 2", + "identation": 2, + "order": 10, + "unit": 2 + } }, { "id": "dev41B3", - "x": 1000, - "y": 525, + "position": { + "x": 1000, + "y": 525 + }, "type": "page", - "title": "Web informativa 3", - "children": ["dev44B3", "dev45B3"], - "identation": 2, - "order": 11, - "unit": 2 + "data": { + "label": "Web informativa 3", + "children": ["dev44B3", "dev45B3"], + "identation": 2, + "order": 11, + "unit": 2 + } }, { "id": "dev44B3", - "x": 1125, - "y": 525, + "position": { + "x": 1125, + "y": 525 + }, "type": "page", - "title": "Web informativa 6", - "children": ["dev46B3", "dev47B3"], - "identation": 2, - "order": 12, - "unit": 2 + "data": { + "label": "Web informativa 6", + "children": ["dev46B3", "dev47B3"], + "identation": 2, + "order": 12, + "unit": 2 + } }, { "id": "dev45B3", - "x": 1125, - "y": 875, + "position": { + "x": 1125, + "y": 875 + }, "type": "page", - "title": "Web informativa 7", - "identation": 2, - "order": 13, - "unit": 2 + "data": { + "label": "Web informativa 7", + "identation": 2, + "order": 13, + "unit": 2 + } }, { "id": "dev46B3", - "x": 1250, - "y": 525, + "position": { + "x": 1250, + "y": 525 + }, "type": "page", - "title": "Web informativa 8", - "children": ["dev48B3", "dev49B3"], - "identation": 2, - "order": 14, - "unit": 2 + "data": { + "label": "Web informativa 8", + "children": ["dev48B3", "dev49B3"], + "identation": 2, + "order": 14, + "unit": 2 + } }, { "id": "dev47B3", - "x": 1250, - "y": 875, + "position": { + "x": 1250, + "y": 875 + }, "type": "page", - "title": "Web informativa 9", - "identation": 2, - "order": 15, - "unit": 2 + "data": { + "label": "Web informativa 9", + "identation": 2, + "order": 15, + "unit": 2 + } }, { "id": "dev48B3", - "x": 1375, - "y": 525, + "position": { + "x": 1375, + "y": 525 + }, "type": "page", - "title": "Web informativa 10", - "children": ["dev-1B3"], - "identation": 2, - "order": 16, - "unit": 2 + "data": { + "label": "Web informativa 10", + "children": ["dev-1B3"], + "identation": 2, + "order": 16, + "unit": 2 + } }, { "id": "dev49B3", - "x": 1375, - "y": 875, + "position": { + "x": 1375, + "y": 875 + }, "type": "page", - "title": "Web informativa 11", - "identation": 2, - "order": 17, - "unit": 2 + "data": { + "label": "Web informativa 11", + "identation": 2, + "order": 17, + "unit": 2 + } } ] } @@ -955,19 +1218,25 @@ "blocksData": [ { "id": "dev-2C1", - "x": 0, - "y": 0, + "position": { + "x": 0, + "y": 0 + }, "type": "start", - "title": "Inicio", - "identation": 1 + "data": { + "label": "Inicio" + } }, { "id": "dev-1C1", - "x": 125, - "y": 0, + "position": { + "x": 125, + "y": 0 + }, "type": "end", - "title": "Final", - "identation": 1 + "data": { + "label": "Final" + } } ] }, @@ -981,17 +1250,25 @@ "id": "dev-2C2", "x": 0, "y": 0, + "position": { + "x": 0, + "y": 0 + }, "type": "start", - "title": "Inicio", - "identation": 1 + "data": { + "label": "Inicio" + } }, { "id": "dev-1C2", - "x": 125, - "y": 0, + "position": { + "x": 125, + "y": 0 + }, "type": "end", - "title": "Final", - "identation": 1 + "data": { + "label": "Final" + } } ] }
Suggested edit: