Browse Source

Merge pull request #7996 from weseek/imprv/flex-expansion

imprv: Flex expansion
Yuki Takei 2 years ago
parent
commit
84f9f75ee0

+ 11 - 9
apps/app/src/client/services/layout.ts

@@ -1,7 +1,6 @@
 import type { IPage } from '@growi/core';
 import type { IPage } from '@growi/core';
 
 
 import { useIsContainerFluid } from '~/stores/context';
 import { useIsContainerFluid } from '~/stores/context';
-import { useSWRxCurrentPage } from '~/stores/page';
 import { useEditorMode } from '~/stores/ui';
 import { useEditorMode } from '~/stores/ui';
 
 
 export const useEditorModeClassName = (): string => {
 export const useEditorModeClassName = (): string => {
@@ -10,17 +9,20 @@ export const useEditorModeClassName = (): string => {
   return `${getClassNamesByEditorMode().join(' ') ?? ''}`;
   return `${getClassNamesByEditorMode().join(' ') ?? ''}`;
 };
 };
 
 
-export const useCurrentGrowiLayoutFluidClassName = (initialPage?: IPage): string => {
-  const { data: currentPage } = useSWRxCurrentPage();
-
+export const useLayoutFluidClassName = (expandContentWidth?: boolean | null): string => {
   const { data: dataIsContainerFluid } = useIsContainerFluid();
   const { data: dataIsContainerFluid } = useIsContainerFluid();
 
 
-  const page = currentPage ?? initialPage;
-  const isContainerFluidEachPage = page == null || !('expandContentWidth' in page)
-    ? null
-    : page.expandContentWidth;
   const isContainerFluidDefault = dataIsContainerFluid;
   const isContainerFluidDefault = dataIsContainerFluid;
-  const isContainerFluid = isContainerFluidEachPage ?? isContainerFluidDefault;
+  const isContainerFluid = expandContentWidth ?? isContainerFluidDefault;
 
 
   return isContainerFluid ? 'growi-layout-fluid' : '';
   return isContainerFluid ? 'growi-layout-fluid' : '';
 };
 };
+
+export const useLayoutFluidClassNameByPage = (initialPage?: IPage): string => {
+  const page = initialPage;
+  const expandContentWidth = page == null || !('expandContentWidth' in page)
+    ? null
+    : page.expandContentWidth;
+
+  return useLayoutFluidClassName(expandContentWidth);
+};

+ 5 - 3
apps/app/src/components/Layout/BasicLayout.tsx

@@ -35,12 +35,14 @@ export const BasicLayout = ({ children, className }: Props): JSX.Element => {
     <RawLayout className={className ?? ''}>
     <RawLayout className={className ?? ''}>
       <DndProvider backend={HTML5Backend}>
       <DndProvider backend={HTML5Backend}>
 
 
-        <div className="page-wrapper d-flex d-print-block">
+        <div className="page-wrapper flex-row">
           <Sidebar />
           <Sidebar />
 
 
-          <div className="flex-fill mw-0">
+          <div className="flex-expand-vert">{/* neccessary for nested {children} make expanded */}
             <AlertSiteUrlUndefined />
             <AlertSiteUrlUndefined />
-            {children}
+            <div className="flex-expand-horiz h-100">{/* neccessary for nested {children} make expanded */}
+              {children}
+            </div>
           </div>
           </div>
         </div>
         </div>
 
 

+ 1 - 30
apps/app/src/components/Layout/SearchResultLayout.module.scss

@@ -3,7 +3,7 @@
 
 
 .on-search :global {
 .on-search :global {
   .page-wrapper {
   .page-wrapper {
-    padding-bottom: unset;
+    height: 100vh;
   }
   }
 
 
   .search-control-include-options {
   .search-control-include-options {
@@ -12,15 +12,6 @@
     }
     }
   }
   }
   .search-result-list {
   .search-result-list {
-    .search-result-list-scroll {
-      // subtract the height of (SearchControl component + other factors)
-      height: calc(100vh - 110px);
-      overflow-y: scroll;
-
-      @include bs.media-breakpoint-down(sm) {
-        height: calc(100vh - (var.$grw-navbar-bottom-height + 123px));
-      }
-    }
 
 
     .search-result-keyword {
     .search-result-keyword {
       font-size: 17.5px;
       font-size: 17.5px;
@@ -54,7 +45,6 @@
     }
     }
 
 
     .search-result-content {
     .search-result-content {
-      height: 100vh;
 
 
       > h2 {
       > h2 {
         margin-right: 10px;
         margin-right: 10px;
@@ -67,12 +57,6 @@
       }
       }
 
 
       .search-result-content-body-container {
       .search-result-content-body-container {
-        // correct apply overflow scrolling for react-markdown on Google Chrome
-        // see: https://github.com/weseek/growi/pull/6731
-        position: relative;
-
-        overflow-y: auto;
-
         .wiki {
         .wiki {
           padding: 16px;
           padding: 16px;
           font-size: 14px;
           font-size: 14px;
@@ -81,16 +65,3 @@
     }
     }
   }
   }
 }
 }
-
-// style to apply when displaying search page
-.on-search :global {
-  // set sidebar height shown in search page
-  $search-page-sidebar-height: 100vh;
-
-  .grw-sidebar {
-    height: $search-page-sidebar-height;
-    .data-layout-container {
-      height: 100%;
-    }
-  }
-}

+ 1 - 4
apps/app/src/components/Layout/SearchResultLayout.tsx

@@ -12,10 +12,7 @@ const SearchResultLayout = ({ children }: Props): JSX.Element => {
 
 
   return (
   return (
     <BasicLayout className={`on-search ${commonStyles['on-search']}`}>
     <BasicLayout className={`on-search ${commonStyles['on-search']}`}>
-      <div id="grw-fav-sticky-trigger" className="sticky-top"></div>
-      <div id="main" className="main search-page mt-0">
-        { children }
-      </div>
+      { children }
     </BasicLayout>
     </BasicLayout>
   );
   );
 };
 };

+ 2 - 4
apps/app/src/components/Layout/ShareLinkLayout.tsx

@@ -22,10 +22,8 @@ export const ShareLinkLayout = ({ children }: Props): JSX.Element => {
   return (
   return (
     <RawLayout className={className}>
     <RawLayout className={className}>
 
 
-      <div className="page-wrapper d-flex d-print-block">
-        <div className="flex-fill mw-0">
-          {children}
-        </div>
+      <div className="page-wrapper">
+        {children}
       </div>
       </div>
 
 
       <GrowiNavbarBottom />
       <GrowiNavbarBottom />

+ 1 - 1
apps/app/src/components/PageEditor/EditorNavbarBottom.tsx

@@ -100,7 +100,7 @@ const EditorNavbarBottom = (): JSX.Element => {
         </Collapse>
         </Collapse>
       )
       )
       }
       }
-      <div className={`navbar navbar-expand border-top px-2 px-md-3 ${additionalClasses.join(' ')}`}>
+      <div className={`flex-expand-horiz align-items-center border-top px-2 px-md-3 ${additionalClasses.join(' ')}`}>
         <form className="form-inline">
         <form className="form-inline">
           { isDeviceSmallerThanMd && renderDrawerButton() }
           { isDeviceSmallerThanMd && renderDrawerButton() }
           { isOptionsSelectorEnabled && !isDeviceSmallerThanMd && <OptionsSelector /> }
           { isOptionsSelectorEnabled && !isDeviceSmallerThanMd && <OptionsSelector /> }

+ 3 - 3
apps/app/src/components/PageEditor/PageEditor.tsx

@@ -573,8 +573,8 @@ export const PageEditor = React.memo((props: Props): JSX.Element => {
   const isUploadable = isUploadableImage || isUploadableFile;
   const isUploadable = isUploadableImage || isUploadableFile;
 
 
   return (
   return (
-    <div data-testid="page-editor" id="page-editor" className={`d-flex flex-grow-1 overflow-auto ${props.visibility ? '' : 'd-none'}`}>
-      <div className="page-editor-editor-container flex-grow-1 flex-basis-0 mw-0">
+    <div data-testid="page-editor" id="page-editor" className={`flex-grow-1 d-flex overflow-y-auto ${props.visibility ? '' : 'd-none'}`}>
+      <div className="page-editor-editor-container flex-expand-vert">
         {/* <Editor
         {/* <Editor
           ref={editorRef}
           ref={editorRef}
           value={initialValue}
           value={initialValue}
@@ -589,7 +589,7 @@ export const PageEditor = React.memo((props: Props): JSX.Element => {
         /> */}
         /> */}
         <CodeMirrorEditorContainer ref={codeMirrorEditorContainerRef} />
         <CodeMirrorEditorContainer ref={codeMirrorEditorContainerRef} />
       </div>
       </div>
-      <div className="d-none d-lg-flex page-editor-preview-container justify-content-center flex-grow-1 flex-basis-0 mw-0">
+      <div className="page-editor-preview-container flex-expand-vert d-none d-lg-flex">
         <Preview
         <Preview
           ref={previewRef}
           ref={previewRef}
           rendererOptions={rendererOptions}
           rendererOptions={rendererOptions}

+ 1 - 1
apps/app/src/components/SearchPage/SearchControl.tsx

@@ -71,7 +71,7 @@ const SearchControl = React.memo((props: Props): JSX.Element => {
   }, [initialSearchConditions.keyword]);
   }, [initialSearchConditions.keyword]);
 
 
   return (
   return (
-    <div className="position-sticky sticky-top shadow-sm">
+    <div className="shadow-sm">
       <div className="grw-search-page-nav d-flex py-3 align-items-center">
       <div className="grw-search-page-nav d-flex py-3 align-items-center">
         <div className="flex-grow-1 mx-4">
         <div className="flex-grow-1 mx-4">
           <SearchForm
           <SearchForm

+ 43 - 45
apps/app/src/components/SearchPage/SearchPageBase.tsx

@@ -169,63 +169,61 @@ const SearchPageBaseSubstance: ForwardRefRenderFunction<ISelectableAll & IReturn
     : undefined;
     : undefined;
 
 
   return (
   return (
-    <div className="content-main">
-      <div className="search-result-base d-flex" data-testid="search-result-base">
+    <div className="search-result-base flex-grow-1 d-flex overflow-y-auto" data-testid="search-result-base">
 
 
-        <div className="mw-0 flex-grow-1 flex-basis-0 border boder-gray search-result-list" id="search-result-list">
+      <div className="flex-expand-vert border boder-gray search-result-list" id="search-result-list">
 
 
-          {searchControl}
+        {searchControl}
 
 
-          <div className="search-result-list-scroll">
+        <div className="overflow-y-scroll">
 
 
-            {/* Loading */}
-            { pages == null && (
-              <div className="mw-0 flex-grow-1 flex-basis-0 m-5 text-muted text-center">
-                <i className="fa fa-2x fa-spinner fa-pulse mr-1"></i>
-              </div>
-            ) }
+          {/* Loading */}
+          { pages == null && (
+            <div className="mw-0 flex-grow-1 flex-basis-0 m-5 text-muted text-center">
+              <i className="fa fa-2x fa-spinner fa-pulse mr-1"></i>
+            </div>
+          ) }
 
 
-            {/* Loaded */}
-            { pages != null && (
-              <>
-                <div className="my-3 px-md-4 px-3">
-                  {searchResultListHead}
-                </div>
+          {/* Loaded */}
+          { pages != null && (
+            <>
+              <div className="my-3 px-md-4 px-3">
+                {searchResultListHead}
+              </div>
 
 
-                { pages.length > 0 && (
-                  <div className={`page-list ${styles['page-list']} px-md-4`}>
-                    <SearchResultList
-                      ref={searchResultListRef}
-                      pages={pages}
-                      selectedPageId={selectedPageWithMeta?.data._id}
-                      forceHideMenuItems={forceHideMenuItems}
-                      onPageSelected={page => (setSelectedPageWithMeta(page))}
-                      onCheckboxChanged={checkboxChangedHandler}
-                    />
-                  </div>
-                ) }
-                <div className="my-4 d-flex justify-content-center">
-                  {searchPager}
+              { pages.length > 0 && (
+                <div className={`page-list ${styles['page-list']} px-md-4`}>
+                  <SearchResultList
+                    ref={searchResultListRef}
+                    pages={pages}
+                    selectedPageId={selectedPageWithMeta?.data._id}
+                    forceHideMenuItems={forceHideMenuItems}
+                    onPageSelected={page => (setSelectedPageWithMeta(page))}
+                    onCheckboxChanged={checkboxChangedHandler}
+                  />
                 </div>
                 </div>
-              </>
-            ) }
-
-          </div>
+              ) }
+              <div className="my-4 d-flex justify-content-center">
+                {searchPager}
+              </div>
+            </>
+          ) }
 
 
         </div>
         </div>
 
 
-        <div className="mw-0 flex-grow-1 flex-basis-0 d-none d-lg-block search-result-content">
-          {pages != null && pages.length !== 0 && selectedPageWithMeta != null && (
-            <SearchResultContent
-              pageWithMeta={selectedPageWithMeta}
-              highlightKeywords={highlightKeywords}
-              showPageControlDropdown={!(isGuestUser || isReadOnlyUser)}
-              forceHideMenuItems={forceHideMenuItems}
-            />
-          )}
-        </div>
+      </div>
 
 
+      <div className="flex-expand-vert d-none d-lg-flex search-result-content">
+        {pages != null && pages.length !== 0 && selectedPageWithMeta != null && (
+          <SearchResultContent
+            pageWithMeta={selectedPageWithMeta}
+            highlightKeywords={highlightKeywords}
+            showPageControlDropdown={!(isGuestUser || isReadOnlyUser)}
+            forceHideMenuItems={forceHideMenuItems}
+          />
+        )}
       </div>
       </div>
+
     </div>
     </div>
   );
   );
 };
 };

+ 16 - 2
apps/app/src/components/SearchPage/SearchResultContent.tsx

@@ -10,6 +10,7 @@ import { animateScroll } from 'react-scroll';
 import { DropdownItem } from 'reactstrap';
 import { DropdownItem } from 'reactstrap';
 import { debounce } from 'throttle-debounce';
 import { debounce } from 'throttle-debounce';
 
 
+import { useLayoutFluidClassName } from '~/client/services/layout';
 import { exportAsMarkdown, updateContentWidth } from '~/client/services/page-operation';
 import { exportAsMarkdown, updateContentWidth } from '~/client/services/page-operation';
 import { toastSuccess } from '~/client/util/toastr';
 import { toastSuccess } from '~/client/util/toastr';
 import type { IPageWithSearchMeta } from '~/interfaces/search';
 import type { IPageWithSearchMeta } from '~/interfaces/search';
@@ -126,6 +127,9 @@ export const SearchResultContent: FC<Props> = (props: Props) => {
 
 
   const [isExpandContentWidth, setIsExpandContentWidth] = useState(page.expandContentWidth);
   const [isExpandContentWidth, setIsExpandContentWidth] = useState(page.expandContentWidth);
 
 
+  // TODO: determine className by the 'expandContentWidth' from the updated page
+  const growiLayoutFluidClass = useLayoutFluidClassName(isExpandContentWidth);
+
   const duplicateItemClickedHandler = useCallback(async(pageToDuplicate) => {
   const duplicateItemClickedHandler = useCallback(async(pageToDuplicate) => {
     // eslint-disable-next-line @typescript-eslint/no-unused-vars
     // eslint-disable-next-line @typescript-eslint/no-unused-vars
     const duplicatedHandler: OnDuplicatedFunction = (fromPath, toPath) => {
     const duplicatedHandler: OnDuplicatedFunction = (fromPath, toPath) => {
@@ -206,7 +210,13 @@ export const SearchResultContent: FC<Props> = (props: Props) => {
   const isRenderable = page != null && rendererOptions != null;
   const isRenderable = page != null && rendererOptions != null;
 
 
   return (
   return (
-    <div key={page._id} data-testid="search-result-content" className={`search-result-content ${styles['search-result-content']} d-flex flex-column`}>
+    <div
+      key={page._id}
+      data-testid="search-result-content"
+      className={`search-result-content ${styles['search-result-content']}
+        dynamic-layout-root ${growiLayoutFluidClass}
+        overflow-y-auto`}
+    >
       <div className="grw-page-path-text-muted-container">
       <div className="grw-page-path-text-muted-container">
         { isRenderable && (
         { isRenderable && (
           <GrowiSubNavigation
           <GrowiSubNavigation
@@ -218,7 +228,11 @@ export const SearchResultContent: FC<Props> = (props: Props) => {
           />
           />
         ) }
         ) }
       </div>
       </div>
-      <div id="search-result-content-body-container" className="search-result-content-body-container" ref={scrollElementRef}>
+      <div
+        id="search-result-content-body-container"
+        ref={scrollElementRef}
+        className="search-result-content-body-container main container-lg grw-container-convertible overflow-y-scroll"
+      >
         { isRenderable && (
         { isRenderable && (
           <RevisionLoader
           <RevisionLoader
             rendererOptions={rendererOptions}
             rendererOptions={rendererOptions}

+ 3 - 3
apps/app/src/pages/[[...path]].page.tsx

@@ -20,7 +20,7 @@ import Head from 'next/head';
 import { useRouter } from 'next/router';
 import { useRouter } from 'next/router';
 import superjson from 'superjson';
 import superjson from 'superjson';
 
 
-import { useCurrentGrowiLayoutFluidClassName, useEditorModeClassName } from '~/client/services/layout';
+import { useLayoutFluidClassNameByPage, useEditorModeClassName } from '~/client/services/layout';
 import { PageView } from '~/components/Page/PageView';
 import { PageView } from '~/components/Page/PageView';
 import { DrawioViewerScript } from '~/components/Script/DrawioViewerScript'; import type { CrowiRequest } from '~/interfaces/crowi-request';
 import { DrawioViewerScript } from '~/components/Script/DrawioViewerScript'; import type { CrowiRequest } from '~/interfaces/crowi-request';
 import type { EditorConfig } from '~/interfaces/editor-settings';
 import type { EditorConfig } from '~/interfaces/editor-settings';
@@ -254,7 +254,7 @@ const Page: NextPageWithLayout<Props> = (props: Props) => {
   useSetupGlobalSocket();
   useSetupGlobalSocket();
   useSetupGlobalSocketForPage(pageId);
   useSetupGlobalSocketForPage(pageId);
 
 
-  const growiLayoutFluidClass = useCurrentGrowiLayoutFluidClassName(pageWithMeta?.data);
+  const growiLayoutFluidClass = useLayoutFluidClassNameByPage(pageWithMeta?.data);
 
 
   // Store initial data (When revisionBody is not SSR)
   // Store initial data (When revisionBody is not SSR)
   useEffect(() => {
   useEffect(() => {
@@ -329,7 +329,7 @@ const Page: NextPageWithLayout<Props> = (props: Props) => {
       <Head>
       <Head>
         <title>{title}</title>
         <title>{title}</title>
       </Head>
       </Head>
-      <div className={`dynamic-layout-root ${growiLayoutFluidClass} h-100 d-flex flex-column justify-content-between`}>
+      <div className={`dynamic-layout-root ${growiLayoutFluidClass} justify-content-between`}>
         <header className="py-0 position-relative">
         <header className="py-0 position-relative">
           <div id="grw-subnav-container">
           <div id="grw-subnav-container">
             <GrowiContextualSubNavigation isLinkSharingDisabled={props.disableLinkSharing} />
             <GrowiContextualSubNavigation isLinkSharingDisabled={props.disableLinkSharing} />

+ 1 - 3
apps/app/src/pages/_search.page.tsx

@@ -73,9 +73,7 @@ const SearchResultPage: NextPageWithLayout<Props> = (props: Props) => {
         <title>{title}</title>
         <title>{title}</title>
       </Head>
       </Head>
 
 
-      <div id="search-page" className="dynamic-layout-root">
-        <SearchPage />
-      </div>
+      <SearchPage />
     </>
     </>
   );
   );
 };
 };

+ 3 - 3
apps/app/src/pages/share/[[...path]].page.tsx

@@ -8,7 +8,7 @@ import { serverSideTranslations } from 'next-i18next/serverSideTranslations';
 import Head from 'next/head';
 import Head from 'next/head';
 import superjson from 'superjson';
 import superjson from 'superjson';
 
 
-import { useCurrentGrowiLayoutFluidClassName } from '~/client/services/layout';
+import { useLayoutFluidClassNameByPage } from '~/client/services/layout';
 import { ShareLinkLayout } from '~/components/Layout/ShareLinkLayout';
 import { ShareLinkLayout } from '~/components/Layout/ShareLinkLayout';
 import GrowiContextualSubNavigationSubstance from '~/components/Navbar/GrowiContextualSubNavigation';
 import GrowiContextualSubNavigationSubstance from '~/components/Navbar/GrowiContextualSubNavigation';
 import { DrawioViewerScript } from '~/components/Script/DrawioViewerScript';
 import { DrawioViewerScript } from '~/components/Script/DrawioViewerScript';
@@ -107,7 +107,7 @@ const SharedPage: NextPageWithLayout<Props> = (props: Props) => {
   }, [mutateCurrentPage, props.isNotFound, props.shareLink?.relatedPage._id, props.skipSSR]);
   }, [mutateCurrentPage, props.isNotFound, props.shareLink?.relatedPage._id, props.skipSSR]);
 
 
 
 
-  const growiLayoutFluidClass = useCurrentGrowiLayoutFluidClassName(props.shareLinkRelatedPage);
+  const growiLayoutFluidClass = useLayoutFluidClassNameByPage(props.shareLinkRelatedPage);
 
 
   const pagePath = props.shareLinkRelatedPage?.path ?? '';
   const pagePath = props.shareLinkRelatedPage?.path ?? '';
 
 
@@ -119,7 +119,7 @@ const SharedPage: NextPageWithLayout<Props> = (props: Props) => {
         <title>{title}</title>
         <title>{title}</title>
       </Head>
       </Head>
 
 
-      <div className={`dynamic-layout-root ${growiLayoutFluidClass} h-100 d-flex flex-column justify-content-between`}>
+      <div className={`dynamic-layout-root ${growiLayoutFluidClass} justify-content-between`}>
         <header className="py-0 position-relative">
         <header className="py-0 position-relative">
           <GrowiContextualSubNavigationForSharedPage page={currentPage ?? props.shareLinkRelatedPage} isLinkSharingDisabled={props.disableLinkSharing} />
           <GrowiContextualSubNavigationForSharedPage page={currentPage ?? props.shareLinkRelatedPage} isLinkSharingDisabled={props.disableLinkSharing} />
         </header>
         </header>

+ 18 - 0
apps/app/src/styles/_layout.scss

@@ -1,7 +1,20 @@
 @use '@growi/core/scss/bootstrap/init' as bs;
 @use '@growi/core/scss/bootstrap/init' as bs;
+@use '@growi/core/scss/flex-expand';
 
 
 @use './variables' as var;
 @use './variables' as var;
 
 
+.flex-expand-horiz {
+  @extend %flex-expand-horiz;
+}
+
+.flex-expand-vert {
+  @extend %flex-expand-vert;
+}
+
+.dynamic-layout-root {
+  @extend %flex-expand-vert;
+}
+
 .dynamic-layout-root.growi-layout-fluid .grw-container-convertible {
 .dynamic-layout-root.growi-layout-fluid .grw-container-convertible {
   width: 100%;
   width: 100%;
   max-width: none;
   max-width: none;
@@ -27,6 +40,11 @@
   overflow-y: scroll;
   overflow-y: scroll;
 }
 }
 
 
+.page-wrapper {
+  display: flex;
+  flex-direction: column;
+}
+
 // padding settings for GrowiNavbarBottom
 // padding settings for GrowiNavbarBottom
 .page-wrapper {
 .page-wrapper {
   padding-bottom: var.$grw-navbar-bottom-height;
   padding-bottom: var.$grw-navbar-bottom-height;

+ 2 - 0
apps/app/src/styles/style-app.scss

@@ -1,3 +1,5 @@
+@import '@growi/core/scss/flex-expand';
+
 @import 'mixins';
 @import 'mixins';
 
 
 // atoms
 // atoms

+ 1 - 1
apps/app/test/cypress/e2e/20-basic-features/20-basic-features--access-to-page.cy.ts

@@ -237,7 +237,7 @@ context('Access to Template Editing Mode', () => {
     cy.waitUntilSkeletonDisappear();
     cy.waitUntilSkeletonDisappear();
 
 
     // Check if the template is applied
     // Check if the template is applied
-    cy.get('.content-main').within(() => {
+    cy.getByTestid('search-result-base').within(() => {
       cy.get('.wiki').should('be.visible');
       cy.get('.wiki').should('be.visible');
       cy.get('.wiki').children().first().should('have.text', expectedBody);
       cy.get('.wiki').children().first().should('have.text', expectedBody);
     })
     })

+ 9 - 0
packages/core/scss/_flex-expand.scss

@@ -0,0 +1,9 @@
+@use './placeholders/flex-expand';
+
+.flex-expand-horiz {
+  @extend %flex-expand-horiz;
+}
+
+.flex-expand-vert {
+  @extend %flex-expand-vert;
+}

+ 13 - 0
packages/core/scss/placeholders/_flex-expand.scss

@@ -0,0 +1,13 @@
+// ref: https://discuss.codemirror.net/t/how-to-fit-the-codemirror-6-widget-into-a-flex-div/4207/4
+%flex-expand-horiz {
+  display: flex;
+  flex: 1;
+  flex-direction: row;
+}
+
+// ref: https://discuss.codemirror.net/t/how-to-fit-the-codemirror-6-widget-into-a-flex-div/4207/4
+%flex-expand-vert {
+  display: flex;
+  flex: 1;
+  flex-direction: column;
+}

+ 4 - 4
packages/editor/src/components/playground/Playground.tsx

@@ -15,18 +15,18 @@ export const Playground = (): JSX.Element => {
 
 
   return (
   return (
     <>
     <>
-      <div className="flex-grow-1 d-flex flex-column justify-content-center align-items-center bg-dark" style={{ minHeight: '83px' }}>
+      <div className="flex-expand-vert justify-content-center align-items-center bg-dark" style={{ minHeight: '83px' }}>
         <div className="text-white">GrowiSubNavigation</div>
         <div className="text-white">GrowiSubNavigation</div>
       </div>
       </div>
       <div className="flex-grow-1 d-flex overflow-y-auto">
       <div className="flex-grow-1 d-flex overflow-y-auto">
-        <div className="flex-grow-1 d-flex flex-column" style={{ flexBasis: 0 }}>
+        <div className="flex-expand-vert">
           <CodeMirrorEditorContainer ref={containerRef} />
           <CodeMirrorEditorContainer ref={containerRef} />
         </div>
         </div>
-        <div className="flex-grow-1 mw-0 d-flex flex-column bg-light border-start border-dark-subtle p-3" style={{ flexBasis: 0 }}>
+        <div className="flex-expand-vert d-none d-lg-flex bg-light border-start border-dark-subtle p-3">
           <PlaygroundController />
           <PlaygroundController />
         </div>
         </div>
       </div>
       </div>
-      <div className="flex-grow-1 d-flex flex-column justify-content-center align-items-center bg-dark" style={{ minHeight: '50px' }}>
+      <div className="flex-expand-vert justify-content-center align-items-center bg-dark" style={{ minHeight: '50px' }}>
         <div className="text-white">EditorNavbarBottom</div>
         <div className="text-white">EditorNavbarBottom</div>
       </div>
       </div>
     </>
     </>

+ 2 - 0
packages/editor/src/main.scss

@@ -1 +1,3 @@
 @import 'bootstrap';
 @import 'bootstrap';
+
+@import '@growi/core/scss/flex-expand';