I am making an dahboard creator tool. Dashboard is set of rows, each row have boxes. So I trying to make list of lists of items. Items can move between lists. Lists can be sorted between each other. Everything is saved in one object, which can be easily exported in JSON format. Cool.
_________ ______________
| A B | move | A B C C |
|-------| C up |------------|
| C | ===> | |
|_______| |____________|
If I move box C to first row and call redraw, the C box duplicate self. One C is original box from the second row and second C box is created by mithril from model.
The point is I want just move the the box between rows, not to copy it.
I tried play with onbeforeupdate and m.redraw(), but it is not solution, because I need to working redraw cycle.
class Grid {
private boxMap = new Map<TDashboardBox, IEditor>();
constructor(
public readonly dashboard: DashboardDAO,
private readonly editorFactory: IBoxFactory
) {
for (const row of dashboard.dashboard.config) {
for (const box of row) {
this.boxMap.set(box, editorFactory.create(box.title, box))
}
}
}
public boxView(box: TDashboardBox): Vnode {
return m(this.boxMap.get(box) || null);
}
public rowView(row: TDashboardRow): Vnode[] {
return row.map(b => m('.Box', {}, this.boxView(b)));
}
public view(): Vnode {
return m('.Grid', {},
this.dashboard.dashboard.config.map(r => m('.Row', {}, this.rowView(r)))
);
}
public oncreate(vnode: VnodeDOM): void {
Sortable.create(<HTMLElement>vnode.dom, {
group: "rows",
animation: 150,
onEnd: (e: SortableEvent) => {
this.dashboard.swapRows(e.oldIndex, e.newIndex);
},
});
const rows = vnode.dom.getElementsByClassName('Row');
for (let i = 0; i < rows.length; i++)
{
const row = <HTMLElement>rows[i];
Sortable.create(row, {
group: "boxes",
animation: 150,
onEnd: (e: SortableEvent) => {
const children = e.from.parentElement.childNodes;
// Search row indexes of "from row" and "to row"
let fromIndex, targetIndex;
for (let j = 0; j < children.length; j++) {
if (children[j] === e.from) {
fromIndex = j;
}
if (children[j] === e.to) {
targetIndex = j;
}
if (fromIndex !== undefined && targetIndex !== undefined) {
break;
}
}
this.dashboard.moveBox(fromIndex, e.oldDraggableIndex, targetIndex, e.newDraggableIndex);
m.redraw();
},
});
}
}
}
EDIT 1
Copied box can be removed in onEnd() callback. It solve the copy problem.
If I swap boxes on one row, they are swapped in model. But on screen after redraw they are swapped back.
EDIT 2
I found solution some solution. It almost there.
// End of onEnd() event
// if boxes moved, then...
m.render(vnode.dom, m(this)); // redraw Grid scratch
I just throw away whole html of Grid and let mithril rebuild it.
But now is in Grid just HTML without events, without redraw cycle.
Ok, I figured it out. It is not "best in class" solution, but it works well.
I create callback function passed to Grid component from its parent component. After boxes are moved, that callback fucntion simply destroy Grid and create new one.
parent component
Grid constructor