utils.ts 5.8 KB

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