Open fcnyp opened 4 years ago
This may help you:
Would like to see an optional feature to disable block deletes if the drag/release action is minimal. I'm finding that when clicking a block I often move it by a few pixels and cause it to be deleted (Likely because my mouse sensitive is set to super high and I'm using a trackball =)). Would be nice to see some error tolerance here.
You can use the on rearrange method to prevent the block from being deleted & instead re-attach to the previous parent. Does that help?
I believe, per the comments in #48 if I were to disable deletes by drag all together and add a trash can icon it would flow a little better. What would the method be for deleting a block (and its children) by its id?
I believe, per the comments in #48 if I were to disable deletes by drag all together and add a trash can icon it would flow a little better. What would the method be for deleting a block (and its children) by its id?
Yeah, you can certainly do that with the on rearrange method, just return false to delete the blocks currently being dragged.
As per deleting blocks by ID, I'm still working on a method! It shouldn't be hard to implement at all, I just want to make sure it's clean and it makes sense.
I added a new method:
flowy.getBlocks = function() {
return blocks;
};
Using that in combination with Lodash I'm accessing different blocks.
const BLOCKS = flowy.getBlocks();
const BLOCK_OBJECT = _.find(BLOCKS, {'id': parseInt(BLOCK_ID)});
Food for thought, provides an easy way to access blocks and I could see a similar delete method being used in combination with getBlocks where you pass in the block object or the ID
I added a new method:
flowy.getBlocks = function() { return blocks; };
Using that in combination with Lodash I'm accessing different blocks.
const BLOCKS = flowy.getBlocks(); const BLOCK_OBJECT = _.find(BLOCKS, {'id': parseInt(BLOCK_ID)});
Food for thought, provides an easy way to access blocks and I could see a similar delete method being used in combination with getBlocks where you pass in the block object or the ID
Hmm, I'm just thinking though, not sure if it's necessary when there's the flowy.output method which returns all the blocks data, including the entire block array. Unless you think it would be better to have a dedicated method only for the array?
Different use case based on my understanding. Output is great for import/export (i.e., JSON). I'm using it to access attributes in the array and to manipulate the attributes, this way my custom attributes live in the import/export data. My plan was to add a sister method to update the attributes (e.g., setBlock).
This should work to remove a block and its children, I would error check me =)
Would be nice to add some easing, as it seems jumpy and may need a bit more error handling, I think that's covered though.
flowy.deleteBlock = function(id) {
if (!Number.isInteger(id)) {
id = parseInt(id);
}
for (var i=0; i < blocks.length; i++) {
if (blocks[i].id === id) {
removeBlockEl(blocks[i].id);
blocks.splice(i, 1);
removeChildren(id);
break;
}
}
if (blocks.length > 1) {
rearrangeMe();
}
function removeChildren(parentId) {
let children = [];
for (var i = blocks.length - 1; i >= 0; i--) {
if (blocks[i].parent === parentId) {
children.push(blocks[i].id);
removeBlockEl(blocks[i].id);
blocks.splice(i, 1);
}
}
for (var i=0; i < children.length; i++) {
removeChildren(children[i]);
}
}
function removeBlockEl(id) {
document.querySelector(".blockid[value='" + id + "']").parentNode.remove();
document.querySelector(".arrowid[value='" + id + "']").parentNode.remove();
}
};
This should work to remove a block and its children, I would error check me =)
Would be nice to add some easing, as it seems jumpy and may need a bit more error handling, I think that's covered though.
flowy.deleteBlock = function(id) { if (!Number.isInteger(id)) { id = parseInt(id); } for (var i=0; i < blocks.length; i++) { if (blocks[i].id === id) { console.log(blocks[i]) removeBlockEl(blocks[i].id); blocks.splice(i, 1); removeChildren(id); break; } } if (blocks.length > 1) { rearrangeMe(); } function removeChildren(parentId) { let children = []; for (var i = blocks.length - 1; i >= 0; i--) { if (blocks[i].parent === parentId) { children.push(blocks[i].id); removeBlockEl(blocks[i].id); blocks.splice(i, 1); } } for (var i=0; i < children.length; i++) { removeChildren(children[i]); } } function removeBlockEl(id) { document.querySelector(".blockid[value='" + id + "']").parentNode.remove(); document.querySelector(".arrowid[value='" + id + "']").parentNode.remove(); } };
Yeah that's good. I am currently trying to make a method to individually remove blocks (so not just a block + children, also just a block inside the tree) so I might focus on that and then use that same method to allow for removing trees as well. The idea would be for the children of the block you want to delete to be attached to the parent of said block, it might not make the most sense, but I guess it may be good to have as an option.
I see, that would be good to have as well. You could use this method and pass in an optional second param that would perform that action and knock both out
If false just skip the el remove and splice and rewrite the block attributes to point to the new parent.
Updated correcting several errors. Still does not rearrange correctly.
flowy.deleteBlock = function(id, removeChildren) {
//Track orginal parent
let newParentId;
if (!Number.isInteger(id)) {
id = parseInt(id);
}
for (var i=0; i < blocks.length; i++) {
if (blocks[i].id === id) {
newParentId = blocks[i].parent;
removeBlockEls(blocks[i].id);
blocks.splice(i, 1);
modifyChildBlocks(id);
break;
}
}
rearrangeMe();
function modifyChildBlocks(parentId) {
let children = [];
for (var i = blocks.length - 1; i >= 0; i--) {
if (blocks[i].parent === parentId) {
children.push(blocks[i].id);
if (removeChildren === true) {
removeBlockEl(blocks[i].id);
blocks.splice(i, 1);
} else {
blocks[i].parent = newParentId;
rearrange = true;
document.querySelector(".arrowid[value='" + blocks[i].id + "']").parentNode.remove();
blockstemp = [];
blockstemp.push(blocks.filter(a => a.id == blocks[i].id)[0]);
snap(document.querySelector(".blockid[value='" + blocks[i].id + "']").parentNode, newParentId, blocks.map(a => a.id));
rearrange = false;
}
}
}
//Only call repeat of function if removing children
if (removeChildren === true) {
for (var i=0; i < children.length; i++) {
modifyChildBlocks(children[i]);
}
}
}
function removeBlockEls(id) {
document.querySelector(".blockid[value='" + id + "']").parentNode.remove();
document.querySelector(".arrowid[value='" + id + "']").parentNode.remove();
}
};
Updated correcting several errors. Still does not rearrange correctly.
flowy.deleteBlock = function(id, removeChildren) { //Track orginal parent let newParentId; if (!Number.isInteger(id)) { id = parseInt(id); } for (var i=0; i < blocks.length; i++) { if (blocks[i].id === id) { newParentId = blocks[i].parent; removeBlockEls(blocks[i].id); blocks.splice(i, 1); modifyChildBlocks(id); break; } } rearrangeMe(); function modifyChildBlocks(parentId) { let children = []; for (var i = blocks.length - 1; i >= 0; i--) { if (blocks[i].parent === parentId) { children.push(blocks[i].id); if (removeChildren === true) { removeBlockEl(blocks[i].id); blocks.splice(i, 1); } else { blocks[i].parent = newParentId; rearrange = true; document.querySelector(".arrowid[value='" + blocks[i].id + "']").parentNode.remove(); blockstemp = []; blockstemp.push(blocks.filter(a => a.id == blocks[i].id)[0]); snap(document.querySelector(".blockid[value='" + blocks[i].id + "']").parentNode, newParentId, blocks.map(a => a.id)); rearrange = false; } } } //Only call repeat of function if removing children if (removeChildren === true) { for (var i=0; i < children.length; i++) { modifyChildBlocks(children[i]); } } } function removeBlockEls(id) { document.querySelector(".blockid[value='" + id + "']").parentNode.remove(); document.querySelector(".arrowid[value='" + id + "']").parentNode.remove(); } };
Hello, did you find why this is not reaarranging? I am quite struggling but would really love to have this feature as well,
Btw @alyssaxuu when you say " It shouldn't be hard to implement at all,", do you think of a simple piece of code that would allow at least to delete a block and all of its children? Sorry to ask, but I am quite in an hurry on my project ;)
Updated correcting several errors. Still does not rearrange correctly.
flowy.deleteBlock = function(id, removeChildren) { //Track orginal parent let newParentId; if (!Number.isInteger(id)) { id = parseInt(id); } for (var i=0; i < blocks.length; i++) { if (blocks[i].id === id) { newParentId = blocks[i].parent; removeBlockEls(blocks[i].id); blocks.splice(i, 1); modifyChildBlocks(id); break; } } rearrangeMe(); function modifyChildBlocks(parentId) { let children = []; for (var i = blocks.length - 1; i >= 0; i--) { if (blocks[i].parent === parentId) { children.push(blocks[i].id); if (removeChildren === true) { removeBlockEl(blocks[i].id); blocks.splice(i, 1); } else { blocks[i].parent = newParentId; rearrange = true; document.querySelector(".arrowid[value='" + blocks[i].id + "']").parentNode.remove(); blockstemp = []; blockstemp.push(blocks.filter(a => a.id == blocks[i].id)[0]); snap(document.querySelector(".blockid[value='" + blocks[i].id + "']").parentNode, newParentId, blocks.map(a => a.id)); rearrange = false; } } } //Only call repeat of function if removing children if (removeChildren === true) { for (var i=0; i < children.length; i++) { modifyChildBlocks(children[i]); } } } function removeBlockEls(id) { document.querySelector(".blockid[value='" + id + "']").parentNode.remove(); document.querySelector(".arrowid[value='" + id + "']").parentNode.remove(); } };
Hello, did you find why this is not reaarranging? I am quite struggling but would really love to have this feature as well,
Unfortunately it's hard to say - I would have to create the method myself to be able to see why the code isn't working. It could be due to the childwidth property in the blocks array not being reset, or possibly the fact that the "i" argument in snap(drag, i, blocko)
is not in fact a block ID, but rather a position in the "blocko" array (so I know this may be a bit counterintuitive, but the way I originally approached it was to have blocko be an array of all the block IDs, so blocko = blocks.map(a => a.id);
).
In that regard, you can try the following:
blocko = blocks.map(a => a.id);
(inside modifyChildBlocks(parentId))blocks[i]
, you would use, for example, blocks.filter(a => a.id == blocko[i])[0]
(this is super counterintuitive and I should definitely make it better it in the future)Aside from that, another possible issue you might encounter is that you might accidentally remove the "blue pulse" that indicates that a block can be attached, since it automatically becomes a child of whichever block is about to become a parent. To avoid that you can just use this before you remove the block div:
canvas_div.appendChild(document.querySelector(".indicator"));
Hope that helps. I am currently working on somewhat of an "overhaul" for the library but I am not going to release new features & enhancements progressively, I would rather wait until everything is polished and release everything at once.
Btw @alyssaxuu when you say " It shouldn't be hard to implement at all,", do you think of a simple piece of code that would allow at least to delete a block and all of its children? Sorry to ask, but I am quite in an hurry on my project ;)
I don't currently have something ready in that regard, but the way I would approach it would be to look at how the code works for when you rearrange blocks (so, under flowy.moveBlock, look for if (dragblock) { }
). The idea, if you wanted to implement this quickly (albeit in a "dirty" way), would be to do the following:
if (dragblock) { }
within flowy.moveBlockblockstemp = [];
canvas_div.appendChild(document.querySelector(".indicator"));
drag.parentNode.removeChild(drag);
In theory that should work as it would act the same way as if you were manually moving a block. Let me know if it works!
Thx a lot, will try asap and come back to you. Could you detail why is childwith property needed?
Thx a lot, will try asap and come back to you. Could you detail why is childwith property needed?
All right!
The childwidth property is simply used to calculate the total width of the children under a parent, combined (including the offset between siblings). I use this in order to make sure, when building the tree, that it is both centered and also spaced out properly.
Basically to understand it better, here's an image. The space between the blue lines is the "childwidth" amount. Thanks to that, I can quickly arrange the sibling of that tree on the right with the correct spacing (red line).
If the childwidth isn't reset it might just cause problems for that reason in terms of alignment and whatnot.
Thank you very much.
I got it working for a DeleteBlock WITH children only using the following piece of code :
` flowy.deleteBlocks = function () { blocks = []; canvas_div.innerHTML = "
"; };flowy.deleteBlock = function (id) {
let newParentId;
if (!Number.isInteger(id)) {
id = parseInt(id);
}
for (var i = 0; i < blocks.length; i++) {
if (blocks[i].id === id) {
newParentId = blocks[i].parent;
canvas_div.appendChild(document.querySelector(".indicator"));
removeBlockEls(blocks[i].id);
blocks.splice(i, 1);
modifyChildBlocks(id);
break;
}
}
if (blocks.length > 1) {
rearrangeMe();
}
function modifyChildBlocks(parentId) {
let children = [];
let blocko = blocks.map((a) => a.id);
for (var i = blocko.length - 1; i >= 0; i--) {
let currentBlock = blocks.filter((a) => a.id == blocko[i])[0];
if (currentBlock.parent === parentId) {
children.push(currentBlock.id);
removeBlockEls(currentBlock.id);
blocks.splice(i, 1);
}
}
for (var i = 0; i < children.length; i++) {
modifyChildBlocks(children[i]);
}
}
function removeBlockEls(id) {
document
.querySelector(".blockid[value='" + id + "']")
.parentNode.remove();
document
.querySelector(".arrowid[value='" + id + "']")
.parentNode.remove();
}
};`
I started having a look for not removing children, but couldnt get it to work so far.
Well, I got this to work. I've been slowly going through and updating/refactoring the code. In this case I used some of the above logic for deleteBlock.
However, I've bypassed the snap function in flowy and have implemented my own draw() method. This method redraws (or moves) block, arrows, etc. Trying to get this to also support zoom in/out using style.transform, but it's slow going. The plan right now is to get insert working and then move on to zoom in/out.
Unfortunately my version of flowy is likely impossible to merge back in. If there is any interest, I may consider publishing this fork but only if there are some other people interested in helping maintain it.
Well, I got this to work. I've been slowly going through and updating/refactoring the code. In this case I used some of the above logic for deleteBlock.
However, I've bypassed the snap function in flowy and have implemented my own draw() method. This method redraws (or moves) block, arrows, etc. Trying to get this to also support zoom in/out using style.transform, but it's slow going. The plan right now is to get insert working and then move on to zoom in/out.
Unfortunately my version of flowy is likely impossible to merge back in. If there is any interest, I may consider publishing this fork but only if there are some other people interested in helping maintain it.
I'm interested, but I'd like to see how you did it before committing my time. I've accepted at this point that I too have gone too far off the beaten path to ever cleanly merge back with the main branch, but I'd like us to not all reinvent the wheel when we have the same needs.
I'm happy to share what I've done. I also now have zoom in/out working along with insert/remove for single blocks.
The Yes/No is specific to my use case where the only conditional splitting we do is on a logic check, e.g. "Has replied to SMS".
Flowy is also pretty de-coupled from the UX. I marry the two up separately along with an abstraction for supporting auto-loading blocks. But I'm likely going to start folding the UX/flowy more tightly together as there is just about no way for me to merge back in at this point.
//
┌──────────────────────────────────────────────────────────────────── // │ Delete a block by id // └──────────────────────────────────────────────────────────────────── flowy.deleteBlock = function (id, removeChildren = true) { let newParentId;
if (!Number.isInteger(id)) {
id = parseInt(id);
}
// how many children?
if (directChildrenCount(id) > 1)
removeChildren = true;
// Loop through blocks and remove the selected block
for (var i = 0; i < blocks.length; i++) {
// found the block to remove
if (blocks[i].id === id) {
// hold a reference to the blocks parent id
newParentId = blocks[i].parent;
// Set the indicator
canvas_div.appendChild(document.querySelector(".indicator"));
// Remove the block
removeBlockEls(blocks[i].id);
// Remove from blocks array
blocks.splice(i, 1);
// update child blocks
modifyChildBlocks(id, newParentId);
break;
}
}
// Ensure there are no yes/no blocks hanging around
checkForTrailingYesNo(newParentId);
if (blocks.length > 1) {
flowy.draw();
}
// Updates the child block of the block getting removed
function modifyChildBlocks(parentId, newParentId) {
let children = [];
let blocko = blocks.map((a) => a.id);
for (var i = blocko.length - 1; i >= 0; i--) {
let currentBlock = blocks.filter((a) => a.id ==
blocko[i])[0]; if (currentBlock.parent === parentId) { children.push(currentBlock.id);
// update parent id?
if (newParentId != undefined)
currentBlock.parent = newParentId;
if (removeChildren) {
removeBlockEls(currentBlock.id);
blocks.splice(i, 1);
}
}
}
// Remove all children blocks?
if (removeChildren) {
for (var i = 0; i < children.length; i++) {
modifyChildBlocks(children[i]);
}
}
}
function removeBlockEls(id) {
document.querySelector(.blockid[value='${id}']
).parentNode.remove();
document.querySelector(.arrowid[value ='${id}']
).parentNode.remove();
}
// Check that we didn't leave a trailing yes/no block
function checkForTrailingYesNo(id) {
for (var i = 0; i < blocks.length; i++) {
if (blocks[i].parent === id) {
let blockElem =
document.querySelector(.blockid[value='${blocks[i].id}']
);
let val =
blockElem.parentElement.querySelector('.blockelemtype').value;
if (val == "Yes" || val == "No") {
flowy.deleteBlock(blocks[i].id, false);
}
}
}
}
};
Founder, DailyStory
DailyStory Marketing Automationdailystory.com | +1-833-914-1105
On Thu, Apr 1, 2021 at 12:32 AM Christopher Boyd @.***> wrote:
Well, I got this to work. I've been slowly going through and updating/refactoring the code. In this case I used some of the above logic for deleteBlock.
However, I've bypassed the snap function in flowy and have implemented my own draw() method. This method redraws (or moves) block, arrows, etc. Trying to get this to also support zoom in/out using style.transform, but it's slow going. The plan right now is to get insert working and then move on to zoom in/out.
Unfortunately my version of flowy is likely impossible to merge back in. If there is any interest, I may consider publishing this fork but only if there are some other people interested in helping maintain it.
I'm interested, but I'd like to see how you did it before committing my time. I've accepted at this point that I too have gone too far off the beaten path to ever cleanly merge back with the main branch, but I'd like us to not all reinvent the wheel when we have the same needs.
— You are receiving this because you commented. Reply to this email directly, view it on GitHub https://github.com/alyssaxuu/flowy/issues/50#issuecomment-811652855, or unsubscribe https://github.com/notifications/unsubscribe-auth/AAHHFGADARQ2IL6K5Y3XJDLTGQAQRANCNFSM4MKNZAAA .
@robhoward @pilootchoum
Probably a dumb question but how to get the id on the fly (not through output)? Is there an event to get the id when clicking an active block that I missed?
Or do I need to get it from the var doneTouch?
var doneTouch = function (event) {
if (event.type === "mouseup" && aclick && !noinfo) {
if (!rightcard && event.target.closest(".block") && !event.target.closest(".block").classList.contains("dragging")) {
tempblock = event.target.closest(".block");
// Look here
console.log(tempblock.getElementsByTagName('input')[1].value)
rightcard = true;
document.getElementById("properties").classList.add("expanded");
document.getElementById("propwrap").classList.add("itson");
tempblock.classList.add("selectedblock");
}
}
}
pilootchoum code works for me, thanks! Also, Rob, you seem to be missing the directChildrenCount(id) function.
This should work -- I've changed the code so much as this point from the original source that I'm not 100% sure though:
document.querySelector('.selectedblock').querySelector('.blockid').value
Hello, I've tried with the code from @robhoward, but I have an error at directChildrenCount(id). Can you help me with that?
All that function does is determine how many children the current block has.
Here is the function if it helps at all:
`
// ┌────────────────────────────────────────────────────────────────────
// │ Returns the count of children for the block id
// │
// │ @id - id of block to get children count
// └────────────────────────────────────────────────────────────────────
directChildrenCount: function(id) {
let children = 0;
// count how many blocks are a child
for (var i = 0; i < this.blocks.length; i++) {
if (this.blocks[i].parent === id)
children++
}
return children;
},`
Hello, can you please share the draw() code.
Would like to see an optional feature to disable block deletes if the drag/release action is minimal. I'm finding that when clicking a block I often move it by a few pixels and cause it to be deleted (Likely because my mouse sensitive is set to super high and I'm using a trackball =)). Would be nice to see some error tolerance here.