slot.tsx 5.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167
  1. /* eslint-disable max-lines-per-function, prefer-destructuring, prefer-const, @typescript-eslint/no-unused-vars */
  2. import React, { useContext } from 'react';
  3. import classNames from 'classnames';
  4. import { cssClasses } from '@douyinfe/semi-foundation/form/constants';
  5. import { isString, isNumber, isObject } from 'lodash';
  6. import Label, { LabelProps } from './label';
  7. import { Col } from '../grid';
  8. import { FormUpdaterContext } from './context';
  9. import ErrorMessage, { ErrorMessageProps } from './errorMessage';
  10. const prefix = cssClasses.PREFIX;
  11. export interface SlotProps {
  12. className?: string;
  13. style?: React.CSSProperties;
  14. label?: LabelProps | React.ReactNode;
  15. noLabel?: boolean;
  16. labelPosition?: 'top' | 'left';
  17. error?: ErrorMessageProps;
  18. children?: React.ReactNode
  19. }
  20. const FormSlot = (props: SlotProps) => {
  21. let labelCol, wrapperCol, labelWidth, labelAlign, content;
  22. let labelPosition = 'top';
  23. try {
  24. const updater = useContext(FormUpdaterContext);
  25. const formProps = updater.getFormProps(['labelPosition', 'labelWidth', 'labelAlign', 'labelCol', 'wrapperCol']);
  26. labelCol = formProps.labelCol;
  27. wrapperCol = formProps.wrapperCol;
  28. labelWidth = formProps.labelWidth;
  29. labelAlign = formProps.labelAlign;
  30. labelPosition = formProps.labelPosition ? formProps.labelPosition : labelPosition;
  31. } catch (error) {
  32. }
  33. // eslint-disable-next-line react/destructuring-assignment
  34. props.labelPosition ? labelPosition = props.labelPosition : null;
  35. let { children, label, className, style, error, noLabel, ...rest } = props;
  36. const appendCol = labelCol && wrapperCol;
  37. const slotCls = classNames(
  38. {
  39. [`${prefix}-field`]: true,
  40. [`${prefix}-slot`]: true,
  41. },
  42. className
  43. );
  44. const labelColCls = classNames({
  45. [`${prefix}-col-${labelAlign}`]: true,
  46. });
  47. switch (true) {
  48. case isObject(label) && !React.isValidElement(label):
  49. // do nothing
  50. break;
  51. case isString(label) || isNumber(label):
  52. // @ts-ignore skip type check, the actual type is already determined
  53. label = { text: label };
  54. break;
  55. case React.isValidElement(label):
  56. // @ts-ignore skip type check, the actual type is already determined
  57. label = { text: label };
  58. break;
  59. default:
  60. break;
  61. }
  62. let slotError = null;
  63. if (typeof error !== undefined) {
  64. let emProps = {};
  65. switch (true) {
  66. case isObject(error) && !React.isValidElement(error):
  67. // do nothing
  68. emProps = error;
  69. break;
  70. case isString(error) || isNumber(error):
  71. emProps = { error };
  72. break;
  73. case React.isValidElement(error):
  74. emProps = { error };
  75. break;
  76. default:
  77. break;
  78. }
  79. slotError = <ErrorMessage {...emProps} />;
  80. }
  81. let mergeLabelProps = {
  82. align: labelAlign,
  83. width: labelWidth,
  84. // @ts-ignore: After the above switch statement, label must be of object type
  85. ...label,
  86. };
  87. let mainCls = classNames({
  88. [`${prefix}-field-main`]: true,
  89. [`${prefix}-slot-main`]: true,
  90. });
  91. switch (true) {
  92. case !appendCol && !noLabel:
  93. content = (
  94. <>
  95. <Label {...mergeLabelProps} />
  96. <div className={mainCls}>
  97. {children}
  98. {slotError}
  99. </div>
  100. </>
  101. );
  102. break;
  103. case !appendCol && noLabel:
  104. content = (
  105. <>
  106. <div className={mainCls}>
  107. {children}
  108. {slotError}
  109. </div>
  110. </>
  111. );
  112. break;
  113. case appendCol && labelPosition === 'top':
  114. // When labelPosition is top, you need to add an overflow hidden div to the label, otherwise it will be arranged horizontally
  115. content = (
  116. <>
  117. <div style={{ overflow: 'hidden' }}>
  118. <Col {...labelCol} className={labelColCls}>
  119. <Label {...mergeLabelProps} />
  120. </Col>
  121. </div>
  122. <Col>
  123. {children}
  124. {slotError}
  125. </Col>
  126. </>
  127. );
  128. break;
  129. case appendCol && labelPosition !== 'top':
  130. content = (
  131. <>
  132. <Col {...labelCol} className={labelColCls}>
  133. <Label {...mergeLabelProps} />
  134. </Col>
  135. <Col>
  136. {children}
  137. {slotError}
  138. </Col>
  139. </>
  140. );
  141. break;
  142. default:
  143. break;
  144. }
  145. return (
  146. <div className={slotCls} x-label-pos={labelPosition} style={style}>
  147. {content}
  148. </div>
  149. );
  150. };
  151. export default FormSlot;