Fab.jsx 3.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990
  1. import React, { useState, useCallback, useEffect } from 'react';
  2. import PropTypes from 'prop-types';
  3. import StickyEvents from 'sticky-events';
  4. import loggerFactory from '~/utils/logger';
  5. import AppContainer from '~/client/services/AppContainer';
  6. import NavigationContainer from '~/client/services/NavigationContainer';
  7. import { usePageCreateModalOpened } from '~/stores/ui';
  8. import { withUnstatedContainers } from './UnstatedUtils';
  9. import CreatePageIcon from './Icons/CreatePageIcon';
  10. import ReturnTopIcon from './Icons/ReturnTopIcon';
  11. const logger = loggerFactory('growi:cli:Fab');
  12. const Fab = (props) => {
  13. const { navigationContainer, appContainer } = props;
  14. const { currentUser } = appContainer;
  15. const { mutate: mutatePageCreateModalOpened } = usePageCreateModalOpened();
  16. const [animateClasses, setAnimateClasses] = useState('invisible');
  17. const [buttonClasses, setButtonClasses] = useState('');
  18. const stickyChangeHandler = useCallback((event) => {
  19. logger.debug('StickyEvents.CHANGE detected');
  20. const newAnimateClasses = event.detail.isSticky ? 'animated fadeInUp faster' : 'animated fadeOut faster';
  21. const newButtonClasses = event.detail.isSticky ? '' : 'disabled grw-pointer-events-none';
  22. setAnimateClasses(newAnimateClasses);
  23. setButtonClasses(newButtonClasses);
  24. }, []);
  25. // setup effect by sticky event
  26. useEffect(() => {
  27. // sticky
  28. // See: https://github.com/ryanwalters/sticky-events
  29. const stickyEvents = new StickyEvents({ stickySelector: '#grw-fav-sticky-trigger' });
  30. const { stickySelector } = stickyEvents;
  31. const elem = document.querySelector(stickySelector);
  32. elem.addEventListener(StickyEvents.CHANGE, stickyChangeHandler);
  33. // return clean up handler
  34. return () => {
  35. elem.removeEventListener(StickyEvents.CHANGE, stickyChangeHandler);
  36. };
  37. }, [stickyChangeHandler]);
  38. function renderPageCreateButton() {
  39. return (
  40. <>
  41. <div className={`rounded-circle position-absolute ${animateClasses}`} style={{ bottom: '2.3rem', right: '4rem' }}>
  42. <button
  43. type="button"
  44. className={`btn btn-lg btn-create-page btn-primary rounded-circle p-0 waves-effect waves-light ${buttonClasses}`}
  45. onClick={() => mutatePageCreateModalOpened(true)}
  46. >
  47. <CreatePageIcon />
  48. </button>
  49. </div>
  50. </>
  51. );
  52. }
  53. return (
  54. <div className="grw-fab d-none d-md-block d-edit-none">
  55. {currentUser != null && renderPageCreateButton()}
  56. <div className={`rounded-circle position-absolute ${animateClasses}`} style={{ bottom: 0, right: 0 }}>
  57. <button
  58. type="button"
  59. className={`btn btn-light btn-scroll-to-top rounded-circle p-0 ${buttonClasses}`}
  60. onClick={() => navigationContainer.smoothScrollIntoView()}
  61. >
  62. <ReturnTopIcon />
  63. </button>
  64. </div>
  65. </div>
  66. );
  67. };
  68. Fab.propTypes = {
  69. appContainer: PropTypes.instanceOf(AppContainer).isRequired,
  70. navigationContainer: PropTypes.instanceOf(NavigationContainer).isRequired,
  71. };
  72. export default withUnstatedContainers(Fab, [AppContainer, NavigationContainer]);