I am stuck with a functionality that i have already done in python long time ago.
I draw a gantt chart in a specific way that i can't reproduce with elm.
here is my code : https://ellie-app.com/8sYLsxTZHk5a1
The problem is in the "calcTaskPosition" function where i try to set the row of the task.
calcTaskPosition : Int -> Task -> List(Task)
calcTaskPosition row task =
let
precs = List.concatMap (calcTaskPosition (row+1)) (taskPrecs task)
in
{ task | col = (Maybe.withDefault -1 <|
List.maximum <|
List.map (\t -> t.col) precs) + 1
--, row = row
}
:: precs
In my example, tasks lines are set by the initTask function. I wish to get the same task order whithout having to set explicit line position in the initTask function.
The first clue is when you look at svg you will notice that your "task1" is actually rendered twice. This is easier to see if you uncomment the line --, row = row
in the snippet you posted.
In elm (and other functional languages) your tasks will not be manipulated in-place, but instead your tasks will be copied when you mutate them. So it is not really useful to keep col and row values in the model (for now).
Also, working with task ids makes more sense than directly linking objects.
With this in mind, I would create two different task records: One for keeping it in the model (Task
in my example) and one for rendering it (I called it DrawableTask
).
And then you need a transformation function like
toDrawableListOfTasks : List Task -> List DrawableTask
that will be called in the view.
The transformation function essentially uses your tasksNotInTaskPrecs
where you select all tasks that can be immediately drawn (because their precs list is empty). I generalized it and called it allDependenciesMet
instead and use it on every iteration to select the tasks that can be drawn.
All tasks that can be drawn will be added to a temporary list (in my case a dictionary for fast look-up of already entered tasks) and then the next iteration starts with all tasks that were not yet drawn. When no tasks are left, you can return the list and the rendering pass will traverse the list once again.
order : Temp -> List Task -> List DrawableTask
order temp todo =
case List.partition (allDependenciesMet temp) todo of
( [], [] ) ->
-- We are done and can return the list
Dict.values temp
|> List.sortBy .row
( [], _ ) ->
Debug.todo "An invalid list of tasks was passed"
( drawableTasks, nextTodo ) ->
let
nextTemp =
List.indexedMap (toDrawable temp) drawableTasks
|> List.map (\t -> ( t.id, t ))
|> Dict.fromList
|> Dict.union temp
in
order nextTemp nextTodo
I'm not sure if this is understandable, but you should be able to follow https://ellie-app.com/8tdzrgLfBfya1