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 |
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 |
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 |
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 |
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 |
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 |
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 |
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 |
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 |
{ Option } = AutoComplete
Example #10
Source File: index.tsx From jetlinks-ui-antd with MIT License | 5 votes |
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 |
{ Option } = AutoComplete
Example #12
Source File: HeaderView.tsx From Protoman with MIT License | 5 votes |
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 |
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 |
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 |
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 |
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">大于(>)</Select.Option>
<Select.Option value="lt">小于(<)</Select.Option>
<Select.Option value="gte">大于等于(>=)</Select.Option>
<Select.Option value="lte">小于等于(<=)</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">大于(>)</Select.Option>
<Select.Option value="lt">小于(<)</Select.Option>
<Select.Option value="gte">大于等于(>=)</Select.Option>
<Select.Option value="lte">小于等于(<=)</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 |
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 |
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 |
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 |
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 |
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 |
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 |
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 |
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 |
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 |
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 |
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 |
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 |
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);
});
});