Fab.jsx 2.6 KB

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