Fab.jsx 3.1 KB

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