Просмотр исходного кода

Merge branch 'master' into fix/113849-error-toaster-appears-after-renaming

Shun Miyazawa 3 лет назад
Родитель
Сommit
54b9ab6fe0

+ 2 - 0
packages/app/src/components/Layout/AdminLayout.tsx

@@ -10,6 +10,7 @@ import styles from './Admin.module.scss';
 
 
 const AdminNavigation = dynamic(() => import('~/components/Admin/Common/AdminNavigation'), { ssr: false });
+const PageCreateModal = dynamic(() => import('../PageCreateModal'), { ssr: false });
 const SystemVersion = dynamic(() => import('../SystemVersion'), { ssr: false });
 const HotkeysManager = dynamic(() => import('../Hotkeys/HotkeysManager'), { ssr: false });
 
@@ -45,6 +46,7 @@ const AdminLayout = ({
           </div>
         </div>
 
+        <PageCreateModal />
         <SystemVersion />
       </div>
 

+ 8 - 2
packages/app/src/components/PageComment/Comment.tsx

@@ -2,7 +2,7 @@ import React, { useEffect, useMemo, useState } from 'react';
 
 import { IUser, pathUtils } from '@growi/core';
 import { UserPicture } from '@growi/ui';
-import { format } from 'date-fns';
+import { format, parseISO } from 'date-fns';
 import { useTranslation } from 'next-i18next';
 import Link from 'next/link';
 import { UncontrolledTooltip } from 'reactstrap';
@@ -79,12 +79,18 @@ export const Comment = (props: CommentProps): JSX.Element => {
   const getRootClassName = (comment: ICommentHasId) => {
     let className = 'page-comment flex-column';
 
+    // TODO: fix so that `comment.createdAt` to be type Date https://redmine.weseek.co.jp/issues/113876
+    let commentCreatedAt = comment.createdAt;
+    if (typeof commentCreatedAt === 'string') {
+      commentCreatedAt = parseISO(commentCreatedAt);
+    }
+
     // Conditional for called from SearchResultContext
     if (revisionId != null && revisionCreatedAt != null) {
       if (comment.revision === revisionId) {
         className += ' page-comment-current';
       }
-      else if (comment.createdAt.getTime() > revisionCreatedAt.getTime()) {
+      else if (commentCreatedAt.getTime() > revisionCreatedAt.getTime()) {
         className += ' page-comment-newer';
       }
       else {

+ 4 - 0
packages/app/src/components/SearchPage/SearchControl.tsx

@@ -66,6 +66,10 @@ const SearchControl: FC <Props> = React.memo((props: Props) => {
     invokeSearch();
   }, [invokeSearch]);
 
+  useEffect(() => {
+    setKeyword(initialSearchConditions.keyword ?? '');
+  }, [initialSearchConditions.keyword]);
+
   return (
     <div className="position-sticky sticky-top shadow-sm">
       <div className="grw-search-page-nav d-flex py-3 align-items-center">

+ 11 - 0
packages/app/src/components/SearchTypeahead.tsx

@@ -45,6 +45,7 @@ type Props = TypeaheadProps & {
 
 // see https://github.com/ericgio/react-bootstrap-typeahead/issues/266#issuecomment-414987723
 type TypeaheadInstance = {
+  setState(input: { text: string | undefined; }): void;
   clear: () => void,
   focus: () => void,
   toggleMenu: () => void,
@@ -164,6 +165,16 @@ const SearchTypeahead: ForwardRefRenderFunction<IFocusable, Props> = (props: Pro
     }
   }, [onSearchError, searchError]);
 
+  useEffect(() => {
+    // update input with Next Link
+    // update input workaround. see: https://github.com/ericgio/react-bootstrap-typeahead/issues/266#issuecomment-414987723
+    if (typeaheadRef.current != null) {
+      typeaheadRef.current.setState({
+        text: keywordOnInit,
+      });
+    }
+  }, [keywordOnInit]);
+
   const labelKey = useCallback((option?: IPageWithSearchMeta) => {
     return option?.data.path ?? '';
   }, []);

+ 3 - 0
packages/app/src/components/Sidebar/PageTree/Item.tsx

@@ -55,6 +55,9 @@ const markTarget = (children: ItemNode[], targetPathOrId?: Nullable<string>): vo
     if (node.page._id === targetPathOrId || node.page.path === targetPathOrId) {
       node.page.isTarget = true;
     }
+    else {
+      node.page.isTarget = false;
+    }
     return node;
   });
 };

+ 14 - 3
packages/app/src/components/TagCloudBox.tsx

@@ -1,7 +1,10 @@
 import React, { FC, memo } from 'react';
 
+import Link from 'next/link';
+
 import { IDataTagCount } from '~/interfaces/tag';
 
+
 type Props = {
   tags:IDataTagCount[],
   minSize?: number,
@@ -22,10 +25,18 @@ const TagCloudBox: FC<Props> = memo((props:(Props & typeof defaultProps)) => {
 
   const tagElements = tags.map((tag:IDataTagCount) => {
     const tagNameFormat = (tag.name).length > maxTagTextLength ? `${(tag.name).slice(0, maxTagTextLength)}...` : tag.name;
+
+    const url = new URL('/_search', 'https://example.com');
+    url.searchParams.append('q', `tag:${tag.name}`);
+
     return (
-      <a key={tag.name} href={`/_search?q=tag:${tag.name}`} className="grw-tag-label badge badge-secondary mr-2">
-        {tagNameFormat}
-      </a>
+      <Link
+        key={tag.name} href={`${url.pathname}${url.search}`}
+      >
+        <a className="grw-tag-label badge badge-secondary mr-2">
+          {tagNameFormat}
+        </a>
+      </Link>
     );
   });
 

+ 4 - 1
packages/app/src/components/TagList.tsx

@@ -33,10 +33,13 @@ const TagList: FC<TagListProps> = (props:(TagListProps & typeof defaultProps)) =
     return tagData.map((tag:IDataTagCount, index:number) => {
       const tagListClasses: string = index === 0 ? 'list-group-item d-flex' : 'list-group-item d-flex border-top-0';
 
+      const url = new URL('/_search', 'https://example.com');
+      url.searchParams.append('q', `tag:${tag.name}`);
+
       return (
         <Link
           key={tag._id}
-          href={`/_search?q=tag:${encodeURIComponent(tag.name)}`}
+          href={`${url.pathname}${url.search}`}
         >
           <a
             className={tagListClasses}

+ 6 - 0
packages/app/src/styles/theme/_apply-colors-dark.scss

@@ -190,6 +190,12 @@
 
     .nologin-dialog {
       background-color: rgba(black, 0.5);
+      .link-switch {
+        color: #7b9bd5;
+        @include hover() {
+          color: lighten(#7b9bd5,10%);
+        }
+      }
     }
 
     .input-group {

+ 6 - 0
packages/app/src/styles/theme/_apply-colors-light.scss

@@ -113,6 +113,12 @@
 
     .nologin-dialog {
       background-color: rgba(white, 0.5);
+      .link-switch {
+        color: #1939b8;
+        @include hover() {
+          color: lighten(#1939b8,20%);
+        }
+      }
     }
 
     .dropdown-with-icon {

+ 4 - 0
packages/core/src/test/util/page-path-utils.test.js

@@ -104,6 +104,10 @@ describe('isCreatablePage test', () => {
     expect(isCreatablePage('http://demo.growi.org/hoge')).toBeFalsy();
     expect(isCreatablePage('https://demo.growi.org/hoge')).toBeFalsy();
 
+    // include backslash
+    expect(isCreatablePage('/foo\\/bar')).toBeFalsy();
+    expect(isCreatablePage('/foo\\\\bar')).toBeFalsy();
+
     expect(isCreatablePage('/_search')).toBeFalsy();
     expect(isCreatablePage('/_search/foo')).toBeFalsy();
     expect(isCreatablePage('/_private-legacy-pages')).toBeFalsy();

+ 1 - 0
packages/core/src/utils/page-path-utils.ts

@@ -117,6 +117,7 @@ const restrictedPatternsToCreate: Array<RegExp> = [
   /.+\.md$/,
   /^(\.\.)$/, // see: https://github.com/weseek/growi/issues/3582
   /(\/\.\.)\/?/, // see: https://github.com/weseek/growi/issues/3582
+  /\\/, // see: https://github.com/weseek/growi/issues/7241
   /^\/(_search|_private-legacy-pages)(\/.*|$)/,
   /^\/(installer|register|login|logout|admin|me|files|trash|paste|comments|tags|share)(\/.*|$)/,
   /^\/user\/[^/]+$/, // see: https://regex101.com/r/utVQct/1

+ 0 - 162
packages/preset-themes/src/styles/_mixins.scss

@@ -1,162 +0,0 @@
-@use './bootstrap/init' as bs;
-
-@mixin variable-font-size($basesize) {
-  font-size: $basesize * 0.6;
-
-  @include bs.media-breakpoint-only(sm) {
-    font-size: #{$basesize * 0.7};
-  }
-  @include bs.media-breakpoint-only(md) {
-    font-size: #{$basesize * 0.8};
-  }
-  @include bs.media-breakpoint-only(lg) {
-    font-size: #{$basesize * 0.9};
-  }
-  @include bs.media-breakpoint-up(xl) {
-    font-size: $basesize;
-  }
-}
-
-@mixin expand-editor($editor-margin-top) {
-  $header-plus-footer: $editor-margin-top + $grw-editor-navbar-bottom-height;
-
-  $editor-margin: $header-plus-footer //
-    + 25px //   add .btn-open-dropzone height
-    + 30px; //  add .navbar-editor height
-
-  .main {
-    width: 100%;
-    height: calc(100vh - #{$editor-margin-top});
-    margin-top: 0px !important;
-
-    .grw-container-convertible {
-      max-width: unset;
-      padding: 0;
-      margin: 0;
-    }
-
-    &,
-    .content-main,
-    .tab-content {
-      display: flex;
-      flex: 1;
-      flex-direction: column;
-
-      .tab-pane {
-        height: calc(100vh - #{$header-plus-footer});
-        min-height: calc(100vh - #{$header-plus-footer}); // for IE11
-      }
-
-      #page-editor {
-        // right(preview)
-        &,
-        & > .row,
-        .page-editor-preview-container,
-        .page-editor-preview-body {
-          height: calc(100vh - #{$header-plus-footer});
-          min-height: calc(100vh - #{$header-plus-footer}); // for IE11
-        }
-
-        // left(editor)
-        .page-editor-editor-container {
-          height: calc(100vh - #{$header-plus-footer});
-          min-height: calc(100vh - #{$header-plus-footer}); // for IE11
-
-          .react-codemirror2,
-          .CodeMirror,
-          .CodeMirror-scroll,
-          .textarea-editor {
-            height: calc(100vh - #{$editor-margin});
-          }
-        }
-      }
-
-      #page-editor-with-hackmd {
-        &,
-        .hackmd-preinit,
-        .hackmd-error,
-        #iframe-hackmd-container > iframe {
-          width: 100%;
-          height: calc(100vh - #{$header-plus-footer});
-          min-height: calc(100vh - #{$header-plus-footer}); // for IE11
-        }
-      }
-    }
-  }
-}
-
-@mixin apply-navigation-transition() {
-  transition-timing-function: cubic-bezier(0.25, 1, 0.5, 1);
-  transition-duration: 300ms;
-}
-
-@mixin border-vertical($beforeOrAfter, $borderLength, $zIndex: initial, $isBtnGroup: false) {
-  position: relative;
-  @if $isBtnGroup {
-    &:not(:first-child) {
-      margin-left: 0;
-      border-left: none;
-    }
-    &:not(:last-child) {
-      border-right: none;
-    }
-  }
-  &:not(:first-child) {
-    &::#{$beforeOrAfter} {
-      position: absolute;
-      top: calc((100% - #{$borderLength}) / 2);
-      left: 0;
-      z-index: $zIndex;
-      width: 100%;
-      height: $borderLength;
-      margin-left: -0.5px;
-      content: '';
-      border-left: 1px solid transparent;
-      transition: border-color 0.15s ease-in-out;
-    }
-  }
-}
-
-@keyframes fadeout {
-  100% {
-    opacity: 0;
-  }
-}
-
-@mixin blink-bgcolor($bgcolor) {
-  position: relative;
-  z-index: 1;
-
-  &::after {
-    position: absolute;
-    top: 15%;
-    left: 0;
-    z-index: -1;
-    width: 100%;
-    height: 70%;
-    content: '';
-    background-color: $bgcolor;
-    border-radius: 2px;
-    animation: fadeout 1s ease-in 1.5s forwards;
-  }
-}
-
-@mixin overlay-processing-style($additionalSelector, $contentFontSize: inherit, $contentPadding: inherit) {
-  .overlay.#{$additionalSelector} {
-    background: rgba(255, 255, 255, 0.5);
-    .overlay-content {
-      padding: $contentPadding;
-      font-size: $contentFontSize;
-      color: bs.$gray-700;
-      background: rgba(200, 200, 200, 0.5);
-    }
-  }
-}
-
-@mixin insertSimpleLineIcons($code) {
-  &:before {
-    margin-right: 0.2em;
-    font-family: 'simple-line-icons';
-    content: $code;
-  }
-}

+ 1 - 2
packages/preset-themes/src/styles/antarctic.scss

@@ -161,8 +161,7 @@
 
   // login and register
   .nologin {
-    a#login.link-switch,
-    a#register.link-switch {
+    .nologin-dialog a.link-switch {
       color: rgba(black, 0.5);
     }
 

+ 3 - 3
packages/preset-themes/src/styles/christmas.scss

@@ -160,9 +160,9 @@
     .nologin-header,
     .nologin-dialog {
       background-color: rgba(#ccc, 0.5);
-    }
-    .link-switch {
-      color: #bd3425;
+      a.link-switch {
+        color: #bd3425;
+      }
     }
 
     .grw-external-auth-form {

+ 4 - 5
packages/preset-themes/src/styles/hufflepuff.scss

@@ -154,10 +154,9 @@
     .nologin-header,
     .nologin-dialog {
       background-color: rgba(black, 0.1);
-    }
-
-    .link-switch {
-      color: #{hsl.darken(var(--color-global),10%)};
+      a.link-switch  {
+        color: #{hsl.darken(var(--color-global),10%)};
+      }
     }
 
     .grw-external-auth-form {
@@ -353,7 +352,7 @@
     }
 
     .link-switch {
-      color: var(--color-global);
+      color: var(--color-global)!important;
     }
 
     .grw-external-auth-form {

+ 3 - 4
packages/preset-themes/src/styles/spring.scss

@@ -145,10 +145,9 @@
     .nologin-header,
     .nologin-dialog {
       background-color: rgba(black, 0.1);
-    }
-
-    .link-switch {
-      color: var(--color-global);
+      a.link-switch {
+        color: var(--color-global);
+      }
     }
 
     .grw-external-auth-form {

+ 3 - 4
packages/preset-themes/src/styles/wood.scss

@@ -175,10 +175,9 @@
     .nologin-header,
     .nologin-dialog {
       background-color: rgba(black, 0.1);
-    }
-
-    .link-switch {
-      color: rgba(black, 0.5);
+      a.link-switch {
+        color: rgba(black, 0.5);
+      }
     }
 
     .grw-external-auth-form {