utils.ts 5.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198
  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. // only work with React Adapter
  47. valid = false;
  48. }
  49. return valid;
  50. }
  51. // Compatible with String and Array
  52. function transformTrigger(trigger: FieldValidateTriggerType): Array<BasicTriggerType> {
  53. let result: BasicTriggerType[] = [];
  54. if (Array.isArray(trigger)) {
  55. result = trigger;
  56. }
  57. if (typeof trigger === 'string') {
  58. result[0] = trigger;
  59. }
  60. return result;
  61. }
  62. export function mergeOptions(opts: WithFieldOption, props: ComponentProps) {
  63. // 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
  64. // valueKey: input, select class component control value props are value, and checkbox, switch is checked
  65. // eg:checkbox、radio { valueKey: 'checked', onKeyChangeFnName: 'onChange', valuePath: 'target.value' }
  66. const defaultOpts = {
  67. valueKey: 'value',
  68. onKeyChangeFnName: 'onChange',
  69. valuePath: '',
  70. maintainCursor: false,
  71. shouldInject: true,
  72. shouldMemo: true,
  73. };
  74. const options = { ...defaultOpts, ...opts };
  75. // 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
  76. const shouldInject = 'field' in props ? true : options.shouldInject;
  77. return { options, shouldInject };
  78. }
  79. export function mergeProps(props: any) {
  80. const defaultProps = {
  81. trigger: 'change',
  82. // validateStatus: 'default',
  83. allowEmptyString: false,
  84. allowEmpty: false,
  85. emptyValue: '',
  86. noLabel: false,
  87. noErrorMessage: false,
  88. isInInputGroup: false,
  89. stopValidateWithError: false,
  90. };
  91. let {
  92. field,
  93. label,
  94. labelPosition,
  95. labelWidth,
  96. labelAlign,
  97. labelCol,
  98. wrapperCol,
  99. initValue,
  100. validate,
  101. /**
  102. * error、warning、default、success
  103. */
  104. validateStatus,
  105. /**
  106. * change、blur、custom、mount
  107. */
  108. trigger,
  109. allowEmptyString,
  110. allowEmpty,
  111. emptyValue,
  112. rules,
  113. onChange,
  114. keepState,
  115. // Conversion before validation
  116. transform,
  117. name,
  118. fieldClassName,
  119. fieldStyle,
  120. noLabel,
  121. noErrorMessage,
  122. isInInputGroup,
  123. stopValidateWithError,
  124. convert,
  125. showValidateIcon,
  126. helpText,
  127. extraText,
  128. extraTextPosition,
  129. pure,
  130. id,
  131. ...rest
  132. }: any = { ...defaultProps, ...props };
  133. // Form中的任何类型组件,初始值都统一通过initValue字段来传入,同时将可能会导致组件行为错误的props抽取出来,防止透传到组件中
  134. // For any type of field component in Form, the initial value is uniformly passed in through the initValue field.
  135. // At the same time, the props that may cause component behavior errors are extracted to prevent transparent transmission to the component.
  136. delete rest.defaultChecked;
  137. delete rest.defaultValue;
  138. delete rest.checked;
  139. if (typeof initValue !== 'undefined') {
  140. initValue = cloneDeep(initValue);
  141. }
  142. const required = isRequired(rules);
  143. trigger = transformTrigger(trigger);
  144. emptyValue = typeof emptyValue !== 'undefined' ? emptyValue : '';
  145. return {
  146. field,
  147. label,
  148. labelPosition,
  149. labelWidth,
  150. labelAlign,
  151. labelCol,
  152. wrapperCol,
  153. noLabel,
  154. noErrorMessage,
  155. isInInputGroup,
  156. initValue,
  157. validate,
  158. validateStatus,
  159. trigger,
  160. allowEmptyString,
  161. allowEmpty,
  162. emptyValue,
  163. rules,
  164. required,
  165. keepState,
  166. transform,
  167. name,
  168. fieldClassName,
  169. fieldStyle,
  170. convert,
  171. stopValidateWithError,
  172. showValidateIcon,
  173. helpText,
  174. extraText,
  175. extraTextPosition,
  176. pure,
  177. rest,
  178. id
  179. };
  180. }
  181. function bothEmptyArray(val: any, otherVal: any) {
  182. return Array.isArray(val) && Array.isArray(otherVal) && !val.length && !otherVal.length;
  183. }