import React, { useEffect } from 'react';
import {
connect,
mapReadPretty,
mapProps,
useField,
observer,
RecursionField,
useFieldSchema,
Schema,
} from '@formily/react';
import { Drawer, Select as AntdSelect, Tag } from 'antd';
import { PreviewText } from '@formily/antd';
import { LoadingOutlined } from '@ant-design/icons';
import { SelectProps } from 'antd/lib/select';
import { isArr, isValid, toArr } from '@formily/shared';
import { useState } from 'react';
import { useDesignable } from '../';
import { createContext } from 'react';
import { useContext } from 'react';
import { get, isEmpty } from 'lodash';
import { Field, isArrayField, isField } from '@formily/core';
import { Action } from '../action';
import { BlockSchemaContext, VisibleContext } from '../../context';
import { SchemaRenderer } from '../../components/schema-renderer';
import { uid } from '@formily/shared';
import { CollectionFieldContext } from '../table';
import {
CollectionProvider,
useCollectionContext,
useResourceRequest,
} from '../../constate';
import { Resource } from '../../resource';
import { useRequest } from 'ahooks';
import constate from 'constate';
import './index.less';
import { useCompile } from '../../hooks/useCompile';
export const Select: any = connect(
(props) => {
const { options = [], ...others } = props;
const compile = useCompile();
return (
{options.map((option: any, key: any) => {
if (option.children) {
return (
{option.children.map((child: any, childKey: any) => (
{compile(child.label)}
))}
);
} else {
return (
{compile(option.label)}
);
}
})}
);
},
mapProps(
{
dataSource: 'options',
loading: true,
},
(props, field) => {
return {
...props,
suffixIcon:
field?.['loading'] || field?.['validating'] ? (
) : (
props.suffixIcon
),
};
},
),
mapReadPretty((props) => {
// console.log('mapReadPretty', props.value)
// return
;
if (!isValid(props.value)) {
return ;
}
const compile = useCompile();
const field = useField();
if (isArrayField(field) && field?.value?.length === 0) {
return ;
}
const dataSource = field.dataSource || [];
console.log('field.value', field.value, dataSource);
const values = toArr(field.value);
const findOptions = (options: any[]) => {
let current = [];
for (const option of options) {
if (values.includes(option.value)) {
current.push(option);
}
if (option.children) {
const children = findOptions(option.children);
current.push(...children);
}
}
return current;
};
const options = findOptions(dataSource);
return (
{options.map((option, key) => (
{compile(option.label)}
))}
);
}),
);
Select.Object = connect(
(props) => {
const field = useField();
const {
value,
onChange,
fieldNames = {
label: 'label',
value: 'value',
},
...others
} = props;
const options = field?.['dataSource'] || props.options || [];
let optionValue = undefined;
if (isArr(value)) {
optionValue = value.map((val) => {
return {
label: val[fieldNames.label],
value: val[fieldNames.value],
};
});
} else if (value) {
optionValue = {
label: value[fieldNames.label],
value: value[fieldNames.value],
};
}
return (
{
if (!isValid(selectValue)) {
onChange(null);
return;
}
if (isArr(selectValue)) {
const selectValues = selectValue.map((s) => s.value);
const values = {};
if (isArr(value)) {
value.forEach((option) => {
const val = option[fieldNames.value];
if (selectValues.includes(val)) {
values[val] = option;
}
});
}
options.forEach((option) => {
const val = option[fieldNames.value];
if (selectValues.includes(val)) {
values[val] = option;
}
});
console.log({ selectValue, values });
onChange(Object.values(values));
} else {
// 这里不能用 undefined,需要用 null
onChange(
options.find(
(option) => option[fieldNames.value] === selectValue.value,
),
);
}
}}
options={options.map((option) => {
return {
label: option[fieldNames.label],
value: option[fieldNames.value],
};
})}
/>
);
},
mapProps(
{
dataSource: 'options',
loading: true,
},
(props, field) => {
return {
...props,
options: field?.['dataSource'],
suffixIcon:
field?.['loading'] || field?.['validating'] ? (
) : (
props.suffixIcon
),
};
},
),
mapReadPretty(
observer((props: any) => {
const {
value,
fieldNames = { label: 'label', color: 'color' },
...others
} = props;
if (!value) {
return null;
}
if (isEmpty(value)) {
return null;
}
const values = toArr(value);
return (
{values.map((val) =>
fieldNames.color ? (
{val[fieldNames.label]}
) : (
{val[fieldNames.label]}
),
)}
);
}),
),
);
const OptionTagContext = createContext(null);
const SelectedRowsContext = createContext({});
Select.useOkAction = () => {
const { props } = useContext(SelectContext);
const { selectedRows } = useContext(SelectedRowsContext);
return {
async run() {
props.onChange(selectedRows);
console.log('selectedRows', selectedRows);
},
};
};
Select.useRowSelection = () => {
const { props } = useContext(SelectContext);
return {
type: props.multiple ? 'checkbox' : 'radio',
};
};
Select.useSelect = () => {
const { setSelectedRows } = useContext(SelectedRowsContext);
return (keys, rows) => {
setSelectedRows && setSelectedRows(rows);
console.log('Select.onSelect', keys, rows);
};
};
export const useSelectedRowKeys = () => {
const { rowKey, selectedRows } = useContext(SelectedRowsContext);
const [selectedRowKeys, setSelectedRowKeys] = useState(
selectedRows.map((row) => row[rowKey]),
);
useEffect(() => {
setSelectedRowKeys(selectedRows.map((row) => row[rowKey]));
}, [selectedRows]);
console.log('useSelectedRowKeys', selectedRows);
return { selectedRowKeys, setSelectedRowKeys };
};
Select.useSelectedRowKeys = useSelectedRowKeys;
const SelectContext = createContext(null);
Select.Drawer = connect(
(props) => {
const field = useField();
const {
onChange,
// fieldNames = {
// label: 'id',
// value: 'id',
// },
...others
} = props;
let value = props.value;
const [visible, setVisible] = useState(false);
const { schema } = useDesignable();
const fieldNames = {
label: 'id',
value: 'id',
...(get(schema['x-component-props'], 'fieldNames') || {}),
};
const options = field?.['dataSource'] || props.options || [];
if (props.multiple) {
Object.assign(others, {
mode: 'multiple',
});
}
let optionValue = undefined;
if (props.multiple) {
value = toArr(value);
}
if (isArr(value)) {
optionValue = value.map((val) => {
return {
label: val[fieldNames.label],
value: val[fieldNames.value],
};
});
} else if (value) {
optionValue = {
label: value[fieldNames.label],
value: value[fieldNames.value],
};
}
const [selectedRows, setSelectedRows] = useState(toArr(field.value));
console.log('fieldNames', { fieldNames, field, value });
console.log('useSelectedRowKeys.toArr', toArr(field.value));
useEffect(() => {
setSelectedRows(toArr(field.value));
}, [field.value]);
const onFieldChange = (selectValue) => {
if (!isValid(selectValue)) {
onChange(null);
return;
}
if (isArr(selectValue)) {
const values = {};
if (isArr(value)) {
value.forEach((option) => {
const val = option[fieldNames.value];
if (selectValue.includes(val)) {
values[val] = option;
}
});
}
options.forEach((option) => {
const val = option[fieldNames.value];
if (selectValue.includes(val)) {
values[val] = option;
}
});
onChange(Object.values(values));
} else {
// 这里不能用 undefined,需要用 null
onChange(
options.find((option) => option[fieldNames.value] === selectValue),
);
}
// setSelectedRows(toArr(field.value));
};
// const selectedKeys = toArr(optionValue).map((item) => item.value);
const collectionField = useContext(CollectionFieldContext);
// console.log({ optionValue, value, schema, collectionField });
return (
{
setVisible(true);
}}
onChange={(selectValue: any) => {
if (!selectValue) {
onChange(null);
return;
}
if (isArr(selectValue)) {
const selectValues = selectValue.map((s) => s.value);
onFieldChange(selectValues);
} else {
onFieldChange(selectValue.value);
}
}}
>
{
if (s['x-component'] === 'Select.Options.Drawer') {
const prop = Object.values(s.properties).shift();
if (prop) {
prop['x-component-props']['rowKey'] = collectionField?.targetKey || 'id';
}
}
return s['x-component'] === 'Select.Options.Drawer';
}}
/>
);
},
mapProps(
{
dataSource: 'options',
loading: true,
},
(props, field) => {
return {
...props,
options: field?.['dataSource'],
suffixIcon:
field?.['loading'] || field?.['validating'] ? (
) : (
props.suffixIcon
),
};
},
),
mapReadPretty(
observer((props: any) => {
const collectionField = useContext(CollectionFieldContext);
const field = useField();
const { ...others } = props;
const value = field.value || field.initialValue;
const { schema } = useDesignable();
const fieldNames = {
label: 'id',
value: 'id',
...(get(schema['x-component-props'], 'fieldNames') || {}),
};
console.log('fieldNames', { fieldNames, field, value });
if (!value) {
return null;
}
const values = toArr(value);
const s = schema.reduceProperties((buf, current) => {
if (current['x-component'] === 'Select.OptionTag') {
return current;
}
return buf;
}, null);
return (
{values.map((data, index) => {
return (
{s ? (
) : (
data[fieldNames.label]
)}
);
})}
);
}),
),
);
Select.Drawer.useResource = ({ onSuccess }) => {
const { collection } = useCollectionContext();
const ctx = useContext(OptionTagContext);
const resource = useResourceRequest({
resourceName: collection?.name,
resourceKey: ctx.data.id,
});
console.log('OptionTagContext', ctx.data.id);
const { schema } = useDesignable();
const fieldFields = (schema: Schema) => {
const names = [];
schema.reduceProperties((buf, current) => {
if (current['x-component'] === 'Form.Field') {
const fieldName = current['x-component-props']?.['fieldName'];
if (fieldName) {
buf.push(fieldName);
}
} else {
const fieldNames = fieldFields(current);
buf.push(...fieldNames);
}
return buf;
}, names);
return names;
};
const [visible] = useContext(VisibleContext);
const service = useRequest(
(params?: any) => {
console.log('Table.useResource', params);
return resource.get({ ...params, appends: fieldFields(schema) });
},
{
formatResult: (result) => result?.data,
onSuccess,
manual: true,
},
);
useEffect(() => {
if (visible) {
service.run();
}
}, [visible]);
return { resource, service, initialValues: service.data, ...service };
};
Select.Options = observer((props) => {
return <>{props.children}>;
});
Select.Options.Drawer = Action.Drawer;
export function useSelect() {
const { onChange } = useContext(SelectContext);
return {
async run() {
onChange && onChange([]);
},
};
}
export function useOptionTagValues() {
const { data } = useContext(OptionTagContext);
return data;
}
Select.OptionTag = observer((props) => {
const [visible, setVisible] = useState(false);
const { data, fieldNames } = useContext(OptionTagContext);
return (
setVisible(true)}>
{data[fieldNames.label]}
{props.children}
);
});
export default Select;