utils.ts 6.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221
  1. import AsyncValidator from 'async-validator';
  2. import { toPath, isUndefined } from 'lodash';
  3. import { FieldValidateTriggerType, BasicTriggerType, ComponentProps, WithFieldOption } from './interface';
  4. import { strings } from './constants';
  5. import copy from 'fast-copy';
  6. /**
  7. *
  8. * @param WrappedComponent React.ComponentType | any
  9. */
  10. export function getDisplayName(WrappedComponent: any) {
  11. const originName = WrappedComponent.displayName || WrappedComponent.name;
  12. return originName ? `SemiField${originName}` : 'SemiField';
  13. }
  14. export function generateValidatesFromRules(field: string, rules: any[] = []) {
  15. const descriptor = {};
  16. descriptor[field] = rules;
  17. const validator = new AsyncValidator(descriptor);
  18. return validator;
  19. }
  20. export function isRequired(rules: any[] | Record<string, any> = []): boolean {
  21. let required = false;
  22. if (typeof rules === 'object' && 'required' in rules) {
  23. required = rules.required;
  24. } else if (Array.isArray(rules) && rules.length) {
  25. rules.forEach(rule => {
  26. rule.required ? (required = true) : null;
  27. });
  28. }
  29. return required;
  30. }
  31. export function isValid(errors: any): boolean {
  32. let valid = true;
  33. if (typeof errors === 'string' && errors.length) {
  34. valid = false;
  35. } else if (Array.isArray(errors) && errors.length) {
  36. valid = errors.every(error => isValid(error));
  37. } else if (typeof errors === 'boolean') {
  38. valid = errors;
  39. } else if (
  40. errors &&
  41. typeof errors.$$typeof === 'symbol' &&
  42. errors.$$typeof.toString() === 'Symbol(react.element)'
  43. ) {
  44. // when error message is reactNode
  45. // only work with React Adapter
  46. valid = false;
  47. }
  48. return valid;
  49. }
  50. /**
  51. * trigger transform rule
  52. 1. If the user has set fieldProps, follow the user's fieldProps
  53. 2. If the user does not set fieldProps, follow formProps
  54. 3. If there is no formProps, follow the change
  55. 4. If it is an array, follow the array, if it is not an array (pure string), convert it to a string array
  56. */
  57. export function transformTrigger(fieldTrigger: FieldValidateTriggerType, formTrigger: FieldValidateTriggerType): Array<BasicTriggerType> {
  58. let result: BasicTriggerType[] | FieldValidateTriggerType = [];
  59. let finalResult = [];
  60. if (!isUndefined(fieldTrigger)) {
  61. result = fieldTrigger;
  62. } else if (!isUndefined(formTrigger)) {
  63. result = formTrigger;
  64. } else {
  65. result = strings.DEFAULT_TRIGGER as BasicTriggerType;
  66. }
  67. if (Array.isArray(result)) {
  68. finalResult = result;
  69. }
  70. if (typeof result === 'string') {
  71. finalResult[0] = result;
  72. }
  73. return finalResult;
  74. }
  75. export function transformDefaultBooleanAPI(fieldProp: boolean, formProp: boolean, defaultVal = false) {
  76. if (!isUndefined(fieldProp)) {
  77. return fieldProp;
  78. } else if (!isUndefined(formProp)) {
  79. return formProp;
  80. } else {
  81. return defaultVal;
  82. }
  83. }
  84. export function mergeOptions(opts: WithFieldOption, props: ComponentProps) {
  85. // Opts: different types of component identification value, value change callback function may be inconsistent, used to adapt 1, input, select 2, radio, checkbox 3, switch
  86. // valueKey: input, select class component control value props are value, and checkbox, switch is checked
  87. // eg:checkbox、radio { valueKey: 'checked', onKeyChangeFnName: 'onChange', valuePath: 'target.value' }
  88. const defaultOpts = {
  89. valueKey: 'value',
  90. onKeyChangeFnName: 'onChange',
  91. valuePath: '',
  92. maintainCursor: false,
  93. shouldInject: true,
  94. shouldMemo: true,
  95. };
  96. const options = { ...defaultOpts, ...opts };
  97. // If the field attribute is declared, then the injection is carried out (mainly used to deal with the case where Checkbox and Radio are used separately from the Group); other cases are subject to options
  98. const shouldInject = 'field' in props ? true : options.shouldInject;
  99. return { options, shouldInject };
  100. }
  101. export function mergeProps(props: any) {
  102. const defaultProps = {
  103. // validateStatus: 'default',
  104. allowEmptyString: false,
  105. allowEmpty: false,
  106. emptyValue: '',
  107. noLabel: false,
  108. noErrorMessage: false,
  109. isInInputGroup: false,
  110. };
  111. let {
  112. field,
  113. label,
  114. labelPosition,
  115. labelWidth,
  116. labelAlign,
  117. labelCol,
  118. wrapperCol,
  119. initValue,
  120. validate,
  121. /**
  122. * error、warning、default、success
  123. */
  124. validateStatus,
  125. /**
  126. * change、blur、custom、mount
  127. */
  128. trigger,
  129. allowEmptyString,
  130. allowEmpty,
  131. emptyValue,
  132. rules,
  133. onChange,
  134. keepState,
  135. // Conversion before validation
  136. transform,
  137. name,
  138. fieldClassName,
  139. fieldStyle,
  140. noLabel,
  141. noErrorMessage,
  142. isInInputGroup,
  143. stopValidateWithError,
  144. convert,
  145. showValidateIcon,
  146. helpText,
  147. extraText,
  148. extraTextPosition,
  149. pure,
  150. id,
  151. ...rest
  152. }: any = { ...defaultProps, ...props };
  153. // Form中的任何类型组件,初始值都统一通过initValue字段来传入,同时将可能会导致组件行为错误的props抽取出来,防止透传到组件中
  154. // For any type of field component in Form, the initial value is uniformly passed in through the initValue field.
  155. // At the same time, the props that may cause component behavior errors are extracted to prevent transparent transmission to the component.
  156. delete rest.defaultChecked;
  157. delete rest.defaultValue;
  158. delete rest.checked;
  159. if (typeof initValue !== 'undefined') {
  160. initValue = copy(initValue);
  161. }
  162. const required = isRequired(rules);
  163. emptyValue = typeof emptyValue !== 'undefined' ? emptyValue : '';
  164. return {
  165. field,
  166. label,
  167. labelPosition,
  168. labelWidth,
  169. labelAlign,
  170. labelCol,
  171. wrapperCol,
  172. noLabel,
  173. noErrorMessage,
  174. isInInputGroup,
  175. initValue,
  176. validate,
  177. validateStatus,
  178. trigger,
  179. allowEmptyString,
  180. allowEmpty,
  181. emptyValue,
  182. rules,
  183. required,
  184. keepState,
  185. transform,
  186. name,
  187. fieldClassName,
  188. fieldStyle,
  189. convert,
  190. stopValidateWithError,
  191. showValidateIcon,
  192. helpText,
  193. extraText,
  194. extraTextPosition,
  195. pure,
  196. rest,
  197. id
  198. };
  199. }
  200. function bothEmptyArray(val: any, otherVal: any) {
  201. return Array.isArray(val) && Array.isArray(otherVal) && !val.length && !otherVal.length;
  202. }