Merge branch 'pnp:main' into main
|
@ -142,7 +142,7 @@ body:
|
|||
required: true
|
||||
attributes:
|
||||
label: Node.js version
|
||||
placeholder: v10.23.1
|
||||
placeholder: E.g. v10.23.1
|
||||
description: |
|
||||
Which version of Node.js are you using?
|
||||
|
||||
|
|
|
@ -9,12 +9,12 @@ An SPFx web part that displays a Scalable Vector Graphics (SVG) image using prop
|
|||
|
||||
# Compatibility
|
||||
|
||||
![SPFx 1.3.4](https://img.shields.io/badge/SPFx-1.3.4-green.svg)
|
||||
![Node.js v6](https://img.shields.io/badge/Node.js-v6-green.svg)
|
||||
![SPFx 1.13.0](https://img.shields.io/badge/SPFx-1.13.0-green.svg)
|
||||
![Node.js v6](https://img.shields.io/badge/Node.js-v14-green.svg)
|
||||
![Compatible with SharePoint Online](https://img.shields.io/badge/SharePoint%20Online-Compatible-green.svg)
|
||||
![Compatible with SharePoint 2019](https://img.shields.io/badge/SharePoint%20Server%202019-Incompatible-red.svg)
|
||||
![Does not work with SharePoint 2016 (Feature Pack 2)](https://img.shields.io/badge/SharePoint%20Server%202016%20(Feature%20Pack%202)-Incompatible-red.svg "SharePoint Server 2016 Feature Pack 2 requires SPFx 1.1")
|
||||
![Local Workbench Compatible](https://img.shields.io/badge/Local%20Workbench-Compatible-green.svg)
|
||||
![Local Workbench Incompatible](https://img.shields.io/badge/Local%20Workbench-Incompatible-red.svg)
|
||||
![Hosted Workbench Compatible](https://img.shields.io/badge/Hosted%20Workbench-Compatible-green.svg)
|
||||
|
||||
## Which PnP SPFx controls are being used in this sample?
|
||||
|
@ -38,6 +38,7 @@ js-propertycontrols-svg | Chris Kent ([thechriskent.com](https://thechriskent.co
|
|||
|
||||
Version|Date|Comments
|
||||
-------|----|--------
|
||||
1.1|December 30, 2021|Update to SPFx 1.13.0
|
||||
1.0|November 12, 2017|Initial release
|
||||
|
||||
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
{
|
||||
"$schema": "https://dev.office.com/json-schemas/spfx-build/config.2.0.schema.json",
|
||||
"$schema": "https://developer.microsoft.com/json-schemas/spfx-build/config.2.0.schema.json",
|
||||
"version": "2.0",
|
||||
"bundles": {
|
||||
"svg-hero-web-part": {
|
||||
|
|
|
@ -1,10 +1,6 @@
|
|||
{
|
||||
"$schema": "https://dev.office.com/json-schemas/core-build/serve.schema.json",
|
||||
"$schema": "https://developer.microsoft.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/"
|
||||
}
|
||||
}
|
||||
"initialPage": "https://enter-your-SharePoint-site/_layouts/workbench.aspx"
|
||||
}
|
|
@ -1,45 +0,0 @@
|
|||
{
|
||||
"$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
|
||||
}
|
||||
}
|
||||
}
|
|
@ -2,5 +2,15 @@
|
|||
|
||||
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.`);
|
||||
|
||||
var getTasks = build.rig.getTasks;
|
||||
build.rig.getTasks = function () {
|
||||
var result = getTasks.call(build.rig);
|
||||
|
||||
result.set('serve', result.get('serve-deprecated'));
|
||||
|
||||
return result;
|
||||
};
|
||||
|
||||
build.initialize(gulp);
|
||||
|
|
|
@ -11,20 +11,22 @@
|
|||
"test": "gulp test"
|
||||
},
|
||||
"dependencies": {
|
||||
"@microsoft/sp-core-library": "~1.3.4",
|
||||
"@microsoft/sp-lodash-subset": "~1.3.4",
|
||||
"@microsoft/sp-office-ui-fabric-core": "~1.3.4",
|
||||
"@microsoft/sp-webpart-base": "~1.3.4",
|
||||
"@pnp/spfx-property-controls": "1.0.0",
|
||||
"@types/webpack-env": ">=1.12.1 <1.14.0"
|
||||
"@microsoft/sp-core-library": "1.13.0",
|
||||
"@microsoft/sp-lodash-subset": "1.13.0",
|
||||
"@microsoft/sp-office-ui-fabric-core": "1.13.0",
|
||||
"@microsoft/sp-webpart-base": "1.13.0",
|
||||
"@pnp/spfx-property-controls": "3.3.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@microsoft/sp-build-web": "~1.3.4",
|
||||
"@microsoft/sp-module-interfaces": "~1.3.4",
|
||||
"@microsoft/sp-webpart-workbench": "~1.3.4",
|
||||
"gulp": "~3.9.1",
|
||||
"@types/chai": ">=3.4.34 <3.6.0",
|
||||
"@types/mocha": ">=2.2.33 <2.6.0",
|
||||
"ajv": "~5.2.2"
|
||||
"microsoft/rush-stack-compiler-3.7": "0.2.3",
|
||||
"@microsoft/rush-stack-compiler-3.9": "0.4.47",
|
||||
"@microsoft/sp-build-web": "1.13.0",
|
||||
"@microsoft/sp-module-interfaces": "1.13.0",
|
||||
"@microsoft/sp-tslint-rules": "1.13.0",
|
||||
"@types/chai": "4.3.0",
|
||||
"@types/mocha": "9.0.0",
|
||||
"@types/webpack-env": "1.13.0",
|
||||
"ajv": "~5.2.2",
|
||||
"gulp": "4.0.2"
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,98 @@
|
|||
{
|
||||
"bundles": {
|
||||
"svg-hero-web-part": {
|
||||
"dependencies": [
|
||||
{
|
||||
"componentId": "73e1dc6c-8441-42cc-ad47-4bd3659f8a3a",
|
||||
"componentName": "@microsoft/sp-lodash-subset",
|
||||
"componentVersion": "1.13.0",
|
||||
"isDirectDependency": true
|
||||
},
|
||||
{
|
||||
"componentId": "7263c7d0-1d6a-45ec-8d85-d4d1d234171b",
|
||||
"componentName": "@microsoft/sp-core-library",
|
||||
"componentVersion": "1.13.0",
|
||||
"isDirectDependency": true
|
||||
},
|
||||
{
|
||||
"componentId": "01c4df03-e775-48cb-aa14-171ee5199a15",
|
||||
"componentName": "tslib",
|
||||
"componentVersion": "1.10.0",
|
||||
"isDirectDependency": false
|
||||
},
|
||||
{
|
||||
"componentId": "2e09fb9b-13bb-48f2-859f-97d6fff71176",
|
||||
"componentName": "@ms/odsp-core-bundle",
|
||||
"componentVersion": "1.1.192",
|
||||
"isDirectDependency": false
|
||||
},
|
||||
{
|
||||
"componentId": "974a7777-0990-4136-8fa6-95d80114c2e0",
|
||||
"componentName": "@microsoft/sp-webpart-base",
|
||||
"componentVersion": "1.13.0",
|
||||
"isDirectDependency": true
|
||||
},
|
||||
{
|
||||
"componentId": "8217e442-8ed3-41fd-957d-b112e841286a",
|
||||
"componentName": "@ms/sp-telemetry",
|
||||
"componentVersion": "0.19.42",
|
||||
"isDirectDependency": false
|
||||
},
|
||||
{
|
||||
"componentId": "467dc675-7cc5-4709-8aac-78e3b71bd2f6",
|
||||
"componentName": "@microsoft/sp-component-base",
|
||||
"componentVersion": "1.13.0",
|
||||
"isDirectDependency": false
|
||||
},
|
||||
{
|
||||
"componentId": "e40f8203-b39d-425a-a957-714852e33b79",
|
||||
"componentName": "@microsoft/sp-dynamic-data",
|
||||
"componentVersion": "1.13.0",
|
||||
"isDirectDependency": false
|
||||
},
|
||||
{
|
||||
"componentId": "78359e4b-07c2-43c6-8d0b-d060b4d577e8",
|
||||
"componentName": "@microsoft/sp-diagnostics",
|
||||
"componentVersion": "1.13.0",
|
||||
"isDirectDependency": false
|
||||
},
|
||||
{
|
||||
"componentId": "1c4541f7-5c31-41aa-9fa8-fbc9dc14c0a8",
|
||||
"componentName": "@microsoft/sp-page-context",
|
||||
"componentVersion": "1.13.0",
|
||||
"isDirectDependency": false
|
||||
},
|
||||
{
|
||||
"componentId": "229b8d08-79f3-438b-8c21-4613fc877abd",
|
||||
"componentName": "@microsoft/load-themed-styles",
|
||||
"componentVersion": "0.1.2",
|
||||
"isDirectDependency": false
|
||||
},
|
||||
{
|
||||
"componentId": "c07208f0-ea3b-4c1a-9965-ac1b825211a6",
|
||||
"componentName": "@microsoft/sp-http",
|
||||
"componentVersion": "1.13.0",
|
||||
"isDirectDependency": false
|
||||
},
|
||||
{
|
||||
"componentId": "1c6c9123-7aac-41f3-a376-3caea41ed83f",
|
||||
"componentName": "@microsoft/sp-loader",
|
||||
"componentVersion": "1.13.0",
|
||||
"isDirectDependency": false
|
||||
},
|
||||
{
|
||||
"componentId": "0d910c1c-13b9-4e1c-9aa4-b008c5e42d7d",
|
||||
"componentName": "react",
|
||||
"componentVersion": "16.13.1",
|
||||
"isDirectDependency": true
|
||||
},
|
||||
{
|
||||
"componentId": "aa0a46ec-1505-43cd-a44a-93f3a5aa460a",
|
||||
"componentName": "react-dom",
|
||||
"componentVersion": "16.13.1",
|
||||
"isDirectDependency": true
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,16 +1,36 @@
|
|||
{
|
||||
"extends": "./node_modules/@microsoft/rush-stack-compiler-3.9/includes/tsconfig-web.json",
|
||||
"compilerOptions": {
|
||||
"target": "es5",
|
||||
"forceConsistentCasingInFileNames": true,
|
||||
"module": "commonjs",
|
||||
"module": "esnext",
|
||||
"moduleResolution": "node",
|
||||
"jsx": "react",
|
||||
"declaration": true,
|
||||
"sourceMap": true,
|
||||
"experimentalDecorators": true,
|
||||
"skipLibCheck": true,
|
||||
"outDir": "lib",
|
||||
"inlineSources": false,
|
||||
"strictNullChecks": false,
|
||||
"noUnusedLocals": false,
|
||||
"typeRoots": [
|
||||
"./node_modules/@types",
|
||||
"./node_modules/@microsoft"
|
||||
],
|
||||
"types": [
|
||||
"es6-promise",
|
||||
"es6-collections",
|
||||
"webpack-env"
|
||||
],
|
||||
"lib": [
|
||||
"es5",
|
||||
"dom",
|
||||
"es2015.collection",
|
||||
"es2015.promise"
|
||||
]
|
||||
}
|
||||
},
|
||||
"include": [
|
||||
"src/**/*.ts",
|
||||
"src/**/*.tsx"
|
||||
],
|
||||
"exclude": []
|
||||
}
|
||||
|
|
|
@ -1,3 +1,29 @@
|
|||
{
|
||||
"rulesDirectory": "./config"
|
||||
}
|
||||
"extends": "./node_modules/@microsoft/sp-tslint-rules/base-tslint.json",
|
||||
"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-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-with-statement": true,
|
||||
"semicolon": true,
|
||||
"trailing-comma": false,
|
||||
"typedef": false,
|
||||
"typedef-whitespace": false,
|
||||
"use-named-parameter": true,
|
||||
"variable-name": false,
|
||||
"whitespace": false
|
||||
}
|
||||
}
|
|
@ -49,6 +49,7 @@ Version|Date|Comments
|
|||
-------|----|--------
|
||||
1.0.0|March 30, 2021|Initial release
|
||||
1.0.1|September 22, 2021|Added support for multi-language sites
|
||||
1.0.2|December 24, 2021|Fix retrieving fields from SitePages
|
||||
|
||||
## Minimal Path to Awesome
|
||||
|
||||
|
|
|
@ -15,7 +15,7 @@
|
|||
"- Improved support for dates"
|
||||
],
|
||||
"creationDateTime": "2021-03-30",
|
||||
"updateDateTime": "2021-09-22",
|
||||
"updateDateTime": "2021-12-24",
|
||||
"products": [
|
||||
"SharePoint"
|
||||
],
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
"solution": {
|
||||
"name": "Advanced Page Properties",
|
||||
"id": "daae06a2-8599-445c-93c0-3bd739305f56",
|
||||
"version": "1.0.1.0",
|
||||
"version": "1.0.2.0",
|
||||
"includeClientSideAssets": true,
|
||||
"isDomainIsolated": false,
|
||||
"developer": {
|
||||
|
|
|
@ -68,7 +68,7 @@ export default class AdvancedPagePropertiesWebPart extends BaseClientSideWebPart
|
|||
|
||||
private async getPageProperties(): Promise<void> {
|
||||
Log.Write("Getting Site Page fields...");
|
||||
const list = await sp.web.lists.ensureSiteAssetsLibrary();
|
||||
const list = await sp.web.lists.ensureSitePagesLibrary();
|
||||
const fi = await list.fields();
|
||||
|
||||
this.availableProperties = [];
|
||||
|
|
|
@ -42,9 +42,9 @@ const AdvancedPageProperties: React.FunctionComponent<IAdvancedPagePropertiesPro
|
|||
|
||||
// Get the value(s) for the field from the list item itself
|
||||
var allValues: any = {};
|
||||
const siteAssetsList = await sp.web.lists.ensureSitePagesLibrary();
|
||||
const sitePagesList = await sp.web.lists.ensureSitePagesLibrary();
|
||||
if (props.context.pageContext.listItem !== undefined && props.context.pageContext.listItem !== null) {
|
||||
allValues = await siteAssetsList.items.getById(props.context.pageContext.listItem.id).select(...props.selectedProperties).get();
|
||||
allValues = await sitePagesList.items.getById(props.context.pageContext.listItem.id).select(...props.selectedProperties).get();
|
||||
}
|
||||
|
||||
for (let i = 0; i < props.selectedProperties.length; i++) {
|
||||
|
@ -53,7 +53,7 @@ const AdvancedPageProperties: React.FunctionComponent<IAdvancedPagePropertiesPro
|
|||
Log.Write(`Selected Property: ${prop}`);
|
||||
|
||||
// Get field information, in case anything is needed in conjunction with value types
|
||||
const field = await siteAssetsList.fields.getByInternalNameOrTitle(prop)();
|
||||
const field = await sitePagesList.fields.getByInternalNameOrTitle(prop)();
|
||||
|
||||
// Establish the values array
|
||||
var values: any[] = [];
|
||||
|
|
|
@ -1,25 +0,0 @@
|
|||
# EditorConfig helps developers define and maintain consistent
|
||||
# coding styles between different editors and IDEs
|
||||
# editorconfig.org
|
||||
|
||||
root = true
|
||||
|
||||
|
||||
[*]
|
||||
|
||||
# change these settings to your own preference
|
||||
indent_style = space
|
||||
indent_size = 2
|
||||
|
||||
# we recommend you to keep these unchanged
|
||||
end_of_line = lf
|
||||
charset = utf-8
|
||||
trim_trailing_whitespace = true
|
||||
insert_final_newline = true
|
||||
|
||||
[*.md]
|
||||
trim_trailing_whitespace = false
|
||||
|
||||
[{package,bower}.json]
|
||||
indent_style = space
|
||||
indent_size = 2
|
|
@ -9,6 +9,7 @@ node_modules
|
|||
# Build generated files
|
||||
dist
|
||||
lib
|
||||
release
|
||||
solution
|
||||
temp
|
||||
#*.sppkg
|
||||
|
|
|
@ -22,7 +22,7 @@
|
|||
"framework": "react",
|
||||
"plusBeta": true,
|
||||
"isCreatingSolution": true,
|
||||
"version": "1.10.0",
|
||||
"version": "1.12.1",
|
||||
"libraryName": "react-calendar",
|
||||
"libraryId": "3a13208b-3874-4036-9262-4edd22e88187",
|
||||
"packageManager": "npm",
|
||||
|
|
|
@ -143,7 +143,7 @@ Version|Date|Comments
|
|||
1.0.13|October 2, 2021|Fix to make sure Today is always visible and highlighted.
|
||||
1.0.14|October 16, 2021|Resolve unhandled exception that happens clicking on recurrent events
|
||||
1.0.15|November 22, 2021|Fix All Day event issue that makes All Day events date expanded to another day instead of being full day event.
|
||||
|
||||
1.0.16|December 21, 2021|Upgraded to SPFx 1.12.1
|
||||
|
||||
## Minimal Path to Awesome
|
||||
|
||||
|
|
|
@ -9,7 +9,7 @@
|
|||
"This Web Part allows you to manage events in a calendar. Uses a list of existing calendars on any website. The location and name of the list and the dates of the events to be displayed are defined in the properties of the web part."
|
||||
],
|
||||
"created": "2020-12-04",
|
||||
"modified": "2021-11-22",
|
||||
"modified": "2021-12-21",
|
||||
"products": [
|
||||
"SharePoint"
|
||||
],
|
||||
|
@ -20,7 +20,7 @@
|
|||
},
|
||||
{
|
||||
"key": "SPFX-VERSION",
|
||||
"value": "1.10.0"
|
||||
"value": "1.12.1"
|
||||
},
|
||||
{
|
||||
"key": "SPFX-TEAMSTAB",
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
{
|
||||
"$schema": "https://developer.microsoft.com/json-schemas/spfx-build/copy-assets.schema.json",
|
||||
"deployCdnPath": "temp/deploy"
|
||||
"deployCdnPath": "./release/assets/"
|
||||
}
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"$schema": "https://developer.microsoft.com/json-schemas/spfx-build/deploy-azure-storage.schema.json",
|
||||
"workingDir": "./temp/deploy/",
|
||||
"workingDir": "./release/assets/",
|
||||
"account": "<!-- STORAGE ACCOUNT NAME -->",
|
||||
"container": "react-calendar",
|
||||
"accessKey": "<!-- ACCESS KEY -->"
|
||||
|
|
|
@ -13,13 +13,20 @@ const gulpSequence = require('gulp-sequence');
|
|||
|
||||
build.addSuppression(`Warning - [sass] The local CSS class 'ms-Grid' is not camelCase and will not be type-safe.`);
|
||||
|
||||
var getTasks = build.rig.getTasks;
|
||||
build.rig.getTasks = function () {
|
||||
var result = getTasks.call(build.rig);
|
||||
|
||||
result.set('serve', result.get('serve-deprecated'));
|
||||
|
||||
return result;
|
||||
};
|
||||
|
||||
// Create clean distrubution package
|
||||
gulp.task('dist', gulpSequence('clean', 'bundle', 'package-solution'));
|
||||
// Create clean development package
|
||||
gulp.task('dev', gulpSequence('clean', 'bundle', 'package-solution'));
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Webpack Bundle Anlayzer
|
||||
* Reference and gulp task
|
||||
|
|
|
@ -15,35 +15,30 @@
|
|||
"test:watch": "./node_modules/.bin/jest --config ./config/jest.config.json --watchAll"
|
||||
},
|
||||
"dependencies": {
|
||||
"@microsoft/sp-core-library": "1.10.0",
|
||||
"@microsoft/sp-lodash-subset": "1.10.0",
|
||||
"@microsoft/sp-office-ui-fabric-core": "1.10.0",
|
||||
"@microsoft/sp-property-pane": "1.10.0",
|
||||
"@microsoft/sp-webpart-base": "1.10.0",
|
||||
"@microsoft/sp-core-library": "1.12.1",
|
||||
"@microsoft/sp-lodash-subset": "1.12.1",
|
||||
"@microsoft/sp-office-ui-fabric-core": "1.12.1",
|
||||
"@microsoft/sp-property-pane": "1.12.1",
|
||||
"@microsoft/sp-webpart-base": "1.12.1",
|
||||
"@pnp/pnpjs": "^1.3.0",
|
||||
"@pnp/spfx-controls-react": "1.12.0",
|
||||
"@pnp/spfx-property-controls": "1.14.1",
|
||||
"@types/draft-js": "^0.10.30",
|
||||
"@types/es6-promise": "0.0.33",
|
||||
"@types/globalize": "0.0.34",
|
||||
"@types/jquery": "^3.3.29",
|
||||
"@types/react": "16.8.8",
|
||||
"@types/react-big-calendar": "^0.24.6",
|
||||
"@types/react-dom": "16.8.3",
|
||||
"@types/webpack-env": "1.13.1",
|
||||
"@uifabric/fluent-theme": "^0.16.7",
|
||||
"browserslist": "^4.12.0",
|
||||
"caniuse-lite": "^1.0.30001079",
|
||||
"draft-js": "^0.10.5",
|
||||
"draftjs-to-html": "^0.8.4",
|
||||
"globalize": "^1.4.2",
|
||||
"immutable": "^4.0.0-rc.12",
|
||||
"jquery": "^3.3.1",
|
||||
"moment": "^2.24.0",
|
||||
"office-ui-fabric-react": "6.189.2",
|
||||
"react": "16.8.5",
|
||||
"office-ui-fabric-react": "7.156.0",
|
||||
"react": "16.9.0",
|
||||
"react-big-calendar": "^0.24.6",
|
||||
"react-dom": "16.8.5",
|
||||
"react-dom": "16.9.0",
|
||||
"react-draft-wysiwyg": "^1.13.2",
|
||||
"spfx-uifabric-themes": "^0.6.0",
|
||||
"string-format": "^2.0.0",
|
||||
|
@ -57,16 +52,19 @@
|
|||
"@microsoft/rush-stack-compiler-2.9": "0.7.7",
|
||||
"@microsoft/rush-stack-compiler-3.2": "0.3.17",
|
||||
"@microsoft/rush-stack-compiler-3.3": "0.3.5",
|
||||
"@microsoft/sp-build-web": "1.10.0",
|
||||
"@microsoft/sp-module-interfaces": "1.10.0",
|
||||
"@microsoft/sp-tslint-rules": "1.10.0",
|
||||
"@microsoft/sp-webpart-workbench": "1.10.0",
|
||||
"@types/chai": "3.4.34",
|
||||
"@types/mocha": "2.2.38",
|
||||
"@microsoft/rush-stack-compiler-3.7": "0.2.3",
|
||||
"@microsoft/sp-build-web": "1.12.1",
|
||||
"@microsoft/sp-module-interfaces": "1.12.1",
|
||||
"@microsoft/sp-tslint-rules": "1.12.1",
|
||||
"@microsoft/sp-webpart-workbench": "1.12.1",
|
||||
"@types/es6-promise": "0.0.33",
|
||||
"@types/react": "16.9.36",
|
||||
"@types/react-dom": "16.9.8",
|
||||
"@types/webpack-env": "1.13.1",
|
||||
"@types/xml2js": "^0.4.4",
|
||||
"@voitanos/jest-preset-spfx-react16": "^1.1.0",
|
||||
"ajv": "~5.2.2",
|
||||
"gulp": "~3.9.1",
|
||||
"gulp": "4.0.2",
|
||||
"gulp-sequence": "1.0.0",
|
||||
"gulp-stylelint": "^8.0.0",
|
||||
"jest": "^23.6.0",
|
||||
|
@ -75,5 +73,14 @@
|
|||
"stylelint-config-standard": "^18.2.0",
|
||||
"stylelint-scss": "^3.5.4",
|
||||
"webpack-bundle-analyzer": "^3.1.0"
|
||||
},
|
||||
"solution": {
|
||||
"developer": {
|
||||
"name": "",
|
||||
"privacyUrl": "",
|
||||
"termsOfUseUrl": "",
|
||||
"websiteUrl": "",
|
||||
"mpnId": ""
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -123,6 +123,7 @@ export class Event extends React.Component<IEventProps, IEventState> {
|
|||
this.onSelectDateStart = this.onSelectDateStart.bind(this);
|
||||
this.onUpdateCoordinates = this.onUpdateCoordinates.bind(this);
|
||||
this.onGetErrorMessageTitle = this.onGetErrorMessageTitle.bind(this);
|
||||
this.onChangeEventTitle = this.onChangeEventTitle.bind(this);
|
||||
this.getPeoplePickerItems = this.getPeoplePickerItems.bind(this);
|
||||
this.hidePanel = this.hidePanel.bind(this);
|
||||
this.onDelete = this.onDelete.bind(this);
|
||||
|
@ -410,10 +411,21 @@ export class Event extends React.Component<IEventProps, IEventState> {
|
|||
if (value.length === 0) {
|
||||
returnMessage = strings.EventTitleErrorMessage;
|
||||
} else {
|
||||
this.setState({ eventData: { ...this.state.eventData, title: value }, disableButton: false, errorMessage: '' });
|
||||
this.setState({disableButton: false, errorMessage: '' });
|
||||
}
|
||||
return returnMessage;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @private
|
||||
* @memberof Event
|
||||
*/
|
||||
private onChangeEventTitle (event:any) {
|
||||
const eventTitle = event.target.value;
|
||||
this.setState({eventData: {...this.state.eventData, title: eventTitle}});
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @private
|
||||
|
@ -923,6 +935,7 @@ export class Event extends React.Component<IEventProps, IEventState> {
|
|||
<TextField
|
||||
label={strings.EventTitleLabel}
|
||||
value={this.state.eventData ? this.state.eventData.title : ''}
|
||||
onChange={this.onChangeEventTitle}
|
||||
onGetErrorMessage={this.onGetErrorMessageTitle}
|
||||
deferredValidationTime={500}
|
||||
disabled={this.state.userPermissions.hasPermissionAdd || this.state.userPermissions.hasPermissionEdit ? false : true}
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
{
|
||||
"extends": "./node_modules/@microsoft/rush-stack-compiler-3.3/includes/tsconfig-web.json",
|
||||
"extends": "./node_modules/@microsoft/rush-stack-compiler-3.7/includes/tsconfig-web.json",
|
||||
"compilerOptions": {
|
||||
"target": "es5",
|
||||
"forceConsistentCasingInFileNames": true,
|
||||
|
@ -20,20 +20,17 @@
|
|||
"./node_modules/@microsoft"
|
||||
],
|
||||
"types": [
|
||||
"es6-promise",
|
||||
"webpack-env"
|
||||
],
|
||||
"lib": [
|
||||
"es5",
|
||||
"dom",
|
||||
"es2015.collection"
|
||||
"es2015.collection",
|
||||
"es2015.promise"
|
||||
]
|
||||
},
|
||||
"include": [
|
||||
"src/**/*.ts"
|
||||
],
|
||||
"exclude": [
|
||||
"node_modules",
|
||||
"lib"
|
||||
"src/**/*.ts",
|
||||
"src/**/*.tsx"
|
||||
]
|
||||
}
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
{
|
||||
"extends": "@microsoft/sp-tslint-rules/base-tslint.json",
|
||||
"extends": "./node_modules/@microsoft/sp-tslint-rules/base-tslint.json",
|
||||
"rules": {
|
||||
"class-name": false,
|
||||
"export-name": false,
|
||||
|
|
|
@ -1,25 +0,0 @@
|
|||
# EditorConfig helps developers define and maintain consistent
|
||||
# coding styles between different editors and IDEs
|
||||
# editorconfig.org
|
||||
|
||||
root = true
|
||||
|
||||
|
||||
[*]
|
||||
|
||||
# change these settings to your own preference
|
||||
indent_style = space
|
||||
indent_size = 2
|
||||
|
||||
# we recommend you to keep these unchanged
|
||||
end_of_line = lf
|
||||
charset = utf-8
|
||||
trim_trailing_whitespace = true
|
||||
insert_final_newline = true
|
||||
|
||||
[*.md]
|
||||
trim_trailing_whitespace = false
|
||||
|
||||
[{package,bower}.json]
|
||||
indent_style = space
|
||||
indent_size = 2
|
|
@ -9,6 +9,7 @@ node_modules
|
|||
# Build generated files
|
||||
dist
|
||||
lib
|
||||
release
|
||||
solution
|
||||
temp
|
||||
*.sppkg
|
||||
|
|
|
@ -0,0 +1,16 @@
|
|||
!dist
|
||||
config
|
||||
|
||||
gulpfile.js
|
||||
|
||||
release
|
||||
src
|
||||
temp
|
||||
|
||||
tsconfig.json
|
||||
tslint.json
|
||||
|
||||
*.log
|
||||
|
||||
.yo-rc.json
|
||||
.vscode
|
|
@ -10,10 +10,10 @@
|
|||
"environment": "spo",
|
||||
"framework": "react",
|
||||
"isCreatingSolution": true,
|
||||
"version": "1.7.1",
|
||||
"version": "1.13.1",
|
||||
"libraryName": "my-teams",
|
||||
"libraryId": "9bf27890-01d8-4041-8030-72831ed570aa",
|
||||
"packageManager": "npm",
|
||||
"componentType": "webpart"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -10,13 +10,11 @@ The web part can be configured to open the team on the web browser or client app
|
|||
|
||||
# Compatibility
|
||||
|
||||
![SPFx 1.7.1](https://img.shields.io/badge/SPFx-1.7.1-green.svg)
|
||||
![Node.js v8](https://img.shields.io/badge/Node.js-v8-green.svg)
|
||||
![SPFx 1.13.1](https://img.shields.io/badge/SPFx-1.13.1-green.svg)
|
||||
![Node.js v14](https://img.shields.io/badge/Node.js-v14-green.svg)
|
||||
![Compatible with SharePoint Online](https://img.shields.io/badge/SharePoint%20Online-Compatible-green.svg)
|
||||
![Compatible with SharePoint 2019](https://img.shields.io/badge/SharePoint%20Server%202019-Compatible-green.svg)
|
||||
![Does not work with SharePoint 2016 (Feature Pack 2)](https://img.shields.io/badge/SharePoint%20Server%202016%20(Feature%20Pack%202)-Incompatible-red.svg "SharePoint Server 2016 Feature Pack 2 requires SPFx 1.1")
|
||||
![Local Workbench Compatible](https://img.shields.io/badge/Local%20Workbench-Compatible-green.svg)
|
||||
![Hosted Workbench Compatible](https://img.shields.io/badge/Hosted%20Workbench-Compatible-green.svg)
|
||||
|
||||
## Applies to
|
||||
|
||||
|
@ -24,7 +22,7 @@ The web part can be configured to open the team on the web browser or client app
|
|||
|
||||
## Prerequisites
|
||||
|
||||
- Office 365 subscription with SharePoint Online licence
|
||||
- Microsoft 365 subscription with SharePoint Online license
|
||||
- SharePoint Framework [development environment](https://docs.microsoft.com/sharepoint/dev/spfx/set-up-your-development-environment) already set up.
|
||||
|
||||
## Solution
|
||||
|
@ -32,12 +30,15 @@ The web part can be configured to open the team on the web browser or client app
|
|||
| Solution | Author(s) |
|
||||
| -------------- | -------------- |
|
||||
| react-my-teams | Joel Rodrigues |
|
||||
| react-my-teams | [Yves Habersaat](https://github.com/yhabersaat) |
|
||||
|
||||
|
||||
## Version history
|
||||
|
||||
| Version | Date | Comments |
|
||||
| ------- | ----------------- | --------------- |
|
||||
| 1.0 | February 26, 2019 | Initial release |
|
||||
| Version | Date | Comments |
|
||||
| ------- | ----------------- | ------------------------- |
|
||||
| 1.1 | December 18, 2021 | Upgraded for SPFx v1.13.1 |
|
||||
| 1.0 | February 26, 2019 | Initial release |
|
||||
|
||||
## Disclaimer
|
||||
|
||||
|
|
|
@ -9,7 +9,7 @@
|
|||
"This sample uses Microsoft Graph to list the Teams the current user is a member of. When the user clicks on one of the teams, the web part retrieves information about the default channel (General) and opens it. The web part can be configured to open the team on the web browser or client app."
|
||||
],
|
||||
"creationDateTime": "2019-02-26",
|
||||
"updateDateTime": "2019-02-26",
|
||||
"updateDateTime": "2021-12-18",
|
||||
"products": [
|
||||
"SharePoint"
|
||||
],
|
||||
|
@ -20,7 +20,7 @@
|
|||
},
|
||||
{
|
||||
"key": "SPFX-VERSION",
|
||||
"value": "1.7.1"
|
||||
"value": "1.13.1"
|
||||
}
|
||||
],
|
||||
"thumbnails": [
|
||||
|
@ -38,6 +38,12 @@
|
|||
"pictureUrl": "https://github.com/joelfmrodrigues.png",
|
||||
"name": "Joel Rodrigues",
|
||||
"twitter": "joelfmrodrigues"
|
||||
},
|
||||
{
|
||||
"gitHubAccount": "yhabersaat",
|
||||
"pictureUrl": "https://github.com/yhabersaat.png",
|
||||
"name": "Yves Habersaat",
|
||||
"twitter": "yhabersaat"
|
||||
}
|
||||
],
|
||||
"references": [
|
||||
|
@ -48,4 +54,4 @@
|
|||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
]
|
|
@ -1,7 +1,7 @@
|
|||
{
|
||||
"$schema": "https://developer.microsoft.com/json-schemas/spfx-build/deploy-azure-storage.schema.json",
|
||||
"workingDir": "./temp/deploy/",
|
||||
"workingDir": "./release/assets/",
|
||||
"account": "<!-- STORAGE ACCOUNT NAME -->",
|
||||
"container": "my-teams",
|
||||
"accessKey": "<!-- ACCESS KEY -->"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
"solution": {
|
||||
"name": "my-teams-client-side-solution",
|
||||
"id": "9bf27890-01d8-4041-8030-72831ed570aa",
|
||||
"version": "1.0.0.0",
|
||||
"version": "1.1.0.0",
|
||||
"includeClientSideAssets": true,
|
||||
"skipFeatureDeployment": true,
|
||||
"webApiPermissionRequests": [
|
||||
|
|
|
@ -2,9 +2,5 @@
|
|||
"$schema": "https://developer.microsoft.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/"
|
||||
}
|
||||
"initialPage": "https://enter-your-SharePoint-site/_layouts/workbench.aspx"
|
||||
}
|
||||
|
|
|
@ -3,8 +3,8 @@
|
|||
|
||||
// check if gulp dist was called
|
||||
if (process.argv.indexOf('dist') !== -1) {
|
||||
// add ship options to command call
|
||||
process.argv.push('--ship');
|
||||
// add ship options to command call
|
||||
process.argv.push('--ship');
|
||||
}
|
||||
|
||||
const path = require('path');
|
||||
|
@ -25,5 +25,13 @@ gulp.task('dist', gulpSequence('clean', 'bundle', 'package-solution'));
|
|||
* Custom Framework Specific gulp tasks
|
||||
*/
|
||||
|
||||
var getTasks = build.rig.getTasks;
|
||||
build.rig.getTasks = function () {
|
||||
var result = getTasks.call(build.rig);
|
||||
|
||||
result.set('serve', result.get('serve-deprecated'));
|
||||
|
||||
return result;
|
||||
};
|
||||
|
||||
build.initialize(gulp);
|
||||
|
|
|
@ -2,9 +2,7 @@
|
|||
"name": "my-teams",
|
||||
"version": "0.0.1",
|
||||
"private": true,
|
||||
"engines": {
|
||||
"node": ">=0.10.0"
|
||||
},
|
||||
"main": "lib/index.js",
|
||||
"scripts": {
|
||||
"build": "gulp bundle",
|
||||
"clean": "gulp clean",
|
||||
|
@ -12,30 +10,27 @@
|
|||
"test:watch": "./node_modules/.bin/jest --config ./config/jest.config.json --watchAll"
|
||||
},
|
||||
"dependencies": {
|
||||
"@microsoft/sp-core-library": "1.7.1",
|
||||
"@microsoft/sp-lodash-subset": "1.7.1",
|
||||
"@microsoft/sp-office-ui-fabric-core": "1.7.1",
|
||||
"@microsoft/sp-webpart-base": "1.7.1",
|
||||
"@types/es6-promise": "0.0.33",
|
||||
"@types/react": "16.4.2",
|
||||
"@types/react-dom": "16.0.5",
|
||||
"@types/webpack-env": "1.13.1",
|
||||
"react": "16.3.2",
|
||||
"react-dom": "16.3.2"
|
||||
"@microsoft/sp-core-library": "1.13.1",
|
||||
"@microsoft/sp-lodash-subset": "1.13.1",
|
||||
"@microsoft/sp-office-ui-fabric-core": "1.13.1",
|
||||
"@microsoft/sp-property-pane": "1.13.1",
|
||||
"@microsoft/sp-webpart-base": "1.13.1",
|
||||
"office-ui-fabric-react": "7.174.1",
|
||||
"react": "16.13.1",
|
||||
"react-dom": "16.13.1"
|
||||
},
|
||||
"resolutions": {
|
||||
"@types/react": "16.4.2"
|
||||
"@types/react": "16.8.8"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@microsoft/sp-build-web": "1.7.1",
|
||||
"@microsoft/sp-module-interfaces": "1.7.1",
|
||||
"@microsoft/sp-tslint-rules": "1.7.1",
|
||||
"@microsoft/sp-webpart-workbench": "1.7.1",
|
||||
"@types/chai": "3.4.34",
|
||||
"@types/mocha": "2.2.38",
|
||||
"@microsoft/rush-stack-compiler-3.9": "0.4.47",
|
||||
"@microsoft/sp-build-web": "1.13.1",
|
||||
"@microsoft/sp-module-interfaces": "1.13.1",
|
||||
"@microsoft/sp-tslint-rules": "1.13.1",
|
||||
"@types/es6-promise": "0.0.33",
|
||||
"@types/webpack-env": "1.13.1",
|
||||
"@voitanos/jest-preset-spfx-react16": "^1.1.0",
|
||||
"ajv": "~5.2.2",
|
||||
"gulp": "~3.9.1",
|
||||
"gulp-sequence": "^1.0.0",
|
||||
"jest": "^23.6.0"
|
||||
}
|
||||
|
|
|
@ -13,6 +13,8 @@
|
|||
// https://support.office.com/en-us/article/Turn-scripting-capabilities-on-or-off-1f2c515f-5d7e-448a-9fd7-835da935584f
|
||||
"requiresCustomScript": false,
|
||||
|
||||
"supportedHosts": ["SharePointWebPart"],
|
||||
|
||||
"preconfiguredEntries": [{
|
||||
"groupId": "5c03119e-3074-46fd-976b-c60198311f70", // Other
|
||||
"group": { "default": "Other" },
|
||||
|
|
|
@ -1,12 +1,8 @@
|
|||
import * as React from 'react';
|
||||
import * as ReactDom from 'react-dom';
|
||||
import { Version, Environment, EnvironmentType } from '@microsoft/sp-core-library';
|
||||
import {
|
||||
BaseClientSideWebPart,
|
||||
IPropertyPaneConfiguration,
|
||||
PropertyPaneToggle
|
||||
} from '@microsoft/sp-webpart-base';
|
||||
|
||||
import { Version } from '@microsoft/sp-core-library';
|
||||
import { BaseClientSideWebPart } from "@microsoft/sp-webpart-base";
|
||||
import { IPropertyPaneConfiguration, PropertyPaneToggle } from "@microsoft/sp-property-pane";
|
||||
import * as strings from 'MyTeamsWebPartStrings';
|
||||
import { MyTeams, IMyTeamsProps } from './components/myTeams';
|
||||
import { MSGraphClient } from '@microsoft/sp-http';
|
||||
|
@ -22,14 +18,8 @@ export default class MyTeamsWebPart extends BaseClientSideWebPart<IMyTeamsWebPar
|
|||
private _teamsService: ITeamsService;
|
||||
|
||||
public async onInit(): Promise<void> {
|
||||
|
||||
if (DEBUG && Environment.type === EnvironmentType.Local) {
|
||||
console.log("Mock data service not implemented yet");
|
||||
} else {
|
||||
|
||||
this._graphClient = await this.context.msGraphClientFactory.getClient();
|
||||
this._teamsService = new TeamsService(this._graphClient);
|
||||
}
|
||||
this._graphClient = await this.context.msGraphClientFactory.getClient();
|
||||
this._teamsService = new TeamsService(this._graphClient);
|
||||
|
||||
return super.onInit();
|
||||
}
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
@import '~@microsoft/sp-office-ui-fabric-core/dist/sass/SPFabricCore.scss';
|
||||
@import '~office-ui-fabric-react/dist/sass/References.scss';
|
||||
|
||||
.myTeams {
|
||||
.container {
|
||||
|
|
|
@ -3,7 +3,6 @@ import { FocusZone } from 'office-ui-fabric-react/lib/FocusZone';
|
|||
import { List } from 'office-ui-fabric-react/lib/List';
|
||||
import styles from '../myTeams/MyTeams.module.scss';
|
||||
import { IMyTeamsProps, IMyTeamsState } from '.';
|
||||
import { escape } from '@microsoft/sp-lodash-subset';
|
||||
import { ITeam, IChannel } from '../../../../shared/interfaces';
|
||||
|
||||
export class MyTeams extends React.Component<IMyTeamsProps, IMyTeamsState> {
|
||||
|
|
After Width: | Height: | Size: 10 KiB |
After Width: | Height: | Size: 542 B |
|
@ -1,4 +1,5 @@
|
|||
{
|
||||
"extends": "./node_modules/@microsoft/rush-stack-compiler-3.9/includes/tsconfig-web.json",
|
||||
"compilerOptions": {
|
||||
"target": "es5",
|
||||
"forceConsistentCasingInFileNames": true,
|
||||
|
@ -10,25 +11,25 @@
|
|||
"experimentalDecorators": true,
|
||||
"skipLibCheck": true,
|
||||
"outDir": "lib",
|
||||
"inlineSources": false,
|
||||
"strictNullChecks": false,
|
||||
"noUnusedLocals": false,
|
||||
"typeRoots": [
|
||||
"./node_modules/@types",
|
||||
"./node_modules/@microsoft"
|
||||
],
|
||||
"types": [
|
||||
"es6-promise",
|
||||
"webpack-env"
|
||||
],
|
||||
"lib": [
|
||||
"es5",
|
||||
"dom",
|
||||
"es2015.collection"
|
||||
"es2015.collection",
|
||||
"es2015.promise"
|
||||
]
|
||||
},
|
||||
"include": [
|
||||
"src/**/*.ts"
|
||||
],
|
||||
"exclude": [
|
||||
"node_modules",
|
||||
"lib"
|
||||
"src/**/*.ts",
|
||||
"src/**/*.tsx"
|
||||
]
|
||||
}
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
{
|
||||
"extends": "@microsoft/sp-tslint-rules/base-tslint.json",
|
||||
"extends": "./node_modules/@microsoft/sp-tslint-rules/base-tslint.json",
|
||||
"rules": {
|
||||
"class-name": false,
|
||||
"export-name": false,
|
||||
|
@ -17,7 +17,6 @@
|
|||
"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,
|
||||
|
|
|
@ -1,25 +0,0 @@
|
|||
# EditorConfig helps developers define and maintain consistent
|
||||
# coding styles between different editors and IDEs
|
||||
# editorconfig.org
|
||||
|
||||
root = true
|
||||
|
||||
|
||||
[*]
|
||||
|
||||
# change these settings to your own preference
|
||||
indent_style = space
|
||||
indent_size = 2
|
||||
|
||||
# we recommend you to keep these unchanged
|
||||
end_of_line = lf
|
||||
charset = utf-8
|
||||
trim_trailing_whitespace = true
|
||||
insert_final_newline = true
|
||||
|
||||
[*.md]
|
||||
trim_trailing_whitespace = false
|
||||
|
||||
[{package,bower}.json]
|
||||
indent_style = space
|
||||
indent_size = 2
|
|
@ -9,6 +9,7 @@ node_modules
|
|||
# Build generated files
|
||||
dist
|
||||
lib
|
||||
release
|
||||
solution
|
||||
temp
|
||||
*.sppkg
|
||||
|
|
|
@ -0,0 +1,16 @@
|
|||
!dist
|
||||
config
|
||||
|
||||
gulpfile.js
|
||||
|
||||
release
|
||||
src
|
||||
temp
|
||||
|
||||
tsconfig.json
|
||||
tslint.json
|
||||
|
||||
*.log
|
||||
|
||||
.yo-rc.json
|
||||
.vscode
|
|
@ -4,23 +4,7 @@
|
|||
* Chrome browser: https://aka.ms/spfx-debugger-extensions
|
||||
*/
|
||||
"version": "0.2.0",
|
||||
"configurations": [{
|
||||
"name": "Local workbench",
|
||||
"type": "chrome",
|
||||
"request": "launch",
|
||||
"url": "https://localhost:4321/temp/workbench.html",
|
||||
"webRoot": "${workspaceRoot}",
|
||||
"sourceMaps": true,
|
||||
"sourceMapPathOverrides": {
|
||||
"webpack:///.././src/*": "${webRoot}/src/*",
|
||||
"webpack:///../../../src/*": "${webRoot}/src/*",
|
||||
"webpack:///../../../../src/*": "${webRoot}/src/*",
|
||||
"webpack:///../../../../../src/*": "${webRoot}/src/*"
|
||||
},
|
||||
"runtimeArgs": [
|
||||
"--remote-debugging-port=9222"
|
||||
]
|
||||
},
|
||||
"configurations": [
|
||||
{
|
||||
"name": "Hosted workbench",
|
||||
"type": "chrome",
|
||||
|
|
|
@ -2,11 +2,11 @@
|
|||
"@microsoft/generator-sharepoint": {
|
||||
"isCreatingSolution": true,
|
||||
"environment": "spo",
|
||||
"version": "1.9.1",
|
||||
"version": "1.13.1",
|
||||
"libraryName": "react-quotes",
|
||||
"libraryId": "aa9211b8-e9b8-44bf-a804-af2db58bc03e",
|
||||
"packageManager": "npm",
|
||||
"isDomainIsolated": false,
|
||||
"componentType": "webpart"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -9,12 +9,11 @@ This web part displays a quote of the day by querying a third-party api or can d
|
|||
|
||||
# Compatibility
|
||||
|
||||
![SPFx 1.9.1](https://img.shields.io/badge/SPFx-1.9.1-green.svg)
|
||||
![Node.js v10 | v8](https://img.shields.io/badge/Node.js-v10%20%7C%20v8-green.svg)
|
||||
![SPFx 1.13.1](https://img.shields.io/badge/SPFx-1.13.1-green.svg)
|
||||
![Node.js v14](https://img.shields.io/badge/Node.js-v14-green.svg)
|
||||
![Compatible with SharePoint Online](https://img.shields.io/badge/SharePoint%20Online-Compatible-green.svg)
|
||||
![Does not work with with SharePoint 2019](https://img.shields.io/badge/SharePoint%20Server%202019-Incompatible-red.svg)
|
||||
![Does not work with SharePoint 2016 (Feature Pack 2)](https://img.shields.io/badge/SharePoint%20Server%202016%20(Feature%20Pack%202)-Incompatible-red.svg "SharePoint Server 2016 Feature Pack 2 requires SPFx 1.1")
|
||||
![Local Workbench Compatible](https://img.shields.io/badge/Local%20Workbench-Compatible-green.svg)
|
||||
![Hosted Workbench Compatible](https://img.shields.io/badge/Hosted%20Workbench-Compatible-green.svg)
|
||||
|
||||
## Applies to
|
||||
|
@ -25,12 +24,14 @@ This web part displays a quote of the day by querying a third-party api or can d
|
|||
|
||||
Solution|Author(s)
|
||||
--------|---------
|
||||
react-quotes | Zach Roberts
|
||||
react-quotes | [Zach Roberts](https://github.com/zachroberts8668)
|
||||
react-quotes | [Yves Habersaat](https://github.com/yhabersaat)
|
||||
|
||||
## Version history
|
||||
|
||||
Version|Date|Comments
|
||||
-------|----|--------
|
||||
1.1| December 31, 2021| Upgraded for SPFx v1.13.1
|
||||
1.0| November 11, 2019| Initial Release
|
||||
|
||||
## Minimal Path to Awesome
|
||||
|
@ -47,9 +48,27 @@ Version|Date|Comments
|
|||
This web part loads a random quote from a third-party api (https://favqs.com/api). Additionally a quote can be entered manually and the text color of the quote and author can be adjusted through the web part properties.
|
||||
|
||||
|
||||
## Help
|
||||
|
||||
|
||||
We do not support samples, but this community is always willing to help, and we want to improve these samples. We use GitHub to track issues, which makes it easy for community members to volunteer their time and help resolve issues.
|
||||
|
||||
If you're having issues building the solution, please run [spfx doctor](https://pnp.github.io/cli-microsoft365/cmd/spfx/spfx-doctor/) from within the solution folder to diagnose incompatibility issues with your environment.
|
||||
|
||||
You can try looking at [issues related to this sample](https://github.com/pnp/sp-dev-fx-webparts/issues?q=label%3A%22sample%3A%20react-quotes%22) to see if anybody else is having the same issues.
|
||||
|
||||
You can also try looking at [discussions related to this sample](https://github.com/pnp/sp-dev-fx-webparts/discussions?discussions_q=react-quotes) and see what the community is saying.
|
||||
|
||||
If you encounter any issues while using this sample, [create a new issue](https://github.com/pnp/sp-dev-fx-webparts/issues/new?assignees=&labels=Needs%3A+Triage+%3Amag%3A%2Ctype%3Abug-suspected%2Csample%3A%20react-quotes&template=bug-report.yml&sample=react-quotes&authors=@zachroberts8668 @yhabersaat&title=react-quotes%20-%20).
|
||||
|
||||
For questions regarding this sample, [create a new question](https://github.com/pnp/sp-dev-fx-webparts/issues/new?assignees=&labels=Needs%3A+Triage+%3Amag%3A%2Ctype%3Aquestion%2Csample%3A%20react-quotes&template=question.yml&sample=react-quotes&authors=@zachroberts8668 @yhabersaat&title=react-quotes%20-%20).
|
||||
|
||||
Finally, if you have an idea for improvement, [make a suggestion](https://github.com/pnp/sp-dev-fx-webparts/issues/new?assignees=&labels=Needs%3A+Triage+%3Amag%3A%2Ctype%3Aenhancement%2Csample%3A%20react-quotes&template=suggestion.yml&sample=react-quotes&authors=@zachroberts8668 @yhabersaat&title=react-quotes%20-%20).
|
||||
|
||||
|
||||
## 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.**
|
||||
|
||||
|
||||
<img src="https://pnptelemetry.azurewebsites.net/sp-dev-fx-webparts/samples/readme-template" />
|
||||
<img src="https://pnptelemetry.azurewebsites.net/sp-dev-fx-webparts/samples/react-quotes" />
|
||||
|
|
|
@ -9,7 +9,7 @@
|
|||
"This webpart displays a quote of the day by querying a third-party api or can display a quote entered manually into the webpart property pane."
|
||||
],
|
||||
"creationDateTime": "2019-11-11",
|
||||
"updateDateTime": "2019-11-11",
|
||||
"updateDateTime": "2022-01-01",
|
||||
"products": [
|
||||
"SharePoint"
|
||||
],
|
||||
|
@ -20,7 +20,7 @@
|
|||
},
|
||||
{
|
||||
"key": "SPFX-VERSION",
|
||||
"value": "1.9.1"
|
||||
"value": "1.13.1"
|
||||
},
|
||||
{
|
||||
"key": "PNPCONTROLS",
|
||||
|
@ -41,6 +41,11 @@
|
|||
"company": "",
|
||||
"pictureUrl": "https://github.com/zachroberts8668.png",
|
||||
"name": "Zach Roberts"
|
||||
},
|
||||
{
|
||||
"gitHubAccount": "yhabersaat",
|
||||
"pictureUrl": "https://github.com/yhabersaat",
|
||||
"name": "Yves Habersaat"
|
||||
}
|
||||
],
|
||||
"references": [
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"$schema": "https://developer.microsoft.com/json-schemas/spfx-build/deploy-azure-storage.schema.json",
|
||||
"workingDir": "./temp/deploy/",
|
||||
"workingDir": "./release/assets/",
|
||||
"account": "<!-- STORAGE ACCOUNT NAME -->",
|
||||
"container": "react-quotes",
|
||||
"accessKey": "<!-- ACCESS KEY -->"
|
||||
|
|
|
@ -3,9 +3,16 @@
|
|||
"solution": {
|
||||
"name": "react-quotes-client-side-solution",
|
||||
"id": "aa9211b8-e9b8-44bf-a804-af2db58bc03e",
|
||||
"version": "1.0.0.0",
|
||||
"version": "1.1.0.0",
|
||||
"includeClientSideAssets": true,
|
||||
"isDomainIsolated": false
|
||||
"isDomainIsolated": false,
|
||||
"developer": {
|
||||
"name": "Zach Roberts",
|
||||
"privacyUrl": "",
|
||||
"termsOfUseUrl": "",
|
||||
"websiteUrl": "",
|
||||
"mpnId": ""
|
||||
}
|
||||
},
|
||||
"paths": {
|
||||
"zippedPackage": "solution/react-quotes.sppkg"
|
||||
|
|
|
@ -2,9 +2,5 @@
|
|||
"$schema": "https://developer.microsoft.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/"
|
||||
}
|
||||
"initialPage": "https://enter-your-SharePoint-site/_layouts/workbench.aspx"
|
||||
}
|
||||
|
|
|
@ -4,4 +4,13 @@ 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.`);
|
||||
|
||||
var getTasks = build.rig.getTasks;
|
||||
build.rig.getTasks = function () {
|
||||
var result = getTasks.call(build.rig);
|
||||
|
||||
result.set('serve', result.get('serve-deprecated'));
|
||||
|
||||
return result;
|
||||
};
|
||||
|
||||
build.initialize(gulp);
|
||||
|
|
|
@ -12,32 +12,27 @@
|
|||
"test": "gulp test"
|
||||
},
|
||||
"dependencies": {
|
||||
"@microsoft/sp-core-library": "1.9.1",
|
||||
"@microsoft/sp-lodash-subset": "1.9.1",
|
||||
"@microsoft/sp-office-ui-fabric-core": "1.9.1",
|
||||
"@microsoft/sp-webpart-base": "1.9.1",
|
||||
"@microsoft/sp-core-library": "1.13.1",
|
||||
"@microsoft/sp-lodash-subset": "1.13.1",
|
||||
"@microsoft/sp-office-ui-fabric-core": "1.13.1",
|
||||
"@microsoft/sp-property-pane": "1.13.1",
|
||||
"@microsoft/sp-webpart-base": "1.13.1",
|
||||
"@pnp/spfx-property-controls": "1.16.0",
|
||||
"@types/es6-promise": "0.0.33",
|
||||
"@types/react": "16.8.8",
|
||||
"@types/react-dom": "16.8.3",
|
||||
"@types/webpack-env": "1.13.1",
|
||||
"axios": "^0.19.0",
|
||||
"office-ui-fabric-react": "6.189.2",
|
||||
"react": "16.8.5",
|
||||
"react-dom": "16.8.5"
|
||||
},
|
||||
"resolutions": {
|
||||
"@types/react": "16.8.8"
|
||||
"office-ui-fabric-react": "7.174.1",
|
||||
"react": "16.13.1",
|
||||
"react-dom": "16.13.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@microsoft/sp-build-web": "1.9.1",
|
||||
"@microsoft/sp-tslint-rules": "1.9.1",
|
||||
"@microsoft/sp-module-interfaces": "1.9.1",
|
||||
"@microsoft/sp-webpart-workbench": "1.9.1",
|
||||
"@microsoft/rush-stack-compiler-2.9": "0.7.16",
|
||||
"gulp": "~3.9.1",
|
||||
"@types/chai": "3.4.34",
|
||||
"@types/mocha": "2.2.38",
|
||||
"ajv": "~5.2.2"
|
||||
"@types/react": "16.8.8",
|
||||
"@types/react-dom": "^17.0.11",
|
||||
"@microsoft/sp-build-web": "1.13.1",
|
||||
"@microsoft/sp-module-interfaces": "1.13.1",
|
||||
"@microsoft/sp-tslint-rules": "1.13.1",
|
||||
"@microsoft/rush-stack-compiler-3.9": "0.4.47",
|
||||
"@types/es6-promise": "0.0.33",
|
||||
"@types/webpack-env": "1.13.1",
|
||||
"ajv": "~5.2.2",
|
||||
"gulp": "4.0.2"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,17 +1,12 @@
|
|||
import * as React from 'react';
|
||||
import * as ReactDom from 'react-dom';
|
||||
import { Version } from '@microsoft/sp-core-library';
|
||||
import {
|
||||
BaseClientSideWebPart,
|
||||
IPropertyPaneConfiguration,
|
||||
PropertyPaneTextField
|
||||
} from '@microsoft/sp-webpart-base';
|
||||
|
||||
import { BaseClientSideWebPart } from '@microsoft/sp-webpart-base';
|
||||
import * as strings from 'ReactQuotesWebPartStrings';
|
||||
import ReactQuotes from './components/ReactQuotes';
|
||||
import { IReactQuotesProps } from './components/IReactQuotesProps';
|
||||
import { PropertyFieldColorPicker, PropertyFieldColorPickerStyle } from '@pnp/spfx-property-controls/lib/PropertyFieldColorPicker';
|
||||
import { PropertyPaneToggle } from '@microsoft/sp-property-pane';
|
||||
import { PropertyFieldColorPicker } from '@pnp/spfx-property-controls/lib/PropertyFieldColorPicker';
|
||||
import { PropertyPaneToggle, IPropertyPaneConfiguration, PropertyPaneTextField } from '@microsoft/sp-property-pane';
|
||||
|
||||
export interface IReactQuotesWebPartProps {
|
||||
description: string;
|
||||
|
@ -25,7 +20,7 @@ export interface IReactQuotesWebPartProps {
|
|||
export default class ReactQuotesWebPart extends BaseClientSideWebPart<IReactQuotesWebPartProps> {
|
||||
|
||||
public render(): void {
|
||||
const element: React.ReactElement<IReactQuotesProps > = React.createElement(
|
||||
const element: React.ReactElement<IReactQuotesProps> = React.createElement(
|
||||
ReactQuotes,
|
||||
{
|
||||
description: this.properties.description,
|
||||
|
|
|
@ -2,13 +2,12 @@ import * as React from 'react';
|
|||
import styles from './ReactQuotes.module.scss';
|
||||
import { IReactQuotesProps } from './IReactQuotesProps';
|
||||
import { IReactQuotesState } from './IReactQuotesState';
|
||||
import { escape } from '@microsoft/sp-lodash-subset';
|
||||
import { Spinner } from 'office-ui-fabric-react/lib/spinner';
|
||||
import { Spinner } from 'office-ui-fabric-react/lib/Spinner';
|
||||
import axios from 'axios';
|
||||
|
||||
export default class ReactQuotes extends React.Component<IReactQuotesProps, IReactQuotesState> {
|
||||
|
||||
constructor(props: IReactQuotesProps){
|
||||
constructor(props: IReactQuotesProps) {
|
||||
super(props);
|
||||
|
||||
this.state = {
|
||||
|
@ -20,26 +19,26 @@ export default class ReactQuotes extends React.Component<IReactQuotesProps, IRea
|
|||
|
||||
public render(): React.ReactElement<IReactQuotesProps> {
|
||||
return (
|
||||
<div className={ styles.reactQuotes }>
|
||||
<div className={styles.reactQuotes}>
|
||||
<h2>{this.props.description}</h2>
|
||||
{this.state.loading !== true ?
|
||||
//If state is loading show spinner otherwise show quote
|
||||
<div>
|
||||
{this.props.manual !== true ?
|
||||
//If manual quote is not on then show quote generated from third party
|
||||
<div>
|
||||
<h3 style={{color: this.props.quoteColor}}>"<i>{this.state.quote}</i>"</h3>
|
||||
<h5 style={{color: this.props.authorColor}}>- {this.state.author}</h5>
|
||||
{this.props.manual !== true ?
|
||||
//If manual quote is not on then show quote generated from third party
|
||||
<div>
|
||||
<h3 style={{ color: this.props.quoteColor }}>"<i>{this.state.quote}</i>"</h3>
|
||||
<h5 style={{ color: this.props.authorColor }}>- {this.state.author}</h5>
|
||||
</div>
|
||||
: //Show Manual quote
|
||||
<div>
|
||||
<h3 style={{ color: this.props.quoteColor }}>"<i>{this.props.manualQuote}</i>"</h3>
|
||||
<h5 style={{ color: this.props.authorColor }}>- {this.props.manualAuthor}</h5>
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
: //Show Manual quote
|
||||
:
|
||||
<div>
|
||||
<h3 style={{color: this.props.quoteColor}}>"<i>{this.props.manualQuote}</i>"</h3>
|
||||
<h5 style={{color: this.props.authorColor}}>- {this.props.manualAuthor}</h5>
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
:
|
||||
<div>
|
||||
<Spinner label="Loading quote..." />
|
||||
</div>
|
||||
}
|
||||
|
@ -47,9 +46,9 @@ export default class ReactQuotes extends React.Component<IReactQuotesProps, IRea
|
|||
);
|
||||
}
|
||||
|
||||
public componentDidMount() {
|
||||
console.log(this.props.manual);
|
||||
this.setState({loading: true});
|
||||
public componentDidMount() {
|
||||
console.log(this.props.manual);
|
||||
this.setState({ loading: true });
|
||||
axios.get('https://favqs.com/api/qotd').then(res => {
|
||||
const quote = res.data.quote.body;
|
||||
const author = res.data.quote.author;
|
||||
|
|
Before Width: | Height: | Size: 3.0 KiB After Width: | Height: | Size: 10 KiB |
Before Width: | Height: | Size: 1.4 KiB After Width: | Height: | Size: 542 B |
|
@ -1,5 +1,5 @@
|
|||
{
|
||||
"extends": "./node_modules/@microsoft/rush-stack-compiler-2.9/includes/tsconfig-web.json",
|
||||
"extends": "./node_modules/@microsoft/rush-stack-compiler-3.9/includes/tsconfig-web.json",
|
||||
"compilerOptions": {
|
||||
"target": "es5",
|
||||
"forceConsistentCasingInFileNames": true,
|
||||
|
@ -19,20 +19,17 @@
|
|||
"./node_modules/@microsoft"
|
||||
],
|
||||
"types": [
|
||||
"es6-promise",
|
||||
"webpack-env"
|
||||
],
|
||||
"lib": [
|
||||
"es5",
|
||||
"dom",
|
||||
"es2015.collection"
|
||||
"es2015.collection",
|
||||
"es2015.promise"
|
||||
]
|
||||
},
|
||||
"include": [
|
||||
"src/**/*.ts"
|
||||
],
|
||||
"exclude": [
|
||||
"node_modules",
|
||||
"lib"
|
||||
"src/**/*.ts",
|
||||
"src/**/*.tsx"
|
||||
]
|
||||
}
|
||||
}
|
|
@ -1,5 +1,5 @@
|
|||
{
|
||||
"extends": "@microsoft/sp-tslint-rules/base-tslint.json",
|
||||
"extends": "./node_modules/@microsoft/sp-tslint-rules/base-tslint.json",
|
||||
"rules": {
|
||||
"class-name": false,
|
||||
"export-name": false,
|
||||
|
@ -17,7 +17,6 @@
|
|||
"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,
|
||||
|
|
|
@ -0,0 +1,16 @@
|
|||
!dist
|
||||
config
|
||||
|
||||
gulpfile.js
|
||||
|
||||
release
|
||||
src
|
||||
temp
|
||||
|
||||
tsconfig.json
|
||||
tslint.json
|
||||
|
||||
*.log
|
||||
|
||||
.yo-rc.json
|
||||
.vscode
|
|
@ -0,0 +1,13 @@
|
|||
{
|
||||
"@microsoft/generator-sharepoint": {
|
||||
"plusBeta": false,
|
||||
"isCreatingSolution": true,
|
||||
"environment": "spo",
|
||||
"version": "1.13.1",
|
||||
"libraryName": "react-taxonomy-file-explorer",
|
||||
"libraryId": "5697a573-1bd1-4ff3-96f1-82263c8eb008",
|
||||
"packageManager": "npm",
|
||||
"isDomainIsolated": false,
|
||||
"componentType": "webpart"
|
||||
}
|
||||
}
|
|
@ -0,0 +1,114 @@
|
|||
# React Taxonomy File Explorer
|
||||
|
||||
## Summary
|
||||
|
||||
This solution renders a given Termset as a Tree and incorporates files similar than a folder structure in file explorer. Benefit: Due to multiple selection in the managed metadata column the same file can occur more than once.
|
||||
Additionally with drag and drop options the file can be changed:
|
||||
- By replacing the managed metadata column with the target term (Move)
|
||||
- By adding the target term to the managed metadata column (Link)
|
||||
- By copying the file to a new one with (only) the target term in the managed metadata column (Copy)
|
||||
|
||||
In action this looks like:
|
||||
|
||||
Link:
|
||||
|
||||
![Adding the target term to the managed metadata column (Link)](./assets/03Link.gif)
|
||||
|
||||
Move:
|
||||
|
||||
![Replacing the managed metadata column with the target term (Move)](./assets/04Move.gif)
|
||||
|
||||
Copy:
|
||||
|
||||
![Copying the file to a new one with (only) the target term in the managed metadata column (Copy)](./assets/05Copy.gif)
|
||||
|
||||
For further details see the author's [blog post](https://mmsharepoint.wordpress.com/2021/12/23/a-sharepoint-file-explorer-based-on-managed-metadata-and-spfx/)
|
||||
|
||||
|
||||
## Compatibility
|
||||
|
||||
![SPFx 1.13.0](https://img.shields.io/badge/SPFx-1.13.0-green.svg)
|
||||
![Node.js v14 | v12 | v10](https://img.shields.io/badge/Node.js-v14%20%7C%20v12%20%7C%20v10-green.svg)
|
||||
![Compatible with SharePoint Online](https://img.shields.io/badge/SharePoint%20Online-Compatible-green.svg)
|
||||
![Does not work with SharePoint 2019](https://img.shields.io/badge/SharePoint%20Server%202019-Incompatible-red.svg "SharePoint Server 2019 requires SPFx 1.4.1 or lower")
|
||||
![Does not work with SharePoint 2016 (Feature Pack 2)](https://img.shields.io/badge/SharePoint%20Server%202016%20(Feature%20Pack%202)-Incompatible-red.svg "SharePoint Server 2016 Feature Pack 2 requires SPFx 1.1")
|
||||
![Local Workbench Unsupported](https://img.shields.io/badge/Local%20Workbench-Unsupported-red.svg "Local workbench is no longer available as of SPFx 1.13 and above")
|
||||
![Hosted Workbench Compatible](https://img.shields.io/badge/Hosted%20Workbench-Compatible-green.svg)
|
||||
|
||||
|
||||
## Applies to
|
||||
|
||||
- [SharePoint Framework](https://aka.ms/spfx)
|
||||
- [Microsoft 365 tenant](https://docs.microsoft.com/en-us/sharepoint/dev/spfx/set-up-your-developer-tenant)
|
||||
|
||||
> Get your own free development tenant by subscribing to [Microsoft 365 developer program](http://aka.ms/o365devprogram)
|
||||
|
||||
## Prerequisites
|
||||
|
||||
- A hierarchical Termset bound to a managed metadata column
|
||||
- A document library using that managed metadata column and several documents with selected terms
|
||||
|
||||
## Solution
|
||||
|
||||
Solution|Author(s)
|
||||
--------|---------
|
||||
react-taxonomy-file-explorer| [Markus Moeller](https://github.com/mmsharepoint) ([@moeller2_0](http://www.twitter.com/moeller2_0))
|
||||
|
||||
## Version history
|
||||
|
||||
Version|Date|Comments
|
||||
-------|----|--------
|
||||
1.0|December 26, 2021|Initial release
|
||||
|
||||
|
||||
## Minimal Path to Awesome
|
||||
|
||||
- Clone this repository
|
||||
- Ensure that you are at the solution folder
|
||||
- in the command-line run:
|
||||
- `npm install`
|
||||
- `gulp serve`
|
||||
- Instantiate the web part in the online workbench on a site where prerequisites are met:
|
||||
- A hierarchical Termset bound to a managed metadata column
|
||||
- A document library using that managed metadata column and several documents with selected terms
|
||||
- In the web part properties set the library name and the internal field name of the managed metadata column
|
||||
- Done!
|
||||
|
||||
## Features
|
||||
|
||||
This web part illustrates the following concepts:
|
||||
|
||||
- Use HTML5 drag and drop event handling
|
||||
- Use options by modifier key pressed
|
||||
- Update Managed Metadata columns with [PnPJS](https://pnp.github.io/pnpjs/)
|
||||
- Build trees with recursive components
|
||||
- Traverse trees with callback functions
|
||||
- [FluentUI React File Type Icons](https://www.npmjs.com/package/@fluentui/react-file-type-icons)
|
||||
- [FluentUI Contextual Menu](https://developer.microsoft.com/en-us/fluentui#/controls/web/contextualmenu)
|
||||
|
||||
|
||||
## References
|
||||
|
||||
- [Getting started with SharePoint Framework](https://docs.microsoft.com/en-us/sharepoint/dev/spfx/set-up-your-developer-tenant)
|
||||
- [Building for Microsoft teams](https://docs.microsoft.com/en-us/sharepoint/dev/spfx/build-for-teams-overview)
|
||||
- [Use Microsoft Graph in your solution](https://docs.microsoft.com/en-us/sharepoint/dev/spfx/web-parts/get-started/using-microsoft-graph-apis)
|
||||
- [Publish SharePoint Framework applications to the Marketplace](https://docs.microsoft.com/en-us/sharepoint/dev/spfx/publish-to-marketplace-overview)
|
||||
- [Microsoft 365 Patterns and Practices](https://aka.ms/m365pnp) - Guidance, tooling, samples and open-source controls for your Microsoft 365 development
|
||||
|
||||
## Help
|
||||
|
||||
We do not support samples, but this community is always willing to help, and we want to improve these samples. We use GitHub to track issues, which makes it easy for community members to volunteer their time and help resolve issues.
|
||||
|
||||
If you're having issues building the solution, please run [spfx doctor](https://pnp.github.io/cli-microsoft365/cmd/spfx/spfx-doctor/) from within the solution folder to diagnose incompatibility issues with your environment.
|
||||
|
||||
You can try looking at [issues related to this sample](https://github.com/pnp/sp-dev-fx-webparts/issues?q=label%3A%22sample%3A%20react-taxonomy-file-explorer") to see if anybody else is having the same issues.
|
||||
|
||||
You can also try looking at [discussions related to this sample](https://github.com/pnp/sp-dev-fx-webparts/discussions?discussions_q=react-taxonomy-file-explorer) and see what the community is saying.
|
||||
|
||||
If you encounter any issues while using this sample, [create a new issue](https://github.com/pnp/sp-dev-fx-webparts/issues/new?assignees=&labels=Needs%3A+Triage+%3Amag%3A%2Ctype%3Abug-suspected%2Csample%3A%20react-taxonomy-file-explorer&template=bug-report.yml&sample=react-taxonomy-file-explorer&authors=@mmsharepoint&title=react-taxonomy-file-explorer%20-%20).
|
||||
|
||||
For questions regarding this sample, [create a new question](https://github.com/pnp/sp-dev-fx-webparts/issues/new?assignees=&labels=Needs%3A+Triage+%3Amag%3A%2Ctype%3Aquestion%2Csample%3A%20react-taxonomy-file-explorer&template=question.yml&sample=react-taxonomy-file-explorer&authors=@mmsharepoint&title=react-taxonomy-file-explorer%20-%20).
|
||||
|
||||
Finally, if you have an idea for improvement, [make a suggestion](https://github.com/pnp/sp-dev-fx-webparts/issues/new?assignees=&labels=Needs%3A+Triage+%3Amag%3A%2Ctype%3Aenhancement%2Csample%3A%20react-taxonomy-file-explorer&template=suggestion.yml&sample=react-taxonomy-file-explorer&authors=@mmsharepoint&title=react-taxonomy-file-explorer%20-%20).
|
||||
|
||||
<img src="https://telemetry.sharepointpnp.com/sp-dev-fx-webparts/samples/react-taxonomy-file-explorer" />
|
After Width: | Height: | Size: 253 KiB |
After Width: | Height: | Size: 162 KiB |
After Width: | Height: | Size: 180 KiB |
|
@ -0,0 +1,62 @@
|
|||
[
|
||||
{
|
||||
"name": "pnp-sp-dev-spfx-web-parts-react-taxonomy-file-explorer",
|
||||
"source": "pnp",
|
||||
"title": "Taxonomy File Explorer",
|
||||
"shortDescription": "This solution renders a given Termset as a Tree and incorporates files similar than a folder structure in file explorer.",
|
||||
"url": "https://github.com/pnp/sp-dev-fx-webparts/tree/main/samples/react-taxonomy-file-explorer",
|
||||
"longDescription": [
|
||||
"This solution renders a given Termset as a Tree and incorporates files similar than a folder structure in file explorer."
|
||||
],
|
||||
"creationDateTime": "2021-12-26",
|
||||
"updateDateTime": "2021-12-26",
|
||||
"products": [
|
||||
"SharePoint",
|
||||
"Office"
|
||||
],
|
||||
"metadata": [
|
||||
{
|
||||
"key": "CLIENT-SIDE-DEV",
|
||||
"value": "React"
|
||||
},
|
||||
{
|
||||
"key": "SPFX-VERSION",
|
||||
"value": "1.13.0"
|
||||
}
|
||||
],
|
||||
"thumbnails": [
|
||||
{
|
||||
"type": "image",
|
||||
"order": 100,
|
||||
"url": "https://github.com/pnp/sp-dev-fx-webparts/raw/main/samples/react-taxonomy-file-explorer/assets/03Link.gif",
|
||||
"alt": "Linking to a file"
|
||||
},
|
||||
{
|
||||
"type": "image",
|
||||
"order": 101,
|
||||
"url": "https://github.com/pnp/sp-dev-fx-webparts/raw/main/samples/react-taxonomy-file-explorer/assets/04Move.gif",
|
||||
"alt": "Moving a file"
|
||||
},
|
||||
{
|
||||
"type": "image",
|
||||
"order": 102,
|
||||
"url": "https://github.com/pnp/sp-dev-fx-webparts/raw/main/samples/react-taxonomy-file-explorer/assets/04Move.gif",
|
||||
"alt": "Copying a file"
|
||||
}
|
||||
],
|
||||
"authors": [
|
||||
{
|
||||
"gitHubAccount": "mmsharepoint",
|
||||
"pictureUrl": "https://github.com/mmsharepoint.png",
|
||||
"name": "Markus Moeller"
|
||||
}
|
||||
],
|
||||
"references": [
|
||||
{
|
||||
"name": "Build your first SharePoint client-side web part",
|
||||
"description": "Client-side web parts are client-side components that run in the context of a SharePoint page. Client-side web parts can be deployed to SharePoint environments that support the SharePoint Framework. You can also use modern JavaScript web frameworks, tools, and libraries to build them.",
|
||||
"url": "https://docs.microsoft.com/en-us/sharepoint/dev/spfx/web-parts/get-started/build-a-hello-world-web-part"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
|
@ -0,0 +1,18 @@
|
|||
{
|
||||
"$schema": "https://developer.microsoft.com/json-schemas/spfx-build/config.2.0.schema.json",
|
||||
"version": "2.0",
|
||||
"bundles": {
|
||||
"taxonomy-file-explorer-web-part": {
|
||||
"components": [
|
||||
{
|
||||
"entrypoint": "./lib/webparts/taxonomyFileExplorer/TaxonomyFileExplorerWebPart.js",
|
||||
"manifest": "./src/webparts/taxonomyFileExplorer/TaxonomyFileExplorerWebPart.manifest.json"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"externals": {},
|
||||
"localizedResources": {
|
||||
"TaxonomyFileExplorerWebPartStrings": "lib/webparts/taxonomyFileExplorer/loc/{locale}.js"
|
||||
}
|
||||
}
|
|
@ -0,0 +1,7 @@
|
|||
{
|
||||
"$schema": "https://developer.microsoft.com/json-schemas/spfx-build/deploy-azure-storage.schema.json",
|
||||
"workingDir": "./release/assets/",
|
||||
"account": "<!-- STORAGE ACCOUNT NAME -->",
|
||||
"container": "react-taxonomy-file-explorer",
|
||||
"accessKey": "<!-- ACCESS KEY -->"
|
||||
}
|
|
@ -0,0 +1,20 @@
|
|||
{
|
||||
"$schema": "https://developer.microsoft.com/json-schemas/spfx-build/package-solution.schema.json",
|
||||
"solution": {
|
||||
"name": "react-taxonomy-file-explorer-client-side-solution",
|
||||
"id": "5697a573-1bd1-4ff3-96f1-82263c8eb008",
|
||||
"version": "1.0.0.0",
|
||||
"includeClientSideAssets": true,
|
||||
"isDomainIsolated": false,
|
||||
"developer": {
|
||||
"name": "",
|
||||
"websiteUrl": "",
|
||||
"privacyUrl": "",
|
||||
"termsOfUseUrl": "",
|
||||
"mpnId": "Undefined-1.13.1"
|
||||
}
|
||||
},
|
||||
"paths": {
|
||||
"zippedPackage": "solution/react-taxonomy-file-explorer.sppkg"
|
||||
}
|
||||
}
|
|
@ -0,0 +1,6 @@
|
|||
{
|
||||
"$schema": "https://developer.microsoft.com/json-schemas/core-build/serve.schema.json",
|
||||
"port": 4321,
|
||||
"https": true,
|
||||
"initialPage": "https://enter-your-SharePoint-site/_layouts/workbench.aspx"
|
||||
}
|
|
@ -0,0 +1,4 @@
|
|||
{
|
||||
"$schema": "https://developer.microsoft.com/json-schemas/spfx-build/write-manifests.schema.json",
|
||||
"cdnBasePath": "<!-- PATH TO CDN -->"
|
||||
}
|
|
@ -0,0 +1,16 @@
|
|||
'use strict';
|
||||
|
||||
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.`);
|
||||
|
||||
var getTasks = build.rig.getTasks;
|
||||
build.rig.getTasks = function () {
|
||||
var result = getTasks.call(build.rig);
|
||||
|
||||
result.set('serve', result.get('serve-deprecated'));
|
||||
|
||||
return result;
|
||||
};
|
||||
|
||||
build.initialize(require('gulp'));
|
|
@ -0,0 +1,34 @@
|
|||
{
|
||||
"name": "react-taxonomy-file-explorer",
|
||||
"version": "0.0.1",
|
||||
"private": true,
|
||||
"main": "lib/index.js",
|
||||
"scripts": {
|
||||
"build": "gulp bundle",
|
||||
"clean": "gulp clean",
|
||||
"test": "gulp test"
|
||||
},
|
||||
"dependencies": {
|
||||
"@fluentui/react-file-type-icons": "^8.5.7",
|
||||
"@microsoft/sp-core-library": "1.13.1",
|
||||
"@microsoft/sp-lodash-subset": "1.13.1",
|
||||
"@microsoft/sp-office-ui-fabric-core": "1.13.1",
|
||||
"@microsoft/sp-property-pane": "1.13.1",
|
||||
"@microsoft/sp-webpart-base": "1.13.1",
|
||||
"@pnp/sp": "^2.11.0",
|
||||
"office-ui-fabric-react": "7.174.1",
|
||||
"react": "16.13.1",
|
||||
"react-dom": "16.13.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/react": "16.9.51",
|
||||
"@types/react-dom": "16.9.8",
|
||||
"@microsoft/sp-build-web": "1.13.1",
|
||||
"@microsoft/sp-tslint-rules": "1.13.1",
|
||||
"@microsoft/sp-module-interfaces": "1.13.1",
|
||||
"@microsoft/rush-stack-compiler-3.9": "0.4.47",
|
||||
"gulp": "~4.0.2",
|
||||
"ajv": "~5.2.2",
|
||||
"@types/webpack-env": "1.13.1"
|
||||
}
|
||||
}
|
|
@ -0,0 +1 @@
|
|||
// A file is required to be in the root of the /src directory by the TypeScript compiler
|
|
@ -0,0 +1,8 @@
|
|||
export interface IFileItem {
|
||||
id: string;
|
||||
title: string;
|
||||
extension: string;
|
||||
url: string;
|
||||
termGuid: string[];
|
||||
taxValue: string[];
|
||||
}
|
|
@ -0,0 +1,9 @@
|
|||
import { IFileItem } from "./IFileItem";
|
||||
|
||||
export interface ITermNode {
|
||||
name: string;
|
||||
guid: string;
|
||||
childDocuments: number;
|
||||
children: ITermNode[];
|
||||
subFiles: IFileItem[];
|
||||
}
|
|
@ -0,0 +1,97 @@
|
|||
import { sp } from "@pnp/sp";
|
||||
import { IFileItem } from "../model/IFileItem";
|
||||
|
||||
export class SPService {
|
||||
private listName: string;
|
||||
private fieldName: string;
|
||||
|
||||
constructor (listname: string, fieldname: string) {
|
||||
this.listName = listname;
|
||||
this.fieldName = fieldname;
|
||||
}
|
||||
|
||||
public async getItems (termsetID: string): Promise<IFileItem[]> {
|
||||
const items: any[] = await sp.web.lists.getByTitle(this.listName).items.select('Id', this.fieldName).expand('File').get();
|
||||
const files: IFileItem[] = [];
|
||||
items.forEach(i => {
|
||||
const nameparts = i.File.Name.split('.');
|
||||
const file: IFileItem = {
|
||||
title: i.File.Name,
|
||||
extension: nameparts[nameparts.length - 1],
|
||||
id: i.Id,
|
||||
termGuid: [""],
|
||||
taxValue: [""],
|
||||
url: i.File.LinkingUrl
|
||||
};
|
||||
if (Array.isArray(i[this.fieldName])) {
|
||||
const termguids: string[] = [];
|
||||
const taxvalues: string[] = [];
|
||||
i[this.fieldName].forEach(f => {
|
||||
termguids.push(f.TermGuid.toLowerCase());
|
||||
taxvalues.push(`${f.Label}|${f.TermGuid}`);
|
||||
});
|
||||
file.termGuid = termguids;
|
||||
file.taxValue = taxvalues;
|
||||
}
|
||||
else {
|
||||
file.termGuid = i[this.fieldName] ? [i[this.fieldName].TermGuid.toLowerCase()]:[""];
|
||||
file.taxValue = i[this.fieldName] ? [`${i[this.fieldName].Label.toLowerCase()}|${i[this.fieldName].TermGuid.toLowerCase()}`]:[""];
|
||||
}
|
||||
files.push(file);
|
||||
});
|
||||
return files;
|
||||
}
|
||||
|
||||
public async updateTaxonomyItemByAdd (file: IFileItem, fieldName: string, newTaxonomyValue: string) {
|
||||
const itemID: number = parseInt(file.id);
|
||||
let fieldValues = file.taxValue.join(';');
|
||||
fieldValues += `;${newTaxonomyValue}`;
|
||||
|
||||
// https://blog.aterentiev.com/how-to-easily-update-managed-metadata
|
||||
await sp.web.lists.getByTitle(this.listName).items.getById(itemID).validateUpdateListItem([{
|
||||
ErrorMessage: null,
|
||||
FieldName: fieldName,
|
||||
FieldValue: fieldValues,
|
||||
HasException: false
|
||||
}]);
|
||||
}
|
||||
|
||||
public async updateTaxonomyItemByReplace (file: IFileItem, fieldName: string, newTaxonomyValue: string) {
|
||||
const itemID: number = parseInt(file.id);
|
||||
|
||||
await sp.web.lists.getByTitle(this.listName).items.getById(itemID).validateUpdateListItem([{
|
||||
ErrorMessage: null,
|
||||
FieldName: fieldName,
|
||||
FieldValue: newTaxonomyValue,
|
||||
HasException: false
|
||||
}]);
|
||||
}
|
||||
|
||||
public async newTaxonomyItemByCopy (file: IFileItem, fieldName: string, newTaxonomyValue: string): Promise<IFileItem> {
|
||||
const fileUrl: URL = new URL(file.url);
|
||||
const currentFileNamePart = file.title.replace(`.${file.extension}`, '');
|
||||
const newFilename = `${currentFileNamePart}_Copy.${file.extension}`;
|
||||
const destinationUrl = decodeURI(fileUrl.pathname).replace(file.title, newFilename);
|
||||
await sp.web.getFileByServerRelativePath(decodeURI(fileUrl.pathname)).copyByPath(destinationUrl, false, true);
|
||||
const newFileItemPromise = await sp.web.getFileByServerRelativePath(destinationUrl).getItem();
|
||||
const newFileItem = await newFileItemPromise.get();
|
||||
console.log(newFileItem);
|
||||
const itemID: number = parseInt(newFileItem.Id);
|
||||
|
||||
await sp.web.lists.getByTitle(this.listName).items.getById(itemID).validateUpdateListItem([{
|
||||
ErrorMessage: null,
|
||||
FieldName: fieldName,
|
||||
FieldValue: newTaxonomyValue,
|
||||
HasException: false
|
||||
}]);
|
||||
const newFile: IFileItem = {
|
||||
extension: file.extension,
|
||||
id: itemID.toString(),
|
||||
taxValue: [newTaxonomyValue],
|
||||
termGuid: [newTaxonomyValue.split('|')[1]],
|
||||
title: newFilename,
|
||||
url: fileUrl.host + '/' + destinationUrl
|
||||
};
|
||||
return newFile;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,76 @@
|
|||
import { sp } from "@pnp/sp";
|
||||
import { IOrderedTermInfo } from "@pnp/sp/taxonomy";
|
||||
import { IFileItem } from "../model/IFileItem";
|
||||
import { ITermNode } from "../model/ITermNode";
|
||||
|
||||
export class TaxonomyService {
|
||||
public async getTermsetInfo (fieldName: string): Promise<string> {
|
||||
const mmFieldInfo = await sp.web.fields.getByInternalNameOrTitle(fieldName).get();
|
||||
const parser = new DOMParser();
|
||||
const xmlField = parser.parseFromString(mmFieldInfo.SchemaXml, "text/xml");
|
||||
const properties = xmlField.getElementsByTagName("ArrayOfProperty")[0].childNodes;
|
||||
let termsetID: string = "";
|
||||
properties.forEach(prop => {
|
||||
if (prop.childNodes[0].textContent == "TermSetId") {
|
||||
termsetID = prop.childNodes[1].textContent;
|
||||
}
|
||||
});
|
||||
return termsetID;
|
||||
}
|
||||
|
||||
public async getTermset (termsetID: string) {
|
||||
// list all the terms available in this term set by term set id
|
||||
const termset: IOrderedTermInfo[] = await sp.termStore.sets.getById(termsetID).getAllChildrenAsOrderedTree();
|
||||
const termnodes: ITermNode[] = [];
|
||||
termset.forEach(async ti => {
|
||||
const tn = this.getTermnode(ti);
|
||||
termnodes.push(tn);
|
||||
});
|
||||
return termnodes;
|
||||
}
|
||||
|
||||
public incorporateFiles (terms: ITermNode[], files: IFileItem[]): ITermNode[] {
|
||||
terms.forEach(term => {
|
||||
term = this.incorporateFilesIntoTerm(term, files);
|
||||
});
|
||||
return terms;
|
||||
}
|
||||
|
||||
private getTermnode (term: IOrderedTermInfo): ITermNode {
|
||||
const node: ITermNode = {
|
||||
guid: term.id,
|
||||
childDocuments: 0,
|
||||
name: term.defaultLabel,
|
||||
children: [],
|
||||
subFiles: []
|
||||
};
|
||||
if (term.childrenCount > 0) {
|
||||
const ctnodes: ITermNode[] = [];
|
||||
term.children.forEach(ct => {
|
||||
const ctnode: ITermNode = this.getTermnode(ct);
|
||||
node.childDocuments += ctnode.childDocuments;
|
||||
ctnodes.push(ctnode);
|
||||
});
|
||||
node.children = ctnodes;
|
||||
}
|
||||
return node;
|
||||
}
|
||||
|
||||
private incorporateFilesIntoTerm (term: ITermNode, files: IFileItem[]): ITermNode {
|
||||
term.childDocuments = 0;
|
||||
term.subFiles = [];
|
||||
if (term.children.length > 0) {
|
||||
term.children.forEach(ct => {
|
||||
ct = this.incorporateFilesIntoTerm(ct, files);
|
||||
term.childDocuments += ct.childDocuments;
|
||||
});
|
||||
}
|
||||
files.forEach(fi => {
|
||||
if (fi.termGuid.indexOf(term.guid.toLowerCase()) > -1) {
|
||||
term.childDocuments++;
|
||||
term.subFiles.push(fi);
|
||||
}
|
||||
});
|
||||
return term;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,29 @@
|
|||
{
|
||||
"$schema": "https://developer.microsoft.com/json-schemas/spfx/client-side-web-part-manifest.schema.json",
|
||||
"id": "e086d23e-079e-42c1-aecf-d042a264ddac",
|
||||
"alias": "TaxonomyFileExplorerWebPart",
|
||||
"componentType": "WebPart",
|
||||
|
||||
// The "*" signifies that the version should be taken from the package.json
|
||||
"version": "*",
|
||||
"manifestVersion": 2,
|
||||
|
||||
// If true, the component can only be installed on sites where Custom Script is allowed.
|
||||
// Components that allow authors to embed arbitrary script code should set this to true.
|
||||
// https://support.office.com/en-us/article/Turn-scripting-capabilities-on-or-off-1f2c515f-5d7e-448a-9fd7-835da935584f
|
||||
"requiresCustomScript": false,
|
||||
"supportedHosts": ["SharePointWebPart", "TeamsPersonalApp", "TeamsTab", "SharePointFullPage"],
|
||||
"supportsThemeVariants": true,
|
||||
|
||||
"preconfiguredEntries": [{
|
||||
"groupId": "5c03119e-3074-46fd-976b-c60198311f70", // Other
|
||||
"group": { "default": "Other" },
|
||||
"title": { "default": "Taxonomy File Explorer" },
|
||||
"description": { "default": "A structural view on files, based on Managed Metadata column" },
|
||||
"officeFabricIconFontName": "BulletedTreeList",
|
||||
"properties": {
|
||||
"listName": "Documents",
|
||||
"fieldName": "FolderStructure"
|
||||
}
|
||||
}]
|
||||
}
|
|
@ -0,0 +1,73 @@
|
|||
import * as React from 'react';
|
||||
import * as ReactDom from 'react-dom';
|
||||
import { Version } from '@microsoft/sp-core-library';
|
||||
import {
|
||||
IPropertyPaneConfiguration,
|
||||
PropertyPaneTextField
|
||||
} from '@microsoft/sp-property-pane';
|
||||
import { sp } from "@pnp/sp/presets/all";
|
||||
import { BaseClientSideWebPart } from '@microsoft/sp-webpart-base';
|
||||
|
||||
import * as strings from 'TaxonomyFileExplorerWebPartStrings';
|
||||
import { TaxonomyFileExplorer } from './components/TaxonomyFileExplorer';
|
||||
import { ITaxonomyFileExplorerProps } from './components/ITaxonomyFileExplorerProps';
|
||||
|
||||
export interface ITaxonomyFileExplorerWebPartProps {
|
||||
fieldName: string;
|
||||
listName: string;
|
||||
}
|
||||
|
||||
export default class TaxonomyFileExplorerWebPart extends BaseClientSideWebPart<ITaxonomyFileExplorerWebPartProps> {
|
||||
protected onInit(): Promise<void> {
|
||||
return super.onInit().then(_ => {
|
||||
sp.setup({
|
||||
spfxContext: this.context
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
public render(): void {
|
||||
const element: React.ReactElement<ITaxonomyFileExplorerProps> = React.createElement(
|
||||
TaxonomyFileExplorer,
|
||||
{
|
||||
fieldName: this.properties.fieldName,
|
||||
listName: this.properties.listName
|
||||
}
|
||||
);
|
||||
|
||||
ReactDom.render(element, this.domElement);
|
||||
}
|
||||
|
||||
protected onDispose(): void {
|
||||
ReactDom.unmountComponentAtNode(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: [
|
||||
PropertyPaneTextField('listName', {
|
||||
label: strings.ListnameFieldLabel
|
||||
}),
|
||||
PropertyPaneTextField('fieldName', {
|
||||
label: strings.FieldnameFieldLabel
|
||||
})
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
};
|
||||
}
|
||||
}
|
|
@ -0,0 +1,14 @@
|
|||
.fileLabel {
|
||||
padding-left: 8px;
|
||||
li {
|
||||
list-style: none;
|
||||
}
|
||||
|
||||
.filelink {
|
||||
margin-left: 6px;
|
||||
text-decoration: none;
|
||||
font-style: italic;
|
||||
font-size: 0.9em;
|
||||
color: "[theme: themePrimary, default: #0078d7]";
|
||||
}
|
||||
}
|
|
@ -0,0 +1,20 @@
|
|||
import * as React from 'react';
|
||||
import styles from './FileLabel.module.scss';
|
||||
import { getFileTypeIconProps, initializeFileTypeIcons } from '@fluentui/react-file-type-icons';
|
||||
import { Icon } from 'office-ui-fabric-react/lib/Icon';
|
||||
import { IFileLabelProps } from './IFileLabelProps';
|
||||
|
||||
initializeFileTypeIcons(undefined);
|
||||
|
||||
export const FileLabel: React.FC<IFileLabelProps> = (props) => {
|
||||
const drag = (ev) => {
|
||||
ev.dataTransfer.setData("text/plain", JSON.stringify(props.file));
|
||||
};
|
||||
|
||||
return (
|
||||
<li className={styles.fileLabel} draggable={true} onDragStart={drag}>
|
||||
<Icon {...getFileTypeIconProps({ extension: props.file.extension, size: 16 })} />
|
||||
<a className={styles.filelink} draggable={false} href={props.file.url}>{props.file.title}</a>
|
||||
</li>
|
||||
);
|
||||
};
|
|
@ -0,0 +1,5 @@
|
|||
import { IFileItem } from "../../../model/IFileItem";
|
||||
|
||||
export interface IFileLabelProps {
|
||||
file: IFileItem;
|
||||
}
|
|
@ -0,0 +1,4 @@
|
|||
export interface ITaxonomyFileExplorerProps {
|
||||
fieldName: string;
|
||||
listName: string;
|
||||
}
|
|
@ -0,0 +1,12 @@
|
|||
import { IFileItem } from "../../../model/IFileItem";
|
||||
import { ITermNode } from "../../../model/ITermNode";
|
||||
|
||||
export interface ITermLabelProps {
|
||||
node: ITermNode;
|
||||
selectedNode: string;
|
||||
renderFiles: (files: IFileItem[]) => void;
|
||||
resetChecked: (s: string) => void;
|
||||
addTerm: (file: IFileItem, newValue: string) => void;
|
||||
replaceTerm: (file: IFileItem, newValue: string) => void;
|
||||
copyFile: (file: IFileItem, newValue: string) => void;
|
||||
}
|
|
@ -0,0 +1,25 @@
|
|||
@import '~office-ui-fabric-react/dist/sass/References.scss';
|
||||
|
||||
.taxonomyFileExplorer {
|
||||
.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;
|
||||
color: "[theme: themePrimary, default: #0078d7]";
|
||||
padding: 20px;
|
||||
}
|
||||
|
||||
.column {
|
||||
@include ms-Grid-col;
|
||||
@include ms-lg6;
|
||||
}
|
||||
|
||||
ul {
|
||||
list-style: none;
|
||||
padding-inline-start: 0px;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,116 @@
|
|||
import * as React from 'react';
|
||||
import styles from './TaxonomyFileExplorer.module.scss';
|
||||
import { ITaxonomyFileExplorerProps } from './ITaxonomyFileExplorerProps';
|
||||
import { IFileItem } from '../../../model/IFileItem';
|
||||
import { ITermNode } from '../../../model/ITermNode';
|
||||
import { TaxonomyService } from '../../../services/TaxonomyService';
|
||||
import { SPService } from '../../../services/SPService';
|
||||
import { FileLabel } from './FileLabel';
|
||||
import { TermLabel } from './TermLabel';
|
||||
|
||||
export const TaxonomyFileExplorer: React.FC<ITaxonomyFileExplorerProps> = (props) => {
|
||||
const [spSvc, setSpSvc] = React.useState<SPService>();
|
||||
const [fileItems, setFileItems] = React.useState<IFileItem[]>([]);
|
||||
const [terms, setTerms] = React.useState<ITermNode[]>([]);
|
||||
const [shownFiles, setShownFiles] = React.useState<IFileItem[]>([]);
|
||||
const [selectedTermnode, setSelectedTermnode] = React.useState<string>("");
|
||||
|
||||
const buildTree = async () => {
|
||||
const taxSvc: TaxonomyService = new TaxonomyService();
|
||||
const termsetID = await taxSvc.getTermsetInfo(props.fieldName);
|
||||
let termnodetree: ITermNode[];
|
||||
const termnodetreeStr = sessionStorage.getItem(`Termtree_${termsetID}`);
|
||||
if (termnodetreeStr === null) {
|
||||
termnodetree = await taxSvc.getTermset(termsetID);
|
||||
sessionStorage.setItem(`Termtree_${termsetID}`, JSON.stringify(termnodetree));
|
||||
}
|
||||
else {
|
||||
termnodetree = JSON.parse(termnodetreeStr);
|
||||
}
|
||||
|
||||
const spSrvc: SPService = new SPService(props.listName, props.fieldName);
|
||||
const files = await spSrvc.getItems(termsetID);
|
||||
setSpSvc(spSrvc);
|
||||
updateFiles(files, termnodetree);
|
||||
};
|
||||
|
||||
const updateFiles = (files: IFileItem[], termnodetree: ITermNode[]) => {
|
||||
const taxSvc: TaxonomyService = new TaxonomyService();
|
||||
termnodetree = taxSvc.incorporateFiles(termnodetree, files);
|
||||
setFileItems(files);
|
||||
setTerms(termnodetree);
|
||||
};
|
||||
|
||||
const renderFiles = (files: IFileItem[]) => {
|
||||
setShownFiles(files);
|
||||
};
|
||||
|
||||
const resetChecked = (newNodeID: string) => {
|
||||
setSelectedTermnode(newNodeID);
|
||||
};
|
||||
|
||||
const reloadFiles = (file: IFileItem) => {
|
||||
const newFiles: IFileItem[] = [];
|
||||
fileItems.forEach(fi => {
|
||||
if (fi.id === file.id && fi.url === file.url) {
|
||||
newFiles.push(file);
|
||||
}
|
||||
else {
|
||||
newFiles.push(fi);
|
||||
}
|
||||
});
|
||||
updateFiles(newFiles, terms);
|
||||
};
|
||||
|
||||
const loadNewFiles = (file: IFileItem) => {
|
||||
const newFiles: IFileItem[] = [file].concat(fileItems);
|
||||
updateFiles(newFiles, terms);
|
||||
};
|
||||
|
||||
const addTerm = (file: IFileItem, newTaxonomyValue: string) => {
|
||||
spSvc.updateTaxonomyItemByAdd(file, props.fieldName, newTaxonomyValue);
|
||||
reloadFiles(file);
|
||||
};
|
||||
|
||||
const replaceTerm = (file: IFileItem, newTaxonomyValue: string) => {
|
||||
spSvc.updateTaxonomyItemByReplace(file, props.fieldName, newTaxonomyValue);
|
||||
reloadFiles(file);
|
||||
};
|
||||
|
||||
const copyFile = async (file: IFileItem, newTaxonomyValue: string) => {
|
||||
const newFile = await spSvc.newTaxonomyItemByCopy(file, props.fieldName, newTaxonomyValue);
|
||||
loadNewFiles(newFile);
|
||||
};
|
||||
|
||||
React.useEffect(() => {
|
||||
buildTree();
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<div className={ styles.taxonomyFileExplorer }>
|
||||
<div className={ styles.container }>
|
||||
<div className={ styles.row }>
|
||||
<div className={ styles.column }>
|
||||
<ul>
|
||||
{terms.map(nc => { return <TermLabel node={nc}
|
||||
renderFiles={renderFiles}
|
||||
resetChecked={resetChecked}
|
||||
selectedNode={selectedTermnode}
|
||||
addTerm={addTerm}
|
||||
replaceTerm={replaceTerm}
|
||||
copyFile={copyFile} />; })}
|
||||
</ul>
|
||||
</div>
|
||||
<div className={ styles.column }>
|
||||
{shownFiles.length > 0 &&
|
||||
<ul>
|
||||
{shownFiles.map(f => {
|
||||
return <FileLabel file={f} />;
|
||||
})}
|
||||
</ul>}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
|
@ -0,0 +1,31 @@
|
|||
.termLabel {
|
||||
li {
|
||||
list-style: none;
|
||||
}
|
||||
.liFilled {
|
||||
border-left-color: "[theme: themePrimary, default: #0078d7]";
|
||||
border-left-style: dotted;
|
||||
border-left-width: 1px;
|
||||
}
|
||||
ul {
|
||||
padding-inline-start: 16px;
|
||||
}
|
||||
.label {
|
||||
margin-bottom: 4px;
|
||||
cursor: pointer;
|
||||
}
|
||||
.checkedLabel {
|
||||
border: 1px dotted "[theme: themePrimary, default: #0078d7]";
|
||||
}
|
||||
.icon {
|
||||
margin-right: 6px;
|
||||
cursor: pointer;
|
||||
}
|
||||
.emptyicon {
|
||||
margin-right: 15px;
|
||||
}
|
||||
.fileCount {
|
||||
font-weight: 600;
|
||||
padding-left: 18px;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,119 @@
|
|||
import * as React from 'react';
|
||||
import styles from './TermLabel.module.scss';
|
||||
import { ContextualMenu, IContextualMenuItem } from "office-ui-fabric-react/lib/ContextualMenu";
|
||||
import { Icon } from 'office-ui-fabric-react/lib/Icon';
|
||||
import { ITermLabelProps } from "./ITermLabelProps";
|
||||
import { IFileItem } from '../../../model/IFileItem';
|
||||
|
||||
export const TermLabel: React.FC<ITermLabelProps> = (props) => {
|
||||
const linkRef = React.useRef(null);
|
||||
const [showChildren, setShowChildren] = React.useState<boolean>(true);
|
||||
const [countDocuments, setCountDocuments] = React.useState<number>(props.node.childDocuments);
|
||||
const [showContextualMenu, setShowContextualMenu] = React.useState<boolean>(false);
|
||||
const [droppedFile, setDroppedFile] = React.useState<IFileItem>();
|
||||
|
||||
const toggleIcon = () => {
|
||||
setShowChildren(!showChildren);
|
||||
};
|
||||
|
||||
const nodeSelected = () => {
|
||||
props.resetChecked(props.node.guid);
|
||||
props.renderFiles(props.node.subFiles);
|
||||
};
|
||||
|
||||
const hideContextualMenu = () => {
|
||||
setShowContextualMenu(false);
|
||||
};
|
||||
|
||||
const drop = (ev) => {
|
||||
ev.preventDefault();
|
||||
var data = ev.dataTransfer.getData("text");
|
||||
const file: IFileItem = JSON.parse(data);
|
||||
setDroppedFile(file);
|
||||
if (ev.ctrlKey) {
|
||||
setShowContextualMenu(true);
|
||||
}
|
||||
else {
|
||||
addNewTerm(file); // Default option: Simply add the new (target) term to existing ones
|
||||
}
|
||||
};
|
||||
|
||||
const dragOver = (ev) => {
|
||||
ev.preventDefault();
|
||||
};
|
||||
|
||||
const addNewTerm = (file: IFileItem) => {
|
||||
const newTaxonomyValue = `${props.node.name}|${props.node.guid}`;
|
||||
file.termGuid.push(props.node.guid);
|
||||
file.taxValue.push(newTaxonomyValue);
|
||||
console.log(file);
|
||||
props.addTerm(file, newTaxonomyValue);
|
||||
};
|
||||
|
||||
const replaceByNewTerm = (file: IFileItem) => {
|
||||
const newTaxonomyValue = `${props.node.name}|${props.node.guid}`;
|
||||
file.termGuid = [props.node.guid];
|
||||
file.taxValue = [newTaxonomyValue];
|
||||
console.log(file);
|
||||
props.addTerm(file, newTaxonomyValue);
|
||||
};
|
||||
|
||||
const copyWithNewTerm = (file: IFileItem) => {
|
||||
const newTaxonomyValue = `${props.node.name}|${props.node.guid}`;
|
||||
console.log(file);
|
||||
props.copyFile(file, newTaxonomyValue);
|
||||
};
|
||||
|
||||
const currentExpandIcon = showChildren? <Icon className={styles.icon} iconName="ChevronDown" onClick={toggleIcon} />:<Icon className={styles.icon} iconName="ChevronRight" onClick={toggleIcon} />;
|
||||
const menuItems: IContextualMenuItem[] = [
|
||||
{
|
||||
key: 'copyItem',
|
||||
text: 'Create new file with term (Copy)',
|
||||
onClick: () => copyWithNewTerm(droppedFile)
|
||||
},
|
||||
{
|
||||
key: 'moveItem',
|
||||
text: 'Replace with new term (Move)',
|
||||
onClick: () => replaceByNewTerm(droppedFile)
|
||||
},
|
||||
{
|
||||
key: 'linkItem',
|
||||
text: 'Add new term (Link)',
|
||||
onClick: () => addNewTerm(droppedFile)
|
||||
}];
|
||||
React.useEffect(() => {
|
||||
if (props.selectedNode===props.node.guid) {
|
||||
props.renderFiles(props.node.subFiles);
|
||||
}
|
||||
if (props.node.childDocuments !== countDocuments) {
|
||||
setCountDocuments(props.node.childDocuments);
|
||||
}
|
||||
}, [props.node.subFiles]);
|
||||
return (
|
||||
<li className={styles.termLabel}>
|
||||
<div ref={linkRef} className={`${styles.label} ${props.selectedNode===props.node.guid ? styles.checkedLabel : ""}`} onClick={nodeSelected} onDrop={drop} onDragOver={dragOver}>
|
||||
<label>
|
||||
{props.node.children.length > 0 ? currentExpandIcon : <i className={styles.emptyicon}> </i>}
|
||||
<Icon className={styles.icon} iconName="FabricFolder" />
|
||||
{props.node.name}{countDocuments>0?<span className={styles.fileCount}>{countDocuments}</span>:""}
|
||||
</label>
|
||||
</div>
|
||||
<ContextualMenu
|
||||
items={menuItems}
|
||||
hidden={!showContextualMenu}
|
||||
target={linkRef}
|
||||
onItemClick={hideContextualMenu}
|
||||
onDismiss={hideContextualMenu}
|
||||
/>
|
||||
{showChildren && <ul className={`${props.node.children.length > 0 ? styles.liFilled : ""}`}>
|
||||
{props.node.children.map(nc => { return <TermLabel node={nc}
|
||||
renderFiles={props.renderFiles}
|
||||
resetChecked={props.resetChecked}
|
||||
selectedNode={props.selectedNode}
|
||||
addTerm={props.addTerm}
|
||||
replaceTerm={props.replaceTerm}
|
||||
copyFile={props.copyFile} />; })}
|
||||
</ul>}
|
||||
</li>
|
||||
);
|
||||
};
|