jerosoler / Drawflow

Simple flow library 🖥️🖱️
https://jerosoler.github.io/Drawflow/
MIT License
4.6k stars 721 forks source link

Cannot set properties of undefined (setting 'pos_x') #440

Closed tarasowski closed 2 years ago

tarasowski commented 2 years ago

Hey Jero,

i'm having issues integrating drawflow into svelte.

Actually everything is working so far, but once the node is deleted (with connections), some strange behavior happens. Once the error occurs, I can't delete the node and the connections anymore.

drawflow-bug

jerosoler commented 2 years ago

Hi! Have you made any changes to the library?

What does the event that displays "updateState" do? It seems that after it breaks.

tarasowski commented 2 years ago

Thanks for your reply!

I haven't made any changes to the lib.

Actually I'm listening to the following events and update internal state (and save the data to my db):

  editor.on("nodeCreated", async () => await saveData())
  editor.on("nodeRemoved", async () => await saveData())
  editor.on("nodeMoved", async () => await saveData())
  editor.on("connectionCreated", async () => await saveData())
  editor.on("connectionRemoved", async () => await saveData())

here is another example. I have loaded the data from my db. Now I'm trying to move the card.

drawflow-bug2

jerosoler commented 2 years ago

How do you add nodes? I see in your picture that it is different from normal.

image

tarasowski commented 2 years ago

I'm using the following function:

  const handleAddObjective = e => {
    const objective = e.detail 
    const objectiveId = e.detail.objectiveId
    editor.addNode(
    objective.objectiveId, 1, 1, 300, 200, "github", {}, 
      createHTML(objective.objectiveId));
  }

After loading editor.load(json)

{
    "drawflow": {
        "Home": {
            "data": {
                "1": {
                    "typenode": false,
                    "outputs": {
                        "output_1": {
                            "connections": []
                        }
                    },
                    "pos_y": 270,
                    "pos_x": 526.4285714285714,
                    "data": {},
                    "inputs": {
                        "input_1": {
                            "connections": []
                        }
                    },
                    "name": "78ca9029-96d1-4624-ab3a-f63bfdfbad4c",
                    "html": "\n    <div  class=\"\">\n<!-- ARCHIVED -->\n    <h5 class=\"text-tint-300 font-bold text-base mb-2\">Create content repository for marketing</h5>\n        <p class=\"text-tint-600 mb-2\">Updated 2022-05-26</p>\n    <ul class=\"\">\n      \n    <ul class=\"\">\n      <li class=\"flex items-center gap-3 mb-3\">\n      <button title=\"Dimitri Test3\" class='text-white w-8 h-8 rounded-full flex items-center justify-center bg-tint-800 bg-gradient-to-r from-cyan-500 to-blue-500'>DT</button>\n      <div>\n      <p class=\"text-tint-300 text-sm\" title=\"Run 5 two-hour workshops for max. 10 employees on how to use / add content to the repository\">Run 5 two-hour workshops for max. 10 employees on ...</p>\n      <div class=\"flex gap-4 items-center\">\n      <div class=\"bg-neutural-000 min-h-[12px] min-w-[12rem]\">\n      <div style=\"width: 38%\" class=\"bg-accent-green min-h-[12px]\"></div>\n      </div>\n      <p class=\"\">38%</p>\n      </div>\n      </div>\n      </div>\n      </div>\n      </div>\n      </li>\n    </ul>\n      \n    <ul class=\"\">\n      <li class=\"flex items-center gap-3 mb-3\">\n      <button title=\"Test User\" class='text-white w-8 h-8 rounded-full flex items-center justify-center bg-tint-800 bg-gradient-to-r from-cyan-500 to-blue-500'>TU</button>\n      <div>\n      <p class=\"text-tint-300 text-sm\" title=\"Establish a dashboard to measure the number of new ideas and run a contest among our employees\">Establish a dashboard to measure the number of new...</p>\n      <div class=\"flex gap-4 items-center\">\n      <div class=\"bg-neutural-000 min-h-[12px] min-w-[12rem]\">\n      <div style=\"width: 49%\" class=\"bg-accent-green min-h-[12px]\"></div>\n      </div>\n      <p class=\"\">49%</p>\n      </div>\n      </div>\n      </div>\n      </div>\n      </div>\n      </li>\n    </ul>\n      \n    <ul class=\"\">\n      <li class=\"flex items-center gap-3 mb-3\">\n      <button title=\"Dimitri Test3\" class='text-white w-8 h-8 rounded-full flex items-center justify-center bg-tint-800 bg-gradient-to-r from-cyan-500 to-blue-500'>DT</button>\n      <div>\n      <p class=\"text-tint-300 text-sm\" title=\"Create a trello board as a content repository and submit 100 potential content ideas\">Create a trello board as a content repository and ...</p>\n      <div class=\"flex gap-4 items-center\">\n      <div class=\"bg-neutural-000 min-h-[12px] min-w-[12rem]\">\n      <div style=\"width: 0%\" class=\"bg-accent-green min-h-[12px]\"></div>\n      </div>\n      <p class=\"\">0%</p>\n      </div>\n      </div>\n      </div>\n      </div>\n      </div>\n      </li>\n    </ul>\n      \n    <ul>\n    </div>\n ",
                    "id": 2,
                    "class": "github"
                },
                "2": {
                    "typenode": false,
                    "outputs": {
                        "output_1": {
                            "connections": []
                        }
                    },
                    "pos_y": -43.42857142857143,
                    "pos_x": 920.4285714285714,
                    "data": {},
                    "inputs": {
                        "input_1": {
                            "connections": []
                        }
                    },
                    "name": "850d5c6a-aef6-4ce2-9e59-a0f127b0b01f",
                    "html": "\n    <div  class=\"\">\n<!-- ARCHIVED -->\n    <h5 class=\"text-tint-300 font-bold text-base mb-2\">Increase company brand awareness and engagement</h5>\n        <p class=\"text-tint-600 mb-2\">Updated 2022-05-26</p>\n    <ul class=\"\">\n      \n    <ul class=\"\">\n      <li class=\"flex items-center gap-3 mb-3\">\n      <button title=\"Dimitri Test3\" class='text-white w-8 h-8 rounded-full flex items-center justify-center bg-tint-800 bg-gradient-to-r from-cyan-500 to-blue-500'>DT</button>\n      <div>\n      <p class=\"text-tint-300 text-sm\" title=\"Create 1 content calendar and assign team members\">Create 1 content calendar and assign team members</p>\n      <div class=\"flex gap-4 items-center\">\n      <div class=\"bg-neutural-000 min-h-[12px] min-w-[12rem]\">\n      <div style=\"width: 50%\" class=\"bg-accent-green min-h-[12px]\"></div>\n      </div>\n      <p class=\"\">50%</p>\n      </div>\n      </div>\n      </div>\n      </div>\n      </div>\n      </li>\n    </ul>\n      \n    <ul class=\"\">\n      <li class=\"flex items-center gap-3 mb-3\">\n      <button title=\"undefined undefined\" class='rounded-full bg-tint-800 w-8 h-8'></button>\n      <div>\n      <p class=\"text-tint-300 text-sm\" title=\"Design 5 TikTok campaigns and run brand awareness surveys\">Design 5 TikTok campaigns and run brand awareness ...</p>\n      <div class=\"flex gap-4 items-center\">\n      <div class=\"bg-neutural-000 min-h-[12px] min-w-[12rem]\">\n      <div style=\"width: 20%\" class=\"bg-accent-green min-h-[12px]\"></div>\n      </div>\n      <p class=\"\">20%</p>\n      </div>\n      </div>\n      </div>\n      </div>\n      </div>\n      </li>\n    </ul>\n      \n    <ul class=\"\">\n      <li class=\"flex items-center gap-3 mb-3\">\n      <button title=\"Test User\" class='text-white w-8 h-8 rounded-full flex items-center justify-center bg-tint-800 bg-gradient-to-r from-cyan-500 to-blue-500'>TU</button>\n      <div>\n      <p class=\"text-tint-300 text-sm\" title=\"Increase media spendings by 50% on Facebook in Q3\">Increase media spendings by 50% on Facebook in Q3</p>\n      <div class=\"flex gap-4 items-center\">\n      <div class=\"bg-neutural-000 min-h-[12px] min-w-[12rem]\">\n      <div style=\"width: 30%\" class=\"bg-accent-green min-h-[12px]\"></div>\n      </div>\n      <p class=\"\">30%</p>\n      </div>\n      </div>\n      </div>\n      </div>\n      </div>\n      </li>\n    </ul>\n      \n    <ul>\n    </div>\n ",
                    "id": 4,
                    "class": "github"
                },
                "3": {
                    "typenode": false,
                    "outputs": {
                        "output_1": {
                            "connections": []
                        }
                    },
                    "pos_y": 592.4285714285714,
                    "pos_x": 284.42857142857144,
                    "data": {},
                    "inputs": {
                        "input_1": {
                            "connections": []
                        }
                    },
                    "name": "9ec383eb-92b0-4320-aee2-c9b6ac33038d",
                    "html": "\n    <div  class=\"\">\n<!-- ARCHIVED -->\n    <h5 class=\"text-tint-300 font-bold text-base mb-2\">Develop a plan to introduce state of the art technology</h5>\n        <p class=\"text-tint-600 mb-2\">Updated 2022-05-24</p>\n    <ul class=\"\">\n      \n    <ul class=\"\">\n      <li class=\"flex items-center gap-3 mb-3\">\n      <button title=\"undefined undefined\" class='rounded-full bg-tint-800 w-8 h-8'></button>\n      <div>\n      <p class=\"text-tint-300 text-sm\" title=\"Create strategy, vision, roadmap and digital product portfolio\">Create strategy, vision, roadmap and digital produ...</p>\n      <div class=\"flex gap-4 items-center\">\n      <div class=\"bg-neutural-000 min-h-[12px] min-w-[12rem]\">\n      <div style=\"width: 0%\" class=\"bg-accent-green min-h-[12px]\"></div>\n      </div>\n      <p class=\"\">0%</p>\n      </div>\n      </div>\n      </div>\n      </div>\n      </div>\n      </li>\n    </ul>\n      \n    <ul class=\"\">\n      <li class=\"flex items-center gap-3 mb-3\">\n      <button title=\"undefined undefined\" class='rounded-full bg-tint-800 w-8 h-8'></button>\n      <div>\n      <p class=\"text-tint-300 text-sm\" title=\"Manage to launch 3 POCs to show improvement to the current state\">Manage to launch 3 POCs to show improvement to the...</p>\n      <div class=\"flex gap-4 items-center\">\n      <div class=\"bg-neutural-000 min-h-[12px] min-w-[12rem]\">\n      <div style=\"width: 0%\" class=\"bg-accent-green min-h-[12px]\"></div>\n      </div>\n      <p class=\"\">0%</p>\n      </div>\n      </div>\n      </div>\n      </div>\n      </div>\n      </li>\n    </ul>\n      \n    <ul class=\"\">\n      <li class=\"flex items-center gap-3 mb-3\">\n      <button title=\"undefined undefined\" class='rounded-full bg-tint-800 w-8 h-8'></button>\n      <div>\n      <p class=\"text-tint-300 text-sm\" title=\"Hold strategy workshops across 5 divisions\">Hold strategy workshops across 5 divisions</p>\n      <div class=\"flex gap-4 items-center\">\n      <div class=\"bg-neutural-000 min-h-[12px] min-w-[12rem]\">\n      <div style=\"width: 0%\" class=\"bg-accent-green min-h-[12px]\"></div>\n      </div>\n      <p class=\"\">0%</p>\n      </div>\n      </div>\n      </div>\n      </div>\n      </div>\n      </li>\n    </ul>\n      \n    <ul>\n    </div>\n ",
                    "id": 6,
                    "class": "github"
                }
            }
        }
    }
}

Before loading

{
    "drawflow": {
        "Home": {
            "data": {
                "1": {
                    "id": 1,
                    "name": "9ec383eb-92b0-4320-aee2-c9b6ac33038d",
                    "data": {},
                    "class": "github",
                    "html": "\n    <div  class=\"\">\n    <h5 class=\"text-tint-300 font-bold text-base mb-2\">Develop a plan to introduce state of the art technology</h5>\n        <p class=\"text-tint-600 mb-2\">Updated 2022-05-24</p>\n    <ul class=\"\">\n      \n    <ul class=\"\">\n      <li class=\"flex items-center gap-3 mb-3\">\n      <button title=\"undefined undefined\" class='rounded-full bg-tint-800 w-8 h-8'></button>\n      <div>\n      <p class=\"text-tint-300 text-sm\" title=\"Create strategy, vision, roadmap and digital product portfolio\">Create strategy, vision, roadmap and digital produ...</p>\n      <div class=\"flex gap-4 items-center\">\n      <div class=\"bg-neutural-000 min-h-[12px] min-w-[12rem]\">\n      <div style=\"width: 0%\" class=\"bg-accent-green min-h-[12px]\"></div>\n      </div>\n      <p class=\"\">0%</p>\n      </div>\n      </div>\n      </div>\n      </div>\n      </div>\n      </li>\n    </ul>\n      \n    <ul class=\"\">\n      <li class=\"flex items-center gap-3 mb-3\">\n      <button title=\"undefined undefined\" class='rounded-full bg-tint-800 w-8 h-8'></button>\n      <div>\n      <p class=\"text-tint-300 text-sm\" title=\"Manage to launch 3 POCs to show improvement to the current state\">Manage to launch 3 POCs to show improvement to the...</p>\n      <div class=\"flex gap-4 items-center\">\n      <div class=\"bg-neutural-000 min-h-[12px] min-w-[12rem]\">\n      <div style=\"width: 0%\" class=\"bg-accent-green min-h-[12px]\"></div>\n      </div>\n      <p class=\"\">0%</p>\n      </div>\n      </div>\n      </div>\n      </div>\n      </div>\n      </li>\n    </ul>\n      \n    <ul class=\"\">\n      <li class=\"flex items-center gap-3 mb-3\">\n      <button title=\"undefined undefined\" class='rounded-full bg-tint-800 w-8 h-8'></button>\n      <div>\n      <p class=\"text-tint-300 text-sm\" title=\"Hold strategy workshops across 5 divisions\">Hold strategy workshops across 5 divisions</p>\n      <div class=\"flex gap-4 items-center\">\n      <div class=\"bg-neutural-000 min-h-[12px] min-w-[12rem]\">\n      <div style=\"width: 0%\" class=\"bg-accent-green min-h-[12px]\"></div>\n      </div>\n      <p class=\"\">0%</p>\n      </div>\n      </div>\n      </div>\n      </div>\n      </div>\n      </li>\n    </ul>\n      \n    <ul>\n    </div>\n ",
                    "typenode": false,
                    "inputs": {
                        "input_1": {
                            "connections": []
                        }
                    },
                    "outputs": {
                        "output_1": {
                            "connections": []
                        }
                    },
                    "pos_x": -161.42857142857142,
                    "pos_y": 222.42857142857142
                },
                "2": {
                    "id": 2,
                    "name": "78ca9029-96d1-4624-ab3a-f63bfdfbad4c",
                    "data": {},
                    "class": "github",
                    "html": "\n    <div  class=\"\">\n    <h5 class=\"text-tint-300 font-bold text-base mb-2\">Create content repository for marketing</h5>\n        <p class=\"text-tint-600 mb-2\">Updated 2022-05-26</p>\n    <ul class=\"\">\n      \n    <ul class=\"\">\n      <li class=\"flex items-center gap-3 mb-3\">\n      <button title=\"Dimitri Test3\" class='text-white w-8 h-8 rounded-full flex items-center justify-center bg-tint-800 bg-gradient-to-r from-cyan-500 to-blue-500'>DT</button>\n      <div>\n      <p class=\"text-tint-300 text-sm\" title=\"Run 5 two-hour workshops for max. 10 employees on how to use / add content to the repository\">Run 5 two-hour workshops for max. 10 employees on ...</p>\n      <div class=\"flex gap-4 items-center\">\n      <div class=\"bg-neutural-000 min-h-[12px] min-w-[12rem]\">\n      <div style=\"width: 38%\" class=\"bg-accent-green min-h-[12px]\"></div>\n      </div>\n      <p class=\"\">38%</p>\n      </div>\n      </div>\n      </div>\n      </div>\n      </div>\n      </li>\n    </ul>\n      \n    <ul class=\"\">\n      <li class=\"flex items-center gap-3 mb-3\">\n      <button title=\"Test User\" class='text-white w-8 h-8 rounded-full flex items-center justify-center bg-tint-800 bg-gradient-to-r from-cyan-500 to-blue-500'>TU</button>\n      <div>\n      <p class=\"text-tint-300 text-sm\" title=\"Establish a dashboard to measure the number of new ideas and run a contest among our employees\">Establish a dashboard to measure the number of new...</p>\n      <div class=\"flex gap-4 items-center\">\n      <div class=\"bg-neutural-000 min-h-[12px] min-w-[12rem]\">\n      <div style=\"width: 49%\" class=\"bg-accent-green min-h-[12px]\"></div>\n      </div>\n      <p class=\"\">49%</p>\n      </div>\n      </div>\n      </div>\n      </div>\n      </div>\n      </li>\n    </ul>\n      \n    <ul class=\"\">\n      <li class=\"flex items-center gap-3 mb-3\">\n      <button title=\"Dimitri Test3\" class='text-white w-8 h-8 rounded-full flex items-center justify-center bg-tint-800 bg-gradient-to-r from-cyan-500 to-blue-500'>DT</button>\n      <div>\n      <p class=\"text-tint-300 text-sm\" title=\"Create a trello board as a content repository and submit 100 potential content ideas\">Create a trello board as a content repository and ...</p>\n      <div class=\"flex gap-4 items-center\">\n      <div class=\"bg-neutural-000 min-h-[12px] min-w-[12rem]\">\n      <div style=\"width: 0%\" class=\"bg-accent-green min-h-[12px]\"></div>\n      </div>\n      <p class=\"\">0%</p>\n      </div>\n      </div>\n      </div>\n      </div>\n      </div>\n      </li>\n    </ul>\n      \n    <ul>\n    </div>\n ",
                    "typenode": false,
                    "inputs": {
                        "input_1": {
                            "connections": []
                        }
                    },
                    "outputs": {
                        "output_1": {
                            "connections": []
                        }
                    },
                    "pos_x": 481,
                    "pos_y": 538
                },
                "3": {
                    "id": 3,
                    "name": "850d5c6a-aef6-4ce2-9e59-a0f127b0b01f",
                    "data": {},
                    "class": "github",
                    "html": "\n    <div  class=\"\">\n    <h5 class=\"text-tint-300 font-bold text-base mb-2\">Increase company brand awareness and engagement</h5>\n        <p class=\"text-tint-600 mb-2\">Updated 2022-05-26</p>\n    <ul class=\"\">\n      \n    <ul class=\"\">\n      <li class=\"flex items-center gap-3 mb-3\">\n      <button title=\"Dimitri Test3\" class='text-white w-8 h-8 rounded-full flex items-center justify-center bg-tint-800 bg-gradient-to-r from-cyan-500 to-blue-500'>DT</button>\n      <div>\n      <p class=\"text-tint-300 text-sm\" title=\"Create 1 content calendar and assign team members\">Create 1 content calendar and assign team members</p>\n      <div class=\"flex gap-4 items-center\">\n      <div class=\"bg-neutural-000 min-h-[12px] min-w-[12rem]\">\n      <div style=\"width: 50%\" class=\"bg-accent-green min-h-[12px]\"></div>\n      </div>\n      <p class=\"\">50%</p>\n      </div>\n      </div>\n      </div>\n      </div>\n      </div>\n      </li>\n    </ul>\n      \n    <ul class=\"\">\n      <li class=\"flex items-center gap-3 mb-3\">\n      <button title=\"undefined undefined\" class='rounded-full bg-tint-800 w-8 h-8'></button>\n      <div>\n      <p class=\"text-tint-300 text-sm\" title=\"Design 5 TikTok campaigns and run brand awareness surveys\">Design 5 TikTok campaigns and run brand awareness ...</p>\n      <div class=\"flex gap-4 items-center\">\n      <div class=\"bg-neutural-000 min-h-[12px] min-w-[12rem]\">\n      <div style=\"width: 20%\" class=\"bg-accent-green min-h-[12px]\"></div>\n      </div>\n      <p class=\"\">20%</p>\n      </div>\n      </div>\n      </div>\n      </div>\n      </div>\n      </li>\n    </ul>\n      \n    <ul class=\"\">\n      <li class=\"flex items-center gap-3 mb-3\">\n      <button title=\"Test User\" class='text-white w-8 h-8 rounded-full flex items-center justify-center bg-tint-800 bg-gradient-to-r from-cyan-500 to-blue-500'>TU</button>\n      <div>\n      <p class=\"text-tint-300 text-sm\" title=\"Increase media spendings by 50% on Facebook in Q3\">Increase media spendings by 50% on Facebook in Q3</p>\n      <div class=\"flex gap-4 items-center\">\n      <div class=\"bg-neutural-000 min-h-[12px] min-w-[12rem]\">\n      <div style=\"width: 30%\" class=\"bg-accent-green min-h-[12px]\"></div>\n      </div>\n      <p class=\"\">30%</p>\n      </div>\n      </div>\n      </div>\n      </div>\n      </div>\n      </li>\n    </ul>\n      \n    <ul>\n    </div>\n ",
                    "typenode": false,
                    "inputs": {
                        "input_1": {
                            "connections": []
                        }
                    },
                    "outputs": {
                        "output_1": {
                            "connections": []
                        }
                    },
                    "pos_x": 860.1428571428571,
                    "pos_y": 247.85714285714286
                }
            }
        }
    }
}
jerosoler commented 2 years ago

You don't have to use the editor.load function.

You have to use the editor.import(JSON) function.

The problem is here.

editor.start();
editor.import(jsonfile);

I see you are using uuid. If you are interested you can use:

editor.useuuid  = true;
tarasowski commented 2 years ago

Yes, I'm using editor.import(), was a mistake above. I'm doing it exactly as you described above

editor.start()
editor.import(json)

Loading works great etc. The error occurs on some nodes, usually after deletion of other nodes.

jerosoler commented 2 years ago

The problem is the id:2 It would have to be a number 1

image

Could you show your code?

tarasowski commented 2 years ago

Yes, sure.

<script>
  export let boards
  export let alignment
  import Drawflow from "drawflow";
  import { onMount, onDestroy } from "svelte";
  import "drawflow/dist/drawflow.min.css";
  import Magnify from "svelte-material-icons/Magnify.svelte";
    import SearchModal from "./SearchModal.svelte"
  import {matchSorter} from 'match-sorter'
  import { error } from "../error/state.js"
  import { updateAlignment } from "../../graphql/mutations.js" 
  import { callAPI } from "../../io.js"

  let showSearch = false
  let container;
  let editor;
  let loaded = false

  const flatBoards = boards => {
    let objectives = []
    boards.forEach(board =>
      board.views.items.forEach(view =>
        objectives.push(view.objectives.items)
        ))
    return objectives.flat()
  }

  const calculateProgress = data => {
    try {
      const val = ((data.currentValue - data.startValue) * 100) / (data.targetValue - data.startValue)
      if (val === Infinity) {
        return 0
      } else {
        return val.toFixed(0)
      }
    } catch(e) {}
  }

const generateOwnerInitials = (user) => { 
  try {
    if (user !== undefined) {
      return ((user ||  {}).firstName || "").charAt(0) 
        + ((user || {}).lastName || "").charAt(0)
    } else {
      return undefined
    }
  } catch(e) {
    console.log(e)
    return ""
  }
}

  const keyResultHTML = keyResult =>  {
    const name = keyResult.name
    const owners = keyResult?.owners?.items?.map(v => v.owner)
    const owner = owners && head(owners)
    const initials = owner && generateOwnerInitials(owner) 
    const percentage = calculateProgress(keyResult)
    return `
    <ul class="">
      <li class="flex items-center gap-3 mb-3">
      <button title="${owner?.firstName} ${owner?.lastName}" class='${initials 
? "text-white w-8 h-8 rounded-full flex items-center justify-center bg-tint-800 bg-gradient-to-r from-cyan-500 to-blue-500" 
: "rounded-full bg-tint-800 w-8 h-8"}'>${initials ? initials : ""}</button>
      <div>
      <p class="text-tint-300 text-sm" title="${name}">${name.substring(0, 50)}${name.length >50 ? "..." : ""}</p>
      <div class="flex gap-4 items-center">
      <div class="bg-neutural-000 min-h-[12px] min-w-[12rem]">
      <div style="width: ${percentage}%" class="bg-accent-green min-h-[12px]"></div>
      </div>
      <p class="">${percentage}%</p>
      </div>
      </div>
      </div>
      </div>
      </div>
      </li>
    </ul>
      `
  }

  const createHTMLLoad = objective => {
    return objective.html
    const objectiveId = objective.name
    const data = objective.data
    const o = flatBoards(boards)
    const obj = o.filter(obj => obj.id === objectiveId)
    if (obj.length > 0) {
      const first = head(obj)
      const updatedAt = first?.keyResults?.items?.map(kr => kr.updatedAt)
      const sorted = updatedAt && head(sortByDate(updatedAt))
      const lastUpdate = sorted && sorted.substring(0, 10)
    return `
    <div  class="">
<!-- ARCHIVED -->
    <h5 class="text-tint-300 font-bold text-base mb-2">${first.name}</h5>
        <p class="text-tint-600 mb-2">Updated ${lastUpdate}</p>
    <ul class="">
      ${first.keyResults.items.map(keyResultHTML).join("")}
    <ul>
    </div>
 ` 

    } else {
      const archived = `
<div  class="text-right">
      <span title="The current objective is archvied or deleted" class="w-4 h-4 bg-accent-rose text-white p-1 rounded-full font-bold px-2">Archived</span>
</div>
`
        return objective.html.replace("<!-- ARCHIVED -->", archived)
    }

  }

  const updateNodes = data => { 
    const modified = Object.keys(data).map(key => ({
      ...data[key], 
      html: createHTMLLoad(data[key])}))
    const reduced = modified.reduce((a, v, i) => ({ ...a, [i+1]: v}), {}) 
    return reduced
  }

  const loadJSON = (alignment) => {
    let json = JSON.parse(alignment.data)
    try {
      let data = (((json || {}).drawflow || {}).Home || {}).data
      json.drawflow.Home.data = updateNodes(data)
      editor && editor.import(json)
      loaded = true
    } catch(e) {
      console.log("error message", e.message)
      $error = e.message
    }
  }

  const saveData = async (e, d) => {
    const data = editor.export()
    alignment.data = JSON.stringify(data)
    try {
      return await callAPI(updateAlignment, {input: {
        organizationId: window.user.profile,
        id: alignment.id,
        data: JSON.stringify(data)
      }})
    } catch(e) {
      console.log(e.message)
      $error = e.message
    }
  }

  onMount(async () => {
    try {
      editor = new Drawflow(container);
      editor.reroute = true;
      editor.start();
      editor.zoom_max = 1.6;
      editor.zoom_min = 0.5;
      editor.zoom_value = 0.1;
      editor.zoom = alignment.lastZoomValue || 0.7
      editor.force_first_input = true;
      editor.zoom_refresh()
      editor.on("nodeCreated", async () => await saveData())
      editor.on("nodeRemoved", async () => await saveData())
      editor.on("nodeMoved", async () => await saveData())
      editor.on("connectionCreated", async () => await saveData())
      editor.on("connectionRemoved", async () => await saveData())
    } catch(e) {
      console.log(e.message)
    }
  });

  $: alignment.data && boards.length > 0 && editor && !loaded &&  loadJSON(alignment)

  const handleZoomOut = () => {
    editor.zoom_out();
    alignment.lastZoomValue = editor.zoom_last_value 
  };
  const handleZoomIn = () => {
    editor.zoom_in();
    alignment.lastZoomValue = editor.zoom_last_value 
  };

  const sortByDate = xs =>
    xs.sort((a, b) => new Date(b) - new Date(b))

  const head = xs => {
    try {
      return xs[0]
    } catch(e) {
      return null
    }
  }

  const createHTML = objectiveId => {
    const o = flatBoards(boards)
    const obj = o.filter(obj => obj.id === objectiveId)
    if (obj.length > 0) {
      const first = head(obj)
      const updatedAt = first?.keyResults?.items?.map(kr => kr.updatedAt)
      const sorted = updatedAt && head(sortByDate(updatedAt))
      const lastUpdate = sorted && sorted.substring(0, 10)
      console.log("lastUpdate", lastUpdate)
    return `
    <div  class="">
    <h5 class="text-tint-300 font-bold text-base mb-2">${first.name}</h5>
        <p class="text-tint-600 mb-2">Updated ${lastUpdate}</p>
    <ul class="">
      ${first.keyResults.items.map(keyResultHTML).join("")}
    <ul>
    </div>
 ` 

    } else {
      console.log("return archived")
    }

  }

  const handleAddObjective = e => {
    const objective = e.detail 
    const objectiveId = e.detail.objectiveId
    editor.addNode(
    objective.objectiveId, 1, 1, 300, 200, "github", {}, 
      createHTML(objective.objectiveId));
  }

  const handleLoad = () => {
    console.log(editor.drawflow)

  }
</script>

{#if showSearch}
<SearchModal bind:showSearch {boards} on:add-objective={handleAddObjective} />
{/if}

<div class="drawflow max-h-[90%]"  bind:this="{container}"></div>
<div
  class="absolute bottom-10 right-10 text-base text-tint-300 shadow-zoombtn right-0 py-2.5 px-6 flex items-center gap-5 justify-center bg-white rounded s-o2zHGApCCFOx"
>
  <button on:click="{handleZoomOut}" class="zoom s-o2zHGApCCFOx" id="zoomMinus">
    <svg width="1em" height="1em" viewBox="0 0 24 24">
      <path d="M19,13H5V11H19V13Z" fill="currentColor"></path></svg
    ><!--<Minus>-->
  </button>
  <p id="zoomVal" class="s-o2zHGApCCFOx">{(alignment?.lastZoomValue ? alignment.lastZoomValue * 100 : 70).toFixed(0)}%</p>
  <button on:click="{handleZoomIn}" class="zoom s-o2zHGApCCFOx" id="zoomPlus">
    <svg width="1em" height="1em" viewBox="0 0 24 24">
      <path
        d="M19,13H13V19H11V13H5V11H11V5H13V11H19V13Z"
        fill="currentColor"
      ></path></svg
    ><!--<Plus>-->
  </button>
</div>
<div
  class="absolute bottom-10 right-[250px] text-base text-tint-300 shadow-zoombtn right-0 py-2.5 px-6 flex items-center gap-5 justify-center bg-white rounded s-o2zHGApCCFOx"
>

<button on:click={handleLoad}>Print State</button>
<button on:click={saveData}>Save State</button>
<button on:click={() => showSearch = !showSearch} class="flex gap-2 w-[150px] text-tint-500 items-center" type="" placeholder="Quick search...">
    <!-- Tailwind style search for key results / objectives -->
    <Magnify width={20} height={20} />
    Quick search...
  </button>
</div>

<style>

  #drawflow {
    position: relative;
    border: 1px solid green;
  }

  :global(.drawflow) {
    user-select: none;
  }

  :global(.drawflow_content_node) {
    /*border: 1px solid green !important;*/
    min-width: 400px;
  }
  :global(.drawflow_content_node > * > .title ) {
    font-family: 'Roboto';
    font-style: normal;
    font-weight: 600;
    font-size: 16px;
    line-height: 121.19%;
    color: #5E6370;
    margin-bottom: 0.5rem;

  }
  :global(.drawflow_content_node > * > .updated ) {
    font-family: 'Roboto';
    font-style: normal;
    font-weight: 400;
    font-size: 14px;
    line-height: 121.19%;
    color: #A3A6AD;
    margin-bottom: 0.5rem;

  }

  :global(.drawflow .drawflow-node) {
    background: white !important;
    border: none !important;
    /*border: 1px solid red !important;*/
    min-width: 450px;
    background: #FFFFFF;
    box-shadow: 0px 10px 20px rgba(0, 0, 0, 0.15);
    border-radius: 3px;
  }

  :global(.drawflow .drawflow-node .input) {
    width: 1.5rem;
    height: 1.5rem;
    background: #FFFFFF;
    /* Primary/Tint/800 */
    border: 2px solid #E8E9EB;
    margin-left: 0px;
  }
  :global(.drawflow .drawflow-node .output) {
    background: #FFFFFF;
    /* Primary/Tint/800 */
    border: 2px solid #E8E9EB;
    width: 1.5rem;
    height: 1.5rem;
    margin-left: 0px;
  }
  :global(.bar-zoom) {
    float: right;
    position: absolute;
    bottom: 10px;
    right: 10px;
    display: flex;
    font-size: 24px;
    color: white;
    padding: 5px 10px;
    background: #555555;
    border-radius: 4px;
    border-right: 1px solid var(--border-color);
    z-index: 5;
  }
  :global(.bar-zoom svg) {
    cursor: pointer;
    padding-left: 10px;
  }
  :global(.bar-zoom svg:nth-child(1)) {
    padding-left: 0px;
  }
  :global(.drawflow-delete) {
    background-color: white;
    border: 1px solid #D1D2D6;
    color: #5E6370;
    text-align: center;
    font-size: 16px;
  }
  :global(.drawflow .connection .arrow) {
        transform: translate(-10005px,-9999px);
  } 
</style>
jerosoler commented 2 years ago

I haven't played Svelte. Is the "alignment" variable reactive?

Could it be that every time "saveData" is called, the flow is being imported again? Remove all events to discard:

editor.on("nodeCreated", async () => await saveData())
       editor.on("nodeRemoved", async () => await saveData())
       editor.on("nodeMoved", async () => await saveData())
       editor.on("connectionCreated", async () => await saveData())
       editor.on("connectionRemoved", async () => await saveData())

You can put a console.log in the "loadJSON" function like so:

    json.drawflow.Home.data = updateNodes(data)
      console.log(json);
      editor && editor.import(json)
tarasowski commented 2 years ago

I think I have figured out the issue.

It looks like the id of the node has to be the same as the key on the data object.

Once the node gets deleted, the node id and the key on the data object doesn't match anymore.

This is what actually causing the issue, it can't find the node anymore after loading. So I changed the updateNode fn, to match the key on the data object with the node id. It is working now!

  const updateNodes = data => 
    Object.keys(data).map(key => ({
      ...data[key], 
      html: createHTMLLoad(data[key])}))
    .reduce((a, v, i) => ({ ...a, [v.id]: v}), {}) 

Thank you very much for your help! Dimi