Fab.tsx 3.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110
  1. import React, {
  2. useState, useCallback, useRef,
  3. } from 'react';
  4. import { animateScroll } from 'react-scroll';
  5. import { useRipple } from 'react-use-ripple';
  6. import StickyEvents from 'sticky-events';
  7. import { DEFAULT_AUTO_SCROLL_OPTS } from '~/client/util/smooth-scroll';
  8. import { useCurrentUser } from '~/stores/context';
  9. import { usePageCreateModal } from '~/stores/modal';
  10. import { useCurrentPagePath } from '~/stores/page';
  11. import loggerFactory from '~/utils/logger';
  12. import { CreatePageIcon } from './Icons/CreatePageIcon';
  13. import { ReturnTopIcon } from './Icons/ReturnTopIcon';
  14. import styles from './Fab.module.scss';
  15. const logger = loggerFactory('growi:cli:Fab');
  16. export const Fab = (): JSX.Element => {
  17. const { data: currentUser } = useCurrentUser();
  18. const { data: currentPath = '' } = useCurrentPagePath();
  19. const { open: openCreateModal } = usePageCreateModal();
  20. const [animateClasses, setAnimateClasses] = useState('invisible');
  21. const [buttonClasses, setButtonClasses] = useState('');
  22. // ripple
  23. const createBtnRef = useRef(null);
  24. useRipple(createBtnRef, { rippleColor: 'rgba(255, 255, 255, 0.3)' });
  25. /*
  26. * TODO: Comment out to prevent err >>> TypeError: Cannot read properties of null (reading 'bottom')
  27. * We need add style={{ position: 'relative }} to child elements if disable StickyEvents. see: use grep = "<Fab".
  28. */
  29. // const stickyChangeHandler = useCallback((event) => {
  30. // logger.debug('StickyEvents.CHANGE detected');
  31. // const newAnimateClasses = event.detail.isSticky ? 'animated fadeInUp faster' : 'animated fadeOut faster';
  32. // const newButtonClasses = event.detail.isSticky ? '' : 'disabled grw-pointer-events-none';
  33. // setAnimateClasses(newAnimateClasses);
  34. // setButtonClasses(newButtonClasses);
  35. // }, []);
  36. // // setup effect by sticky event
  37. // useEffect(() => {
  38. // // sticky
  39. // // See: https://github.com/ryanwalters/sticky-events
  40. // const stickyEvents = new StickyEvents({ stickySelector: '#grw-fav-sticky-trigger' });
  41. // const { stickySelector } = stickyEvents;
  42. // const elem = document.querySelector(stickySelector);
  43. // elem.addEventListener(StickyEvents.CHANGE, stickyChangeHandler);
  44. // // return clean up handler
  45. // return () => {
  46. // elem.removeEventListener(StickyEvents.CHANGE, stickyChangeHandler);
  47. // };
  48. // }, [stickyChangeHandler]);
  49. const PageCreateButton = useCallback(() => {
  50. return (
  51. <div className={`rounded-circle position-absolute ${animateClasses}`} style={{ bottom: '2.3rem', right: '4rem' }}>
  52. <button
  53. type="button"
  54. className={`btn btn-lg btn-create-page btn-primary rounded-circle p-0 ${buttonClasses}`}
  55. ref={createBtnRef}
  56. onClick={currentPath != null
  57. ? () => openCreateModal(currentPath)
  58. : undefined}
  59. >
  60. <CreatePageIcon />
  61. </button>
  62. </div>
  63. );
  64. }, [animateClasses, buttonClasses, currentPath, openCreateModal]);
  65. const ScrollToTopButton = useCallback(() => {
  66. const clickHandler = () => {
  67. animateScroll.scrollToTop(DEFAULT_AUTO_SCROLL_OPTS);
  68. };
  69. return (
  70. <div className={`rounded-circle position-absolute ${animateClasses}`} style={{ bottom: 0, right: 0 }} data-testid="grw-fab-return-to-top">
  71. <button
  72. type="button"
  73. className={`btn btn-light btn-scroll-to-top rounded-circle p-0 ${buttonClasses}`}
  74. onClick={clickHandler}
  75. >
  76. <ReturnTopIcon />
  77. </button>
  78. </div>
  79. );
  80. }, [animateClasses, buttonClasses]);
  81. if (currentPath == null) {
  82. return <></>;
  83. }
  84. return (
  85. <div className={`${styles['grw-fab']} grw-fab d-none d-md-block d-edit-none`} data-testid="grw-fab-container">
  86. {currentUser != null && <PageCreateButton />}
  87. <ScrollToTopButton />
  88. </div>
  89. );
  90. };