function renderOptionsPropInput()

in web-console/src/views/explore-view/components/control-pane/control-pane.tsx [81:348]


  function renderOptionsPropInput(
    parameter: ParameterDefinition,
    value: any,
    onValueChange: (value: any) => void,
  ): {
    element: JSX.Element;
    onDropColumn?: (column: Column) => void;
    onDropMeasure?: (measure: Measure) => void;
  } {
    const required = evaluateFunctor(parameter.required, parameterValues, querySource, where);
    switch (parameter.type) {
      case 'boolean': {
        return {
          element: (
            <SegmentedControl
              value={String(value)}
              onValueChange={v => {
                onValueChange(v === 'true');
              }}
              options={[
                { value: 'false', label: 'False' },
                { value: 'true', label: 'True' },
              ]}
              small
            />
          ),
        };
      }

      case 'number':
        return {
          element: (
            <FancyNumericInput
              value={value}
              onValueChange={onValueChange}
              placeholder={parameter.placeholder}
              fill
              min={parameter.min}
              max={parameter.max}
            />
          ),
        };

      case 'string':
        return {
          element: (
            <InputGroup
              value={(value as string) || ''}
              onChange={e => onValueChange(e.target.value)}
              placeholder={parameter.placeholder}
              fill
            />
          ),
        };

      case 'option': {
        const controlOptions =
          evaluateFunctor(parameter.options, parameterValues, querySource, where) || [];
        const selectedOption: OptionValue | undefined = controlOptions.find(o => o === value);
        return {
          element: (
            <Popover
              fill
              position="bottom-left"
              minimal
              content={
                <Menu>
                  {controlOptions.map((o, i) => (
                    <MenuItem
                      key={i}
                      text={getModuleOptionLabel(o, parameter)}
                      labelElement={
                        o === selectedOption ? <Icon icon={IconNames.TICK} /> : undefined
                      }
                      onClick={() => onValueChange(o)}
                    />
                  ))}
                </Menu>
              }
            >
              <InputGroup
                value={
                  typeof selectedOption === 'undefined'
                    ? `Unknown option: ${value}`
                    : getModuleOptionLabel(selectedOption, parameter)
                }
                readOnly
                fill
                rightElement={<Button icon={IconNames.CARET_DOWN} minimal />}
              />
            </Popover>
          ),
        };
      }

      case 'options': {
        const controlOptions =
          evaluateFunctor(parameter.options, parameterValues, querySource, where) || [];
        return {
          element: (
            <OptionsInput
              options={controlOptions}
              value={(value as OptionValue[]) || []}
              onValueChange={onValueChange}
              optionLabel={o => getModuleOptionLabel(o, parameter)}
              allowDuplicates={parameter.allowDuplicates}
              nonEmpty={parameter.nonEmpty}
            />
          ),
        };
      }

      case 'expression': {
        const handleSelectColumn = (c: Column) => {
          onValueChange(ExpressionMeta.fromColumn(c));
        };
        return {
          element: (
            <NamedExpressionsInput<ExpressionMeta>
              allowReordering
              values={value ? [value] : []}
              onValuesChange={vs => onValueChange(vs[0])}
              singleton
              nonEmpty={required}
              itemMenu={(initExpression, onClose) => (
                <ExpressionMenu
                  columns={columns}
                  initExpression={initExpression}
                  onSelectExpression={onValueChange}
                  onClose={onClose}
                  onAddToSourceQueryAsColumn={onAddToSourceQueryAsColumn}
                />
              )}
            />
          ),
          onDropColumn: handleSelectColumn,
        };
      }

      case 'expressions': {
        const disabledColumnNames = parameter.allowDuplicates
          ? []
          : filterMap(value as ExpressionMeta[], ({ expression }) =>
              expression instanceof SqlColumn ? expression.getName() : undefined,
            );

        return {
          element: (
            <NamedExpressionsInput<ExpressionMeta>
              allowReordering
              values={value as ExpressionMeta[]}
              onValuesChange={onValueChange}
              nonEmpty={parameter.nonEmpty}
              itemMenu={(initExpression, onClose) => (
                <ExpressionMenu
                  columns={columns}
                  initExpression={initExpression}
                  onSelectExpression={c => onValueChange(changeOrAdd(value, initExpression, c))}
                  disabledColumnNames={disabledColumnNames}
                  onClose={onClose}
                  onAddToSourceQueryAsColumn={onAddToSourceQueryAsColumn}
                />
              )}
            />
          ),
          onDropColumn: (column: Column) => {
            const columnName = column.name;
            if (
              !parameter.allowDuplicates &&
              value.find((v: ExpressionMeta) => v.name === columnName)
            ) {
              AppToaster.show({
                intent: Intent.WARNING,
                message: (
                  <>
                    <Tag minimal>{columnName}</Tag> already selected
                  </>
                ),
              });
              return;
            }
            onValueChange(value.concat(ExpressionMeta.fromColumn(column)));
          },
        };
      }

      case 'measure': {
        return {
          element: (
            <NamedExpressionsInput<Measure>
              values={value ? [value] : []}
              onValuesChange={vs => onValueChange(vs[0])}
              singleton
              nonEmpty={required}
              itemMenu={(initMeasure, onClose) => (
                <MeasureMenu
                  columns={columns}
                  measures={measures}
                  initMeasure={initMeasure}
                  onSelectMeasure={onValueChange}
                  onClose={onClose}
                  onAddToSourceQueryAsMeasure={onAddToSourceQueryAsMeasure}
                />
              )}
            />
          ),
          onDropColumn: column => {
            const candidateMeasures = Measure.getPossibleMeasuresForColumn(column).filter(
              p => !value || value.name !== p.name,
            );
            if (!candidateMeasures.length) return;
            onValueChange(candidateMeasures[0]);
          },
          onDropMeasure: onValueChange,
        };
      }

      case 'measures': {
        const disabledMeasureNames = parameter.allowDuplicates
          ? []
          : filterMap(value as Measure[], measure => measure.getAggregateMeasureName());

        return {
          element: (
            <NamedExpressionsInput<Measure>
              values={value}
              onValuesChange={onValueChange}
              allowReordering
              nonEmpty={parameter.nonEmpty}
              itemMenu={(initMeasure, onClose) => (
                <MeasureMenu
                  columns={columns}
                  measures={measures}
                  initMeasure={initMeasure}
                  disabledMeasureNames={disabledMeasureNames}
                  onSelectMeasure={m => onValueChange(changeOrAdd(value, initMeasure, m))}
                  onClose={onClose}
                  onAddToSourceQueryAsMeasure={onAddToSourceQueryAsMeasure}
                />
              )}
            />
          ),
          onDropColumn: column => {
            const candidateMeasures = Measure.getPossibleMeasuresForColumn(column).filter(
              p => !value.some((v: Measure) => v.name === p.name),
            );
            if (!candidateMeasures.length) return;
            onValueChange(value.concat(candidateMeasures[0]));
          },
          onDropMeasure: measure => {
            onValueChange(value.concat(measure));
          },
        };
      }

      default:
        return {
          element: (
            <Button
              icon={IconNames.ERROR}
              text={`Type not supported: ${(parameter as { type: string }).type}`}
              disabled
              fill
            />
          ),
        };
    }
  }