* Moved configuration of Columns and Lists to the PropertyPane

* Ypdated Images * added comments on enabling columnn types
This commit is contained in:
Russell gove 2017-01-09 16:57:41 -05:00 committed by Vesa Juvonen
parent 641b406289
commit e048476201
17 changed files with 650 additions and 843 deletions

Binary file not shown.

Before

Width:  |  Height:  |  Size: 37 KiB

After

Width:  |  Height:  |  Size: 31 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 38 KiB

After

Width:  |  Height:  |  Size: 39 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 32 KiB

After

Width:  |  Height:  |  Size: 51 KiB

View File

@ -1,8 +1,8 @@
import ListDefinition from "./model/ListDefinition";
import column from "./model/ColumnDefinition";
import ColumnDefinition from "./model/ColumnDefinition";
export interface ISpfxReactGridWebPartProps {
description: string;
lists: ListDefinition[];
columns: column[];
columns: ColumnDefinition[];
}

View File

@ -1,29 +1,21 @@
import * as React from "react";
import * as ReactDom from "react-dom";
import { Provider } from "react-redux";
// require("office-ui-fabric/dist/css/Fabric.css");
// require("office-ui-fabric/dist/css/Fabric.Components.css");
// require("office-ui-fabric/dist/components/CommandBar/CommandBar.css");
import configureStore from "./store/configure-store";
const { Router, createMemoryHistory } = require("react-router");
import { addLists } from "./actions/listActions";
import { addColumns } from "./actions/columnActions";
import { addPageContext } from "./actions/PageContextActions";
import { addColumns, removeAllColumns } from "./actions/columnActions";
import { addLists, removeAllLists } from "./actions/listActions";
import { PropertyFieldColumnDefinitions, IPropertyFieldColumnDefinitionsProps } from "./containers/PropertyFieldColumnDefinitions";
import { PropertyFieldListDefinitions, IPropertyFieldListDefinitionsProps } from "./containers/PropertyFieldListDefinitions";
import {
BaseClientSideWebPart,
IPropertyPaneSettings, IWebPartData, IHtmlProperties,
IPropertyPaneSettings,
IWebPartContext,
PropertyPaneTextField
} from "@microsoft/sp-webpart-base";
import { Log } from "@microsoft/sp-client-base";
import routes from "./store/routes";
import * as strings from "spfxReactGridStrings";
import { ISpfxReactGridWebPartProps } from "./ISpfxReactGridWebPartProps";
const store: Redux.Store<any> = configureStore({});
const history = createMemoryHistory(location);
@ -35,31 +27,64 @@ const App: React.StatelessComponent<any> = () => (
</Provider>
);
export default class SpfxReactGridWebPart extends BaseClientSideWebPart<ISpfxReactGridWebPartProps> {
private cdProps: IPropertyFieldColumnDefinitionsProps;
private ldProps: IPropertyFieldListDefinitionsProps;
public constructor(context: IWebPartContext) {
super(context);
Log.verbose("SpfxReactGridWebPart", "In constructor of SpfxReactGridWebPart");
this.onPropertyChange = this.onPropertyChange.bind(this);
this.cdProps = {
label: strings.ColumnDefinitionFieldLabel,
onPropertyChange: this.onPropertyChange,
getColumnDefinitions: ()=>{
return this.properties.columns||[];
},
};
this.ldProps = {
label: strings.ListDefinitionFieldLabel,
onPropertyChange: this.onPropertyChange,
getColumnDefinitions: ()=>{
return this.properties.columns||[];
},
getListDefinitions:() =>{
return this.properties.lists||[];
},
PageContext: this.context.pageContext
};
}
public onInit<T>(): Promise<T> {
return Promise.resolve(null);
}
public render(): void {
store.dispatch(addLists(this.properties.lists));
store.dispatch(addColumns(this.properties.columns));
store.dispatch(addPageContext(this.context.pageContext));
Log.verbose("SpfxReactGridWebPart", "In render of SpfxReactGridWebPart");
ReactDom.render(App(), this.domElement);
}
protected deserialize(data: IWebPartData): ISpfxReactGridWebPartProps {
const info = super.deserialize(data);
return info;
}
protected onBeforeSerialize(): IHtmlProperties {
// this.properties.columns = [];
// this.properties.lists =[];
this.properties.columns = store.getState().columns;
this.properties.lists = store.getState().lists;
return super.onBeforeSerialize();
}
private onPropertyChange(propertyPath: string, oldValue: any, newValue: any) {
switch (propertyPath) {
case "ColumnDefinitions":
this.properties.columns = newValue;
store.dispatch(removeAllColumns());
store.dispatch(addColumns(this.properties.columns));
break;
case "ListDefinitions":
this.properties.lists = newValue;
store.dispatch(removeAllLists());
store.dispatch(addLists(this.properties.lists));
break;
default:
break;
}
};
protected get propertyPaneSettings(): IPropertyPaneSettings {
Log.verbose("SpfxReactGridWebPart", "In propertyPaneSettings of SpfxReactGridWebPart");
return {
pages: [
@ -73,7 +98,9 @@ export default class SpfxReactGridWebPart extends BaseClientSideWebPart<ISpfxRea
groupFields: [
PropertyPaneTextField("description", {
label: strings.DescriptionFieldLabel
})
}),
PropertyFieldColumnDefinitions("ColumnDefinitions", this.cdProps),
PropertyFieldListDefinitions("ListDefinitions", this.ldProps)
]
}
]

View File

@ -1,15 +1,25 @@
import * as React from "react";
import { SharePointLookupCellFormatter } from "../components/SharePointFormatters";
const connect = require("react-redux").connect;
import { addColumn, removeColumn, saveColumn, removeAllColumns, moveCulumnUp, moveCulumnDown } from "../actions/columnActions";
import { addColumn, removeColumn, removeAllColumns, moveCulumnUp, moveCulumnDown } from "../actions/columnActions";
import ColumnDefinition from "../model/ColumnDefinition";
import { Button, ButtonType, TextField, CommandBar, Dropdown, IDropdownOption, Toggle } from "office-ui-fabric-react";
import Container from "../components/container";
import { Guid, Log } from "@microsoft/sp-client-base";
/** NOTE:
* To enable other column types
* 1. Uncomment it here
* 2. In Containers\ListItemContainer Add case in the CellContents method to display the field in a non-editable mode
* 3. In Containers\ListItemContainer Add case in the CellContentsEditabe method to display the field in an editable mode
* 4. In Containers\ListItemContainer Add case in the handleCellUpdated method to get the data entered by the user and change it back to the format that Sharepoint gave it to us in (See dateTime for an example)
* 4. If any other data is needed to render the contents in an editable mode (maybe Managed Metadata) Add a case in the Containers\ListItemContaine\toggleEditing
* method to get the data. Also will need to add another enitity to the store (actions, reducers, etc.)
* 5. Special logic may be needed when moving an item between lists (as in the cas of Users ). Add this to Containers\ListItemContaine\mapOldListFieldsToNewListFields
*
*/
const fieldTypes: Array<IDropdownOption> = [
{ key: null, text: "(Selecte one)" },
{ key: "__LISTDEFINITIONTITLE__", text: "List Title" }, //used to display the ListDefinition Title in the grid, for when users add a new item
{ key: "__LISTDEFINITIONTITLE__", text: "List Title" }, //used to display the ListDefinition Title in the grid, for when users add a new item
{ key: "Text", text: "Text" },
{ key: "Integer", text: "Integer" },
{ key: "Note", text: "Note" },
@ -36,14 +46,15 @@ const fieldTypes: Array<IDropdownOption> = [
// { name: "WorkflowEventType", value: "WorkflowEventType" },
];
interface IColumnsPageProps extends React.Props<any> {
export interface IColumnsPageProps extends React.Props<any> {
columns: Array<ColumnDefinition>;
addColumn: () => void;
removeAllColumns: () => void;
removeColumn: (column) => void;
saveColumn: (Column) => void;
moveColumnUp: (Column: ColumnDefinition) => void;
moveColumnDown: (Column: ColumnDefinition) => void;
save: () => void;
}
interface IContextMenu extends React.Props<any> {
onRowDelete: AdazzleReactDataGrid.ColumnEventCallback;
@ -60,9 +71,7 @@ function mapDispatchToProps(dispatch) {
const col: ColumnDefinition = new ColumnDefinition(id.toString(), "", 80, true);
dispatch(addColumn(col));
},
saveColumn: (updatedRowData): void => {
dispatch(saveColumn(updatedRowData));
},
removeColumn: (column): void => {
dispatch(removeColumn(column));
@ -77,6 +86,9 @@ function mapDispatchToProps(dispatch) {
moveColumnDown: (column): void => {
dispatch(moveCulumnDown(column));
},
save: (column): void => {
// // dispatch(moveCulumnDown(column));
},
};
}
@ -88,13 +100,13 @@ export interface GridColumn {
formatter?: string;
editor?: string;
}
interface IGridProps {
export interface IGridProps {
editing: {
entityid: string;
columnid: string;
};
}
class ColumnDefinitionContainer extends React.Component<IColumnsPageProps, IGridProps> {
export class ColumnDefinitionContainerNative extends React.Component<IColumnsPageProps, IGridProps> {
public constructor() {
super();
this.CellContents = this.CellContents.bind(this);
@ -108,6 +120,7 @@ class ColumnDefinitionContainer extends React.Component<IColumnsPageProps, IGrid
this.moveColumnUp = this.moveColumnUp.bind(this);
this.moveColumnDown = this.moveColumnDown.bind(this);
}
public gridColulumns: Array<GridColumn> = [{
id: "guid",
@ -146,7 +159,7 @@ class ColumnDefinitionContainer extends React.Component<IColumnsPageProps, IGrid
const entity: ColumnDefinition = this.props.columns.find((temp) => temp.guid === entityid);
const column = this.gridColulumns.find(temp => temp.id === columnid);
entity[column.name] = value;
this.props.saveColumn(entity);
// this.props.saveColumn(entity);
}
@ -308,7 +321,6 @@ class ColumnDefinitionContainer extends React.Component<IColumnsPageProps, IGrid
const { addColumn } = this.props;
return (
<Container testid="columns" size={2} center>
<h1>Column Definitions</h1>
<CommandBar items={[{
key: "AddColumns",
name: "Add a Column",
@ -321,7 +333,16 @@ class ColumnDefinitionContainer extends React.Component<IColumnsPageProps, IGrid
canCheck: true,
icon: "Delete",
onClick: this.props.removeAllColumns
}]} />
},
{
key: "save",
name: "save",
canCheck: true,
icon: "Save",
onClick: this.props.save
}
]} />
<table style={{ borderColor: "#600", borderWidth: "0 0 0 0", borderStyle: "solid" }}>
<thead>
<tr>
@ -341,4 +362,4 @@ class ColumnDefinitionContainer extends React.Component<IColumnsPageProps, IGrid
export default connect(
mapStateToProps,
mapDispatchToProps
)(ColumnDefinitionContainer);
)(ColumnDefinitionContainerNative);

View File

@ -25,7 +25,7 @@ export class GridColumn {
public formatter: string = "",
public editor?: string) { }
}
interface IListViewPageProps extends React.Props<any> {
export interface IListViewPageProps extends React.Props<any> {
lists: Array<ListDefinition>;
columnRefs: Array<ColumnDefinition>;
sites: Array<Site>;
@ -36,6 +36,7 @@ interface IListViewPageProps extends React.Props<any> {
getWebs: (siteUrl) => Promise<any>;
getListsForWeb: (webUrl) => Promise<any>;
getFieldsForList: (webUrl, listId) => Promise<any>;
save: () => void;
pageContext: PageContext;
}
function mapStateToProps(state) {
@ -75,13 +76,13 @@ function mapDispatchToProps(dispatch) {
},
};
}
interface IGridProps {
export interface IGridProps {
editing: {
entityid: string;
columnid: string;
};
}
class ListDefinitionContainer extends React.Component<IListViewPageProps, IGridProps> {
export class ListDefinitionContainerNative extends React.Component<IListViewPageProps, IGridProps> {
public defaultColumns: Array<GridColumn> = [
{
id: "rowGuid",
@ -208,7 +209,7 @@ class ListDefinitionContainer extends React.Component<IListViewPageProps, IGridP
else {
this.updateExtendedColumn(entity, columnid, value);
}
this.props.saveList(entity);
// this.props.saveList(entity);
}
public addList(event): any {
@ -322,6 +323,7 @@ class ListDefinitionContainer extends React.Component<IListViewPageProps, IGridP
switch (column.editor) {
case "WebEditor":
let webs = this.getWebsForSite(entity);
return (<WebEditor webs={webs} selectedValue={columnValue} onChange={cellUpdated} />);
case "ListEditor":
@ -387,7 +389,7 @@ class ListDefinitionContainer extends React.Component<IListViewPageProps, IGridP
}
public TableRow(props: { entity: ListDefinition, columns: Array<GridColumn>, cellUpdated: (newValue) => void, cellUpdatedEvent: (event: React.SyntheticEvent) => void; }): JSX.Element {
const {entity, columns, cellUpdated, cellUpdatedEvent} = props;
debugger;
return (
<tr>
{
@ -422,12 +424,10 @@ class ListDefinitionContainer extends React.Component<IListViewPageProps, IGridP
}
public render() {
debugger;
return (
<Container testid="columns" size={2} center>
<h1>List Definitions</h1>
<CommandBar items={[{
<CommandBar items={[{
key: "Add LIST",
name: "Add a List",
icon: "Add",
@ -446,6 +446,13 @@ debugger;
isChecked: true,
icon: "ClearFilter"
},
{
key: "save",
name: "save",
canCheck: true,
icon: "Save",
onClick: this.props.save
}]} />
<table border="1">
@ -469,4 +476,4 @@ debugger;
export default connect(
mapStateToProps,
mapDispatchToProps
)(ListDefinitionContainer);
)(ListDefinitionContainerNative);

View File

@ -167,6 +167,14 @@ class ListItemContainer extends React.Component<IListViewPageProps, IGridState>
this.props.getListItems(this.props.listDefinitions);
}
public componentWillReceiveProps(newProps: IListViewPageProps) {
if (newProps.listDefinitions === this.props.listDefinitions && newProps.columns === this.props.columns) {
return;
}
this.props.getListItems(this.props.listDefinitions);
}
/**
* Method to get the parent TD of any cell,
* The listItemId and columnID are stored as attributes of the cells parent TD.
@ -323,25 +331,26 @@ class ListItemContainer extends React.Component<IListViewPageProps, IGridState>
const {entityid, columnid} = this.state.editing;
const entity: ListItem = this.props.listItems.find((temp) => temp.GUID === entityid);
const listDef = this.getListDefinition(entity.__metadata__ListDefinitionId);
const titlecolumnid = this.props.columns.find(c => { return c.type === "__LISTDEFINITIONTITLE__"; }).guid;
if (columnid === titlecolumnid) { // user just changed the listDef,
if (entity.__metadata__GridRowStatus === GridRowStatus.pristine) {
if (!entity.__metadata__OriginalValues) { //SAVE orgininal values so we can undo;
entity.__metadata__OriginalValues = _.cloneDeep(entity); // need deep if we have lookup values
const titleColumn = this.props.columns.find(c => { return c.type === "__LISTDEFINITIONTITLE__"; });
if (titleColumn) {
if (columnid === titleColumn.guid) { // user just changed the listDef,
if (entity.__metadata__GridRowStatus === GridRowStatus.pristine) {
if (!entity.__metadata__OriginalValues) { //SAVE orgininal values so we can undo;
entity.__metadata__OriginalValues = _.cloneDeep(entity); // need deep if we have lookup values
}
}
}
entity.__metadata__ListDefinitionId = value.key; // value is a DropDDownOptions
if (entity.__metadata__GridRowStatus !== GridRowStatus.new) {
const newListDef = this.getListDefinition(value.key);
this.props.getSiteUsersAction(newListDef.siteUrl).then(r => {
this.mapOldListFieldsToNewListFields(entity);
entity.__metadata__ListDefinitionId = value.key; // value is a DropDDownOptions
if (entity.__metadata__GridRowStatus !== GridRowStatus.new) {
const newListDef = this.getListDefinition(value.key);
this.props.getSiteUsersAction(newListDef.siteUrl).then(r => {
this.mapOldListFieldsToNewListFields(entity);
this.props.saveListItem(entity);
});
} else {
this.props.saveListItem(entity);
});
} else {
this.props.saveListItem(entity);
}
return;
}
return;
}
const columnReference = listDef.columnReferences.find(cr => cr.columnDefinitionId === columnid);
const internalName = utils.ParseSPField(columnReference.name).id;
@ -363,8 +372,16 @@ class ListItemContainer extends React.Component<IListViewPageProps, IGridState>
entity[internalName] = value.text;
break;
case "DateTime":
entity[internalName] = (value.getFullYear().toString()) + "-" + (value.getMonth() + 1).toString() + "-" + value.getDate().toString() + "T00:00:00Z";
let year = value.getFullYear().toString();
let month = (value.getMonth() + 1).toString();
let day = value.getDate().toString();
if (month.length === 1) {
month = "0" + month;
}
if (day.length === 1) {
day = "0" + day;
}
entity[internalName] = year + "-" + month + "-" + day + "T00:00:00Z";
break;
case "Lookup":
if (!entity[internalName]) {// if value was not previously set , then this is undefined//
@ -583,7 +600,7 @@ class ListItemContainer extends React.Component<IListViewPageProps, IGridState>
goToToday: "yes"
};
let date = null;
if (columnValue !== null) {
if (columnValue) {
const year = parseInt(columnValue.substring(0, 34));
const month = parseInt(columnValue.substring(5, 7)) - 1;
const day = parseInt(columnValue.substring(8, 10));

View File

@ -1,761 +0,0 @@
// import * as utils from "../utils/utils";
// import * as React from "react";
// const connect = require("react-redux").connect;
// import {
// addListItem, removeListItem, getListItemsAction, saveListItemAction,
// undoListItemChangesAction, updateListItemAction,
// } from "../actions/listItemActions";
// import { getLookupOptionAction } from "../actions/lookupOptionsActions";
// import { getSiteUsersAction } from "../actions/siteUsersActions";
// import ListItem from "../model/ListItem";
// import ColumnDefinition from "../model/ColumnDefinition";
// import { LookupOptions, LookupOptionStatus } from "../model/LookupOptions";
// import { SiteUsers, SiteUsersStatus } from "../model/SiteUsers";
// import GridRowStatus from "../model/GridRowStatus";
// import ListDefinition from "../model/ListDefinition";
// import { FocusZone,Button, ButtonType, TextField, IDropdownOption, Dropdown, Spinner, SpinnerType, DetailsList, IColumn } from "office-ui-fabric-react";
// import { CommandBar } from "office-ui-fabric-react/lib/CommandBar";
// import { DatePicker, IDatePickerStrings } from "office-ui-fabric-react/lib/DatePicker";
// import Container from "../components/container";
// import { Log } from "@microsoft/sp-client-base";
// interface SPColumn extends IColumn {
// editable: boolean;
// type: string;
// }
// interface IListViewPageProps extends React.Props<any> {
// /** An array of ListItems fetched from sharepoint */
// siteUsers: Array<SiteUsers>;
// /** An array of ListItems fetched from sharepoint */
// listItems: Array<ListItem>;
// /** An array of LookupOptions. One for each Lookup Column */
// lookupOptions: Array<LookupOptions>;
// /** An array of columns to be displayed on the grid */
// columns: Array<ColumnDefinition>;
// /** The listDefinitions. Says which lists to pull data from */
// listDefinitions: Array<ListDefinition>;
// /** Redux Action to add a new listitem */
// addListItem: (ListItem) => void;
// /** Redux Action to add a new remove a list item */
// removeListItem: (l: ListItem, ListDef: ListDefinition) => void;
// /** Redux Action to get listitems from a specific list */
// getListItems: (listDefinitions: Array<ListDefinition>) => void;
// /** Redux Action to update a listitem in sharepoint */
// updateListItem: (ListItem: ListItem, ListDef: ListDefinition) => Promise<any>;
// /** Redux Action to get the lookup options for a specific field */
// getLookupOptionAction: (lookupSite, lookupWebId, lookupListId, lookupField) => void;
// /** Redux Action to get the lookup options for a specific field */
// getSiteUsersAction: (site) => Promise<any>;
// /** Redux Action to undo changes made to the listitem */
// undoItemChanges: (ListItem) => void;
// /** Redux Action to save the listitem in the store (NOT to sharepoint*/
// saveListItem: (ListItem) => void;
// }
// function mapStateToProps(state) {
// return {
// listItems: state.items,
// columns: state.columns,
// listDefinitions: state.lists,
// systemStatus: state.systemStatus,
// lookupOptions: state.lookupOptions,
// siteUsers: state.siteUsers
// };
// }
// export class GridColumn {
// constructor(
// public id: string,
// public name: string,
// public editable: boolean,
// public width: number,
// public formatter: string = "",
// public editor?: string) { }
// }
// function mapDispatchToProps(dispatch) {
// return {
// addListItem: (listItem: ListItem): void => {
// dispatch(addListItem(listItem));
// },
// removeListItem: (listItem: ListItem, listDef: ListDefinition): void => {
// dispatch(removeListItem(dispatch, listItem, listDef));
// },
// updateListItem: (listItem: ListItem, listDef: ListDefinition): Promise<any> => {
// const action = updateListItemAction(dispatch, listDef, listItem);
// dispatch(action); // need to ewname this one to be digfferent from the omported ome
// return action.payload.promise;
// },
// saveListItem: (listItem: ListItem): void => {
// dispatch(saveListItemAction(listItem));
// },
// undoItemChanges: (listItem: ListItem): void => {
// dispatch(undoListItemChangesAction(listItem));
// },
// getListItems: (listDefinitions: Array<ListDefinition>): void => {
// dispatch(getListItemsAction(dispatch, listDefinitions));
// },
// getLookupOptionAction: (lookupSite, lookupWebId, lookupListId, lookupField): void => {
// dispatch(getLookupOptionAction(dispatch, lookupSite, lookupWebId, lookupListId, lookupField));
// },
// getSiteUsersAction: (site): void => {
// const action = getSiteUsersAction(dispatch, site);
// dispatch(action);
// return action.payload.promise;
// },
// };
// }
// /**
// *
// */
// interface IGridState {
// editing: {
// /**The Sharepoint GUID of the listitem being edited */
// entityid: string;
// /**The id of the column being edited */
// columnid: string;
// };
// }
// /**
// * This component is the Grid for editing listitems.
// */
// class ListItemContainerUIF extends React.Component<IListViewPageProps, IGridState> {
// public constructor() {
// super();
// this.CellContentsEditable = this.CellContentsEditable.bind(this);
// this.CellContents = this.CellContents.bind(this);
// this.TableDetail = this.TableDetail.bind(this);
// this.toggleEditing = this.toggleEditing.bind(this);
// this.addListItem = this.addListItem.bind(this);
// this.removeListItem = this.removeListItem.bind(this);
// this.handleCellUpdated = this.handleCellUpdated.bind(this);
// this.handleCellUpdatedEvent = this.handleCellUpdatedEvent.bind(this);
// this.undoItemChanges = this.undoItemChanges.bind(this);
// this.updateListItem = this.updateListItem.bind(this);
// this.getLookupOptions = this.getLookupOptions.bind(this);
// this.onRenderItemColumn = this.onRenderItemColumn.bind(this);
// }
// private addListItem(): void {
// let listItem = new ListItem();
// for (const column of this.props.columns) {
// listItem[column.name] === null;
// }
// if (this.props.listDefinitions.length === 1) {
// listItem.__metadata__ListDefinitionId = this.props.listDefinitions[0].guid;
// } else {
// listItem.__metadata__ListDefinitionId = null;
// }
// this.props.addListItem(listItem);
// }
// private removeListItem(event): void {
// const parentTD = this.getParent(event.target, "DIV", "spCell");
// const attributes: NamedNodeMap = parentTD.attributes;
// const entityid = attributes.getNamedItem("data-entityid").value; // theid of the SPListItem
// const listItem: ListItem = this.props.listItems.find((temp) => temp.GUID === entityid); // the listItemItself
// const listDef = this.getListDefinition(listItem.__metadata__ListDefinitionId);// The list Definition this item is associated with.
// this.props.removeListItem(listItem, listDef);
// }
// /**
// * When the component Mounts, call an action to get the listitems for all the listdefinitions
// */
// public componentWillMount() {
// this.props.getListItems(this.props.listDefinitions);
// }
// /**
// * Method to get the parent span of any cell whose classnAME IS spcell
// * The listItemId and columnID are stored as attributes of THAT SPAN
// */
// public getParent(node: Node, type: string, className: string): Node {
// while (node.nodeName !== type && node["className"] !== className) {
// node = node.parentNode;
// }
// return node;
// }
// /**
// * This event gets fired whenever a cell on the grid recieves focus.
// * The "editing" propery of this component determines which cell is being edited.
// * This method gets the clicked on (the entityid-- the id of the SPLIstItem) and the columnId (the id of the ColumnDefinition)
// * and sets them in the "editing"property of state.
// * When Component then redraws, it draws that cell as an editable (See the TableDetail method).
// *
// * If the rendering of that column in edit mode requires additional Info, dispatch a redux action to get the data.
// * (Dispatching the action from within the render mehod itself cused infinite loop)
// *
// */
// public toggleEditing(event) {
// Log.verbose("list-Page", "focus event fired editing when entering cell");
// const target = this.getParent(event.target, "DIV", "spCell"); // walk up the Dom to the TD, thats where the IDs are stored
// const attributes: NamedNodeMap = target.attributes;
// const entityid = attributes.getNamedItem("data-entityid").value;
// const columnid = attributes.getNamedItem("data-columnid").value;
// if (columnid != "") { //user clicked on a column, not a button( Buttons are in a td with am column id of ""
// /**
// * Need to fire events here to get data needed for the rerender
// */
// const listitem = this.props.listItems.find(li => li.GUID === entityid);
// const listDef = this.getListDefinition(listitem.__metadata__ListDefinitionId);
// if (listDef) {// if user just added an item we may not hava a lisdef yest
// const colref = listDef.columnReferences.find(cr => cr.columnDefinitionId === columnid);
// if (colref) {// Listname does not have a columnReference
// switch (colref.fieldDefinition.TypeAsString) {
// case "Lookup":
// let lookupField = colref.fieldDefinition.LookupField;
// let lookupListId = colref.fieldDefinition.LookupList;
// let lookupWebId = colref.fieldDefinition.LookupWebId;
// /**
// * We are assuming here that the lookup listy is in the same web.
// *
// */
// lookupWebId = utils.ParseSPField(listDef.webLookup).id; // temp fix. Need to use graph to get the web by id in the site
// let lookupSite = listDef.siteUrl;
// this.ensureLookupOptions(lookupSite, lookupWebId, lookupListId, lookupField);
// break;
// case "User":
// lookupWebId = utils.ParseSPField(listDef.webLookup).id; // temp fix. Need to use graph to get the web by id in the site
// let site = listDef.siteUrl;
// this.ensureSiteUsers(site);
// break;
// default:
// break;
// }
// }
// }
// }
// this.setState({ "editing": { entityid: entityid, columnid: columnid } });
// }
// /**
// * This event gets fired to revert any changes made to the ListItem.
// */
// public undoItemChanges(event): void {
// const parentTD = this.getParent(event.target, "DIV", "spCell"); // the listitemId and the column ID are always stored as attributes of the parent TD.
// const attributes: NamedNodeMap = parentTD.attributes;
// const entityitem = attributes.getNamedItem("data-entityid");
// const entityid = entityitem.value;
// const entity: ListItem = this.props.listItems.find((temp) => temp.GUID === entityid);
// this.props.undoItemChanges(entity);
// }
// /**
// * This event gets fired, to save the item back to SharePoint.
// */
// public updateListItem(event): void {
// const parentTD = this.getParent(event.target, "DIV", "spCell");
// const attributes: NamedNodeMap = parentTD.attributes;
// const entityid = attributes.getNamedItem("data-entityid").value; // theid of the SPListItem
// const entity: ListItem = this.props.listItems.find((temp) => temp.GUID === entityid);
// const listDef: ListDefinition = this.getListDefinition(entity.__metadata__ListDefinitionId);
// if (entity.__metadata__ListDefinitionId === entity.__metadata__OriginalValues.__metadata__ListDefinitionId
// || entity.__metadata__GridRowStatus === GridRowStatus.new) {// List not changed
// this.props.updateListItem(entity, listDef);
// }
// else {// list changed, add to new, delete from old (will need to do some fiorld mapping in here
// entity.__metadata__GridRowStatus = GridRowStatus.new;
// this.props.updateListItem(entity, listDef).then(response => {
// const oldListDef: ListDefinition = this.getListDefinition(entity.__metadata__OriginalValues.__metadata__ListDefinitionId);
// this.props.removeListItem(entity.__metadata__OriginalValues, oldListDef);
// });
// }
// }
// /**
// * This method gets called when react events are used to update a cell in the grid.
// * It just gets the value and passes it to handleCellUpdated.
// */
// private handleCellUpdatedEvent(event) { //native react uses a Synthetic event
// this.handleCellUpdated(event.target.value);
// }
// /**
// * This method gets called when user changes the listdefinition for an item.
// * the old fields are moved to the corresponing new fields and translated as needed
// */
// private mapOldListFieldsToNewListFields(listItem: ListItem) {
// const newListDef = this.getListDefinition(listItem.__metadata__ListDefinitionId);
// const oldListDef = this.getListDefinition(listItem.__metadata__OriginalValues.__metadata__ListDefinitionId);
// for (const newColRef of newListDef.columnReferences) {
// // find the old columnReference
// const oldColRef = oldListDef.columnReferences.find(cr => cr.columnDefinitionId === newColRef.columnDefinitionId);
// const newFieldName = utils.ParseSPField(newColRef.name).id;
// const oldFieldName = utils.ParseSPField(oldColRef.name).id;
// switch (newColRef.fieldDefinition.TypeAsString) {
// case "User":
// // should male a local copy befor i start messing with these.// fieldd names may overlap on old and new
// // const name = listItem.__metadata__OriginalValues[oldFieldName].Name;// the user login name
// const name = listItem[oldFieldName].Name;// the user login name
// const siteUsersOnNewSite = this.props.siteUsers.find(su => su.siteUrl === newListDef.siteUrl);
// const newUser = siteUsersOnNewSite.siteUser.find(user => user.loginName === name);
// listItem[newFieldName].Id = newUser.id;
// listItem[newFieldName].Name = newUser.loginName;
// listItem[newFieldName].Title = newUser.value;
// break;
// default:
// listItem[newFieldName] = listItem[oldFieldName];
// }
// }
// }
// /**
// * This method gets called when react cells in the gid get updated.
// * Office UI Fabric does not use events. It just calls this method with the new value.
// * It reformats the data to fit the format we recievbed from SP in the first place ,
// * and dispatches an action to save the data in the store.
// *
// * Also, it saves the original version of the record, so we can undo later.
// */
// private handleCellUpdated(value) {
// const {entityid, columnid} = this.state.editing;
// const entity: ListItem = this.props.listItems.find((temp) => temp.GUID === entityid);
// const listDef = this.getListDefinition(entity.__metadata__ListDefinitionId);
// const titlecolumnid = this.props.columns.find(c => { return c.type === "__LISTDEFINITIONTITLE__" }).guid
// if (columnid === titlecolumnid) { // user just changed the listDef,
// if (entity.__metadata__GridRowStatus === GridRowStatus.pristine) {
// if (!entity.__metadata__OriginalValues) { //SAVE orgininal values so we can undo;
// entity.__metadata__OriginalValues = _.cloneDeep(entity); // need deep if we have lookup values
// }
// }
// entity.__metadata__ListDefinitionId = value.key; // value is a DropDDownOptions
// if (entity.__metadata__GridRowStatus !== GridRowStatus.new) {
// const listDef = this.getListDefinition(value.key);
// this.props.getSiteUsersAction(listDef.siteUrl).then(r => {
// this.mapOldListFieldsToNewListFields(entity);
// this.props.saveListItem(entity);
// });
// } else {
// this.props.saveListItem(entity);
// }
// return;
// }
// const columnReference = listDef.columnReferences.find(cr => cr.columnDefinitionId === columnid);
// const internalName = utils.ParseSPField(columnReference.name).id;
// if (!entity.__metadata__OriginalValues) { //SAVE orgininal values so we can undo;
// entity.__metadata__OriginalValues = _.cloneDeep(entity); // need deep if we have lookup values
// }
// if (entity.__metadata__GridRowStatus === GridRowStatus.pristine) {
// entity.__metadata__GridRowStatus = GridRowStatus.modified;
// }
// switch (columnReference.fieldDefinition.TypeAsString) {
// case "User":
// if (!entity[internalName]) {// if value was not previously set , then this is undefined//
// entity[internalName] = {};// set new value to an empty objecte
// }
// entity[internalName].Id = value.key;//and then fill in the values
// entity[internalName].Title = value.text;
// break;
// case "DateTime":
// const year = value.getFullYear().toString();
// entity[internalName] = (value.getFullYear().toString()) + "-" + (value.getMonth() + 1).toString() + "-" + value.getDate().toString() + "T00:00:00Z";
// break;
// case "Lookup":
// if (!entity[internalName]) {// if value was not previously set , then this is undefined//
// entity[internalName] = {};// set new value to an empty objecte
// }
// entity[internalName]["Id"] = value.key;//and then fill in the values
// entity[internalName][columnReference.fieldDefinition.LookupField] = value.text;
// break;
// default:
// entity[internalName] = value;
// }
// this.props.saveListItem(entity);
// }
// /**
// * If the the options for a lookup list are not in the cache, fire an event to get them
// * This method is called when a lookup column receives focus.
// */
// public ensureSiteUsers(siteUrl: string): SiteUsers {
// // see if the options are in the store, if so, return them, otherwoise dispatch an action to get them
// const siteUsers = this.props.siteUsers.find(x => {
// return (x.siteUrl === siteUrl);
// });
// if (siteUsers === undefined) {
// this.props.getSiteUsersAction(siteUrl);
// }
// return siteUsers;
// }
// /**
// * Gets the options to display for a lookupField
// * This method is called when a lookup column gets rendered... we fire the event to get the data when its focused,
// * then we use the data when it gets renderd
// */
// public getSiteUsers(siteUrl: string): SiteUsers {
// // see if the options are in the store, if so, return them, otherwoise dispatch an action to get them
// const siteUsers = this.props.siteUsers.find(x => {
// return (x.siteUrl === siteUrl);
// });
// return siteUsers;
// }
// /**
// * If the the options for a lookup list are not in the cache, fire an event to get them
// * This method is called when a lookup column receives focus.
// */
// public ensureLookupOptions(lookupSite: string, lookupWebId: string, lookupListId: string, lookupField: string): LookupOptions {
// // see if the options are in the store, if so, return them, otherwoise dispatch an action to get them
// const lookupoptions = this.props.lookupOptions.find(x => {
// return (x.lookupField === lookupField) &&
// (x.lookupListId === lookupListId) &&
// (x.lookupSite === lookupSite) &&
// (x.lookupWebId === lookupWebId);
// });
// if (lookupoptions === undefined) {
// this.props.getLookupOptionAction(lookupSite, lookupWebId, lookupListId, lookupField);
// }
// return lookupoptions;
// }
// /**
// * Gets the options to display for a lookupField
// * This method is called when a lookup column gets rendered... we fire the event to get the data when its focused,
// * then we use the data when it gets renderd
// */
// public getLookupOptions(lookupSite: string, lookupWebId: string, lookupListId: string, lookupField: string): LookupOptions {
// // see if the options are in the store, if so, return them, otherwoise dispatch an action to get them
// let lookupoptions = this.props.lookupOptions.find(x => {
// return (x.lookupField === lookupField) &&
// (x.lookupListId === lookupListId) &&
// (x.lookupSite === lookupSite) &&
// (x.lookupWebId === lookupWebId);
// });
// return lookupoptions;
// }
// /**
// * Returns the ListDefinition for the given ListDefinionId
// *
// */
// public getListDefinition(
// /** The id of the list definition to be retrieved */
// listdefid: string
// ): ListDefinition {
// return this.props.listDefinitions.find(ld => ld.guid === listdefid);
// }
// /**
// * This method renders the contents of an individual cell in an editable format.
// */
// public CellContentsEditable(props: { entity: ListItem, column: SPColumn, cellUpdated: (newValue) => void, cellUpdatedEvent: (event: React.SyntheticEvent) => void; }): JSX.Element {
// const {entity, column, cellUpdated, cellUpdatedEvent} = props;
// if (column.type === "__LISTDEFINITIONTITLE__") {
// entity.__metadata__ListDefinitionId
// const opts: Array<IDropdownOption> = this.props.listDefinitions.map(ld => {
// return { key: ld.guid, text: ld.title };
// });
// // if (!entity.__metadata__ListDefinitionId) {
// // opts.unshift({ key: null, text: "Select one" });
// // }
// // should I have a different handler for this?
// return (
// <Dropdown options={opts} selectedKey={entity.__metadata__ListDefinitionId} label=""
// onChanged={(selection: IDropdownOption) => { cellUpdated(selection); } } />
// );
// }
// const listDef = this.getListDefinition(entity.__metadata__ListDefinitionId);
// const colref = listDef.columnReferences.find(cr => cr.columnDefinitionId === column.key);
// const internalName = utils.ParseSPField(colref.name).id;
// const columnValue = entity[internalName];
// switch (colref.fieldDefinition.TypeAsString) {
// case "User":
// let siteUrl = listDef.siteUrl;
// let siteUsers = this.getSiteUsers(siteUrl);
// if (siteUsers) {
// switch (siteUsers.status) {
// case SiteUsersStatus.fetched:
// let options: IDropdownOption[] = siteUsers.siteUser.map((opt, index, options) => {
// return { key: opt.id, text: opt.value };
// });
// const selectedKey = columnValue ? columnValue.Id : null;
// return (
// <Dropdown label="" options={options} selectedKey={selectedKey} onChanged={(selection: IDropdownOption) => { cellUpdated(selection); } } >
// </Dropdown >
// );
// case SiteUsersStatus.fetching:
// return (
// <Spinner type={SpinnerType.normal} />
// );
// case SiteUsersStatus.error:
// return (
// <Spinner label="Error" type={SpinnerType.normal} />
// );
// default:
// return (
// <Spinner type={SpinnerType.normal} />
// );
// }
// } else {
// return (
// <Spinner type={SpinnerType.normal} />
// );
// }
// case "Lookup":
// let lookupField = colref.fieldDefinition.LookupField;
// let lookupListId = colref.fieldDefinition.LookupList;
// let lookupWebId = colref.fieldDefinition.LookupWebId;
// /**
// * We are assuming here that the lookup listy is in the same web.
// *
// */
// lookupWebId = utils.ParseSPField(listDef.webLookup).id; // temp fix. Need to use graph to get the web by id in the site
// let lookupSite = listDef.siteUrl;
// let lookupOptions = this.getLookupOptions(lookupSite, lookupWebId, lookupListId, lookupField);
// if (lookupOptions) {
// switch (lookupOptions.status) {
// case LookupOptionStatus.fetched:
// let options: IDropdownOption[] = lookupOptions.lookupOption.map((opt, index, options) => {
// return { key: opt.id, text: opt.value };
// });
// return (
// <Dropdown label="" options={options} selectedKey={(columnValue ? columnValue.Id : null)} onChanged={(selection: IDropdownOption) => { cellUpdated(selection); } } >
// </Dropdown >
// );
// case LookupOptionStatus.fetching:
// return (
// <Spinner type={SpinnerType.normal} />
// );
// case LookupOptionStatus.error:
// return (
// <Spinner label="Error" type={SpinnerType.normal} />
// );
// default:
// return (
// <Spinner type={SpinnerType.normal} />
// );
// }
// } else {
// return (
// <Spinner type={SpinnerType.normal} />
// );
// }
// case "Choice":
// const choices = colref.fieldDefinition.Choices.map((c, i) => {
// let opt: IDropdownOption = {
// index: i,
// key: i,
// text: c,
// isSelected: (c === columnValue)
// };
// return opt;
// });
// return (
// <Dropdown label="" selectedKey={entity[columnValue]} options={choices} onChanged={(selection: IDropdownOption) => cellUpdated(selection)} >
// </Dropdown >
// );
// case "Text":
// return (
// <input autoFocus type="text"
// value={columnValue}
// onChange={cellUpdatedEvent} />);
// case "Note":
// return (
// <TextField autoFocus
// value={columnValue}
// onChanged={cellUpdated} />);
// case "DateTime":
// const datpickerStrings: IDatePickerStrings = {
// "months": [""],
// "shortMonths": [""],
// "days": [""],
// "shortDays": [""],
// goToToday: "yes"
// };
// const year = parseInt(columnValue.substring(0, 34));
// const month = parseInt(columnValue.substring(5, 7)) - 1;
// const day = parseInt(columnValue.substring(8, 10));
// const date = new Date(year, month, day);
// return (
// <DatePicker strings={datpickerStrings} onSelectDate={cellUpdated} value={date}
// allowTextInput={true} isRequired={colref.fieldDefinition.Required}
// />);
// default:
// return (
// <input autoFocus type="text"
// value={columnValue}
// onChange={cellUpdatedEvent} />);
// }
// }
// /**
// * This method renders the contents of an individual cell in a non-editable format.
// */
// public CellContents(props: { entity: ListItem, column: SPColumn }): JSX.Element {
// const {entity, column} = props;
// if (!entity.__metadata__ListDefinitionId) { // item is new and list not yet selected
// }
// const listDef = this.getListDefinition(entity.__metadata__ListDefinitionId);
// if (column.type === "__LISTDEFINITIONTITLE__") {// this type is sued to show the listdefinition name
// if (listDef != null) {//listdef has been selected
// return (<a href="#" onFocus={this.toggleEditing} style={{ textDecoration: "none" }} >
// {listDef.title}
// </a>);
// }
// else {//listdef not yet selected
// return (<a href="#" onFocus={this.toggleEditing} style={{ textDecoration: "none" }} >
// Select a list
// </a>);
// }
// }
// if (!listDef) { // cant edit columns til we select a listdef, not NO onFocus={this.toggleEditing}
// return (<a href="#" style={{ textDecoration: "none" }} >
// select a list first
// </a>
// );
// }
// const colref = listDef.columnReferences.find(cr => cr.columnDefinitionId === column.key);
// if (colref === undefined) { //Column has not been configured for this list
// return (<a href="#" onFocus={this.toggleEditing} style={{ textDecoration: "none" }} >
// 'Column Not Defined'
// </a>
// );
// }
// const internalName = utils.ParseSPField(colref.name).id;
// switch (colref.fieldDefinition.TypeAsString) {
// case "User":
// if (entity[internalName] === undefined) { // value not set
// return (<a href="#" onFocus={this.toggleEditing} style={{ textDecoration: "none" }} >
// </a>
// );
// } else {
// return (<a href="#" onFocus={this.toggleEditing} style={{ textDecoration: "none" }} >
// {entity[internalName]["Title"]}
// </a>
// );
// }
// case "Lookup":
// if (entity[internalName] === undefined) { // value not set
// return (<a href="#" onFocus={this.toggleEditing} style={{ textDecoration: "none" }} >
// </a>
// );
// } else {
// return (<a href="#" onFocus={this.toggleEditing} style={{ textDecoration: "none" }} >
// {entity[internalName][colref.fieldDefinition.LookupField]}
// </a>
// );
// }
// case "Text":
// return (<a href="#" onFocus={this.toggleEditing} style={{ textDecoration: "none" }} >
// {entity[internalName]}
// </a>
// );
// case "Note":
// return (<a href="#" onFocus={this.toggleEditing} style={{ textDecoration: "none" }} dangerouslySetInnerHTML={{ __html: entity[internalName] }} >
// </a>
// );
// case "DateTime":
// let value: string;
// if (entity[internalName] === null) {
// return (<a href="#" onFocus={this.toggleEditing} style={{ textDecoration: "none" }} >
// </a>);
// }
// if (colref.fieldDefinition.EntityPropertyName === "DateOnly") {
// value = entity[internalName].split("T")[0];
// }
// else {
// value = entity[internalName];
// }
// return (<a href="#" onFocus={this.toggleEditing} style={{ textDecoration: "none" }} >
// {value}
// </a>
// );
// default:
// return (<a href="#" onFocus={this.toggleEditing} style={{ textDecoration: "none" }} >
// {entity[internalName]}
// </a>
// );
// }
// }
// /**
// * This method renders the A TD for an individual Cell. The TD contains the listItemID and the ColumnID as attributes.
// * It calls CellContentsEditable or CellContents based on whether the cell is being edited.
// * It determines if the cell is being edited by looking at this,props.editing(which got set by ToggleEditing).
// */
// public TableDetail(props: { entity: ListItem, column: SPColumn, cellUpdated: (newValue) => void, cellUpdatedEvent: (event: React.SyntheticEvent) => void; }): JSX.Element {
// const {entity, column, cellUpdated, cellUpdatedEvent} = props;
// if (this.state && this.state.editing && this.state.editing.entityid === entity.GUID && this.state.editing.columnid === column.key) {
// return (<div width={column.minWidth} className="spCell" key={entity.GUID + column.key} data-entityid={entity.GUID} data-columnid={column.key} style={{ border: "2px solid black", padding: "0px" }}>
// <this.CellContentsEditable entity={entity} column={column} cellUpdated={this.handleCellUpdated} cellUpdatedEvent={this.handleCellUpdatedEvent} />
// </div>
// );
// } else {
// return (<div width={column.minWidth} className="spCell" key={entity.GUID + column.key} data-entityid={entity.GUID} data-columnid={column.key} style={{ border: "1px solid black", padding: "0px" }} onClick={this.toggleEditing} >
// <this.CellContents entity={entity} column={column} />
// </div>
// );
// }
// }
// public onRenderItemColumn(item: any, index: number, column: SPColumn): any {
// const listDef = this.getListDefinition(item.__metadata__ListDefinitionId);
// const colRef = listDef.columnReferences.find(cr => cr.columnDefinitionId === column.key);
// const internalName = utils.ParseSPField(colRef.name).id;
// return (
// <FocusZone>
// <this.TableDetail entity={item} column={column} cellUpdated={this.handleCellUpdated} cellUpdatedEvent={this.handleCellUpdatedEvent} />
// </FocusZone>
// );
// }
// public render() {
// const { listItems } = this.props;
// Log.info("ListItemContainer", "In Render");
// const uColumns: SPColumn[] = this.props.columns.map((c, i, a) => {
// return {
// key: c.guid,
// name: c.name,
// fieldName: c.name,
// minWidth: 80,
// maxWidth: 80,
// width: 80,
// type: c.type,
// editable: c.editable,
// };
// });
// return (
// <Container testid="columns" size={2} center>
// <DetailsList columns={uColumns} items={listItems} onRenderItemColumn={this.onRenderItemColumn}>
// </DetailsList>
// </Container>
// );
// }
// }
// export default connect(
// mapStateToProps,
// mapDispatchToProps
// )(ListItemContainerUIF);

View File

@ -0,0 +1,73 @@
import ColumnDefinition from "../model/ColumnDefinition";
import * as React from 'react';
import * as ReactDom from 'react-dom';
import {
IPropertyPaneField,
IPropertyPaneFieldType,
IPropertyPaneCustomFieldProps
} from '@microsoft/sp-webpart-base';
import PropertyFieldColumnDefinitionsHost, { IPropertyFieldColumnDefinitionsHostProps } from './PropertyFieldColumnDefinitionsHost';
export interface IPropertyFieldColumnDefinitionsProps {
label: string;
initialValue?: Array<ColumnDefinition>;
onPropertyChange(propertyPath: string, oldValue: any, newValue: any): void;
getColumnDefinitions: () =>Array<ColumnDefinition>;
}
export interface IPropertyFieldColumnDefinitionsPropsInternal extends IPropertyPaneCustomFieldProps {
label: string;
initialValue?: Array<ColumnDefinition>;
targetProperty: string;
onRender(elem: HTMLElement): void;
onDispose(elem: HTMLElement): void;
onPropertyChange(propertyPath: string, oldValue: any, newValue: any): void;
columnDefinitions: Array<ColumnDefinition>;
}
class PropertyFieldColumnDefinitionsBuilder implements IPropertyPaneField<IPropertyFieldColumnDefinitionsPropsInternal> {
//Properties defined by IPropertyPaneField
public type: IPropertyPaneFieldType = 1;//IPropertyPaneFieldType.Custom;
public targetProperty: string;
public properties: IPropertyFieldColumnDefinitionsPropsInternal;
//Custom properties
private label: string;
private onPropertyChange: (propertyPath: string, oldValue: any, newValue: any) => void;
private customProperties: any;
public constructor(_targetProperty: string, _properties: IPropertyFieldColumnDefinitionsPropsInternal) {
this.render = this.render.bind(this);
this.properties = _properties;
this.label = _properties.label;
this.properties.onDispose = this.dispose;
this.properties.onRender = this.render;
this.onPropertyChange = _properties.onPropertyChange;
this.customProperties = _properties.columnDefinitions;
}
private render(elem: HTMLElement): void {
const element: React.ReactElement<IPropertyFieldColumnDefinitionsHostProps> = React.createElement(PropertyFieldColumnDefinitionsHost, {
label: this.label,
onPropertyChange: this.onPropertyChange,
columnDefinitions: this.customProperties,
});
ReactDom.render(element, elem);
}
private dispose(elem: HTMLElement): void {
}
}
export function PropertyFieldColumnDefinitions(targetProperty: string, properties: IPropertyFieldColumnDefinitionsProps): IPropertyPaneField<IPropertyFieldColumnDefinitionsPropsInternal> {
//Create an internal properties object from the given properties
var newProperties: IPropertyFieldColumnDefinitionsPropsInternal = {
label: properties.label,
targetProperty: targetProperty,
initialValue: properties.initialValue,
onPropertyChange: properties.onPropertyChange,
columnDefinitions: properties.getColumnDefinitions(),
onDispose: null,
onRender: null,
};
//Calles the PropertyFieldColumnDefinitions builder object
//This object will simulate a PropertyFieldCustom to manage his rendering process
return new PropertyFieldColumnDefinitionsBuilder(targetProperty, newProperties);
}

View File

@ -0,0 +1,110 @@
import { Guid } from "@microsoft/sp-client-base";
import { ColumnDefinitionContainerNative } from "./ColumnDefinitionContainer";
import ColumnDefinition from "../model/ColumnDefinition";
import * as React from 'react';
import { Label } from 'office-ui-fabric-react/lib/Label';
import { Button } from 'office-ui-fabric-react/lib/Button';
import { Panel, PanelType } from 'office-ui-fabric-react/lib/Panel';
import * as strings from "spfxReactGridStrings";
export interface IPropertyFieldColumnDefinitionsHostProps {
label: string;
initialValue?: Array<ColumnDefinition>;
onPropertyChange(propertyPath: string, oldValue: any, newValue: any): void;
columnDefinitions: Array<ColumnDefinition>;
}
export interface IPropertyFieldColumnDefinitionsHostState {
openPanel?: boolean;
columnDefinitions: Array<ColumnDefinition>;
}
export default class PropertyFieldColumnDefinitionsHost extends React.Component<IPropertyFieldColumnDefinitionsHostProps, IPropertyFieldColumnDefinitionsHostState> {
constructor(props: IPropertyFieldColumnDefinitionsHostProps) {
super(props);
this.onOpenPanel = this.onOpenPanel.bind(this);
this.onClosePanel = this.onClosePanel.bind(this);
this.removeColumn = this.removeColumn.bind(this);
this.addColumn = this.addColumn.bind(this);
this.moveColumnDown = this.moveColumnDown.bind(this);
this.moveColumnUp = this.moveColumnUp.bind(this);
this.saveChanges = this.saveChanges.bind(this);
this.state = {
columnDefinitions: this.props.columnDefinitions,
openPanel: false
};
}
private addColumn(): void {
const id = Guid.newGuid();
const col: ColumnDefinition = new ColumnDefinition(id.toString(), "", 80, true);
this.state.columnDefinitions.push(col);
this.setState(this.state);
}
private removeColumn(column): void {
this.state.columnDefinitions = _.filter(this.state.columnDefinitions, (o) => { return o.guid !== column.guid; });
this.setState(this.state);
}
private removeAllColumns(): void {
this.state.columnDefinitions = [];
this.setState(this.state);
}
private moveColumnUp(column: ColumnDefinition): void {
const index = _.findIndex(this.state.columnDefinitions, (cd) => cd.guid === column.guid);
this.state.columnDefinitions[index] = this.state.columnDefinitions.splice(index - 1, 1, this.state.columnDefinitions[index])[0];
this.setState(this.state);
}
private moveColumnDown(column): void {
const index = _.findIndex(this.state.columnDefinitions, (cd) => cd.guid === column.guid);
this.state.columnDefinitions[index] = this.state.columnDefinitions.splice(index + 1, 1, this.state.columnDefinitions[index])[0];
this.setState(this.state);
}
private saveChanges(): void {
if (this.props.onPropertyChange) {
this.props.onPropertyChange("ColumnDefinitions", this.props.initialValue, this.state.columnDefinitions);
this.onClosePanel();
}
}
private onOpenPanel(element?: any): void {
this.state.openPanel = true;
this.setState(this.state);
}
private onClosePanel(element?: any): void {
this.state.openPanel = false;
this.setState(this.state);
}
public render(): JSX.Element {
//Renders content
return (
<div style={{ marginBottom: '8px' }}>
<Label>{this.props.label}</Label>
<Button onClick={this.onOpenPanel}>{strings.ColumnDefinitionsButtonSelect}</Button>
{this.state.openPanel === true ?
<Panel
isOpen={this.state.openPanel} hasCloseButton={true} onDismiss={this.onClosePanel}
isLightDismiss={true} type={PanelType.large}
headerText={strings.ColumnDefinitionsTitle}>
<ColumnDefinitionContainerNative
columns={this.state.columnDefinitions}
addColumn={this.addColumn}
moveColumnDown={this.moveColumnDown}
moveColumnUp={this.moveColumnUp}
removeAllColumns={this.removeAllColumns}
removeColumn={this.removeColumn}
save={this.saveChanges}
/>
</Panel>
: ''}
</div>
);
}
}

View File

@ -0,0 +1,87 @@
import ListDefinition from "../model/ListDefinition";
import ColumnDefinition from "../model/ColumnDefinition";
import * as React from 'react';
import * as ReactDom from 'react-dom';
import {
IPropertyPaneField,
IPropertyPaneFieldType,
IPropertyPaneCustomFieldProps
} from '@microsoft/sp-webpart-base';
import PropertyFieldListDefinitionsHost, { IPropertyFieldListDefinitionsHostProps } from './PropertyFieldListDefinitionsHost';
import { PageContext } from "@microsoft/sp-client-base";
export interface IPropertyFieldListDefinitionsProps {
label: string;
initialValue?: Array<ListDefinition>;
onPropertyChange(propertyPath: string, oldValue: any, newValue: any): void;
getListDefinitions: () => Array<ListDefinition>;
getColumnDefinitions: () =>Array<ColumnDefinition>;
PageContext: PageContext;
}
export interface IPropertyFieldListDefinitionsPropsInternal extends IPropertyPaneCustomFieldProps {
label: string;
initialValue?: Array<ListDefinition>;
targetProperty: string;
onRender(elem: HTMLElement): void;
onDispose(elem: HTMLElement): void;
onPropertyChange(propertyPath: string, oldValue: any, newValue: any): void;
getListDefinitions: () => Array<ListDefinition>;
getColumnDefinitions: () =>Array<ColumnDefinition>;
PageContext: PageContext;
}
class PropertyFieldListDefinitionsBuilder implements IPropertyPaneField<IPropertyFieldListDefinitionsPropsInternal> {
//Properties defined by IPropertyPaneField
public type: IPropertyPaneFieldType = 1;//IPropertyPaneFieldType.Custom;
public targetProperty: string;
public properties: IPropertyFieldListDefinitionsPropsInternal;
//Custom properties
private label: string;
private onPropertyChange: (propertyPath: string, oldValue: any, newValue: any) => void;
private customProperties: any;
public constructor(_targetProperty: string, _properties: IPropertyFieldListDefinitionsPropsInternal) {
this.render = this.render.bind(this);
this.properties = _properties;
this.label = _properties.label;
this.properties.onDispose = this.dispose;
this.properties.onRender = this.render;
this.onPropertyChange = _properties.onPropertyChange;
}
private render(elem: HTMLElement): void {
const ldProps: IPropertyFieldListDefinitionsHostProps = {
label: this.label,
getColumnDefinitions: this.properties.getColumnDefinitions,
getListDefinitions: this.properties.getListDefinitions,
onPropertyChange: this.onPropertyChange,
PageContext: this.properties.PageContext
};
const element: React.ReactElement<IPropertyFieldListDefinitionsHostProps> = React.createElement(PropertyFieldListDefinitionsHost, ldProps);
ReactDom.render(element, elem);
}
private dispose(elem: HTMLElement): void {
}
}
export function PropertyFieldListDefinitions(targetProperty: string, properties: IPropertyFieldListDefinitionsProps): IPropertyPaneField<IPropertyFieldListDefinitionsPropsInternal> {
//Create an internal properties object from the given properties
var newProperties: IPropertyFieldListDefinitionsPropsInternal = {
label: properties.label,
targetProperty: targetProperty,
initialValue: properties.initialValue,
onPropertyChange: properties.onPropertyChange,
getListDefinitions: properties.getListDefinitions,
getColumnDefinitions: properties.getColumnDefinitions,
PageContext:properties.PageContext,
onDispose: null,
onRender: null,
};
//Calles the PropertyFieldListDefinitions builder object
//This object will simulate a PropertyFieldCustom to manage his rendering process
return new PropertyFieldListDefinitionsBuilder(targetProperty, newProperties);
}

View File

@ -0,0 +1,205 @@
import { ListDefinitionContainerNative } from "./ListDefinitionContainer";
import ListDefinition from "../model/ListDefinition";
import ColumnDefinition from "../model/ColumnDefinition";
import { Site, Web, WebList, WebListField } from "../model/Site";
import * as React from 'react';
import { Label } from 'office-ui-fabric-react/lib/Label';
import { Button } from 'office-ui-fabric-react/lib/Button';
import { Panel, PanelType } from 'office-ui-fabric-react/lib/Panel';
import * as strings from "spfxReactGridStrings";
import * as utils from "../utils/utils";
import { Web as SPWeb } from "sp-pnp-js";
import { Site as SPSite } from "sp-pnp-js";
import { Guid, PageContext } from "@microsoft/sp-client-base";
export interface IPropertyFieldListDefinitionsHostProps {
label: string;
initialValue?: Array<ListDefinition>;
onPropertyChange(propertyPath: string, oldValue: any, newValue: any): void;
getListDefinitions: () => Array<ListDefinition>;
getColumnDefinitions: () => Array<ColumnDefinition>;
PageContext: PageContext;
}
export interface IPropertyFieldListDefinitionsHostState {
openPanel?: boolean;
ListDefinitions: Array<ListDefinition>;
Sites: Array<Site>;
}
export default class PropertyFieldListDefinitionsHost extends React.Component<IPropertyFieldListDefinitionsHostProps, IPropertyFieldListDefinitionsHostState> {
constructor(props: IPropertyFieldListDefinitionsHostProps) {
super(props);
this.onOpenPanel = this.onOpenPanel.bind(this);
this.onClosePanel = this.onClosePanel.bind(this);
this.removeList = this.removeList.bind(this);
this.addList = this.addList.bind(this);
this.moveListUp = this.moveListUp.bind(this);
this.moveListDown = this.moveListDown.bind(this);
this.getWebs = this.getWebs.bind(this);
this.getListsForWeb = this.getListsForWeb.bind(this);
this.getFieldsForList = this.getFieldsForList.bind(this);
this.saveChanges = this.saveChanges.bind(this);
this.state = {
ListDefinitions: this.props.getListDefinitions(),
openPanel: false,
Sites: []
};
}
private getWebs(siteUrl: string): any {
const spSite: SPSite = new SPSite(siteUrl);
const promise = spSite.rootWeb.webs.orderBy("Title").get()
.then((response) => {
const data = _.map(response, (item: any) => {
const web: Web = new Web(item.Id, item.Title, item.Url);
return web;
});
console.log(data);
let site: Site = new Site(siteUrl);
site.webs = data;
this.state.Sites.push(site);
this.setState(this.state);
})
.catch((error) => {
console.log(error);
});
return promise;
}
private getListsForWeb(webUrl: string): any {
const spWeb = new SPWeb(webUrl);
const promise = spWeb.lists.orderBy("Title").get()
.then((response) => {
const data = _.map(response, (item: any) => {
return new WebList(item.Id, item.Title, item.Url, );
});
for (const site of this.state.Sites) {
for (const web of site.webs) {
if (web.url === webUrl) {
web.lists = data;
web.listsFetched = true;
}
}
}
this.setState(this.state);
})
.catch((error) => {
console.log(error);
});
return promise;
}
private getFieldsForList(webUrl: string, listId: string): any {
const spWeb = new SPWeb(webUrl);
const promise = spWeb.lists.getById(listId).fields.filter("Hidden eq false").orderBy("Title").get()
.then((response) => {
const data = _.map(response, (item: any) => {
return new WebListField(item.id, new utils.ParsedSPField(item.InternalName, item.Title).toString(), item);
});
for (const site of this.state.Sites) {
for (const web of site.webs) {
if (web.url === webUrl) {
for (const list of web.lists) {
if (list.id === listId) {
list.fields = data;
list.fieldsFetched = true;
}
}
}
}
}
this.setState(this.state);
})
.catch((error) => {
console.log(error);
});
return promise;
}
private addList(siteUrl): void {
const id = Guid.newGuid();
const list: ListDefinition = new ListDefinition(id.toString(), null, null, siteUrl, null, null);
this.state.ListDefinitions.push(list);
this.setState(this.state);
}
private removeList(list: ListDefinition): void {
this.state.ListDefinitions = _.filter(this.state.ListDefinitions, (o) => { return o.guid !== list.guid; });
this.setState(this.state);
}
private removeAllLists(): void {
this.state.ListDefinitions = [];
this.setState(this.state);
}
private moveListUp(list: ListDefinition): void {
const index = _.findIndex(this.state.ListDefinitions, (cd) => cd.guid === list.guid);
this.state.ListDefinitions[index] = this.state.ListDefinitions.splice(index - 1, 1, this.state.ListDefinitions[index])[0];
this.setState(this.state);
}
private moveListDown(list: ListDefinition): void {
const index = _.findIndex(this.state.ListDefinitions, (cd) => cd.guid === list.guid);
this.state.ListDefinitions[index] = this.state.ListDefinitions.splice(index + 1, 1, this.state.ListDefinitions[index])[0];
this.setState(this.state);
}
private saveChanges(): void {
if (this.props.onPropertyChange) {
this.props.onPropertyChange("ListDefinitions", this.props.initialValue, this.state.ListDefinitions);
this.onClosePanel();
}
}
private onOpenPanel(element?: any): void {
this.state.openPanel = true;
this.setState(this.state);
}
private onClosePanel(element?: any): void {
this.state.openPanel = false;
this.setState(this.state);
}
public render(): JSX.Element {
//Renders content
return (
<div style={{ marginBottom: '8px' }}>
<Label>{this.props.label}</Label>
<Button onClick={this.onOpenPanel}>{strings.ListDefinitionsButtonSelect}</Button>
{this.state.openPanel === true ?
<Panel
isOpen={this.state.openPanel} hasCloseButton={true} onDismiss={this.onClosePanel}
isLightDismiss={true} type={PanelType.large}
headerText={strings.ListDefinitionsTitle}>
<ListDefinitionContainerNative
columnRefs={this.props.getColumnDefinitions()}
lists={this.state.ListDefinitions}
addList={this.addList}
getFieldsForList={this.getFieldsForList}
getListsForWeb={this.getListsForWeb}
getWebs={this.getWebs}
removeAllLists={this.removeAllLists}
removeList={this.removeList}
sites={this.state.Sites}
saveList={this.saveChanges}
save={this.saveChanges}
pageContext={this.props.PageContext}
/>
</Panel>
: ''}
</div>
);
}
}

View File

@ -1,12 +1,10 @@
import * as React from "react";
import {
Button,
MessageBar,
MessageBarType
} from "office-ui-fabric-react";
const connect = require("react-redux").connect;
import SystemStatus from "../model/SystemStatus";
const Link = require("react-router").Link;
import Content from "../components/content";
interface IAppProps extends React.Props<any> {
systemStatus: SystemStatus;
@ -35,9 +33,7 @@ class App extends React.Component<IAppProps, void> {
const { children} = this.props;
return (
<div>
<Button> <Link to="/lists">List Definitions</Link></Button>
<Button> <Link to="/columns">Column Definitions</Link></Button>
<Button> <Link to="/">List Items</Link></Button>
<div>
{this.messageBar(this.props.systemStatus.fetchStatus)}
<div>{this.props.systemStatus.currentAction}

View File

@ -1,7 +1,18 @@
define([], function() {
define([], function () {
return {
"PropertyPaneDescription": "Description",
"BasicGroupName": "Group Name",
"DescriptionFieldLabel": "Description Field"
"DescriptionFieldLabel": "Description Field",
"ColumnDefinitionFieldLabel": "Column Definitions",
"ColumnDefinitionsButtonSelect": "Update Columns",
"ColumnDefinitionsButtonReset": "Clear",
"ColumnDefinitionsTitle": "Column Definitions",
"ListDefinitionFieldLabel": "List Definitions",
"ListDefinitionsButtonSelect": "Update Lists",
"ListDefinitionsButtonReset": "Clear",
"ListDefinitionsTitle": "List Definitions"
}
});

View File

@ -2,8 +2,22 @@ declare interface ISpfxReactGridStrings {
PropertyPaneDescription: string;
BasicGroupName: string;
DescriptionFieldLabel: string;
}
ListDefinitionFieldLabel:string;
/**The Label on the reset CD Button */
ListDefinitionsButtonSelect:string;
/**The Label on the reset Button */
ListDefinitionsButtonReset:string;
/**The Title on the popupPage */
ListDefinitionsTitle:string;
ColumnDefinitionFieldLabel:string;
/**The Label on the reset CD Button */
ColumnDefinitionsButtonSelect:string;
/**The Label on the reset Button */
ColumnDefinitionsButtonReset:string;
/**The Title on the popupPage */
ColumnDefinitionsTitle:string;
}
declare module 'spfxReactGridStrings' {
const strings: ISpfxReactGridStrings;
export = strings;

View File

@ -9,14 +9,14 @@ import {
MOVE_COLUMN_DOWN
} from "../constants";
function moveColumnUp(state: Array<ColumnDefinition>, action) {
export function moveColumnUp(state: Array<ColumnDefinition>, action) {
let newstate = _.clone(state);
const index = _.findIndex<ColumnDefinition>(newstate, c => c.guid === action.payload.column.guid);
newstate[index] = newstate.splice(index - 1, 1, newstate[index])[0];
return newstate;
}
function moveColumnDown(state: Array<ColumnDefinition>, action) {
export function moveColumnDown(state: Array<ColumnDefinition>, action) {
let newstate = _.clone(state);
let index = _.findIndex<ColumnDefinition>(newstate, c => c.guid === action.payload.column.guid);