Office UI Fabric 的布局问题

Layout problems with Office UI Fabric

[编辑:更新以将问题集中在覆盖问题上;还对 CodePens 进行了编辑] [EDIT2:更新以包含有关 Mohamed Mansour 建议的方法的详细信息]

我在设计 Office UI Fabric React UI 的布局时遇到困难。这是我目前的布局:

目标是将顶部的 CommandBar 和 headers(文本标签和搜索框)固定在顶部,DetailsList 和归档按钮固定在底部并占据尽可能多的空间 space 尽可能在 header 区域之后。

我已成功使用 ScrollablePane 和两个 Sticky 组件将 CommandBar 和按钮固定在所需位置。但是,DetailsList 滚动到包含 CommandBar 和 header 的顶部 Sticky,按钮略微覆盖在 DetailsList 网格的顶部:

我试过使用 Fabric Grid 组件来布置所有部分,但它没有什么不同(如果它有帮助的话)。最好的布局是在两个 Sticky 元素之间放置项目部分;如果将项目部分添加到 Header 或 Footer Sticky 元素中的任何一个,则布局很糟糕。如何确保 DetailsList 不会在顶部和底部组件下方滚动?

这里有两支代码笔可以说明问题:

无网格:https://codepen.io/elegault/pen/aQayvY?editors=0010(文件Header为其元素使用网格,但所有其他元素不在网格中)

const columns = [{
  key: 'projectNameColumn',
  name: 'Project',
  fieldName: 'name',
  minWidth: 100,
  maxWidth: 200,
  isResizable: true,
  ariaLabel: 'Operations for Project'
}];
const items = [{
    id: '0',
    name: 'ABC Construction'
  },
  {
    id: '1',
    name: 'Air Bee and Bee'
  },
  {
    id: '2',
    name: 'Architectural Salvage'
  },
  {
    id: '3',
    name: 'Arkham Airport'
  },
  {
    id: '4',
    name: 'Arkham Assembly Hall'
  },
  {
    id: '5',
    name: 'Arkham Library'
  },
  {
    id: '6',
    name: 'zArkham Renovation'
  },
  {
    id: '7',
    name: 'Foo'
  },
  {
    id: '8',
    name: 'Foo1'
  },
  {
    id: '9',
    name: 'Foo2'
  },
  {
    id: '10',
    name: 'Foo3'
  },
  {
    id: '11',
    name: 'Foo4'
  },
  {
    id: '12',
    name: 'Foo5'
  },
  {
    id: '13',
    name: 'Foo6'
  },
  {
    id: '14',
    name: 'Foo7'
  },
  {
    id: '15',
    name: 'Foo8'
  },
  {
    id: '16',
    name: 'Foo9'
  },
  {
    id: '17',
    name: 'Foo10'
  },
];
class Content extends React.Component {
  public render() {
    const fileHeader = <
      div className = 'ms-Grid' >
      <
      div className = 'ms-Grid-row' >
      <
      div className = 'ms-Grid-col ms-sm2 ms-md2 ms-lg2' >
      <
      img width = '32'
    height = '32'
    alt = 'logo'
    title = 'logo' / >
      <
      /div> <
      div className = 'ms-Grid-col ms-sm10 ms-md10 ms-lg10' >
      <
      Fabric.Label className = 'ms-font-l ms-fontWeight-bold ms-fontColor-blue' > [TITLE] < /Fabric.Label> <
      /div> <
      /div> <
      div className = 'ms-Grid-row' >
      <
      div className = 'ms-Grid-col ms-sm2 ms-md2 ms-lg2' >
      <
      /div> <
      div className = 'ms-Grid-col ms-sm10 ms-md10 ms-lg10' >
      <
      Fabric.Label
    className = 'ms-font-m ms-fontWeight-bold ms-fontColor-neutralPrimary' > SELECTED PROJECT: < /Fabric.Label> <
      /div> <
      /div> <
      /div>;

    const commandBar = < div >
      <
      Fabric.CommandBar
    isSearchBoxVisible = {
      false
    }
    items = {
      [{
        key: 'openWebApp',
        name: 'Open Web App',
        icon: 'OpenInNewWindow', // Or Link
        ['data-automation-id']: 'openWebAppButton',
        title: 'Open web app',
        href: 'http://www.codepen.io',
        target: '_blank',
      }]
    }
    farItems = {
      [{
        key: 'menuOptions',
        name: 'Options',
        icon: 'Settings',
        iconOnly: 'true',
        ['data-automation-id']: 'settingsButton',
        subMenuProps: {
          items: [{
              key: 'privacyPolicy',
              name: 'Privacy Policy',
              icon: 'PageLock',
              href: 'http://www.codepen.io',
              target: '_blank',

            },
            {
              key: 'termsOfUse',
              name: 'Terms & Conditions',
              icon: 'TextDocument',
              href: 'http://www.codepen.io',
              target: '_blank'
            }
          ]
        }
      }]
    }
    /> <
    /div>;

    const projects = < Fabric.MarqueeSelection selection = {
      null
    }
    data - is - scrollable = {
        false
      } >
      <
      Fabric.DetailsList
    items = {
      items
    }
    columns = {
      columns
    }
    /> <
    /Fabric.MarqueeSelection>;
    const selection = < div > [project name] < /div>;
    const search = < Fabric.TextField label = 'Search projects:' / > ;
    const fileButton = <
      div >
      <
      Fabric.DefaultButton primary = {
        true
      } > File To Project < /Fabric.DefaultButton> <
      /div>;

    return ( <
      Fabric.Fabric >
      <
      div style = {
        {
          height: '500px',
          position: 'relative',
          maxHeight: 'inherit'
        }
      } >
      <
      Fabric.ScrollablePane scrollbarVisibility = {
        Fabric.ScrollbarVisibility.auto
      } >
      <
      Fabric.Sticky stickyPosition = {
        Fabric.StickyPositionType.Header
      } > {
        commandBar
      } {
        fileHeader
      } {
        selection
      } {
        search
      } <
      /Fabric.Sticky>     {
        projects
      } <
      Fabric.Sticky stickyPosition = {
        Fabric.StickyPositionType.Footer
      } > {
        fileButton
      } <
      /Fabric.Sticky>       <
      /Fabric.ScrollablePane> <
      /div> <
      /Fabric.Fabric>
    );
  }
}

ReactDOM.render( <
  Content / > ,
  document.getElementById('content')
);

完整网格:https://codepen.io/elegault/pen/wQxoRR

const columns = [
    {
        key: 'projectNameColumn',
        name: 'Project',
        fieldName: 'name',
        minWidth: 100,
        maxWidth: 200,
        isResizable: true,
        ariaLabel: 'Operations for Project'
    }
];
const items = [
    {id: '0', name: 'ABC Construction'},
    {id: '1', name: 'Air Bee and Bee'},
    {id: '2', name: 'Architectural Salvage'},
    {id: '3', name: 'Arkham Airport'},
    {id: '4', name: 'Arkham Assembly Hall'},
    {id: '5', name: 'Arkham Library'},
    {id: '6', name: 'zArkham Renovation'},
    {id: '7', name: 'Foo'},
    {id: '8', name: 'Foo1'},
    {id: '9', name: 'Foo2'},
    {id: '10', name: 'Foo3'},
    {id: '11', name: 'Foo4'},
    {id: '12', name: 'Foo5'},
    {id: '13', name: 'Foo6'},
    {id: '14', name: 'Foo7'},
    {id: '15', name: 'Foo8'},
    {id: '16', name: 'Foo9'},
    {id: '17', name: 'Foo10'},
];
class Content extends React.Component {
    public render() {        
        
  const commandBar = <div>
            <Fabric.CommandBar
                isSearchBoxVisible={false}
                items={[
                    {
                        key: 'openWebApp',
                        name: 'Open Web App',
                        icon: 'OpenInNewWindow', // Or Link                        
                        title: 'Open web app',
                        href: 'http://www.codepen.io',
                        target: '_blank',
                    }
                ]}
                farItems={[
                    {
                        key: 'menuOptions',
                        name: 'Options',
                        icon: 'Settings',
                        iconOnly: 'true',
                        ['data-automation-id']: 'settingsButton',
                        subMenuProps: {
                            items: [
                                {
                                    key: 'privacyPolicy',
                                    name: 'Privacy Policy',
                                    icon: 'PageLock',
                                    href: 'http://www.codepen.io',
                                    target: '_blank',

                                },
                                {
                                    key: 'termsOfUse',
                                    name: 'Terms & Conditions',
                                    icon: 'TextDocument',
                                    href: 'http://www.codepen.io',
                                    target: '_blank'
                                }
                            ]
                        }
                    }
                ]}
            />
        </div>;
      
        const projects = <Fabric.MarqueeSelection selection={null} data-is-scrollable={false}>
            <Fabric.DetailsList
                items={items}
                columns={columns}              
            />
        </Fabric.MarqueeSelection>;
        const selection = <div>[project name]</div>;
        const search = <Fabric.TextField label='Search projects:'/>;
        const fileButton = 
              <div>
                <Fabric.DefaultButton primary={true}>File To Project</Fabric.DefaultButton>
              </div>;
  
      const fileHeader =             
            <div className='ms-Grid'>
                <div className='ms-Grid-row'>
                    <div className='ms-Grid-col ms-sm2 ms-md2 ms-lg2'>
                        <img width='32' height='32' alt='logo' title='logo'/>
                    </div>
                    <div className='ms-Grid-col ms-sm10 ms-md10 ms-lg10'>
                        <Fabric.Label className='ms-font-l ms-fontWeight-bold ms-fontColor-blue'>[TITLE]</Fabric.Label>
                    </div>
                </div>
                <div className='ms-Grid-row'>
                    <div className='ms-Grid-col ms-sm2 ms-md2 ms-lg2'>
                    </div>
                    <div className='ms-Grid-col ms-sm10 ms-md10 ms-lg10'>
                        <Fabric.Label
                            className='ms-font-m ms-fontWeight-bold ms-fontColor-neutralPrimary'>SELECTED PROJECT:</Fabric.Label>
                    </div>
                </div>
            </div>;
   
        return (
   <Fabric.Fabric>                
    <div style={{height:'500px', position:'relative', maxHeight:'inherit'}}>          
          <Fabric.ScrollablePane scrollbarVisibility={Fabric.ScrollbarVisibility.auto}>
      <div className='ms-Grid'>
       <Fabric.Sticky stickyPosition={Fabric.StickyPositionType.Header}>
        <div className='ms-Grid-row'>
         <div className='ms-Grid-col ms-sm10 ms-md10 ms-lg10'>
          {commandBar}
         </div>
        </div>        
        <div className='ms-Grid-row'>
         <div className='ms-Grid-col ms-sm10 ms-md10 ms-lg10'>
          {fileHeader}
         </div>
        </div>         
        <div className='ms-Grid-row'>
         <div className='ms-Grid-col ms-sm10 ms-md10 ms-lg10'>
          {selection}
         </div>
        </div>       
        <div className='ms-Grid-row'>
         <div className='ms-Grid-col ms-sm10 ms-md10 ms-lg10'>
          {search}            
         </div>
        </div>                  
       </Fabric.Sticky>    
       <div className='ms-Grid-row'>
        <div className='ms-Grid-col ms-sm10 ms-md10 ms-lg10'>
         {projects}            
        </div>
       </div>                              
       <Fabric.Sticky stickyPosition={Fabric.StickyPositionType.Footer}>      
        <div className='ms-Grid-row'>
         <div className='ms-Grid-col ms-sm10 ms-md10 ms-lg10'>
          {fileButton}            
         </div>
        </div>        
       </Fabric.Sticky>      
      </div>
          </Fabric.ScrollablePane>
    </div>
      </Fabric.Fabric>
        );   
  
    }
}

ReactDOM.render( 
  <Content />,
  document.getElementById('content')
);

此外,当尝试在使用嵌套自定义 React 组件的 codepens 之外的项目中使用 Flex 类 或在 parent div 中定义高度(根据 Mohamed Mansour 的建议)时,整个DetailsList 无法呈现。例如,这个 DOM 资源管理器显示了即使设置定义的高度并在多个 'main' div 中使用 flex 仍然无法呈现 DetailsList:

让我们澄清一些术语,这些来自文档:

  • ScrollablePane:使用绝对定位,父级必须有相对和高度(来自 flex 或 static)。所以如果你想让它以你自己的方式伸展,你需要满足这些约束。
  • Sticky:这些是页眉和页脚的理想选择,但仍将位于滚动条区域内。通常用于多节。
  • 重叠内容: 这是完全正常的,你的页眉和页脚没有纯色背景,设置为白色,它会按预期工作。

要实现您想要的效果,让滚动条仅出现在页眉和页脚之间,您可以添加一些正常的 CSS。 运行 看下面的片段。基本上只是一个弹性布局。

我也更新了你的例子:https://codepen.io/mohamedhmansour/pen/rQqqvP

let { DetailsList, Fabric, ScrollablePane, ScrollbarVisibility } = window.Fabric;

function Content() {
  const items = [...Array(500).keys()].map(i => ({key: i, text: `Item ${i}`}));
  
  return (
    <ScrollablePane  scrollbarVisibility={ScrollbarVisibility.auto}> 
      <DetailsList items={items} />
    </ScrollablePane>
  );
}

ReactDOM.render( 
  <Fabric className='wrapper'>
    <div className='header'>Header</div>
    <div className='main'>
      <Content />
    </div>
    <div className='footer'>Footer</div>
  </Fabric>,
  document.getElementById('content')
);
.wrapper {
  display: flex;
  flex-direction: column;
  width: 100%;
  position: absolute;
  top: 0;
  bottom: 0;
}

.main {
  flex: 1;
  position: relative;
}


.header, .footer {
  background: black;
  color: white;
  padding: 10px;
}
<script src="//unpkg.com/react@16/umd/react.development.js"></script>
<script src="//unpkg.com/react-dom@16/umd/react-dom.development.js"></script>

<!-- could point to local version -->
<script src="//unpkg.com/office-ui-fabric-react/dist/office-ui-fabric-react.min.js"></script>

<div id='content'></div>