GrowiSubNavigation.jsx 4.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149
  1. import React from 'react';
  2. import PropTypes from 'prop-types';
  3. import { withTranslation } from 'react-i18next';
  4. import { isTrashPage } from '@commons/util/path-utils';
  5. import DevidedPagePath from '@commons/models/devided-page-path';
  6. import LinkedPagePath from '@commons/models/linked-page-path';
  7. import PagePathHierarchicalLink from '@commons/components/PagePathHierarchicalLink';
  8. import { createSubscribedElement } from '../UnstatedUtils';
  9. import AppContainer from '../../services/AppContainer';
  10. import RevisionPathControls from '../Page/RevisionPathControls';
  11. import PageContainer from '../../services/PageContainer';
  12. import TagLabels from '../Page/TagLabels';
  13. import LikeButton from '../LikeButton';
  14. import BookmarkButton from '../BookmarkButton';
  15. import PageCreator from './PageCreator';
  16. import RevisionAuthor from './RevisionAuthor';
  17. // eslint-disable-next-line react/prop-types
  18. const PagePathNav = ({ pageId, pagePath, isPageForbidden }) => {
  19. const dPagePath = new DevidedPagePath(pagePath, false, true);
  20. let formerLink;
  21. let latterLink;
  22. // when the path is root or first level
  23. if (dPagePath.isRoot || dPagePath.isFormerRoot) {
  24. const linkedPagePath = new LinkedPagePath(pagePath);
  25. latterLink = <PagePathHierarchicalLink linkedPagePath={linkedPagePath} />;
  26. }
  27. // when the path is second level or deeper
  28. else {
  29. const linkedPagePathFormer = new LinkedPagePath(dPagePath.former);
  30. const linkedPagePathLatter = new LinkedPagePath(dPagePath.latter);
  31. formerLink = <PagePathHierarchicalLink linkedPagePath={linkedPagePathFormer} />;
  32. latterLink = <PagePathHierarchicalLink linkedPagePath={linkedPagePathLatter} basePath={dPagePath.former} />;
  33. }
  34. return (
  35. <div className="grw-page-path-nav">
  36. {formerLink}
  37. <span className="d-flex align-items-center flex-wrap">
  38. <h1 className="m-0">{latterLink}</h1>
  39. <RevisionPathControls
  40. pageId={pageId}
  41. pagePath={pagePath}
  42. isPageForbidden={isPageForbidden}
  43. />
  44. </span>
  45. </div>
  46. );
  47. };
  48. const GrowiSubNavigation = (props) => {
  49. const isPageForbidden = document.querySelector('#grw-subnav').getAttribute('data-is-forbidden-page') === 'true';
  50. const { appContainer, pageContainer } = props;
  51. const {
  52. pageId, path, createdAt, creator, updatedAt, revisionAuthor, isHeaderSticky, isSubnavCompact,
  53. } = pageContainer.state;
  54. const isPageNotFound = pageId == null;
  55. const isPageInTrash = isTrashPage(path);
  56. // Display only the RevisionPath
  57. if (isPageNotFound || isPageForbidden) {
  58. return (
  59. <div className="px-3 py-3 grw-subnavbar">
  60. <PagePathNav pageId={pageId} pagePath={path} isPageForbidden={isPageForbidden} />
  61. </div>
  62. );
  63. }
  64. const additionalClassNames = ['grw-subnavbar'];
  65. const layoutType = appContainer.getConfig().layoutType;
  66. if (layoutType === 'growi') {
  67. if (isHeaderSticky) {
  68. additionalClassNames.push('grw-subnavbar-sticky');
  69. }
  70. if (isSubnavCompact) {
  71. additionalClassNames.push('grw-subnavbar-compact');
  72. }
  73. }
  74. return (
  75. <div className={`d-flex align-items-center justify-content-between px-3 py-1 ${additionalClassNames.join(' ')}`}>
  76. {/* Page Path */}
  77. <div>
  78. <PagePathNav pageId={pageId} pagePath={path} isPageForbidden={isPageForbidden} />
  79. { !isPageNotFound && !isPageForbidden && (
  80. <TagLabels />
  81. ) }
  82. </div>
  83. <div className="d-flex align-items-center">
  84. { !isPageInTrash && (
  85. /* Header Button */
  86. <div className="mr-2">
  87. <LikeButton pageId={pageId} isLiked={pageContainer.state.isLiked} />
  88. </div>
  89. ) }
  90. { !isPageInTrash && (
  91. <div>
  92. <BookmarkButton pageId={pageId} crowi={appContainer} />
  93. </div>
  94. ) }
  95. {/* Page Authors */}
  96. <ul className="authors text-nowrap d-none d-lg-block d-edit-none">
  97. { creator != null && (
  98. <li>
  99. <PageCreator creator={creator} createdAt={createdAt} isCompactMode={isSubnavCompact} />
  100. </li>
  101. ) }
  102. { revisionAuthor != null && (
  103. <li className="mt-1">
  104. <RevisionAuthor revisionAuthor={revisionAuthor} updatedAt={updatedAt} isCompactMode={isSubnavCompact} />
  105. </li>
  106. ) }
  107. </ul>
  108. </div>
  109. </div>
  110. );
  111. };
  112. /**
  113. * Wrapper component for using unstated
  114. */
  115. const GrowiSubNavigationWrapper = (props) => {
  116. return createSubscribedElement(GrowiSubNavigation, props, [AppContainer, PageContainer]);
  117. };
  118. GrowiSubNavigation.propTypes = {
  119. t: PropTypes.func.isRequired, // i18next
  120. appContainer: PropTypes.instanceOf(AppContainer).isRequired,
  121. pageContainer: PropTypes.instanceOf(PageContainer).isRequired,
  122. };
  123. export default withTranslation()(GrowiSubNavigationWrapper);