Fixed exploits in react-events-aggregator
Before Width: | Height: | Size: 426 KiB |
Before Width: | Height: | Size: 2.9 MiB |
Before Width: | Height: | Size: 344 KiB |
Before Width: | Height: | Size: 1.9 MiB |
Before Width: | Height: | Size: 1.9 MiB |
Before Width: | Height: | Size: 1.9 MiB |
Before Width: | Height: | Size: 2.9 MiB |
Before Width: | Height: | Size: 594 KiB |
Before Width: | Height: | Size: 471 KiB |
Before Width: | Height: | Size: 506 KiB |
Before Width: | Height: | Size: 1.9 MiB |
Before Width: | Height: | Size: 2.7 MiB |
Before Width: | Height: | Size: 929 KiB |
Before Width: | Height: | Size: 2.7 MiB |
|
@ -1,26 +0,0 @@
|
|||
name: $(TeamProject)_$(BuildDefinitionName)_$(SourceBranchName)_$(Date:yyyyMMdd)$(Rev:.r)
|
||||
resources:
|
||||
- repo: self
|
||||
|
||||
trigger:
|
||||
branches:
|
||||
include:
|
||||
- master
|
||||
- develop
|
||||
|
||||
stages:
|
||||
- stage: build
|
||||
displayName: build
|
||||
jobs:
|
||||
- template: ./azure-pipelines-build-template.yml
|
||||
parameters:
|
||||
name: 'buildsolution'
|
||||
- stage: 'deployqa'
|
||||
# uncomment if you want deployments to occur only for a specific branch
|
||||
#condition: and(succeeded(), eq(variables['Build.SourceBranch'], 'refs/heads/develop'))
|
||||
jobs:
|
||||
- template: ./azure-pipelines-deploy-template.yml
|
||||
parameters:
|
||||
job_name: deploy_solution
|
||||
target_environment: 'qa'
|
||||
variable_group_name: qa_configuration
|
|
@ -1,20 +0,0 @@
|
|||
{
|
||||
"$schema": "https://developer.microsoft.com/json-schemas/spfx-build/config.2.0.schema.json",
|
||||
"version": "2.0",
|
||||
"bundles": {
|
||||
"personal-apps-web-part": {
|
||||
"components": [
|
||||
{
|
||||
"entrypoint": "./lib/webparts/personalApps/PersonalAppsWebPart.js",
|
||||
"manifest": "./src/webparts/personalApps/PersonalAppsWebPart.manifest.json"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"externals": {},
|
||||
"localizedResources": {
|
||||
"PersonalAppsWebPartStrings": "lib/webparts/personalApps/loc/{locale}.js",
|
||||
"ControlStrings": "node_modules/@pnp/spfx-controls-react/lib/loc/{locale}.js",
|
||||
"PropertyControlStrings": "node_modules/@pnp/spfx-property-controls/lib/loc/{locale}.js"
|
||||
}
|
||||
}
|
|
@ -1,4 +0,0 @@
|
|||
{
|
||||
"$schema": "https://developer.microsoft.com/json-schemas/spfx-build/copy-assets.schema.json",
|
||||
"deployCdnPath": "temp/deploy"
|
||||
}
|
|
@ -1,7 +0,0 @@
|
|||
{
|
||||
"$schema": "https://developer.microsoft.com/json-schemas/spfx-build/deploy-azure-storage.schema.json",
|
||||
"workingDir": "./temp/deploy/",
|
||||
"account": "<!-- STORAGE ACCOUNT NAME -->",
|
||||
"container": "personal-apps",
|
||||
"accessKey": "<!-- ACCESS KEY -->"
|
||||
}
|
|
@ -1,24 +0,0 @@
|
|||
{
|
||||
"preset": "@voitanos/jest-preset-spfx-react16",
|
||||
"rootDir": "../src",
|
||||
"collectCoverageFrom": [
|
||||
"<rootDir>/**/*.{ts,tsx}",
|
||||
"!<rootDir>/**/*.scss.*",
|
||||
"!<rootDir>/loc/**/*.*"
|
||||
],
|
||||
"coverageReporters": [
|
||||
"text",
|
||||
"json",
|
||||
"lcov",
|
||||
"text-summary",
|
||||
"cobertura"
|
||||
],
|
||||
"reporters": [
|
||||
"default",
|
||||
["jest-junit", {
|
||||
"suiteName": "jest tests",
|
||||
"outputDirectory": "temp/test/junit",
|
||||
"outputName": "junit.xml"
|
||||
}]
|
||||
]
|
||||
}
|
|
@ -1,18 +0,0 @@
|
|||
{
|
||||
"$schema": "https://developer.microsoft.com/json-schemas/spfx-build/package-solution.schema.json",
|
||||
"solution": {
|
||||
"name": "personal-apps-client-side-solution",
|
||||
"id": "ab3683c6-0388-47a2-84f5-90d01259d0ed",
|
||||
"version": "1.0.0.0",
|
||||
"includeClientSideAssets": true,
|
||||
"skipFeatureDeployment": true,
|
||||
"isDomainIsolated": false,
|
||||
"webApiPermissionRequests": [{
|
||||
"resource": "Microsoft Graph",
|
||||
"scope": "User.ReadWrite.All"
|
||||
}]
|
||||
},
|
||||
"paths": {
|
||||
"zippedPackage": "solution/personal-apps.sppkg"
|
||||
}
|
||||
}
|
|
@ -1,10 +0,0 @@
|
|||
{
|
||||
"$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/"
|
||||
}
|
||||
}
|
|
@ -1,4 +0,0 @@
|
|||
{
|
||||
"$schema": "https://developer.microsoft.com/json-schemas/spfx-build/write-manifests.schema.json",
|
||||
"cdnBasePath": "<!-- PATH TO CDN -->"
|
||||
}
|
|
@ -1,54 +0,0 @@
|
|||
'use strict';
|
||||
|
||||
// check if gulp dist was called
|
||||
if (process.argv.indexOf('dist') !== -1) {
|
||||
// add ship options to command call
|
||||
process.argv.push('--ship');
|
||||
}
|
||||
|
||||
const path = require('path');
|
||||
const gulp = require('gulp');
|
||||
const build = require('@microsoft/sp-build-web');
|
||||
const gulpSequence = require('gulp-sequence');
|
||||
|
||||
build.addSuppression(`Warning - [sass] The local CSS class 'ms-Grid' is not camelCase and will not be type-safe.`);
|
||||
|
||||
// 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
|
||||
*/
|
||||
const bundleAnalyzer = require('webpack-bundle-analyzer');
|
||||
|
||||
build.configureWebpack.mergeConfig({
|
||||
|
||||
additionalConfiguration: (generatedConfiguration) => {
|
||||
const lastDirName = path.basename(__dirname);
|
||||
const dropPath = path.join(__dirname, 'temp', 'stats');
|
||||
generatedConfiguration.plugins.push(new bundleAnalyzer.BundleAnalyzerPlugin({
|
||||
openAnalyzer: false,
|
||||
analyzerMode: 'static',
|
||||
reportFilename: path.join(dropPath, `${lastDirName}.stats.html`),
|
||||
generateStatsFile: true,
|
||||
statsFilename: path.join(dropPath, `${lastDirName}.stats.json`),
|
||||
logLevel: 'error'
|
||||
}));
|
||||
|
||||
return generatedConfiguration;
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
|
||||
/**
|
||||
* Custom Framework Specific gulp tasks
|
||||
*/
|
||||
|
||||
|
||||
build.initialize(gulp);
|
|
@ -1,67 +0,0 @@
|
|||
{
|
||||
"name": "personal-apps",
|
||||
"version": "0.0.1",
|
||||
"private": true,
|
||||
"main": "lib/index.js",
|
||||
"engines": {
|
||||
"node": ">=0.10.0"
|
||||
},
|
||||
"scripts": {
|
||||
"build": "gulp bundle",
|
||||
"clean": "gulp clean",
|
||||
"preversion": "node ./tools/pre-version.js",
|
||||
"postversion": "gulp dist",
|
||||
"test": "./node_modules/.bin/jest --config ./config/jest.config.json",
|
||||
"test:watch": "./node_modules/.bin/jest --config ./config/jest.config.json --watchAll"
|
||||
},
|
||||
"dependencies": {
|
||||
"@material-ui/core": "^4.9.9",
|
||||
"@material-ui/icons": "^4.9.1",
|
||||
"@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",
|
||||
"@pnp/pnpjs": "^2.0.3",
|
||||
"@pnp/spfx-controls-react": "^1.17.0",
|
||||
"@pnp/spfx-property-controls": "1.16.0",
|
||||
"@types/es6-promise": "0.0.33",
|
||||
"@types/jquery": "^3.3.33",
|
||||
"@types/react": "16.8.8",
|
||||
"@types/react-dom": "16.8.3",
|
||||
"@types/webpack-env": "1.13.1",
|
||||
"jquery": "^3.4.1",
|
||||
"material-table": "^1.57.2",
|
||||
"office-ui-fabric-react": "^7.83.1",
|
||||
"react": "16.8.5",
|
||||
"react-dom": "16.8.5"
|
||||
},
|
||||
"resolutions": {
|
||||
"@types/react": "16.8.8"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@microsoft/microsoft-graph-types": "^1.12.0",
|
||||
"@microsoft/rush-stack-compiler-3.7": "^0.2.x",
|
||||
"@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",
|
||||
"@types/react": "^16.9.17",
|
||||
"@voitanos/jest-preset-spfx-react16": "^1.3.2",
|
||||
"ajv": "~5.2.2",
|
||||
"gulp": "~3.9.1",
|
||||
"gulp-sequence": "1.0.0",
|
||||
"jest": "^23.6.0",
|
||||
"jest-junit": "^10.0.0",
|
||||
"lodash": "^4.17.15",
|
||||
"spfx-uifabric-themes": "^0.8.0",
|
||||
"typescript": "~3.7.x",
|
||||
"webpack-bundle-analyzer": "^3.6.0"
|
||||
},
|
||||
"jest-junit": {
|
||||
"output": "temp/test/junit/junit.xml",
|
||||
"usePathForSuiteName": "true"
|
||||
}
|
||||
}
|
|
@ -16,7 +16,31 @@
|
|||
"@microsoft/sp-office-ui-fabric-core": "~1.4.1",
|
||||
"@types/webpack-env": ">=1.12.1 <1.14.0",
|
||||
"minimist": ">=1.2.3",
|
||||
"acorn": ">=5.7.4"
|
||||
"acorn": ">=5.7.4",
|
||||
"kind-of": ">=6.0.3",
|
||||
"cryptiles": ">=4.1.2",
|
||||
"fstream": ">=1.0.12",
|
||||
"js-yaml": ">=3.13.1",
|
||||
"lodash.merge": ">=4.6.2",
|
||||
"macaddress": ">=0.2.9",
|
||||
"node.extend": ">=1.1.7",
|
||||
"randomatic": ">=3.0.0",
|
||||
"tar": ">=2.2.2",
|
||||
"atob": ">=2.1.0",
|
||||
"extend": ">=3.0.2",
|
||||
"growl": ">=1.10.0",
|
||||
"lodash.mergewith": ">=4.6.2",
|
||||
"mem": ">=4.0.0",
|
||||
"mixin-deep": ">=1.3.2",
|
||||
"open": ">=6.0.0",
|
||||
"set-value": ">=2.0.1",
|
||||
"base64-url": ">=2.0.0",
|
||||
"fresh": ">=0.5.2",
|
||||
"handlebars": ">=4.3.0",
|
||||
"lodash": ">=4.17.12",
|
||||
"merge": ">=1.2.1",
|
||||
"morgan": ">=1.9.1",
|
||||
"stringstream": ">=0.0.6"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@microsoft/sp-build-web": "~1.4.1",
|
||||
|
|
|
@ -1,75 +1,13 @@
|
|||
# SharePoint FrameWork client-side web part samples
|
||||
|
||||
# React My Personal Apps (Links)
|
||||
Samples around the SharePoint Framework client-side web parts to demonstrate different capabilities and possibilities on the framework. Each sample has it's own dedicated readme file to explain setup instructions and demonstrated capability.
|
||||
|
||||
## Summary
|
||||
The Web Part My Personal Apps allows the user to define links to Applications or Sites for quick access.
|
||||
You can head directly to the folders below and start looking around if you'd like. But if you're looking for something specific, we've grouped our samples into various categories on our [
|
||||
SharePoint Framework Client-Side Web Part Samples site](https://sharepoint.github.io/sp-dev-fx-webparts):
|
||||
|
||||
This Web Part uses MSgraph Open Extension to save the personal information on user object.
|
||||
|
||||
|
||||
![Birthdays Web Part](./assets/image14.png)
|
||||
|
||||
![Birthdays Web Part](./assets/image13.png)
|
||||
|
||||
![Birthdays Web Part](./assets/image12.png)
|
||||
|
||||
![PersonalApps](./assets/Image2.png)
|
||||
|
||||
![PersonalApps](./assets/Image3.png)
|
||||
|
||||
![PersonalApps](./assets/Image4.png)
|
||||
|
||||
![PersonalApps](./assets/Image5.png)
|
||||
|
||||
![PersonalApps](./assets/Image06.png)
|
||||
|
||||
![PersonalApps](./assets/Image7.png)
|
||||
|
||||
![PersonalApps](./assets/Image8.png)
|
||||
|
||||
![PersonalApps](./assets/Image9.png)
|
||||
|
||||
![PersonalApps](./assets/Image10.png)
|
||||
|
||||
## Used SharePoint Framework Version
|
||||
|
||||
![drop](https://img.shields.io/badge/version-1.10.0-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)
|
||||
|
||||
## Solution
|
||||
|
||||
Solution|Author(s)
|
||||
--------|---------
|
||||
react My Personal Apps|João Mendes
|
||||
|
||||
## Version history
|
||||
|
||||
Version|Date|Comments
|
||||
-------|----|--------
|
||||
1.0.0|April 9, 2020|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
|
||||
|
||||
Please follow all the steps:
|
||||
|
||||
- Clone this repository
|
||||
- in the command line run:
|
||||
- `npm install`
|
||||
- `gulp build`
|
||||
- `gulp bundle --ship`
|
||||
- `gulp package-solution --ship`
|
||||
- Add and deploy package to your tenant's App Catalog
|
||||
- Go to **API Access** - from **SharePoint Admin Center** new experience, and **Approve** the permission to use Microsoft Graph scope **User.ReadWrite.All**
|
||||
|
||||
|
||||
<img src="https://telemetry.sharepointpnp.com/sp-dev-fx-webparts/samples/react-my-personal-apps" />
|
||||
- [By Framework](https://sharepoint.github.io/sp-dev-fx-webparts/samples/framework/)
|
||||
- [By SPFx Version](https://sharepoint.github.io/sp-dev-fx-webparts/samples/spfx/)
|
||||
- [By Compatibility](https://sharepoint.github.io/sp-dev-fx-webparts/samples/compatibility/)
|
||||
- [By Year](https://sharepoint.github.io/sp-dev-fx-webparts/samples/year/)
|
||||
- [By Author](https://sharepoint.github.io/sp-dev-fx-webparts/samples/author/)
|
||||
- [All](https://sharepoint.github.io/sp-dev-fx-webparts/samples/all/)
|
|
@ -1,24 +0,0 @@
|
|||
{
|
||||
"themePrimary": "#6264a7",
|
||||
"themeLighterAlt": "#f7f7fb",
|
||||
"themeLighter": "#e1e1f1",
|
||||
"themeLight": "#c8c9e4",
|
||||
"themeTertiary": "#989ac9",
|
||||
"themeSecondary": "#7173b0",
|
||||
"themeDarkAlt": "#585a95",
|
||||
"themeDark": "#4a4c7e",
|
||||
"themeDarker": "#37385d",
|
||||
"neutralLighterAlt": "#0b0b0b",
|
||||
"neutralLighter": "#151515",
|
||||
"neutralLight": "#252525",
|
||||
"neutralQuaternaryAlt": "#2f2f2f",
|
||||
"neutralQuaternary": "#373737",
|
||||
"neutralTertiaryAlt": "#595959",
|
||||
"neutralTertiary": "#c8c8c8",
|
||||
"neutralSecondary": "#d0d0d0",
|
||||
"neutralPrimaryAlt": "#dadada",
|
||||
"neutralPrimary": "#ffffff",
|
||||
"neutralDark": "#f4f4f4",
|
||||
"black": "#f8f8f8",
|
||||
"white": "#000000"
|
||||
}
|
|
@ -1,24 +0,0 @@
|
|||
{
|
||||
"themePrimary": "#6264a7",
|
||||
"themeLighterAlt": "#f7f7fb",
|
||||
"themeLighter": "#e1e1f1",
|
||||
"themeLight": "#c8c9e4",
|
||||
"themeTertiary": "#989ac9",
|
||||
"themeSecondary": "#7173b0",
|
||||
"themeDarkAlt": "#585a95",
|
||||
"themeDark": "#4a4c7e",
|
||||
"themeDarker": "#37385d",
|
||||
"neutralLighterAlt": "#2d2c2c",
|
||||
"neutralLighter": "#2c2b2b",
|
||||
"neutralLight": "#2a2929",
|
||||
"neutralQuaternaryAlt": "#272626",
|
||||
"neutralQuaternary": "#252525",
|
||||
"neutralTertiaryAlt": "#242323",
|
||||
"neutralTertiary": "#c8c8c8",
|
||||
"neutralSecondary": "#d0d0d0",
|
||||
"neutralPrimaryAlt": "#dadada",
|
||||
"neutralPrimary": "#ffffff",
|
||||
"neutralDark": "#f4f4f4",
|
||||
"black": "#f8f8f8",
|
||||
"white": "#2d2c2c"
|
||||
}
|
|
@ -1,24 +0,0 @@
|
|||
{
|
||||
"themePrimary": "#6264a7",
|
||||
"themeLighterAlt": "#f7f7fb",
|
||||
"themeLighter": "#e1e1f1",
|
||||
"themeLight": "#c8c9e4",
|
||||
"themeTertiary": "#989ac9",
|
||||
"themeSecondary": "#7173b0",
|
||||
"themeDarkAlt": "#585a95",
|
||||
"themeDark": "#4a4c7e",
|
||||
"themeDarker": "#37385d",
|
||||
"neutralLighterAlt": "#ecebe9",
|
||||
"neutralLighter": "#e8e7e6",
|
||||
"neutralLight": "#dedddc",
|
||||
"neutralQuaternaryAlt": "#cfcecd",
|
||||
"neutralQuaternary": "#c6c5c4",
|
||||
"neutralTertiaryAlt": "#bebdbc",
|
||||
"neutralTertiary": "#b5b4b2",
|
||||
"neutralSecondary": "#9d9c9a",
|
||||
"neutralPrimaryAlt": "#868482",
|
||||
"neutralPrimary": "#252423",
|
||||
"neutralDark": "#565453",
|
||||
"black": "#3e3d3b",
|
||||
"white": "#f3f2f1"
|
||||
}
|
|
@ -1,16 +0,0 @@
|
|||
$default-background: #f3f2f1;
|
||||
$default-color: #252423;
|
||||
$default-button-background: #6264a7;
|
||||
$default-Button-color: #f3f2f1;
|
||||
|
||||
// dark theme
|
||||
$dark-background: #2d2c2c;
|
||||
$dark-color: #ffffff;
|
||||
$dark-button-background: #6264a7;
|
||||
$dark-button-color: #2d2c2c;
|
||||
|
||||
// contrast theme
|
||||
$contrast-background: #000000;
|
||||
$contrast-color: #ffffff;
|
||||
$contrast-button-background: #b5c01c;
|
||||
$contrast-Button-color: #000000;
|
|
@ -1,30 +0,0 @@
|
|||
export interface IIconPickerProps {
|
||||
/**
|
||||
* call-back function when icon selection has been confirmed
|
||||
*/
|
||||
onSave(iconName: string): void;
|
||||
/**
|
||||
* call-back function when icon has changed
|
||||
*/
|
||||
onChange?(iconName: string): void;
|
||||
/**
|
||||
* Specifies the label of the icon picker button
|
||||
*/
|
||||
buttonLabel?: string;
|
||||
/**
|
||||
* Specifies if the picker button is disabled
|
||||
*/
|
||||
disabled?: boolean;
|
||||
/**
|
||||
* Specifies a custom className for the picker button
|
||||
*/
|
||||
buttonClassName?: string;
|
||||
/**
|
||||
* Specifies a custom className for the panel element
|
||||
*/
|
||||
panelClassName?: string;
|
||||
/**
|
||||
* initially selected icon
|
||||
*/
|
||||
currentIcon?: string;
|
||||
}
|
|
@ -1,5 +0,0 @@
|
|||
export interface IIconPickerState {
|
||||
items: string[];
|
||||
currentIcon?: string;
|
||||
isPanelOpen: boolean;
|
||||
}
|
|
@ -1,138 +0,0 @@
|
|||
@import '~office-ui-fabric-react/dist/sass/References.scss';
|
||||
|
||||
.navArea {
|
||||
display: flex;
|
||||
width: 100%;
|
||||
padding-left: 11px;
|
||||
}
|
||||
.headTitle {
|
||||
display: inline-block;
|
||||
flex-grow: 1;
|
||||
flex-shrink: 2;
|
||||
font-size: 28px;
|
||||
font-weight: 300;
|
||||
margin: auto 8px 5px 0;
|
||||
white-space: nowrap;
|
||||
}
|
||||
.searchBox {
|
||||
flex-grow: 5;
|
||||
flex-shrink: 1;
|
||||
margin: 5px 0;
|
||||
}
|
||||
.closeBtnContainer {
|
||||
flex: 0 0 54px;
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
}
|
||||
|
||||
.iconList {
|
||||
list-style-type: none;
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
padding: 0;
|
||||
margin: 10px -5px;
|
||||
}
|
||||
.iconItem {
|
||||
display: inline-block;
|
||||
padding: 0;
|
||||
margin: 0 2px 4px;
|
||||
list-style-type: none;
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
}
|
||||
.iconRadio {
|
||||
position: absolute;
|
||||
left: -1000px;
|
||||
opacity: 0;
|
||||
}
|
||||
.iconLabel {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: space-evenly;
|
||||
width: 78px;
|
||||
padding: 5px;
|
||||
height: 70px;
|
||||
border-radius: 3px;
|
||||
background-color: "[theme:neutralLighterAlt, default:#f8f8f8]";
|
||||
|
||||
&:after {
|
||||
content: "";
|
||||
display: block;
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
border: 2px solid transparent;
|
||||
border-color: "[theme:neutralQuarternary, default:#d0d0d0]";
|
||||
border-radius: 3px;
|
||||
opacity: 0;
|
||||
will-change: opacity, border-color;
|
||||
transition: opacity ease-out .05s;
|
||||
}
|
||||
&:hover {
|
||||
&:after {
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
.iconRadio:checked + .iconLabel {
|
||||
&:after {
|
||||
opacity: 1;
|
||||
border-color: "[theme: themePrimary, default: #0078d7]";
|
||||
}
|
||||
color: "[theme: themePrimary, default: #0078d7]";
|
||||
}
|
||||
.iconRadio:focus + .iconLabel {
|
||||
outline: 1px dashed;
|
||||
outline-color: "[theme: themePrimary, default: #0078d7]";
|
||||
outline-offset: -5px;
|
||||
}
|
||||
.iconGlyph {
|
||||
font-size: 24px;
|
||||
width: 24px;
|
||||
height: 24px;
|
||||
margin-bottom: 5px;
|
||||
color: inherit;
|
||||
}
|
||||
.iconName {
|
||||
display: inline-block;
|
||||
max-width: 100%;
|
||||
text-align: left;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.footer {
|
||||
display: flex;
|
||||
width: 100%;
|
||||
}
|
||||
.selectionDisplay {
|
||||
order: 2;
|
||||
display: flex;
|
||||
flex: 1 0 32px;
|
||||
margin: 0 auto;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
&:global {
|
||||
&.noSelection {
|
||||
opacity: .3;
|
||||
}
|
||||
}
|
||||
}
|
||||
.selectionLabel {
|
||||
display: inline-block;
|
||||
}
|
||||
.selectionIcon {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-size: 24px;
|
||||
min-width: 32px;
|
||||
}
|
||||
.btnCancel {
|
||||
order: 1;
|
||||
}
|
||||
.btnSave {
|
||||
order: 3;
|
||||
}
|
|
@ -1,146 +0,0 @@
|
|||
import * as React from 'react';
|
||||
import { IIconPickerProps } from '.';
|
||||
import { PrimaryButton, DefaultButton } from 'office-ui-fabric-react/lib/Button';
|
||||
import { Icon } from 'office-ui-fabric-react/lib/Icon';
|
||||
import { SearchBox } from 'office-ui-fabric-react/lib/SearchBox';
|
||||
import { IRenderFunction, getId } from 'office-ui-fabric-react/lib/Utilities';
|
||||
import styles from './IconPicker.module.scss';
|
||||
import * as strings from "PersonalAppsWebPartStrings";
|
||||
import { IconNames } from './IconNames';
|
||||
import { Panel, PanelType, IPanelProps } from 'office-ui-fabric-react/lib/Panel';
|
||||
import { debounce } from 'lodash';
|
||||
import { IIconPickerState } from './IIconPickerState';
|
||||
|
||||
|
||||
|
||||
export class IconPicker extends React.Component<IIconPickerProps, IIconPickerState> {
|
||||
private radioIdBase: string = getId("radio");
|
||||
|
||||
constructor(props: IIconPickerProps) {
|
||||
super(props);
|
||||
|
||||
this.state = {
|
||||
items: IconNames.Icons,
|
||||
isPanelOpen: false,
|
||||
currentIcon: this.props.currentIcon || null
|
||||
};
|
||||
}
|
||||
|
||||
public render(): React.ReactElement<IIconPickerProps> {
|
||||
return <div>
|
||||
<PrimaryButton
|
||||
text={this.props.buttonLabel}
|
||||
onClick={this.iconPickerOnClick}
|
||||
className={this.props.buttonClassName}
|
||||
disabled={this.props.disabled}
|
||||
data-automation-id={`icon-picker-open`}
|
||||
/>
|
||||
<Panel
|
||||
isOpen={this.state.isPanelOpen}
|
||||
onDismiss={this.closePanel}
|
||||
type={PanelType.medium}
|
||||
data-automation-id={`icon-picker-panel`}
|
||||
closeButtonAriaLabel={strings.IconPickerCloseLabel}
|
||||
className={this.props.panelClassName}
|
||||
onRenderNavigation={this.renderPanelNav}
|
||||
onRenderFooterContent={this.renderPanelFooter}
|
||||
>
|
||||
{this.renderPanelContent()}
|
||||
</Panel>
|
||||
</div>;
|
||||
}
|
||||
|
||||
private closePanel = (): void => {
|
||||
this.setState({
|
||||
currentIcon: null,
|
||||
isPanelOpen: false
|
||||
});
|
||||
}
|
||||
|
||||
private iconPickerOnClick = (): void => {
|
||||
this.setState({
|
||||
isPanelOpen: true
|
||||
});
|
||||
}
|
||||
|
||||
private iconOnClick = (iconName: string): void => {
|
||||
if (this.props.onChange) this.props.onChange(iconName);
|
||||
this.setState({
|
||||
currentIcon: iconName,
|
||||
});
|
||||
}
|
||||
|
||||
private onAbort = (): void => {
|
||||
this.setState({ items: IconNames.Icons });
|
||||
}
|
||||
|
||||
private onChange = (event?: React.ChangeEvent<HTMLInputElement>, newValue?: string): void => {
|
||||
let items: string[];
|
||||
if (newValue && newValue.trim().length > 2) {
|
||||
items = IconNames.Icons.filter(item => {
|
||||
return item.toLocaleLowerCase().indexOf(newValue.toLocaleLowerCase()) !== -1;
|
||||
});
|
||||
} else {
|
||||
items = IconNames.Icons;
|
||||
}
|
||||
this.setState({
|
||||
items: items
|
||||
});
|
||||
}
|
||||
|
||||
private confirmSelection = (): void => {
|
||||
if (this.props.onSave) this.props.onSave(this.state.currentIcon);
|
||||
this.setState({
|
||||
isPanelOpen: false,
|
||||
});
|
||||
}
|
||||
|
||||
private renderPanelNav: IRenderFunction<IPanelProps> = (props: IPanelProps, defaultRender: IRenderFunction<IPanelProps>) => {
|
||||
return <div className={styles.navArea}>
|
||||
<h2 className={styles.headTitle}>{strings.IconPickerSelectLabel}</h2>
|
||||
<SearchBox className={styles.searchBox}
|
||||
onAbort={this.onAbort}
|
||||
data-automation-id={`icon-picker-search`}
|
||||
|
||||
onChange={this.onChange} />
|
||||
<div className={styles.closeBtnContainer}>{defaultRender!(props)}</div>
|
||||
</div>;
|
||||
}
|
||||
|
||||
private renderPanelContent = () => {
|
||||
return <div>
|
||||
{this.renderIcons()}
|
||||
</div>;
|
||||
}
|
||||
|
||||
private renderPanelFooter: IRenderFunction<IPanelProps> = () => {
|
||||
return <div className={styles.footer} data-automation-id={`icon-picker-footer`}>
|
||||
<PrimaryButton text={strings.IconPickerSaveLabel} onClick={this.confirmSelection} disabled={!this.state.currentIcon} className={styles.btnSave} data-automation-id={`icon-picker-save`} />
|
||||
<div className={`${styles.selectionDisplay} ${!this.state.currentIcon ? 'noSelection' : ''}`}>
|
||||
<span className={styles.selectionLabel}>{strings.IconPickerSelectedLabel}:</span>
|
||||
<Icon iconName={this.state.currentIcon} className={styles.selectionIcon} />
|
||||
</div>
|
||||
<DefaultButton text={strings.IconPickerCancelLabel} onClick={this.closePanel} className={styles.btnCancel} data-automation-id={`icon-picker-close`} />
|
||||
</div>;
|
||||
}
|
||||
|
||||
private renderIcons = (): React.ReactElement<IIconPickerProps> => {
|
||||
return (<ul className={styles.iconList}>
|
||||
{this.state.items.map(this.renderIcon)}
|
||||
</ul>);
|
||||
}
|
||||
|
||||
private renderIcon = (item: string): JSX.Element => {
|
||||
const radioId: string = `${this.radioIdBase}-${item}`;
|
||||
return <li className={styles.iconItem}>
|
||||
<input type="radio" name={this.radioIdBase} id={radioId} className={styles.iconRadio}
|
||||
data-automation-id={`icon-picker-${item}`}
|
||||
checked={item == this.state.currentIcon}
|
||||
onChange={() => this.iconOnClick(item)} />
|
||||
<label className={styles.iconLabel} htmlFor={radioId} title={item}>
|
||||
<Icon iconName={item} className={styles.iconGlyph} />
|
||||
<span className={styles.iconName}>{item}</span>
|
||||
</label>
|
||||
</li>;
|
||||
}
|
||||
}
|
|
@ -1,2 +0,0 @@
|
|||
export * from './IIconPickerProps';
|
||||
export * from './IconPicker';
|
|
@ -1 +0,0 @@
|
|||
// A file is required to be in the root of the /src directory by the TypeScript compiler
|
|
@ -1,6 +0,0 @@
|
|||
export interface IAppItem {
|
||||
title:string;
|
||||
name:string;
|
||||
description: string;
|
||||
iconName: string;
|
||||
}
|
|
@ -1,6 +0,0 @@
|
|||
export interface ITenantProperty {
|
||||
key: string;
|
||||
Comment?: string;
|
||||
Description?: string;
|
||||
Value: string;
|
||||
}
|
|
@ -1,74 +0,0 @@
|
|||
import { WebPartContext } from "@microsoft/sp-webpart-base";
|
||||
import {
|
||||
MSGraphClient,
|
||||
} from "@microsoft/sp-http";
|
||||
|
||||
import * as MicrosoftGraph from "@microsoft/microsoft-graph-types";
|
||||
import { IListItem } from "../webparts/personalApps/components/ManageApps/IListItem";
|
||||
|
||||
export default class dataservices {
|
||||
private static _MSGraphClient: MSGraphClient;
|
||||
private static _hasExtension: boolean = false;
|
||||
|
||||
/*
|
||||
initialize the static class
|
||||
*/
|
||||
public static async init(context: WebPartContext) {
|
||||
//obtain the httpClient from the webpart context
|
||||
this._MSGraphClient = await context.msGraphClientFactory.getClient();
|
||||
|
||||
}
|
||||
|
||||
// Get Sources
|
||||
public static async getUserApps(): Promise<IListItem[]> {
|
||||
try {
|
||||
let _myApps = await this._MSGraphClient
|
||||
.api(`/me/extensions/MyApps`)
|
||||
.get();
|
||||
this._hasExtension = true;
|
||||
return _myApps ? _myApps.Apps : [];
|
||||
} catch (error) {
|
||||
console.log (error);
|
||||
return [];
|
||||
}
|
||||
}
|
||||
|
||||
public static async createOrUpdateUserApps(
|
||||
listApps: IListItem[]
|
||||
): Promise<microsoftgraph.OpenTypeExtension> {
|
||||
try {
|
||||
let _extensionResult: any;
|
||||
let extentionData: Object = {};
|
||||
|
||||
// User has extention created ?
|
||||
if (this._hasExtension) {
|
||||
extentionData = {
|
||||
Apps: listApps
|
||||
};
|
||||
// Call the REST API
|
||||
_extensionResult = await this._MSGraphClient
|
||||
.api(`/me/extensions/MyApps`)
|
||||
.patch(extentionData);
|
||||
} else {
|
||||
// Create Extention with Data
|
||||
extentionData = {
|
||||
"@odata.type": "#microsoft.graph.openTypeExtension",
|
||||
extensionName: "MyApps",
|
||||
Apps: listApps
|
||||
};
|
||||
// Call the Rest API
|
||||
_extensionResult = await this._MSGraphClient
|
||||
.api(`/me/extensions`)
|
||||
.post(extentionData);
|
||||
// Flag user has Extention crerated
|
||||
this._hasExtension = true;
|
||||
}
|
||||
|
||||
return _extensionResult;
|
||||
} catch (error) {
|
||||
console.log(error);
|
||||
throw new Error("Error create or Update Extention");
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
|
@ -1,28 +0,0 @@
|
|||
{
|
||||
"$schema": "https://developer.microsoft.com/json-schemas/spfx/client-side-web-part-manifest.schema.json",
|
||||
"id": "4dfcaee3-8a85-4d80-9fa3-23bcb1e08923",
|
||||
"alias": "PersonalAppsWebPart",
|
||||
"componentType": "WebPart",
|
||||
|
||||
// The "*" signifies that the version should be taken from the package.json
|
||||
"version": "*",
|
||||
"manifestVersion": 2,
|
||||
"supportsThemeVariants": true,
|
||||
// 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","TeamsTab","TeamsPersonalApp","SharePointFullPage"],
|
||||
|
||||
"preconfiguredEntries": [{
|
||||
"groupId": "5c03119e-3074-46fd-976b-c60198311f70", // Other
|
||||
"group": { "default": "Other" },
|
||||
"title": { "default": "My Personal Apps and Links" },
|
||||
"description": { "default": "My Personal Apps and Links " },
|
||||
"officeFabricIconFontName": "AppIconDefaultList",
|
||||
"properties": {
|
||||
"title": "My Personal Apps",
|
||||
"view": "List"
|
||||
}
|
||||
}]
|
||||
}
|
|
@ -1,156 +0,0 @@
|
|||
import * as React from "react";
|
||||
import * as ReactDom from "react-dom";
|
||||
import { Version } from "@microsoft/sp-core-library";
|
||||
import {
|
||||
IPropertyPaneConfiguration,
|
||||
PropertyPaneTextField,
|
||||
PropertyPaneChoiceGroup
|
||||
} from "@microsoft/sp-property-pane";
|
||||
import { BaseClientSideWebPart } from "@microsoft/sp-webpart-base";
|
||||
import { DisplayMode } from "@microsoft/sp-core-library";
|
||||
import * as strings from "PersonalAppsWebPartStrings";
|
||||
import PersonalApps from "./components/PersonalApps";
|
||||
import { IPersonalAppsProps } from "./components/IPersonalAppsProps";
|
||||
import {
|
||||
PropertyFieldCollectionData,
|
||||
CustomCollectionFieldType
|
||||
} from "@pnp/spfx-property-controls/lib/PropertyFieldCollectionData";
|
||||
import { ThemeProvider, ThemeChangedEventArgs, IReadonlyTheme } from '@microsoft/sp-component-base';
|
||||
import dataservices from "../../services/dataservices";
|
||||
import { loadTheme } from "office-ui-fabric-react";
|
||||
const teamsDefaultTheme = require("../../common/TeamsDefaultTheme.json");
|
||||
const teamsDarkTheme = require("../../common/TeamsDarkTheme.json");
|
||||
const teamsContrastTheme = require("../../common/TeamsContrastTheme.json");
|
||||
|
||||
export interface IPersonalAppsWebPartProps {
|
||||
title: string;
|
||||
view: string | number;
|
||||
|
||||
}
|
||||
|
||||
export default class PersonalAppsWebPart extends BaseClientSideWebPart<
|
||||
IPersonalAppsWebPartProps
|
||||
> {
|
||||
private _themeProvider: ThemeProvider;
|
||||
private _themeVariant: IReadonlyTheme | undefined;
|
||||
protected async onInit<T>(): Promise<T> {
|
||||
await dataservices.init(this.context);
|
||||
|
||||
this._themeProvider = this.context.serviceScope.consume(ThemeProvider.serviceKey);
|
||||
|
||||
// If it exists, get the theme variant
|
||||
this._themeVariant = this._themeProvider.tryGetTheme();
|
||||
|
||||
// Register a handler to be notified if the theme variant changes
|
||||
this._themeProvider.themeChangedEvent.add(this, this._handleThemeChangedEvent);
|
||||
|
||||
if (this.context.sdks.microsoftTeams) {
|
||||
// in teams ?
|
||||
const context = this.context.sdks.microsoftTeams!.context;
|
||||
this._applyTheme(context.theme || "default");
|
||||
this.context.sdks.microsoftTeams.teamsJs.registerOnThemeChangeHandler(
|
||||
this._applyTheme
|
||||
);
|
||||
}
|
||||
return Promise.resolve();
|
||||
}
|
||||
|
||||
/**
|
||||
* Update the current theme variant reference and re-render.
|
||||
*
|
||||
* @param args The new theme
|
||||
*/
|
||||
private _handleThemeChangedEvent(args: ThemeChangedEventArgs): void {
|
||||
this._themeVariant = args.theme;
|
||||
this.render();
|
||||
}
|
||||
|
||||
// Apply btheme id in Teams
|
||||
private _applyTheme = (theme: string): void => {
|
||||
this.context.domElement.setAttribute("data-theme", theme);
|
||||
document.body.setAttribute("data-theme", theme);
|
||||
|
||||
if (theme == "dark") {
|
||||
loadTheme({
|
||||
palette: teamsDarkTheme
|
||||
});
|
||||
}
|
||||
|
||||
if (theme == "default") {
|
||||
loadTheme({
|
||||
palette: teamsDefaultTheme
|
||||
});
|
||||
}
|
||||
|
||||
if (theme == "contrast") {
|
||||
loadTheme({
|
||||
palette: teamsContrastTheme
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public render(): void {
|
||||
const element: React.ReactElement<IPersonalAppsProps> = React.createElement(
|
||||
PersonalApps,
|
||||
{
|
||||
title: this.properties.title,
|
||||
view: this.properties.view,
|
||||
displayMode: this.displayMode,
|
||||
themeVariant: this._themeVariant,
|
||||
}
|
||||
);
|
||||
|
||||
ReactDom.render(element, this.domElement);
|
||||
}
|
||||
|
||||
protected onDispose(): void {
|
||||
ReactDom.unmountComponentAtNode(this.domElement);
|
||||
}
|
||||
|
||||
protected get dataVersion(): Version {
|
||||
return Version.parse("1.0");
|
||||
}
|
||||
|
||||
protected getPropertyPaneConfiguration(): IPropertyPaneConfiguration {
|
||||
const { view } = this.properties;
|
||||
|
||||
return {
|
||||
pages: [
|
||||
{
|
||||
header: {
|
||||
description: strings.PropertyPaneDescription
|
||||
},
|
||||
groups: [
|
||||
{
|
||||
groupName: strings.BasicGroupName,
|
||||
groupFields: [
|
||||
PropertyPaneTextField("title", {
|
||||
label: strings.DescriptionFieldLabel
|
||||
}),
|
||||
PropertyPaneChoiceGroup("view", {
|
||||
label: "view option",
|
||||
|
||||
options: [
|
||||
{
|
||||
key: "List",
|
||||
text: "List",
|
||||
iconProps: { officeFabricIconFontName: "List" },
|
||||
checked: view === "List" ? true : false
|
||||
},
|
||||
{
|
||||
key: "Tiles",
|
||||
text: "Tiles",
|
||||
iconProps: { officeFabricIconFontName: "Tiles" },
|
||||
checked: view === "Tiles" ? true : false
|
||||
}
|
||||
]
|
||||
})
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
};
|
||||
}
|
||||
}
|
|
@ -1,108 +0,0 @@
|
|||
@import "~office-ui-fabric-react/dist/sass/References.scss";
|
||||
@import "~office-ui-fabric-react/dist/sass/semanticSlots";
|
||||
@import './node_modules/spfx-uifabric-themes/office.theme';
|
||||
@import "./../../../../Common/themeColors.module.scss";
|
||||
|
||||
|
||||
|
||||
.card {
|
||||
display: flex;
|
||||
|
||||
min-height: 60px;
|
||||
max-height: 60px;
|
||||
|
||||
justify-content: start;
|
||||
margin-bottom: 5px;
|
||||
margin-right: 5px;
|
||||
flex-direction: row;
|
||||
border-style: solid;
|
||||
border-width: 1px;
|
||||
border-color: $ms-color-neutralTertiaryAlt;
|
||||
padding-left: 20px;
|
||||
padding-right: 20px;
|
||||
padding-top: 12px;
|
||||
padding-bottom: 12px;
|
||||
align-items: flex-start;
|
||||
background-color:$bodyBackgroundColor;
|
||||
color: $bodyTextColor;
|
||||
max-width:100%;
|
||||
}
|
||||
|
||||
.card:hover {
|
||||
box-shadow: 0 2px 4px 0 rgba(0, 0, 0, 0.2), 0 25px 50px 0 rgba(0, 0, 0, 0.1);
|
||||
background-color: $ms-themePrimary;
|
||||
color: $ms-color-white;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.image {
|
||||
font-size: 40px;
|
||||
|
||||
width: 40px;
|
||||
height: 40px;
|
||||
}
|
||||
|
||||
.imageContainer {
|
||||
display: flex;
|
||||
margin-right: 30px;
|
||||
align-items: center;
|
||||
width: 45px;
|
||||
height: 60px;
|
||||
}
|
||||
|
||||
.title {
|
||||
font-size: 16px;
|
||||
font-weight: 300px;
|
||||
line-height: 25px;
|
||||
line-break: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: 'ellipsis';
|
||||
}
|
||||
|
||||
.separator {
|
||||
border-color: $ms-color-themePrimary;
|
||||
width: 90%;
|
||||
margin: 0 auto;
|
||||
height: 0px;
|
||||
border-style: solid;
|
||||
border-width: 1px;
|
||||
|
||||
}
|
||||
|
||||
[data-theme="dark"] {
|
||||
|
||||
color: $ms-color-neutralTertiaryAlt;
|
||||
}
|
||||
|
||||
[data-theme="contrast"] {
|
||||
|
||||
// dark theme
|
||||
color: $contrast-button-background;
|
||||
.card:hover{
|
||||
color: $ms-color-black;
|
||||
background-color: $contrast-button-background;
|
||||
}
|
||||
}
|
||||
|
||||
[data-theme="default"] {
|
||||
.card{
|
||||
background-color: $ms-color-white;
|
||||
}
|
||||
.card:hover {
|
||||
box-shadow: 0 2px 4px 0 rgba(0, 0, 0, 0.2), 0 25px 50px 0 rgba(0, 0, 0, 0.1);
|
||||
background-color: $ms-themePrimary;
|
||||
color: $ms-color-white;
|
||||
cursor: pointer;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
[data-theme="default"] {
|
||||
$default-background: #f3f2f1;
|
||||
$default-color: #252423;
|
||||
$default-button-background: #6264a7;
|
||||
$default-Button-color: #f3f2f1;
|
||||
|
||||
color: $default-color;
|
||||
}*/
|
||||
|
|
@ -1,34 +0,0 @@
|
|||
import {
|
||||
FontIcon,
|
||||
Text,
|
||||
} from "office-ui-fabric-react";
|
||||
import { IAppItemProps } from "./IAppItemProps";
|
||||
import * as React from "react";
|
||||
import styles from "./AppItem.module.scss";
|
||||
|
||||
export const AppItem = (props: IAppItemProps) => {
|
||||
return (
|
||||
<>
|
||||
<div
|
||||
className={styles.card}
|
||||
onClick={event => {
|
||||
event.preventDefault();
|
||||
window.open(props.url, "_blank");
|
||||
}}
|
||||
>
|
||||
<div className={styles.imageContainer}>
|
||||
<FontIcon
|
||||
iconName={props.iconName}
|
||||
className={styles.image}
|
||||
></FontIcon>
|
||||
</div>
|
||||
<div>
|
||||
<div className={styles.title}>{props.title}</div>
|
||||
<Text variant="small" block>
|
||||
{props.description}
|
||||
</Text>
|
||||
</div>
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
};
|
|
@ -1,6 +0,0 @@
|
|||
export interface IAppItemProps {
|
||||
title:string;
|
||||
description: string;
|
||||
iconName: string;
|
||||
url:string;
|
||||
}
|
|
@ -1,87 +0,0 @@
|
|||
@import "~office-ui-fabric-react/dist/sass/References.scss";
|
||||
@import "./node_modules/spfx-uifabric-themes/office.theme";
|
||||
@import "./../../../../Common/themeColors.module.scss";
|
||||
|
||||
.tile {
|
||||
display: flex;
|
||||
min-height: 120px;
|
||||
max-height: 120px;
|
||||
/* min-width: 120px;*/
|
||||
justify-content: start;
|
||||
margin: 5px;
|
||||
flex-direction: column;
|
||||
border-style: solid;
|
||||
border-width: 1px;
|
||||
border-color: $ms-color-neutralTertiaryAlt;
|
||||
/* padding: 10px; */
|
||||
align-items: center;
|
||||
justify-items: center;
|
||||
justify-content: center;
|
||||
|
||||
}
|
||||
|
||||
.tile:hover {
|
||||
box-shadow: 0 2px 4px 0 rgba(0, 0, 0, 0.2), 0 25px 50px 0 rgba(0, 0, 0, 0.1);
|
||||
background-color: $ms-themePrimary;
|
||||
color: $ms-color-white;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.image {
|
||||
font-size: 48px;
|
||||
|
||||
width: 48px;
|
||||
height: 48px;
|
||||
}
|
||||
|
||||
.imageContainer {
|
||||
display: flex;
|
||||
|
||||
align-items: center;
|
||||
width: 48px;
|
||||
height: 48px;
|
||||
margin-bottom: 5px;
|
||||
}
|
||||
|
||||
.title {
|
||||
font-size: 16px;
|
||||
font-weight: 600px;
|
||||
line-height: 25px;
|
||||
line-break: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: "ellipsis";
|
||||
}
|
||||
|
||||
.separator {
|
||||
border-color: $ms-color-themePrimary;
|
||||
width: 90%;
|
||||
margin: 0 auto;
|
||||
height: 0px;
|
||||
border-style: solid;
|
||||
border-width: 1px;
|
||||
}
|
||||
|
||||
[data-theme="default"] {
|
||||
.tile {
|
||||
background-color: $ms-color-white;
|
||||
}
|
||||
.tile:hover {
|
||||
box-shadow: 0 2px 4px 0 rgba(0, 0, 0, 0.2), 0 25px 50px 0 rgba(0, 0, 0, 0.1);
|
||||
background-color: $ms-themePrimary;
|
||||
color: $ms-color-white;
|
||||
cursor: pointer;
|
||||
}
|
||||
}
|
||||
|
||||
[data-theme="dark"] {
|
||||
color: $ms-color-neutralTertiaryAlt;
|
||||
}
|
||||
|
||||
[data-theme="contrast"] {
|
||||
// dark theme
|
||||
color: $contrast-button-background;
|
||||
.tile:hover {
|
||||
color: $ms-color-black;
|
||||
background-color: $contrast-button-background;
|
||||
}
|
||||
}
|
|
@ -1,31 +0,0 @@
|
|||
import { FontIcon, Text } from "office-ui-fabric-react";
|
||||
import { IAppTileProps } from "./IAppTileProps";
|
||||
import * as React from "react";
|
||||
import * as ReactDom from "react-dom";
|
||||
import styles from "./AppTile.module.scss";
|
||||
import { PropertyFieldCollectionDataHost } from "@pnp/spfx-property-controls/lib/PropertyFieldCollectionData";
|
||||
|
||||
export const AppTile = (props: IAppTileProps) => {
|
||||
return (
|
||||
<>
|
||||
<div
|
||||
className={styles.tile}
|
||||
title={props.description}
|
||||
onClick={event => {
|
||||
event.preventDefault();
|
||||
window.open(props.url, "_blank");
|
||||
}}
|
||||
>
|
||||
<div className={styles.imageContainer}>
|
||||
<FontIcon
|
||||
iconName={props.iconName}
|
||||
className={styles.image}
|
||||
></FontIcon>
|
||||
</div>
|
||||
<div>
|
||||
<div className={styles.title}>{props.title}</div>
|
||||
</div>
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
};
|
|
@ -1,6 +0,0 @@
|
|||
export interface IAppTileProps {
|
||||
title:string;
|
||||
description: string;
|
||||
iconName: string;
|
||||
url:string;
|
||||
}
|
|
@ -1,8 +0,0 @@
|
|||
import { DisplayMode } from "@microsoft/sp-core-library";
|
||||
import { IReadonlyTheme } from '@microsoft/sp-component-base';
|
||||
export interface IPersonalAppsProps {
|
||||
title: string;
|
||||
view:string | number;
|
||||
displayMode: DisplayMode;
|
||||
themeVariant: IReadonlyTheme | undefined;
|
||||
}
|
|
@ -1,9 +0,0 @@
|
|||
import { IListItem } from "./ManageApps/IListItem";
|
||||
|
||||
export interface IPersonalAppsState {
|
||||
showPanel: boolean;
|
||||
apps : IListItem[];
|
||||
isLoading: boolean;
|
||||
hasError: boolean;
|
||||
errorMessage: string;
|
||||
}
|
|
@ -1,6 +0,0 @@
|
|||
export interface IListItem {
|
||||
name:string;
|
||||
description:string;
|
||||
url:string;
|
||||
iconName:string;
|
||||
}
|
|
@ -1,7 +0,0 @@
|
|||
import { IListItem } from "./IListItem";
|
||||
|
||||
export interface IManageAppsProps {
|
||||
showPanel: boolean;
|
||||
onDismiss: (list: IListItem[], changed:boolean) => void;
|
||||
Apps: IListItem[];
|
||||
}
|
|
@ -1,7 +0,0 @@
|
|||
import { IManageAppsProps } from "./IManageAppsProps";
|
||||
|
||||
export interface IManageAppsState {
|
||||
columns: any[];
|
||||
data: IManageAppsProps[];
|
||||
|
||||
}
|
|
@ -1,171 +0,0 @@
|
|||
@import "~office-ui-fabric-react/dist/sass/References.scss";
|
||||
@import "./node_modules/spfx-uifabric-themes/office.theme";
|
||||
@import "./../../../../Common/themeColors.module.scss";
|
||||
@import "~office-ui-fabric-react/dist/sass/semanticSlots";
|
||||
|
||||
.MuiPaperRoot {
|
||||
transition: box-shadow 300ms cubic-bezier(0.4, 0, 0.2, 1) 0ms;
|
||||
min-height: 100%;
|
||||
opacity: 0.8;
|
||||
}
|
||||
|
||||
.overlay {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
width: "100%";
|
||||
}
|
||||
|
||||
:global {
|
||||
|
||||
[data-theme="default"] {
|
||||
.MuiTableCell-head {
|
||||
background-color: inherit !important;
|
||||
}
|
||||
.MuiTable-root {
|
||||
background-color: inherit!important;
|
||||
}
|
||||
.MuiIconButton-root:hover {
|
||||
background-color: inherit;
|
||||
color: inherit !important;
|
||||
}
|
||||
.MuiTableCell-body {
|
||||
color: inherit;
|
||||
}
|
||||
}
|
||||
[data-theme="dark"] {
|
||||
.MuiTableCell-head {
|
||||
background-color: $dark-background !important;
|
||||
color: $ms-color-neutralTertiaryAlt !important;
|
||||
}
|
||||
.MuiButtonBase-root {
|
||||
color: $ms-color-neutralTertiaryAlt !important;
|
||||
}
|
||||
.MuiTextField-root {
|
||||
color: $ms-color-neutralTertiaryAlt !important;
|
||||
}
|
||||
.MuiTablePagination-toolbar {
|
||||
color: $ms-color-neutralTertiaryAlt !important;
|
||||
}
|
||||
.ms-Button--default {
|
||||
background-color: $ms-color-neutralTertiary;
|
||||
}
|
||||
.MuiInput-underline:before {
|
||||
border-bottom-color: $ms-color-neutralTertiaryAlt !important;
|
||||
}
|
||||
.MuiSelect-root {
|
||||
color: $ms-color-neutralTertiaryAlt !important;
|
||||
}
|
||||
.MuiTypography-caption {
|
||||
color: $ms-color-neutralTertiaryAlt !important;
|
||||
}
|
||||
.MuiSelect-icon {
|
||||
color: $ms-color-neutralTertiaryAlt !important;
|
||||
}
|
||||
.MuiInputBase-root {
|
||||
color: $ms-color-neutralTertiaryAlt !important;
|
||||
}
|
||||
.MuiTable-root {
|
||||
background-color: $dark-background !important;
|
||||
}
|
||||
.MuiIconButton-root:hover {
|
||||
background-color: $ms-themePrimary;
|
||||
color: $ms-color-neutralTertiaryAlt !important;
|
||||
}
|
||||
.MuiTableCell-body {
|
||||
color: inherit;
|
||||
}
|
||||
}
|
||||
|
||||
[data-theme="contrast"] {
|
||||
.MuiButtonBase-root {
|
||||
color: $contrast-button-background !important;
|
||||
}
|
||||
.ms-Button--default {
|
||||
background-color: $ms-color-black;
|
||||
}
|
||||
.ms-Button--Primary {
|
||||
color: $ms-color-black;
|
||||
background-color: $contrast-button-background;
|
||||
}
|
||||
|
||||
.MuiTableCell-head {
|
||||
background-color: $contrast-background !important;
|
||||
color: $contrast-button-background !important;
|
||||
}
|
||||
.MuiButtonBase-root {
|
||||
color: $ms-color-neutralTertiaryAlt !important;
|
||||
}
|
||||
.MuiTextField-root {
|
||||
color: $ms-color-neutralTertiaryAlt !important;
|
||||
}
|
||||
.MuiTablePagination-toolbar {
|
||||
color: $ms-color-neutralTertiaryAlt !important;
|
||||
}
|
||||
.ms-Button--default {
|
||||
background-color: $ms-color-neutralTertiary;
|
||||
}
|
||||
.MuiInput-underline:before {
|
||||
border-bottom-color: $ms-color-neutralTertiaryAlt !important;
|
||||
}
|
||||
.MuiSelect-root {
|
||||
color: $ms-color-neutralTertiaryAlt !important;
|
||||
}
|
||||
.MuiTypography-caption {
|
||||
color: $ms-color-neutralTertiaryAlt !important;
|
||||
}
|
||||
.MuiSelect-icon {
|
||||
color: $ms-color-neutralTertiaryAlt !important;
|
||||
}
|
||||
.MuiInputBase-root {
|
||||
color: $ms-color-neutralTertiaryAlt !important;
|
||||
}
|
||||
.MuiTable-root {
|
||||
background-color: $contrast-background !important;
|
||||
}
|
||||
.MuiIconButton-root:hover {
|
||||
background-color: $contrast-button-background;
|
||||
color: $contrast-background !important;
|
||||
}
|
||||
.MuiTableCell-body {
|
||||
color: inherit;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
[data-theme="dark"] {
|
||||
.MuiPaperRoot {
|
||||
background-color: $dark-background !important;
|
||||
color: $ms-color-neutralTertiaryAlt !important;
|
||||
}
|
||||
.overlay {
|
||||
background-color: $dark-background !important;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
width: "100%";
|
||||
}
|
||||
}
|
||||
[data-theme="contrast"] {
|
||||
.MuiPaperRoot {
|
||||
// dark theme
|
||||
background-color: $contrast-background !important;
|
||||
color: $contrast-button-background !important;
|
||||
.overlay {
|
||||
background-color: $contrast-background !important;
|
||||
}
|
||||
}
|
||||
}
|
||||
[data-theme="default"] {
|
||||
.MuiPaperRoot {
|
||||
// dark theme
|
||||
background-color: $default-background !important;
|
||||
}
|
||||
.overlay {
|
||||
background-color: $default-background !important;
|
||||
opacity: 0.6;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
width: "100%";
|
||||
height: "100%";
|
||||
align-items: center´;
|
||||
}
|
||||
}
|
|
@ -1,331 +0,0 @@
|
|||
import * as React from "react";
|
||||
import { useState, useEffect } from "react";
|
||||
import { IManageAppsProps } from "../ManageApps/IManageAppsProps";
|
||||
import { IManageAppsState } from "../ManageApps/IManageAppsState";
|
||||
import MaterialTable, { Icons, MTableHeader } from "material-table";
|
||||
import dataservices from "../../../../services/dataservices";
|
||||
import * as strings from "PersonalAppsWebPartStrings";
|
||||
import { IconPicker } from "../../../../controls/iconPicker";
|
||||
import {
|
||||
FontIcon,
|
||||
TextField,
|
||||
Spinner,
|
||||
SpinnerSize,
|
||||
SpinnerType,
|
||||
Panel,
|
||||
PanelType,
|
||||
Text,
|
||||
Label,
|
||||
MessageBar,
|
||||
MessageBarType,
|
||||
PrimaryButton,
|
||||
DefaultButton
|
||||
} from "office-ui-fabric-react";
|
||||
|
||||
import Check from "@material-ui/icons/Check";
|
||||
import ChevronLeft from "@material-ui/icons/ChevronLeft";
|
||||
import ChevronRight from "@material-ui/icons/ChevronRight";
|
||||
import Clear from "@material-ui/icons/Clear";
|
||||
import DeleteOutline from "@material-ui/icons/DeleteOutline";
|
||||
import Edit from "@material-ui/icons/Edit";
|
||||
import FilterList from "@material-ui/icons/FilterList";
|
||||
import FirstPage from "@material-ui/icons/FirstPage";
|
||||
import LastPage from "@material-ui/icons/LastPage";
|
||||
import Remove from "@material-ui/icons/Remove";
|
||||
import SaveAlt from "@material-ui/icons/SaveAlt";
|
||||
import Search from "@material-ui/icons/Search";
|
||||
import ViewColumn from "@material-ui/icons/ViewColumn";
|
||||
import { initializeIcons } from "office-ui-fabric-react/lib/Icons";
|
||||
import { IListItem } from "./IListItem";
|
||||
import { Paper, CircularProgress } from "@material-ui/core";
|
||||
import styles from "./ManageApps.module.scss";
|
||||
|
||||
initializeIcons();
|
||||
|
||||
const tableIcons: Icons = {
|
||||
Add: React.forwardRef((props, ref) => (
|
||||
<FontIcon iconName="AppIconDefaultAdd" style={{ fontWeight: 700 }} />
|
||||
)),
|
||||
Check: React.forwardRef((props, ref) => <Check />),
|
||||
Clear: React.forwardRef((props, ref) => <Clear />),
|
||||
Delete: React.forwardRef((props, ref) => <FontIcon iconName="Delete" style={{fontSize: 20}} />),
|
||||
DetailPanel: React.forwardRef((props, ref) => <ChevronRight />),
|
||||
Edit: React.forwardRef((props, ref) => <FontIcon iconName="Edit" style={{fontSize: 20}} />),
|
||||
Export: React.forwardRef((props, ref) => <SaveAlt />),
|
||||
Filter: React.forwardRef((props, ref) => <FilterList />),
|
||||
FirstPage: React.forwardRef((props, ref) => <FirstPage />),
|
||||
LastPage: React.forwardRef((props, ref) => <LastPage />),
|
||||
NextPage: React.forwardRef((props, ref) => <ChevronRight />),
|
||||
PreviousPage: React.forwardRef((props, ref) => <ChevronLeft />),
|
||||
|
||||
ResetSearch: React.forwardRef((props, ref) => <Clear />),
|
||||
Search: React.forwardRef((props, ref) => <Search />),
|
||||
SortArrow: React.forwardRef((props, ref) => (
|
||||
<FontIcon iconName="Sort" style={{ marginLeft: 5 }} />
|
||||
)),
|
||||
ThirdStateCheck: React.forwardRef((props, ref) => <Remove />),
|
||||
ViewColumn: React.forwardRef((props, ref) => <ViewColumn />)
|
||||
};
|
||||
|
||||
export function ManageApps(appProps: IManageAppsProps) {
|
||||
const [isLoading, setIsLoading] = useState(false);
|
||||
const [changeData, setChangeData] = useState(false);
|
||||
const [hasError, setHasError] = useState(false);
|
||||
const [errorMessage, setErrorMessage] = useState("");
|
||||
const [showPanel, setShowPanel] = useState(appProps.showPanel);
|
||||
const [isSaving, setIsSaving] = useState(false);
|
||||
|
||||
const [state, setState] = useState({
|
||||
columns: [
|
||||
{
|
||||
title: "Name ",
|
||||
field: "name",
|
||||
editComponent: props => (
|
||||
<TextField
|
||||
underlined
|
||||
required
|
||||
placeholder="Enter Name here"
|
||||
onGetErrorMessage={(newValue: string) => {
|
||||
return newValue.trim().length > 0 ? "" : "Please enter Name";
|
||||
}}
|
||||
validateOnFocusOut
|
||||
validateOnLoad={false}
|
||||
value={props.value}
|
||||
onChange={(event: React.FormEvent<HTMLInputElement>, newValue) => {
|
||||
props.onChange(newValue);
|
||||
}}
|
||||
/>
|
||||
)
|
||||
},
|
||||
{
|
||||
title: "Derscription",
|
||||
field: "description",
|
||||
editComponent: props => (
|
||||
<TextField
|
||||
underlined
|
||||
required
|
||||
placeholder="Enter description here"
|
||||
validateOnFocusOut
|
||||
validateOnLoad={false}
|
||||
onGetErrorMessage={(newValue: string) => {
|
||||
return newValue.trim().length > 0
|
||||
? ""
|
||||
: "Please enter Description";
|
||||
}}
|
||||
value={props.value}
|
||||
onChange={(event: React.FormEvent<HTMLInputElement>, newValue) => {
|
||||
props.onChange(newValue);
|
||||
}}
|
||||
/>
|
||||
)
|
||||
},
|
||||
{
|
||||
title: "Url",
|
||||
field: "Url",
|
||||
editComponent: props => (
|
||||
<TextField
|
||||
underlined
|
||||
required
|
||||
placeholder="Enter URL here"
|
||||
onGetErrorMessage={(newValue: string) => {
|
||||
try {
|
||||
const _URL = new URL(newValue);
|
||||
return "";
|
||||
} catch (error) {
|
||||
return "Please enter valid Url";
|
||||
}
|
||||
}}
|
||||
validateOnFocusOut
|
||||
validateOnLoad={false}
|
||||
value={props.value}
|
||||
onChange={(event: React.FormEvent<HTMLInputElement>, newValue) => {
|
||||
props.onChange(newValue);
|
||||
}}
|
||||
/>
|
||||
)
|
||||
},
|
||||
{
|
||||
title: "Icon",
|
||||
field: "iconName",
|
||||
render: rowData => (
|
||||
<FontIcon
|
||||
iconName={rowData.iconName}
|
||||
style={{ width: 24, height: 24, fontSize: 24 }}
|
||||
/>
|
||||
),
|
||||
editComponent: props => (
|
||||
<div style={{ display: "Flex", flexDirection: "row" }}>
|
||||
{" "}
|
||||
<FontIcon
|
||||
iconName={props.value}
|
||||
style={{ width: 24, height: 24, fontSize: 24, marginRight: 7 }}
|
||||
/>
|
||||
<IconPicker
|
||||
buttonLabel={" select Icon"}
|
||||
currentIcon={props.value}
|
||||
onSave={(iconName: string) => {
|
||||
props.onChange(iconName);
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
],
|
||||
data: appProps.Apps
|
||||
});
|
||||
|
||||
// Load Schema Extension Data
|
||||
|
||||
useEffect(() => {
|
||||
(async () => {
|
||||
// Get Tenant Property with id of Extension Id to check if exists or needs to create
|
||||
})();
|
||||
});
|
||||
|
||||
// Cancel command
|
||||
const _onDismiss = async () => {
|
||||
appProps.onDismiss(state.data, false);
|
||||
};
|
||||
|
||||
// Save command
|
||||
const _onSave = async () => {
|
||||
try {
|
||||
setIsSaving(true);
|
||||
const _result: microsoftgraph.OpenTypeExtension = await dataservices.createOrUpdateUserApps(
|
||||
state.data
|
||||
);
|
||||
console.log("extention created or updated", _result);
|
||||
appProps.onDismiss(state.data, true);
|
||||
} catch (error) {
|
||||
setHasError(true);
|
||||
setErrorMessage(error.message);
|
||||
}
|
||||
};
|
||||
|
||||
// Render Panel commands
|
||||
const _onRenderFooterContent = () => (
|
||||
<div
|
||||
style={{
|
||||
display: "flex",
|
||||
justifyContent: "flex-end",
|
||||
width: "100%",
|
||||
marginBottom: 35
|
||||
}}
|
||||
>
|
||||
<PrimaryButton
|
||||
onClick={_onSave}
|
||||
disabled={isSaving}
|
||||
|
||||
style={{ marginRight: 7, width: 100 }}
|
||||
>
|
||||
{isSaving ? (
|
||||
<Spinner size={SpinnerSize.xSmall}></Spinner>
|
||||
) : (
|
||||
strings.SaveLabelButtom
|
||||
)}
|
||||
</PrimaryButton>
|
||||
<DefaultButton style={{ width: 100 }} onClick={_onDismiss}>
|
||||
{strings.CancelLabelButton}
|
||||
</DefaultButton>
|
||||
</div>
|
||||
);
|
||||
|
||||
return (
|
||||
<Panel
|
||||
isOpen={showPanel}
|
||||
onDismiss={_onDismiss}
|
||||
type={PanelType.custom}
|
||||
customWidth="888px"
|
||||
closeButtonAriaLabel="Close"
|
||||
headerText="My Apps"
|
||||
onRenderFooterContent={_onRenderFooterContent}
|
||||
isFooterAtBottom={true}
|
||||
>
|
||||
<div style={{ marginTop: 20, marginBottom: 25 }}>
|
||||
<Text variant="large" block>
|
||||
Please add links for your favorite Apps
|
||||
</Text>
|
||||
</div>
|
||||
{hasError && (
|
||||
<MessageBar messageBarType={MessageBarType.error}>
|
||||
{errorMessage}
|
||||
</MessageBar>
|
||||
)}
|
||||
{isLoading ? (
|
||||
<Spinner size={SpinnerSize.medium} />
|
||||
) : (
|
||||
<div style={{ height: "100%" }}>
|
||||
<MaterialTable
|
||||
title="My Apps"
|
||||
isLoading={false}
|
||||
columns={state.columns}
|
||||
components={{
|
||||
OverlayLoading: props => (
|
||||
<div className={styles.overlay}><CircularProgress /></div>
|
||||
),
|
||||
Container: props => (
|
||||
<Paper
|
||||
{...props}
|
||||
elevation={0}
|
||||
classes={{ root: styles.MuiPaperRoot }}
|
||||
/>
|
||||
)
|
||||
}}
|
||||
|
||||
data={state.data}
|
||||
icons={tableIcons}
|
||||
options={{
|
||||
paging: true,
|
||||
showTitle:false,
|
||||
searchFieldAlignment:'left',
|
||||
pageSize: 7,
|
||||
search: true,
|
||||
minBodyHeight: "100%"
|
||||
}}
|
||||
editable={{
|
||||
onRowAdd: (newData: IListItem) =>
|
||||
new Promise(resolve => {
|
||||
setTimeout(() => {
|
||||
resolve();
|
||||
setChangeData(true);
|
||||
setState(prevState => {
|
||||
const data = [...prevState.data];
|
||||
data.push(newData);
|
||||
return { ...prevState, data };
|
||||
});
|
||||
}, 600);
|
||||
}),
|
||||
onRowUpdate: (newData, oldData) =>
|
||||
new Promise(resolve => {
|
||||
setTimeout(() => {
|
||||
resolve();
|
||||
if (oldData) {
|
||||
setChangeData(true);
|
||||
setState(prevState => {
|
||||
const data = [...prevState.data];
|
||||
data[data.indexOf(oldData)] = newData;
|
||||
|
||||
return { ...prevState, data };
|
||||
});
|
||||
}
|
||||
}, 600);
|
||||
}),
|
||||
onRowDelete: oldData =>
|
||||
new Promise(resolve => {
|
||||
setTimeout(() => {
|
||||
resolve();
|
||||
setChangeData(true);
|
||||
setState(prevState => {
|
||||
const data = [...prevState.data];
|
||||
data.splice(data.indexOf(oldData), 1);
|
||||
return { ...prevState, data };
|
||||
});
|
||||
}, 600);
|
||||
})
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
</Panel>
|
||||
);
|
||||
}
|
|
@ -1,98 +0,0 @@
|
|||
@import "~office-ui-fabric-react/dist/sass/References.scss";
|
||||
@import "./node_modules/spfx-uifabric-themes/office.theme";
|
||||
@import "~office-ui-fabric-react/dist/sass/semanticSlots";
|
||||
|
||||
.imageSetting {
|
||||
width: 28px;
|
||||
height: 28px;
|
||||
font-size: 28px;
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
.imageSetting:hover {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.title {
|
||||
@include ms-font-xl;
|
||||
|
||||
}
|
||||
|
||||
.personalApps {
|
||||
padding-top: 15px;
|
||||
.containerTiles {
|
||||
margin-top:15px;
|
||||
|
||||
display: grid;
|
||||
grid-template-columns: repeat( auto-fit, minmax(120px, 1fr) );
|
||||
grid-template-rows: auto;
|
||||
}
|
||||
.containerItems {
|
||||
margin-top:15px;
|
||||
width: 100%;
|
||||
display: grid;
|
||||
grid-template-columns: repeat( auto-fit, minmax(220px, 1fr) );
|
||||
grid-template-rows: auto;
|
||||
}
|
||||
|
||||
.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;
|
||||
}
|
||||
|
||||
.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;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,159 +0,0 @@
|
|||
import * as React from "react";
|
||||
import styles from "./PersonalApps.module.scss";
|
||||
import { IPersonalAppsProps } from "./IPersonalAppsProps";
|
||||
import { IPersonalAppsState } from "./IPersonalAppsState";
|
||||
import { escape } from "@microsoft/sp-lodash-subset";
|
||||
import { AppItem } from "../components/AppItem/AppItem";
|
||||
import { AppTile } from "../components/AppTile/AppTile";
|
||||
import { FontIcon, Label } from "office-ui-fabric-react";
|
||||
import { ManageApps } from "../components/ManageApps/ManageApps";
|
||||
import { IListItem } from "./ManageApps/IListItem";
|
||||
import dataservices from "../../../services/dataservices";
|
||||
import { Placeholder } from "@pnp/spfx-controls-react/lib/Placeholder";
|
||||
import { DisplayMode } from "@microsoft/sp-core-library";
|
||||
import { Customizer } from '@uifabric/utilities/lib/';
|
||||
import {
|
||||
Spinner,
|
||||
SpinnerSize,
|
||||
MessageBar,
|
||||
MessageBarType
|
||||
} from "office-ui-fabric-react";
|
||||
import { FormHelperText } from "@material-ui/core";
|
||||
import strings from "PersonalAppsWebPartStrings";
|
||||
|
||||
export default class PersonalApps extends React.Component<
|
||||
IPersonalAppsProps,
|
||||
IPersonalAppsState
|
||||
> {
|
||||
constructor(props: IPersonalAppsProps) {
|
||||
super(props);
|
||||
this.state = {
|
||||
showPanel: false,
|
||||
apps: [],
|
||||
isLoading: false,
|
||||
hasError: false,
|
||||
errorMessage: ""
|
||||
};
|
||||
}
|
||||
|
||||
public async componentDidMount(): Promise<void> {
|
||||
this.setState({ isLoading: true });
|
||||
try {
|
||||
const _listApps = await dataservices.getUserApps();
|
||||
this.setState({
|
||||
apps: _listApps,
|
||||
isLoading: false,
|
||||
hasError: false,
|
||||
errorMessage: ""
|
||||
});
|
||||
} catch (error) {
|
||||
this.setState({ hasError: true, errorMessage: error.message });
|
||||
}
|
||||
}
|
||||
|
||||
private _onPanelDismiss = (apps: IListItem[], changed: boolean) => {
|
||||
if (changed) {
|
||||
this.setState({
|
||||
apps: apps,
|
||||
showPanel: false
|
||||
});
|
||||
} else {
|
||||
this.setState({
|
||||
showPanel: false
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
public render(): React.ReactElement<IPersonalAppsProps> {
|
||||
const { apps, isLoading, hasError, errorMessage } = this.state;
|
||||
const { view } = this.props;
|
||||
|
||||
return (
|
||||
<>
|
||||
<Customizer settings={{ theme: this.props.themeVariant }}>
|
||||
<div
|
||||
style={{
|
||||
display: "flex",
|
||||
flexDirection: "row",
|
||||
width: "100%",
|
||||
justifyContent: "space-between"
|
||||
}}
|
||||
>
|
||||
<Label className={styles.title}>{this.props.title}</Label>
|
||||
<FontIcon
|
||||
iconName="PlayerSettings"
|
||||
title={"My Apps Settings"}
|
||||
className={styles.imageSetting}
|
||||
onClick={event => {
|
||||
event.preventDefault();
|
||||
this.setState({ showPanel: true });
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
<div className={styles.personalApps}>
|
||||
{apps && apps.length == 0 && !isLoading && (
|
||||
<Placeholder
|
||||
iconName="AppIconDefaultList"
|
||||
iconText={strings.PlaceholderIconText}
|
||||
description={strings.PlaceHolderDescription}
|
||||
buttonLabel={strings.PlaceHolderButtonLabel}
|
||||
onConfigure={() => {
|
||||
this.setState({ showPanel: true });
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
|
||||
{isLoading && (
|
||||
<div
|
||||
style={{
|
||||
display: "flex",
|
||||
justifyContent: "center",
|
||||
width: "100%"
|
||||
}}
|
||||
>
|
||||
<Spinner size={SpinnerSize.medium} />
|
||||
</div>
|
||||
)}
|
||||
{hasError && (
|
||||
<MessageBar messageBarType={MessageBarType.error}>
|
||||
{errorMessage}
|
||||
</MessageBar>
|
||||
)}
|
||||
<div className={view == 'Tiles' ? styles.containerTiles : styles.containerItems }>
|
||||
{apps &&
|
||||
apps.length > 0 &&
|
||||
apps.map(item => {
|
||||
return (
|
||||
<>
|
||||
{view == "Tiles" ? (
|
||||
<AppTile
|
||||
title={item.name}
|
||||
description={item.description}
|
||||
iconName={item.iconName}
|
||||
url={item.url}
|
||||
/>
|
||||
) : (
|
||||
<AppItem
|
||||
title={item.name}
|
||||
description={item.description}
|
||||
iconName={item.iconName}
|
||||
url={item.url}
|
||||
/>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
})}
|
||||
{this.state.showPanel && (
|
||||
<ManageApps
|
||||
onDismiss={this._onPanelDismiss}
|
||||
showPanel={this.state.showPanel}
|
||||
Apps={this.state.apps}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</Customizer>
|
||||
</>
|
||||
);
|
||||
}
|
||||
}
|
|
@ -1,17 +0,0 @@
|
|||
define([], function() {
|
||||
return {
|
||||
IconPickerCancelLabel: "Cancel",
|
||||
IconPickerSelectedLabel: "Selected",
|
||||
IconPickerSaveLabel: "Save",
|
||||
IconPickerSelectLabel: "Select an Icon",
|
||||
IconPickerCloseLabel: "Close",
|
||||
PlaceHolderButtonLabel: "Add",
|
||||
PlaceHolderDescription: "Please add your favorite Apps ",
|
||||
PlaceholderIconText: "You don't have any favorite Apps",
|
||||
CancelLabelButton: "Cancel",
|
||||
SaveLabelButtom: "Save",
|
||||
"PropertyPaneDescription": "Description",
|
||||
"BasicGroupName": "Group Name",
|
||||
"DescriptionFieldLabel": "Description Field"
|
||||
}
|
||||
});
|
|
@ -1,20 +0,0 @@
|
|||
declare interface IPersonalAppsWebPartStrings {
|
||||
IconPickerCancelLabel: string;
|
||||
IconPickerSelectedLabel: string;
|
||||
IconPickerSaveLabel: string;
|
||||
IconPickerSelectLabel: string;
|
||||
IconPickerCloseLabel: string;
|
||||
PlaceHolderButtonLabel: string;
|
||||
PlaceHolderDescription: string;
|
||||
PlaceholderIconText: string;
|
||||
CancelLabelButton: string;
|
||||
SaveLabelButtom: string;
|
||||
PropertyPaneDescription: string;
|
||||
BasicGroupName: string;
|
||||
DescriptionFieldLabel: string;
|
||||
}
|
||||
|
||||
declare module 'PersonalAppsWebPartStrings' {
|
||||
const strings: IPersonalAppsWebPartStrings;
|
||||
export = strings;
|
||||
}
|
Before Width: | Height: | Size: 3.0 KiB |
Before Width: | Height: | Size: 1.4 KiB |
|
@ -1,47 +0,0 @@
|
|||
{
|
||||
"$schema": "https://developer.microsoft.com/en-us/json-schemas/teams/v1.2/MicrosoftTeams.schema.json",
|
||||
"manifestVersion": "1.2",
|
||||
"packageName": "personal-apps",
|
||||
"id": "ab3683c6-0388-47a2-84f5-90d01259d0ed",
|
||||
"version": "0.1",
|
||||
"developer": {
|
||||
"name": "Dev",
|
||||
"websiteUrl": "https://products.office.com/en-us/sharepoint/collaboration",
|
||||
"privacyUrl": "https://privacy.microsoft.com/en-us/privacystatement",
|
||||
"termsOfUseUrl": "https://www.microsoft.com/en-us/servicesagreement"
|
||||
},
|
||||
"name": {
|
||||
"short": "My Personal Apps"
|
||||
},
|
||||
"description": {
|
||||
"short": "My Personal Apps",
|
||||
"full": "My Personal Apps "
|
||||
},
|
||||
"icons": {
|
||||
"outline": "4dfcaee3-8a85-4d80-9fa3-23bcb1e08923_outline.png",
|
||||
"color": "4dfcaee3-8a85-4d80-9fa3-23bcb1e08923_color.png"
|
||||
},
|
||||
"accentColor": "#004578",
|
||||
"configurableTabs": [
|
||||
{
|
||||
"configurationUrl": "https://{teamSiteDomain}{teamSitePath}/_layouts/15/TeamsLogon.aspx?SPFX=true&dest={teamSitePath}/_layouts/15/teamshostedapp.aspx%3FopenPropertyPane=true%26teams%26componentId=ab3683c6-0388-47a2-84f5-90d01259d0ed",
|
||||
"canUpdateConfiguration": false,
|
||||
"scopes": [
|
||||
"team"
|
||||
]
|
||||
}
|
||||
],
|
||||
"validDomains": [
|
||||
"*.login.microsoftonline.com",
|
||||
"*.sharepoint.com",
|
||||
"*.sharepoint-df.com",
|
||||
"spoppe-a.akamaihd.net",
|
||||
"spoprod-a.akamaihd.net",
|
||||
"resourceseng.blob.core.windows.net",
|
||||
"msft.spoppe.com"
|
||||
],
|
||||
"webApplicationInfo": {
|
||||
"resource": "https://{teamSiteDomain}",
|
||||
"id": "00000003-0000-0ff1-ce00-000000000000"
|
||||
}
|
||||
}
|
|
@ -1,64 +0,0 @@
|
|||
/**
|
||||
* This script updates the package-solution version analogue to the
|
||||
* the package.json file.
|
||||
*/
|
||||
|
||||
if (process.env.npm_package_version === undefined) {
|
||||
|
||||
throw 'Package version cannot be evaluated';
|
||||
|
||||
}
|
||||
|
||||
// define path to package-solution file
|
||||
const solution = './config/package-solution.json',
|
||||
teams = './teams/manifest.json';
|
||||
|
||||
// require filesystem instance
|
||||
const fs = require('fs');
|
||||
|
||||
// get next automated package version from process variable
|
||||
const nextPkgVersion = process.env.npm_package_version;
|
||||
|
||||
// make sure next build version match
|
||||
const nextVersion = nextPkgVersion.indexOf('-') === -1 ?
|
||||
nextPkgVersion : nextPkgVersion.split('-')[0];
|
||||
|
||||
// Update version in SPFx package-solution if exists
|
||||
if (fs.existsSync(solution)) {
|
||||
|
||||
// read package-solution file
|
||||
const solutionFileContent = fs.readFileSync(solution, 'UTF-8');
|
||||
// parse file as json
|
||||
const solutionContents = JSON.parse(solutionFileContent);
|
||||
|
||||
// set property of version to next version
|
||||
solutionContents.solution.version = nextVersion + '.0';
|
||||
|
||||
// save file
|
||||
fs.writeFileSync(
|
||||
solution,
|
||||
// convert file back to proper json
|
||||
JSON.stringify(solutionContents, null, 2),
|
||||
'UTF-8');
|
||||
|
||||
}
|
||||
|
||||
// Update version in teams manifest if exists
|
||||
if (fs.existsSync(teams)) {
|
||||
|
||||
// read package-solution file
|
||||
const teamsManifestContent = fs.readFileSync(teams, 'UTF-8');
|
||||
// parse file as json
|
||||
const teamsContent = JSON.parse(teamsManifestContent);
|
||||
|
||||
// set property of version to next version
|
||||
teamsContent.version = nextVersion;
|
||||
|
||||
// save file
|
||||
fs.writeFileSync(
|
||||
teams,
|
||||
// convert file back to proper json
|
||||
JSON.stringify(teamsContent, null, 2),
|
||||
'UTF-8');
|
||||
|
||||
}
|
|
@ -1,40 +0,0 @@
|
|||
{
|
||||
"extends": "./node_modules/@microsoft/rush-stack-compiler-3.7/includes/tsconfig-web.json",
|
||||
"compilerOptions": {
|
||||
"target": "es5",
|
||||
"forceConsistentCasingInFileNames": true,
|
||||
"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",
|
||||
"webpack-env"
|
||||
],
|
||||
"lib": [
|
||||
"es5",
|
||||
"dom",
|
||||
"es2015.collection"
|
||||
],
|
||||
"allowSyntheticDefaultImports": true,
|
||||
"esModuleInterop": true
|
||||
},
|
||||
"include": [
|
||||
"src/**/*.ts", "src/webparts/personalApps/components/AppItem/IAppItem.tsx"
|
||||
],
|
||||
"exclude": [
|
||||
"node_modules",
|
||||
"lib"
|
||||
]
|
||||
}
|
|
@ -1,30 +0,0 @@
|
|||
{
|
||||
"extends": "@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-use-before-declare": true,
|
||||
"no-with-statement": true,
|
||||
"semicolon": true,
|
||||
"trailing-comma": false,
|
||||
"typedef": false,
|
||||
"typedef-whitespace": false,
|
||||
"use-named-parameter": true,
|
||||
"variable-name": false,
|
||||
"whitespace": false
|
||||
}
|
||||
}
|