row.tsx 5.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178
  1. /**
  2. * Implementation reference from: https://github.com/ant-design/ant-design/blob/master/components/grid/row.tsx
  3. */
  4. import React from 'react';
  5. import classnames from 'classnames';
  6. import PropTypes from 'prop-types';
  7. import { cssClasses } from '@douyinfe/semi-foundation/grid/constants';
  8. import '@douyinfe/semi-foundation/grid/grid.scss';
  9. import { registerMediaQuery } from '../_utils';
  10. const responsiveArray = ['xxl', 'xl', 'lg', 'md', 'sm', 'xs'];
  11. export interface RowContextType {
  12. gutters?: Gutter | [Gutter, Gutter]
  13. }
  14. export const RowContext = React.createContext<RowContextType>(null);
  15. export type Breakpoint = 'xxl' | 'xl' | 'lg' | 'md' | 'sm' | 'xs';
  16. export type Gutter = number | Partial<Record<Breakpoint, number>>;
  17. export interface RowProps {
  18. type?: 'flex';
  19. align?: 'top' | 'middle' | 'bottom';
  20. justify?: 'start' | 'end' | 'center' | 'space-around' | 'space-between';
  21. className?: string;
  22. style?: React.CSSProperties;
  23. children?: React.ReactNode;
  24. gutter?: Gutter | [Gutter, Gutter];
  25. prefixCls?: string
  26. }
  27. export interface RowState {
  28. screens: Partial<Record<Breakpoint, boolean>>
  29. }
  30. const responsiveMap = {
  31. xs: '(max-width: 575px)',
  32. sm: '(min-width: 576px)',
  33. md: '(min-width: 768px)',
  34. lg: '(min-width: 992px)',
  35. xl: '(min-width: 1200px)',
  36. xxl: '(min-width: 1600px)',
  37. };
  38. class Row extends React.Component<RowProps, RowState> {
  39. static propTypes = {
  40. type: PropTypes.oneOf(['flex']),
  41. align: PropTypes.oneOf(['top', 'middle', 'bottom']),
  42. justify: PropTypes.oneOf(['start', 'end', 'center', 'space-around', 'space-between']),
  43. className: PropTypes.string,
  44. style: PropTypes.object,
  45. children: PropTypes.node,
  46. gutter: PropTypes.oneOfType([PropTypes.object, PropTypes.number, PropTypes.array]),
  47. prefixCls: PropTypes.string,
  48. };
  49. static defaultProps = {
  50. prefixCls: cssClasses.PREFIX,
  51. };
  52. static RowContext = {
  53. gutters: PropTypes.any,
  54. };
  55. state = {
  56. screens: {
  57. xs: true,
  58. sm: true,
  59. md: true,
  60. lg: true,
  61. xl: true,
  62. xxl: true,
  63. }
  64. };
  65. unRegisters: Array<() => void> = [];
  66. componentDidMount() {
  67. this.unRegisters = Object.keys(responsiveMap).map(screen => registerMediaQuery(responsiveMap[screen], {
  68. match: () => {
  69. if (typeof this.props.gutter !== 'object') {
  70. return;
  71. }
  72. this.setState(prevState => ({
  73. screens: {
  74. ...prevState.screens,
  75. [screen]: true,
  76. },
  77. }));
  78. },
  79. unmatch: () => {
  80. if (typeof this.props.gutter !== 'object') {
  81. return;
  82. }
  83. this.setState(prevState => ({
  84. screens: {
  85. ...prevState.screens,
  86. [screen]: false,
  87. },
  88. }));
  89. },
  90. }));
  91. }
  92. componentWillUnmount() {
  93. this.unRegisters.forEach(unRegister => unRegister());
  94. }
  95. getGutter() {
  96. const { gutter = 0 } = this.props;
  97. const results: [number, number] = [0, 0];
  98. const normalizedGutter = Array.isArray(gutter) ? gutter.slice(0, 2) : [gutter, 0];
  99. normalizedGutter.forEach((g, index) => {
  100. if (typeof g === 'object') {
  101. for (let i = 0; i < responsiveArray.length; i++) {
  102. const breakpoint = responsiveArray[i];
  103. if (this.state.screens[breakpoint] && g[breakpoint] !== undefined) {
  104. results[index] = g[breakpoint];
  105. break;
  106. }
  107. }
  108. } else {
  109. results[index] = g || 0;
  110. }
  111. });
  112. return results;
  113. }
  114. render() {
  115. const { prefixCls, type, justify, align, className, style, children, ...others } = this.props;
  116. const gutters = this.getGutter();
  117. const prefix = `${prefixCls}-row`;
  118. const classes = classnames(
  119. {
  120. [prefix]: type !== 'flex',
  121. [`${prefix}-${type}`]: type,
  122. [`${prefix}-${type}-${justify}`]: type && justify,
  123. [`${prefix}-${type}-${align}`]: type && align,
  124. },
  125. className
  126. );
  127. const rowStyle = {
  128. ...(gutters[0] > 0 ?
  129. {
  130. marginLeft: gutters[0] / -2,
  131. marginRight: gutters[0] / -2,
  132. } :
  133. {}),
  134. ...(gutters[1] > 0 ?
  135. {
  136. marginTop: gutters[1] / -2,
  137. marginBottom: gutters[1] / -2,
  138. } :
  139. {}),
  140. ...style,
  141. };
  142. const otherProps = { ...others };
  143. delete otherProps.gutter;
  144. return (
  145. <RowContext.Provider
  146. value={{
  147. gutters,
  148. }}
  149. >
  150. <div {...otherProps} className={classes} style={rowStyle} x-semi-prop="children">
  151. {children}
  152. </div>
  153. </RowContext.Provider>
  154. );
  155. }
  156. }
  157. export default Row;