Add react-file-upload sample (#415)
* Adds SPFx react slide swiper sample (#412) * Add files via upload Add initial files to the repository * Add package-lock.json to react-file-upload it will fix the this error when you run Gulp serve: SPFileUpload-master/node_modules/react-dropzone-component/typescript/types"' has no default export.
This commit is contained in:
parent
c177a89297
commit
a342425d05
|
@ -0,0 +1,61 @@
|
||||||
|
# React File Upload WebPart
|
||||||
|
|
||||||
|
## Summary
|
||||||
|
The file upload web part allowing users to upload multiple files to a document library or as item attachments.
|
||||||
|
|
||||||
|
![File upload web part built on the SharePoint Framework using React](./assets/SPFileUploadPreview.gif)
|
||||||
|
|
||||||
|
## Used SharePoint Framework Version
|
||||||
|
![drop](https://img.shields.io/badge/version-GA-green.svg)
|
||||||
|
|
||||||
|
## Applies to
|
||||||
|
|
||||||
|
* [SharePoint Framework](https:/dev.office.com/sharepoint)
|
||||||
|
* [Office 365 tenant](https://dev.office.com/sharepoint/docs/spfx/set-up-your-development-environment)
|
||||||
|
|
||||||
|
> Update accordingly as needed.
|
||||||
|
|
||||||
|
## Prerequisites
|
||||||
|
|
||||||
|
> Site Collection created under the **/sites/** Managed Path
|
||||||
|
> Existing document library or a list
|
||||||
|
|
||||||
|
## Solution
|
||||||
|
|
||||||
|
Solution|Author(s)
|
||||||
|
--------|---------
|
||||||
|
react-file-upload|Ramin Ahmadi
|
||||||
|
|
||||||
|
## Version history
|
||||||
|
|
||||||
|
Version|Date|Comments
|
||||||
|
-------|----|--------
|
||||||
|
1.0.0|February 14, 2018|Initial release
|
||||||
|
|
||||||
|
## Disclaimer
|
||||||
|
**THIS CODE IS PROVIDED *AS IS* WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING ANY IMPLIED WARRANTIES OF FITNESS FOR A PARTICULAR PURPOSE, MERCHANTABILITY, OR NON-INFRINGEMENT.**
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Minimal Path to Awesome
|
||||||
|
|
||||||
|
- Clone this repository
|
||||||
|
- in the command line run:
|
||||||
|
- `npm install`
|
||||||
|
- `gulp serve`
|
||||||
|
|
||||||
|
## Features
|
||||||
|
This project contains sample client-side web parts built on the SharePoint Framework using React illustrating working with file upload web part.
|
||||||
|
This sample illustrates the following concepts on top of the SharePoint Framework:
|
||||||
|
- using React for building SharePoint Framework client-side web parts
|
||||||
|
- using React components for building file upload web part
|
||||||
|
- using [DropzoneJs](http://www.dropzonejs.com/) for uploading files
|
||||||
|
- uploading files to a document library
|
||||||
|
- uploading files as item attachments by getting the item ID from the query string parameter
|
||||||
|
- uploading files using RestAPI
|
||||||
|
- drag and drop feature for uploading files
|
||||||
|
- ability to remove uploaded files
|
||||||
|
- ability limit users to upload accepted file types
|
||||||
|
- using sp-pnp-js to delete uploaded files
|
||||||
|
|
||||||
|
<img src="https://telemetry.sharepointpnp.com/sp-dev-fx-webparts/samples/readme-template" />
|
Binary file not shown.
After Width: | Height: | Size: 804 KiB |
|
@ -0,0 +1,18 @@
|
||||||
|
{
|
||||||
|
"$schema": "https://dev.office.com/json-schemas/spfx-build/config.2.0.schema.json",
|
||||||
|
"version": "2.0",
|
||||||
|
"bundles": {
|
||||||
|
"file-upload-web-part": {
|
||||||
|
"components": [
|
||||||
|
{
|
||||||
|
"entrypoint": "./lib/webparts/fileUpload/FileUploadWebPart.js",
|
||||||
|
"manifest": "./src/webparts/fileUpload/FileUploadWebPart.manifest.json"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"externals": {},
|
||||||
|
"localizedResources": {
|
||||||
|
"FileUploadWebPartStrings": "lib/webparts/fileUpload/loc/{locale}.js"
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,4 @@
|
||||||
|
{
|
||||||
|
"$schema": "https://dev.office.com/json-schemas/spfx-build/copy-assets.schema.json",
|
||||||
|
"deployCdnPath": "temp/deploy"
|
||||||
|
}
|
|
@ -0,0 +1,7 @@
|
||||||
|
{
|
||||||
|
"$schema": "https://dev.office.com/json-schemas/spfx-build/deploy-azure-storage.schema.json",
|
||||||
|
"workingDir": "./temp/deploy/",
|
||||||
|
"account": "<!-- STORAGE ACCOUNT NAME -->",
|
||||||
|
"container": "dixons-carphone",
|
||||||
|
"accessKey": "<!-- ACCESS KEY -->"
|
||||||
|
}
|
|
@ -0,0 +1,13 @@
|
||||||
|
{
|
||||||
|
"$schema": "https://dev.office.com/json-schemas/spfx-build/package-solution.schema.json",
|
||||||
|
"solution": {
|
||||||
|
"name": "SPFileUpload-client-side-solution",
|
||||||
|
"id": "297016d1-8144-431f-87e5-fe5272d0a226",
|
||||||
|
"version": "1.0.0.0",
|
||||||
|
"includeClientSideAssets": true,
|
||||||
|
"skipFeatureDeployment": true
|
||||||
|
},
|
||||||
|
"paths": {
|
||||||
|
"zippedPackage": "solution/SPFileUpload.sppkg"
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,10 @@
|
||||||
|
{
|
||||||
|
"$schema": "https://dev.office.com/json-schemas/core-build/serve.schema.json",
|
||||||
|
"port": 4321,
|
||||||
|
"https": true,
|
||||||
|
"initialPage": "https://localhost:5432/workbench",
|
||||||
|
"api": {
|
||||||
|
"port": 5432,
|
||||||
|
"entryPath": "node_modules/@microsoft/sp-webpart-workbench/lib/api/"
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,45 @@
|
||||||
|
{
|
||||||
|
"$schema": "https://dev.office.com/json-schemas/core-build/tslint.schema.json",
|
||||||
|
// Display errors as warnings
|
||||||
|
"displayAsWarning": true,
|
||||||
|
// The TSLint task may have been configured with several custom lint rules
|
||||||
|
// before this config file is read (for example lint rules from the tslint-microsoft-contrib
|
||||||
|
// project). If true, this flag will deactivate any of these rules.
|
||||||
|
"removeExistingRules": true,
|
||||||
|
// When true, the TSLint task is configured with some default TSLint "rules.":
|
||||||
|
"useDefaultConfigAsBase": false,
|
||||||
|
// Since removeExistingRules=true and useDefaultConfigAsBase=false, there will be no lint rules
|
||||||
|
// which are active, other than the list of rules below.
|
||||||
|
"lintConfig": {
|
||||||
|
// Opt-in to Lint rules which help to eliminate bugs in JavaScript
|
||||||
|
"rules": {
|
||||||
|
"class-name": false,
|
||||||
|
"export-name": false,
|
||||||
|
"forin": false,
|
||||||
|
"label-position": false,
|
||||||
|
"member-access": true,
|
||||||
|
"no-arg": false,
|
||||||
|
"no-console": false,
|
||||||
|
"no-construct": false,
|
||||||
|
"no-duplicate-case": true,
|
||||||
|
"no-duplicate-variable": true,
|
||||||
|
"no-eval": false,
|
||||||
|
"no-function-expression": true,
|
||||||
|
"no-internal-module": true,
|
||||||
|
"no-shadowed-variable": true,
|
||||||
|
"no-switch-case-fall-through": true,
|
||||||
|
"no-unnecessary-semicolons": true,
|
||||||
|
"no-unused-expression": true,
|
||||||
|
"no-use-before-declare": true,
|
||||||
|
"no-with-statement": true,
|
||||||
|
"semicolon": true,
|
||||||
|
"trailing-comma": false,
|
||||||
|
"typedef": false,
|
||||||
|
"typedef-whitespace": false,
|
||||||
|
"use-named-parameter": true,
|
||||||
|
"valid-typeof": true,
|
||||||
|
"variable-name": false,
|
||||||
|
"whitespace": false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,4 @@
|
||||||
|
{
|
||||||
|
"$schema": "https://dev.office.com/json-schemas/spfx-build/write-manifests.schema.json",
|
||||||
|
"cdnBasePath": "<!-- PATH TO CDN -->"
|
||||||
|
}
|
|
@ -0,0 +1,7 @@
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
const gulp = require('gulp');
|
||||||
|
const build = require('@microsoft/sp-build-web');
|
||||||
|
build.addSuppression(`Warning - [sass] The local CSS class 'ms-Grid' is not camelCase and will not be type-safe.`);
|
||||||
|
|
||||||
|
build.initialize(gulp);
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,35 @@
|
||||||
|
{
|
||||||
|
"name": "dixons-carphone",
|
||||||
|
"version": "0.0.1",
|
||||||
|
"private": true,
|
||||||
|
"engines": {
|
||||||
|
"node": ">=0.10.0"
|
||||||
|
},
|
||||||
|
"scripts": {
|
||||||
|
"build": "gulp bundle",
|
||||||
|
"clean": "gulp clean",
|
||||||
|
"test": "gulp test"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"@microsoft/sp-core-library": "~1.4.0",
|
||||||
|
"@microsoft/sp-lodash-subset": "~1.4.0",
|
||||||
|
"@microsoft/sp-office-ui-fabric-core": "~1.4.0",
|
||||||
|
"@microsoft/sp-webpart-base": "~1.4.0",
|
||||||
|
"@types/react": "15.6.6",
|
||||||
|
"@types/react-dom": "15.5.6",
|
||||||
|
"@types/webpack-env": ">=1.12.1 <1.14.0",
|
||||||
|
"react": "15.6.2",
|
||||||
|
"react-dom": "15.6.2",
|
||||||
|
"react-dropzone-component": "^3.0.0",
|
||||||
|
"sp-pnp-js": "^3.0.4"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"@microsoft/sp-build-web": "~1.4.0",
|
||||||
|
"@microsoft/sp-module-interfaces": "~1.4.0",
|
||||||
|
"@microsoft/sp-webpart-workbench": "~1.4.0",
|
||||||
|
"gulp": "~3.9.1",
|
||||||
|
"@types/chai": ">=3.4.34 <3.6.0",
|
||||||
|
"@types/mocha": ">=2.2.33 <2.6.0",
|
||||||
|
"ajv": "~5.2.2"
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1 @@
|
||||||
|
export * from './common/propertyFieldHeader/index';
|
|
@ -0,0 +1 @@
|
||||||
|
export * from './propertyFields/listPicker/index';
|
|
@ -0,0 +1,51 @@
|
||||||
|
import * as React from 'react';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Enum to describe possible events to show callout
|
||||||
|
*/
|
||||||
|
export enum CalloutTriggers {
|
||||||
|
Click = 1,
|
||||||
|
Hover
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Interface that discibes available settings of Header callout
|
||||||
|
*/
|
||||||
|
export interface IPropertyFieldHeaderCalloutProps {
|
||||||
|
/**
|
||||||
|
* Callout content - any HTML
|
||||||
|
*/
|
||||||
|
calloutContent?: React.ReactNode;
|
||||||
|
/**
|
||||||
|
* Custom width for callout including borders. If value is 0, no width is applied.
|
||||||
|
*/
|
||||||
|
calloutWidth?: number;
|
||||||
|
/**
|
||||||
|
* Event to show the callout
|
||||||
|
*/
|
||||||
|
calloutTrigger?: CalloutTriggers;
|
||||||
|
/**
|
||||||
|
* The gap between the Callout and the target
|
||||||
|
*/
|
||||||
|
gapSpace?: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* PropertyFieldHeader component props
|
||||||
|
*/
|
||||||
|
export interface IPropertyFieldHeaderProps extends IPropertyFieldHeaderCalloutProps {
|
||||||
|
/**
|
||||||
|
* The label to be shown in the header
|
||||||
|
*/
|
||||||
|
label?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* PropertyFieldHeader component state
|
||||||
|
*/
|
||||||
|
export interface IPropertyFieldHeaderState {
|
||||||
|
/**
|
||||||
|
* Flag if the callout is currently visible
|
||||||
|
*/
|
||||||
|
isCalloutVisible?: boolean;
|
||||||
|
}
|
|
@ -0,0 +1,20 @@
|
||||||
|
.headerBar {
|
||||||
|
position: relative;
|
||||||
|
margin-bottom: 5px;
|
||||||
|
.header {
|
||||||
|
margin-right: 24px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.info {
|
||||||
|
position: absolute;
|
||||||
|
font-size: 14px;
|
||||||
|
background-color: transparent;
|
||||||
|
top: 3px;
|
||||||
|
right: 0px;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.headerCallout {
|
||||||
|
padding: 10px;
|
||||||
|
}
|
|
@ -0,0 +1,11 @@
|
||||||
|
/* tslint:disable */
|
||||||
|
require('./PropertyFieldHeader.module.css');
|
||||||
|
const styles = {
|
||||||
|
headerBar: 'headerBar_de667ef4',
|
||||||
|
header: 'header_de667ef4',
|
||||||
|
info: 'info_de667ef4',
|
||||||
|
headerCallout: 'headerCallout_de667ef4',
|
||||||
|
};
|
||||||
|
|
||||||
|
export default styles;
|
||||||
|
/* tslint:enable */
|
|
@ -0,0 +1,101 @@
|
||||||
|
import * as React from 'react';
|
||||||
|
import { IconButton, Callout, DirectionalHint } from 'office-ui-fabric-react';
|
||||||
|
import { IPropertyFieldHeaderProps, IPropertyFieldHeaderState, CalloutTriggers } from './IPropertyFieldHeader';
|
||||||
|
|
||||||
|
import styles from './PropertyFieldHeader.module.scss';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* PropertyFieldHeader component.
|
||||||
|
* Displays a label and a callout
|
||||||
|
*/
|
||||||
|
export default class PropertyFieldHeader extends React.Component<IPropertyFieldHeaderProps, IPropertyFieldHeaderState> {
|
||||||
|
|
||||||
|
private _infoIcon: HTMLElement;
|
||||||
|
|
||||||
|
public constructor(props: IPropertyFieldHeaderProps, state: IPropertyFieldHeaderState) {
|
||||||
|
super(props, state);
|
||||||
|
this._onCalloutDismiss = this._onCalloutDismiss.bind(this);
|
||||||
|
this.state = {
|
||||||
|
isCalloutVisible: false
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
public render(): JSX.Element {
|
||||||
|
return (
|
||||||
|
<div className={styles.headerBar}>
|
||||||
|
<div className={styles.header}>
|
||||||
|
{this.props.label}
|
||||||
|
</div>
|
||||||
|
<div className={styles.info}>
|
||||||
|
<i className={'ms-Icon ms-Icon--Info'} ref={(infoIcon) => { this._infoIcon = infoIcon; }}
|
||||||
|
onMouseOver={this.props.calloutTrigger === CalloutTriggers.Hover ? this._onInfoIconMouseOver.bind(this) : null}
|
||||||
|
onMouseOut={this.props.calloutTrigger === CalloutTriggers.Hover ? this._onInfoIconMouseOut.bind(this) : null}
|
||||||
|
onClick={this.props.calloutTrigger === CalloutTriggers.Click ? this._onInfoIconClick.bind(this) : null}></i>
|
||||||
|
</div>
|
||||||
|
{this.state.isCalloutVisible && (
|
||||||
|
<Callout
|
||||||
|
className={styles.headerCallout}
|
||||||
|
target={this._infoIcon}
|
||||||
|
isBeakVisible={true}
|
||||||
|
directionalHint={DirectionalHint.leftCenter}
|
||||||
|
directionalHintForRTL={DirectionalHint.rightCenter}
|
||||||
|
onDismiss={this._onCalloutDismiss}
|
||||||
|
gapSpace={this.props.gapSpace !== undefined ? this.props.gapSpace : 5}
|
||||||
|
calloutWidth={this.props.calloutWidth}>
|
||||||
|
{this.props.calloutContent}
|
||||||
|
</Callout>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
</div>);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private _onCalloutDismiss() {
|
||||||
|
if (this.state.isCalloutVisible) {
|
||||||
|
this.setState({
|
||||||
|
isCalloutVisible: false
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private _onInfoIconMouseOver(): void {
|
||||||
|
if (this.props.calloutTrigger !== CalloutTriggers.Hover) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!this.state.isCalloutVisible) {
|
||||||
|
this.setState({
|
||||||
|
isCalloutVisible: true
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private _onInfoIconMouseOut(e: MouseEvent): void {
|
||||||
|
if (this.props.calloutTrigger !== CalloutTriggers.Hover) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (e.relatedTarget) {
|
||||||
|
|
||||||
|
let relatedTarget: HTMLElement = (e.relatedTarget as HTMLElement);
|
||||||
|
if (relatedTarget && relatedTarget.closest('.ms-Callout-container')) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
this.setState({
|
||||||
|
isCalloutVisible: false
|
||||||
|
});
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private _onInfoIconClick(): void {
|
||||||
|
if (this.props.calloutTrigger !== CalloutTriggers.Click) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.setState({
|
||||||
|
isCalloutVisible: !this.state.isCalloutVisible
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,2 @@
|
||||||
|
export * from './IPropertyFieldHeader';
|
||||||
|
export * from './PropertyFieldHeader';
|
|
@ -0,0 +1,26 @@
|
||||||
|
import * as React from 'react';
|
||||||
|
|
||||||
|
|
||||||
|
export interface IFieldErrorMessageProps {
|
||||||
|
|
||||||
|
errorMessage: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Component that shows an error message when something went wront with the property control
|
||||||
|
*/
|
||||||
|
export default class FieldErrorMessage extends React.Component<IFieldErrorMessageProps> {
|
||||||
|
public render(): JSX.Element {
|
||||||
|
if (this.props.errorMessage !== 'undefined' && this.props.errorMessage !== null && this.props.errorMessage !== '') {
|
||||||
|
return (
|
||||||
|
<div style={{ paddingBottom: '8px' }}><div aria-live='assertive' className='ms-u-screenReaderOnly' data-automation-id='error-message'>{this.props.errorMessage}</div>
|
||||||
|
<span>
|
||||||
|
<p className='ms-TextField-errorMessage ms-u-slideDownIn20'>{this.props.errorMessage}</p>
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
return <div />;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,21 @@
|
||||||
|
import { IChoiceGroupOption } from 'office-ui-fabric-react/lib/ChoiceGroup';
|
||||||
|
import { IPropertyFieldListPickerPropsInternal } from './IPropertyFieldListPicker';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* PropertyFieldListPickerHost properties interface
|
||||||
|
*/
|
||||||
|
export interface IPropertyFieldListMultiPickerHostProps extends IPropertyFieldListPickerPropsInternal {
|
||||||
|
|
||||||
|
onChange: (targetProperty?: string, newValue?: any) => void;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* PropertyFieldSPListMultiplePickerHost state interface
|
||||||
|
*/
|
||||||
|
export interface IPropertyFieldListMultiPickerHostState {
|
||||||
|
|
||||||
|
results: IChoiceGroupOption[];
|
||||||
|
selectedKeys: string[];
|
||||||
|
loaded: boolean;
|
||||||
|
errorMessage?: string;
|
||||||
|
}
|
|
@ -0,0 +1,106 @@
|
||||||
|
import { IWebPartContext, IPropertyPaneCustomFieldProps } from '@microsoft/sp-webpart-base';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Enum for specifying how the lists should be sorted
|
||||||
|
*/
|
||||||
|
export enum PropertyFieldListPickerOrderBy {
|
||||||
|
Id = 1,
|
||||||
|
Title
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Public properties of the PropertyFieldListPicker custom field
|
||||||
|
*/
|
||||||
|
export interface IPropertyFieldListPickerProps {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Property field label displayed on top
|
||||||
|
*/
|
||||||
|
label: string;
|
||||||
|
/**
|
||||||
|
* Context of the current web part
|
||||||
|
*/
|
||||||
|
context: IWebPartContext;
|
||||||
|
/**
|
||||||
|
* Initial selected list set of the control
|
||||||
|
*/
|
||||||
|
selectedList?: string | string[];
|
||||||
|
/**
|
||||||
|
* BaseTemplate ID of the lists or libaries you want to return.
|
||||||
|
*/
|
||||||
|
baseTemplate?: number;
|
||||||
|
/**
|
||||||
|
* Specify if you want to include or exclude hidden lists. By default this is true.
|
||||||
|
*/
|
||||||
|
includeHidden?: boolean;
|
||||||
|
/**
|
||||||
|
* Specify the property on which you want to order the retrieve set of lists.
|
||||||
|
*/
|
||||||
|
orderBy?: PropertyFieldListPickerOrderBy;
|
||||||
|
/**
|
||||||
|
* Specify if you want to have a single or mult list selector.
|
||||||
|
*/
|
||||||
|
multiSelect?: boolean;
|
||||||
|
/**
|
||||||
|
* Defines a onPropertyChange function to raise when the selected value changed.
|
||||||
|
* Normally this function must be always defined with the 'this.onPropertyChange'
|
||||||
|
* method of the web part object.
|
||||||
|
*/
|
||||||
|
onPropertyChange(propertyPath: string, oldValue: any, newValue: any): void;
|
||||||
|
/**
|
||||||
|
* Parent Web Part properties
|
||||||
|
*/
|
||||||
|
properties: any;
|
||||||
|
/**
|
||||||
|
* An UNIQUE key indicates the identity of this control
|
||||||
|
*/
|
||||||
|
key?: string;
|
||||||
|
/**
|
||||||
|
* Whether the property pane field is enabled or not.
|
||||||
|
*/
|
||||||
|
disabled?: boolean;
|
||||||
|
/**
|
||||||
|
* The method is used to get the validation error message and determine whether the input value is valid or not.
|
||||||
|
*
|
||||||
|
* When it returns string:
|
||||||
|
* - If valid, it returns empty string.
|
||||||
|
* - If invalid, it returns the error message string and the text field will
|
||||||
|
* show a red border and show an error message below the text field.
|
||||||
|
*
|
||||||
|
* When it returns Promise<string>:
|
||||||
|
* - The resolved value is display as error message.
|
||||||
|
* - The rejected, the value is thrown away.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
onGetErrorMessage?: (value: string) => string | Promise<string>;
|
||||||
|
/**
|
||||||
|
* Custom Field will start to validate after users stop typing for `deferredValidationTime` milliseconds.
|
||||||
|
* Default value is 200.
|
||||||
|
*/
|
||||||
|
deferredValidationTime?: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Private properties of the PropertyFieldListPicker custom field.
|
||||||
|
* We separate public & private properties to include onRender & onDispose method waited
|
||||||
|
* by the PropertyFieldCustom, witout asking to the developer to add it when he's using
|
||||||
|
* the PropertyFieldListPicker.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
export interface IPropertyFieldListPickerPropsInternal extends IPropertyFieldListPickerProps, IPropertyPaneCustomFieldProps {
|
||||||
|
|
||||||
|
label: string;
|
||||||
|
targetProperty: string;
|
||||||
|
context: IWebPartContext;
|
||||||
|
selectedList?: string;
|
||||||
|
selectedLists?: string[];
|
||||||
|
baseTemplate?: number;
|
||||||
|
orderBy?: PropertyFieldListPickerOrderBy;
|
||||||
|
includeHidden?: boolean;
|
||||||
|
onPropertyChange(propertyPath: string, oldValue: any, newValue: any): void;
|
||||||
|
properties: any;
|
||||||
|
key: string;
|
||||||
|
disabled?: boolean;
|
||||||
|
onGetErrorMessage?: (value: string | string[]) => string | Promise<string>;
|
||||||
|
deferredValidationTime?: number;
|
||||||
|
}
|
|
@ -0,0 +1,38 @@
|
||||||
|
import { IPropertyFieldListPickerPropsInternal } from './IPropertyFieldListPicker';
|
||||||
|
import { IDropdownOption } from 'office-ui-fabric-react/lib/Dropdown';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* PropertyFieldListPickerHost properties interface
|
||||||
|
*/
|
||||||
|
export interface IPropertyFieldListPickerHostProps extends IPropertyFieldListPickerPropsInternal {
|
||||||
|
|
||||||
|
onChange: (targetProperty?: string, newValue?: any) => void;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* PropertyFieldListPickerHost state interface
|
||||||
|
*/
|
||||||
|
export interface IPropertyFieldListPickerHostState {
|
||||||
|
|
||||||
|
results: IDropdownOption[];
|
||||||
|
selectedKey?: string;
|
||||||
|
errorMessage?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Defines a collection of SharePoint lists
|
||||||
|
*/
|
||||||
|
export interface ISPLists {
|
||||||
|
|
||||||
|
value: ISPList[];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Defines a SharePoint list
|
||||||
|
*/
|
||||||
|
export interface ISPList {
|
||||||
|
|
||||||
|
Title: string;
|
||||||
|
Id: string;
|
||||||
|
BaseTemplate: string;
|
||||||
|
}
|
|
@ -0,0 +1,197 @@
|
||||||
|
import * as React from 'react';
|
||||||
|
import { Label } from 'office-ui-fabric-react/lib/Label';
|
||||||
|
import { IChoiceGroupOption } from 'office-ui-fabric-react/lib/ChoiceGroup';
|
||||||
|
import { Spinner, SpinnerType } from 'office-ui-fabric-react/lib/Spinner';
|
||||||
|
import { Async } from 'office-ui-fabric-react/lib/Utilities';
|
||||||
|
import { Checkbox } from 'office-ui-fabric-react/lib/Checkbox';
|
||||||
|
import { IPropertyFieldListMultiPickerHostProps, IPropertyFieldListMultiPickerHostState } from './IPropertyFieldListMultiPickerHost';
|
||||||
|
import { ISPLists, ISPList } from './IPropertyFieldListPickerHost';
|
||||||
|
import SPListPickerService from '../../services/SPListPickerService';
|
||||||
|
import FieldErrorMessage from '../errorMessage/FieldErrorMessage';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Renders the controls for PropertyFieldSPListMultiplePicker component
|
||||||
|
*/
|
||||||
|
export default class PropertyFieldListMultiPickerHost extends React.Component<IPropertyFieldListMultiPickerHostProps, IPropertyFieldListMultiPickerHostState> {
|
||||||
|
private options: IChoiceGroupOption[] = [];
|
||||||
|
private loaded: boolean = false;
|
||||||
|
private async: Async;
|
||||||
|
private delayedValidate: (value: string[]) => void;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructor
|
||||||
|
*/
|
||||||
|
constructor(props: IPropertyFieldListMultiPickerHostProps) {
|
||||||
|
super(props);
|
||||||
|
|
||||||
|
this.onChanged = this.onChanged.bind(this);
|
||||||
|
this.state = {
|
||||||
|
results: this.options,
|
||||||
|
selectedKeys: [],
|
||||||
|
loaded: this.loaded,
|
||||||
|
errorMessage: ''
|
||||||
|
};
|
||||||
|
|
||||||
|
this.async = new Async(this);
|
||||||
|
this.validate = this.validate.bind(this);
|
||||||
|
this.notifyAfterValidate = this.notifyAfterValidate.bind(this);
|
||||||
|
this.delayedValidate = this.async.debounce(this.validate, this.props.deferredValidationTime);
|
||||||
|
|
||||||
|
this.loadLists();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Loads the list from SharePoint current web site
|
||||||
|
*/
|
||||||
|
private loadLists(): void {
|
||||||
|
// Builds the SharePoint List service
|
||||||
|
const listService: SPListPickerService = new SPListPickerService(this.props, this.props.context);
|
||||||
|
// Gets the libs
|
||||||
|
listService.getLibs().then((response: ISPLists) => {
|
||||||
|
response.value.map((list: ISPList) => {
|
||||||
|
let isSelected: boolean = false;
|
||||||
|
let indexInExisting: number = -1;
|
||||||
|
// Defines if the current list must be selected by default
|
||||||
|
if (this.props.selectedLists) {
|
||||||
|
indexInExisting = this.props.selectedLists.indexOf(list.Id);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (indexInExisting > -1) {
|
||||||
|
isSelected = true;
|
||||||
|
this.state.selectedKeys.push(list.Id);
|
||||||
|
}
|
||||||
|
// Add the option to the list
|
||||||
|
this.options.push({
|
||||||
|
key: list.Id,
|
||||||
|
text: list.Title,
|
||||||
|
checked: isSelected
|
||||||
|
});
|
||||||
|
});
|
||||||
|
this.loaded = true;
|
||||||
|
this.setState({ results: this.options, selectedKeys: this.state.selectedKeys, loaded: true });
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Raises when a list has been selected
|
||||||
|
*/
|
||||||
|
private onChanged(element: React.FormEvent<HTMLElement>, isChecked: boolean): void {
|
||||||
|
if (element) {
|
||||||
|
const value: string = (element.currentTarget as any).value;
|
||||||
|
let selectedKeys = this.state.selectedKeys;
|
||||||
|
// Check if the element is selected
|
||||||
|
if (isChecked === false) {
|
||||||
|
// Remove the unselected item
|
||||||
|
selectedKeys = selectedKeys.filter(s => s !== value);
|
||||||
|
} else {
|
||||||
|
// Add the selected item and filter out the doubles
|
||||||
|
selectedKeys.push(value);
|
||||||
|
selectedKeys = selectedKeys.filter((item, pos, self) => {
|
||||||
|
return self.indexOf(item) == pos;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
// Update the state and validate
|
||||||
|
this.setState({
|
||||||
|
selectedKeys: selectedKeys
|
||||||
|
});
|
||||||
|
this.delayedValidate(selectedKeys);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Validates the new custom field value
|
||||||
|
*/
|
||||||
|
private validate(value: string[]): void {
|
||||||
|
if (this.props.onGetErrorMessage === null || typeof this.props.onGetErrorMessage === 'undefined') {
|
||||||
|
this.notifyAfterValidate(this.props.selectedLists, value);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const result: string | PromiseLike<string> = this.props.onGetErrorMessage(value || []);
|
||||||
|
if (typeof result !== 'undefined') {
|
||||||
|
if (typeof result === 'string') {
|
||||||
|
if (result === '') {
|
||||||
|
this.notifyAfterValidate(this.props.selectedLists, value);
|
||||||
|
}
|
||||||
|
this.setState({
|
||||||
|
errorMessage: result
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
result.then((errorMessage: string) => {
|
||||||
|
if (typeof errorMessage === 'undefined' || errorMessage === '') {
|
||||||
|
this.notifyAfterValidate(this.props.selectedLists, value);
|
||||||
|
}
|
||||||
|
this.setState({
|
||||||
|
errorMessage: errorMessage
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
this.notifyAfterValidate(this.props.selectedLists, value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Notifies the parent Web Part of a property value change
|
||||||
|
*/
|
||||||
|
private notifyAfterValidate(oldValue: string[], newValue: string[]) {
|
||||||
|
if (this.props.onPropertyChange && newValue !== null) {
|
||||||
|
this.props.properties[this.props.targetProperty] = newValue;
|
||||||
|
this.props.onPropertyChange(this.props.targetProperty, oldValue, newValue);
|
||||||
|
// Trigger the apply button
|
||||||
|
if (typeof this.props.onChange !== 'undefined' && this.props.onChange !== null) {
|
||||||
|
this.props.onChange(this.props.targetProperty, newValue);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Called when the component will unmount
|
||||||
|
*/
|
||||||
|
public componentWillUnmount() {
|
||||||
|
this.async.dispose();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Renders the SPListMultiplePicker controls with Office UI Fabric
|
||||||
|
*/
|
||||||
|
public render(): JSX.Element {
|
||||||
|
if (this.loaded === false) {
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
<Label>{this.props.label}</Label>
|
||||||
|
<Spinner type={SpinnerType.normal} />
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
const styleOfLabel: any = {
|
||||||
|
color: this.props.disabled === true ? '#A6A6A6' : 'auto'
|
||||||
|
};
|
||||||
|
|
||||||
|
// Renders content
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
<Label>{this.props.label}</Label>
|
||||||
|
{
|
||||||
|
this.options.map((item: IChoiceGroupOption, index: number) => {
|
||||||
|
const uniqueKey = this.props.targetProperty + '-' + item.key;
|
||||||
|
return (
|
||||||
|
<div style={{ marginBottom: '5px' }} className='ms-ChoiceField' key={`${this.props.key}-multiplelistpicker-${index}`}>
|
||||||
|
<Checkbox
|
||||||
|
defaultChecked={item.checked}
|
||||||
|
disabled={this.props.disabled}
|
||||||
|
label={item.text}
|
||||||
|
onChange={this.onChanged}
|
||||||
|
inputProps={{ value: item.key }}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
<FieldErrorMessage errorMessage={this.state.errorMessage} />
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,151 @@
|
||||||
|
import * as React from 'react';
|
||||||
|
import * as ReactDom from 'react-dom';
|
||||||
|
import {
|
||||||
|
IPropertyPaneField,
|
||||||
|
PropertyPaneFieldType,
|
||||||
|
IWebPartContext
|
||||||
|
} from '@microsoft/sp-webpart-base';
|
||||||
|
import PropertyFieldListPickerHost from './PropertyFieldListPickerHost';
|
||||||
|
import PropertyFieldListMultiPickerHost from './PropertyFieldListMultiPickerHost';
|
||||||
|
import { IPropertyFieldListPickerHostProps } from './IPropertyFieldListPickerHost';
|
||||||
|
import { IPropertyFieldListMultiPickerHostProps } from './IPropertyFieldListMultiPickerHost';
|
||||||
|
import { PropertyFieldListPickerOrderBy, IPropertyFieldListPickerProps, IPropertyFieldListPickerPropsInternal } from './IPropertyFieldListPicker';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Represents a PropertyFieldListPicker object
|
||||||
|
*/
|
||||||
|
class PropertyFieldListPickerBuilder implements IPropertyPaneField<IPropertyFieldListPickerPropsInternal> {
|
||||||
|
|
||||||
|
//Properties defined by IPropertyPaneField
|
||||||
|
public type: PropertyPaneFieldType = PropertyPaneFieldType.Custom;
|
||||||
|
public targetProperty: string;
|
||||||
|
public properties: IPropertyFieldListPickerPropsInternal;
|
||||||
|
|
||||||
|
//Custom properties label: string;
|
||||||
|
private label: string;
|
||||||
|
private context: IWebPartContext;
|
||||||
|
private selectedList: string;
|
||||||
|
private selectedLists: string[];
|
||||||
|
private baseTemplate: number;
|
||||||
|
private orderBy: PropertyFieldListPickerOrderBy;
|
||||||
|
private multiSelect: boolean;
|
||||||
|
private includeHidden: boolean;
|
||||||
|
|
||||||
|
public onPropertyChange(propertyPath: string, oldValue: any, newValue: any): void { }
|
||||||
|
private customProperties: any;
|
||||||
|
private key: string;
|
||||||
|
private disabled: boolean = false;
|
||||||
|
private onGetErrorMessage: (value: string) => string | Promise<string>;
|
||||||
|
private deferredValidationTime: number = 200;
|
||||||
|
private renderWebPart: () => void;
|
||||||
|
private disableReactivePropertyChanges: boolean = false;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructor method
|
||||||
|
*/
|
||||||
|
public constructor(_targetProperty: string, _properties: IPropertyFieldListPickerPropsInternal) {
|
||||||
|
this.render = this.render.bind(this);
|
||||||
|
this.targetProperty = _targetProperty;
|
||||||
|
this.properties = _properties;
|
||||||
|
this.properties.onDispose = this.dispose;
|
||||||
|
this.properties.onRender = this.render;
|
||||||
|
this.label = _properties.label;
|
||||||
|
this.context = _properties.context;
|
||||||
|
this.selectedList = _properties.selectedList;
|
||||||
|
this.selectedLists = _properties.selectedLists;
|
||||||
|
this.baseTemplate = _properties.baseTemplate;
|
||||||
|
this.orderBy = _properties.orderBy;
|
||||||
|
this.multiSelect = _properties.multiSelect;
|
||||||
|
this.includeHidden = _properties.includeHidden;
|
||||||
|
this.onPropertyChange = _properties.onPropertyChange;
|
||||||
|
this.customProperties = _properties.properties;
|
||||||
|
this.key = _properties.key;
|
||||||
|
this.onGetErrorMessage = _properties.onGetErrorMessage;
|
||||||
|
|
||||||
|
if (_properties.disabled === true) {
|
||||||
|
this.disabled = _properties.disabled;
|
||||||
|
}
|
||||||
|
if (_properties.deferredValidationTime) {
|
||||||
|
this.deferredValidationTime = _properties.deferredValidationTime;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Renders the SPListPicker field content
|
||||||
|
*/
|
||||||
|
private render(elem: HTMLElement, ctx?: any, changeCallback?: (targetProperty?: string, newValue?: any) => void): void {
|
||||||
|
const componentProps = {
|
||||||
|
label: this.label,
|
||||||
|
targetProperty: this.targetProperty,
|
||||||
|
context: this.context,
|
||||||
|
baseTemplate: this.baseTemplate,
|
||||||
|
orderBy: this.orderBy,
|
||||||
|
multiSelect: this.multiSelect,
|
||||||
|
includeHidden: this.includeHidden,
|
||||||
|
onDispose: this.dispose,
|
||||||
|
onRender: this.render,
|
||||||
|
onChange: changeCallback,
|
||||||
|
onPropertyChange: this.onPropertyChange,
|
||||||
|
properties: this.customProperties,
|
||||||
|
key: this.key,
|
||||||
|
disabled: this.disabled,
|
||||||
|
onGetErrorMessage: this.onGetErrorMessage,
|
||||||
|
deferredValidationTime: this.deferredValidationTime
|
||||||
|
};
|
||||||
|
|
||||||
|
// Check if the multi or single select component has to get loaded
|
||||||
|
if (this.multiSelect) {
|
||||||
|
// Multi selector
|
||||||
|
componentProps['selectedLists'] = this.selectedLists;
|
||||||
|
const element: React.ReactElement<IPropertyFieldListMultiPickerHostProps> = React.createElement(PropertyFieldListMultiPickerHost, componentProps);
|
||||||
|
// Calls the REACT content generator
|
||||||
|
ReactDom.render(element, elem);
|
||||||
|
} else {
|
||||||
|
// Single selector
|
||||||
|
componentProps['selectedList'] = this.selectedList;
|
||||||
|
const element: React.ReactElement<IPropertyFieldListPickerHostProps> = React.createElement(PropertyFieldListPickerHost, componentProps);
|
||||||
|
// Calls the REACT content generator
|
||||||
|
ReactDom.render(element, elem);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Disposes the current object
|
||||||
|
*/
|
||||||
|
private dispose(elem: HTMLElement): void {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Helper method to create a SPList Picker on the PropertyPane.
|
||||||
|
* @param targetProperty - Target property the SharePoint list picker is associated to.
|
||||||
|
* @param properties - Strongly typed SPList Picker properties.
|
||||||
|
*/
|
||||||
|
export function PropertyFieldListPicker(targetProperty: string, properties: IPropertyFieldListPickerProps): IPropertyPaneField<IPropertyFieldListPickerPropsInternal> {
|
||||||
|
|
||||||
|
//Create an internal properties object from the given properties
|
||||||
|
const newProperties: IPropertyFieldListPickerPropsInternal = {
|
||||||
|
label: properties.label,
|
||||||
|
targetProperty: targetProperty,
|
||||||
|
context: properties.context,
|
||||||
|
selectedList: typeof properties.selectedList === 'string' ? properties.selectedList : null,
|
||||||
|
selectedLists: typeof properties.selectedList !== 'string' ? properties.selectedList : null,
|
||||||
|
baseTemplate: properties.baseTemplate,
|
||||||
|
orderBy: properties.orderBy,
|
||||||
|
multiSelect: properties.multiSelect || false,
|
||||||
|
includeHidden: properties.includeHidden,
|
||||||
|
onPropertyChange: properties.onPropertyChange,
|
||||||
|
properties: properties.properties,
|
||||||
|
onDispose: null,
|
||||||
|
onRender: null,
|
||||||
|
key: properties.key,
|
||||||
|
disabled: properties.disabled,
|
||||||
|
onGetErrorMessage: properties.onGetErrorMessage,
|
||||||
|
deferredValidationTime: properties.deferredValidationTime
|
||||||
|
};
|
||||||
|
//Calls the PropertyFieldListPicker builder object
|
||||||
|
//This object will simulate a PropertyFieldCustom to manage his rendering process
|
||||||
|
return new PropertyFieldListPickerBuilder(targetProperty, newProperties);
|
||||||
|
}
|
|
@ -0,0 +1,187 @@
|
||||||
|
import * as React from 'react';
|
||||||
|
import { Dropdown, IDropdownOption } from 'office-ui-fabric-react/lib/Dropdown';
|
||||||
|
import { Async } from 'office-ui-fabric-react/lib/Utilities';
|
||||||
|
import { Label } from 'office-ui-fabric-react/lib/Label';
|
||||||
|
import { IPropertyFieldListPickerHostProps, IPropertyFieldListPickerHostState, ISPList, ISPLists } from './IPropertyFieldListPickerHost';
|
||||||
|
import SPListPickerService from '../../services/SPListPickerService';
|
||||||
|
import FieldErrorMessage from '../errorMessage/FieldErrorMessage';
|
||||||
|
|
||||||
|
// Empty list value, to be checked for single list selection
|
||||||
|
const EMPTY_LIST_KEY = 'NO_LIST_SELECTED';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Renders the controls for PropertyFieldListPicker component
|
||||||
|
*/
|
||||||
|
export default class PropertyFieldListPickerHost extends React.Component<IPropertyFieldListPickerHostProps, IPropertyFieldListPickerHostState> {
|
||||||
|
private options: IDropdownOption[] = [];
|
||||||
|
private selectedKey: string;
|
||||||
|
|
||||||
|
private latestValidateValue: string;
|
||||||
|
private async: Async;
|
||||||
|
private delayedValidate: (value: string) => void;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructor method
|
||||||
|
*/
|
||||||
|
constructor(props: IPropertyFieldListPickerHostProps) {
|
||||||
|
super(props);
|
||||||
|
|
||||||
|
this.state = {
|
||||||
|
results: this.options,
|
||||||
|
errorMessage: ''
|
||||||
|
};
|
||||||
|
|
||||||
|
this.async = new Async(this);
|
||||||
|
this.validate = this.validate.bind(this);
|
||||||
|
this.onChanged = this.onChanged.bind(this);
|
||||||
|
this.notifyAfterValidate = this.notifyAfterValidate.bind(this);
|
||||||
|
this.delayedValidate = this.async.debounce(this.validate, this.props.deferredValidationTime);
|
||||||
|
}
|
||||||
|
|
||||||
|
public componentDidMount(): void {
|
||||||
|
// Start retrieving the SharePoint lists
|
||||||
|
this.loadLists();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Loads the list from SharePoint current web site
|
||||||
|
*/
|
||||||
|
private loadLists(): void {
|
||||||
|
const listService: SPListPickerService = new SPListPickerService(this.props, this.props.context);
|
||||||
|
listService.getLibs().then((response: ISPLists) => {
|
||||||
|
// Start mapping the list that are selected
|
||||||
|
response.value.map((list: ISPList) => {
|
||||||
|
if (this.props.selectedList === list.Id) {
|
||||||
|
this.selectedKey = list.Id;
|
||||||
|
}
|
||||||
|
this.options.push({
|
||||||
|
key: list.Id,
|
||||||
|
text: list.Title
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
// Option to unselect the list
|
||||||
|
this.options.unshift({
|
||||||
|
key: EMPTY_LIST_KEY,
|
||||||
|
text: ''
|
||||||
|
});
|
||||||
|
|
||||||
|
// Update the current component state
|
||||||
|
this.setState({
|
||||||
|
results: this.options,
|
||||||
|
selectedKey: this.selectedKey
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Raises when a list has been selected
|
||||||
|
*/
|
||||||
|
private onChanged(option: IDropdownOption, index?: number): void {
|
||||||
|
const newValue: string = option.key as string;
|
||||||
|
this.delayedValidate(newValue);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Validates the new custom field value
|
||||||
|
*/
|
||||||
|
private validate(value: string): void {
|
||||||
|
if (this.props.onGetErrorMessage === null || this.props.onGetErrorMessage === undefined) {
|
||||||
|
this.notifyAfterValidate(this.props.selectedList, value);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.latestValidateValue === value) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.latestValidateValue = value;
|
||||||
|
|
||||||
|
const result: string | PromiseLike<string> = this.props.onGetErrorMessage(value || '');
|
||||||
|
if (typeof result !== 'undefined') {
|
||||||
|
if (typeof result === 'string') {
|
||||||
|
if (result === '') {
|
||||||
|
this.notifyAfterValidate(this.props.selectedList, value);
|
||||||
|
}
|
||||||
|
this.setState({
|
||||||
|
errorMessage: result
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
result.then((errorMessage: string) => {
|
||||||
|
if (typeof errorMessage === 'undefined' || errorMessage === '') {
|
||||||
|
this.notifyAfterValidate(this.props.selectedList, value);
|
||||||
|
}
|
||||||
|
this.setState({
|
||||||
|
errorMessage: errorMessage
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
this.notifyAfterValidate(this.props.selectedList, value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Notifies the parent Web Part of a property value change
|
||||||
|
*/
|
||||||
|
private notifyAfterValidate(oldValue: string, newValue: string) {
|
||||||
|
// Check if the user wanted to unselect the list
|
||||||
|
const propValue = newValue === EMPTY_LIST_KEY ? '' : newValue;
|
||||||
|
|
||||||
|
// Deselect all options
|
||||||
|
this.options = this.state.results.map(option => {
|
||||||
|
if (option.selected) {
|
||||||
|
option.selected = false;
|
||||||
|
}
|
||||||
|
return option;
|
||||||
|
});
|
||||||
|
// Set the current selected key
|
||||||
|
this.selectedKey = newValue;
|
||||||
|
// Update the state
|
||||||
|
this.setState({
|
||||||
|
selectedKey: this.selectedKey,
|
||||||
|
results: this.options
|
||||||
|
});
|
||||||
|
|
||||||
|
if (this.props.onPropertyChange && propValue !== null) {
|
||||||
|
// Store the new property value
|
||||||
|
this.props.properties[this.props.targetProperty] = propValue;
|
||||||
|
// Trigger the default onPrpertyChange event
|
||||||
|
this.props.onPropertyChange(this.props.targetProperty, oldValue, propValue);
|
||||||
|
// Trigger the apply button
|
||||||
|
if (typeof this.props.onChange !== 'undefined' && this.props.onChange !== null) {
|
||||||
|
this.props.onChange(this.props.targetProperty, propValue);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Called when the component will unmount
|
||||||
|
*/
|
||||||
|
public componentWillUnmount() {
|
||||||
|
if (typeof this.async !== 'undefined') {
|
||||||
|
this.async.dispose();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Renders the SPListpicker controls with Office UI Fabric
|
||||||
|
*/
|
||||||
|
public render(): JSX.Element {
|
||||||
|
// Renders content
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
<Label>{this.props.label}</Label>
|
||||||
|
<Dropdown
|
||||||
|
disabled={this.props.disabled}
|
||||||
|
label=''
|
||||||
|
onChanged={this.onChanged}
|
||||||
|
options={this.state.results}
|
||||||
|
selectedKey={this.state.selectedKey}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<FieldErrorMessage errorMessage={this.state.errorMessage} />
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,4 @@
|
||||||
|
export * from './PropertyFieldListPicker';
|
||||||
|
export * from './IPropertyFieldListPicker';
|
||||||
|
export * from './PropertyFieldListPickerHost';
|
||||||
|
export * from './IPropertyFieldListPickerHost';
|
|
@ -0,0 +1,20 @@
|
||||||
|
import { ISPLists } from '../propertyFields/listPicker/IPropertyFieldListPickerHost';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Defines a http client to request mock data to use the web part with the local workbench
|
||||||
|
*/
|
||||||
|
export default class SPListPickerMockHttpClient {
|
||||||
|
/**
|
||||||
|
* Mock SharePoint result sample
|
||||||
|
*/
|
||||||
|
private static _results: ISPLists = { value: [] };
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Mock search People method
|
||||||
|
*/
|
||||||
|
public static getLists(restUrl: string, options?: any): Promise<ISPLists> {
|
||||||
|
return new Promise<ISPLists>((resolve) => {
|
||||||
|
resolve(SPListPickerMockHttpClient._results);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,83 @@
|
||||||
|
import { SPHttpClientResponse } from '@microsoft/sp-http';
|
||||||
|
import { SPHttpClient } from '@microsoft/sp-http';
|
||||||
|
import { Environment, EnvironmentType } from '@microsoft/sp-core-library';
|
||||||
|
import { IWebPartContext } from '@microsoft/sp-webpart-base';
|
||||||
|
import { ISPLists, IPropertyFieldListPickerHostProps } from '../propertyFields/listPicker/IPropertyFieldListPickerHost';
|
||||||
|
import { PropertyFieldListPickerOrderBy } from '../propertyFields/listPicker/IPropertyFieldListPicker';
|
||||||
|
import SPListPickerMockHttpClient from './SPListPickerMockService';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Service implementation to get list & list items from current SharePoint site
|
||||||
|
*/
|
||||||
|
export default class SPListPickerService {
|
||||||
|
|
||||||
|
private context: IWebPartContext;
|
||||||
|
private props: IPropertyFieldListPickerHostProps;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Service constructor
|
||||||
|
*/
|
||||||
|
constructor(_props: IPropertyFieldListPickerHostProps, pageContext: IWebPartContext) {
|
||||||
|
this.props = _props;
|
||||||
|
this.context = pageContext;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the collection of libs in the current SharePoint site
|
||||||
|
*/
|
||||||
|
public getLibs(): Promise<ISPLists> {
|
||||||
|
if (Environment.type === EnvironmentType.Local) {
|
||||||
|
// If the running environment is local, load the data from the mock
|
||||||
|
return this.getLibsFromMock();
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
// If the running environment is SharePoint, request the lists REST service
|
||||||
|
let queryUrl: string = `${this.context.pageContext.web.absoluteUrl}/_api/lists?$select=Title,id,BaseTemplate`;
|
||||||
|
// Check if the orderBy property is provided
|
||||||
|
if (this.props.orderBy !== null) {
|
||||||
|
queryUrl += '&$orderby=';
|
||||||
|
switch (this.props.orderBy) {
|
||||||
|
case PropertyFieldListPickerOrderBy.Id:
|
||||||
|
queryUrl += 'Id';
|
||||||
|
break;
|
||||||
|
case PropertyFieldListPickerOrderBy.Title:
|
||||||
|
queryUrl += 'Title';
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Check if the list have get filtered based on the list base template type
|
||||||
|
if (this.props.baseTemplate !== null && this.props.baseTemplate) {
|
||||||
|
queryUrl += '&$filter=BaseTemplate%20eq%20';
|
||||||
|
queryUrl += this.props.baseTemplate;
|
||||||
|
// Check if you also want to exclude hidden list in the list
|
||||||
|
if (this.props.includeHidden === false) {
|
||||||
|
queryUrl += '%20and%20Hidden%20eq%20false';
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (this.props.includeHidden === false) {
|
||||||
|
queryUrl += '&$filter=Hidden%20eq%20false';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return this.context.spHttpClient.get(queryUrl, SPHttpClient.configurations.v1).then((response: SPHttpClientResponse) => {
|
||||||
|
return response.json();
|
||||||
|
}) as Promise<ISPLists>;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns 3 fake SharePoint lists for the Mock mode
|
||||||
|
*/
|
||||||
|
private getLibsFromMock(): Promise<ISPLists> {
|
||||||
|
return SPListPickerMockHttpClient.getLists(this.context.pageContext.web.absoluteUrl).then(() => {
|
||||||
|
const listData: ISPLists = {
|
||||||
|
value:
|
||||||
|
[
|
||||||
|
{ Title: 'Mock List One', Id: '6770c83b-29e8-494b-87b6-468a2066bcc6', BaseTemplate: '109' },
|
||||||
|
{ Title: 'Mock List Two', Id: '2ece98f2-cc5e-48ff-8145-badf5009754c', BaseTemplate: '109' },
|
||||||
|
{ Title: 'Mock List Three', Id: 'bd5dbd33-0e8d-4e12-b289-b276e5ef79c2', BaseTemplate: '109' }
|
||||||
|
]
|
||||||
|
};
|
||||||
|
return listData;
|
||||||
|
}) as Promise<ISPLists>;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,21 @@
|
||||||
|
{
|
||||||
|
"$schema": "https://dev.office.com/json-schemas/spfx/client-side-web-part-manifest.schema.json",
|
||||||
|
"id": "786e7cdf-d1c8-4412-8abb-1aa18ea555e0",
|
||||||
|
"alias": "FileUploadWebPart",
|
||||||
|
"componentType": "WebPart",
|
||||||
|
"version": "*",
|
||||||
|
"manifestVersion": 2,
|
||||||
|
"requiresCustomScript": false,
|
||||||
|
"preconfiguredEntries": [{
|
||||||
|
"groupId": "5c03119e-3074-46fd-976b-c60198322f80",
|
||||||
|
"group": { "default": "SuperWebParts" },
|
||||||
|
"title": { "default": "SPFileUpload" },
|
||||||
|
"description": { "default": "Use this webpart to upload files to a library or as list attachments" },
|
||||||
|
"officeFabricIconFontName": "OpenFile",
|
||||||
|
"properties": {
|
||||||
|
"listName": "Documents",
|
||||||
|
"fileTypes":"jpg,png,gif,avi,mp4",
|
||||||
|
"uploadFilesTo":"DocumentLibrary"
|
||||||
|
}
|
||||||
|
}]
|
||||||
|
}
|
|
@ -0,0 +1,106 @@
|
||||||
|
import * as React from 'react';
|
||||||
|
import * as ReactDom from 'react-dom';
|
||||||
|
import { Version } from '@microsoft/sp-core-library';
|
||||||
|
import {
|
||||||
|
BaseClientSideWebPart,
|
||||||
|
IPropertyPaneConfiguration,
|
||||||
|
PropertyPaneTextField,
|
||||||
|
IWebPartContext
|
||||||
|
} from '@microsoft/sp-webpart-base';
|
||||||
|
import { IDigestCache, DigestCache } from '@microsoft/sp-http';
|
||||||
|
import * as strings from 'FileUploadWebPartStrings';
|
||||||
|
import FileUpload from './components/FileUpload';
|
||||||
|
import { IFileUploadProps } from './components/IFileUploadProps';
|
||||||
|
import * as loader from '@microsoft/sp-loader';
|
||||||
|
import { PropertyFieldListPicker, PropertyFieldListPickerOrderBy } from '../../PropertyFieldListPicker';
|
||||||
|
import { PropertyPaneDropdown } from '@microsoft/sp-webpart-base/lib/propertyPane/propertyPaneFields/propertyPaneDropdown/PropertyPaneDropdown';
|
||||||
|
export interface IFileUploadWebPartProps {
|
||||||
|
listName:string;
|
||||||
|
fileTypes:string;
|
||||||
|
queryString:string;
|
||||||
|
uploadFilesTo:string;
|
||||||
|
}
|
||||||
|
require("./filepicker.css");
|
||||||
|
require("./dropzone.css");
|
||||||
|
export default class FileUploadWebPart extends BaseClientSideWebPart<IFileUploadWebPartProps> {
|
||||||
|
public digest:string="";
|
||||||
|
public constructor(context:IWebPartContext){
|
||||||
|
super();
|
||||||
|
loader.SPComponentLoader.loadCss('https://code.ionicframework.com/ionicons/2.0.1/css/ionicons.min.css');
|
||||||
|
}
|
||||||
|
protected onInit(): Promise<void> {
|
||||||
|
return new Promise<void>((resolve: () => void, reject: (error: any) => void): void => {
|
||||||
|
const digestCache: IDigestCache = this.context.serviceScope.consume(DigestCache.serviceKey);
|
||||||
|
digestCache.fetchDigest(this.context.pageContext.web.serverRelativeUrl).then((digest: string): void => {
|
||||||
|
// use the digest here
|
||||||
|
this.digest=digest;
|
||||||
|
resolve();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
public render(): void {
|
||||||
|
const element: React.ReactElement<IFileUploadProps > = React.createElement(
|
||||||
|
FileUpload,
|
||||||
|
{
|
||||||
|
digest:this.digest,
|
||||||
|
context:this.context,
|
||||||
|
listName:this.properties.listName,
|
||||||
|
fileTypes:this.properties.fileTypes,
|
||||||
|
queryString:this.properties.queryString,
|
||||||
|
uploadFilesTo:this.properties.uploadFilesTo
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
ReactDom.render(element, this.domElement);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected get dataVersion(): Version {
|
||||||
|
return Version.parse('1.0');
|
||||||
|
}
|
||||||
|
|
||||||
|
protected getPropertyPaneConfiguration(): IPropertyPaneConfiguration {
|
||||||
|
return {
|
||||||
|
pages: [
|
||||||
|
{
|
||||||
|
header: {
|
||||||
|
description: strings.PropertyPaneDescription
|
||||||
|
},
|
||||||
|
groups: [
|
||||||
|
{
|
||||||
|
groupName: strings.BasicGroupName,
|
||||||
|
groupFields: [
|
||||||
|
PropertyPaneDropdown('uploadFilesTo',{
|
||||||
|
label:'Upload files to',
|
||||||
|
options:[{key:'DocumentLibrary',text:'Document Library'},
|
||||||
|
{key:'List',text:'As item attachments'} ]
|
||||||
|
}),
|
||||||
|
PropertyFieldListPicker('listName', {
|
||||||
|
label: 'Select a list or library',
|
||||||
|
selectedList: this.properties.listName,
|
||||||
|
includeHidden: false,
|
||||||
|
//baseTemplate: 109,
|
||||||
|
orderBy: PropertyFieldListPickerOrderBy.Title,
|
||||||
|
// multiSelect: false,
|
||||||
|
disabled: false,
|
||||||
|
onPropertyChange: this.onPropertyPaneFieldChanged.bind(this),
|
||||||
|
properties: this.properties,
|
||||||
|
context: this.context,
|
||||||
|
onGetErrorMessage: null,
|
||||||
|
deferredValidationTime: 0,
|
||||||
|
key: 'listPickerFieldId'
|
||||||
|
}),
|
||||||
|
PropertyPaneTextField('fileTypes',{
|
||||||
|
label:'File Types (use , as seperator)',
|
||||||
|
}),
|
||||||
|
PropertyPaneTextField('queryString',{
|
||||||
|
label:'Query String parameter',
|
||||||
|
description:'If you want to attach files to a list item you need to define the ID of the item in a query string parameter, example: ID=1'
|
||||||
|
})
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,74 @@
|
||||||
|
@import '~@microsoft/sp-office-ui-fabric-core/dist/sass/SPFabricCore.scss';
|
||||||
|
|
||||||
|
.fileUpload {
|
||||||
|
.container {
|
||||||
|
max-width: 700px;
|
||||||
|
margin: 0px auto;
|
||||||
|
box-shadow: 0 2px 4px 0 rgba(0, 0, 0, 0.2), 0 25px 50px 0 rgba(0, 0, 0, 0.1);
|
||||||
|
}
|
||||||
|
|
||||||
|
.row {
|
||||||
|
@include ms-Grid-row;
|
||||||
|
@include ms-fontColor-white;
|
||||||
|
background-color: $ms-color-themeDark;
|
||||||
|
padding: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.column {
|
||||||
|
@include ms-Grid-col;
|
||||||
|
@include ms-lg10;
|
||||||
|
@include ms-xl8;
|
||||||
|
@include ms-xlPush2;
|
||||||
|
@include ms-lgPush1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.title {
|
||||||
|
@include ms-font-xl;
|
||||||
|
@include ms-fontColor-white;
|
||||||
|
}
|
||||||
|
|
||||||
|
.subTitle {
|
||||||
|
@include ms-font-l;
|
||||||
|
@include ms-fontColor-white;
|
||||||
|
}
|
||||||
|
|
||||||
|
.description {
|
||||||
|
@include ms-font-l;
|
||||||
|
@include ms-fontColor-white;
|
||||||
|
}
|
||||||
|
|
||||||
|
.button {
|
||||||
|
// Our button
|
||||||
|
text-decoration: none;
|
||||||
|
height: 32px;
|
||||||
|
|
||||||
|
// Primary Button
|
||||||
|
min-width: 80px;
|
||||||
|
background-color: $ms-color-themePrimary;
|
||||||
|
border-color: $ms-color-themePrimary;
|
||||||
|
color: $ms-color-white;
|
||||||
|
|
||||||
|
// Basic Button
|
||||||
|
outline: transparent;
|
||||||
|
position: relative;
|
||||||
|
font-family: "Segoe UI WestEuropean","Segoe UI",-apple-system,BlinkMacSystemFont,Roboto,"Helvetica Neue",sans-serif;
|
||||||
|
-webkit-font-smoothing: antialiased;
|
||||||
|
font-size: $ms-font-size-m;
|
||||||
|
font-weight: $ms-font-weight-regular;
|
||||||
|
border-width: 0;
|
||||||
|
text-align: center;
|
||||||
|
cursor: pointer;
|
||||||
|
display: inline-block;
|
||||||
|
padding: 0 16px;
|
||||||
|
|
||||||
|
.label {
|
||||||
|
font-weight: $ms-font-weight-semibold;
|
||||||
|
font-size: $ms-font-size-m;
|
||||||
|
height: 32px;
|
||||||
|
line-height: 32px;
|
||||||
|
margin: 0 4px;
|
||||||
|
vertical-align: top;
|
||||||
|
display: inline-block;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,17 @@
|
||||||
|
/* tslint:disable */
|
||||||
|
require('./FileUpload.module.css');
|
||||||
|
const styles = {
|
||||||
|
fileUpload: 'fileUpload_207465a2',
|
||||||
|
container: 'container_207465a2',
|
||||||
|
row: 'row_207465a2',
|
||||||
|
column: 'column_207465a2',
|
||||||
|
'ms-Grid': 'ms-Grid_207465a2',
|
||||||
|
title: 'title_207465a2',
|
||||||
|
subTitle: 'subTitle_207465a2',
|
||||||
|
description: 'description_207465a2',
|
||||||
|
button: 'button_207465a2',
|
||||||
|
label: 'label_207465a2',
|
||||||
|
};
|
||||||
|
|
||||||
|
export default styles;
|
||||||
|
/* tslint:enable */
|
|
@ -0,0 +1,83 @@
|
||||||
|
import * as React from 'react';
|
||||||
|
import styles from './FileUpload.module.scss';
|
||||||
|
import { IFileUploadProps } from './IFileUploadProps';
|
||||||
|
import { escape } from '@microsoft/sp-lodash-subset';
|
||||||
|
import { Log,UrlQueryParameterCollection } from '@microsoft/sp-core-library';
|
||||||
|
import DropzoneComponent from 'react-dropzone-component';
|
||||||
|
import pnp,{Web} from 'sp-pnp-js';
|
||||||
|
export default class FileUpload extends React.Component<IFileUploadProps, {}> {
|
||||||
|
constructor(props: IFileUploadProps){
|
||||||
|
super(props);
|
||||||
|
}
|
||||||
|
public render(): React.ReactElement<IFileUploadProps> {
|
||||||
|
let _context = this.props.context;
|
||||||
|
let _listName = this.props.listName;
|
||||||
|
let _fileUploadTo=this.props.uploadFilesTo;
|
||||||
|
let _queryStringParam = this.props.queryString;
|
||||||
|
let queryParameters = new UrlQueryParameterCollection(window.location.href);
|
||||||
|
let _itemId = queryParameters.getValue(_queryStringParam);
|
||||||
|
let _parent = this;
|
||||||
|
let componentConfig = {
|
||||||
|
iconFiletypes: this.props.fileTypes.split(','),
|
||||||
|
showFiletypeIcon: true,
|
||||||
|
postUrl: _context.pageContext.web.absoluteUrl
|
||||||
|
};
|
||||||
|
let myDropzone;
|
||||||
|
let eventHandlers = {
|
||||||
|
// This one receives the dropzone object as the first parameter
|
||||||
|
// and can be used to additional work with the dropzone.js
|
||||||
|
// object
|
||||||
|
init: function(dz){
|
||||||
|
myDropzone=dz;
|
||||||
|
},
|
||||||
|
removedfile: function(file){
|
||||||
|
let web:Web=new Web(_context.pageContext.web.absoluteUrl);
|
||||||
|
if(_fileUploadTo=="DocumentLibrary"){
|
||||||
|
web.lists.getById(_listName).rootFolder.files.getByName(file.name).delete().then(t=>{
|
||||||
|
//add your code here if you want to do more after deleting the file
|
||||||
|
});
|
||||||
|
}
|
||||||
|
else{
|
||||||
|
web.lists.getById(_listName).items.getById(Number(_itemId)).attachmentFiles.deleteMultiple(file.name).then(t=>{
|
||||||
|
//add your code here if you want to do more after deleting the file
|
||||||
|
});
|
||||||
|
}
|
||||||
|
},
|
||||||
|
processing: function (file, xhr) {
|
||||||
|
|
||||||
|
if(_fileUploadTo=="DocumentLibrary")
|
||||||
|
myDropzone.options.url = `${_context.pageContext.web.absoluteUrl}/_api/web/Lists/getById('${_listName}')/rootfolder/files/add(overwrite=true,url='${file.name}')`;
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if(_itemId)
|
||||||
|
myDropzone.options.url = `${_context.pageContext.web.absoluteUrl}/_api/web/lists/getById('${_listName}')/items(${_itemId})/AttachmentFiles/add(FileName='${file.name}')`;
|
||||||
|
else
|
||||||
|
alert('Item not found or query string value is null!')
|
||||||
|
}
|
||||||
|
},
|
||||||
|
sending: function (file, xhr) {
|
||||||
|
let _send = xhr.send;
|
||||||
|
xhr.send = function () {
|
||||||
|
_send.call(xhr, file);
|
||||||
|
};
|
||||||
|
},
|
||||||
|
error:function(file,error,xhr){
|
||||||
|
if(_fileUploadTo!="DocumentLibrary")
|
||||||
|
alert(`File '${file.name}' is already exists, please rename your file or select another file.`);
|
||||||
|
//if(myDropzone)
|
||||||
|
// myDropzone.removeFile(file);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
var djsConfig = {
|
||||||
|
headers: {
|
||||||
|
"X-RequestDigest": this.props.digest
|
||||||
|
},
|
||||||
|
addRemoveLinks:true
|
||||||
|
};
|
||||||
|
return (
|
||||||
|
<DropzoneComponent eventHandlers={eventHandlers} djsConfig={djsConfig} config={componentConfig}>
|
||||||
|
<div className="dz-message icon ion-upload">Drop files here or click to upload.</div>
|
||||||
|
</DropzoneComponent>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,11 @@
|
||||||
|
import {
|
||||||
|
IWebPartContext
|
||||||
|
} from '@microsoft/sp-webpart-base';
|
||||||
|
export interface IFileUploadProps {
|
||||||
|
digest:string;
|
||||||
|
context:IWebPartContext;
|
||||||
|
listName:string;
|
||||||
|
fileTypes:string;
|
||||||
|
queryString:string;
|
||||||
|
uploadFilesTo:string;
|
||||||
|
}
|
|
@ -0,0 +1,389 @@
|
||||||
|
/*
|
||||||
|
* The MIT License
|
||||||
|
* Copyright (c) 2012 Matias Meno <m@tias.me>
|
||||||
|
*/
|
||||||
|
@-webkit-keyframes passing-through {
|
||||||
|
0% {
|
||||||
|
opacity: 0;
|
||||||
|
-webkit-transform: translateY(40px);
|
||||||
|
-moz-transform: translateY(40px);
|
||||||
|
-ms-transform: translateY(40px);
|
||||||
|
-o-transform: translateY(40px);
|
||||||
|
transform: translateY(40px); }
|
||||||
|
30%, 70% {
|
||||||
|
opacity: 1;
|
||||||
|
-webkit-transform: translateY(0px);
|
||||||
|
-moz-transform: translateY(0px);
|
||||||
|
-ms-transform: translateY(0px);
|
||||||
|
-o-transform: translateY(0px);
|
||||||
|
transform: translateY(0px); }
|
||||||
|
100% {
|
||||||
|
opacity: 0;
|
||||||
|
-webkit-transform: translateY(-40px);
|
||||||
|
-moz-transform: translateY(-40px);
|
||||||
|
-ms-transform: translateY(-40px);
|
||||||
|
-o-transform: translateY(-40px);
|
||||||
|
transform: translateY(-40px); } }
|
||||||
|
@-moz-keyframes passing-through {
|
||||||
|
0% {
|
||||||
|
opacity: 0;
|
||||||
|
-webkit-transform: translateY(40px);
|
||||||
|
-moz-transform: translateY(40px);
|
||||||
|
-ms-transform: translateY(40px);
|
||||||
|
-o-transform: translateY(40px);
|
||||||
|
transform: translateY(40px); }
|
||||||
|
30%, 70% {
|
||||||
|
opacity: 1;
|
||||||
|
-webkit-transform: translateY(0px);
|
||||||
|
-moz-transform: translateY(0px);
|
||||||
|
-ms-transform: translateY(0px);
|
||||||
|
-o-transform: translateY(0px);
|
||||||
|
transform: translateY(0px); }
|
||||||
|
100% {
|
||||||
|
opacity: 0;
|
||||||
|
-webkit-transform: translateY(-40px);
|
||||||
|
-moz-transform: translateY(-40px);
|
||||||
|
-ms-transform: translateY(-40px);
|
||||||
|
-o-transform: translateY(-40px);
|
||||||
|
transform: translateY(-40px); } }
|
||||||
|
@keyframes passing-through {
|
||||||
|
0% {
|
||||||
|
opacity: 0;
|
||||||
|
-webkit-transform: translateY(40px);
|
||||||
|
-moz-transform: translateY(40px);
|
||||||
|
-ms-transform: translateY(40px);
|
||||||
|
-o-transform: translateY(40px);
|
||||||
|
transform: translateY(40px); }
|
||||||
|
30%, 70% {
|
||||||
|
opacity: 1;
|
||||||
|
-webkit-transform: translateY(0px);
|
||||||
|
-moz-transform: translateY(0px);
|
||||||
|
-ms-transform: translateY(0px);
|
||||||
|
-o-transform: translateY(0px);
|
||||||
|
transform: translateY(0px); }
|
||||||
|
100% {
|
||||||
|
opacity: 0;
|
||||||
|
-webkit-transform: translateY(-40px);
|
||||||
|
-moz-transform: translateY(-40px);
|
||||||
|
-ms-transform: translateY(-40px);
|
||||||
|
-o-transform: translateY(-40px);
|
||||||
|
transform: translateY(-40px); } }
|
||||||
|
@-webkit-keyframes slide-in {
|
||||||
|
0% {
|
||||||
|
opacity: 0;
|
||||||
|
-webkit-transform: translateY(40px);
|
||||||
|
-moz-transform: translateY(40px);
|
||||||
|
-ms-transform: translateY(40px);
|
||||||
|
-o-transform: translateY(40px);
|
||||||
|
transform: translateY(40px); }
|
||||||
|
30% {
|
||||||
|
opacity: 1;
|
||||||
|
-webkit-transform: translateY(0px);
|
||||||
|
-moz-transform: translateY(0px);
|
||||||
|
-ms-transform: translateY(0px);
|
||||||
|
-o-transform: translateY(0px);
|
||||||
|
transform: translateY(0px); } }
|
||||||
|
@-moz-keyframes slide-in {
|
||||||
|
0% {
|
||||||
|
opacity: 0;
|
||||||
|
-webkit-transform: translateY(40px);
|
||||||
|
-moz-transform: translateY(40px);
|
||||||
|
-ms-transform: translateY(40px);
|
||||||
|
-o-transform: translateY(40px);
|
||||||
|
transform: translateY(40px); }
|
||||||
|
30% {
|
||||||
|
opacity: 1;
|
||||||
|
-webkit-transform: translateY(0px);
|
||||||
|
-moz-transform: translateY(0px);
|
||||||
|
-ms-transform: translateY(0px);
|
||||||
|
-o-transform: translateY(0px);
|
||||||
|
transform: translateY(0px); } }
|
||||||
|
@keyframes slide-in {
|
||||||
|
0% {
|
||||||
|
opacity: 0;
|
||||||
|
-webkit-transform: translateY(40px);
|
||||||
|
-moz-transform: translateY(40px);
|
||||||
|
-ms-transform: translateY(40px);
|
||||||
|
-o-transform: translateY(40px);
|
||||||
|
transform: translateY(40px); }
|
||||||
|
30% {
|
||||||
|
opacity: 1;
|
||||||
|
-webkit-transform: translateY(0px);
|
||||||
|
-moz-transform: translateY(0px);
|
||||||
|
-ms-transform: translateY(0px);
|
||||||
|
-o-transform: translateY(0px);
|
||||||
|
transform: translateY(0px); } }
|
||||||
|
@-webkit-keyframes pulse {
|
||||||
|
0% {
|
||||||
|
-webkit-transform: scale(1);
|
||||||
|
-moz-transform: scale(1);
|
||||||
|
-ms-transform: scale(1);
|
||||||
|
-o-transform: scale(1);
|
||||||
|
transform: scale(1); }
|
||||||
|
10% {
|
||||||
|
-webkit-transform: scale(1.1);
|
||||||
|
-moz-transform: scale(1.1);
|
||||||
|
-ms-transform: scale(1.1);
|
||||||
|
-o-transform: scale(1.1);
|
||||||
|
transform: scale(1.1); }
|
||||||
|
20% {
|
||||||
|
-webkit-transform: scale(1);
|
||||||
|
-moz-transform: scale(1);
|
||||||
|
-ms-transform: scale(1);
|
||||||
|
-o-transform: scale(1);
|
||||||
|
transform: scale(1); } }
|
||||||
|
@-moz-keyframes pulse {
|
||||||
|
0% {
|
||||||
|
-webkit-transform: scale(1);
|
||||||
|
-moz-transform: scale(1);
|
||||||
|
-ms-transform: scale(1);
|
||||||
|
-o-transform: scale(1);
|
||||||
|
transform: scale(1); }
|
||||||
|
10% {
|
||||||
|
-webkit-transform: scale(1.1);
|
||||||
|
-moz-transform: scale(1.1);
|
||||||
|
-ms-transform: scale(1.1);
|
||||||
|
-o-transform: scale(1.1);
|
||||||
|
transform: scale(1.1); }
|
||||||
|
20% {
|
||||||
|
-webkit-transform: scale(1);
|
||||||
|
-moz-transform: scale(1);
|
||||||
|
-ms-transform: scale(1);
|
||||||
|
-o-transform: scale(1);
|
||||||
|
transform: scale(1); } }
|
||||||
|
@keyframes pulse {
|
||||||
|
0% {
|
||||||
|
-webkit-transform: scale(1);
|
||||||
|
-moz-transform: scale(1);
|
||||||
|
-ms-transform: scale(1);
|
||||||
|
-o-transform: scale(1);
|
||||||
|
transform: scale(1); }
|
||||||
|
10% {
|
||||||
|
-webkit-transform: scale(1.1);
|
||||||
|
-moz-transform: scale(1.1);
|
||||||
|
-ms-transform: scale(1.1);
|
||||||
|
-o-transform: scale(1.1);
|
||||||
|
transform: scale(1.1); }
|
||||||
|
20% {
|
||||||
|
-webkit-transform: scale(1);
|
||||||
|
-moz-transform: scale(1);
|
||||||
|
-ms-transform: scale(1);
|
||||||
|
-o-transform: scale(1);
|
||||||
|
transform: scale(1); } }
|
||||||
|
.dropzone, .dropzone * {
|
||||||
|
box-sizing: border-box; }
|
||||||
|
|
||||||
|
.dropzone {
|
||||||
|
min-height: 150px;
|
||||||
|
border: 2px solid rgba(0, 0, 0, 0.3);
|
||||||
|
padding: 20px 20px; }
|
||||||
|
.dropzone.dz-clickable {
|
||||||
|
cursor: pointer; }
|
||||||
|
.dropzone.dz-clickable * {
|
||||||
|
cursor: default; }
|
||||||
|
.dropzone.dz-clickable .dz-message, .dropzone.dz-clickable .dz-message * {
|
||||||
|
cursor: pointer; }
|
||||||
|
.dropzone.dz-started .dz-message {
|
||||||
|
display: none; }
|
||||||
|
.dropzone.dz-drag-hover {
|
||||||
|
border-style: solid; }
|
||||||
|
.dropzone.dz-drag-hover .dz-message {
|
||||||
|
opacity: 0.5; }
|
||||||
|
.dropzone .dz-message {
|
||||||
|
text-align: center;
|
||||||
|
margin: 2em 0;
|
||||||
|
font-size: 22px;
|
||||||
|
}
|
||||||
|
.dropzone .dz-preview {
|
||||||
|
position: relative;
|
||||||
|
display: inline-block;
|
||||||
|
vertical-align: top;
|
||||||
|
margin: 16px;
|
||||||
|
min-height: 100px; }
|
||||||
|
.dropzone .dz-preview:hover {
|
||||||
|
z-index: 1000; }
|
||||||
|
.dropzone .dz-preview:hover .dz-details {
|
||||||
|
opacity: 1; }
|
||||||
|
.dropzone .dz-preview.dz-file-preview .dz-image {
|
||||||
|
border-radius: 20px;
|
||||||
|
background: #999;
|
||||||
|
background: linear-gradient(to bottom, #eee, #ddd); }
|
||||||
|
.dropzone .dz-preview.dz-file-preview .dz-details {
|
||||||
|
opacity: 1; }
|
||||||
|
.dropzone .dz-preview.dz-image-preview {
|
||||||
|
}
|
||||||
|
.dropzone .dz-preview.dz-image-preview .dz-details {
|
||||||
|
-webkit-transition: opacity 0.2s linear;
|
||||||
|
-moz-transition: opacity 0.2s linear;
|
||||||
|
-ms-transition: opacity 0.2s linear;
|
||||||
|
-o-transition: opacity 0.2s linear;
|
||||||
|
transition: opacity 0.2s linear; }
|
||||||
|
.dropzone .dz-preview .dz-remove {
|
||||||
|
font-size: 14px;
|
||||||
|
text-align: center;
|
||||||
|
display: block;
|
||||||
|
cursor: pointer;
|
||||||
|
border: none; }
|
||||||
|
.dropzone .dz-preview .dz-remove:hover {
|
||||||
|
text-decoration: underline; }
|
||||||
|
.dropzone .dz-preview:hover .dz-details {
|
||||||
|
opacity: 1; }
|
||||||
|
.dropzone .dz-preview .dz-details {
|
||||||
|
z-index: 20;
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
opacity: 0;
|
||||||
|
font-size: 13px;
|
||||||
|
min-width: 100%;
|
||||||
|
max-width: 100%;
|
||||||
|
padding: 2em 1em;
|
||||||
|
text-align: center;
|
||||||
|
color: rgba(0, 0, 0, 0.9);
|
||||||
|
line-height: 150%; }
|
||||||
|
.dropzone .dz-preview .dz-details .dz-size {
|
||||||
|
margin-bottom: 1em;
|
||||||
|
font-size: 16px; }
|
||||||
|
.dropzone .dz-preview .dz-details .dz-filename {
|
||||||
|
white-space: nowrap; }
|
||||||
|
.dropzone .dz-preview .dz-details .dz-filename:hover span {
|
||||||
|
border: 1px solid rgba(200, 200, 200, 0.8);
|
||||||
|
background-color: rgba(255, 255, 255, 0.8); }
|
||||||
|
.dropzone .dz-preview .dz-details .dz-filename:not(:hover) {
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis; }
|
||||||
|
.dropzone .dz-preview .dz-details .dz-filename:not(:hover) span {
|
||||||
|
border: 1px solid transparent; }
|
||||||
|
.dropzone .dz-preview .dz-details .dz-filename span, .dropzone .dz-preview .dz-details .dz-size span {
|
||||||
|
background-color: rgba(255, 255, 255, 0.4);
|
||||||
|
padding: 0 0.4em;
|
||||||
|
border-radius: 3px; }
|
||||||
|
.dropzone .dz-preview:hover .dz-image img {
|
||||||
|
-webkit-transform: scale(1.05, 1.05);
|
||||||
|
-moz-transform: scale(1.05, 1.05);
|
||||||
|
-ms-transform: scale(1.05, 1.05);
|
||||||
|
-o-transform: scale(1.05, 1.05);
|
||||||
|
transform: scale(1.05, 1.05);
|
||||||
|
-webkit-filter: blur(8px);
|
||||||
|
filter: blur(8px); }
|
||||||
|
.dropzone .dz-preview .dz-image {
|
||||||
|
border-radius: 20px;
|
||||||
|
overflow: hidden;
|
||||||
|
width: 120px;
|
||||||
|
height: 120px;
|
||||||
|
position: relative;
|
||||||
|
display: block;
|
||||||
|
z-index: 10; }
|
||||||
|
.dropzone .dz-preview .dz-image img {
|
||||||
|
display: block; }
|
||||||
|
.dropzone .dz-preview.dz-success .dz-success-mark {
|
||||||
|
-webkit-animation: passing-through 3s cubic-bezier(0.77, 0, 0.175, 1);
|
||||||
|
-moz-animation: passing-through 3s cubic-bezier(0.77, 0, 0.175, 1);
|
||||||
|
-ms-animation: passing-through 3s cubic-bezier(0.77, 0, 0.175, 1);
|
||||||
|
-o-animation: passing-through 3s cubic-bezier(0.77, 0, 0.175, 1);
|
||||||
|
animation: passing-through 3s cubic-bezier(0.77, 0, 0.175, 1); }
|
||||||
|
.dropzone .dz-preview.dz-error .dz-error-mark {
|
||||||
|
opacity: 1;
|
||||||
|
-webkit-animation: slide-in 3s cubic-bezier(0.77, 0, 0.175, 1);
|
||||||
|
-moz-animation: slide-in 3s cubic-bezier(0.77, 0, 0.175, 1);
|
||||||
|
-ms-animation: slide-in 3s cubic-bezier(0.77, 0, 0.175, 1);
|
||||||
|
-o-animation: slide-in 3s cubic-bezier(0.77, 0, 0.175, 1);
|
||||||
|
animation: slide-in 3s cubic-bezier(0.77, 0, 0.175, 1); }
|
||||||
|
.dropzone .dz-preview .dz-success-mark, .dropzone .dz-preview .dz-error-mark {
|
||||||
|
pointer-events: none;
|
||||||
|
opacity: 0;
|
||||||
|
z-index: 500;
|
||||||
|
position: absolute;
|
||||||
|
display: block;
|
||||||
|
top: 50%;
|
||||||
|
left: 50%;
|
||||||
|
margin-left: -27px;
|
||||||
|
margin-top: -27px; }
|
||||||
|
.dropzone .dz-preview .dz-success-mark svg, .dropzone .dz-preview .dz-error-mark svg {
|
||||||
|
display: block;
|
||||||
|
width: 54px;
|
||||||
|
height: 54px; }
|
||||||
|
.dropzone .dz-preview.dz-processing .dz-progress {
|
||||||
|
opacity: 1;
|
||||||
|
-webkit-transition: all 0.2s linear;
|
||||||
|
-moz-transition: all 0.2s linear;
|
||||||
|
-ms-transition: all 0.2s linear;
|
||||||
|
-o-transition: all 0.2s linear;
|
||||||
|
transition: all 0.2s linear; }
|
||||||
|
.dropzone .dz-preview.dz-complete .dz-progress {
|
||||||
|
opacity: 0;
|
||||||
|
-webkit-transition: opacity 0.4s ease-in;
|
||||||
|
-moz-transition: opacity 0.4s ease-in;
|
||||||
|
-ms-transition: opacity 0.4s ease-in;
|
||||||
|
-o-transition: opacity 0.4s ease-in;
|
||||||
|
transition: opacity 0.4s ease-in; }
|
||||||
|
.dropzone .dz-preview:not(.dz-processing) .dz-progress {
|
||||||
|
-webkit-animation: pulse 6s ease infinite;
|
||||||
|
-moz-animation: pulse 6s ease infinite;
|
||||||
|
-ms-animation: pulse 6s ease infinite;
|
||||||
|
-o-animation: pulse 6s ease infinite;
|
||||||
|
animation: pulse 6s ease infinite; }
|
||||||
|
.dropzone .dz-preview .dz-progress {
|
||||||
|
opacity: 1;
|
||||||
|
z-index: 1000;
|
||||||
|
pointer-events: none;
|
||||||
|
position: absolute;
|
||||||
|
height: 16px;
|
||||||
|
left: 50%;
|
||||||
|
top: 50%;
|
||||||
|
margin-top: -8px;
|
||||||
|
width: 80px;
|
||||||
|
margin-left: -40px;
|
||||||
|
background: rgba(255, 255, 255, 0.9);
|
||||||
|
-webkit-transform: scale(1);
|
||||||
|
border-radius: 8px;
|
||||||
|
overflow: hidden; }
|
||||||
|
.dropzone .dz-preview .dz-progress .dz-upload {
|
||||||
|
background: #333;
|
||||||
|
background: linear-gradient(to bottom, #666, #444);
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
bottom: 0;
|
||||||
|
width: 0;
|
||||||
|
-webkit-transition: width 300ms ease-in-out;
|
||||||
|
-moz-transition: width 300ms ease-in-out;
|
||||||
|
-ms-transition: width 300ms ease-in-out;
|
||||||
|
-o-transition: width 300ms ease-in-out;
|
||||||
|
transition: width 300ms ease-in-out; }
|
||||||
|
.dropzone .dz-preview.dz-error .dz-error-message {
|
||||||
|
display: block; }
|
||||||
|
.dropzone .dz-preview.dz-error:hover .dz-error-message {
|
||||||
|
opacity: 1;
|
||||||
|
pointer-events: auto; }
|
||||||
|
.dropzone .dz-preview .dz-error-message {
|
||||||
|
pointer-events: none;
|
||||||
|
z-index: 1000;
|
||||||
|
position: absolute;
|
||||||
|
display: block;
|
||||||
|
display: none;
|
||||||
|
opacity: 0;
|
||||||
|
-webkit-transition: opacity 0.3s ease;
|
||||||
|
-moz-transition: opacity 0.3s ease;
|
||||||
|
-ms-transition: opacity 0.3s ease;
|
||||||
|
-o-transition: opacity 0.3s ease;
|
||||||
|
transition: opacity 0.3s ease;
|
||||||
|
border-radius: 8px;
|
||||||
|
font-size: 13px;
|
||||||
|
top: 130px;
|
||||||
|
left: -10px;
|
||||||
|
width: 140px;
|
||||||
|
background: #be2626;
|
||||||
|
background: linear-gradient(to bottom, #be2626, #a92222);
|
||||||
|
padding: 0.5em 1.2em;
|
||||||
|
color: white; }
|
||||||
|
.dropzone .dz-preview .dz-error-message:after {
|
||||||
|
content: '';
|
||||||
|
position: absolute;
|
||||||
|
top: -6px;
|
||||||
|
left: 64px;
|
||||||
|
width: 0;
|
||||||
|
height: 0;
|
||||||
|
border-left: 6px solid transparent;
|
||||||
|
border-right: 6px solid transparent;
|
||||||
|
border-bottom: 6px solid #be2626; }
|
|
@ -0,0 +1,74 @@
|
||||||
|
/* Filepicker CSS */
|
||||||
|
.filepicker {
|
||||||
|
font-family: sans-serif;
|
||||||
|
}
|
||||||
|
|
||||||
|
div.filepicker {
|
||||||
|
text-align: center;
|
||||||
|
padding: 5px;
|
||||||
|
background-color: #fff;
|
||||||
|
border-radius: 5px;
|
||||||
|
height: 200px;
|
||||||
|
min-height: 60px;
|
||||||
|
border: 2px dashed #7092BE;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Icon */
|
||||||
|
.filepicker-file-icon
|
||||||
|
{
|
||||||
|
position: relative;
|
||||||
|
|
||||||
|
display: inline-block;
|
||||||
|
|
||||||
|
margin: 1.5em 0 2.5em 0;
|
||||||
|
padding-left: 45px;
|
||||||
|
|
||||||
|
color: black;
|
||||||
|
}
|
||||||
|
.filepicker-file-icon::before
|
||||||
|
{
|
||||||
|
position: absolute;
|
||||||
|
top: -7px;
|
||||||
|
left: 0;
|
||||||
|
|
||||||
|
width: 29px;
|
||||||
|
height: 34px;
|
||||||
|
|
||||||
|
content: '';
|
||||||
|
|
||||||
|
border: solid 2px #7F7F7F;
|
||||||
|
border-radius: 2px;
|
||||||
|
}
|
||||||
|
.filepicker-file-icon::after
|
||||||
|
{
|
||||||
|
font-size: 11px;
|
||||||
|
line-height: 1.3;
|
||||||
|
|
||||||
|
position: absolute;
|
||||||
|
top: 9px;
|
||||||
|
left: -4px;
|
||||||
|
|
||||||
|
padding: 0 2px;
|
||||||
|
|
||||||
|
content: 'file';
|
||||||
|
content: attr(data-filetype);
|
||||||
|
text-align: right;
|
||||||
|
letter-spacing: 1px;
|
||||||
|
text-transform: uppercase;
|
||||||
|
|
||||||
|
color: #fff;
|
||||||
|
background-color: #000;
|
||||||
|
}
|
||||||
|
.filepicker-file-icon .fileCorner
|
||||||
|
{
|
||||||
|
position: absolute;
|
||||||
|
top: -7px;
|
||||||
|
left: 22px;
|
||||||
|
|
||||||
|
width: 0;
|
||||||
|
height: 0;
|
||||||
|
|
||||||
|
border-width: 11px 0 0 11px;
|
||||||
|
border-style: solid;
|
||||||
|
border-color: white transparent transparent #920035;
|
||||||
|
}
|
|
@ -0,0 +1,6 @@
|
||||||
|
define([], function() {
|
||||||
|
return {
|
||||||
|
"PropertyPaneDescription": "File Uploader WebPart",
|
||||||
|
"BasicGroupName": "Look and feel"
|
||||||
|
}
|
||||||
|
});
|
|
@ -0,0 +1,10 @@
|
||||||
|
declare interface IFileUploadWebPartStrings {
|
||||||
|
PropertyPaneDescription: string;
|
||||||
|
BasicGroupName: string;
|
||||||
|
DescriptionFieldLabel: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
declare module 'FileUploadWebPartStrings' {
|
||||||
|
const strings: IFileUploadWebPartStrings;
|
||||||
|
export = strings;
|
||||||
|
}
|
|
@ -0,0 +1,25 @@
|
||||||
|
{
|
||||||
|
"compilerOptions": {
|
||||||
|
"target": "es5",
|
||||||
|
"forceConsistentCasingInFileNames": true,
|
||||||
|
"module": "commonjs",
|
||||||
|
"jsx": "react",
|
||||||
|
"declaration": true,
|
||||||
|
"sourceMap": true,
|
||||||
|
"experimentalDecorators": true,
|
||||||
|
"skipLibCheck": true,
|
||||||
|
"typeRoots": [
|
||||||
|
"./node_modules/@types",
|
||||||
|
"./node_modules/@microsoft"
|
||||||
|
],
|
||||||
|
"types": [
|
||||||
|
"es6-promise",
|
||||||
|
"webpack-env"
|
||||||
|
],
|
||||||
|
"lib": [
|
||||||
|
"es5",
|
||||||
|
"dom",
|
||||||
|
"es2015.collection"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue