group.tsx 5.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153
  1. /* eslint-disable react/destructuring-assignment */
  2. import React, { Component } from 'react';
  3. import classNames from 'classnames';
  4. import { isString } from 'lodash';
  5. import { isValid } from '@douyinfe/semi-foundation/form/utils';
  6. import { cssClasses } from '@douyinfe/semi-foundation/form/constants';
  7. import * as ObjectUtil from '@douyinfe/semi-foundation/utils/object';
  8. import ErrorMessage, { ReactFieldError } from './errorMessage';
  9. import Label, { LabelProps } from './label';
  10. import { FormUpdaterContext } from './context';
  11. import { useFormState } from './hooks/index';
  12. import InputGroup, { InputGroupProps as BacisInputGroupProps } from '../input/inputGroup';
  13. import { BaseFormProps, FormState } from './interface';
  14. import { FormUpdaterContextType } from '@douyinfe/semi-foundation/form/interface';
  15. import { Col, Row } from '../grid/index';
  16. interface GroupErrorProps {
  17. showValidateIcon?: boolean;
  18. isInInputGroup?: boolean;
  19. error?: ReactFieldError;
  20. fieldSet?: string[];
  21. }
  22. export interface InputGroupProps extends BacisInputGroupProps {
  23. label?: LabelProps;
  24. labelPosition?: 'left' | 'top';
  25. }
  26. const prefix = cssClasses.PREFIX;
  27. // Group component to remove Labels and ErrorMessages from its child fields
  28. // Unified insertion of Labels and ErrorMessages from the group level
  29. // Get Errors of all field in this group
  30. const GroupError = (props: GroupErrorProps) => {
  31. const { fieldSet } = props;
  32. const formState: FormState = useFormState();
  33. const error = fieldSet.map((field: string) => ObjectUtil.get(formState.errors, field));
  34. if (isValid(error)) {
  35. return null;
  36. }
  37. return (
  38. <ErrorMessage error={error} showValidateIcon={props.showValidateIcon} isInInputGroup={props.isInInputGroup} />
  39. );
  40. };
  41. class FormInputGroup extends Component<InputGroupProps> {
  42. static contextType = FormUpdaterContext;
  43. context: FormUpdaterContextType;
  44. renderLabel(label: LabelProps, formProps: BaseFormProps) {
  45. if (label) {
  46. if (isString(label)) {
  47. return (<Label width={formProps.labelWidth} text={label} />);
  48. } else {
  49. return (<Label width={formProps.labelWidth} {...label} />);
  50. }
  51. }
  52. return null;
  53. }
  54. render() {
  55. const { children, label, ...rest } = this.props;
  56. const updater = this.context;
  57. const formProps = updater.getFormProps(['labelPosition', 'labelWidth', 'labelAlign', 'showValidateIcon', 'wrapperCol', 'labelCol']);
  58. const labelPosition = this.props.labelPosition || formProps.labelPosition;
  59. const groupFieldSet: Array<string> = [];
  60. const inner = React.Children.map(children, (child: any) => {
  61. if (child && child.props && child.props.field) {
  62. groupFieldSet.push(child.props.field);
  63. return React.cloneElement(child, {
  64. isInInputGroup: true,
  65. // noErrorMessage: true,
  66. // noLabel: true
  67. });
  68. }
  69. return null;
  70. });
  71. const groupCls = classNames({
  72. [`${prefix}-field-group`]: true
  73. });
  74. const labelCol = formProps.labelCol;
  75. const wrapperCol = formProps.wrapperCol;
  76. const labelAlign = formProps.labelAlign;
  77. const appendCol = labelCol && wrapperCol;
  78. const labelColCls = labelCol ? `${prefix}-col-${labelAlign}` : '';
  79. const labelContent = this.renderLabel(label, formProps);
  80. const inputGroupContent = (
  81. <InputGroup {...rest}>
  82. {inner}
  83. </InputGroup>
  84. );
  85. const groupErrorContent = (<GroupError fieldSet={groupFieldSet} showValidateIcon={formProps.showValidateIcon} isInInputGroup />);
  86. let content: any;
  87. switch (true) {
  88. case !appendCol:
  89. content = (
  90. <>
  91. {labelContent}
  92. <div>
  93. {inputGroupContent}
  94. {groupErrorContent}
  95. </div>
  96. </>
  97. );
  98. break;
  99. case appendCol && labelPosition === 'top':
  100. // When labelPosition is top, you need to add an overflow hidden div to the label, otherwise it will be arranged horizontally
  101. content = (
  102. <>
  103. <div style={{ overflow: 'hidden' }}>
  104. <Col {...labelCol} className={labelColCls}>
  105. {labelContent}
  106. </Col>
  107. </div>
  108. <Col {...wrapperCol}>
  109. {inputGroupContent}
  110. {groupErrorContent}
  111. </Col>
  112. </>
  113. );
  114. break;
  115. case appendCol && labelPosition !== 'top':
  116. content = (
  117. <>
  118. <Col {...labelCol} className={labelColCls}>
  119. {labelContent}
  120. </Col>
  121. <Col {...wrapperCol}>
  122. {inputGroupContent}
  123. {groupErrorContent}
  124. </Col>
  125. </>
  126. );
  127. break;
  128. default:
  129. break;
  130. }
  131. return (
  132. <div x-label-pos={labelPosition} className={groupCls}>
  133. {content}
  134. </div>
  135. );
  136. }
  137. }
  138. export default FormInputGroup;