Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

New feature extension ListView > dragDropFiles V1 #711

Merged
merged 1 commit into from
Nov 6, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
20 changes: 19 additions & 1 deletion docs/documentation/docs/controls/ListView.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,10 @@ This control renders a list view for the given set of items.

![ListView control with grouping](../assets/ListView-grouping.png)

**List view control with drag and drop applied**

![ListView control with grouping](../assets/ListView-DragDrop.png)

## How to use this control in your solutions

- Check that you installed the `@pnp/spfx-controls-react` dependency. Check out the [getting started](../../#getting-started) page for more information about installing the dependency.
Expand All @@ -30,7 +34,9 @@ import { ListView, IViewField, SelectionMode, GroupOrder, IGrouping } from "@pnp
showFilter={true}
defaultFilter="John"
filterPlaceHolder="Search..."
groupByFields={groupByFields} />
groupByFields={groupByFields}
dragDropFiles={true}
onDrop={this._getDropFiles} />
```
- The control provides full text filtering through all the columns. If you want to exectue filtering on the specified columns, you can use syntax : `<ColumndName>`:`<FilterValue>`. Use `':'` as a separator between column name and value. Control support both `'fieldName'` and `'name'` properties of IColumn interface.

Expand Down Expand Up @@ -61,6 +67,16 @@ const groupByFields: IGrouping[] = [
!!! note "Extend ListView with a ContextualMenu"
To extend the `ListView` control with a [ContextualMenu](https://developer.microsoft.com/en-us/fabric#/components/contextualmenu) refer to [ListView.ContextualMenu](./ListView.ContextualMenu.md).

- With the `onDrop` handler you can define a method that returns files that where drag and drop by user in the list view:

```typescript
private _getDropFiles = (files) => {
for (var i = 0; i < files.length; i++) {
console.log(files[i].name);
}
}
```

## Implementation

The ListView control can be configured with the following properties:
Expand All @@ -78,6 +94,8 @@ The ListView control can be configured with the following properties:
| filterPlaceHolder | string | no | Specify the placeholder for the filter text box. Default 'Search' |
| showFilter | boolean | no | Specify if the filter text box should be rendered. |
| defaultFilter | string | no | Specify the initial filter to be applied to the list. |
| onDrop | file | no | Event handler returns files from drag and drop. |
| stickyHeader | boolean | no | Specifies if the header of the `ListView`, including search box, is sticky |

The `IViewField` has the following implementation:

Expand Down
11 changes: 11 additions & 0 deletions src/controls/listView/IListView.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,15 @@ export enum GroupOrder {
}

export interface IListViewProps {

/**
* Specify if drag and drop option is selected.
**/
dragDropFiles?: boolean;
/**
* Handler to return the files from drag and drop.
**/
onDrop?: any;
/**
* Specify the name of the file URL path which will be used to show the file icon.
*/
Expand Down Expand Up @@ -73,6 +82,8 @@ export interface IListViewState {
columns?: IColumn[];

groups?: IGroup[];

dragStatus?: boolean;
}

export interface IGrouping {
Expand Down
25 changes: 25 additions & 0 deletions src/controls/listView/ListView.DragDrop.module.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
.DragDropArea {
position: relative;
min-height: 150px;
}

.DragDropAreaBorder {
border: dashed grey 1px;
background-color: rgba(255,255,255,.6);
position: absolute;
top: 0;
bottom: 0;
left: 0;
right: 0;
z-index: 1;
}

.DragDropAreaZone {
position: absolute;
top: 35%;
right: 0;
left: 0;
text-align: center;
color: grey;
font-size: 34px;
}
94 changes: 89 additions & 5 deletions src/controls/listView/ListView.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import * as React from 'react';
import styles from './ListView.DragDrop.module.scss';
import { DetailsList, DetailsListLayoutMode, Selection, SelectionMode, IGroup } from 'office-ui-fabric-react/lib/DetailsList';
import { IListViewProps, IListViewState, IViewField, IGrouping, GroupOrder } from './IListView';
import { IColumn, IGroupRenderProps } from 'office-ui-fabric-react/lib/components/DetailsList';
Expand All @@ -10,6 +11,7 @@ import * as telemetry from '../../common/telemetry';
import filter = require('lodash/filter');
import { SearchBox } from 'office-ui-fabric-react/lib/SearchBox';
import { Guid } from '@microsoft/sp-core-library';
import { Icon } from 'office-ui-fabric-react/lib/Icon';

/**
* File type icon component
Expand All @@ -19,6 +21,11 @@ export class ListView extends React.Component<IListViewProps, IListViewState> {
private originalItems: any[];
private originalGroups: IGroup[];
private originalColumns: IColumn[];
private dragCounter = 0;
private dropArea = null;
private dropRef = element => {
this.dropArea = element;
};

constructor(props: IListViewProps) {
super(props);
Expand All @@ -34,7 +41,8 @@ export class ListView extends React.Component<IListViewProps, IListViewState> {
// Initialize state
this.state = {
items: [],
filterValue: this.props.defaultFilter
filterValue: this.props.defaultFilter,
dragStatus: false
};

if (this.props.selection) {
Expand All @@ -53,6 +61,17 @@ export class ListView extends React.Component<IListViewProps, IListViewState> {
this._processProperties();
}

public componentWillUnmount(): void {
const { dragDropFiles } = this.props;
if (dragDropFiles) {
let divDropArea = this.dropArea;
divDropArea.removeEventListener('dragenter', this.handleonDragEnter);
divDropArea.removeEventListener('dragleave', this.handleonDragLeave);
divDropArea.removeEventListener('dragover', this.handleonDragOver);
divDropArea.removeEventListener('drop', this.handleonDrop);
}
}

/**
* Lifecycle hook when component did update after state or property changes
* @param prevProps
Expand Down Expand Up @@ -174,7 +193,7 @@ export class ListView extends React.Component<IListViewProps, IListViewState> {
* Process all the component properties
*/
private _processProperties() {
const { items, iconFieldName, viewFields, groupByFields, showFilter } = this.props;
const { dragDropFiles, items, iconFieldName, viewFields, groupByFields, showFilter } = this.props;

let tempState: IListViewState = cloneDeep(this.state);
let columns: IColumn[] = null;
Expand Down Expand Up @@ -225,6 +244,15 @@ export class ListView extends React.Component<IListViewProps, IListViewState> {
// Update the current component state with the new values
this.setState(tempState);
}

// Add EventListeners for drag zone area
if (dragDropFiles) {
let divDropArea = this.dropArea;
divDropArea.addEventListener('dragenter', this.handleonDragEnter);
divDropArea.addEventListener('dragleave', this.handleonDragLeave);
divDropArea.addEventListener('dragover', this.handleonDragOver);
divDropArea.addEventListener('drop', this.handleonDrop);
}
}

/**
Expand Down Expand Up @@ -490,14 +518,61 @@ export class ListView extends React.Component<IListViewProps, IListViewState> {
return result;
}

/**
* Stop listeners from onDragOver event.
* @param e
*/
private handleonDragOver = (e) => {
e.preventDefault();
e.stopPropagation();
}
/**
* Stop listeners from onDragEnter event, enable drag and drop view.
* @param e
*/
private handleonDragEnter = (e) => {
e.preventDefault();
e.stopPropagation();
this.dragCounter++;
if (e.dataTransfer.items && e.dataTransfer.items.length > 0) {
this.setState({ dragStatus: true });
}
}
/**
* Stop listeners from ondragenter event, disable drag and drop view.
* @param e
*/
private handleonDragLeave = (e) => {
e.preventDefault();
e.stopPropagation();
this.dragCounter--;
if (this.dragCounter === 0) {
this.setState({ dragStatus: false });
}
}
/**
* Stop listeners from onDrop event and load files to property onDrop.
* @param e
*/
private handleonDrop = (e) => {
e.preventDefault();
e.stopPropagation();
this.setState({ dragStatus: false });
if (e.dataTransfer.files && e.dataTransfer.files.length > 0) {
this.props.onDrop(e.dataTransfer.files);
e.dataTransfer.clearData();
this.dragCounter = 0;
}
}

/**
* Default React component render method
*/
public render(): React.ReactElement<IListViewProps> {
let groupProps: IGroupRenderProps = {};

let { showFilter, filterPlaceHolder } = this.props;
let { filterValue, items } = this.state;
let { showFilter, filterPlaceHolder, dragDropFiles } = this.props;
let { filterValue, items, dragStatus } = this.state;

// Check if selection mode is single selection,
// if that is the case, disable the selection on grouping headers
Expand All @@ -511,7 +586,16 @@ export class ListView extends React.Component<IListViewProps, IListViewState> {
}

return (
<div>
<div className={styles.DragDropArea}
ref={this.dropRef}>
{(dragStatus && dragDropFiles) &&
<div className={styles.DragDropAreaBorder}>
<div className={styles.DragDropAreaZone}>
<Icon iconName="Download" className="ms-IconExample" />
<div>{strings.UploadFileHeader}</div>
</div>
</div>
}
{
showFilter && <SearchBox placeholder={filterPlaceHolder || strings.ListViewFilterLabel} onSearch={this._updateFilterValue} onChange={this._updateFilterValue} value={filterValue} />
}
Expand Down
12 changes: 12 additions & 0 deletions src/webparts/controlsTest/components/ControlsTest.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -361,6 +361,16 @@ export default class ControlsTest extends React.Component<IControlsTestProps, IC
console.log('Items:', items);
}

/**
* Method that retrieves files from drag and drop
* @param files
*/
private _getDropFiles = (files) => {
for (var i = 0; i < files.length; i++) {
console.log(files[i].name);
}
}

/**
*
*Method that retrieves the selected terms from the taxonomy picker and sets state
Expand Down Expand Up @@ -960,6 +970,8 @@ export default class ControlsTest extends React.Component<IControlsTestProps, IC
selectionMode={SelectionMode.single}
selection={this._getSelection}
showFilter={true}
dragDropFiles={true}
onDrop={this._getDropFiles}
// defaultFilter="Team"
/>

Expand Down