slot.tsx 5.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165
  1. import React, { useContext } from 'react';
  2. import classNames from 'classnames';
  3. import { cssClasses } from '@douyinfe/semi-foundation/form/constants';
  4. import { isString, isNumber, isObject } from 'lodash';
  5. import Label, { LabelProps } from './label';
  6. import { Col } from '../grid';
  7. import { FormUpdaterContext } from './context';
  8. import ErrorMessage, { ErrorMessageProps } from './errorMessage';
  9. const prefix = cssClasses.PREFIX;
  10. export interface SlotProps {
  11. className?: string;
  12. style?: React.CSSProperties;
  13. label?: LabelProps | React.ReactNode;
  14. noLabel?: boolean;
  15. labelPosition?: 'top' | 'left';
  16. error?: ErrorMessageProps;
  17. children?: React.ReactNode
  18. }
  19. const FormSlot = (props: SlotProps) => {
  20. let labelCol, wrapperCol, labelWidth, labelAlign, content;
  21. let labelPosition = 'top';
  22. try {
  23. const updater = useContext(FormUpdaterContext);
  24. const formProps = updater.getFormProps(['labelPosition', 'labelWidth', 'labelAlign', 'labelCol', 'wrapperCol']);
  25. labelCol = formProps.labelCol;
  26. wrapperCol = formProps.wrapperCol;
  27. labelWidth = formProps.labelWidth;
  28. labelAlign = formProps.labelAlign;
  29. labelPosition = formProps.labelPosition ? formProps.labelPosition : labelPosition;
  30. } catch (error) {
  31. }
  32. props.labelPosition ? labelPosition = props.labelPosition : null;
  33. let { children, label, className, style, error, noLabel, ...rest } = props;
  34. const appendCol = labelCol && wrapperCol;
  35. const slotCls = classNames(
  36. {
  37. [`${prefix}-field`]: true,
  38. [`${prefix}-slot`]: true,
  39. },
  40. className
  41. );
  42. const labelColCls = classNames({
  43. [`${prefix}-col-${labelAlign}`]: true,
  44. });
  45. switch (true) {
  46. case isObject(label) && !React.isValidElement(label):
  47. // do nothing
  48. break;
  49. case isString(label) || isNumber(label):
  50. // @ts-ignore skip type check, the actual type is already determined
  51. label = { text: label };
  52. break;
  53. case React.isValidElement(label):
  54. // @ts-ignore skip type check, the actual type is already determined
  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;