slot.tsx 5.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165
  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 | number | string;
  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. label = { text: label };
  53. break;
  54. case React.isValidElement(label):
  55. label = { text: label };
  56. break;
  57. default:
  58. break;
  59. }
  60. let slotError = null;
  61. if (typeof error !== undefined) {
  62. let emProps = {};
  63. switch (true) {
  64. case isObject(error) && !React.isValidElement(error):
  65. // do nothing
  66. emProps = error;
  67. break;
  68. case isString(error) || isNumber(error):
  69. emProps = { error };
  70. break;
  71. case React.isValidElement(error):
  72. emProps = { error };
  73. break;
  74. default:
  75. break;
  76. }
  77. slotError = <ErrorMessage {...emProps} />;
  78. }
  79. let mergeLabelProps = {
  80. align: labelAlign,
  81. width: labelWidth,
  82. // @ts-ignore: After the above switch statement, label must be of object type
  83. ...label,
  84. };
  85. let mainCls = classNames({
  86. [`${prefix}-field-main`]: true,
  87. [`${prefix}-slot-main`]: true,
  88. });
  89. switch (true) {
  90. case !appendCol && !noLabel:
  91. content = (
  92. <>
  93. <Label {...mergeLabelProps} />
  94. <div className={mainCls}>
  95. {children}
  96. {slotError}
  97. </div>
  98. </>
  99. );
  100. break;
  101. case !appendCol && noLabel:
  102. content = (
  103. <>
  104. <div className={mainCls}>
  105. {children}
  106. {slotError}
  107. </div>
  108. </>
  109. );
  110. break;
  111. case appendCol && labelPosition === 'top':
  112. // When labelPosition is top, you need to add an overflow hidden div to the label, otherwise it will be arranged horizontally
  113. content = (
  114. <>
  115. <div style={{ overflow: 'hidden' }}>
  116. <Col {...labelCol} className={labelColCls}>
  117. <Label {...mergeLabelProps} />
  118. </Col>
  119. </div>
  120. <Col>
  121. {children}
  122. {slotError}
  123. </Col>
  124. </>
  125. );
  126. break;
  127. case appendCol && labelPosition !== 'top':
  128. content = (
  129. <>
  130. <Col {...labelCol} className={labelColCls}>
  131. <Label {...mergeLabelProps} />
  132. </Col>
  133. <Col>
  134. {children}
  135. {slotError}
  136. </Col>
  137. </>
  138. );
  139. break;
  140. default:
  141. break;
  142. }
  143. return (
  144. <div className={slotCls} x-label-pos={labelPosition} style={style}>
  145. {content}
  146. </div>
  147. );
  148. };
  149. export default FormSlot;