Sider.tsx 3.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122
  1. import React, { AriaRole, CSSProperties } from 'react';
  2. import cls from 'classnames';
  3. import PropTypes from 'prop-types';
  4. import { cssClasses, strings } from '@douyinfe/semi-foundation/layout/constants';
  5. import getDataAttr from '@douyinfe/semi-foundation/utils/getDataAttr';
  6. import LayoutContext, { ContextType } from './layout-context';
  7. import { registerMediaQuery } from '../_utils';
  8. const responsiveMap: ResponsiveMap = {
  9. xs: '(max-width: 575px)',
  10. sm: '(min-width: 576px)',
  11. md: '(min-width: 768px)',
  12. lg: '(min-width: 992px)',
  13. xl: '(min-width: 1200px)',
  14. xxl: '(min-width: 1600px)',
  15. };
  16. export interface ResponsiveMap {
  17. xs: string;
  18. sm: string;
  19. md: string;
  20. lg: string;
  21. xl: string;
  22. xxl: string;
  23. }
  24. const generateId = ((): () => string => {
  25. let i = 0;
  26. return (): string => {
  27. i += 1;
  28. return `${cssClasses.PREFIX}-sider-${i}`;
  29. };
  30. })();
  31. const bpt = strings.BREAKPOINT;
  32. export interface SiderProps {
  33. prefixCls?: string;
  34. style?: CSSProperties;
  35. className?: string;
  36. children?: React.ReactNode;
  37. breakpoint?: Array<keyof ResponsiveMap>;
  38. onBreakpoint?: (screen: keyof ResponsiveMap, match: boolean) => void;
  39. 'aria-label'?: React.AriaAttributes['aria-label'];
  40. 'role'?:React.AriaRole
  41. }
  42. class Sider extends React.PureComponent<SiderProps> {
  43. static propTypes = {
  44. prefixCls: PropTypes.string,
  45. style: PropTypes.object,
  46. className: PropTypes.string,
  47. breakpoint: PropTypes.arrayOf(PropTypes.oneOf(bpt)),
  48. onBreakpoint: PropTypes.func,
  49. 'aria-label': PropTypes.string,
  50. role: PropTypes.string,
  51. };
  52. static defaultProps = {
  53. prefixCls: cssClasses.PREFIX,
  54. };
  55. static contextType = LayoutContext;
  56. unRegisters: Array<() => void> = [];
  57. context: ContextType;
  58. uniqueId = '';
  59. constructor(props: SiderProps) {
  60. super(props);
  61. this.uniqueId = generateId();
  62. }
  63. componentDidMount(): void {
  64. const { breakpoint } = this.props;
  65. const matchBpt: Array<keyof ResponsiveMap> = (Object.keys(responsiveMap) as (keyof ResponsiveMap)[]).filter((item) => breakpoint && breakpoint.indexOf(item) !== -1) as any;
  66. const unRegisters = matchBpt.map(screen => registerMediaQuery(responsiveMap[screen], {
  67. match: () => {
  68. this.responsiveHandler(screen, true);
  69. },
  70. unmatch: () => {
  71. this.responsiveHandler(screen, false);
  72. },
  73. }));
  74. this.unRegisters = unRegisters;
  75. if (this.context.siderHook) {
  76. this.context.siderHook.addSider(this.uniqueId);
  77. }
  78. }
  79. componentWillUnmount(): void {
  80. this.unRegisters.forEach(unRegister => unRegister());
  81. if (this.context.siderHook) {
  82. this.context.siderHook.removeSider(this.uniqueId);
  83. }
  84. }
  85. responsiveHandler(screen: keyof ResponsiveMap, matches: boolean): void {
  86. const { onBreakpoint } = this.props;
  87. if (onBreakpoint) {
  88. onBreakpoint(screen, matches);
  89. }
  90. }
  91. render() {
  92. const { prefixCls, className, children, style, ...others } = this.props;
  93. const classString = cls(className, {
  94. [`${prefixCls}-sider`]: true,
  95. });
  96. return (
  97. <aside className={classString} aria-label={this.props['aria-label']} style={style} {...getDataAttr(others)}>
  98. <div className={`${prefixCls}-sider-children`}>
  99. {children}
  100. </div>
  101. </aside>
  102. );
  103. }
  104. }
  105. export default Sider;