antd#AutoComplete TypeScript Examples

The following examples show how to use antd#AutoComplete. 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: Email.tsx    From gant-design with MIT License 6 votes vote down vote up
renderSelect = () => {
    const { dropdownClassName, className, list, wrapperRef, ...props } = this.props;
    return (
      <AutoComplete
        className={classnames('gant-select-email', className)}
        showSearch
        dropdownMatchSelectWidth={false}
        dropdownClassName={classnames('gant-select-email-dropdown', dropdownClassName)}
        {...props}
        onSearch={this.onSearch}
        ref={wrapperRef}

      >
        {this.getDataSource()}
      </AutoComplete>
    )
  }
Example #2
Source File: index.tsx    From drip-table with MIT License 6 votes vote down vote up
public render() {
    const config = this.props.schema;
    const uiProps = this.props.schema['ui:props'] || {};

    return (
      <AutoComplete
        value={this.props.value as string}
        placeholder={uiProps.placeholder as string}
        disabled={uiProps.disabled as boolean}
        style={{ width: 240, ...uiProps.style }}
        options={this.options as LabeledValue}
        onChange={(value) => {
          const formattedValue = this.transform(value);
          this.props.onChange?.(formattedValue);
          if (config.validate) {
            const res = config.validate(formattedValue);
            (res instanceof Promise ? res : Promise.resolve(res))
              .then((message) => {
                this.props.onValidate?.(message);
                return message;
              })
              .catch((error) => { throw error; });
          }
        }}
      />
    );
  }
Example #3
Source File: public.tsx    From jetlinks-ui-antd with MIT License 6 votes vote down vote up
export function renderUnit(units: any): ReactNode {
  const filterOption = (inputValue: string, option: any) => {
    return option.key.indexOf(inputValue) != -1;
  };
  const grouped = groupBy(units, unit => unit.type);
  const types = Array.from(new Set<string>(units.map((unit: any) => {
    return unit.type;
  })));
  const options = types.map(type => {
    const typeData = grouped[type];
    return (
      <Select.OptGroup label={type} key={type}>
        {
          typeData.map((e: Unit) => {
            return <Select.Option value={e.id} key={`${e.name}/${e.symbol}`}
                                  title={`${e.name}/${e.symbol}`}>{e.name} / {e.symbol}</Select.Option>
          })
        }
      </Select.OptGroup>
    );
  });

  return (
    <AutoComplete
      dropdownMatchSelectWidth={false}
      dropdownStyle={{width: 300}}
      style={{width: '100%'}}
      dataSource={options}
      optionLabelProp="title"
      filterOption={(inputValue, option) => {
        return filterOption(inputValue, option);
      }}
    >
      <Input/>
    </AutoComplete>
  );
}
Example #4
Source File: utils.tsx    From antdp with MIT License 6 votes vote down vote up
getItem = ({ attr, type, inputNode }: {
  attr?: Partial<ItemChildAttr<any, any>>;
  type?: ItemChildType;
  inputNode?: ((...arg: any[]) => React.ReactNode) | React.ReactNode;
}) => {
  let renderItem = undefined;
  if (type === 'Input') {
    const inputAttr = attr as InputProps;
    renderItem = <Input {...inputAttr} />;
  } else if (type === 'TextArea') {
    const inputAttr = attr as TextAreaProps;
    renderItem = <Input.TextArea {...inputAttr} />;
  } else if (type === 'InputNumber') {
    const inputAttr = attr as InputNumberProps;
    renderItem = <InputNumber {...inputAttr} />;
  } else if (type === 'AutoComplete') {
    const inputAttr = attr as AutoCompleteProps;
    renderItem = <AutoComplete {...inputAttr} />;
  } else if (type === 'Cascader') {
    const inputAttr = attr as CascaderProps;
    renderItem = <Cascader {...inputAttr} />;
  } else if (type === 'DatePicker') {
    const inputAttr = attr as DatePickerProps;
    renderItem = <DatePicker {...inputAttr} />;
  } else if (type === 'Rate') {
    const inputAttr = attr as RateProps;
    renderItem = <Rate {...inputAttr} />;
  } else if (type === 'Slider') {
    const inputAttr = attr as SliderSingleProps;
    renderItem = <Slider {...inputAttr} />;
  } else if (type === 'TreeSelect') {
    const inputAttr = attr as TreeSelectProps<any>;
    renderItem = <TreeSelect {...inputAttr} />;
  } else if (type === 'Select') {
    const inputAttr = attr as SelectProps<any>;
    renderItem = <Select {...inputAttr} />;
  } else if (type === 'Checkbox') {
    const inputAttr = attr as CheckboxGroupProps;
    renderItem = <Checkbox.Group {...inputAttr} />;
  } else if (type === 'Mentions') {
    const inputAttr = attr as MentionProps;
    renderItem = <Mentions {...inputAttr} />;
  } else if (type === 'Radio') {
    const inputAttr = attr as RadioProps;
    renderItem = <Radio.Group {...inputAttr} />;
  } else if (type === 'Switch') {
    const inputAttr = attr as SwitchProps;
    renderItem = <Switch {...inputAttr} />;
  } else if (type === 'TimePicker') {
    const inputAttr = attr as TimePickerProps;
    renderItem = <TimePicker {...inputAttr} />;
  } else if (type === 'Upload') {
    const inputAttr = attr as UploadProps;
    renderItem = <Upload {...inputAttr} />;
  } else if (type === 'RangePicker') {
    const inputAttr = attr as RangePickerProps;
    renderItem = <RangePicker {...inputAttr} />;
  } else if (type === 'Custom') {
    renderItem = inputNode;
  }
  return renderItem;
}
Example #5
Source File: GeneralAutoComplete.tsx    From next-basics with GNU General Public License v3.0 6 votes vote down vote up
export function GeneralAutoComplete(
  props: GeneralAutoCompleteProps
): React.ReactElement {
  const { t } = useTranslation(NS_FORMS);
  const originalOptions: OptionType[] = useMemo(
    () =>
      typeof props.options?.[0] === "string"
        ? (props.options as string[]).map((option) => ({
            label: option,
            value: option,
          }))
        : (props.options as OptionType[]),
    [props.options]
  );
  const [options, setOptions] = React.useState(originalOptions);

  const onSearch = (v: string) => {
    const q = v.trim().toLowerCase();

    setOptions(filterOptions(q, originalOptions));
  };

  React.useEffect(() => {
    setOptions(originalOptions);
  }, [originalOptions]);

  return (
    <FormItemWrapper {...props}>
      <AutoComplete
        value={props.name && props.formElement ? undefined : props.value}
        options={options}
        style={{ width: 200, ...props.inputBoxStyle }}
        placeholder={props.placeholder}
        onChange={props.onChange}
        onSearch={onSearch}
      />
    </FormItemWrapper>
  );
}
Example #6
Source File: Email.tsx    From gant-design with MIT License 5 votes vote down vote up
getDataSource = () => {
    const { list } = this.props
    return list.map(item => (<AutoComplete.Option key={item} value={item}>{item}</AutoComplete.Option>))
  }
Example #7
Source File: index.tsx    From fe-v5 with Apache License 2.0 5 votes vote down vote up
export default function Resolution(props: Props) {
  const { onChange, value, initialValue } = props;
  const [inputContent, setInputContent] = useState<string>(String(initialValue || ''));
  const [setp, setStep] = useState<number | null>(initialValue || null);

  useEffect(() => {
    setInputContent(String(value || ''));
  }, [value]);

  const onInputChange = (val) => {
    setInputContent(typeof val === 'number' ? val : val.replace(/^(0+)|[^\d]+/g, ''));
  };

  const handleEnter = (e) => {
    if (e.keyCode === 13) handleResult();
  };

  const handleResult = () => {
    // 如果清空输入,则不设置 Step 值
    if (!inputContent && setp !== null) {
      setStep(null);
      onChange && onChange(null);
      return;
    }
    const newStep = Number(inputContent);
    if (Number.isInteger(newStep) && newStep > 0 && newStep <= Number.MAX_SAFE_INTEGER && newStep !== setp) {
      setStep(newStep);
      onChange && onChange(newStep);
    }
  };

  const handelSelect = (v) => {
    const newStep = Number(v);
    if (newStep !== setp) {
      setStep(newStep);
      onChange && onChange(newStep);
    }
  };

  return (
    <div className='resolution'>
      <AutoComplete
        options={options}
        value={inputContent}
        style={{ width: 72 }}
        onChange={onInputChange}
        onSelect={handelSelect}
        onKeyDown={handleEnter}
        onBlur={handleResult}
        placeholder='Res. (s)'
      ></AutoComplete>
    </div>
  );
}
Example #8
Source File: ContractAutoComplete.spec.tsx    From next-basics with GNU General Public License v3.0 5 votes vote down vote up
describe("ContractSelect", () => {
  it("should work", () => {
    const changeFn = jest.fn();
    const wrapper = mount(
      <ContractAutoComplete
        value="cmdb.instance@postSearch:1.0.0"
        onChange={changeFn}
      />
    );

    wrapper.find(AutoComplete).at(0).invoke("onChange")(
      "cmdb.instance@customApi",
      null
    );

    expect(changeFn).toHaveBeenCalledWith("cmdb.instance@customApi:");

    wrapper.find(AutoComplete).at(0).invoke("onChange")(
      "cmdb.instance@getDetail",
      null
    );
    expect(changeFn).toHaveBeenCalledWith("cmdb.instance@getDetail:1.3.0");

    wrapper.find(AutoComplete).at(0).invoke("onSearch")("getDetail");

    jest.advanceTimersByTime(500);

    expect(useContract).toHaveBeenLastCalledWith({
      pageSize: 20,
      q: "getDetail",
    });

    wrapper
      .find("ForwardRef(AutoComplete)")
      .at(0)
      .prop("dropdownRender")()
      .props.children[1].props.onBlur(10);

    expect(useContract).toHaveBeenLastCalledWith({
      pageSize: 10,
      q: "getDetail",
    });

    wrapper.find(AutoComplete).at(1).invoke("onChange")("1.0.0", null);
    expect(changeFn).toHaveBeenCalledWith("cmdb.instance@getDetail:1.0.0");
  });

  it("should render ContractAutoCompleteLegacyWrapper", () => {
    const mockFn = jest.fn();
    const wrapper = shallow(<ContractAutoCompleteLegacyWrapper />);

    expect(wrapper.find(FormItemWrapper).length).toEqual(1);

    wrapper.find(FormItemWrapper).prop("validator")(
      null,
      "cmdb.instance@getDetail:1.0.0",
      mockFn
    );
    expect(mockFn.mock.calls[0][0]).toEqual(undefined);

    wrapper.find(FormItemWrapper).prop("validator")(
      null,
      "cmdb.instance@getDetail",
      mockFn
    );
    expect(mockFn.mock.calls[1][0]).toEqual(
      "next-builder:CONTRACT_VALIDATE_MESSAGE"
    );
  });
});
Example #9
Source File: index.tsx    From nanolooker with MIT License 5 votes vote down vote up
{ Option } = AutoComplete
Example #10
Source File: index.tsx    From jetlinks-ui-antd with MIT License 5 votes vote down vote up
render() {
    const { className, defaultValue, placeholder, open, ...restProps } = this.props;
    const { searchMode, value } = this.state;
    delete restProps.defaultOpen; // for rc-select not affected
    const inputClass = classNames(styles.input, {
      [styles.show]: searchMode,
    });

    return (
      <span
        className={classNames(className, styles.headerSearch)}
        onClick={this.enterSearchMode}
        onTransitionEnd={({ propertyName }) => {
          if (propertyName === 'width' && !searchMode) {
            const { onVisibleChange } = this.props;
            onVisibleChange(searchMode);
          }
        }}
      >
        <Icon type="search" key="Icon" />
        <AutoComplete
          key="AutoComplete"
          {...restProps}
          className={inputClass}
          value={value}
          onChange={this.onChange}
        >
          <Input
            ref={node => {
              this.inputRef = node;
            }}
            defaultValue={defaultValue}
            aria-label={placeholder}
            placeholder={placeholder}
            onKeyDown={this.onKeyDown}
            onBlur={this.leaveSearchMode}
          />
        </AutoComplete>
      </span>
    );
  }
Example #11
Source File: MailTagFormItem.tsx    From datart with Apache License 2.0 5 votes vote down vote up
{ Option } = AutoComplete
Example #12
Source File: HeaderView.tsx    From Protoman with MIT License 5 votes vote down vote up
SingleHeaderView: React.FunctionComponent<SingleProps> = ({
  editable,
  name,
  value,
  onDelete,
  onNameChange,
  onValueChange,
}) => {
  const nameOptions = COMMON_HEADER_NAMES;

  return (
    <Row gutter={8} align="middle" style={{ marginBottom: 8 }}>
      <Col span={6}>
        <AutoComplete
          style={{ width: '100%' }}
          placeholder="name"
          value={name}
          {...(editable ? {} : { open: false })}
          onChange={(s): void => {
            if (editable) {
              onNameChange(s.toString());
            }
          }}
          filterOption={(input, option): boolean => {
            return option && option.value.toString().includes(input.toString());
          }}
        >
          {nameOptions.map(option => (
            <AutoComplete.Option key={option} value={option}>
              {option}
            </AutoComplete.Option>
          ))}
        </AutoComplete>
      </Col>
      <Col span={editable ? 16 : 18}>
        <HighlightInput
          placeholder="value"
          value={value}
          colored={editable}
          onChange={(e): void => {
            if (editable) {
              onValueChange(e.target.value);
            }
          }}
        />
      </Col>
      {editable ? (
        <Col span={2}>
          <Button shape="circle" size="small" danger onClick={onDelete} disabled={!editable}>
            <DeleteOutlined />
          </Button>
        </Col>
      ) : null}
    </Row>
  );
}
Example #13
Source File: index.tsx    From ant-design-pro-V5-multitab with MIT License 5 votes vote down vote up
HeaderSearch: React.FC<HeaderSearchProps> = (props) => {
  const {
    className,
    defaultValue,
    onVisibleChange,
    placeholder,
    visible,
    defaultVisible,
    ...restProps
  } = props;

  const inputRef = useRef<Input | null>(null);

  const [value, setValue] = useMergeValue<string | undefined>(defaultValue, {
    value: props.value,
    onChange: props.onChange,
  });

  const [searchMode, setSearchMode] = useMergeValue(defaultVisible ?? false, {
    value: props.visible,
    onChange: onVisibleChange,
  });

  const inputClass = classNames(styles.input, {
    [styles.show]: searchMode,
  });
  return (
    <div
      className={classNames(className, styles.headerSearch)}
      onClick={() => {
        setSearchMode(true);
        if (searchMode && inputRef.current) {
          inputRef.current.focus();
        }
      }}
      onTransitionEnd={({ propertyName }) => {
        if (propertyName === 'width' && !searchMode) {
          if (onVisibleChange) {
            onVisibleChange(searchMode);
          }
        }
      }}
    >
      <SearchOutlined
        key="Icon"
        style={{
          cursor: 'pointer',
        }}
      />
      <AutoComplete
        key="AutoComplete"
        className={inputClass}
        value={value}
        options={restProps.options}
        onChange={setValue}
      >
        <Input
          size="small"
          ref={inputRef}
          defaultValue={defaultValue}
          aria-label={placeholder}
          placeholder={placeholder}
          onKeyDown={(e) => {
            if (e.key === 'Enter') {
              if (restProps.onSearch) {
                restProps.onSearch(value);
              }
            }
          }}
          onBlur={() => {
            setSearchMode(false);
          }}
        />
      </AutoComplete>
    </div>
  );
}
Example #14
Source File: index.tsx    From anew-server with MIT License 5 votes vote down vote up
HeaderSearch: React.FC<HeaderSearchProps> = (props) => {
  const {
    className,
    defaultValue,
    onVisibleChange,
    placeholder,
    visible,
    defaultVisible,
    ...restProps
  } = props;

  const inputRef = useRef<Input | null>(null);

  const [value, setValue] = useMergedState<string | undefined>(defaultValue, {
    value: props.value,
    onChange: props.onChange,
  });

  const [searchMode, setSearchMode] = useMergedState(defaultVisible ?? false, {
    value: props.visible,
    onChange: onVisibleChange,
  });

  const inputClass = classNames(styles.input, {
    [styles.show]: searchMode,
  });
  return (
    <div
      className={classNames(className, styles.headerSearch)}
      onClick={() => {
        setSearchMode(true);
        if (searchMode && inputRef.current) {
          inputRef.current.focus();
        }
      }}
      onTransitionEnd={({ propertyName }) => {
        if (propertyName === 'width' && !searchMode) {
          if (onVisibleChange) {
            onVisibleChange(searchMode);
          }
        }
      }}
    >
      <SearchOutlined
        key="Icon"
        style={{
          cursor: 'pointer',
        }}
      />
      <AutoComplete
        key="AutoComplete"
        className={inputClass}
        value={value}
        options={restProps.options}
        onChange={setValue}
      >
        <Input
          size="small"
          ref={inputRef}
          defaultValue={defaultValue}
          aria-label={placeholder}
          placeholder={placeholder}
          onKeyDown={(e) => {
            if (e.key === 'Enter') {
              if (restProps.onSearch) {
                restProps.onSearch(value);
              }
            }
          }}
          onBlur={() => {
            setSearchMode(false);
          }}
        />
      </AutoComplete>
    </div>
  );
}
Example #15
Source File: inputUtil.tsx    From yakit with GNU Affero General Public License v3.0 5 votes vote down vote up
InputItem: React.FC<InputItemProps> = (props) => {
    return <Item
        label={props.label}
        required={!!props.required} style={props.style} {...props.extraFormItemProps}
        help={props.help}
    >
        {props.prefixNode}
        {props.autoComplete ? <AutoComplete
            style={{width: props.width || "100%"}}
            dropdownMatchSelectWidth={400}
            disabled={!!props.disable}
            placeholder={props.placeholder}
            allowClear={true}
            value={props.value} onChange={e => props.setValue && props.setValue(e)}
            options={(props.autoComplete || []).map(i => {
                return {value: i}
            })}
        /> : props.textarea ? <>
            <Input.TextArea
                style={{width: props.width}}
                // type={props.type}
                rows={props.textareaRow}
                autoSize={props.autoSize}
                cols={props.textareaCol}
                required={!!props.required}
                disabled={!!props.disable}
                placeholder={props.placeholder}
                allowClear={props.allowClear}
                value={props.value} onChange={e => {
                props.setValue && props.setValue(e.target.value);
                if (props.isBubbing) e.stopPropagation()
            }}
                onPressEnter={(e) => {
                    if (props.isBubbing) e.stopPropagation()
                }}
                onFocus={(e) => {
                    if (props.isBubbing) e.stopPropagation()
                }}
                onClick={(e) => {
                    if (props.isBubbing) e.stopPropagation()
                }}
            />
        </> : <Input
            style={{width: props.width}}
            type={props.type}
            required={!!props.required}
            disabled={!!props.disable}
            placeholder={props.placeholder}
            allowClear={props.allowClear}
            value={props.value} onChange={e => {
            props.setValue && props.setValue(e.target.value);
            if (props.isBubbing) e.stopPropagation()
        }}
            prefix={props.prefix}
            suffix={props.suffix}
            onPressEnter={(e) => {
                if (props.isBubbing) e.stopPropagation()
            }}
            onFocus={(e) => {
                if (props.isBubbing) e.stopPropagation()
            }}
            onClick={(e) => {
                if (props.isBubbing) e.stopPropagation()
            }}
        />}
        {props.suffixNode}
    </Item>
}
Example #16
Source File: trigger.tsx    From jetlinks-ui-antd with MIT License 4 votes vote down vote up
Trigger: React.FC<Props> = props => {
  const initState: State = {
    triggerType: '',
    messageType: '',
    parameters: props.trigger.parameters ? (props.trigger.parameters.length > 0 ? props.trigger.parameters
      : [{_id: 0}]) : [{_id: 0}],
    trigger: props.trigger,
    filters: props.trigger.filters ? (props.trigger.filters.length > 0 ? props.trigger.filters : [{_id: 0}]) : [{_id: 0}],
    dataSource: [],
  };

  const [triggerType, setTriggerType] = useState(initState.triggerType);
  const [messageType, setMessageType] = useState(initState.messageType);
  const [parameters, setParameters] = useState(initState.parameters);
  const [filters, setFilters] = useState(initState.filters);
  const [dataSource, setDataSource] = useState(initState.dataSource);
  const [trigger, setTrigger] = useState(initState.trigger);

  useEffect(() => {
    setTriggerType(trigger.trigger);
    setMessageType(trigger.type);

    let data: any;
    if (props.metaData) {
      let metadata = JSON.parse(props.metaData);
      if (trigger.type === 'event') {
        data = metadata['events'];
      } else if (trigger.type === 'function') {
        data = metadata['functions'];
      } else {
        data = metadata[trigger.type];
      }
      if (data) {
        dataSource.length = 0;
        dataSource.push(trigger.modelId);
        data.map((item: any) => {
          if (item.id === trigger.modelId) {
            setDataSourceValue(trigger.type, item, trigger.modelId);
          } else {
            setDataSource([]);
          }
        });
      }
    }
  }, []);

  const submitData = () => {
    props.save({
      ...trigger,
    });
  };

  const setDataSourceValue = (type: string, data: any, value: string) => {
    dataSource.length = 0;
    dataSource.push(value);
    if (type === 'function') {
      if (data.output.type === 'object') {
        data.valueType.properties.map((p: any) => {
          dataSource.push(`${value}.${p.id}`);
        });
      }
    } else if (type === 'event') {
      dataSource.push('this');
      if (data.valueType.type === 'object') {
        data.valueType.properties.map((p: any) => {
          dataSource.push(`${p.id}`);
        });
      }
    } else if (type === 'properties') {
      if (data.valueType.type === 'object') {
        data.valueType.properties.map((p: any) => {
          dataSource.push(`${value}.${p.id}`);
        });
      }
    }
    setDataSource([...dataSource]);
  };

  const renderType = () => {
    switch (messageType) {
      case 'properties':
        return (
          <Col span={6} style={{paddingBottom: 10, paddingLeft: 3, paddingRight: 9}}>
            <Select placeholder="物模型属性" defaultValue={trigger.modelId}
                    onChange={(value: string, data: any) => {
                      setDataSourceValue('properties', data.props.data, value);
                      trigger.modelId = value;
                      setTrigger(trigger);
                      submitData();
                    }}
            >
              {JSON.parse(props.metaData).properties?.map((item: any) => (
                <Select.Option key={item.id} data={item}>{`${item.name}(${item.id})`}</Select.Option>
              ))}
            </Select>
          </Col>
        );
      case 'event':
        return (
          <Col span={6} style={{paddingBottom: 10, paddingLeft: 3, paddingRight: 9}}>
            <Select placeholder="物模型事件" defaultValue={trigger.modelId}
                    onChange={(value: string, data: any) => {
                      setDataSourceValue('event', data.props.data, value);
                      trigger.modelId = value;
                      setTrigger(trigger);
                      submitData();
                    }}
            >
              {JSON.parse(props.metaData).events?.map((item: any) => (
                <Select.Option key={item.id} data={item}>{`${item.name}(${item.id})`}</Select.Option>
              ))}
            </Select>
          </Col>
        );
      case 'function':
        return (
          <Col span={6} style={{paddingBottom: 10, paddingLeft: 3, paddingRight: 9}}>
            <Select placeholder="物模型功能" defaultValue={trigger.modelId}
                    onChange={(value: string, data: any) => {
                      setDataSourceValue('function', data.props.data, value);
                      trigger.modelId = value;
                      setTrigger(trigger);
                      submitData();
                    }}
            >
              {JSON.parse(props.metaData).functions?.map((item: any) => (
                <Select.Option key={item.id} data={item}>{`${item.name}(${item.id})`}</Select.Option>
              ))}
            </Select>
          </Col>
        );
      default:
        return null;
    }
  };

  const renderDataType = () => {
    switch (triggerType) {
      case 'device':
        return (
          <div>
            <Col span={24}>
              <Col span={6} style={{paddingBottom: 10, paddingLeft: -1, paddingRight: 12}}>
                <Select placeholder="选择类型,如:属性/事件" defaultValue={trigger.type}
                        onChange={(value: string) => {
                          setMessageType(() => value);
                          trigger.type = value;
                          setTrigger(trigger);
                          submitData();
                        }}>
                  <Select.Option value="online">上线</Select.Option>
                  <Select.Option value="offline">离线</Select.Option>
                  {props.metaData && JSON.parse(props.metaData).properties && (
                    <Select.Option value="properties">属性</Select.Option>
                  )}
                  {props.metaData && JSON.parse(props.metaData).events && (
                    <Select.Option value="event">事件</Select.Option>
                  )}
                  {/*{props.metaData && JSON.parse(props.metaData).functions && (
                  <Select.Option value="function">功能</Select.Option>
                )}*/}
                </Select>
              </Col>
              {renderType()}
            </Col>
            <Col span={24}>
              {filters.map((item: any, index: number) => (
                <div className="ant-row" key={index}>
                  <Col span={6} style={{paddingLeft: -1, paddingRight: 12, paddingBottom: 10}}>
                    <AutoComplete dataSource={dataSource} placeholder="过滤条件KEY" children={item.key}
                                  defaultValue={item.key}
                                  onBlur={value => {
                                    filters[index].key = value;
                                    trigger.filters = filters;
                                    setTrigger(trigger);
                                    submitData();
                                  }}
                      /*onChange={value => {
                        filters[index].key = value;
                        trigger.filters = filters;
                        setTrigger(trigger);
                        submitData();
                      }}*/
                                  filterOption={(inputValue, option) =>
                                    option?.props?.children?.toUpperCase()?.indexOf(inputValue.toUpperCase()) !== -1
                                  }
                    />
                  </Col>
                  <Col span={6} style={{paddingLeft: 3, paddingRight: 9, paddingBottom: 10}}>
                    <Select placeholder="操作符" defaultValue={item.operator}
                            onChange={(value: string) => {
                              filters[index].operator = value;
                              trigger.filters = filters;
                              setTrigger(trigger);
                              submitData();
                            }}>
                      <Select.Option value="eq">等于(=)</Select.Option>
                      <Select.Option value="not">不等于(!=)</Select.Option>
                      <Select.Option value="gt">大于(&gt;)</Select.Option>
                      <Select.Option value="lt">小于(&lt;)</Select.Option>
                      <Select.Option value="gte">大于等于(&gt;=)</Select.Option>
                      <Select.Option value="lte">小于等于(&lt;=)</Select.Option>
                      <Select.Option value="like">模糊(%)</Select.Option>
                    </Select>
                  </Col>
                  <Col span={7} style={{paddingLeft: 7, paddingRight: 3, paddingBottom: 10}}>
                    <Input placeholder="过滤条件值" defaultValue={item.value}
                           onChange={event => {
                             filters[index].value = event.target.value;
                             trigger.filters = filters;
                             setTrigger(trigger);
                             submitData();
                           }}
                    />
                  </Col>
                  <Col span={5} style={{textAlign: 'right', marginTop: 6, paddingBottom: 10}}>
                    <a style={{paddingLeft: 10, paddingTop: 7}}
                       onClick={() => {
                         filters.splice(index, 1);
                         setFilters([...filters]);
                         trigger.filters = filters;
                         setTrigger({...trigger});
                         submitData();
                       }}
                    >删除</a>
                  </Col>
                </div>
              ))}
            </Col>
            <Col span={24}>
              <div>
                <a onClick={() => {
                  setFilters([...filters, {_id: Math.round(Math.random() * 100000)}]);
                }}>添加</a>
              </div>
            </Col>
          </div>
        );
      case 'timer':
        return (
          <div>
            <Col span={6} style={{paddingBottom: 10}}>
              <Input placeholder="cron表达式" defaultValue={trigger.cron} key="cron"
                     onBlur={event => {
                       trigger.cron = event.target.value;
                       setTrigger(trigger);
                       submitData();
                     }}
                /*onChange={event => {
                  trigger.cron = event.target.value;
                  setTrigger(trigger);
                  submitData();
                }}*/
              />
            </Col>
            <Col span={24}>
              <Col span={6} style={{paddingBottom: 10, paddingLeft: -1, paddingRight: 12}}>
                <Select placeholder="选择类型,如:属性/事件" defaultValue={trigger.type}
                        onChange={(value: string) => {
                          setMessageType(() => value);
                          trigger.type = value;
                          setTrigger(trigger);
                          submitData();
                        }}>
                  {props.metaData && JSON.parse(props.metaData).properties && (
                    <Select.Option value="properties">属性</Select.Option>
                  )}
                  {props.metaData && JSON.parse(props.metaData).events && (
                    <Select.Option value="event">事件</Select.Option>
                  )}
                  {/*{props.metaData && JSON.parse(props.metaData).functions && (
                  <Select.Option value="function">功能</Select.Option>
                )}*/}
                </Select>
              </Col>
              {renderType()}
            </Col>
            {triggerType === 'timer' && messageType === 'function' && (
              <Col span={24} style={{backgroundColor: '#F5F5F6', paddingBottom: 10}}>
                {parameters.map((item: any, index: number) => (
                  <Row style={{paddingBottom: 5, paddingTop: 5}} key={index}>
                    <Col span={7}>
                      <Input placeholder="请输入属性" value={item.property}
                             onChange={event => {
                               parameters[index].property = event.target.value;
                               setParameters([...parameters]);
                               trigger.parameters = parameters;
                               setTrigger({...trigger});
                               submitData();
                             }}
                      />
                    </Col>
                    <Col span={1} style={{textAlign: 'center'}}/>
                    <Col span={7}>
                      <Input placeholder="请输入别名" value={item.alias}
                             onChange={event => {
                               parameters[index].alias = event.target.value;
                               setParameters([...parameters]);
                               trigger.parameters = parameters;
                               setTrigger({...trigger});
                               submitData();
                             }}
                      />
                    </Col>
                    <Col span={1} style={{textAlign: 'center'}}>
                      {index === 0 ? (
                        <Row>
                          <Icon type="plus-circle" title="新增转换"
                                style={{fontSize: 20, paddingTop: 7}}
                                onClick={() => {
                                  setParameters([...parameters, {_id: Math.round(Math.random() * 100000)}]);
                                }}
                          />
                          <Icon style={{paddingLeft: 10, paddingTop: 7, fontSize: 20}}
                                type="minus-circle" title="删除转换"
                                onClick={() => {
                                  parameters.splice(index, 1);
                                  setParameters([...parameters]);
                                  trigger.parameters = parameters;
                                  setTrigger({...trigger});
                                  submitData();
                                }}
                          />
                        </Row>
                      ) : (
                        <Icon type="minus-circle" title="删除转换"
                              style={{fontSize: 20, paddingTop: 7}}
                              onClick={() => {
                                parameters.splice(index, 1);
                                setParameters([...parameters]);
                                trigger.parameters = parameters;
                                setTrigger({...trigger});
                                submitData();
                              }}
                        />
                      )}
                    </Col>
                  </Row>
                ))}
              </Col>
            )}
            <Col span={24}>
              {filters.map((item: any, index: number) => (
                <div className="ant-row" key={index}>
                  <Col span={6} style={{paddingLeft: -1, paddingRight: 12, paddingBottom: 10}}>
                    <AutoComplete dataSource={dataSource} placeholder="过滤条件KEY" children={item.key}
                                  defaultValue={item.key}
                                  onBlur={value => {
                                    filters[index].key = value;
                                    trigger.filters = filters;
                                    setTrigger(trigger);
                                    submitData();
                                  }}
                      /*onChange={value => {
                        filters[index].key = value;
                        trigger.filters = filters;
                        setTrigger(trigger);
                        submitData();
                      }}*/
                                  filterOption={(inputValue, option) =>
                                    option?.props?.children?.toUpperCase()?.indexOf(inputValue.toUpperCase()) !== -1
                                  }
                    />
                  </Col>
                  <Col span={6} style={{paddingLeft: 3, paddingRight: 9, paddingBottom: 10}}>
                    <Select placeholder="操作符" defaultValue={item.operator}
                            onChange={(value: string) => {
                              filters[index].operator = value;
                              trigger.filters = filters;
                              setTrigger(trigger);
                              submitData();
                            }}>
                      <Select.Option value="eq">等于(=)</Select.Option>
                      <Select.Option value="not">不等于(!=)</Select.Option>
                      <Select.Option value="gt">大于(&gt;)</Select.Option>
                      <Select.Option value="lt">小于(&lt;)</Select.Option>
                      <Select.Option value="gte">大于等于(&gt;=)</Select.Option>
                      <Select.Option value="lte">小于等于(&lt;=)</Select.Option>
                      <Select.Option value="like">模糊(%)</Select.Option>
                    </Select>
                  </Col>
                  <Col span={7} style={{paddingLeft: 7, paddingRight: 3, paddingBottom: 10}}>
                    <Input placeholder="过滤条件值" defaultValue={item.value}
                           onBlur={event => {
                             filters[index].value = event.target.value;
                             trigger.filters = filters;
                             setTrigger(trigger);
                             submitData();
                           }}
                      /*onChange={event => {
                        filters[index].value = event.target.value;
                        trigger.filters = filters;
                        setTrigger(trigger);
                        submitData();
                      }}*/
                    />
                  </Col>
                  <Col span={5} style={{textAlign: 'right', marginTop: 6, paddingBottom: 10}}>
                    <a style={{paddingLeft: 10, paddingTop: 7}}
                       onClick={() => {
                         filters.splice(index, 1);
                         setFilters([...filters]);
                         if (filters.length > 0) {
                           trigger.filters = filters;
                           setTrigger({...trigger});
                         }
                         submitData();
                       }}
                    >删除</a>
                  </Col>
                </div>
              ))}
            </Col>
            <Col span={24}>
              <div>
                <a onClick={() => {
                  setFilters([...filters, {_id: Math.round(Math.random() * 100000)}]);
                }}>添加</a>
              </div>
            </Col>
          </div>
        );
      default:
        return null;
    }
  };

  return (
    <div style={{paddingBottom: 5}}>
      <Card size="small" bordered={false} style={{backgroundColor: '#F5F5F6'}}>
        <Row style={{marginLeft: -2}}>
          <span>触发器: {props.position + 1}</span>
          <Popconfirm
            title="确认删除此触发器?"
            onConfirm={() => {
              props.remove(props.position);
            }}
          >
            <a style={{paddingLeft: 30}}>删除</a>
          </Popconfirm>
        </Row>

        <Row gutter={16} style={{paddingLeft: 10}}>
          <Col span={6} style={{paddingBottom: 10}}>
            <Select
              placeholder="选择触发器类型"
              value={trigger.trigger}
              onChange={(value: string) => {
                setTriggerType(value);
                trigger.trigger = value;
                submitData();
              }}
            >
              <Select.Option value="device">设备触发</Select.Option>
              <Select.Option value="timer">定时触发</Select.Option>
            </Select>
          </Col>
          {renderDataType()}
        </Row>
      </Card>
    </div>
  );
}
Example #17
Source File: index.tsx    From ui-visualization with MIT License 4 votes vote down vote up
HeaderSearch: React.FC<HeaderSearchProps> = (props) => {
  const {
    className,
    defaultValue,
    onVisibleChange,
    placeholder,
    open,
    defaultOpen,
    ...restProps
  } = props;

  const inputRef = useRef<Input | null>(null);

  const [value, setValue] = useMergeValue<string | undefined>(defaultValue, {
    value: props.value,
    onChange: props.onChange,
  });

  const [searchMode, setSearchMode] = useMergeValue(defaultOpen || false, {
    value: props.open,
    onChange: onVisibleChange,
  });

  const inputClass = classNames(styles.input, {
    [styles.show]: searchMode,
  });

  return (
    <div
      className={classNames(className, styles.headerSearch)}
      onClick={() => {
        setSearchMode(true);
        if (searchMode && inputRef.current) {
          inputRef.current.focus();
        }
      }}
      onTransitionEnd={({ propertyName }) => {
        if (propertyName === 'width' && !searchMode) {
          if (onVisibleChange) {
            onVisibleChange(searchMode);
          }
        }
      }}
    >
      <SearchOutlined
        key="Icon"
        style={{
          cursor: 'pointer',
        }}
      />
      <AutoComplete
        key="AutoComplete"
        className={inputClass}
        value={value}
        style={{
          height: 28,
          marginTop: -6,
        }}
        options={restProps.options}
        onChange={setValue}
      >
        <Input
          ref={inputRef}
          defaultValue={defaultValue}
          aria-label={placeholder}
          placeholder={placeholder}
          onKeyDown={(e) => {
            if (e.key === 'Enter') {
              if (restProps.onSearch) {
                restProps.onSearch(value);
              }
            }
          }}
          onBlur={() => {
            setSearchMode(false);
          }}
        />
      </AutoComplete>
    </div>
  );
}
Example #18
Source File: ContactSyncedAutocompleteFields.tsx    From condo with MIT License 4 votes vote down vote up
ContactSyncedAutocompleteFields: React.FC<IContactSyncedAutocompleteFieldsProps> = ({ refetch, initialQuery, initialValue, onChange, onChecked, checked, contacts, displayMinusButton, onClickMinusButton }) => {
    const intl = useIntl()
    const NamePlaceholder = intl.formatMessage({ id: 'contact.Contact.ContactsEditor.Name.placeholder' })
    const [value, setValue] = useState(initialValue)

    const searchSuggestions = useCallback(
        async (query) => {
            return refetch({
                where: { ...initialQuery, ...query },
            })
        },
        [initialQuery, refetch],
    )

    const debouncedSearch = useMemo(
        () => {
            return debounce(searchSuggestions, DEBOUNCE_TIMEOUT_IN_MS)
        },
        [searchSuggestions],
    )

    const searchContactByPhone = useCallback(async (query) => {
        const contactName = get(value, 'name', undefined)

        await debouncedSearch({
            phone_contains_i: query,
            name_contains_i: contactName,
        })
    }, [debouncedSearch, value])

    const searchContactByName = useCallback(async (query) => {
        const contactPhone = get(value, 'phone', undefined)

        await debouncedSearch({
            name_contains_i: query,
            phone_contains_i: contactPhone,
        })
    }, [debouncedSearch, value])

    const handleSelectContact = (value: string, option: OptionProps) => {
        setValueAndTriggerOnChange(option.item)
    }

    const handleChangeContact = (field) => (fieldValue) => {
        const newValue = {
            ...value,
            [field]: fieldValue,
        }
        setValueAndTriggerOnChange(newValue)
    }

    const setValueAndTriggerOnChange = (contact) => {
        setValue(contact)
        onChange(contact)
    }

    const handleClearContact = () => {
        setValue(null)
    }

    const handleChecked = () => {
        onChecked && onChecked()
    }

    const renderOptionsBy = useCallback((prop) =>
        contacts.map(contact => ({
            value: contact[prop],
            item: contact,
        }))
    , [contacts])

    const phoneOptions = useMemo(() => renderOptionsBy('phone'), [renderOptionsBy])
    const nameOptions = useMemo(() => renderOptionsBy('name'), [renderOptionsBy])

    return (
        <>
            <Col span={10}>
                <AutoComplete
                    allowClear
                    value={get(value, 'phone')}
                    options={phoneOptions}
                    onSelect={handleSelectContact}
                    onSearch={searchContactByPhone}
                    onChange={handleChangeContact('phone')}
                    onClear={handleClearContact}
                    style={{ width: '100%' }}
                >
                    <PhoneInput
                        block
                        compatibilityWithAntAutoComplete={true}
                    />
                </AutoComplete>
            </Col>
            <Col span={10}>
                <AutoComplete
                    allowClear
                    placeholder={NamePlaceholder}
                    value={get(value, 'name')}
                    options={nameOptions}
                    onSelect={handleSelectContact}
                    onSearch={searchContactByName}
                    onChange={handleChangeContact('name')}
                    onClear={handleClearContact}
                    style={{ width: '100%' }}
                />
            </Col>
            <Col span={2}>
                {onChecked && (
                    <Radio
                        onClick={handleChecked}
                        checked={checked}
                        style={{ marginTop: '8px' }}
                    />
                )}
            </Col>
            <Col span={2}>
                {displayMinusButton && (
                    <MinusCircleOutlined
                        style={{
                            color: colors.black,
                            fontSize: '21px',
                            marginTop: '9px',
                            marginLeft: '-4px',
                        }}
                        onClick={onClickMinusButton}
                    />
                )}
            </Col>
        </>
    )
}
Example #19
Source File: index.tsx    From nanolooker with MIT License 4 votes vote down vote up
CryptocurrencyPreferences: React.FC<Props> = ({ isDetailed }) => {
  const { t } = useTranslation();
  const {
    cryptocurrency,
    addCryptocurrency,
    removeCryptocurrency,
    reorderCryptocurrency,
  } = React.useContext(PreferencesContext);
  const [search, setSearch] = React.useState<string>("");

  const onSearch = (value: string) => {
    setSearch(value);
  };

  const onSelect = (value: string) => {
    const { symbol = "" } = dataSource.find(({ name }) => name === value) || {};

    addCryptocurrency(symbol);
    setSearch("");
  };

  const options = dataSource.map(({ name, symbol }) => (
    <Option
      key={name}
      value={name}
      symbol={symbol}
      disabled={cryptocurrency.includes(symbol) || symbol === "nano"}
    >
      <img
        src={`/cryptocurrencies/logo/${symbol}.png`}
        alt={name}
        width="16px"
        height="16px"
        style={{ marginRight: "6px" }}
      />
      {name}
    </Option>
  ));

  const reorder = (list: string[], startIndex: number, endIndex: number) => {
    const result = Array.from(list);
    const [removed] = result.splice(startIndex, 1);
    result.splice(endIndex, 0, removed);

    return result;
  };

  const onDragEnd = (result: any) => {
    const items = reorder(
      cryptocurrency,
      result.source?.index || 0,
      result.destination?.index || 0,
    );
    reorderCryptocurrency(items);
  };

  return (
    <Row>
      <Col xs={24}>
        <Text
          className={isDetailed ? "preference-detailed-title" : ""}
          style={{
            display: "block",
            marginBottom: "6px",
          }}
        >
          {t("preferences.watch")}
        </Text>
      </Col>
      {isDetailed ? (
        <Col xs={24} style={{ marginBottom: "6px" }}>
          <Text>{t("preferences.watchDetailed")}</Text>
        </Col>
      ) : null}

      <Col xs={24} md={isDetailed ? 12 : 24}>
        <AutoComplete
          value={search}
          style={{ width: "100%" }}
          filterOption={(value = "", option) => {
            const { value: name, symbol } = option as any;

            return (
              name.toLowerCase().includes(value.toLowerCase()) ||
              symbol.toLowerCase().includes(value.toLowerCase())
            );
          }}
          onSearch={onSearch}
          onSelect={onSelect}
          placeholder={t("preferences.watchSearch")}
        >
          {options}
        </AutoComplete>

        {cryptocurrency.length ? (
          <DragDropContext onDragEnd={onDragEnd}>
            <Droppable droppableId={`hello${isDetailed ? "-detailed" : ""}`}>
              {(provided, snapshot) => (
                <ul
                  style={{
                    margin: 0,
                    padding: "6px",
                    marginTop: "6px",
                    backgroundColor: snapshot.isDraggingOver
                      ? "#1890ff24"
                      : "#f6f6f6",
                    listStyle: "none",
                  }}
                  ref={provided.innerRef}
                  {...provided.droppableProps}
                >
                  {cryptocurrency.map((symbol, index) => {
                    const { name = "" } =
                      dataSource.find(
                        ({ symbol: sourceSymbol }) => sourceSymbol === symbol,
                      ) || {};
                    return (
                      <Draggable draggableId={name} index={index} key={name}>
                        {provided => {
                          // https://github.com/atlassian/react-beautiful-dnd/issues/1662#issuecomment-708538811
                          if (
                            typeof provided.draggableProps.onTransitionEnd ===
                            "function"
                          ) {
                            window?.requestAnimationFrame(() =>
                              // @ts-ignore
                              provided?.draggableProps?.onTransitionEnd?.({
                                propertyName: "transform",
                              }),
                            );
                          }

                          return (
                            <li
                              {...provided.draggableProps}
                              {...provided.dragHandleProps}
                              ref={provided.innerRef}
                            >
                              <div
                                style={{
                                  display: "flex",
                                  justifyContent: "space-between",
                                  alignItems: "center",
                                  width: "100%",
                                  padding: "6px",
                                  marginTop: "-1px",
                                  backgroundColor: "#fff",
                                  border: "1px solid #d9d9d9",
                                  ...(index !== cryptocurrency.length - 1
                                    ? { marginBottom: "6px" }
                                    : { marginBottom: "-1px" }),
                                }}
                              >
                                <span>
                                  <img
                                    src={`/cryptocurrencies/logo/${symbol}.png`}
                                    alt={name}
                                    width="16px"
                                    height="16px"
                                    style={{ marginRight: "6px" }}
                                  />
                                  {name}
                                </span>
                                <DeleteButton
                                  onClick={(e: Event) => {
                                    e.stopPropagation();
                                    removeCryptocurrency(symbol);
                                  }}
                                />
                              </div>
                            </li>
                          );
                        }}
                      </Draggable>
                    );
                  })}
                  {provided.placeholder}
                </ul>
              )}
            </Droppable>
          </DragDropContext>
        ) : null}
      </Col>
    </Row>
  );
}
Example #20
Source File: MailTagFormItem.tsx    From datart with Apache License 2.0 4 votes vote down vote up
MailTagFormItem: FC<MailTagFormItemProps> = ({
  value,
  onChange,
}) => {
  const [dataSource, setDataSource] = useState<IUserInfo[]>([]);
  const [keyword, setKeyword] = useState('');
  const t = useI18NPrefix(
    'main.pages.schedulePage.sidebar.editorPage.emailSettingForm.mailTagFormItem',
  );

  const emails = useMemo(() => {
    return value ? value.split(';').filter(v => !!v) : [];
  }, [value]);

  const onSearch = useCallback(async keyword => {
    if (keyword) {
      const res = await searchUserEmails(keyword);
      setDataSource(res);
    } else {
      setDataSource([]);
    }
  }, []);
  const onDebouncedSearch = useMemo(
    () => debounce(onSearch, DEFAULT_DEBOUNCE_WAIT),
    [onSearch],
  );

  const onSelectOrRemoveEmail = useCallback(
    (email: string) => {
      const _emails = [...emails];
      const index = _emails.indexOf(email);
      if (index > -1) {
        _emails.splice(index, 1);
      } else {
        _emails.push(email);
      }
      onChange?.(_emails.join(';'));
    },
    [onChange, emails],
  );

  useEffect(() => {
    setKeyword('');
  }, [value]);

  const options = useMemo(() => {
    const items = dataSource.filter(v => !emails.includes(v?.email));
    return items.map(({ id, username, email, avatar }) => (
      <Option key={id} value={email}>
        <Space>
          <Avatar src={''} size="small" icon={<UserOutlined />} />
          <span>{username}</span>
          <span>{email}</span>
        </Space>
      </Option>
    ));
  }, [dataSource, emails]);

  const appendOptions = useMemo(() => {
    const newEmail = keyword as string;
    if (
      !regexEmail.test(newEmail) ||
      ~dataSource.findIndex(({ email }) => email === newEmail) < 0
    ) {
      return [];
    }
    return [
      <Option key={newEmail} value={newEmail}>
        <Space>
          <Avatar size="small" icon={<UserOutlined />} />
          <span>{newEmail.split('@')[0]}</span>
          <span>{newEmail}</span>
        </Space>
      </Option>,
    ];
  }, [keyword, dataSource]);
  const autoCompleteOptions = useMemo(
    () => options.concat(appendOptions),
    [appendOptions, options],
  );

  return (
    <>
      {emails.map(email => (
        <EmailTag
          closable
          key={email}
          color="blue"
          onClose={() => onSelectOrRemoveEmail(email)}
        >
          {email}
        </EmailTag>
      ))}
      <AutoComplete
        value={keyword}
        onChange={setKeyword}
        dataSource={autoCompleteOptions}
        onSearch={onDebouncedSearch}
        onSelect={onSelectOrRemoveEmail}
        onBlur={() => onSearch('')}
      >
        <Input suffix={<SearchOutlined />} placeholder={t('placeholder')} />
      </AutoComplete>
    </>
  );
}
Example #21
Source File: WalletForm.tsx    From subscan-multisig-react with Apache License 2.0 4 votes vote down vote up
export function WalletForm() {
  const { t } = useTranslation();
  const { accounts, api, network, chain } = useApi();
  const { contacts } = useContacts();
  const [form] = useForm();
  const history = useHistory();
  const [selectedAccounts, setSelectedAccounts] = useState<string[]>([]);
  const [shareScope, setShareScope] = useState<ShareScope>(ShareScope.all);
  const mainColor = useMemo(() => {
    return getThemeColor(network);
  }, [network]);

  const presetNetworks = useMemo(() => {
    return keys(chains);
  }, []);

  const options = useMemo<{ label: string; value: string }[]>(() => {
    const accountOptions = accounts?.map(({ address, meta }) => ({
      label: meta?.name ? `${meta?.name} - ${address}` : address,
      value: address,
    }));
    const contactOptions = contacts?.map(({ address, meta }) => ({
      label: meta?.name ? `${meta?.name} - ${address}` : address,
      value: address,
    }));
    const composeOptions: { label: string; value: string }[] = [];
    if (accountOptions) {
      composeOptions.push(...accountOptions);
    }
    if (contactOptions) {
      composeOptions.push(...contactOptions);
    }

    return composeOptions.filter(({ value }) => !selectedAccounts.includes(value)) || [];
  }, [accounts, contacts, selectedAccounts]);

  const updateSelectedAccounts = (namePath?: (string | number)[]) => {
    const selected: {
      name: string;
      address: string;
    }[] = form.getFieldValue('members') || [];
    let result = selected.map((item) => item?.address);

    if (namePath) {
      const value = form.getFieldValue(namePath);

      result = result.filter((item) => item !== value);
    }

    setSelectedAccounts(result);
  };

  const uploadProps = {
    name: 'file',
    headers: {
      authorization: 'authorization-text',
    },
    onChange(info: any) {
      if (info.file.status !== 'uploading') {
        // console.log(info.file, info.fileList);
      }
      if (info.file.status === 'done') {
        message.success(`${info.file.name} file uploaded successfully`);
      } else if (info.file.status === 'error') {
        message.error(`${info.file.name} file upload failed.`);
      }
    },
    customRequest(info: any) {
      try {
        const reader = new FileReader();

        reader.onload = (e: any) => {
          // eslint-disable-next-line no-console
          // console.log(e.target.result);

          const config = JSON.parse(e.target.result) as MultisigAccountConfig;
          if (!config || !config.members || !config.threshold) {
            message.error(t('account config error'));
            return;
          }
          const encodeMembers = config.members.map((member) => {
            return {
              name: member.name,
              address: encodeAddress(member.address, Number(chain.ss58Format)),
            };
          });
          form.setFieldsValue({ threshold: config.threshold, name: config.name, members: encodeMembers });
        };
        reader.readAsText(info.file);
      } catch (err: unknown) {
        message.error(t('account config error'));
        if (err instanceof Error) {
          // eslint-disable-next-line no-console
          console.log('err:', err);
        }
      }
    },
  };

  return (
    <Form
      name="wallet"
      layout="vertical"
      validateMessages={validateMessages[i18n.language as 'en' | 'en-US' | 'zh-CN' | 'zh']}
      form={form}
      initialValues={{
        name: '',
        threshold: 2,
        members: [
          { name: '', address: '' },
          { name: '', address: '' },
          { name: '', address: '' },
        ],
        rememberExternal: true,
      }}
      onFinish={async (values: WalletFormValue) => {
        const { members, name, threshold, rememberExternal } = values;
        const signatories = members.map(({ address }) => address);
        const addressPair = members.map(({ address, ...other }) => ({
          ...other,
          address: encodeAddress(address, Number(chain.ss58Format)),
        }));
        // Add external address to contact list.
        const addExternalToContact = () => {
          members.forEach((item) => {
            const account = accounts?.find((accountItem) => {
              return accountItem.address === item.address;
            });
            const contact = contacts?.find((contactItem) => {
              return contactItem.address === item.address;
            });

            if (!account && !contact) {
              try {
                keyring.saveAddress(item.address, {
                  name: item.name,
                });
              } catch (err: unknown) {
                if (err instanceof Error) {
                  message.error(err.message);
                }
              }
            }
          });
        };
        const exec = () => {
          try {
            keyring.addMultisig(signatories, threshold, {
              name,
              addressPair,
              genesisHash: api?.genesisHash.toHex(),
            });

            if (rememberExternal) {
              addExternalToContact();
            }

            updateMultiAccountScope(values, network);
            message.success(t('success'));
            history.push('/' + history.location.hash);
          } catch (error: unknown) {
            if (error instanceof Error) {
              message.error(t(error.message));
            }
          }
        };

        const acc = findMultiAccount(values);

        if (acc) {
          confirmToAdd(acc, exec);
        } else {
          exec();
        }
      }}
      className="max-w-screen-xl mx-auto"
    >
      <Form.Item>
        <div className="w-full grid grid-cols-4 items-center gap-8">
          <Upload {...uploadProps} showUploadList={false}>
            <Button type="primary" size="middle" block className="flex justify-center items-center">
              {t('import from config')}
            </Button>
          </Upload>
        </div>
      </Form.Item>

      <Form.Item
        name="name"
        label={<LabelWithTip name="name" tipMessage="wallet.tip.name" />}
        rules={[{ required: true }]}
      >
        <Input size="large" />
      </Form.Item>

      <Form.Item
        name="threshold"
        label={<LabelWithTip name="threshold" tipMessage="wallet.tip.threshold" />}
        rules={[{ required: true }]}
      >
        <InputNumber size="large" min={THRESHOLD} className="w-full" />
      </Form.Item>

      <Form.Item label={<LabelWithTip name="share scope" tipMessage="wallet.tip.share" />}>
        <div className="flex items-center">
          <Form.Item name="share" rules={[{ required: true }]} initialValue={1} className="mb-0">
            <Radio.Group onChange={(event) => setShareScope(event.target.value)}>
              <Radio value={ShareScope.all}>{t('All Networks')}</Radio>
              <Radio value={ShareScope.current}>{t('Current Network')}</Radio>
              <Radio value={ShareScope.custom}>{t('Custom')}</Radio>
            </Radio.Group>
          </Form.Item>

          {shareScope === ShareScope.custom && (
            <Form.Item name="scope" rules={[{ required: true }]} initialValue={[network]} className="mb-0 flex-1">
              <Select mode="multiple" disabled={shareScope !== ShareScope.custom}>
                {presetNetworks.map((net) => (
                  <Select.Option value={net} key={net}>
                    <Tag
                      color={getThemeColor(net as Network)}
                      style={{
                        borderRadius: '2px',
                      }}
                    >
                      {net}
                    </Tag>
                  </Select.Option>
                ))}
              </Select>
            </Form.Item>
          )}
        </div>
      </Form.Item>

      <LabelWithTip name="members" tipMessage="wallet.tip.members" />

      <Row gutter={20} className="bg-gray-100 mt-2 mb-6 p-4">
        <Col span={2}>{t('id')}</Col>
        <Col span={5}>{t('name')}</Col>
        <Col span={17}>{t('address')}</Col>
      </Row>

      <Form.List name="members">
        {(fields, { add, remove }) => (
          <>
            {fields.map((field, index) => (
              <Row key={field.key} gutter={20} className="px-4">
                <Col span={2} className="pl-2 pt-2">
                  {index + 1}
                </Col>
                <Col span={5}>
                  <Form.Item
                    {...field}
                    name={[field.name, 'name']}
                    fieldKey={[field.key, 'name']}
                    rules={[{ required: true, message: t('Member name is required') }]}
                  >
                    <Input size="large" placeholder={t('wallet.tip.member_name')} className="wallet-member" />
                  </Form.Item>
                </Col>
                <Col span={16}>
                  <Form.Item
                    {...field}
                    name={[field.name, 'address']}
                    fieldKey={[field.key, 'address']}
                    validateFirst
                    rules={[
                      { required: true, message: t('Account address is required') },
                      {
                        validator: (_, value) =>
                          convertToSS58(value, Number(chain.ss58Format)) ? Promise.resolve() : Promise.reject(),
                        message: t('You must input a ss58 format address'),
                      },
                    ]}
                  >
                    <AutoComplete
                      options={options}
                      onChange={(addr) => {
                        let account: KeyringAddress | InjectedAccountWithMeta | undefined = accounts?.find(
                          (item) => item.address === addr
                        );

                        if (!account) {
                          account = contacts?.find((item) => item.address === addr);
                        }

                        if (!account) {
                          return;
                        }

                        const members: { name?: string; address: string }[] = form.getFieldValue('members');

                        if (account) {
                          members[index].name = account?.meta?.name ?? '';
                          form.setFieldsValue({ members: [...members] });
                        }

                        setSelectedAccounts(members.map((item) => item?.address));
                      }}
                    >
                      <Input
                        suffix={<img src={iconDownFilled} alt="down" />}
                        size="large"
                        placeholder={t('wallet.tip.member_address')}
                        className="wallet-member"
                      />
                    </AutoComplete>
                  </Form.Item>
                </Col>

                <Col span={1}>
                  <Form.Item>
                    <DeleteOutlined
                      className="text-xl mt-2"
                      style={{
                        color: mainColor,
                      }}
                      onClick={() => {
                        updateSelectedAccounts(['members', field.name, 'address']);

                        if (fields.length > THRESHOLD) {
                          remove(field.name);
                        } else {
                          const members = form.getFieldValue('members');

                          members[index] = { name: '', address: '' };
                          form.setFieldsValue({ members: [...members] });
                          message.warn(`You must set at least ${THRESHOLD} members.`);
                        }
                      }}
                    />
                  </Form.Item>
                </Col>
              </Row>
            ))}

            <Row>
              <Col span={24}>
                <Form.Item>
                  <Button
                    size="large"
                    onClick={() => add()}
                    block
                    className="flex justify-center items-center w-full"
                    style={{ color: mainColor }}
                  >
                    {t('add_members')}
                  </Button>
                </Form.Item>
              </Col>
            </Row>
          </>
        )}
      </Form.List>

      <Form.Item label={null} name="rememberExternal" valuePropName="checked">
        <Checkbox>{t('contact.Add External Address')}</Checkbox>
      </Form.Item>

      <Form.Item>
        <div className="w-2/5 grid grid-cols-2 items-center gap-8">
          <Button type="primary" size="large" block htmlType="submit" className="flex justify-center items-center">
            {t('create')}
          </Button>
          <Link to="/" className="block">
            <Button
              type="default"
              size="large"
              className="flex justify-center items-center w-full"
              style={{ color: mainColor }}
            >
              {t('cancel')}
            </Button>
          </Link>
        </div>
      </Form.Item>
    </Form>
  );
}
Example #22
Source File: add-flow.tsx    From erda-ui with GNU Affero General Public License v3.0 4 votes vote down vote up
AddFlow: React.FC<IProps> = ({ onAdd, type, metaData = {} }) => {
  const [form] = Form.useForm<Omit<CreateFlowNode, 'issueID'>>();
  const allBranch = getBranches.useData();
  const { id: projectId, name: projectName } = projectStore.useStore((s) => s.info);
  const [visible, setVisible] = React.useState(false);
  const [flowName, setFlowName] = React.useState('');
  const apps = getJoinedApps.useData();
  const metaWorkflow = queryWorkflow.useData();
  const workflow = metaWorkflow?.flows ?? [];
  const branches = workflow.filter((item) => item.flowType !== FlowType.SINGLE_BRANCH);
  const { iteration, issue } = metaData;

  React.useEffect(() => {
    if (projectId) {
      getJoinedApps.fetch({
        projectId,
        pageNo: 1,
        pageSize: 200,
      });
      queryWorkflow.fetch({ projectID: projectId });
    }
  }, [projectId]);
  React.useEffect(() => {
    if (!visible) {
      setFlowName('');
    }
  }, [visible]);
  const content = React.useMemo(() => {
    const handleCancel = () => {
      setVisible(false);
      form.resetFields();
    };
    const handleOk = async () => {
      const formData = await form.validateFields();
      await createFlow.fetch({
        issueID: issue.id,
        ...formData,
      });
      handleCancel();
      onAdd();
    };

    const getBranchInfo = (flowName: string) => {
      const flow = branches.find((item) => item.name === flowName)!;
      let sourceBranch;
      if (flow && flow.flowType !== FlowType.SINGLE_BRANCH) {
        const { changeBranchRule } = flow.startWorkflowHints.find((t) => t.place === type)!;
        sourceBranch = changeBranchRule.replace('*', issue.id);
      }
      return {
        targetBranch: flow.targetBranch,
        sourceBranch,
      };
    };

    const list = [
      {
        label: i18n.t('dop:R&D Workflow'),
        type: 'select',
        name: 'flowName',
        options: branches?.map((branch) => ({ name: branch.name, value: branch.name })),
        itemProps: {
          onChange: (v: string) => {
            setFlowName(v);
            if (form.getFieldValue('appID')) {
              const result = getBranchInfo(v);
              form.setFieldsValue(result);
              form.validateFields(['targetBranch']);
            }
          },
        },
      },
      {
        label: i18n.t('App'),
        type: 'select',
        name: 'appID',
        options: apps?.list
          .filter((item) => !item.isExternalRepo)
          .map((app) => ({ name: app.displayName, value: app.id })),
        itemProps: {
          showSearch: true,
          onChange: (v: number) => {
            const { name } = apps?.list?.find((item) => item.id === v)!;
            const result = getBranchInfo(form.getFieldValue('flowName'));
            form.setFieldsValue(result);
            getBranches
              .fetch({
                projectName,
                appName: name,
              })
              .then(() => {
                form.validateFields(['targetBranch']);
              });
          },
        },
      },
      {
        label: i18n.t('dop:target branch'),
        type: 'select',
        name: 'targetBranch',
        options: branches
          ?.filter((item) => item.name === flowName)
          .map((branch) => ({ name: branch.targetBranch, value: branch.targetBranch })),
        itemProps: {
          onChange: (v: string) => {
            const { name } = branches.find((item) => item.targetBranch === v)!;
            const { sourceBranch } = getBranchInfo(name);
            form.setFieldsValue({ sourceBranch });
          },
        },
        rules: [
          {
            validator: (_rule: any, value: string) => {
              if (allBranch?.some((item) => item.name === value)) {
                return Promise.resolve();
              }
              return Promise.reject(new Error(i18n.t('dop:The branch does not exist, Please create the branch first')));
            },
          },
        ],
      },
      {
        label: i18n.t('dop:Change branch'),
        name: 'sourceBranch',
        getComp: () => <AutoComplete options={allBranch?.map((item) => ({ value: item.name }))} />,
      },
    ];
    return (
      <div className="w-[400px]">
        <RenderPureForm form={form} list={list} />
        <div className="flex justify-end">
          <Button className="mr-4" onClick={handleCancel}>
            {i18n.t('Cancel')}
          </Button>
          <Button type="primary" onClick={handleOk}>
            {i18n.t('OK')}
          </Button>
        </div>
      </div>
    );
  }, [form, apps, branches, iteration]);
  return (
    <Popover trigger={['click']} content={content} visible={visible} onVisibleChange={setVisible}>
      <div
        className="h-7 mr-2 p-1 rounded-sm text-sub hover:text-default hover:bg-default-04 cursor-pointer"
        onClick={() => {
          setVisible(true);
        }}
      >
        <ErdaIcon type="plus" size={20} />
      </div>
    </Popover>
  );
}
Example #23
Source File: palette.tsx    From jmix-frontend with Apache License 2.0 4 votes vote down vote up
palette = () => (
  <Palette>
    <Category name="Text">
      <Component name="Formatted Message">
        <Variant>
          <FormattedMessage />
        </Variant>
      </Component>
      <Component name="Heading">
        <Variant name="h1">
          <Typography.Title></Typography.Title>
        </Variant>
        <Variant name="h2">
          <Typography.Title level={2}></Typography.Title>
        </Variant>
        <Variant name="h3">
          <Typography.Title level={3}></Typography.Title>
        </Variant>
        <Variant name="h4">
          <Typography.Title level={4}></Typography.Title>
        </Variant>
        <Variant name="h5">
          <Typography.Title level={5}></Typography.Title>
        </Variant>
      </Component>
      <Component name="Text">
        <Variant>
          <Typography.Text></Typography.Text>
        </Variant>
        <Variant name="Secondary">
          <Typography.Text type="secondary"></Typography.Text>
        </Variant>
        <Variant name="Success">
          <Typography.Text type="success"></Typography.Text>
        </Variant>
        <Variant name="Warning">
          <Typography.Text type="warning"></Typography.Text>
        </Variant>
        <Variant name="Danger">
          <Typography.Text type="danger"></Typography.Text>
        </Variant>
        <Variant name="Disabled">
          <Typography.Text disabled></Typography.Text>
        </Variant>
      </Component>
    </Category>
    <Category name="Layout">
      <Component name="Divider">
        <Variant>
          <Divider />
        </Variant>
      </Component>

      <Component name="Grid">
        <Variant name="Simple Row">
          <Row></Row>
        </Variant>
        <Variant name="Two columns">
          <Row>
            <Col span={12}></Col>
            <Col span={12}></Col>
          </Row>
        </Variant>
        <Variant name="Three columns">
          <Row>
            <Col span={8}></Col>
            <Col span={8}></Col>
            <Col span={8}></Col>
          </Row>
        </Variant>
      </Component>

      <Component name="Space">
        <Variant>
          <Space />
        </Variant>
        <Variant name="Small">
          <Space size={"small"} />
        </Variant>
        <Variant name="Large">
          <Space size={"large"} />
        </Variant>
      </Component>
    </Category>
    <Category name="Controls">
      <Component name="Autocomplete">
        <Variant>
          <AutoComplete placeholder="input here" />
        </Variant>
      </Component>

      <Component name="Button">
        <Variant>
          <Button></Button>
        </Variant>
        <Variant name="Primary">
          <Button type="primary"></Button>
        </Variant>
        <Variant name="Link">
          <Button type="link"></Button>
        </Variant>
        <Variant name="Dropdown">
          <Dropdown
            trigger={["click"]}
            overlay={
              <Menu>
                <Menu.Item></Menu.Item>
                <Menu.Item></Menu.Item>
                <Menu.Item></Menu.Item>
              </Menu>
            }
          >
            <Button></Button>
          </Dropdown>
        </Variant>
      </Component>

      <Component name="Checkbox">
        <Variant>
          <Checkbox />
        </Variant>
      </Component>

      <Component name="Switch">
        <Variant>
          <Switch />
        </Variant>
      </Component>

      <Component name="Radio Group">
        <Variant>
          <Radio.Group>
            <Radio value={1}>A</Radio>
            <Radio value={2}>B</Radio>
            <Radio value={3}>C</Radio>
            <Radio value={4}>D</Radio>
          </Radio.Group>
        </Variant>
        <Variant name="Button">
          <Radio.Group>
            <Radio.Button value={1}>A</Radio.Button>
            <Radio.Button value={2}>B</Radio.Button>
            <Radio.Button value={3}>C</Radio.Button>
            <Radio.Button value={4}>D</Radio.Button>
          </Radio.Group>
        </Variant>
      </Component>

      <Component name="DatePicker">
        <Variant>
          <DatePicker />
        </Variant>
        <Variant name="Range">
          <DatePicker.RangePicker />
        </Variant>
      </Component>

      <Component name="TimePicker">
        <Variant>
          <TimePicker />
        </Variant>
        <Variant name="Range">
          <TimePicker.RangePicker />
        </Variant>
      </Component>

      <Component name="Input">
        <Variant>
          <Input />
        </Variant>
        <Variant name="Number">
          <InputNumber />
        </Variant>
      </Component>

      <Component name="Select">
        <Variant>
          <Select defaultValue="1">
            <Select.Option value="1">1</Select.Option>
            <Select.Option value="2">2</Select.Option>
          </Select>
        </Variant>
        <Variant name="Multiple">
          <Select defaultValue={["1"]} mode="multiple" allowClear>
            <Select.Option value="1">1</Select.Option>
            <Select.Option value="2">2</Select.Option>
          </Select>
        </Variant>
      </Component>

      <Component name="Link">
        <Variant>
          <Typography.Link href="" target="_blank"></Typography.Link>
        </Variant>
      </Component>

      <Component name="Slider">
        <Variant>
          <Slider defaultValue={30} />
        </Variant>
        <Variant name="Range">
          <Slider range defaultValue={[20, 50]} />
        </Variant>
      </Component>
    </Category>
    <Category name="Data Display">
      <Component name="Field">
        <Variant>
          <Field
            entityName={ENTITY_NAME}
            disabled={readOnlyMode}
            propertyName=""
            formItemProps={{
              style: { marginBottom: "12px" }
            }}
          />
        </Variant>
      </Component>
      <Component name="Card">
        <Variant>
          <Card />
        </Variant>
        <Variant name="With Title">
          <Card>
            <Card title="Card title">
              <p>Card content</p>
            </Card>
          </Card>
        </Variant>
        <Variant name="My custom card">
          <Card>
            <Card title="Card title">
              <p>Card content</p>
              <Avatar />
            </Card>
          </Card>
        </Variant>
      </Component>
      <Component name="Tabs">
        <Variant>
          <Tabs defaultActiveKey="1">
            <Tabs.TabPane tab="Tab 1" key="1">
              Content of Tab Pane 1
            </Tabs.TabPane>
            <Tabs.TabPane tab="Tab 2" key="2">
              Content of Tab Pane 2
            </Tabs.TabPane>
            <Tabs.TabPane tab="Tab 3" key="3">
              Content of Tab Pane 3
            </Tabs.TabPane>
          </Tabs>
        </Variant>
        <Variant name="Tab Pane">
          <Tabs.TabPane></Tabs.TabPane>
        </Variant>
      </Component>
      <Component name="Collapse">
        <Variant>
          <Collapse defaultActiveKey="1">
            <Collapse.Panel
              header="This is panel header 1"
              key="1"
            ></Collapse.Panel>
            <Collapse.Panel
              header="This is panel header 2"
              key="2"
            ></Collapse.Panel>
            <Collapse.Panel
              header="This is panel header 3"
              key="3"
            ></Collapse.Panel>
          </Collapse>
        </Variant>
      </Component>
      <Component name="Image">
        <Variant>
          <Image width={200} src="" />
        </Variant>
      </Component>
      <Component name="Avatar">
        <Variant>
          <Avatar icon={<UserOutlined />} />
        </Variant>
        <Variant name="Image">
          <Avatar src="https://joeschmoe.io/api/v1/random" />
        </Variant>
      </Component>
      <Component name="Badge">
        <Variant>
          <Badge count={1}></Badge>
        </Variant>
      </Component>
      <Component name="Statistic">
        <Variant>
          <Statistic title="Title" value={112893} />
        </Variant>
      </Component>
      <Component name="Alert">
        <Variant name="Success">
          <Alert message="Text" type="success" />
        </Variant>
        <Variant name="Info">
          <Alert message="Text" type="info" />
        </Variant>
        <Variant name="Warning">
          <Alert message="Text" type="warning" />
        </Variant>
        <Variant name="Error">
          <Alert message="Text" type="error" />
        </Variant>
      </Component>
      <Component name="List">
        <Variant>
          <List
            bordered
            dataSource={[]}
            renderItem={item => <List.Item></List.Item>}
          />
        </Variant>
      </Component>
    </Category>
    <Category name="Icons">
      <Component name="Arrow">
        <Variant name="Up">
          <ArrowUpOutlined />
        </Variant>
        <Variant name="Down">
          <ArrowDownOutlined />
        </Variant>
        <Variant name="Left">
          <ArrowLeftOutlined />
        </Variant>
        <Variant name="Right">
          <ArrowRightOutlined />
        </Variant>
      </Component>
      <Component name="Question">
        <Variant>
          <QuestionOutlined />
        </Variant>
        <Variant name="Circle">
          <QuestionCircleOutlined />
        </Variant>
      </Component>
      <Component name="Plus">
        <Variant>
          <PlusOutlined />
        </Variant>
        <Variant name="Circle">
          <PlusCircleOutlined />
        </Variant>
      </Component>
      <Component name="Info">
        <Variant>
          <InfoOutlined />
        </Variant>
        <Variant name="Circle">
          <InfoCircleOutlined />
        </Variant>
      </Component>
      <Component name="Exclamation">
        <Variant>
          <ExclamationOutlined />
        </Variant>
        <Variant name="Circle">
          <ExclamationCircleOutlined />
        </Variant>
      </Component>
      <Component name="Close">
        <Variant>
          <CloseOutlined />
        </Variant>
        <Variant name="Circle">
          <CloseCircleOutlined />
        </Variant>
      </Component>
      <Component name="Check">
        <Variant>
          <CheckOutlined />
        </Variant>
        <Variant name="Circle">
          <CheckCircleOutlined />
        </Variant>
      </Component>
      <Component name="Edit">
        <Variant>
          <EditOutlined />
        </Variant>
      </Component>
      <Component name="Copy">
        <Variant>
          <CopyOutlined />
        </Variant>
      </Component>
      <Component name="Delete">
        <Variant>
          <DeleteOutlined />
        </Variant>
      </Component>
      <Component name="Bars">
        <Variant>
          <BarsOutlined />
        </Variant>
      </Component>
      <Component name="Bell">
        <Variant>
          <BellOutlined />
        </Variant>
      </Component>
      <Component name="Clear">
        <Variant>
          <ClearOutlined />
        </Variant>
      </Component>
      <Component name="Download">
        <Variant>
          <DownloadOutlined />
        </Variant>
      </Component>
      <Component name="Upload">
        <Variant>
          <UploadOutlined />
        </Variant>
      </Component>
      <Component name="Sync">
        <Variant>
          <SyncOutlined />
        </Variant>
      </Component>
      <Component name="Save">
        <Variant>
          <SaveOutlined />
        </Variant>
      </Component>
      <Component name="Search">
        <Variant>
          <SearchOutlined />
        </Variant>
      </Component>
      <Component name="Settings">
        <Variant>
          <SettingOutlined />
        </Variant>
      </Component>
      <Component name="Paperclip">
        <Variant>
          <PaperClipOutlined />
        </Variant>
      </Component>
      <Component name="Phone">
        <Variant>
          <PhoneOutlined />
        </Variant>
      </Component>
      <Component name="Mail">
        <Variant>
          <MailOutlined />
        </Variant>
      </Component>
      <Component name="Home">
        <Variant>
          <HomeOutlined />
        </Variant>
      </Component>
      <Component name="Contacts">
        <Variant>
          <ContactsOutlined />
        </Variant>
      </Component>
      <Component name="User">
        <Variant>
          <UserOutlined />
        </Variant>
        <Variant name="Add">
          <UserAddOutlined />
        </Variant>
        <Variant name="Remove">
          <UserDeleteOutlined />
        </Variant>
      </Component>
      <Component name="Team">
        <Variant>
          <TeamOutlined />
        </Variant>
      </Component>
    </Category>
    <Category name="Screens">
      <Component name="ExampleCustomScreen">
        <Variant>
          <ExampleCustomScreen />
        </Variant>
      </Component>
      <Component name="CustomEntityFilterTest">
        <Variant>
          <CustomEntityFilterTest />
        </Variant>
      </Component>
      <Component name="CustomFormControls">
        <Variant>
          <CustomFormControls />
        </Variant>
      </Component>
      <Component name="CustomDataDisplayComponents">
        <Variant>
          <CustomDataDisplayComponents />
        </Variant>
      </Component>
      <Component name="CustomAppLayouts">
        <Variant>
          <CustomAppLayouts />
        </Variant>
      </Component>
      <Component name="CustomControls">
        <Variant>
          <CustomControls />
        </Variant>
      </Component>
      <Component name="ErrorBoundaryTests">
        <Variant>
          <ErrorBoundaryTests />
        </Variant>
      </Component>
      <Component name="TestBlankScreen">
        <Variant>
          <TestBlankScreen />
        </Variant>
      </Component>
      <Component name="CarEditor">
        <Variant>
          <CarEditor />
        </Variant>
      </Component>
      <Component name="CarBrowserCards">
        <Variant>
          <CarBrowserCards />
        </Variant>
      </Component>
      <Component name="CarBrowserList">
        <Variant>
          <CarBrowserList />
        </Variant>
      </Component>
      <Component name="CarBrowserTable">
        <Variant>
          <CarBrowserTable />
        </Variant>
      </Component>
      <Component name="CarCardsGrid">
        <Variant>
          <CarCardsGrid />
        </Variant>
      </Component>
      <Component name="FavoriteCars">
        <Variant>
          <FavoriteCars />
        </Variant>
      </Component>
      <Component name="CarCardsWithDetails">
        <Variant>
          <CarCardsWithDetails />
        </Variant>
      </Component>
      <Component name="CarTableWithFilters">
        <Variant>
          <CarTableWithFilters />
        </Variant>
      </Component>
      <Component name="CarMasterDetail">
        <Variant>
          <CarMasterDetail />
        </Variant>
      </Component>
      <Component name="FormWizardCompositionO2O">
        <Variant>
          <FormWizardCompositionO2O />
        </Variant>
      </Component>
      <Component name="FormWizardEditor">
        <Variant>
          <FormWizardEditor />
        </Variant>
      </Component>
      <Component name="FormWizardBrowserTable">
        <Variant>
          <FormWizardBrowserTable />
        </Variant>
      </Component>
      <Component name="CarMultiSelectionTable">
        <Variant>
          <CarMultiSelectionTable />
        </Variant>
      </Component>
      <Component name="DatatypesTestEditor">
        <Variant>
          <DatatypesTestEditor />
        </Variant>
      </Component>
      <Component name="DatatypesTestBrowserCards">
        <Variant>
          <DatatypesTestBrowserCards />
        </Variant>
      </Component>
      <Component name="DatatypesTestBrowserList">
        <Variant>
          <DatatypesTestBrowserList />
        </Variant>
      </Component>
      <Component name="DatatypesTestBrowserTable">
        <Variant>
          <DatatypesTestBrowserTable />
        </Variant>
      </Component>
      <Component name="DatatypesTestCards">
        <Variant>
          <DatatypesTestCards />
        </Variant>
      </Component>
      <Component name="AssociationO2OEditor">
        <Variant>
          <AssociationO2OEditor />
        </Variant>
      </Component>
      <Component name="AssociationO2OBrowserTable">
        <Variant>
          <AssociationO2OBrowserTable />
        </Variant>
      </Component>
      <Component name="AssociationO2MEditor">
        <Variant>
          <AssociationO2MEditor />
        </Variant>
      </Component>
      <Component name="AssociationO2MBrowserTable">
        <Variant>
          <AssociationO2MBrowserTable />
        </Variant>
      </Component>
      <Component name="AssociationM2OEditor">
        <Variant>
          <AssociationM2OEditor />
        </Variant>
      </Component>
      <Component name="AssociationM2OBrowserTable">
        <Variant>
          <AssociationM2OBrowserTable />
        </Variant>
      </Component>
      <Component name="AssociationM2MEditor">
        <Variant>
          <AssociationM2MEditor />
        </Variant>
      </Component>
      <Component name="AssociationM2MBrowserTable">
        <Variant>
          <AssociationM2MBrowserTable />
        </Variant>
      </Component>
      <Component name="CompositionO2OEditor">
        <Variant>
          <CompositionO2OEditor />
        </Variant>
      </Component>
      <Component name="CompositionO2OBrowserTable">
        <Variant>
          <CompositionO2OBrowserTable />
        </Variant>
      </Component>
      <Component name="CompositionO2MEditor">
        <Variant>
          <CompositionO2MEditor />
        </Variant>
      </Component>
      <Component name="CompositionO2MBrowserTable">
        <Variant>
          <CompositionO2MBrowserTable />
        </Variant>
      </Component>
      <Component name="DeeplyNestedTestEntityEditor">
        <Variant>
          <DeeplyNestedTestEntityEditor />
        </Variant>
      </Component>
      <Component name="DeeplyNestedO2MTestEntityTable">
        <Variant>
          <DeeplyNestedO2MTestEntityTable />
        </Variant>
      </Component>
      <Component name="DeeplyNestedO2MTestEntityEditor">
        <Variant>
          <DeeplyNestedO2MTestEntityEditor />
        </Variant>
      </Component>
      <Component name="IntIdEditor">
        <Variant>
          <IntIdEditor />
        </Variant>
      </Component>
      <Component name="IntIdBrowserTable">
        <Variant>
          <IntIdBrowserTable />
        </Variant>
      </Component>
      <Component name="IntIdBrowserCards">
        <Variant>
          <IntIdBrowserCards />
        </Variant>
      </Component>
      <Component name="IntIdBrowserList">
        <Variant>
          <IntIdBrowserList />
        </Variant>
      </Component>
      <Component name="IntIdentityIdCards">
        <Variant>
          <IntIdentityIdCards />
        </Variant>
      </Component>
      <Component name="IntIdentityIdEditor">
        <Variant>
          <IntIdentityIdEditor />
        </Variant>
      </Component>
      <Component name="IntIdentityIdBrowserTable">
        <Variant>
          <IntIdentityIdBrowserTable />
        </Variant>
      </Component>
      <Component name="IntIdentityIdBrowserCards">
        <Variant>
          <IntIdentityIdBrowserCards />
        </Variant>
      </Component>
      <Component name="IntIdentityIdBrowserList">
        <Variant>
          <IntIdentityIdBrowserList />
        </Variant>
      </Component>
      <Component name="StringIdCards">
        <Variant>
          <StringIdCards />
        </Variant>
      </Component>
      <Component name="StringIdMgtCardsEdit">
        <Variant>
          <StringIdMgtCardsEdit />
        </Variant>
      </Component>
      <Component name="StringIdBrowserCards">
        <Variant>
          <StringIdBrowserCards />
        </Variant>
      </Component>
      <Component name="StringIdBrowserList">
        <Variant>
          <StringIdBrowserList />
        </Variant>
      </Component>
      <Component name="StringIdBrowserTable">
        <Variant>
          <StringIdBrowserTable />
        </Variant>
      </Component>
      <Component name="WeirdStringIdEditor">
        <Variant>
          <WeirdStringIdEditor />
        </Variant>
      </Component>
      <Component name="WeirdStringIdBrowserCards">
        <Variant>
          <WeirdStringIdBrowserCards />
        </Variant>
      </Component>
      <Component name="WeirdStringIdBrowserList">
        <Variant>
          <WeirdStringIdBrowserList />
        </Variant>
      </Component>
      <Component name="WeirdStringIdBrowserTable">
        <Variant>
          <WeirdStringIdBrowserTable />
        </Variant>
      </Component>
      <Component name="BoringStringIdEditor">
        <Variant>
          <BoringStringIdEditor />
        </Variant>
      </Component>
      <Component name="BoringStringIdBrowserTable">
        <Variant>
          <BoringStringIdBrowserTable />
        </Variant>
      </Component>
      <Component name="TrickyIdEditor">
        <Variant>
          <TrickyIdEditor />
        </Variant>
      </Component>
      <Component name="TrickyIdBrowserTable">
        <Variant>
          <TrickyIdBrowserTable />
        </Variant>
      </Component>
    </Category>
  </Palette>
)
Example #24
Source File: index.tsx    From drip-table with MIT License 4 votes vote down vote up
private renderAttributeComponent(schema: DTGComponentPropertySchema, index: number, parentIndex: number) {
    const currentValue = (this.props.value || [])[parentIndex] || {};
    const options = schema['ui:props']?.options as LabeledValue[] || this.props.fieldOptions || [];
    if (schema['ui:type'] === 'radio') {
      return (
        <Radio.Group
          style={{ width: '100%' }}
          defaultValue={schema.default}
          value={currentValue[schema.name]}
          onChange={e => this.changeColumnItem(schema.name, e.target.value, parentIndex)}
        >
          {
            options?.map(
              (option, i) =>
                <Radio key={i} value={option.value}>{ option.label }</Radio>,
            )
          }
        </Radio.Group>
      );
    }
    if (schema['ui:type'] === 'input') {
      return (
        <Input
          style={{ width: '100%' }}
          defaultValue={schema.default as string}
          value={currentValue[schema.name] as string}
          placeholder={schema['ui:props']?.placeholder as string}
          onChange={e => this.changeColumnItem(schema.name, e.target.value, parentIndex)}
        />
      );
    }
    if (schema['ui:type'] === 'text') {
      return (
        <Input.TextArea
          style={{ width: '100%' }}
          autoSize={schema['ui:autoSize']}
          defaultValue={schema.default as string}
          value={currentValue[schema.name] as string}
          placeholder={schema['ui:props']?.placeholder as string}
          onChange={e => this.changeColumnItem(schema.name, e.target.value, parentIndex)}
        />
      );
    }
    if (schema['ui:type'] === 'auto-complete') {
      return (
        <AutoComplete
          style={{ width: '100%' }}
          defaultValue={schema.default as string}
          value={currentValue[schema.name] as string}
          options={options}
          onChange={value => this.changeColumnItem(schema.name, value, parentIndex)}
        />
      );
    }
    if (schema['ui:type'] === 'number') {
      return (
        <InputNumber
          style={{ width: '100%' }}
          min={schema['ui:minium']}
          max={schema['ui:maximum']}
          step={schema['ui:step']}
          defaultValue={Number(schema.default)}
          value={Number(currentValue[schema.name])}
          onChange={value => this.changeColumnItem(schema.name, Number(value), parentIndex)}
        />
      );
    }
    if (schema['ui:type'] === 'switch') {
      const value = typeof currentValue[schema.name] === 'undefined' ? schema.default : currentValue[schema.name];
      return (
        <Switch
          checked={value as boolean}
          checkedChildren={schema['ui:checkedContent']}
          unCheckedChildren={schema['ui:unCheckedContent']}
          onChange={checked => this.changeColumnItem(schema.name, checked, parentIndex)}
        />
      );
    }
    if (schema['ui:type'] === 'select') {
      const formattedValue = (schema['ui:mode'] === 'multiple' || schema['ui:mode'] === 'tags') && !Array.isArray(currentValue[schema.name]) ? [currentValue[schema.name]] : currentValue[schema.name];
      return (
        <Select
          showSearch
          style={{ width: '100%' }}
          mode={schema['ui:mode']}
          defaultValue={schema.default as SelectValue}
          value={formattedValue as SelectValue}
          options={options}
          onChange={value => this.changeColumnItem(schema.name, value, parentIndex)}
        />
      );
    }
    if (schema['ui:type'] === 'array-list') {
      return (
        <ArrayComponent
          theme={this.props.theme}
          schema={schema}
          value={currentValue[schema.name] as Record<string, unknown>[] | undefined}
          onChange={value => this.changeColumnItem(schema.name, value, parentIndex)}
          onValidate={msg => this.props.onValidate?.(msg)}
        />
      );
    }
    return null;
  }
Example #25
Source File: PropertyValue.tsx    From posthog-foss with MIT License 4 votes vote down vote up
export function PropertyValue({
    propertyKey,
    type,
    endpoint = undefined,
    placeholder = undefined,
    style = {},
    bordered = true,
    onSet,
    value,
    operator,
    outerOptions = undefined,
    autoFocus = false,
    allowCustom = true,
}: PropertyValueProps): JSX.Element {
    const isMultiSelect = operator && isOperatorMulti(operator)
    const [input, setInput] = useState(isMultiSelect ? '' : toString(value))
    const [shouldBlur, setShouldBlur] = useState(false)
    const [options, setOptions] = useState({} as Record<string, Option>)
    const autoCompleteRef = useRef<HTMLElement>(null)

    const { formatForDisplay } = useValues(propertyDefinitionsModel)

    // update the input field if passed a new `value` prop
    useEffect(() => {
        if (!value) {
            setInput('')
        } else if (value !== input) {
            const valueObject = options[propertyKey]?.values?.find((v) => v.id === value)
            if (valueObject) {
                setInput(toString(valueObject.name))
            }
        }
    }, [value])

    const loadPropertyValues = useThrottledCallback((newInput) => {
        if (type === 'cohort') {
            return
        }
        const key = propertyKey.split('__')[0]
        setOptions({ ...options, [propertyKey]: { ...options[propertyKey], status: 'loading' } })
        if (outerOptions) {
            setOptions({
                ...options,
                [propertyKey]: {
                    values: [...Array.from(new Set(outerOptions))],
                    status: 'loaded',
                },
            })
        } else {
            api.get(endpoint || 'api/' + type + '/values/?key=' + key + (newInput ? '&value=' + newInput : '')).then(
                (propValues: PropValue[]) => {
                    setOptions({
                        ...options,
                        [propertyKey]: {
                            values: [...Array.from(new Set(propValues))],
                            status: 'loaded',
                        },
                    })
                }
            )
        }
    }, 300)

    function setValue(newValue: PropertyValueProps['value']): void {
        onSet(newValue)
        if (isMultiSelect) {
            setInput('')
        }
    }

    useEffect(() => {
        loadPropertyValues('')
    }, [propertyKey])

    useEffect(() => {
        if (input === '' && shouldBlur) {
            ;(document.activeElement as HTMLElement)?.blur()
            setShouldBlur(false)
        }
    }, [input, shouldBlur])

    const displayOptions = (options[propertyKey]?.values || []).filter(
        (option) => input === '' || matchesLowerCase(input, toString(option?.name))
    )

    const validationError = operator ? getValidationError(operator, value) : null

    const commonInputProps = {
        style: { width: '100%', ...style },
        onSearch: (newInput: string) => {
            setInput(newInput)
            if (!Object.keys(options).includes(newInput) && !(operator && isOperatorFlag(operator))) {
                loadPropertyValues(newInput)
            }
        },
        ['data-attr']: 'prop-val',
        dropdownMatchSelectWidth: 350,
        bordered,
        placeholder,
        allowClear: Boolean(value),
        onKeyDown: (e: React.KeyboardEvent) => {
            if (e.key === 'Escape') {
                setInput('')
                setShouldBlur(true)
                return
            }
            if (!isMultiSelect && e.key === 'Enter') {
                // We have not explicitly selected a dropdown item by pressing the up/down keys; or the ref is unavailable
                if (
                    !autoCompleteRef.current ||
                    autoCompleteRef.current?.querySelectorAll?.('.ant-select-item-option-active')?.length === 0
                ) {
                    setValue(input)
                }
            }
        },
        handleBlur: () => {
            if (input != '') {
                if (Array.isArray(value) && !value.includes(input)) {
                    setValue([...value, ...[input]])
                } else if (!Array.isArray(value)) {
                    setValue(input)
                }
                setInput('')
            }
        },
    }

    const dayJSMightParse = (
        candidateDateTimeValue: string | number | (string | number)[] | null | undefined
    ): candidateDateTimeValue is string | number | undefined =>
        ['string', 'number'].includes(typeof candidateDateTimeValue)

    return (
        <>
            {isMultiSelect ? (
                <SelectGradientOverflow
                    loading={options[propertyKey]?.status === 'loading'}
                    propertyKey={propertyKey}
                    {...commonInputProps}
                    autoFocus={autoFocus}
                    value={value === null ? [] : value}
                    mode="multiple"
                    showSearch
                    onChange={(val, payload) => {
                        if (Array.isArray(payload) && payload.length > 0) {
                            setValue(val)
                        } else if (payload instanceof Option) {
                            setValue(payload?.value ?? [])
                        } else {
                            setValue([])
                        }
                    }}
                >
                    {input && !displayOptions.some(({ name }) => input.toLowerCase() === toString(name).toLowerCase()) && (
                        <Select.Option key="specify-value" value={input} className="ph-no-capture">
                            Specify: {formatForDisplay(propertyKey, input)}
                        </Select.Option>
                    )}
                    {displayOptions.map(({ name: _name }, index) => {
                        const name = toString(_name)
                        return (
                            <Select.Option
                                key={name}
                                value={name}
                                data-attr={'prop-val-' + index}
                                className="ph-no-capture"
                                title={name}
                            >
                                {name === '' ? <i>(empty string)</i> : formatForDisplay(propertyKey, name)}
                            </Select.Option>
                        )
                    })}
                </SelectGradientOverflow>
            ) : operator && isOperatorDate(operator) ? (
                <>
                    <DatePicker
                        {...commonInputProps}
                        inputReadOnly={true}
                        className={'filter-date-picker'}
                        dropdownClassName={'filter-date-picker-dropdown'}
                        format="YYYY-MM-DD HH:mm:ss"
                        showTime={true}
                        showNow={false}
                        value={dayJSMightParse(value) ? dayjs(value) : null}
                        onOk={(selectedDate) => {
                            setValue(selectedDate.format('YYYY-MM-DD HH:MM:ss'))
                        }}
                        getPopupContainer={(trigger: Element | null) => {
                            const container = trigger?.parentElement?.parentElement?.parentElement
                            return container ?? document.body
                        }}
                    />
                </>
            ) : (
                <AutoComplete
                    {...commonInputProps}
                    autoFocus={autoFocus}
                    value={input}
                    onClear={() => {
                        setInput('')
                        setValue('')
                    }}
                    onChange={(val) => {
                        setInput(toString(val))
                    }}
                    onSelect={(val, option) => {
                        setInput(option.title)
                        setValue(toString(val))
                    }}
                    onKeyDown={(e) => {
                        if (e.key === 'Enter') {
                            setInput(toString(input))
                            setValue(toString(input))
                        }
                    }}
                    ref={autoCompleteRef}
                >
                    {[
                        ...(input && allowCustom && !displayOptions.some(({ name }) => input === toString(name))
                            ? [
                                  <AutoComplete.Option key="@@@specify-value" value={input} className="ph-no-capture">
                                      Specify: {input}
                                  </AutoComplete.Option>,
                              ]
                            : []),
                        ...displayOptions.map(({ name: _name, id }, index) => {
                            const name = toString(_name)
                            return (
                                <AutoComplete.Option
                                    key={id ? toString(id) : name}
                                    value={id ? toString(id) : name}
                                    data-attr={'prop-val-' + index}
                                    className="ph-no-capture"
                                    title={name}
                                >
                                    {name}
                                </AutoComplete.Option>
                            )
                        }),
                    ]}
                </AutoComplete>
            )}
            {validationError && <p className="text-danger">{validationError}</p>}
        </>
    )
}
Example #26
Source File: index.tsx    From antdp with MIT License 4 votes vote down vote up
QuickForm: QuickFormComponent = (props, ref) => {
  const {
    collapseAttributes,
    panelAttributes,
    visible = false,
    type = 'cardform',
    extra,
    formDatas,
    colspan = 3,
    header,
    defaultFormLayout = 'vertical',
    defaultFormItemLayout = formDefaultFormItemLayout,
    size = 'default',
    formHide,
    initialHide,
    ...otherProps
  } = props;

  const [hide] = useFormItemHide(formHide)
  hide.setInitialValues(initialHide || {}, true)

  const HideFormItemDoM = []; // 隐藏的表单
  const FormItemDoM = [];
  let rowcolspan: string | any; // col 里的布局
  let formitemlayout: string | any; // formitem 的布局

  for (var i = 0; i < formDatas.length; i++) {
    if (formDatas[i].hideInForm) {
      HideFormItemDoM.push(formDatas[i]);
    } else {
      FormItemDoM.push(formDatas[i]);
    }
  }
  // 计算一个row里排几个表单;
  const result = [];
  for (let i = 0, j = FormItemDoM.length; i < j; i++) {
    if (FormItemDoM[i].full) {
      result.push(FormItemDoM.slice(i, i + 1));
    } else {
      if (FormItemDoM[i + 1] && FormItemDoM[i + 1].full) {
        result.push(FormItemDoM.slice(i, i + 1));
      } else if (FormItemDoM[i].defaultcolspan) {
        result.push(FormItemDoM.slice(i, i + FormItemDoM[i].defaultcolspan));
        i = i + FormItemDoM[i].defaultcolspan - 1;
      } else {
        result.push(FormItemDoM.slice(i, i + colspan));
        i = i + colspan - 1;
      }
    }
  }
  // 渲染成表单;
  const CollapseFormDoM = (item: any, idx: React.Key | null | undefined) => {
    const {
      label,
      name,
      attributes,
      type,
      options,
      onlyimg,
      defaultFormItemLayout,
      full,
      defaultRowColspan,
      hideInForm,
      descItem,
      render,
      // 用于判断是否需要进行隐藏显示 (在组件外层包裹一层组件用于控制item显示和隐藏)
      isHide,
      ...otherts
    } = item;
    const dataList = options || [];
    const optionDatas =
      dataList &&
      dataList.length > 0 &&
      dataList.map(
        (
          { value, label, ...others }: any,
          _idx: React.Key | null | undefined,
        ) => {
          if (type === 'select' || type === 'Select') {
            return (
              <Option value={value} key={_idx} {...others}>
                {label}
              </Option>
            );
          } else if (type === 'radio' || type === 'Radio') {
            return (
              <Radio.Button value={value} key={_idx} {...others}>
                {label}
              </Radio.Button>
            );
          }
        },
      );
    const selectOption = optionDatas ? optionDatas : [];
    const rowcolspan_num = [
      colLayout_one,
      colLayout_two,
      colLayout_third,
      colLayout_fourth,
    ];
    const formitemlayout_num = [
      fromItemLayout_conspan_one,
      fromItemLayout_conspan_two,
      fromItemLayout_conspan_third,
      fromItemLayout_conspan_fourth,
    ];
    if (colspan && full) {
      rowcolspan = colLayout_one;
      if (colspan === 3 || colspan === 4) {
        if (props.defaultFormItemLayout) {
          // 如果FormCollapse组件上带有defaulFormItemLayout参数
          formitemlayout = props.defaultFormItemLayout;
          // eslint-disable-next-line max-depth
          if (item.defaultFormItemLayout || item.defaultRowColspan) {
            // 如果FormCollapse组件内部的某个小组件带有defaulFormItemLayout参数
            formitemlayout = item.defaultFormItemLayout;
            rowcolspan = item.defaultRowColspan; // 单独的表单col 布局
          }
        } else if (item.defaultFormItemLayout || item.defaultRowColspan) {
          //FormCollapse组件内部只有某个小组件带了defaulFormItemLayout参数
          formitemlayout = item.defaultFormItemLayout;
          rowcolspan = item.defaultRowColspan; // 单独的表单col 布局
        } else {
          formitemlayout = fromItemLayout_third_row;
        }
      } else {
        formitemlayout = fromItemLayout_two_row;
      }
    } else {
      rowcolspan = rowcolspan_num[colspan - 1];
      if (props.defaultFormItemLayout) {
        formitemlayout = props.defaultFormItemLayout;
        if (item.defaultFormItemLayout || item.defaultRowColspan) {
          // 如果FormCollapse组件内部的某个小组件带有defaultFormItemLayout参数
          formitemlayout = item.defaultFormItemLayout;
          rowcolspan = item.defaultRowColspan; // 单独的表单col 布局
        }
      } else if (item.defaultFormItemLayout || item.defaultRowColspan) {
        formitemlayout =
          item.defaultFormItemLayout || formitemlayout_num[colspan - 1];
        rowcolspan = item.defaultRowColspan; // 单独的表单col 布局
      } else {
        formitemlayout = formitemlayout_num[colspan - 1];
      }
    }

    // 上传图片的按钮展示
    const uploadButtonDom = () => {
      if (item.attributes.listType === 'picture-card') {
        if (item.attributes.imageUrl && item.attributes.imageUrl !== '') {
          return (
            <img
              src={item.attributes.imageUrl}
              alt="avatar"
              style={{ width: '100%' }}
            />
          );
        } else if (item.attributes.fileList) {
          // 上传的图片大于或等于8张时 并且 没有onlyimg参数,显示icon上传按钮
          if (item.attributes.fileList.length >= 8 && !onlyimg) {
            return (
              <div>
                {item.attributes.loading === 'loading' ? (
                  <LoadingOutlined />
                ) : (
                  <PlusOutlined />
                )}
                <div className="ant-upload-text">上传</div>
              </div>
            );
            // 上传的图片大于或等于maxCount张时 并且 有onlyimg参数,不显示上传按钮
          } else if (item.attributes.maxCount && item.attributes.fileList.length >= item.attributes.maxCount && onlyimg) {
            return null;
          }
          return (
            <div>
              {item.attributes.loading === 'loading' ? (
                <LoadingOutlined />
              ) : (
                <PlusOutlined />
              )}
              <div className="ant-upload-text">上传</div>
            </div>
          );
        }
      } else {
        return (
          <div>
            <Button>
              <UploadOutlined />
              上传
            </Button>
          </div>
        );
      }
    };
    let renderItem = (
      <Col
        key={idx}
        style={{
          display: item.hideInForm ? 'none' : 'block',
          padding:
            defaultFormLayout && defaultFormLayout === 'vertical'
              ? '0px 12px 8px 12px'
              : '0',
        }}
        className={
          defaultFormLayout && defaultFormLayout === 'vertical'
            ? 'antdp-FormCol'
            : ''
        }
        {...rowcolspan}
      >
        <FormItem
          className="antdp-FormItem"
          colon={false}
          label={label}
          name={name}
          {...(defaultFormLayout && defaultFormLayout === 'vertical'
            ? null
            : formitemlayout)}
          {...otherts}
        >
          {name ? (
            (() => {
              // 组件基础参数
              const componentprams = {
                size: size ? size : 'small',
                ...attributes,
              };
              if (type === 'select' || type === 'Select') {
                return (
                  <Select
                    dropdownMatchSelectWidth={false}
                    allowClear
                    placeholder={
                      attributes && attributes.disabled ? '' : `请选择${label} `
                    }
                    {...componentprams}
                  >
                    {selectOption}
                  </Select>
                );
              } else if (type === 'radio' || type === 'Radio') {
                return (
                  <Radio.Group size={size ? size : 'small'} {...attributes}>
                    {selectOption}
                  </Radio.Group>
                );
              } else if (type === 'datePicker' || type === 'DatePicker') {
                return (
                  <DatePicker
                    locale={locale}
                    style={{ width: '100%' }}
                    placeholder={
                      attributes && attributes.disabled ? '' : `请选择${label} `
                    }
                    {...componentprams}
                  />
                );
              } else if (type === 'monthPicker' || type === 'MonthPicker') {
                return (
                  <MonthPicker
                    locale={locale}
                    style={{ width: '100%' }}
                    placeholder={
                      attributes && attributes.disabled ? '' : `请选择${label} `
                    }
                    {...componentprams}
                  />
                );
              } else if (type === 'rangePicker' || type === 'RangePicker') {
                return (
                  <RangePicker
                    locale={locale}
                    style={{ width: '100%' }}
                    {...componentprams}
                  />
                );
              } else if (
                type === 'timepicker' ||
                type === 'timePicker' ||
                type === 'TimePicker'
              ) {
                return (
                  <TimePicker
                    locale={locale}
                    style={{ width: '100%' }}
                    placeholder={
                      attributes && attributes.disabled ? '' : `请选择${label} `
                    }
                    {...componentprams}
                  />
                );
              } else if (type === 'cascader' || type === 'Cascader') {
                return (
                  <Cascader
                    placeholder={
                      attributes && attributes.disabled ? '' : `请选择${label} `
                    }
                    {...componentprams}
                  />
                );
              } else if (type === 'textarea' || type === 'TextArea') {
                return (
                  <Input.TextArea
                    placeholder={
                      attributes && attributes.disabled ? '' : `请输入${label} `
                    }
                    {...componentprams}
                  />
                );
              } else if (type === 'inputNumber' || type === 'InputNumber') {
                return (
                  <InputNumber
                    placeholder={
                      attributes && attributes.disabled ? '' : `请输入${label} `
                    }
                    style={{ width: '100%' }}
                    {...componentprams}
                  />
                );
              } else if (type === 'treeSelect' || type === 'TreeSelect') {
                return (
                  <TreeSelect
                    placeholder={
                      attributes && attributes.disabled ? '' : `请选择${label} `
                    }
                    {...componentprams}
                  />
                );
              } else if (type === 'checkbox' || type === 'Checkbox') {
                if (
                  (item.options && item.options.length > 0) ||
                  (item.option && item.option.length > 0)
                ) {
                  return (
                    <Checkbox.Group
                      options={item.options || item.option}
                      {...attributes}
                    />
                  );
                }
                return (
                  <Checkbox {...attributes}>
                    {label || item.checkboxLable}
                  </Checkbox>
                );
              } else if (type === 'UploadGrid' || type === 'uploadGrid') {
                return (
                  <UploadGrid {...attributes}>{uploadButtonDom()}</UploadGrid>
                );
              } else if (type === 'autoComplete' || type === 'AutoComplete') {
                return (
                  <AutoComplete
                    placeholder={
                      attributes && attributes.disabled ? '' : `请输入${label} `
                    }
                    {...componentprams}
                  />
                );
              } else if (type === 'Password') {
                return (
                  <Input.Password
                    placeholder={
                      attributes && attributes.disabled ? '' : `请输入${label} `
                    }
                    {...componentprams}
                  />
                );
              } else if (type === 'inputCount' || type === 'InputCount') {
                return (
                  <InputCount
                    placeholder={
                      attributes && attributes.disabled ? '' : `请输入${label} `
                    }
                    {...attributes}
                  />
                );
              } else if (type === 'render') {
                return render && render
              } else {
                if (
                  (attributes && attributes.type === 'Search') ||
                  type === 'InputSearch'
                ) {
                  const suffix = (
                    <AudioOutlined
                      style={{
                        fontSize: 16,
                        color: '#fff',
                      }}
                    />
                  );
                  return (
                    <Search
                      suffix={suffix}
                      placeholder={
                        attributes && attributes.disabled
                          ? ''
                          : `请输入${label} `
                      }
                      {...componentprams}
                    />
                  );
                }
                return (
                  <Input
                    placeholder={
                      attributes && attributes.disabled ? '' : `请输入${label} `
                    }
                    {...componentprams}
                  />
                );
              }
            })()
          ) : (
            <Input
              placeholder={
                attributes && attributes.disabled ? '' : `请输入${label} `
              }
              size={size}
              {...attributes}
            />
          )}
        </FormItem>
      </Col>
    )

    if (isHide && name) {
      return (
        <Hide key={idx} name={name}>
          {renderItem}
        </Hide>
      );
    }


    return renderItem;
  };
  // 隐藏的表单集合
  const hideCollapseForm = HideFormItemDoM.map((item, idx) =>
    CollapseFormDoM(item, idx),
  );
  // 表单集合
  const CollapseForm = result.map((it, indix) => {
    return (
      <Row key={indix}>
        {it.map((item, idx) => {
          return CollapseFormDoM(item, idx);
        })}
      </Row>
    );
  });
  // Form+表单集合
  const FormDom = (
    <HideContext.Provider value={hide} >
      <ConfigProvider locale={zhCN}>
        <Form
          layout={defaultFormLayout ? defaultFormLayout : 'horizontal'}
          ref={ref}
          {...(defaultFormLayout && defaultFormLayout === 'vertical'
            ? null
            : formitemlayout)}
          {...otherProps}
        >
          <Row>{hideCollapseForm}</Row>
          <div>{CollapseForm}</div>
        </Form>
      </ConfigProvider>
    </HideContext.Provider>
  );
  // type 为 modal时没有折叠,没有标题,直接显示form表单内容
  if (type === 'modal') {
    return <div style={{ margin: -10 }}>{FormDom}</div>
  }
  // type 为CardPro  带标题
  if (type === 'CardPro') {
    return (
      <CardPro title={header}>
        <div className="antdp-FormBox">{FormDom}</div>
      </CardPro>
    );
  }
  // type 为cardform 时 显示表单,分割线 分离每个表单
  if (type === 'cardform') {
    return (
      <div>
        <h3 className="antdp-FormTitle">{header}</h3>
        {FormDom}
        <Divider type="horizontal" className="antdp-FormDivider" />
      </div>
    );
  }
  return (
    <Collapse
      defaultActiveKey={!visible ? ['1'] : ''}
      {...collapseAttributes}
      className="antdp-mb10"
    >
      <Panel header={header} key="1" {...panelAttributes} extra={extra}>
        {FormDom}
      </Panel>
    </Collapse>
  );
}
Example #27
Source File: Management.tsx    From nodestatus with MIT License 4 votes vote down vote up
Management: FC = () => {
  const [regionResult, setRegionResult] = useState<string[]>([]);
  const [state, dispatch] = useReducer(reducer, initialState);
  const { data, mutate } = useSWR<IResp<IServer[]>>('/api/servers');

  const [form] = Form.useForm<IServer & { password: string }>();
  const { confirm } = Modal;
  const dataSource = data?.data!;

  const handleModify = useCallback(() => {
    const data = form.getFieldsValue();
    axios.put<IResp>('/api/servers', { username: state.currentNode, data }).then(res => {
      notify('Success', res.data.msg, 'success');
      dispatch({ type: 'resetState', payload: { form, mutate } });
    });
  }, [state.currentNode, form, mutate]);

  const handleCreate = useCallback(() => {
    const data = form.getFieldsValue();
    axios.post<IResp>('/api/servers', { ...data }).then(res => {
      notify('Success', res.data.msg, 'success');
      dispatch({ type: 'resetState', payload: { form, mutate } });
    });
  }, [form, mutate]);

  const handleDelete = useCallback((username: string) => {
    axios.delete<IResp>(`/api/servers/${username}`).then(res => {
      notify('Success', res.data.msg, 'success');
      dispatch({ type: 'resetState', payload: { form, mutate } });
    });
  }, [form, mutate]);

  const handleSortOrder = useCallback((order: number[]) => {
    axios.put<IResp>('/api/servers/order', { order }).then(res => {
      notify('Success', res.data.msg, 'success');
      dispatch({ type: 'resetState', payload: { form, mutate } });
    });
  }, [form, mutate]);

  const columns = useMemo<ColumnsType<IServer>>(() => [
    {
      title: 'SORT',
      dataIndex: 'sort',
      width: 30,
      align: 'center',
      render: () => undefined
    },
    {
      title: 'SERVER',
      dataIndex: 'server',
      align: 'center',
      render(_, record) {
        return (
          <div className="flex items-center text-sm">
            <svg viewBox="0 0 100 100" className="mr-3 block h-12 w-12">
              <use xlinkHref={`#${record.region}`} />
            </svg>
            <div className="whitespace-nowrap">
              <p className="font-semibold">{record.name}</p>
              <p className="text-left text-xs text-gray-600">{record.location}</p>
            </div>
          </div>
        );
      }
    },
    {
      title: 'USERNAME',
      dataIndex: 'username',
      align: 'center'
    },
    {
      title: 'TYPE',
      dataIndex: 'type',
      align: 'center'
    },
    {
      title: 'LOCATION',
      dataIndex: 'location',
      align: 'center'
    },
    {
      title: 'REGION',
      dataIndex: 'region',
      align: 'center'
    },
    {
      title: 'STATUS',
      dataIndex: 'disabled',
      align: 'center',
      render: disabled => (
        disabled
          ? <Tag color="error">Disabled</Tag>
          : <Tag color="success">Enabled</Tag>
      )
    },
    {
      title: 'ACTION',
      dataIndex: 'action',
      align: 'center',
      render(_, record) {
        return (
          <div className="flex justify-evenly items-center">
            <EditOutlined onClick={() => {
              form.setFieldsValue(record);
              dispatch({
                type: 'setNode',
                payload: {
                  currentNode: record.username,
                  installationScript: parseInstallationScript(record.username, '')
                }
              });
            }}
            />
            <DeleteOutlined onClick={() => confirm({
              title: 'Are you sure you want to delete this item?',
              icon: <ExclamationCircleOutlined />,
              onOk: () => handleDelete(record.username)
            })}
            />
          </div>
        );
      }
    }

  ], [confirm, form, handleDelete]);

  const TableFooter = useCallback(() => (
    <>
      <Button type="primary" className="mr-6" onClick={() => dispatch({ type: 'showModal' })}>New</Button>
      <Button
        type="primary"
        className="mr-6"
        onClick={() => dispatch({ type: 'showImportForm' })}
      >
        Import
      </Button>
      <Button
        type="primary"
        danger={state.sortEnabled}
        onClick={() => {
          if (state.sortEnabled) {
            const order = dataSource.map(item => item.id);
            order.reverse();
            handleSortOrder(order);
          }
          dispatch({ type: 'reverseSortEnabled' });
        }}
      >
        {!state.sortEnabled ? 'Sort' : 'Save'}
      </Button>
    </>
  ), [dataSource, handleSortOrder, state.sortEnabled]);

  const DraggableContainer = useCallback<FC>(props => (
    <Droppable droppableId="table">
      {
        provided => (
          <tbody {...props} {...provided.droppableProps} ref={provided.innerRef}>
            {props.children}
            {provided.placeholder}
          </tbody>
        )
      }
    </Droppable>
  ), []);

  const DraggableBodyRow = useCallback<FC<any>>(props => {
    const index = dataSource.findIndex(x => x.id === props['data-row-key']);
    return (
      <Draggable
        draggableId={props['data-row-key']?.toString()}
        index={index}
        isDragDisabled={!state.sortEnabled}
      >
        {provided => {
          const children = props.children?.map?.((el: ReactElement) => {
            if (el.props.dataIndex === 'sort') {
              const props = el.props ? { ...el.props } : {};
              props.render = () => (
                <MenuOutlined
                  style={{ cursor: 'grab', color: '#999' }}
                  {...provided.dragHandleProps}
                />
              );
              return React.cloneElement(el, props);
            }
            return el;
          }) || props.children;
          return (
            <tr {...props} {...provided.draggableProps} ref={provided.innerRef}>
              {children}
            </tr>
          );
        }}
      </Draggable>
    );
  }, [dataSource, state.sortEnabled]);

  return (
    <>
      <Title level={2} className="my-6">Management</Title>
      {
        data ? (
          <DragDropContext
            onDragEnd={result => {
              const { destination, source } = result;
              if (!destination) return;
              if (destination.droppableId === source.droppableId && destination.index === source.index) return;
              const newDataSource = arrayMoveImmutable(dataSource, source.index, destination.index);
              mutate({ ...data, data: newDataSource }, false).then();
            }}
          >
            <Table
              dataSource={dataSource}
              columns={columns}
              rowKey="id"
              components={{
                body: {
                  wrapper: DraggableContainer,
                  row: DraggableBodyRow
                }
              }}
              pagination={state.sortEnabled ? false : undefined}
              footer={TableFooter}
            />
            <Modal
              title={state.currentNode ? 'Modify Configuration' : 'New'}
              visible={state.showModal}
              onOk={state.currentNode ? handleModify : handleCreate}
              onCancel={() => dispatch({ type: 'resetState', payload: { form } })}
              className="top-12"
            >
              <Form
                layout="vertical"
                form={form}
                onValuesChange={(field, allFields) => {
                  if (field.username || field.password) {
                    dispatch({
                      type: 'setInstallationScript',
                      payload: {
                        installationScript: parseInstallationScript(
                          field.username || allFields.username,
                          field.password || allFields.password
                        )
                      }
                    });
                  }
                }}
              >
                {state.isImport ? (
                  <Form.Item label="Data" name="data">
                    <Input.TextArea rows={4} />
                  </Form.Item>
                ) : (
                  <>
                    <Form.Item label="Username" name="username">
                      <Input />
                    </Form.Item>
                    <Form.Item label="Password" name="password">
                      <Input.Password placeholder="留空不修改" />
                    </Form.Item>
                    <Form.Item label="Name" name="name">
                      <Input />
                    </Form.Item>
                    <Form.Item label="Type" name="type">
                      <Input />
                    </Form.Item>
                    <Form.Item label="Location" name="location">
                      <Input />
                    </Form.Item>
                    <Form.Item
                      label="Region"
                      name="region"
                      rules={[{
                        validator(_, value) {
                          if (countries.isValid(value)) return Promise.resolve();
                          return Promise.reject(new Error('Country not found!'));
                        }
                      }]}
                    >
                      <AutoComplete
                        options={regionResult.map(value => ({
                          value,
                          label: value
                        }))}
                        onChange={value => {
                          const code = countries.getAlpha2Code(value, 'zh');
                          const codeEn = countries.getAlpha2Code(value, 'en');
                          return setRegionResult([code, codeEn].filter(v => !!v));
                        }}
                      >
                        <Input />
                      </AutoComplete>
                    </Form.Item>
                    <Form.Item label="Disabled" name="disabled" valuePropName="checked">
                      <Switch />
                    </Form.Item>
                    <Form.Item label="Script">
                      <code
                        className="bg-gray-200 px-2 py-0.5 leading-6 rounded break-all"
                      >
                        {state.installationScript}
                      </code>
                    </Form.Item>
                  </>
                )}
              </Form>
            </Modal>
          </DragDropContext>
        )
          : <Loading />
      }
    </>
  );
}
Example #28
Source File: DendronSearch.tsx    From dendron with GNU Affero General Public License v3.0 4 votes vote down vote up
function DendronSearchComponent(props: DendronCommonProps & SearchProps) {
  const { noteIndex, dendronRouter, search, error, loading, notes } = props;

  const [searchResults, setSearchResults] =
    React.useState<SearchResults>(undefined);
  const [lookupResults, setLookupResults] = React.useState<NoteIndexProps[]>(
    []
  );
  const [results, setResults] = React.useState<SearchMode>();
  const dispatch = useCombinedDispatch();
  const { noteBodies, requestNotes } = useNoteBodies();
  const lookup = useDendronLookup();
  const { noteActive } = useNoteActive(dendronRouter.getActiveNoteId());
  const initValue = noteActive?.fname || "";
  const [searchQueryValue, setSearchQueryValue] = React.useState(initValue);

  useEffect(() => {
    if (search) {
      search(searchQueryValue, setSearchResults);
    }
  }, [searchQueryValue, search]);

  useEffect(() => {
    requestNotes(searchResults?.map(({ item: note }) => note.id) || []);
  }, [requestNotes, searchResults]);

  useEffect(() => {
    if (searchQueryValue?.startsWith("?")) {
      setResults(SearchMode.SEARCH_MODE);
    } else {
      setResults(SearchMode.LOOKUP_MODE);
    }
  }, [searchQueryValue]);

  const onLookup = useCallback(
    (qs: string) => {
      if (_.isUndefined(qs) || !notes || !verifyNoteData({ notes })) {
        return;
      }
      const out =
        qs === ""
          ? NoteLookupUtils.fetchRootResults(notes)
          : lookup?.queryNote({ qs, originalQS: qs });

      setLookupResults(_.isUndefined(out) ? [] : out);
    },
    [lookup, notes, setLookupResults]
  );

  // This is needed to make sure the lookup results are updated when notes are fetched
  useEffect(() => {
    if (results === SearchMode.LOOKUP_MODE) {
      onLookup(searchQueryValue);
    }
  }, [notes]); // intentionally not including searchQueryValue, so that it triggers only when notes are fetched

  const onClickLookup = useCallback(() => {
    const qs = NoteLookupUtils.getQsForCurrentLevel(initValue);
    onLookup(qs);
  }, [initValue, onLookup]);

  const onChangeLookup = useCallback(
    (val: string) => {
      setSearchQueryValue(val);
      onLookup(val);
    },
    [onLookup, setSearchQueryValue]
  );

  const onChangeSearch = useCallback(
    (val: string) => {
      setSearchQueryValue(val);
    },
    [setSearchQueryValue]
  );

  const onSelect = useCallback(
    (_selection, option) => {
      if (!noteIndex) {
        return;
      }

      const id = option.key?.toString()!;
      dendronRouter.changeActiveNote(id, { noteIndex });
      dispatch(
        browserEngineSlice.actions.setLoadingStatus(LoadingStatus.PENDING)
      );
      setSearchQueryValue("");
    },
    [dendronRouter, dispatch, noteIndex]
  );

  if (error) {
    return (
      <Alert
        type="error"
        closable={false}
        message="Error loading data for the search."
      />
    );
  }

  let autocompleteChildren;
  if (!verifyNoteData({ notes })) {
    autocompleteChildren = (
      <AutoComplete.Option value="Loading...">
        <DendronSpinner />
      </AutoComplete.Option>
    );
  } else if (results === SearchMode.SEARCH_MODE) {
    autocompleteChildren = searchResults?.map(({ item: note, matches }) => {
      return (
        <AutoComplete.Option key={note.id} value={note.fname}>
          <Row justify="center" align="middle">
            <Col xs={0} md={1}>
              <div style={{ position: "relative", top: -12, left: 0 }}>
                <FileTextOutlined style={{ color: "#43B02A" }} />
              </div>
            </Col>
            <Col
              xs={24}
              sm={24}
              md={11}
              lg={11}
              style={{ borderRight: "1px solid #d4dadf" }}
            >
              <Row>
                <Typography.Text>
                  <TitleHighlight
                    hit={{ item: note, matches }}
                    attribute="title"
                    title={note.title}
                  />
                </Typography.Text>
              </Row>
              <Row>
                <Typography.Text type="secondary" ellipsis>
                  {note.fname}
                </Typography.Text>
              </Row>
            </Col>
            <Col
              className="gutter-row"
              xs={24}
              sm={24}
              md={11}
              lg={11}
              offset={1}
            >
              <Row>
                <MatchBody
                  matches={matches}
                  id={note.id}
                  noteBodies={noteBodies}
                />
              </Row>
            </Col>
          </Row>
        </AutoComplete.Option>
      );
    });
  } else {
    autocompleteChildren = lookupResults.map((noteIndex: NoteIndexProps) => {
      return (
        <AutoComplete.Option key={noteIndex.id} value={noteIndex.fname}>
          <div>{noteIndex.fname}</div>
        </AutoComplete.Option>
      );
    });
  }

  return (
    <AutoComplete
      size="large"
      allowClear
      style={{ width: "100%" }}
      value={searchQueryValue}
      getPopupContainer={(trigger) => trigger.parentElement}
      // @ts-ignore
      onClick={results === SearchMode.SEARCH_MODE ? () => null : onClickLookup}
      onChange={
        results === SearchMode.SEARCH_MODE ? onChangeSearch : onChangeLookup
      }
      // @ts-ignore
      onSelect={onSelect}
      placeholder="For full text search please use the '?' prefix. e.g. ? Onboarding"
    >
      {autocompleteChildren}
    </AutoComplete>
  );
}
Example #29
Source File: ContextItemForm.spec.tsx    From next-basics with GNU General Public License v3.0 4 votes vote down vote up
describe("ContextItemForm", () => {
  it("should work", async () => {
    const onContextItemUpdate = jest.fn();
    mockUseBuilderUIContext.mockReturnValue({
      providerList: undefined,
    });
    const Component = (
      props: Omit<ContextItemFormProps, "settingItemForm">
    ): React.ReactElement => {
      const [settingItemForm] = Form.useForm();
      return <ContextItemForm {...props} settingItemForm={settingItemForm} />;
    };
    const wrapper = mount(
      <Component
        data={{
          name: "data-a",
          resolve: {
            useProvider: "provider-a",
            args: ["arg1"],
            if: false,
            transform: {
              value: "<% DATA %>",
            },
            onReject: {
              transform: {
                value: "<% DATA.message %>",
              },
            },
          },
        }}
        onContextItemUpdate={onContextItemUpdate}
      />
    );
    expect(wrapper.find(Form.Item).length).toBe(10);

    mockUseBuilderUIContext.mockReturnValue({
      providerList: ["provider-a", "provider-b"],
    });
    // Trigger component updating.
    wrapper.setProps({});
    wrapper.update();

    expect(
      wrapper
        .find('FormItemInput[label="useProvider"]')
        .find(Select)
        .prop("options")
    ).toEqual([
      { label: "provider-a", value: "provider-a" },
      { label: "provider-b", value: "provider-b" },
    ]);

    expect(wrapper.html().indexOf("onChange is error") <= 0).toBeTruthy();

    wrapper.find(Form).invoke("onFinish")({
      name: "data-a",
      type: "resolve",
      onChange: "-a: \nb",
    });

    expect(onContextItemUpdate).toBeCalledTimes(0);
    expect(wrapper.html().indexOf("onChange is error") >= 0).toBeTruthy();

    wrapper.find(CodeEditorItem).last().invoke("onChange")("onChange");
    await jest.runAllTimers();

    expect(wrapper.html().indexOf("onChange is error") <= 0).toBeTruthy();

    wrapper.find(Form).invoke("onFinish")({
      name: "data-a",
      type: "resolve",
      resolve: {
        useProvider: "provider-a",
        args: "- arg1\n",
        if: "false\n",
        transform: "value: <% DATA %>\n",
        onReject: "transform:\n  value: <% DATA.message %>",
      },
    });

    expect(onContextItemUpdate).toBeCalled();

    wrapper.find(AutoComplete).invoke("onSearch")("provider-a");
    wrapper.find(Radio.Group).invoke("onChange")({
      target: {
        value: "value",
      },
    } as RadioChangeEvent);
    expect(wrapper.find(Form.Item).length).toBe(5);
    wrapper.setProps({
      data: {
        name: "data-b",
        value: "<% QUERY.objectId %>",
      },
    });
    expect(wrapper.find(Form.Item).length).toBe(5);
    expect(wrapper.find(Radio).children().length).toBe(3);

    wrapper.setProps({
      data: {
        name: "data-c",
        type: "selector-resolve",
        resolve: {
          provider: "provider-a",
          args: "- arg1\n",
          if: "false\n",
          transform: "value: <% DATA %>\n",
        },
      },
    });
    expect(wrapper.find(Radio).children().length).toBe(4);

    wrapper.setProps({
      data: {
        name: "data-d",
        type: "flow-api",
        resolve: {
          useProvider: "[email protected]",
          args: "- arg1\n",
          if: "false\n",
          transform: "value: <% DATA %>\n",
        },
      },
    });

    wrapper.update();
    expect(wrapper.find(ContractAutoComplete).length).toEqual(1);
  });
});