utils.ts 5.8 KB

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