diff --git a/packages/office-ui-fabric-react/src/OverflowSet.ts b/packages/office-ui-fabric-react/src/OverflowSet.ts new file mode 100644 index 00000000000000..ec6e7f73fadbcf --- /dev/null +++ b/packages/office-ui-fabric-react/src/OverflowSet.ts @@ -0,0 +1 @@ +export * from './components/OverflowSet/index'; diff --git a/packages/office-ui-fabric-react/src/ResizeGroup.ts b/packages/office-ui-fabric-react/src/ResizeGroup.ts new file mode 100644 index 00000000000000..1943569105e4de --- /dev/null +++ b/packages/office-ui-fabric-react/src/ResizeGroup.ts @@ -0,0 +1 @@ +export * from './components/ResizeGroup/index'; diff --git a/packages/office-ui-fabric-react/src/components/OverflowSet/OverflowSet.Props.ts b/packages/office-ui-fabric-react/src/components/OverflowSet/OverflowSet.Props.ts new file mode 100644 index 00000000000000..fb980865ef2bf9 --- /dev/null +++ b/packages/office-ui-fabric-react/src/components/OverflowSet/OverflowSet.Props.ts @@ -0,0 +1,12 @@ +import * as React from 'react'; +import { OverflowSet } from './OverflowSet'; +import { IContextualMenuItem } from '../../ContextualMenu'; + +export interface IOverflowSetProps extends React.Props { + items?: any[]; + overflowItems?: IContextualMenuItem[]; + overflowIcon?: string; + + /** Method to call when trying to render an item. */ + onRenderItem?: (item?: any, index?: number) => React.ReactNode; +} diff --git a/packages/office-ui-fabric-react/src/components/OverflowSet/OverflowSet.scss b/packages/office-ui-fabric-react/src/components/OverflowSet/OverflowSet.scss new file mode 100644 index 00000000000000..f87f28f2290dab --- /dev/null +++ b/packages/office-ui-fabric-react/src/components/OverflowSet/OverflowSet.scss @@ -0,0 +1,12 @@ +@import '../../common/common'; + +.root { + position: relative; + display: flex; + flex-wrap: nowrap; +} + + +.item { + flex-shrink: 0; +} diff --git a/packages/office-ui-fabric-react/src/components/OverflowSet/OverflowSet.tsx b/packages/office-ui-fabric-react/src/components/OverflowSet/OverflowSet.tsx new file mode 100644 index 00000000000000..3dd157965cabc4 --- /dev/null +++ b/packages/office-ui-fabric-react/src/components/OverflowSet/OverflowSet.tsx @@ -0,0 +1,57 @@ +import * as React from 'react'; +import { + css, + autobind, + BaseComponent +} from '../../Utilities'; +import { IOverflowSetProps } from './OverflowSet.Props'; +import { IconButton } from '../../Button'; + +const styles: any = require('./OverflowSet.scss'); + +export class OverflowSet extends BaseComponent { + + constructor(props: IOverflowSetProps) { + super(props); + } + + public render() { + let { items, overflowItems } = this.props; + return ( +
+ { items && this._onRenderItems(items) } + { overflowItems && this._onRenderOverflowButton(overflowItems) } +
+ ); + } + + @autobind + private _onRenderOverflowButton(items) { + let { overflowIcon = 'More' } = this.props; + return ( + + ); + } + + @autobind + private _onRenderItems(items) { + return items.map((item, i) => { + let key = item.key ? item.key : i; + let onRender = item.onRender ? item.onRender : this.props.onRenderItem; + return ( +
+ { onRender ? onRender(item, i) : item } +
+ ); + }); + } +} diff --git a/packages/office-ui-fabric-react/src/components/OverflowSet/OverflowSetPage.tsx b/packages/office-ui-fabric-react/src/components/OverflowSet/OverflowSetPage.tsx new file mode 100644 index 00000000000000..2e1cd37b3896a6 --- /dev/null +++ b/packages/office-ui-fabric-react/src/components/OverflowSet/OverflowSetPage.tsx @@ -0,0 +1,42 @@ +import * as React from 'react'; +import { Link } from 'office-ui-fabric-react/lib/Link'; +import { LayerHost } from 'office-ui-fabric-react/lib/Layer'; +import { + ExampleCard, + ComponentPage, + PropertiesTableSet +} from '@uifabric/example-app-base'; +import { OverflowSetBasicExample } from './examples/OverflowSet.Basic.Example'; + +const OverflowSetBasicExampleCode = require('!raw-loader!office-ui-fabric-react/src/components/OverflowSet/examples/OverflowSet.Basic.Example.tsx') as string; + +export class OverflowSetPage extends React.Component { + public render() { + return ( + + + + + + } + propertiesTables={ + ('!raw-loader!office-ui-fabric-react/src/components/OverflowSet/OverflowSet.Props.ts') + ] } + /> + } + overview={ +
+ OverflowSets + supplement content associated with a specific UI component. +
+ } + /> + ); + } +} diff --git a/packages/office-ui-fabric-react/src/components/OverflowSet/examples/OverflowSet.Basic.Example.tsx b/packages/office-ui-fabric-react/src/components/OverflowSet/examples/OverflowSet.Basic.Example.tsx new file mode 100644 index 00000000000000..d45f941031350f --- /dev/null +++ b/packages/office-ui-fabric-react/src/components/OverflowSet/examples/OverflowSet.Basic.Example.tsx @@ -0,0 +1,110 @@ +/* tslint:disable:no-unused-variable */ +import * as React from 'react'; +/* tslint:enable:no-unused-variable */ +import { BaseComponent } from 'office-ui-fabric-react/lib/Utilities'; +import { SearchBox } from 'office-ui-fabric-react/lib/SearchBox'; +import { DefaultButton } from 'office-ui-fabric-react/lib/Button'; +import { + OverflowSet +} from '../index'; + +export class OverflowSetBasicExample extends BaseComponent { + + public render() { + return ( + { + return ( + console.log('SearchBox onChange fired: ' + newValue) } + onSearch={ (newValue) => console.log('SearchBox onSearch fired: ' + newValue) } + /> + ); + } + }, + { + key: 'newItem', + name: 'New', + icon: 'Add', + ariaLabel: 'New. Use left and right arrow keys to navigate', + onClick: () => { return; }, + subMenuProps: { + items: [ + { + key: 'emailMessage', + name: 'Email message', + icon: 'Mail', + }, + { + key: 'calendarEvent', + name: 'Calendar event', + icon: 'Calendar' + } + ], + }, + }, + { + key: 'upload', + name: 'Upload', + icon: 'Upload', + onClick: () => { return; }, + }, + { + key: 'share', + name: 'Share', + icon: 'Share', + onClick: () => { return; } + } + ] } + overflowItems={ [ + { + key: 'newItem', + name: 'New', + icon: 'Add', + ariaLabel: 'New. Use left and right arrow keys to navigate', + onClick: () => { return; }, + subMenuProps: { + items: [ + { + key: 'emailMessage', + name: 'Email message', + icon: 'Mail', + }, + { + key: 'calendarEvent', + name: 'Calendar event', + icon: 'Calendar' + } + ], + }, + }, + { + key: 'upload', + name: 'Upload', + icon: 'Upload', + onClick: () => { return; }, + }, + { + key: 'share', + name: 'Share', + icon: 'Share', + onClick: () => { return; } + } + ] + } + onRenderItem={ (item, i) => { + return ( + + ); + } } + /> + ); + } +} \ No newline at end of file diff --git a/packages/office-ui-fabric-react/src/components/OverflowSet/index.ts b/packages/office-ui-fabric-react/src/components/OverflowSet/index.ts new file mode 100644 index 00000000000000..24d67e9dadc931 --- /dev/null +++ b/packages/office-ui-fabric-react/src/components/OverflowSet/index.ts @@ -0,0 +1,2 @@ +export * from './OverflowSet'; +export * from './OverflowSet.Props'; diff --git a/packages/office-ui-fabric-react/src/components/ResizeGroup/ResizeGroup.Props.ts b/packages/office-ui-fabric-react/src/components/ResizeGroup/ResizeGroup.Props.ts new file mode 100644 index 00000000000000..d17f0087e65a0e --- /dev/null +++ b/packages/office-ui-fabric-react/src/components/ResizeGroup/ResizeGroup.Props.ts @@ -0,0 +1,8 @@ +import * as React from 'react'; +import { IOverFlowItemState } from './ResizeGroup'; + +export interface IResizeGroupProps extends React.HTMLProps { + items?: any[]; + onRenderItems?: (items: any[]) => JSX.Element; + onReduceItems?: (prevItems: IOverFlowItemState, props: IResizeGroupProps) => IOverFlowItemState; +} diff --git a/packages/office-ui-fabric-react/src/components/ResizeGroup/ResizeGroup.scss b/packages/office-ui-fabric-react/src/components/ResizeGroup/ResizeGroup.scss new file mode 100644 index 00000000000000..c4036825c6c3ba --- /dev/null +++ b/packages/office-ui-fabric-react/src/components/ResizeGroup/ResizeGroup.scss @@ -0,0 +1,13 @@ +@import '../../common/common'; + +.root { + display: block; +} + +.measured { + flex-direction: horizontal; + flex-wrap: nowrap; + display: flex; + position: absolute; + visibility: hidden; +} \ No newline at end of file diff --git a/packages/office-ui-fabric-react/src/components/ResizeGroup/ResizeGroup.tsx b/packages/office-ui-fabric-react/src/components/ResizeGroup/ResizeGroup.tsx new file mode 100644 index 00000000000000..e345c794d57f23 --- /dev/null +++ b/packages/office-ui-fabric-react/src/components/ResizeGroup/ResizeGroup.tsx @@ -0,0 +1,91 @@ +import * as React from 'react'; +import { + css, + BaseComponent +} from '../../Utilities'; +import { IResizeGroupProps } from './ResizeGroup.Props'; +import styles = require('./ResizeGroup.scss'); + +export interface IResizeGroupState { + renderedItems?: any[]; + measuredItems: any[]; + shouldMeasure?: boolean; +} + +export interface IOverFlowItemState { + [index: number]: any; +} + +export class ResizeGroup extends BaseComponent { + + private _root: HTMLElement; + private _measured: HTMLElement; + + constructor(props: IResizeGroupProps) { + super(props); + this.state = { + shouldMeasure: true, + renderedItems: [], + measuredItems: this.props.items.concat([]), + }; + } + + public componentDidMount() { + this._measureItems(); + this._events.on(window, 'resize', this._onResize); + } + + public render() { + let { onRenderItems, onReduceItems, items } = this.props; + let { shouldMeasure, renderedItems, measuredItems } = this.state; + return ( +
+ { shouldMeasure && ( +
+ { onRenderItems(measuredItems) } +
+ ) } + + { renderedItems.length > 0 && onRenderItems(renderedItems) } +
+ + ); + } + + public componentDidUpdate(prevProps: IResizeGroupProps) { + this._measureItems(); + } + + protected _onResize() { + this.setState({ shouldMeasure: true }); + } + + private _measureItems() { + let { items, onReduceItems } = this.props; + let { + shouldMeasure, + renderedItems, + measuredItems, + } = this.state; + + if (shouldMeasure && items && items.length > 0) { + let container = this._root.getBoundingClientRect(); + let measured = this._measured.getBoundingClientRect(); + if ((measured.width > container.width)) { + this.setState((prevState, props) => { + return { + measuredItems: onReduceItems(prevState.measuredItems, props), + }; + }); + } else { + this.setState((prevState, props) => { + return { + renderedItems: measuredItems, + measuredItems: this.props.items.concat([]), + shouldMeasure: false + }; + }); + } + } + } +} diff --git a/packages/office-ui-fabric-react/src/components/ResizeGroup/ResizeGroupPage.tsx b/packages/office-ui-fabric-react/src/components/ResizeGroup/ResizeGroupPage.tsx new file mode 100644 index 00000000000000..60e0baeef4619a --- /dev/null +++ b/packages/office-ui-fabric-react/src/components/ResizeGroup/ResizeGroupPage.tsx @@ -0,0 +1,42 @@ +import * as React from 'react'; +import { Link } from 'office-ui-fabric-react/lib/Link'; +import { LayerHost } from 'office-ui-fabric-react/lib/Layer'; +import { + ExampleCard, + ComponentPage, + PropertiesTableSet +} from '@uifabric/example-app-base'; +import { ResizeGroupOverflowSetExample } from './examples/ResizeGroup.OverflowSet.Example'; + +const ResizeGroupBasicExampleCode = require('!raw-loader!office-ui-fabric-react/src/components/ResizeGroup/examples/ResizeGroup.OverflowSet.Example.tsx') as string; + +export class ResizeGroupPage extends React.Component { + public render() { + return ( + + + + + + } + propertiesTables={ + ('!raw-loader!office-ui-fabric-react/src/components/ResizeGroup/ResizeGroup.Props.ts') + ] } + /> + } + overview={ +
+ ResizeGroup + supplement content associated with a specific UI component. +
+ } + /> + ); + } +} diff --git a/packages/office-ui-fabric-react/src/components/ResizeGroup/examples/ResizeGroup.OverflowSet.Example.tsx b/packages/office-ui-fabric-react/src/components/ResizeGroup/examples/ResizeGroup.OverflowSet.Example.tsx new file mode 100644 index 00000000000000..89626f11e1dff1 --- /dev/null +++ b/packages/office-ui-fabric-react/src/components/ResizeGroup/examples/ResizeGroup.OverflowSet.Example.tsx @@ -0,0 +1,46 @@ +/* tslint:disable:no-unused-variable */ +import * as React from 'react'; +/* tslint:enable:no-unused-variable */ +import { BaseComponent } from 'office-ui-fabric-react/lib/Utilities'; +import { SearchBox } from 'office-ui-fabric-react/lib/SearchBox'; +import { DefaultButton } from 'office-ui-fabric-react/lib/Button'; +import { + OverflowSet +} from '../../OverflowSet'; +import { ResizeGroup } from '../../ResizeGroup'; + +import { items } from './items'; + +export class ResizeGroupOverflowSetExample extends BaseComponent { + + public render() { + return ( + { + let overflow = currentItems[0].overflow.concat(currentItems[0].primary.slice(-1)); + let primary = currentItems[0].primary.slice(0, -1); + + return [{ primary, overflow }]; + } } + onRenderItems={ (items) => { + return ( + { + return ( + + ); + } } + /> + ); + } } + /> + ); + }; +} \ No newline at end of file diff --git a/packages/office-ui-fabric-react/src/components/ResizeGroup/examples/items.ts b/packages/office-ui-fabric-react/src/components/ResizeGroup/examples/items.ts new file mode 100644 index 00000000000000..4bc9eb325205f9 --- /dev/null +++ b/packages/office-ui-fabric-react/src/components/ResizeGroup/examples/items.ts @@ -0,0 +1,83 @@ +export const items = [ + { + primary: [ + { + key: 'newItem', + name: 'New', + icon: 'Add', + ariaLabel: 'New. Use left and right arrow keys to navigate', + onClick: () => { return; }, + }, + { + key: 'upload', + name: 'Upload', + icon: 'Upload', + onClick: () => { return; }, + }, + { + key: 'share', + name: 'Share', + icon: 'Share', + onClick: () => { return; } + }, + { + key: 'newItem2', + name: 'New', + icon: 'Add', + ariaLabel: 'New. Use left and right arrow keys to navigate', + onClick: () => { return; }, + }, + { + key: 'upload2', + name: 'Upload', + icon: 'Upload', + onClick: () => { return; }, + }, + { + key: 'share2', + name: 'Share', + icon: 'Share', + onClick: () => { return; } + }, + { + key: 'newItem3', + name: 'New', + icon: 'Add', + ariaLabel: 'New. Use left and right arrow keys to navigate', + onClick: () => { return; }, + }, + { + key: 'upload3', + name: 'Upload', + icon: 'Upload', + onClick: () => { return; }, + }, + { + key: 'share3', + name: 'Share', + icon: 'Share', + onClick: () => { return; } + }, + { + key: 'newItem4', + name: 'New', + icon: 'Add', + ariaLabel: 'New. Use left and right arrow keys to navigate', + onClick: () => { return; }, + }, + { + key: 'upload4', + name: 'Upload', + icon: 'Upload', + onClick: () => { return; }, + }, + { + key: 'share4', + name: 'Share', + icon: 'Share', + onClick: () => { return; } + } + ], + overflow: [] + } +]; diff --git a/packages/office-ui-fabric-react/src/components/ResizeGroup/index.ts b/packages/office-ui-fabric-react/src/components/ResizeGroup/index.ts new file mode 100644 index 00000000000000..16254cca3279d9 --- /dev/null +++ b/packages/office-ui-fabric-react/src/components/ResizeGroup/index.ts @@ -0,0 +1,2 @@ +export * from './ResizeGroup'; +export * from './ResizeGroup.Props'; \ No newline at end of file diff --git a/packages/office-ui-fabric-react/src/demo/AppDefinition.tsx b/packages/office-ui-fabric-react/src/demo/AppDefinition.tsx index 9313881244f2fd..77f59d9bdceedc 100644 --- a/packages/office-ui-fabric-react/src/demo/AppDefinition.tsx +++ b/packages/office-ui-fabric-react/src/demo/AppDefinition.tsx @@ -130,6 +130,12 @@ export const AppDefinition: IAppDefinition = { name: 'MessageBar', url: '#/examples/messagebar' }, + { + getComponent: cb => cb(require('../components/OverflowSet/OverflowSetPage').OverflowSetPage), + key: 'OverflowSet', + name: 'OverflowSet', + url: '#/examples/overflowset' + }, { getComponent: cb => cb(require('../components/Overlay/OverlayPage').OverlayPage), key: 'Overlay', @@ -178,6 +184,12 @@ export const AppDefinition: IAppDefinition = { name: 'Rating', url: '#/examples/rating' }, + { + getComponent: cb => cb(require('../components/ResizeGroup/ResizeGroupPage').ResizeGroupPage), + key: 'ResizeGroup', + name: 'ResizeGroup', + url: '#/examples/resizegroup' + }, { getComponent: cb => cb(require('../components/SearchBox/SearchBoxPage').SearchBoxPage), key: 'SearchBox', @@ -212,7 +224,7 @@ export const AppDefinition: IAppDefinition = { getComponent: cb => cb(require('../components/Tooltip/TooltipPage').TooltipPage), key: 'Tooltip', name: 'Tooltip', - url: '#/examples/Tooltip' + url: '#/examples/tooltip' } ], name: 'Basic components'