TableOfContents.tsx 3.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100
  1. import React, { useCallback, useEffect, useState } from 'react';
  2. import ReactMarkdown from 'react-markdown';
  3. import { blinkElem } from '~/client/util/blink-section-header';
  4. import { addSmoothScrollEvent } from '~/client/util/smooth-scroll';
  5. import { useIsUserPage } from '~/stores/context';
  6. import { useTocOptions } from '~/stores/renderer';
  7. import loggerFactory from '~/utils/logger';
  8. import { StickyStretchableScroller } from './StickyStretchableScroller';
  9. // eslint-disable-next-line no-unused-vars
  10. const logger = loggerFactory('growi:TableOfContents');
  11. const TableOfContents = (): JSX.Element => {
  12. const { data: isUserPage } = useIsUserPage();
  13. const [tocHtml, setTocHtml] = useState('');
  14. const { data: rendererOptions } = useTocOptions();
  15. const calcViewHeight = useCallback(() => {
  16. // calculate absolute top of '#revision-toc' element
  17. const parentElem = document.querySelector('.grw-side-contents-container');
  18. const containerElem = document.querySelector('#revision-toc');
  19. if (parentElem == null || containerElem == null) {
  20. return 0;
  21. }
  22. const parentBottom = parentElem.getBoundingClientRect().bottom;
  23. const containerTop = containerElem.getBoundingClientRect().top;
  24. const containerComputedStyle = getComputedStyle(containerElem);
  25. const containerPaddingTop = parseFloat(containerComputedStyle['padding-top']);
  26. // get smaller bottom line of window height - .system-version height - margin 5px) and containerTop
  27. let bottom = Math.min(window.innerHeight - 20 - 5, parentBottom);
  28. if (isUserPage) {
  29. // raise the bottom line by the height and margin-top of UserContentLinks
  30. bottom -= 45;
  31. }
  32. // bottom - revisionToc top
  33. return bottom - (containerTop + containerPaddingTop);
  34. }, [isUserPage]);
  35. useEffect(() => {
  36. const tocDom = document.getElementById('revision-toc-content');
  37. if (tocDom == null) { return }
  38. const anchorsInToc = Array.from(tocDom.getElementsByTagName('a'));
  39. addSmoothScrollEvent(anchorsInToc, blinkElem);
  40. }, [tocHtml]);
  41. // == TODO: render ToC without globalEmitter -- Yuki Takei
  42. //
  43. // set handler to render ToC
  44. // useEffect(() => {
  45. // const handler = html => setTocHtml(html);
  46. // globalEmitter.on('renderTocHtml', handler);
  47. // return function cleanup() {
  48. // globalEmitter.removeListener('renderTocHtml', handler);
  49. // };
  50. // }, [globalEmitter]);
  51. return (
  52. <StickyStretchableScroller
  53. stickyElemSelector=".grw-side-contents-sticky-container"
  54. calcViewHeight={calcViewHeight}
  55. >
  56. <ReactMarkdown {...rendererOptions}>
  57. {''}
  58. </ReactMarkdown>
  59. {/* { tocHtml !== ''
  60. ? (
  61. // <div
  62. // id="revision-toc-content"
  63. // className="revision-toc-content mb-3"
  64. // // eslint-disable-next-line react/no-danger
  65. // dangerouslySetInnerHTML={{ __html: tocHtml }}
  66. // />
  67. <ReactMarkdown {...rendererOptions}>
  68. {''}
  69. </ReactMarkdown>
  70. )
  71. : (
  72. <div
  73. id="revision-toc-content"
  74. className="revision-toc-content mb-2"
  75. >
  76. </div>
  77. ) } */}
  78. </StickyStretchableScroller>
  79. );
  80. };
  81. export default TableOfContents;