/packages/react-examples/src/office-ui-fabric-react/DetailsList/DetailsList.Advanced.Example.tsx

https://github.com/OfficeDev/office-ui-fabric-react · TypeScript · 745 lines · 683 code · 61 blank · 1 comment · 78 complexity · e3beb5c15059a062bcce4d31ae71da35 MD5 · raw file

  1. import * as React from 'react';
  2. import { Link } from 'office-ui-fabric-react/lib/Link';
  3. import { TextField } from 'office-ui-fabric-react/lib/TextField';
  4. import { CommandBar, ICommandBarStyles } from 'office-ui-fabric-react/lib/CommandBar';
  5. import { Announced } from 'office-ui-fabric-react/lib/Announced';
  6. import {
  7. IContextualMenuProps,
  8. IContextualMenuItem,
  9. DirectionalHint,
  10. ContextualMenu,
  11. } from 'office-ui-fabric-react/lib/ContextualMenu';
  12. import {
  13. CheckboxVisibility,
  14. ColumnActionsMode,
  15. ConstrainMode,
  16. DetailsList,
  17. DetailsListLayoutMode,
  18. IColumn,
  19. IGroup,
  20. Selection,
  21. SelectionMode,
  22. buildColumns,
  23. IDetailsColumnProps,
  24. } from 'office-ui-fabric-react/lib/DetailsList';
  25. import { createListItems, isGroupable, IExampleItem } from '@uifabric/example-data';
  26. import { memoizeFunction } from 'office-ui-fabric-react/lib/Utilities';
  27. import { getTheme, mergeStyleSets } from 'office-ui-fabric-react/lib/Styling';
  28. const theme = getTheme();
  29. const headerDividerClass = 'DetailsListAdvancedExample-divider';
  30. const classNames = mergeStyleSets({
  31. headerDivider: {
  32. display: 'inline-block',
  33. height: '100%',
  34. },
  35. headerDividerBar: [
  36. {
  37. display: 'none',
  38. background: theme.palette.themePrimary,
  39. position: 'absolute',
  40. top: 16,
  41. bottom: 0,
  42. width: '1px',
  43. zIndex: 5,
  44. },
  45. headerDividerClass,
  46. ],
  47. linkField: {
  48. display: 'block',
  49. overflow: 'hidden',
  50. textOverflow: 'ellipsis',
  51. maxWidth: '100%',
  52. },
  53. root: {
  54. selectors: {
  55. [`.${headerDividerClass}:hover + .${headerDividerClass}`]: {
  56. display: 'inline',
  57. },
  58. },
  59. },
  60. });
  61. const commandBarStyles: Partial<ICommandBarStyles> = { root: { marginBottom: '40px' } };
  62. const DEFAULT_ITEM_LIMIT = 5;
  63. const PAGING_SIZE = 10;
  64. const PAGING_DELAY = 2000;
  65. const ITEMS_COUNT = 5000;
  66. export interface IDetailsListAdvancedExampleState {
  67. canResizeColumns?: boolean;
  68. checkboxVisibility?: CheckboxVisibility;
  69. columns: IColumn[];
  70. constrainMode?: ConstrainMode;
  71. contextualMenuProps?: IContextualMenuProps;
  72. groupItemLimit?: number;
  73. groups?: IGroup[];
  74. isHeaderVisible?: boolean;
  75. isLazyLoaded?: boolean;
  76. isSortedDescending?: boolean;
  77. items: IExampleItem[];
  78. layoutMode?: DetailsListLayoutMode;
  79. selectionMode?: SelectionMode;
  80. sortedColumnKey?: string;
  81. selectionCount: number;
  82. announcedMessage?: string;
  83. }
  84. export class DetailsListAdvancedExample extends React.Component<{}, IDetailsListAdvancedExampleState> {
  85. private _isFetchingItems: boolean;
  86. private _selection: Selection;
  87. private _allItems: IExampleItem[];
  88. constructor(props: {}) {
  89. super(props);
  90. this._getCommandItems = memoizeFunction(this._getCommandItems);
  91. this._allItems = createListItems(ITEMS_COUNT);
  92. this._selection = new Selection({
  93. onSelectionChanged: this._onItemsSelectionChanged,
  94. });
  95. this._selection.setItems(this._allItems, false);
  96. this.state = {
  97. items: this._allItems,
  98. selectionCount: 0,
  99. groups: undefined,
  100. groupItemLimit: DEFAULT_ITEM_LIMIT,
  101. layoutMode: DetailsListLayoutMode.justified,
  102. constrainMode: ConstrainMode.horizontalConstrained,
  103. selectionMode: SelectionMode.multiple,
  104. canResizeColumns: true,
  105. checkboxVisibility: CheckboxVisibility.onHover,
  106. columns: this._buildColumns(
  107. this._allItems,
  108. true,
  109. this._onColumnClick,
  110. '',
  111. undefined,
  112. undefined,
  113. this._onColumnContextMenu,
  114. ),
  115. contextualMenuProps: undefined,
  116. sortedColumnKey: 'name',
  117. isSortedDescending: false,
  118. isLazyLoaded: false,
  119. isHeaderVisible: true,
  120. };
  121. }
  122. public render(): JSX.Element {
  123. const {
  124. canResizeColumns,
  125. checkboxVisibility,
  126. columns,
  127. constrainMode,
  128. contextualMenuProps,
  129. groupItemLimit,
  130. groups,
  131. isHeaderVisible,
  132. isLazyLoaded,
  133. items,
  134. layoutMode,
  135. selectionMode,
  136. announcedMessage,
  137. } = this.state;
  138. const isGrouped = groups && groups.length > 0;
  139. const groupProps = {
  140. getGroupItemLimit: (group: IGroup) => {
  141. if (group) {
  142. return group.isShowingAll ? group.count : Math.min(group.count, groupItemLimit as number);
  143. } else {
  144. return items.length;
  145. }
  146. },
  147. footerProps: {
  148. showAllLinkText: 'Show all',
  149. },
  150. };
  151. return (
  152. <div className={classNames.root}>
  153. <CommandBar
  154. styles={commandBarStyles}
  155. items={this._getCommandItems(
  156. canResizeColumns,
  157. checkboxVisibility,
  158. constrainMode,
  159. isHeaderVisible,
  160. isLazyLoaded,
  161. layoutMode,
  162. selectionMode,
  163. )}
  164. farItems={[{ key: 'count', text: `${this.state.selectionCount} selected` }]}
  165. />
  166. <Announced message={`${this.state.selectionCount} selected`} />
  167. {isGrouped ? <TextField label="Group item limit" onChange={this._onItemLimitChanged} /> : null}
  168. {announcedMessage ? <Announced message={announcedMessage} /> : undefined}
  169. <DetailsList
  170. setKey="items"
  171. items={items}
  172. selection={this._selection}
  173. groups={groups}
  174. columns={columns}
  175. checkboxVisibility={checkboxVisibility}
  176. layoutMode={layoutMode}
  177. isHeaderVisible={isHeaderVisible}
  178. selectionMode={selectionMode}
  179. constrainMode={constrainMode}
  180. groupProps={groupProps}
  181. enterModalSelectionOnTouch={true}
  182. onItemInvoked={this._onItemInvoked}
  183. onItemContextMenu={this._onItemContextMenu}
  184. selectionZoneProps={{
  185. selection: this._selection,
  186. disableAutoSelectOnInputElements: true,
  187. selectionMode: selectionMode,
  188. }}
  189. ariaLabelForListHeader="Column headers. Click to sort."
  190. ariaLabelForSelectAllCheckbox="Toggle selection for all items"
  191. ariaLabelForSelectionColumn="Toggle selection"
  192. checkButtonAriaLabel="Row checkbox"
  193. onRenderMissingItem={this._onRenderMissingItem}
  194. />
  195. {contextualMenuProps && <ContextualMenu {...contextualMenuProps} />}
  196. </div>
  197. );
  198. }
  199. private _onRenderDivider = (
  200. columnProps: IDetailsColumnProps,
  201. defaultRenderer: (props?: IDetailsColumnProps) => JSX.Element | null,
  202. ): JSX.Element => {
  203. const { columnIndex } = columnProps;
  204. return (
  205. <React.Fragment key={`divider-wrapper-${columnIndex}`}>
  206. <span className={classNames.headerDivider}>{defaultRenderer(columnProps)}</span>
  207. <span className={classNames.headerDividerBar} />
  208. </React.Fragment>
  209. );
  210. };
  211. private _onDataMiss(index: number): void {
  212. index = Math.floor(index / PAGING_SIZE) * PAGING_SIZE;
  213. if (!this._isFetchingItems) {
  214. this._isFetchingItems = true;
  215. setTimeout(() => {
  216. this._isFetchingItems = false;
  217. const itemsCopy = [...this.state.items];
  218. itemsCopy.splice(index, PAGING_SIZE).concat(this._allItems.slice(index, index + PAGING_SIZE));
  219. this.setState({
  220. items: itemsCopy,
  221. });
  222. }, PAGING_DELAY);
  223. }
  224. }
  225. private _onRenderMissingItem = (index: number): null => {
  226. this._onDataMiss(index);
  227. return null;
  228. };
  229. private _onToggleLazyLoad = (): void => {
  230. let { isLazyLoaded } = this.state;
  231. isLazyLoaded = !isLazyLoaded;
  232. this.setState({
  233. isLazyLoaded: isLazyLoaded,
  234. items: isLazyLoaded
  235. ? this._allItems.slice(0, PAGING_SIZE).concat(new Array(ITEMS_COUNT - PAGING_SIZE))
  236. : this._allItems,
  237. });
  238. };
  239. private _onToggleHeaderVisible = (): void => {
  240. this.setState({ isHeaderVisible: !this.state.isHeaderVisible });
  241. };
  242. private _onToggleResizing = (): void => {
  243. const { items, sortedColumnKey, isSortedDescending } = this.state;
  244. let { canResizeColumns } = this.state;
  245. canResizeColumns = !canResizeColumns;
  246. this.setState({
  247. canResizeColumns: canResizeColumns,
  248. columns: this._buildColumns(items, canResizeColumns, this._onColumnClick, sortedColumnKey, isSortedDescending),
  249. });
  250. };
  251. private _onCheckboxVisibilityChanged = (ev: React.MouseEvent<HTMLElement>, menuItem: IContextualMenuItem): void => {
  252. this.setState({ checkboxVisibility: menuItem.data });
  253. };
  254. private _onLayoutChanged = (ev: React.MouseEvent<HTMLElement>, menuItem: IContextualMenuItem): void => {
  255. this.setState({ layoutMode: menuItem.data });
  256. };
  257. private _onConstrainModeChanged = (ev: React.MouseEvent<HTMLElement>, menuItem: IContextualMenuItem): void => {
  258. this.setState({ constrainMode: menuItem.data });
  259. };
  260. private _onSelectionChanged = (ev: React.MouseEvent<HTMLElement>, menuItem: IContextualMenuItem): void => {
  261. this.setState({ selectionMode: menuItem.data });
  262. };
  263. private _onItemLimitChanged = (ev: React.FormEvent<HTMLInputElement | HTMLTextAreaElement>, value: string): void => {
  264. let newValue = parseInt(value, 10);
  265. if (isNaN(newValue)) {
  266. newValue = DEFAULT_ITEM_LIMIT;
  267. }
  268. this.setState({ groupItemLimit: newValue });
  269. };
  270. private _getCommandItems = (
  271. canResizeColumns?: boolean,
  272. checkboxVisibility?: CheckboxVisibility,
  273. constrainMode?: ConstrainMode,
  274. isHeaderVisible?: boolean,
  275. isLazyLoaded?: boolean,
  276. layoutMode?: DetailsListLayoutMode,
  277. selectionMode?: SelectionMode,
  278. ): IContextualMenuItem[] => {
  279. return [
  280. {
  281. key: 'addRow',
  282. text: 'Insert row',
  283. iconProps: { iconName: 'Add' },
  284. onClick: this._onAddRow,
  285. },
  286. {
  287. key: 'deleteRow',
  288. text: 'Delete row',
  289. iconProps: { iconName: 'Delete' },
  290. onClick: this._onDeleteRow,
  291. },
  292. {
  293. key: 'configure',
  294. text: 'Configure',
  295. iconProps: { iconName: 'Settings' },
  296. subMenuProps: {
  297. items: [
  298. {
  299. key: 'resizing',
  300. text: 'Allow column resizing',
  301. canCheck: true,
  302. checked: canResizeColumns,
  303. onClick: this._onToggleResizing,
  304. },
  305. {
  306. key: 'headerVisible',
  307. text: 'Is header visible',
  308. canCheck: true,
  309. checked: isHeaderVisible,
  310. onClick: this._onToggleHeaderVisible,
  311. },
  312. {
  313. key: 'lazyload',
  314. text: 'Simulate async loading',
  315. canCheck: true,
  316. checked: isLazyLoaded,
  317. onClick: this._onToggleLazyLoad,
  318. },
  319. {
  320. key: 'dash',
  321. text: '-',
  322. },
  323. {
  324. key: 'checkboxVisibility',
  325. text: 'Checkbox visibility',
  326. subMenuProps: {
  327. items: [
  328. {
  329. key: 'checkboxVisibility.always',
  330. text: 'Always',
  331. canCheck: true,
  332. isChecked: checkboxVisibility === CheckboxVisibility.always,
  333. onClick: this._onCheckboxVisibilityChanged,
  334. data: CheckboxVisibility.always,
  335. },
  336. {
  337. key: 'checkboxVisibility.onHover',
  338. text: 'On hover',
  339. canCheck: true,
  340. isChecked: checkboxVisibility === CheckboxVisibility.onHover,
  341. onClick: this._onCheckboxVisibilityChanged,
  342. data: CheckboxVisibility.onHover,
  343. },
  344. {
  345. key: 'checkboxVisibility.hidden',
  346. text: 'Hidden',
  347. canCheck: true,
  348. isChecked: checkboxVisibility === CheckboxVisibility.hidden,
  349. onClick: this._onCheckboxVisibilityChanged,
  350. data: CheckboxVisibility.hidden,
  351. },
  352. ],
  353. },
  354. },
  355. {
  356. key: 'layoutMode',
  357. text: 'Layout mode',
  358. subMenuProps: {
  359. items: [
  360. {
  361. key: DetailsListLayoutMode[DetailsListLayoutMode.fixedColumns],
  362. text: 'Fixed columns',
  363. canCheck: true,
  364. checked: layoutMode === DetailsListLayoutMode.fixedColumns,
  365. onClick: this._onLayoutChanged,
  366. data: DetailsListLayoutMode.fixedColumns,
  367. },
  368. {
  369. key: DetailsListLayoutMode[DetailsListLayoutMode.justified],
  370. text: 'Justified columns',
  371. canCheck: true,
  372. checked: layoutMode === DetailsListLayoutMode.justified,
  373. onClick: this._onLayoutChanged,
  374. data: DetailsListLayoutMode.justified,
  375. },
  376. ],
  377. },
  378. },
  379. {
  380. key: 'selectionMode',
  381. text: 'Selection mode',
  382. subMenuProps: {
  383. items: [
  384. {
  385. key: SelectionMode[SelectionMode.none],
  386. text: 'None',
  387. canCheck: true,
  388. checked: selectionMode === SelectionMode.none,
  389. onClick: this._onSelectionChanged,
  390. data: SelectionMode.none,
  391. },
  392. {
  393. key: SelectionMode[SelectionMode.single],
  394. text: 'Single select',
  395. canCheck: true,
  396. checked: selectionMode === SelectionMode.single,
  397. onClick: this._onSelectionChanged,
  398. data: SelectionMode.single,
  399. },
  400. {
  401. key: SelectionMode[SelectionMode.multiple],
  402. text: 'Multi select',
  403. canCheck: true,
  404. checked: selectionMode === SelectionMode.multiple,
  405. onClick: this._onSelectionChanged,
  406. data: SelectionMode.multiple,
  407. },
  408. ],
  409. },
  410. },
  411. {
  412. key: 'constrainMode',
  413. text: 'Constrain mode',
  414. subMenuProps: {
  415. items: [
  416. {
  417. key: ConstrainMode[ConstrainMode.unconstrained],
  418. text: 'Unconstrained',
  419. canCheck: true,
  420. checked: constrainMode === ConstrainMode.unconstrained,
  421. onClick: this._onConstrainModeChanged,
  422. data: ConstrainMode.unconstrained,
  423. },
  424. {
  425. key: ConstrainMode[ConstrainMode.horizontalConstrained],
  426. text: 'Horizontal constrained',
  427. canCheck: true,
  428. checked: constrainMode === ConstrainMode.horizontalConstrained,
  429. onClick: this._onConstrainModeChanged,
  430. data: ConstrainMode.horizontalConstrained,
  431. },
  432. ],
  433. },
  434. },
  435. ],
  436. },
  437. },
  438. ];
  439. };
  440. private _getContextualMenuProps(ev: React.MouseEvent<HTMLElement>, column: IColumn): IContextualMenuProps {
  441. const items = [
  442. {
  443. key: 'aToZ',
  444. name: 'A to Z',
  445. iconProps: { iconName: 'SortUp' },
  446. canCheck: true,
  447. checked: column.isSorted && !column.isSortedDescending,
  448. onClick: () => this._onSortColumn(column.key, false),
  449. },
  450. {
  451. key: 'zToA',
  452. name: 'Z to A',
  453. iconProps: { iconName: 'SortDown' },
  454. canCheck: true,
  455. checked: column.isSorted && column.isSortedDescending,
  456. onClick: () => this._onSortColumn(column.key, true),
  457. },
  458. ];
  459. if (isGroupable(column.key)) {
  460. items.push({
  461. key: 'groupBy',
  462. name: 'Group by ' + column.name,
  463. iconProps: { iconName: 'GroupedDescending' },
  464. canCheck: true,
  465. checked: column.isGrouped,
  466. onClick: () => this._onGroupByColumn(column),
  467. });
  468. }
  469. return {
  470. items: items,
  471. target: ev.currentTarget as HTMLElement,
  472. directionalHint: DirectionalHint.bottomLeftEdge,
  473. gapSpace: 10,
  474. isBeakVisible: true,
  475. onDismiss: this._onContextualMenuDismissed,
  476. };
  477. }
  478. private _onItemInvoked = (item: IExampleItem, index: number): void => {
  479. console.log('Item invoked', item, index);
  480. };
  481. private _onItemContextMenu = (item: IExampleItem, index: number, ev: MouseEvent): boolean => {
  482. const contextualMenuProps: IContextualMenuProps = {
  483. target: ev.target as HTMLElement,
  484. items: [
  485. {
  486. key: 'text',
  487. name: `${this._selection.getSelectedCount()} selected`,
  488. },
  489. ],
  490. onDismiss: () => {
  491. this.setState({
  492. contextualMenuProps: undefined,
  493. });
  494. },
  495. };
  496. if (index > -1) {
  497. this.setState({
  498. contextualMenuProps: contextualMenuProps,
  499. });
  500. }
  501. return false;
  502. };
  503. private _onColumnClick = (ev: React.MouseEvent<HTMLElement>, column: IColumn): void => {
  504. if (column.columnActionsMode !== ColumnActionsMode.disabled) {
  505. this.setState({
  506. contextualMenuProps: this._getContextualMenuProps(ev, column),
  507. });
  508. }
  509. };
  510. private _onColumnContextMenu = (column: IColumn, ev: React.MouseEvent<HTMLElement>): void => {
  511. if (column.columnActionsMode !== ColumnActionsMode.disabled) {
  512. this.setState({
  513. contextualMenuProps: this._getContextualMenuProps(ev, column),
  514. });
  515. }
  516. };
  517. private _onContextualMenuDismissed = (): void => {
  518. this.setState({
  519. contextualMenuProps: undefined,
  520. });
  521. };
  522. private _onSortColumn = (columnKey: string, isSortedDescending: boolean): void => {
  523. const sortedItems = _copyAndSort(this._allItems, columnKey, isSortedDescending);
  524. this.setState({
  525. items: sortedItems,
  526. announcedMessage: `${columnKey} is sorted ${isSortedDescending ? 'descending' : 'ascending'}`,
  527. groups: undefined,
  528. columns: this._buildColumns(
  529. sortedItems,
  530. true,
  531. this._onColumnClick,
  532. columnKey,
  533. isSortedDescending,
  534. undefined,
  535. this._onColumnContextMenu,
  536. ),
  537. isSortedDescending: isSortedDescending,
  538. sortedColumnKey: columnKey,
  539. });
  540. };
  541. private _onGroupByColumn = (column: IColumn): void => {
  542. const { key, isGrouped } = column;
  543. const { sortedColumnKey, isSortedDescending, groups, items, columns } = this.state;
  544. if (isGrouped) {
  545. // ungroup
  546. this._onSortColumn(sortedColumnKey!, !!isSortedDescending);
  547. } else {
  548. let groupedItems = [];
  549. let newGroups: IGroup[];
  550. if (groups) {
  551. newGroups = [...groups];
  552. groupedItems = this._groupByKey(newGroups, items, key as keyof IExampleItem);
  553. } else {
  554. groupedItems = _copyAndSort(items, key);
  555. newGroups = this._getGroups(groupedItems, key as keyof IExampleItem);
  556. }
  557. for (const c of columns) {
  558. if (c.key === key) {
  559. c.isGrouped = true;
  560. break;
  561. }
  562. }
  563. this.setState({
  564. items: groupedItems,
  565. columns: [...columns],
  566. groups: newGroups,
  567. });
  568. }
  569. };
  570. private _groupByKey(groups: IGroup[], items: IExampleItem[], key: keyof IExampleItem): IExampleItem[] {
  571. let groupedItems: IExampleItem[] = [];
  572. if (groups) {
  573. for (const group of groups) {
  574. if (group.children && group.children.length > 0) {
  575. const childGroupedItems = this._groupByKey(group.children, items, key);
  576. groupedItems = groupedItems.concat(childGroupedItems);
  577. } else {
  578. const itemsInGroup = items.slice(group.startIndex, group.startIndex + group.count);
  579. const nextLevelGroupedItems = _copyAndSort(itemsInGroup, key);
  580. groupedItems = groupedItems.concat(nextLevelGroupedItems);
  581. group.children = this._getGroups(nextLevelGroupedItems, key, group);
  582. }
  583. }
  584. }
  585. return groupedItems;
  586. }
  587. private _getGroups(groupedItems: IExampleItem[], key: keyof IExampleItem, parentGroup?: IGroup): IGroup[] {
  588. const separator = '-';
  589. const groups = groupedItems.reduce((current: IGroup[], item: IExampleItem, index: number) => {
  590. const currentGroup = current[current.length - 1];
  591. const itemColumnValue = item[key];
  592. if (!currentGroup || this._getLeafGroupKey(currentGroup.key, separator) !== itemColumnValue) {
  593. current.push({
  594. key: (parentGroup ? parentGroup.key + separator : '') + itemColumnValue,
  595. name: key + ': ' + itemColumnValue,
  596. startIndex: parentGroup ? parentGroup.startIndex + index : index,
  597. count: 1,
  598. level: parentGroup ? parentGroup.level! + 1 : 0,
  599. });
  600. } else {
  601. currentGroup.count++;
  602. }
  603. return current;
  604. }, [] as IGroup[]);
  605. return groups;
  606. }
  607. private _getLeafGroupKey(key: string, separator: string): string {
  608. let leafKey = key;
  609. if (key.indexOf(separator) !== -1) {
  610. const arrKeys = key.split(separator);
  611. leafKey = arrKeys[arrKeys.length - 1];
  612. }
  613. return leafKey;
  614. }
  615. private _onAddRow = (): void => {
  616. this.setState({
  617. items: createListItems(1).concat(this.state.items),
  618. });
  619. };
  620. private _onDeleteRow = (): void => {
  621. if (this._selection.getSelectedCount() > 0) {
  622. this.setState((previousState: IDetailsListAdvancedExampleState) => {
  623. return {
  624. items: previousState.items.filter((item, index) => !this._selection.isIndexSelected(index)),
  625. };
  626. });
  627. } else {
  628. this.setState({
  629. items: this.state.items.slice(1),
  630. });
  631. }
  632. };
  633. private _onItemsSelectionChanged = () => {
  634. this.setState({
  635. selectionCount: this._selection.getSelectedCount(),
  636. });
  637. };
  638. private _buildColumns(
  639. items: IExampleItem[],
  640. canResizeColumns?: boolean,
  641. onColumnClick?: (ev: React.MouseEvent<HTMLElement>, column: IColumn) => any,
  642. sortedColumnKey?: string,
  643. isSortedDescending?: boolean,
  644. groupedColumnKey?: string,
  645. onColumnContextMenu?: (column: IColumn, ev: React.MouseEvent<HTMLElement>) => any,
  646. ) {
  647. const columns = buildColumns(
  648. items,
  649. canResizeColumns,
  650. onColumnClick,
  651. sortedColumnKey,
  652. isSortedDescending,
  653. groupedColumnKey,
  654. );
  655. columns.forEach(column => {
  656. column.onRenderDivider = this._onRenderDivider;
  657. column.onColumnContextMenu = onColumnContextMenu;
  658. column.ariaLabel = `Operations for ${column.name}`;
  659. if (column.key === 'thumbnail') {
  660. column.iconName = 'Picture';
  661. column.isIconOnly = true;
  662. } else if (column.key === 'description') {
  663. column.isMultiline = true;
  664. column.minWidth = 200;
  665. } else if (column.key === 'name') {
  666. column.onRender = (item: IExampleItem) => <Link data-selection-invoke={true}>{item.name}</Link>;
  667. } else if (column.key === 'key') {
  668. column.columnActionsMode = ColumnActionsMode.disabled;
  669. column.onRender = (item: IExampleItem) => (
  670. <Link className={classNames.linkField} href="https://microsoft.com" target="_blank" rel="noopener">
  671. {item.key}
  672. </Link>
  673. );
  674. column.minWidth = 90;
  675. column.maxWidth = 90;
  676. }
  677. });
  678. return columns;
  679. }
  680. }
  681. function _copyAndSort<T>(items: T[], columnKey: string, isSortedDescending?: boolean): T[] {
  682. const key = columnKey as keyof T;
  683. return items.slice(0).sort((a: T, b: T) => ((isSortedDescending ? a[key] < b[key] : a[key] > b[key]) ? 1 : -1));
  684. }