public buildGridTableData()

in frontend/apps/quantgrid/src/app/context/ViewportContext/GridBuilder.ts [387:723]


  public buildGridTableData(tableData: TableData, zIndex: number): GridData {
    const data: GridData = {};
    const { table, diff } = tableData;
    const { tableName } = table;
    const isTableHorizontal = table.getIsTableDirectionHorizontal();
    const tableDimensions = this.getTableDimensions(
      tableName,
      table.getIsTableDirectionHorizontal()
    );
    const { startCol: tableStartCol, startRow: tableStartRow } =
      tableDimensions;
    const totalSize = table.getTotalSize();
    const allDataMainDirectionStart = isTableHorizontal
      ? tableStartCol + table.getTableFieldsHeaderHeight() + totalSize
      : tableStartRow +
        table.getTableNameHeaderHeight() +
        table.getTableFieldsHeaderHeight() +
        totalSize;
    const allDataSecondaryDirectionStart = isTableHorizontal
      ? tableStartRow + table.getTableNameHeaderHeight()
      : tableStartCol;

    const buildChunk = (chunk: ColumnChunk, chunkIndex: number) => {
      let minDirectionIndex = Number.MAX_SAFE_INTEGER;
      let maxDirectionIndex = Number.MIN_SAFE_INTEGER;
      const cachedOverrideValues: Record<number, CachedOverrideRow> = {};

      // Add cells that have data in chunks
      for (const chunkKey of Object.keys(chunk)) {
        if (chunkKey === dynamicFieldName) {
          continue;
        }

        const field = table.fields.find((f) => f.key.fieldName === chunkKey);
        const fieldIndex = table.fields
          .filter((f) => f.key.fieldName !== dynamicFieldName)
          .findIndex((f) => f.key.fieldName === chunkKey);
        const dataFieldSecondaryDirectionStart = table.fields
          .filter((f) => f.key.fieldName !== dynamicFieldName)
          .map((field) => (isTableHorizontal ? 1 : field.getSize()))
          .slice(0, fieldIndex)
          .reduce((acc, curr) => acc + curr, allDataSecondaryDirectionStart);

        const chunkData = chunk[chunkKey];

        if (fieldIndex === -1 || !field) continue;

        const accChunkOffset = chunkIndex * chunkSize;

        for (
          let innerChunkDataIndex = 0;
          innerChunkDataIndex < chunkData.length;
          innerChunkDataIndex++
        ) {
          const resultedChunkDataIndex = innerChunkDataIndex + accChunkOffset;
          const dataDirectionIndex =
            innerChunkDataIndex + accChunkOffset + allDataMainDirectionStart;

          minDirectionIndex = Math.min(minDirectionIndex, dataDirectionIndex);
          maxDirectionIndex = Math.max(maxDirectionIndex, dataDirectionIndex);

          const tableDirectionData: Record<string, string> = {};

          Object.keys(chunk).forEach((fieldName) => {
            tableDirectionData[fieldName] =
              chunk[fieldName][innerChunkDataIndex];
          });

          const referenceTableName =
            tableData.columnReferenceTableNames[chunkKey];
          const type = tableData.types[chunkKey];
          const fieldName = chunkKey;
          const expression = field?.expressionMetadata?.text || '';
          const isPeriodSeries = type === ColumnDataType.PERIOD_SERIES;
          const isNested = tableData.nestedColumnNames.has(chunkKey);
          const isKey = field?.isKey;
          const isDim = field?.isDim;
          const isDynamic = field?.isDynamic;
          const note = field?.note?.text || '';
          const { sort, isFiltered, numericFilter, isFieldUsedInSort } =
            this.getApplyBlockGridParams(tableData, table, chunkKey);
          const fieldSize = isTableHorizontal ? 1 : field.getSize();
          const viewportErrorMessage = tableData?.fieldErrors[fieldName];
          const fieldErrorMessage = getFieldErrors(
            this.viewGridData.getParsingErrors(),
            this.viewGridData.getCompilationErrors(),
            viewportErrorMessage,
            tableName,
            fieldName
          );
          const isRightAligned = this.isValueRightAligned(isNested, type);

          let overrideValue: OverrideValue = null;
          let overrideIndex = null;

          const {
            overrideRow,
            overrideIndex: index,
            overrideSectionIndex,
          } = getOverrideRow(
            table,
            chunkKey,
            resultedChunkDataIndex,
            tableDirectionData,
            cachedOverrideValues
          );

          let isOverrideChanged = false;
          if (overrideRow) {
            overrideValue = overrideRow[fieldName];
            overrideIndex = index;
            isOverrideChanged =
              !!overrideValue &&
              !!diff?.overrides?.some(
                (diffOverrideRow) =>
                  overrideValue && diffOverrideRow[fieldName] === overrideValue
              );
          }

          const overrideErrorMessage =
            overrideSectionIndex !== null &&
            overrideSectionIndex !== undefined &&
            getOverrideErrors(
              this.viewGridData.getParsingErrors(),
              this.viewGridData.getCompilationErrors(),
              tableName,
              fieldName,
              overrideSectionIndex + 1
            );

          const dataIndex = resultedChunkDataIndex;

          for (
            let fieldInnerIndex = 0;
            fieldInnerIndex < fieldSize;
            fieldInnerIndex++
          ) {
            const col = isTableHorizontal
              ? dataDirectionIndex
              : dataFieldSecondaryDirectionStart + fieldInnerIndex;
            const row = isTableHorizontal
              ? dataFieldSecondaryDirectionStart
              : dataDirectionIndex;
            const value = chunkData[innerChunkDataIndex];
            const fieldStartCol = isTableHorizontal
              ? col
              : dataFieldSecondaryDirectionStart;
            const fieldEndCol = isTableHorizontal
              ? col
              : dataFieldSecondaryDirectionStart + fieldSize - 1;
            const finalValue =
              fieldStartCol === col ||
              [ColumnDataType.DOUBLE, ColumnDataType.INTEGER].includes(type)
                ? value
                : '';

            GridBuilder.setSafeDataCell(data, row, col, {
              table: {
                ...tableDimensions,
                tableName,
                isTableNameHeaderHidden: table.getIsTableHeaderHidden(),
                isTableFieldsHeaderHidden: table.getIsTableFieldsHidden(),
                hasKeys: table.hasKeys(),
                isTableHorizontal,
                totalSize,
                isManual: table.isManual(),
                isNewAdded: !!diff?.table,
              },

              field: {
                fieldName,
                note,
                expression,
                isPeriodSeries,
                isNested,
                isKey,
                isDim,
                isDynamic,
                isFiltered,
                numericFilter,
                type,
                referenceTableName,
                sort,
                isFieldUsedInSort,
                hasError: !!fieldErrorMessage,
                errorMessage: fieldErrorMessage,
                isChanged: !!diff?.fields.includes(fieldName),
              },
              isOverride: !!overrideValue,
              isOverrideChanged,
              overrideValue: overrideValue,
              overrideIndex: overrideIndex !== null ? overrideIndex : undefined,
              value: finalValue,
              isUrl: isValidUrl(finalValue),
              row,
              col,
              dataIndex,
              isRightAligned,
              startCol: fieldStartCol,
              endCol: fieldEndCol,
              zIndex,
              isFieldHeader: false,
              isTableHeader: false,
              hasError: !!overrideErrorMessage,
              errorMessage: overrideErrorMessage || undefined,
            });
          }
        }
      }

      // TODO: Possible redraw problem
      // Add cells that have no data in chunks
      const noDataFieldNames = table.fields.filter(
        (f) => !chunk[f.key.fieldName] && f.key.fieldName !== dynamicFieldName
      );

      for (const field of noDataFieldNames) {
        const dataName = field.key.fieldName;
        const noDynamicFields = table.fields.filter(
          (f) => f.key.fieldName !== dynamicFieldName
        );
        const fieldIndex = noDynamicFields.findIndex(
          (f) => f.key.fieldName === dataName
        );
        const dataFieldSecondaryDirectionStart = noDynamicFields
          .filter((f) => f.key.fieldName !== dynamicFieldName)
          .map((field) => (isTableHorizontal ? 1 : field.getSize()))
          .slice(0, fieldIndex)
          .reduce((acc, curr) => acc + curr, allDataSecondaryDirectionStart);
        const type = tableData.types[dataName];
        const { sort, isFiltered, numericFilter, isFieldUsedInSort } =
          this.getApplyBlockGridParams(tableData, table, dataName);
        const fieldSize = isTableHorizontal ? 1 : field.getSize();
        const viewportErrorMessage = tableData?.fieldErrors[dataName];
        const fieldErrorMessage = getFieldErrors(
          this.viewGridData.getParsingErrors(),
          this.viewGridData.getCompilationErrors(),
          viewportErrorMessage,
          tableName,
          dataName
        );

        const referenceTableName =
          tableData.columnReferenceTableNames[dataName];

        for (
          let directionIndex = minDirectionIndex;
          directionIndex <= maxDirectionIndex;
          directionIndex++
        ) {
          for (
            let fieldInnerIndex = 0;
            fieldInnerIndex < field.getSize();
            fieldInnerIndex++
          ) {
            const col = isTableHorizontal
              ? directionIndex
              : dataFieldSecondaryDirectionStart + fieldInnerIndex;
            const row = isTableHorizontal
              ? dataFieldSecondaryDirectionStart
              : directionIndex;
            const fieldStartCol = isTableHorizontal
              ? col
              : dataFieldSecondaryDirectionStart;
            const fieldEndCol = isTableHorizontal
              ? col
              : dataFieldSecondaryDirectionStart + fieldSize - 1;

            GridBuilder.setSafeDataCell(data, row, col, {
              table: {
                ...tableDimensions,
                tableName,
                isTableNameHeaderHidden: table.getIsTableHeaderHidden(),
                isTableFieldsHeaderHidden: table.getIsTableFieldsHidden(),
                hasKeys: table.hasKeys(),
                isTableHorizontal,
                totalSize,
                isManual: table.isManual(),
                isNewAdded: !!diff?.table,
              },
              isOverride: false,
              field: {
                fieldName: dataName,
                note: field?.note?.text || '',
                expression: field?.expressionMetadata?.text || '',
                isPeriodSeries: type === ColumnDataType.PERIOD_SERIES,
                isNested: tableData.nestedColumnNames.has(dataName),
                isKey: field?.isKey,
                isDim: field?.isDim,
                isDynamic: field?.isDynamic,
                hasError: !!fieldErrorMessage,
                errorMessage: fieldErrorMessage,
                isFiltered,
                numericFilter,
                isFieldUsedInSort,
                sort,
                type,
                referenceTableName,
                isChanged: !!diff?.fields.includes(dataName),
              },
              overrideIndex: undefined,
              value: undefined,
              row,
              col,
              dataIndex: directionIndex - minDirectionIndex,
              startCol: fieldStartCol,
              endCol: fieldEndCol,
              zIndex,
              isFieldHeader: false,
              isTableHeader: false,
            });
          }
        }
      }
    };

    const existingIndexes = [
      ...Object.keys(tableData.fallbackChunks),
      ...Object.keys(tableData.chunks),
    ];

    const indexes = Array.from(new Set(existingIndexes));

    for (const index of indexes) {
      const chunkIndex = +index;

      buildChunk(
        {
          ...tableData.chunks[chunkIndex],
          ...tableData.fallbackChunks[chunkIndex],
        },
        chunkIndex
      );
    }

    return data;
  }