Controlando a cor dos elementos

Para substituir as cores do modelo e das barras com base na configuração do gráfico de Gantt, precisamos de um método para calcular o status de uma tarefa, um campo no arquivo de configuração para definir as cores para cada status e uma função reagindo aos eventos acionados quando mudamos o gráfico. Para sobrepor as cores dos elementos vamos utilizar o método setThemingColor.

Primeiro precisamos incrementar o arquivo config.js adicionando os campos abaixo:

export const phasing_config = {
  "propFilter": "Type Name",
  "tasks": [],
  "requiredProps": {
    "id": "ID",
    "taskName": "NAME",
    "startDate": "START",
    "endDate": "END",
    "taskProgress": "PROGRESS",
    "dependencies": "DEPENDENCIES"
  },
  "objects": {},
  "mapTaksNProps": {},
  ========START OF THE  ADDITIONAL CONTENT========
  "statusColors": {
    "finished": "31,246,14",
    "inProgress": "235,246,14",
    "late": "246,55,14",
    "notYetStarted": "",
    "advanced": "14,28,246"
  }
  ========END OF THE  ADDITIONAL CONTENT========
}

Observe que isso definirá a cor de cada status, e os números definem o valor dos componentes vermelho, verde e azul da cor a ser usada. Agora precisamos da função para obter o status de cada tarefa. Vamos adicionar a função checkStatus dentro da classe PhasingPanel:

...
addPropToMap(filterValue, taskId) {
  phasing_config.mapTaksNProps[filterValue] = taskId;
}
========START OF THE  ADDITIONAL CONTENT========
checkTaskStatus(task) {
  let currentDate = new Date();

  let taskStart = new Date(task._start);
  let taskEnd = new Date(task._end);

  let shouldHaveStarted = currentDate > taskStart;
  let shouldHaveEnded = currentDate > taskEnd;

  let taskProgress = parseInt(task.progress, 10);

  //We need to map finished, in progress, late, not yet started or advanced
  //finished should have started and ended and actually ended (progress 100%)
  //in progress should have started, not ended and progress should be greater than 0
  //late should have started and ended but progress is less than 100, or should have started not ended and progress is 0
  //not yet started should not have started nor ended and progress is 0
  //advanced should not have started and have progress greater than 0 or should not have ended and progress is 100

  if (shouldHaveStarted && shouldHaveEnded && taskProgress === 100)
    return 'finished';

  else if (shouldHaveStarted && !shouldHaveEnded) {
    switch (taskProgress) {
      case 100:
        return 'advanced';
      case 0:
        return 'late';
      default:
        return 'inProgress';
    }
  }

  else if (shouldHaveStarted && shouldHaveEnded && taskProgress < 100)
    return 'late';

  else if (!shouldHaveStarted && !shouldHaveEnded && taskProgress === 0)
    return 'notYetStarted';

  else if (!shouldHaveStarted && taskProgress > 0)
    return 'advanced';
}
========END OF THE  ADDITIONAL CONTENT========

Agora só precisamos chamar esse método sempre que o gráfico de Gantt for alterado. Para isso, podemos aproveitar os eventos on_progress_change e on_date_change da nossa biblioteca, modificando a instanciação do gráfico de Gantt em PhasingPanel.js mais uma vez:

...
createGanttChart() {
  document.getElementById('phasing-container').innerHTML = `<svg id="phasing-container"></svg>`;

  let newGantt = new Gantt("#phasing-container", phasing_config.tasks, {
    on_click: this.barCLickEvent.bind(this),
    ========START OF THE  ADDITIONAL CONTENT========
    on_progress_change: this.handleColors.bind(this),
    on_date_change: this.handleColors.bind(this)
    ========END OF THE  ADDITIONAL CONTENT========
  });

  return newGantt;
}

========START OF THE  ADDITIONAL CONTENT========
handleColors() {
  this.handleElementsColor.call(this);
  this.handleBarsColor.call(this);
}

handleElementsColor() {
  const overrideCheckbox = document.getElementById('colormodel');
  if (overrideCheckbox.checked) {
    let tasksNStatusArray = this.gantt.tasks.map(this.checkTaskStatus);
    let mappeddbIds = [];
    for (let index = 0; index < this.gantt.tasks.length; index++) {
      const currentTaskId = this.gantt.tasks[index].id;
      const currentdbIds = phasing_config.objects[currentTaskId];
      const colorVector4 = this.fromRGB2Color(phasing_config.statusColors[tasksNStatusArray[index]]);
      currentdbIds.forEach(dbId => {
        if (colorVector4) {
          this.extension.viewer.setThemingColor(dbId, colorVector4)
        }
        else {
          this.extension.viewer.hide(dbId);
        }
      });
      mappeddbIds.push(...currentdbIds);
    }
    this.extension.viewer.isolate(mappeddbIds);
  }
  else {
    this.extension.viewer.clearThemingColors();
    this.extension.viewer.showAll();
  }
}

handleBarsColor() {
  this.gantt.bars.map(bar => {
    let tasksStatus = this.checkTaskStatus(bar.task);
    let barColor = phasing_config.statusColors[tasksStatus];
    bar.$bar.style = `fill: rgb(${barColor})`;
  })
}

fromRGB2Color(rgbString) {
  if (rgbString) {
    let colorsInt = rgbString.replaceAll(' ', '').split(',').map(colorString => parseInt(colorString, 10));
    return new THREE.Vector4(colorsInt[0] / 255, colorsInt[1] / 255, colorsInt[2] / 255, 0.5);
  }
  else {
    return null;
  }
}
========END OF THE  ADDITIONAL CONTENT========
...

Para gerenciar as cores das barras assim que tivermos o input carregado, precisamos incrementar a função update da classe PhasingPanel:

...
update(model, dbids) {
  if (phasing_config.tasks.length === 0) {
    this.inputCSV();
  }
  model.getBulkProperties(dbids, { propFilter: phasing_config.propFilter }, (results) => {
    results.map((result => {
      this.updateObjects(result);
    }))
  }, (err) => {
    console.error(err);
  });
  if (phasing_config.tasks.length > 0) {
    this.gantt = this.createGanttChart();
    ========START OF THE  ADDITIONAL CONTENT========
    this.handleColors.call(this);
    ========END OF THE  ADDITIONAL CONTENT========
  }
}
...

Agora só falta uma parte. Como vamos controlar a cor dos elementos com base no status?

Para isso, podemos adicionar uma caixa de seleção em nosso painel. Adicione o conteúdo abaixo dentro da função initialize da classe PhasingPanel:

...
initialize() {
  this.title = this.createTitleBar(this.titleLabel || this.container.id);
  this.title.style.overflow = 'auto';
  this.initializeMoveHandlers(this.title);
  this.container.appendChild(this.title);

  this.div = document.createElement('div');
  this.container.appendChild(this.div);

  ========START OF THE  ADDITIONAL CONTENT========

  //Here we create a switch to control vision of the schedule based on the GANTT chart
  this.checkbox = document.createElement('input');
  this.checkbox.type = 'checkbox';
  this.checkbox.id = 'colormodel';
  this.checkbox.style.width = (this.options.checkboxWidth || 30) + 'px';
  this.checkbox.style.height = (this.options.checkboxHeight || 28) + 'px';
  this.checkbox.style.margin = '0 0 0 ' + (this.options.margin || 5) + 'px';
  this.checkbox.style.verticalAlign = (this.options.verticalAlign || 'middle');
  this.checkbox.style.backgroundColor = (this.options.backgroundColor || 'white');
  this.checkbox.style.borderRadius = (this.options.borderRadius || 8) + 'px';
  this.checkbox.style.borderStyle = (this.options.borderStyle || 'groove');

  this.checkbox.onchange = this.handleColors.bind(this);
  this.div.appendChild(this.checkbox);

  this.label = document.createElement('label');
  this.label.for = 'colormodel';
  this.label.innerHTML = 'Show Phases';
  this.label.style.fontSize = (this.options.fontSize || 18) + 'px';
  this.label.style.verticalAlign = (this.options.verticalAlign || 'middle');
  this.div.appendChild(this.label);

  ========END OF THE  ADDITIONAL CONTENT========

  //Here we add the svg for the GANTT chart
  this.content = document.createElement('div');
  this.content.style.backgroundColor = (this.options.backgroundColor || 'white');
  this.content.innerHTML = `<svg id="phasing-container"></svg>`;
  this.container.appendChild(this.content);

  this.updateTasks();
}
...

Com tudo definido, você deve conseguir ver o modelo colorido, como no gif abaixo:

Second Step Result

Próxima etapa - Otimizando a experiência