You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

Drawer.js 3.1KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121
  1. // @flow
  2. import React, { useEffect, useRef, useState } from 'react';
  3. import { translate } from '../../../base/i18n';
  4. import { Icon, IconArrowUpWide, IconArrowDownWide } from '../../../base/icons';
  5. type Props = {
  6. /**
  7. * Whether the drawer should have a button that expands its size or not.
  8. */
  9. canExpand: ?boolean,
  10. /**
  11. * The component(s) to be displayed within the drawer menu.
  12. */
  13. children: React$Node,
  14. /**
  15. Whether the drawer should be shown or not.
  16. */
  17. isOpen: boolean,
  18. /**
  19. Function that hides the drawer.
  20. */
  21. onClose: Function,
  22. /**
  23. * Invoked to obtain translated strings.
  24. */
  25. t: Function
  26. };
  27. /**
  28. * Component that displays the mobile friendly drawer on web.
  29. *
  30. * @returns {ReactElement}
  31. */
  32. function Drawer({
  33. canExpand,
  34. children,
  35. isOpen,
  36. onClose,
  37. t }: Props) {
  38. const [ expanded, setExpanded ] = useState(false);
  39. const drawerRef: Object = useRef(null);
  40. /**
  41. * Closes the drawer when clicking or touching outside of it.
  42. *
  43. * @param {Event} event - Mouse down/start touch event object.
  44. * @returns {void}
  45. */
  46. function handleOutsideClickOrTouch(event: Event) {
  47. if (drawerRef.current && !drawerRef.current.contains(event.target)) {
  48. onClose();
  49. }
  50. }
  51. useEffect(() => {
  52. window.addEventListener('mousedown', handleOutsideClickOrTouch);
  53. window.addEventListener('touchstart', handleOutsideClickOrTouch);
  54. return () => {
  55. window.removeEventListener('mousedown', handleOutsideClickOrTouch);
  56. window.removeEventListener('touchstart', handleOutsideClickOrTouch);
  57. };
  58. }, [ drawerRef ]);
  59. /**
  60. * Toggles the menu state between expanded/collapsed.
  61. *
  62. * @returns {void}
  63. */
  64. function toggleExpanded() {
  65. setExpanded(!expanded);
  66. }
  67. /**
  68. * KeyPress handler for accessibility.
  69. *
  70. * @param {React.KeyboardEventHandler<HTMLDivElement>} e - The key event to handle.
  71. *
  72. * @returns {void}
  73. */
  74. function onKeyPress(e) {
  75. if (e.key === ' ' || e.key === 'Enter') {
  76. e.preventDefault();
  77. toggleExpanded();
  78. }
  79. }
  80. return (
  81. isOpen ? (
  82. <div
  83. className = { `drawer-menu${expanded ? ' expanded' : ''}` }
  84. ref = { drawerRef }>
  85. {canExpand && (
  86. <div
  87. aria-expanded = { expanded }
  88. aria-label = { expanded ? t('toolbar.accessibilityLabel.collapse')
  89. : t('toolbar.accessibilityLabel.expand') }
  90. className = 'drawer-toggle'
  91. onClick = { toggleExpanded }
  92. onKeyPress = { onKeyPress }
  93. role = 'button'
  94. tabIndex = { 0 }>
  95. <Icon
  96. size = { 24 }
  97. src = { expanded ? IconArrowDownWide : IconArrowUpWide } />
  98. </div>
  99. )}
  100. {children}
  101. </div>
  102. ) : null
  103. );
  104. }
  105. export default translate(Drawer);