readme for react-multilist-grid (#131)
* rc0 * rc0 * more rc0 * rc0 * RC0 Done * cleanup manifest * Working on Web Selecor * qqqqqqq * 111 * Added Web selector to drill down to web * fix merge conflicts * s * renmove react-router * added saveall and undoall * Add support for ID field * saveAll * fixed saveall across sites * mark items as deleted (soft delete) * work on soft delete * work on soft delete * Enable soft deletes * Add SortInfo to columnDefinitions * Work on initial sort * pass columnDefinitions into listitemreducer for sort * trying to get sortablecolumns in reducer... * setup sort sequences * work on compareres * work on sort * basic sorting workoing * fix linting errors * Fided issus with Duplicate 'Require" * added images * updated readme * Update README.md * fix coltype display and lists in subsites * Added Images * docs * Update README.md * Update README.md
|
@ -1,9 +1,68 @@
|
||||||
# SPFX React Grid
|
# SPFX React Grid
|
||||||
|
|
||||||
## Summary
|
## Summary
|
||||||
An SPFX Webpart that uses React, Office-UI-Fabric, and Redux to let users edit list data from multiple Webs and Multiple Sites in a single grid.
|
React-multilist-grid is an SPFX webpart that uses React, Office-UI-Fabric, and Redux to let users edit list data from lists that reside in multiple webs and multiple sites in a single grid, similar to Quick Edit mode. The lists do not to be of the same type – you just need to create column mappings to tell the webpart which fields to show in which columns of the grid.
|
||||||
|
|
||||||
![alt tag](/samples/react-spfx-multilist-grid/src/images/editListItems.PNG)
|
The configuration panel of the webpart is show below. It has two buttons—one to configure the columns that will be displayed on the grid, and another to configure the lists that contain the data to be edited.
|
||||||
|
![config panel](./src/images/Configuration.PNG)
|
||||||
|
|
||||||
|
Clicking on the Update Columns button shows the Column Definition configuration panel shown below:
|
||||||
|
![columnDefinitions panel](./src/images/columnDefinitions.PNG)
|
||||||
|
|
||||||
|
When adding a column the webpart automatically assigns a guid for internal use. In the 'name' field you can use any name you like. The text entered here will be used as the column header in the grid. In the 'type' field, you need to select the type of data to be displayed:
|
||||||
|
![ColumnTypes panel](./src/images/ColumnTypes.PNG)
|
||||||
|
|
||||||
|
These are SharePoint field types for the most part(not all field types have been implemented yet). There is one 'special' field type called 'List Title'. It is used to identify which list an item came from when displayed in the grid. You can move an item from one list (and site) to another by changing the selected list title in the grid. In the 'editable' field you can specify whether this field can be edited in the grid(not yet implemented). The sortSequence and sortDirection field let you set up default sorting on a given column.
|
||||||
|
|
||||||
|
The columns will be displayed in the grid in the order they appear on the Column Definition panel. You can use the up and down arrow icons on each row to rearrange the columns. The delete icon on each row can be used to delete a column from the grid.
|
||||||
|
|
||||||
|
After defining all the columns to be displayed in your grid, click the save icon in the command bar and you will be returned to the configuration panel for the webpart.
|
||||||
|
|
||||||
|
Clicking the Update Lists button on the configuration panel will open the List Definitions panel:
|
||||||
|
![ListDefinitions panel](./src/images/ListDefinitions.PNG)
|
||||||
|
Here you configure which lists the webpart should display in the grid. When adding a list the webpart automatically assigns a guid for internal use. In the 'SiteUrl' you must enter the url of the site collection that contains the list. In the 'List Definition Title' field enter a user friendly title for this list. The text you enter here will be shown on the grid for all items that came from this list in the 'List Title' column (provided you have created a 'List Title' column.
|
||||||
|
|
||||||
|
When you tab to the 'Web Containing List' field, you can use the search icon:
|
||||||
|
|
||||||
|
![SelectWeb1 panel](./src/images/SelectWeb1.PNG)
|
||||||
|
|
||||||
|
to open the Web Selector which allows you to choose the web that contains the list to display:
|
||||||
|
![SelectWeb2 panel](./src/images/SelectWeb2.PNG)
|
||||||
|
|
||||||
|
The Web Selector initially shows all the webs under the rootweb. When you select a different web, it then shows the subwebs under the selected web. You can keep selecting subwebs until you reach the web that contains the list to display.
|
||||||
|
|
||||||
|
After selecting the web, tab over to the list column, and choose the list from the selected web that you want to show in the grid:
|
||||||
|
|
||||||
|
![Select List panel](./src/images/Select List.PNG)
|
||||||
|
|
||||||
|
Following the 'List' field, there is one field for each column you created in the Column Definitions panel. When you tab to each of these columns a dropdown list is presented showing you all the fields in this list that match the field type you specified on the Column Definition panel:
|
||||||
|
![Select List panel](./src/images/Select List.PNG)
|
||||||
|
|
||||||
|
Choose the field you want to have displayed for this list in the specified column.
|
||||||
|
|
||||||
|
[Selecting the Title Field]
|
||||||
|
![Select the title field](./src/images/SelectField1.PNG)
|
||||||
|
|
||||||
|
[Selecting the Due Date Field]
|
||||||
|
![Select the Due Date field](./src/images/SelectField2.PNG)
|
||||||
|
|
||||||
|
Click the save button in the command bar once you are done defining all your lists.
|
||||||
|
|
||||||
|
The webpart will now display a grid showing all of the items in the lists you have selected:
|
||||||
|
![editListItems](./src/images/editListItems.PNG)
|
||||||
|
|
||||||
|
You can edit any cell in the grid by tabbing to it, or clicking on it. To move an item between lists, simply click on the 'List Title' column and choose a new list:
|
||||||
|
|
||||||
|
![MoveListItem](./src/images/MoveListItem.PNG)
|
||||||
|
|
||||||
|
If you change a field of type 'user', the webpart will attempt to lookup the user in the destination site and use that user when adding the item to the new site. Similar mappings are done for other column types.
|
||||||
|
|
||||||
|
You can click the save icon on each row to save the selected row back to SharePoint. The delete icon can be used to mark an item to be deleted:
|
||||||
|
![deleteitem](./src/images/deleteitem.PNG)
|
||||||
|
|
||||||
|
The item will not be removed from SharePoint until the save icon is clicked.
|
||||||
|
|
||||||
|
Use the Undo icon to undo the changes made to an item.
|
||||||
|
|
||||||
|
|
||||||
## Used SharePoint Framework Version
|
## Used SharePoint Framework Version
|
||||||
|
|
After Width: | Height: | Size: 60 KiB |
After Width: | Height: | Size: 12 KiB |
Before Width: | Height: | Size: 31 KiB After Width: | Height: | Size: 72 KiB |
After Width: | Height: | Size: 49 KiB |
After Width: | Height: | Size: 73 KiB |
After Width: | Height: | Size: 71 KiB |
After Width: | Height: | Size: 75 KiB |
After Width: | Height: | Size: 9.5 KiB |
After Width: | Height: | Size: 80 KiB |
Before Width: | Height: | Size: 39 KiB After Width: | Height: | Size: 51 KiB |
After Width: | Height: | Size: 6.8 KiB |
Before Width: | Height: | Size: 51 KiB After Width: | Height: | Size: 64 KiB |
|
@ -150,7 +150,7 @@ export class ColumnDefinitionContainerNative extends React.Component<IColumnsPag
|
||||||
name: "type",
|
name: "type",
|
||||||
editable: true,
|
editable: true,
|
||||||
editor: "FieldTypesEditor",
|
editor: "FieldTypesEditor",
|
||||||
formatter: "",
|
formatter: "FieldTypesFormatter",
|
||||||
width: 80
|
width: 80
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
@ -307,6 +307,13 @@ export class ColumnDefinitionContainerNative extends React.Component<IColumnsPag
|
||||||
|
|
||||||
case "SharePointLookupCellFormatter":
|
case "SharePointLookupCellFormatter":
|
||||||
return (<SharePointLookupCellFormatter value={entity[gridColumn.name]} onFocus={this.toggleEditing} />);
|
return (<SharePointLookupCellFormatter value={entity[gridColumn.name]} onFocus={this.toggleEditing} />);
|
||||||
|
case "FieldTypesFormatter":
|
||||||
|
debugger;
|
||||||
|
const displayName=_.find(fieldTypes,ft=>{return ft.key===entity[gridColumn.name]}).text;
|
||||||
|
return (<a href="#" onFocus={this.toggleEditing} style={{ textDecoration: "none" }}>
|
||||||
|
{displayName}
|
||||||
|
</a>
|
||||||
|
);
|
||||||
default:
|
default:
|
||||||
return (<a href="#" onFocus={this.toggleEditing} style={{ textDecoration: "none" }}>
|
return (<a href="#" onFocus={this.toggleEditing} style={{ textDecoration: "none" }}>
|
||||||
{entity[gridColumn.name]}
|
{entity[gridColumn.name]}
|
||||||
|
|
|
@ -259,6 +259,7 @@ export class ListDefinitionContainerNative extends React.Component<IListViewPage
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
this.props.getListsForWeb(utils.ParseSPField(listDef.webLookup).id);
|
||||||
return []; // havent fetched parent yet,
|
return []; // havent fetched parent yet,
|
||||||
}
|
}
|
||||||
public getFieldsForlist(listDef: ListDefinition, colType?: string): Array<WebListField> {
|
public getFieldsForlist(listDef: ListDefinition, colType?: string): Array<WebListField> {
|
||||||
|
|
|
@ -70,19 +70,38 @@ export default class PropertyFieldListDefinitionsHost extends React.Component<IP
|
||||||
}
|
}
|
||||||
|
|
||||||
private getListsForWeb(webUrl: string): any {
|
private getListsForWeb(webUrl: string): any {
|
||||||
|
debugger;
|
||||||
const spWeb = new SPWeb(webUrl);
|
const spWeb = new SPWeb(webUrl);
|
||||||
const promise = spWeb.lists.orderBy("Title").get()
|
const promise = spWeb.lists.orderBy("Title").get()
|
||||||
.then((response) => {
|
.then((response) => {
|
||||||
const data = _.map(response, (item: any) => {
|
const data = _.map(response, (item: any) => {
|
||||||
return new WebList(item.Id, item.Title, item.Url, );
|
return new WebList(item.Id, item.Title, item.Url, );
|
||||||
});
|
});
|
||||||
for (const site of this.state.Sites) {
|
// for (const site of this.state.Sites) {
|
||||||
for (const web of site.webs) {
|
// for (const web of site.webs) {
|
||||||
if (web.url === webUrl) {
|
// if (web.url === webUrl) {
|
||||||
|
// web.lists = data;
|
||||||
|
// web.listsFetched = true;
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
|
const site: Site = _.find(this.state.Sites, (s: Site) => {
|
||||||
|
return (webUrl.substr(0, s.url.length).toLowerCase() === s.url.toLowerCase());
|
||||||
|
});
|
||||||
|
if (site) {
|
||||||
|
let web = _.find(site.webs, (w: Web) => {
|
||||||
|
return w.url = webUrl;
|
||||||
|
})
|
||||||
|
if (web) {
|
||||||
web.lists = data;
|
web.lists = data;
|
||||||
web.listsFetched = true;
|
web.listsFetched = true;
|
||||||
}
|
}
|
||||||
|
else {
|
||||||
|
// TODO : frtch the title and ID
|
||||||
|
var idx: number = site.webs.push(new Web("", "", webUrl));// dont have id and titlle here
|
||||||
|
site.webs[idx].lists = data;
|
||||||
|
site.webs[idx].listsFetched = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
this.setState(this.state);
|
this.setState(this.state);
|
||||||
|
|
|
@ -2,30 +2,51 @@
|
||||||
|
|
||||||
## Summary
|
## Summary
|
||||||
A set of 3 SPFX webparts that use different open-source carousels (react-3d-carousel, reactjs-coverface, and react-slick)
|
A set of 3 SPFX webparts that use different open-source carousels (react-3d-carousel, reactjs-coverface, and react-slick)
|
||||||
to display videos stored on an O365 Video Channel. The idea being to display a carousel of the thumbnail images, and then
|
to display videos stored on an Office 365 Video Channel. The idea being to display a carousel of the thumbnail images, and then
|
||||||
when a user clicks on one of the thumbnails, replace the tumbnail with a video player and start the video up.
|
when a user clicks on one of the thumbnails, replace the tumbnail with a video player, or an Iframe playing the video.
|
||||||
|
|
||||||
The first webpart used react-3d-carousel. The carousel looks great, but i found no way to swap out the image and replace
|
All 3 webparts share a common utility class (O365Vutilities) that is used to talk to the tenants Video Service through its rest
|
||||||
it with a video player. This carousel would be fine for displayin a picture library though,
|
API (https://msdn.microsoft.com/en-us/office/office365/api/video-rest-operations)
|
||||||
|
|
||||||
The second webpart used react-slick. The carousel is not as fancy as react-3d-carousel, but i was able to to swap out the
|
The first webpart used react-3d-carousel. The carousel looks great, but I found no way to swap out the image and replace
|
||||||
image and replace it with a video player once a user clicked it. I had trouble with the css and getting the next and previous
|
it with a video player or Iframe. This carousel would be fine for displaying a picture library though. A sample of the webpart
|
||||||
buttons to show. If you run the webpart, the buttons are there, they are just not visible.
|
being used on a modern page is shown below:
|
||||||
|
![react-3d-carousel](./src/assets/react-3d-carousel.PNG)
|
||||||
Finally I tried reactjs-coverface. It has nice scrolling through the images withe the mousweheel, and some cool 3d effects.
|
|
||||||
It was also simple to swap the image with a video player once a user clicked it (same code as react-slick). This is the best
|
|
||||||
of the three for my purposes.
|
|
||||||
|
|
||||||
|
|
||||||
In the future I want to modify this webpart to link a Sharepoint list with the video channel so that users can enter additional
|
And a sample of the webparts configuration:
|
||||||
|
|
||||||
|
|
||||||
|
![react-3d-carousel configuration](./src/assets/react-3d-carousel config.PNG)
|
||||||
|
|
||||||
|
The getPropertyPaneConfiguration of the webpart calls a method in the O365Vutilities class to get a list of
|
||||||
|
channels on the tenants Video Service, and allows the user to select a channel.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
The second webpart used react-slick. The carousel is not as fancy as react-3d-carousel, but I was able to to swap out the
|
||||||
|
image and replace it with an Iframe playing the Video once a user clicked it. I had trouble with the css and getting the next and previous
|
||||||
|
buttons to show. If you run the webpart, the buttons are there to the left and the right of the image, they are just not visible.You can find them by moving the mouse along the left and right borders. Hopefully someone with better css skiils than I can fix this. You can also change videos by clicking the dots at the bottom of the webpart, A sample of the webpart is shown below:
|
||||||
|
|
||||||
|
![react-slick](./src/assets/react-slick.PNG)
|
||||||
|
And a sample of the webparts configuration:
|
||||||
|
|
||||||
|
![react-slick](./src/assets/react-slick-configuration.PNG)
|
||||||
|
|
||||||
|
Finally I tried reactjs-coverflow. It has nice scrolling through the images with the mousweheel, and some cool 3d effects.
|
||||||
|
It was also simple to swap the image with an Iframe playing the Video once a user clicked it (same code as react-slick). This is the best
|
||||||
|
of the three for my purposes. A sample of the webpart is shown below:
|
||||||
|
![react-coverflow](./src/assets/react-coverflow.PNG)
|
||||||
|
|
||||||
|
And a sample of its configuration (this one I made fully configurable, the others had a lot of hard-coded values)
|
||||||
|
![react-coverflow](./src/assets/react-coverflow configuratioion.PNG)
|
||||||
|
|
||||||
|
In the future I would like to modify this webpart to link a SharePoint list with the video channel so that users can enter additional
|
||||||
metadata for the video and be anle to search/filter the videos using this metadata.
|
metadata for the video and be anle to search/filter the videos using this metadata.
|
||||||
|
|
||||||
See also https://github.com/russgove/O365VideoSync. It's a console app that you can schedule to run to synchronize an Office 365 Video Channel with a sharepoint list (on prem or otherwise).
|
See also https://github.com/russgove/O365VideoSync. It's a console app that you can schedule to run to synchronize an Office 365 Video Channel with a sharepoint list (on prem or otherwise).
|
||||||
|
|
||||||
|
|
||||||
![alt tag](/samples/react-spfx-multilist-grid/src/images/editListItems.PNG)
|
|
||||||
|
|
||||||
|
|
||||||
## Used SharePoint Framework Version
|
## Used SharePoint Framework Version
|
||||||
![drop](https://img.shields.io/badge/drop-drop5-red.svg)
|
![drop](https://img.shields.io/badge/drop-drop5-red.svg)
|
||||||
|
|
||||||
|
@ -63,7 +84,6 @@ Version|Date|Comments
|
||||||
- Clone this repository
|
- Clone this repository
|
||||||
- in the command line run:
|
- in the command line run:
|
||||||
- `npm install`
|
- `npm install`
|
||||||
- `tsd install`
|
|
||||||
- `gulp serve`
|
- `gulp serve`
|
||||||
|
|
||||||
> Include any additional steps as needed.
|
> Include any additional steps as needed.
|
||||||
|
|
After Width: | Height: | Size: 16 KiB |
After Width: | Height: | Size: 277 KiB |
After Width: | Height: | Size: 24 KiB |
After Width: | Height: | Size: 262 KiB |
After Width: | Height: | Size: 10 KiB |
After Width: | Height: | Size: 463 KiB |
|
@ -4,7 +4,7 @@
|
||||||
"module": "commonjs",
|
"module": "commonjs",
|
||||||
"jsx": "react",
|
"jsx": "react",
|
||||||
"declaration": true,
|
"declaration": true,
|
||||||
"sourceMap": true,
|
"sourceMap": true
|
||||||
"types": [ "webpack-env" ]
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,10 +1,5 @@
|
||||||
// Type definitions for Microsoft ODSP projects
|
// Type definitions for Microsoft ODSP projects
|
||||||
// Project: ODSP
|
// Project: ODSP
|
||||||
|
|
||||||
/// <reference path="odsp-webpack.d.ts" />
|
|
||||||
|
|
||||||
/* Global definition for DEBUG builds */
|
|
||||||
declare const DEBUG: boolean;
|
|
||||||
|
|
||||||
/* Global definition for UNIT_TEST builds */
|
/* Global definition for UNIT_TEST builds */
|
||||||
declare const UNIT_TEST: boolean;
|
declare const UNIT_TEST: boolean;
|