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

Merge pull request #8726 from weseek/imprv/143963-144050-truncate-page-path-title-in-editor

imprv: Truncate page path title in editor
Shun Miyazawa 1 год назад
Родитель
Сommit
9921909d66

+ 1 - 1
apps/app/src/components/Navbar/GrowiContextualSubNavigation.tsx

@@ -309,7 +309,7 @@ const GrowiContextualSubNavigation = (props: GrowiContextualSubNavigationProps):
 
           <nav
             className={`${styles['grw-contextual-sub-navigation']}
-              d-flex align-items-center justify-content-end px-2 px-sm-3 px-md-4 py-1 gap-2 gap-md-4 d-print-none
+              d-flex align-items-center justify-content-end pe-2 pe-sm-3 pe-md-4 py-1 gap-2 gap-md-4 d-print-none
             `}
             data-testid="grw-contextual-sub-nav"
             id="grw-contextual-sub-nav"

+ 17 - 3
apps/app/src/components/PageHeader/PageHeader.tsx

@@ -1,6 +1,7 @@
-import type { FC } from 'react';
+import { useMemo, useRef } from 'react';
 
 import { useSWRxCurrentPage } from '~/stores/page';
+import { usePageControlsX } from '~/stores/ui';
 
 import { PagePathHeader } from './PagePathHeader';
 import { PageTitleHeader } from './PageTitleHeader';
@@ -9,21 +10,34 @@ import styles from './PageHeader.module.scss';
 
 const moduleClass = styles['page-header'] ?? '';
 
-export const PageHeader: FC = () => {
+export const PageHeader = (): JSX.Element => {
   const { data: currentPage } = useSWRxCurrentPage();
+  const { data: pageControlsX } = usePageControlsX();
+  const pageHeaderRef = useRef<HTMLDivElement>(null);
+
+  const maxWidth = useMemo(() => {
+    if (pageControlsX == null || pageHeaderRef.current == null) {
+      // Length that allows users to use PageHeader functionality.
+      return 300;
+    }
+    // At least 10px space between PageHeader and PageControls
+    return pageControlsX - pageHeaderRef.current.getBoundingClientRect().x - 10;
+  }, [pageControlsX]);
 
   if (currentPage == null) {
     return <></>;
   }
 
   return (
-    <div className={`${moduleClass} w-100`}>
+    <div className={`${moduleClass} w-100`} ref={pageHeaderRef}>
       <PagePathHeader
         currentPage={currentPage}
+        maxWidth={maxWidth}
       />
       <div className="mt-0 mt-md-1">
         <PageTitleHeader
           currentPage={currentPage}
+          maxWidth={maxWidth}
         />
       </div>
     </div>

+ 5 - 1
apps/app/src/components/PageHeader/PagePathHeader.module.scss

@@ -1,5 +1,4 @@
 .page-path-header :global {
-  max-width: calc(100vw - 650px);
   input {
     min-width: 20px;
     min-height: unset;
@@ -17,4 +16,9 @@
       transform: translateY(12px);
     }
   }
+
+  // Make Truncated elements horizontally scrollable and hide the scroll bar
+  .page-path-header-input {
+    scrollbar-width: none;
+  }
 }

+ 22 - 17
apps/app/src/components/PageHeader/PagePathHeader.tsx

@@ -1,7 +1,6 @@
 import {
   useState, useCallback, memo,
 } from 'react';
-import type { FC } from 'react';
 
 import type { IPagePopulatedToShowRevision } from '@growi/core';
 import { DevidedPagePath } from '@growi/core/dist/models';
@@ -25,11 +24,12 @@ const moduleClass = styles['page-path-header'];
 type Props = {
   currentPage: IPagePopulatedToShowRevision,
   className?: string,
+  maxWidth?: number,
 }
 
-export const PagePathHeader: FC<Props> = memo((props: Props) => {
+export const PagePathHeader = memo((props: Props): JSX.Element => {
   const { t } = useTranslation();
-  const { currentPage, className } = props;
+  const { currentPage, className, maxWidth } = props;
 
   const dPagePath = new DevidedPagePath(currentPage.path, true);
   const parentPagePath = dPagePath.former;
@@ -104,25 +104,28 @@ export const PagePathHeader: FC<Props> = memo((props: Props) => {
     <div
       id="page-path-header"
       className={`d-flex ${moduleClass} ${className ?? ''} small position-relative ms-2`}
+      style={{ maxWidth }}
       onMouseEnter={() => setHover(true)}
       onMouseLeave={() => setHover(false)}
     >
       <div
-        id="grw-page-path-header-container"
-        className="me-2 d-inline-block overflow-hidden"
+        className="page-path-header-input d-inline-block overflow-x-scroll"
       >
         { isRenameInputShown && (
-          <div className="position-absolute w-100">
-            <ClosableTextInput
-              value={editingParentPagePath}
-              placeholder={t('Input parent page path')}
-              inputClassName="form-control-sm"
-              onPressEnter={onPressEnter}
-              onPressEscape={onPressEscape}
-              onChange={onInputChange}
-              validationTarget={ValidationTarget.PAGE}
-              onClickOutside={onPressEscape}
-            />
+          <div className="position-relative">
+            <div className="position-absolute w-100">
+              <ClosableTextInput
+                value={editingParentPagePath}
+                placeholder={t('Input parent page path')}
+                inputClassName="form-control-sm"
+                onPressEnter={onPressEnter}
+                onPressEscape={onPressEscape}
+                onChange={onInputChange}
+                validationTarget={ValidationTarget.PAGE}
+                onClickOutside={onPressEscape}
+                useAutosizeInput
+              />
+            </div>
           </div>
         ) }
         <div
@@ -136,7 +139,9 @@ export const PagePathHeader: FC<Props> = memo((props: Props) => {
         </div>
       </div>
 
-      <div className={`page-path-header-buttons d-flex align-items-center ${isHover && !isRenameInputShown ? '' : 'invisible'}`}>
+      <div
+        className={`page-path-header-buttons d-flex align-items-center ${isHover && !isRenameInputShown ? '' : 'invisible'}`}
+      >
         <button
           type="button"
           className="btn btn-outline-neutral-secondary me-2 d-flex align-items-center justify-content-center"

+ 5 - 1
apps/app/src/components/PageHeader/PageTitleHeader.module.scss

@@ -1,5 +1,4 @@
 .page-title-header :global {
-  max-width: calc(100vw - 650px);
   input {
     min-width: 20px;
     min-height: unset;
@@ -7,6 +6,11 @@
     line-height: 1em;
     transform: translateX(0.05rem) translateY(0.05rem);
   }
+
+  // Make Truncated elements horizontally scrollable and hide the scroll bar
+  .page-title-header-input {
+    scrollbar-width: none;
+  }
 }
 
 .page-title-header-border-color {

+ 19 - 16
apps/app/src/components/PageHeader/PageTitleHeader.tsx

@@ -1,4 +1,3 @@
-import type { FC } from 'react';
 import { useState, useCallback, useEffect } from 'react';
 
 import nodePath from 'path';
@@ -24,11 +23,12 @@ const borderColorClass = styles['page-title-header-border-color'] ?? '';
 type Props = {
   currentPage: IPagePopulatedToShowRevision,
   className?: string,
+  maxWidth?: number,
 };
 
-export const PageTitleHeader: FC<Props> = (props) => {
+export const PageTitleHeader = (props: Props): JSX.Element => {
   const { t } = useTranslation();
-  const { currentPage } = props;
+  const { currentPage, maxWidth } = props;
 
   const currentPagePath = currentPage.path;
 
@@ -93,20 +93,23 @@ export const PageTitleHeader: FC<Props> = (props) => {
   // }, [currentPage._id, isNewlyCreatedPage]);
 
   return (
-    <div className={`d-flex ${moduleClass} ${props.className ?? ''} position-relative`}>
-      <div className="me-1 d-inline-block overflow-hidden">
+    <div className={`d-flex ${moduleClass} ${props.className ?? ''} position-relative`} style={{ maxWidth }}>
+      <div className="page-title-header-input me-1 d-inline-block overflow-x-scroll">
         { isRenameInputShown && (
-          <div className="position-absolute w-100">
-            <ClosableTextInput
-              value={isNewlyCreatedPage ? '' : editedPageTitle}
-              placeholder={t('Input page name')}
-              inputClassName="fs-4"
-              onPressEnter={onPressEnter}
-              onPressEscape={onPressEscape}
-              onChange={onInputChange}
-              onClickOutside={() => { setRenameInputShown(false) }}
-              validationTarget={ValidationTarget.PAGE}
-            />
+          <div className="position-relative">
+            <div className="position-absolute w-100">
+              <ClosableTextInput
+                value={isNewlyCreatedPage ? '' : editedPageTitle}
+                placeholder={t('Input page name')}
+                inputClassName="fs-4"
+                onPressEnter={onPressEnter}
+                onPressEscape={onPressEscape}
+                onChange={onInputChange}
+                onClickOutside={() => { setRenameInputShown(false) }}
+                validationTarget={ValidationTarget.PAGE}
+                useAutosizeInput
+              />
+            </div>
           </div>
         ) }
         <h1