public render()

in src/app/views/panels/scales_panel.tsx [74:332]


  public render(): any {
    const store = this.props.store;
    let scales = store.chart.scales;

    const filterElementByScalePredicate = (scaleID: string) => (
      element: any
    ) => {
      return (
        Object.keys(element.mappings).find((key) => {
          const mapping = element.mappings[key];
          return (
            (mapping.type === MappingType.scale ||
              mapping.type === MappingType.expressionScale) &&
            (mapping as ScaleMapping).scale === scaleID
          );
        }) != undefined
      );
    };

    const filterElementProperties = (scaleID: string, element: any) => {
      return Object.keys(element.mappings).filter((key) => {
        const mapping = element.mappings[key];
        return (
          (mapping.type === MappingType.scale ||
            mapping.type === MappingType.expressionScale) &&
          (mapping as ScaleMapping).scale === scaleID
        );
      });
    };

    // eslint-disable-next-line
    const mapToUI = (scale: Scale<ObjectProperties>) => (
      glyph: Glyph,
      element: ChartElement<ObjectProperties>
      // eslint-disable-next-line
    ) => (key: string) => {
      if (!element) {
        return (
          <div key={scale._id} className="el-object-item">
            <SVGImageIcon
              url={R.getSVGIcon(
                Prototypes.ObjectClasses.GetMetadata(scale.classID).iconPath
              )}
            />
            <span className="el-text">{scale.properties.name}</span>
          </div>
        );
      } else {
        const expr = (element.mappings[key] as any).expression;
        let rawColumnExpr: string = null; // TODO handle
        return (
          <div
            className="el-object-item el-object-scale-attribute"
            key={scale._id + "_" + element._id + "_" + key}
            onClick={() => {
              if (glyph) {
                this.dispatch(new Actions.SelectMark(null, glyph, element));
              } else {
                this.dispatch(new Actions.SelectChartElement(element));
              }
              this.dispatch(new Actions.FocusToMarkAttribute(key));
            }}
          >
            <DraggableElement
              key={key}
              className={classNames("charticulator__scale-panel-property", [
                "is-active",
                this.state.isSelected === expr,
              ])}
              onDragStart={() => this.setState({ isSelected: expr })}
              onDragEnd={() => this.setState({ isSelected: null })}
              dragData={() => {
                const type = (element.mappings[key] as any).valueType;
                const scaleID = (element.mappings[key] as any).scale;
                const allowSelectValue = (element.mappings[key] as any)
                  .allowSelectValue;
                const aggregation = Expression.getDefaultAggregationFunction(
                  type,
                  null
                );
                const applyAggregation = (expr: string) => {
                  return Expression.functionCall(
                    aggregation,
                    Expression.parse(expr)
                  ).toString();
                };
                const table = this.store.dataset.tables.find(
                  (table) => table.name === (element.mappings[key] as any).table
                );
                const parsedExpression = Expression.parse(expr);
                let metadata: ColumnMetadata = {};
                if (
                  parsedExpression instanceof FunctionCall &&
                  parsedExpression.args[0] instanceof Variable
                ) {
                  const firstArgument = parsedExpression.args[0] as Variable;
                  const column = table.columns.find(
                    (col) => col.name === firstArgument.name
                  );
                  metadata = column.metadata;
                  rawColumnExpr =
                    metadata.rawColumnName &&
                    applyAggregation(metadata.rawColumnName);
                }
                this.setState({ isSelected: expr });
                const r = new DragData.DataExpression(
                  table,
                  expr,
                  type,
                  metadata,
                  rawColumnExpr,
                  scaleID,
                  allowSelectValue
                );
                return r;
              }}
              renderDragElement={() => [
                <span className="dragging-table-cell">
                  {(element.mappings[key] as any).expression}
                </span>,
                { x: -10, y: -8 },
              ]}
            >
              <SVGImageIcon
                url={R.getSVGIcon(
                  Prototypes.ObjectClasses.GetMetadata(element.classID).iconPath
                )}
              />
              <span className="el-text">{`${
                element.properties.name
              }.${this.getPropertyDisplayName(key)}`}</span>
            </DraggableElement>
          </div>
        );
      }
    };
    scales = scales.sort(
      (a: Scale<ObjectProperties>, b: Scale<ObjectProperties>) => {
        if (a.properties.name < b.properties.name) {
          return -1;
        }
        if (a.properties.name > b.properties.name) {
          return 1;
        }
        return 0;
      }
    );
    // Collect all used scales and object with properties into one list
    const propertyList = scales.flatMap((scale) => {
      return [0]
        .map(() => {
          return {
            scale,
            mark: null as ChartElement<ObjectProperties>,
            property: null as string,
            glyph: null as Glyph,
          };
        })
        .concat(
          // take all chart elements
          store.chart.elements
            // filter elements by scale
            .filter(filterElementByScalePredicate(scale._id))
            .flatMap((mark: ChartElement<ObjectProperties>) => {
              // Take all properties of object/element where scale was used and map them into {property, element, scale} object/element
              return filterElementProperties(scale._id, mark).map(
                (property) => {
                  return {
                    property,
                    mark,
                    scale,
                    glyph: null,
                  };
                }
              );
            })
        )
        .concat(
          store.chart.glyphs
            // map all glyphs into {glyph & marks} group
            .flatMap((glyph: Glyph): {
              glyph: Glyph;
              mark: Element<ObjectProperties>;
            }[] =>
              glyph.marks.map((mark) => {
                return {
                  glyph,
                  mark,
                };
              })
            )
            // filter elements by scale
            .filter(
              ({ mark }: { glyph: Glyph; mark: Element<ObjectProperties> }) =>
                filterElementByScalePredicate(scale._id)(mark)
            )
            // Take all properties of object/element where scale was used and map them into {property, element, scale} object/element
            .flatMap(
              ({
                mark,
                glyph,
              }: {
                glyph: Glyph;
                mark: Element<ObjectProperties>;
              }) => {
                return filterElementProperties(scale._id, mark).map(
                  (property) => {
                    return {
                      property,
                      mark,
                      scale,
                      glyph,
                    };
                  }
                );
              }
            )
        );
    });

    return (
      <div className="charticulator__object-list-editor charticulator__object-scales">
        <ReorderListView
          restrict={true}
          enabled={true}
          onReorder={(IndexA, IndexB) => {
            // Drag properties item only
            if (!propertyList[IndexA].property || IndexA === IndexB) {
              return;
            }

            // Find next scale in the list
            if (IndexB > 0) {
              IndexB--;
            }
            while (
              IndexB > 0 &&
              !propertyList[IndexB] &&
              propertyList[IndexB].property != null
            ) {
              IndexB--;
            }

            store.dispatcher.dispatch(
              new Actions.SetObjectMappingScale(
                propertyList[IndexA].mark,
                propertyList[IndexA].property,
                propertyList[IndexB].scale._id
              )
            );
          }}
        >
          {propertyList.map((el) => {
            return mapToUI(el.scale)(el.glyph, el.mark)(el.property);
          })}
        </ReorderListView>
      </div>
    );
  }