antd/lib/select#ModeOption TypeScript Examples

The following examples show how to use antd/lib/select#ModeOption. You can vote up the ones you like or vote down the ones you don't like, and go to the original project or source file by following the links above each example. You may check out the related API usage on the sidebar.
Example #1
Source File: CmdbInstanceSelect.tsx    From next-basics with GNU General Public License v3.0 4 votes vote down vote up
export function CmdbInstanceSelectItem(
  props: CmdbInstanceSelectProps,
  ref: any
): React.ReactElement {
  const {
    showKeyField,
    // 默认显示 label 为模型的 name/hostname, value 为 instanceId
    // 当showKeyField时,实例展示是用showKey里的数据展示
    fields = {
      label: [showKeyField ? "#showKey" : getInstanceNameKey(props.objectId)],
      value: "instanceId",
    },

    minimumInputLength = 0,
    extraSearchKey = [],
    extraFields = [],
    mode,
    placeholder,
    allowClear,
    pageSize,
    showSearchTip,
    permission,
    ignoreMissingFieldError,
  } = props;
  const userQuery = formatUserQuery(props.instanceQuery);
  //istanbul ignore else
  if (!fields.value) {
    fields.value = "instanceId";
  }

  const [value, setValue] = React.useState();
  const [options, setOptions] = React.useState<ComplexOption[]>([]);
  const [selectedOptions, setSelectedOptions] = React.useState<ComplexOption[]>(
    []
  );
  const [total, setTotal] = React.useState(0);
  const [loading, setLoading] = React.useState(false);
  const computeFields = () => {
    const result = [
      fields.value,
      ...(Array.isArray(fields.label) ? fields.label : [fields.label]),
      ...extraSearchKey,
      ...extraFields,
    ];

    if (props.objectId === "USER") {
      result.push("user_icon");
    }
    return result;
  };

  const handleChange = (newValue: any): void => {
    let selected: any | any[];
    if (mode === "multiple") {
      const valueSet = new Set(newValue);
      selected = options.filter((item) => valueSet.has(item.value));
      const oldValueSet = new Set(
        difference(
          newValue,
          selected.map((item) => item.value)
        )
      );
      selected = selected.concat(
        selectedOptions.filter((item) => oldValueSet.has(item.value))
      );
    } else {
      selected = options.find((item) => item.value === newValue);
    }
    setValue(newValue);
    setSelectedOptions(selected);
    props.onChange && props.onChange(newValue, selected);
  };
  //istanbul ignore else
  const handleSearch = async (
    q: string,
    extraQuery: any,
    forceSearch = false,
    pageSizeQuery?: number
  ): Promise<ComplexOption[]> => {
    if (forceSearch || q.length >= minimumInputLength) {
      try {
        let list = [];
        if (!props.objectId) {
          return;
        }
        setLoading(true);
        const fieldsQuery = Array.isArray(fields.label)
          ? fields.label.map((label) => ({ [label]: { $like: `%${q}%` } }))
          : [{ [fields.label]: { $like: `%${q}%` } }];
        const data = await InstanceApi_postSearchV3(props.objectId, {
          query: {
            $and: [
              {
                $or: [
                  ...fieldsQuery,
                  ...extraSearchKey.map((key) => ({
                    [key]: { $like: `%${q}%` },
                  })),
                ],
              },

              ...extraQuery,
            ],
          },
          ...(permission ? { permission } : {}),
          fields: computeFields(),
          page_size: pageSizeQuery || pageSize,
          ignore_missing_field_error: ignoreMissingFieldError,
        });

        list = data.list;
        setTotal(data.total);
        // 根据用户设置路径显示特定的 label 和 value
        const option = list.map((item) => ({
          ...item,
          label: Array.isArray(fields.label)
            ? fields.label.map((label) => get(item, label))
            : get(item, fields.label),
          value: get(item, fields.value),
          ...(props.objectId === "USER"
            ? {
                user_icon: get(item, "user_icon", "defaultIcon"),
              }
            : {}),
        }));
        setOptions(option);
        return option;
      } catch (e) {
        handleHttpError(e);
      } finally {
        setLoading(false);
      }
    }
  };
  const fetchInstanceData = async (): Promise<void> => {
    await handleSearch("", userQuery);
  };
  const getLabelOptions = (op: any) => {
    if (props.labelTemplate) {
      return parseTemplate(props.labelTemplate, op);
    } else {
      const label = op.label;
      if (Array.isArray(label)) {
        const firstKey = label[0];
        const resKey = label.slice(1, label.length).join(",");
        if (Array.isArray(firstKey) && props.showKeyField) {
          const subFirstKey = firstKey[0];
          const subResKey = firstKey.slice(1, firstKey.length).join(",");
          return subResKey && props.isMultiLabel
            ? `${subFirstKey}(${subResKey})`
            : subFirstKey ?? "";
        }
        return resKey && props.isMultiLabel
          ? `${firstKey}(${resKey})`
          : firstKey ?? "";
      } else {
        return label;
      }
    }
  };

  React.useEffect(() => {
    // 初始化时通过用户的 value 得出首次 label 的值
    // 由于value的不确定性,可能存在首次查询的值不唯一,初始化时也添加instanceQuery
    (async () => {
      if (!isEqual(props.value, value) && props.value !== undefined) {
        const option = await handleSearch(
          "",
          [
            {
              [fields.value || "instanceId"]: {
                $in: castArray(props.value),
              },
            },

            ...userQuery,
          ],
          true,
          props.value?.length >= pageSize ? props.value.length : pageSize
        );
        setSelectedOptions(option);
      }
      setValue(props.value);
    })();
  }, [props.value]);

  React.useEffect(() => {
    if (!props.firstRender) {
      const resetVal: [] | "" = mode === "multiple" ? [] : "";
      setValue(resetVal);
    }
  }, [props.objectId]);
  //istanbul ignore else
  const debounceSearch = debounce(
    (q: string) => handleSearch(q, userQuery),
    500
  );

  return (
    <Spin spinning={loading}>
      <Select
        ref={ref}
        allowClear={allowClear}
        style={defaults(props.inputBoxStyle, { width: "100%" })}
        showSearch
        filterOption={false}
        value={value}
        mode={mode as ModeOption}
        placeholder={
          placeholder || i18n.t(`${NS_FORMS}:${K.BACKGROUND_SEARCH}`)
        }
        onChange={handleChange}
        onSearch={debounceSearch}
        onFocus={fetchInstanceData}
        optionLabelProp="label"
        disabled={props.disabled}
        dropdownRender={(menu) => {
          return (
            <div>
              {menu}
              {showSearchTip && total > pageSize && (
                <div className={style.moreChoices}>
                  仅显示前{pageSize}项,更多结果请搜索
                </div>
              )}
            </div>
          );
        }}
        {...(props.popoverPositionType === "parent"
          ? { getPopupContainer: (triggerNode) => triggerNode.parentElement }
          : {})}
      >
        {options.map((op, index) => {
          const optionLabel = getLabelOptions(op);
          return (
            <Select.Option key={index} value={op.value} label={optionLabel}>
              {props.showTooltip ? (
                <Tooltip title={optionLabel}>
                  {op.user_icon && (
                    <Avatar
                      src={op.user_icon}
                      size={24}
                      className={classNames(style.avatar, {
                        [style.defaultIcon]: op.user_icon === "defaultIcon",
                      })}
                    >
                      {op.user_icon === "defaultIcon" && op.label?.slice(0, 2)}
                    </Avatar>
                  )}
                  {optionLabel}
                </Tooltip>
              ) : (
                <>
                  {op.user_icon && (
                    <Avatar
                      src={op.user_icon}
                      size={24}
                      className={classNames(style.avatar, {
                        [style.defaultIcon]: op.user_icon === "defaultIcon",
                      })}
                    >
                      {op.user_icon === "defaultIcon" && op.label?.slice(0, 2)}
                    </Avatar>
                  )}
                  {optionLabel}
                </>
              )}
            </Select.Option>
          );
        })}
      </Select>
    </Spin>
  );
}