Add 'samples/react-cherry-picked-content/' from commit '85f9d532109135b43305e2f00923188c16c04226'
git-subtree-dir: samples/react-cherry-picked-content git-subtree-mainline:51eebc82de
git-subtree-split:85f9d53210
This commit is contained in:
commit
8e5c265a01
|
@ -0,0 +1,33 @@
|
|||
# Logs
|
||||
logs
|
||||
*.log
|
||||
npm-debug.log*
|
||||
|
||||
# Dependency directories
|
||||
node_modules
|
||||
|
||||
# Build generated files
|
||||
dist
|
||||
lib
|
||||
release
|
||||
solution
|
||||
temp
|
||||
*.sppkg
|
||||
|
||||
# Coverage directory used by tools like istanbul
|
||||
coverage
|
||||
|
||||
# OSX
|
||||
.DS_Store
|
||||
|
||||
# Visual Studio files
|
||||
.ntvs_analysis.dat
|
||||
.vs
|
||||
bin
|
||||
obj
|
||||
|
||||
# Resx Generated Code
|
||||
*.resx.ts
|
||||
|
||||
# Styles Generated Code
|
||||
*.scss.ts
|
|
@ -0,0 +1,16 @@
|
|||
!dist
|
||||
config
|
||||
|
||||
gulpfile.js
|
||||
|
||||
release
|
||||
src
|
||||
temp
|
||||
|
||||
tsconfig.json
|
||||
tslint.json
|
||||
|
||||
*.log
|
||||
|
||||
.yo-rc.json
|
||||
.vscode
|
|
@ -0,0 +1,23 @@
|
|||
{
|
||||
"version": "0.2.0",
|
||||
"configurations": [
|
||||
{
|
||||
"name": "Hosted workbench",
|
||||
"type": "pwa-chrome",
|
||||
"request": "launch",
|
||||
"url": "https://enter-your-SharePoint-site/_layouts/workbench.aspx",
|
||||
"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",
|
||||
"-incognito"
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
|
@ -0,0 +1,13 @@
|
|||
// Place your settings in this file to overwrite default and user settings.
|
||||
{
|
||||
// Configure glob patterns for excluding files and folders in the file explorer.
|
||||
"files.exclude": {
|
||||
"**/.git": true,
|
||||
"**/.DS_Store": true,
|
||||
"**/bower_components": true,
|
||||
"**/coverage": true,
|
||||
"**/lib-amd": true,
|
||||
"src/**/*.scss.ts": true
|
||||
},
|
||||
"typescript.tsdk": ".\\node_modules\\typescript\\lib"
|
||||
}
|
|
@ -0,0 +1,16 @@
|
|||
{
|
||||
"@microsoft/generator-sharepoint": {
|
||||
"plusBeta": false,
|
||||
"isCreatingSolution": true,
|
||||
"version": "1.14.0",
|
||||
"libraryName": "react-cherry-picked-content",
|
||||
"libraryId": "6a0a135e-c421-4508-955a-dbb0b392104a",
|
||||
"environment": "spo",
|
||||
"packageManager": "npm",
|
||||
"solutionName": "react-cherry-picked-content",
|
||||
"solutionShortDescription": "react-cherry-picked-content description",
|
||||
"skipFeatureDeployment": true,
|
||||
"isDomainIsolated": false,
|
||||
"componentType": "webpart"
|
||||
}
|
||||
}
|
|
@ -0,0 +1,98 @@
|
|||
# react-cherry-picked-content
|
||||
|
||||
## Summary
|
||||
|
||||
The Cherry=Picked Content Web Part is a modern replacement for the classic Content Editor Web Part, with a twist: code snippets can only be picked from approved document libraries.
|
||||
|
||||
![React Cherry=Picked Content Sample](./assets/React-Cherry-Picked-Content-Sample.png)
|
||||
|
||||
## Used SharePoint Framework Version
|
||||
|
||||
![version](https://img.shields.io/badge/version-1.14.0-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
|
||||
|
||||
Start by editing the ApprovedLibraries.ts file to list your approved libraries. Then upload your code snippets to those locations. If you are looking for ideas, check out the samples folder.
|
||||
|
||||
The code can be rendered in two ways:
|
||||
- isolated: the code is wrapped in an iframe to prevent conflicts with other Web Parts. Note: this is not a security feature.
|
||||
- non isolated: the code is directly inserted in the page.
|
||||
|
||||
## Solution
|
||||
|
||||
Solution|Author(s)
|
||||
--------|---------
|
||||
React-Cherry-Picked-Content | [Christophe Humbert](https://github.com/PathToSharePoint)
|
||||
|
||||
## Version history
|
||||
|
||||
Version|Date|Comments
|
||||
-------|----|--------
|
||||
0.3.0|March 9, 2022|4 samples added
|
||||
0.2.0|March 6, 2022|Refactoring
|
||||
0.1.0|February 21, 2022|Initial draft
|
||||
|
||||
## Disclaimer
|
||||
|
||||
**THIS CODE IS PROVIDED *AS IS* WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING ANY IMPLIED WARRANTIES OF FITNESS FOR A PARTICULAR PURPOSE, MERCHANTABILITY, OR NON-INFRINGEMENT.**
|
||||
|
||||
---
|
||||
|
||||
## Minimal Path to Awesome
|
||||
|
||||
- Clone this repository
|
||||
- Under components, edit ApprovedLibraries.ts to list your approved libraries that contain HTML snippets
|
||||
- Upload the code snippets
|
||||
- Ensure that you are at the solution folder
|
||||
- in the command-line run:
|
||||
- **npm install**
|
||||
- **gulp serve**
|
||||
|
||||
## Features
|
||||
|
||||
This Web Part illustrates the following concepts:
|
||||
|
||||
- Cascading dropdown and conditional display in the Property Pane
|
||||
- Use of SPHttpClient and the SharePoint REST API to query SharePoint content
|
||||
- React function component with useState and useEffect hooks
|
||||
- React Portal in combination with an iframe
|
||||
- Various examples of client-side code in the samples: Microsoft Graph (teams), Microsoft Graph Toolkit (people, email, agenda), charts (Chart.js, Chartist), widgets (map, stock, countdown, clock, video, game), SharePoint SOAP and REST APIs.
|
||||
|
||||
## 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-ppw-html%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-ppw-html) 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-ppw-html&template=bug-report.yml&sample=react-ppw-html&authors=@PathToSharePoint&title=react-ppw-html%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-ppw-html&template=question.yml&sample=react-ppw-html&authors=@PathToSharePoint&title=react-ppw-html%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-ppw-html&template=suggestion.yml&sample=react-ppw-html&authors=@PathToSharePoint&title=react-ppw-html%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/react-cherry-picked-content" />
|
Binary file not shown.
After Width: | Height: | Size: 1.1 MiB |
|
@ -0,0 +1,18 @@
|
|||
{
|
||||
"$schema": "https://developer.microsoft.com/json-schemas/spfx-build/config.2.0.schema.json",
|
||||
"version": "2.0",
|
||||
"bundles": {
|
||||
"cherry-picked-content-web-part": {
|
||||
"components": [
|
||||
{
|
||||
"entrypoint": "./lib/webparts/cherryPickedContent/CherryPickedContentWebPart.js",
|
||||
"manifest": "./src/webparts/cherryPickedContent/CherryPickedContentWebPart.manifest.json"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"externals": {},
|
||||
"localizedResources": {
|
||||
"CherryPickedContentWebPartStrings": "lib/webparts/cherryPickedContent/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-cherry-picked-content",
|
||||
"accessKey": "<!-- ACCESS KEY -->"
|
||||
}
|
|
@ -0,0 +1,40 @@
|
|||
{
|
||||
"$schema": "https://developer.microsoft.com/json-schemas/spfx-build/package-solution.schema.json",
|
||||
"solution": {
|
||||
"name": "react-cherry-picked-content-client-side-solution",
|
||||
"id": "6a0a135e-c421-4508-955a-dbb0b392104a",
|
||||
"version": "1.0.0.0",
|
||||
"includeClientSideAssets": true,
|
||||
"skipFeatureDeployment": true,
|
||||
"isDomainIsolated": false,
|
||||
"developer": {
|
||||
"name": "Christophe Humbert",
|
||||
"websiteUrl": "",
|
||||
"privacyUrl": "",
|
||||
"termsOfUseUrl": "",
|
||||
"mpnId": "Undefined-1.14.0"
|
||||
},
|
||||
"metadata": {
|
||||
"shortDescription": {
|
||||
"default": "react-cherry-picked-content description"
|
||||
},
|
||||
"longDescription": {
|
||||
"default": "react-cherry-picked-content description"
|
||||
},
|
||||
"screenshotPaths": [],
|
||||
"videoUrl": "",
|
||||
"categories": []
|
||||
},
|
||||
"features": [
|
||||
{
|
||||
"title": "react-cherry-picked-content Feature",
|
||||
"description": "The feature that activates elements of the react-cherry-picked-content solution.",
|
||||
"id": "bb64f400-c482-4467-83ae-3a05b9eacf4d",
|
||||
"version": "1.0.0.0"
|
||||
}
|
||||
]
|
||||
},
|
||||
"paths": {
|
||||
"zippedPackage": "solution/react-cherry-picked-content.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'));
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,32 @@
|
|||
{
|
||||
"name": "react-cherry-picked-content",
|
||||
"version": "0.3.0",
|
||||
"private": true,
|
||||
"main": "lib/index.js",
|
||||
"scripts": {
|
||||
"build": "gulp bundle",
|
||||
"clean": "gulp clean",
|
||||
"test": "gulp test"
|
||||
},
|
||||
"dependencies": {
|
||||
"react": "16.13.1",
|
||||
"react-dom": "16.13.1",
|
||||
"office-ui-fabric-react": "7.174.1",
|
||||
"@microsoft/sp-core-library": "1.14.0",
|
||||
"@microsoft/sp-property-pane": "1.14.0",
|
||||
"@microsoft/sp-webpart-base": "1.14.0",
|
||||
"@microsoft/sp-lodash-subset": "1.14.0",
|
||||
"@microsoft/sp-office-ui-fabric-core": "1.14.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/react": "16.9.51",
|
||||
"@types/react-dom": "16.9.8",
|
||||
"@microsoft/sp-build-web": "1.14.0",
|
||||
"@microsoft/sp-tslint-rules": "1.14.0",
|
||||
"@microsoft/sp-module-interfaces": "1.14.0",
|
||||
"@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,79 @@
|
|||
<div style="background-color: CadetBlue; padding: 20px;">
|
||||
<h3>Chart.js Bubble Chart</h3>
|
||||
<p>Isolated mode: ⚠ mandatory. Source: <a href="https://tobiasahlin.com/blog/chartjs-charts-to-get-you-started/" target="_blank">Tobias Ahlin</a>.</p>
|
||||
<div style="width:500px; height:300px; background-color:#f7f7f7;">
|
||||
<canvas id="bubble-chart"></canvas>
|
||||
<div style="padding:10px;">Source: </div>
|
||||
</div>
|
||||
|
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/2.5.0/Chart.min.js" onload="drawBubble()"></script>
|
||||
|
||||
<script>
|
||||
function drawBubble() {
|
||||
new Chart(document.getElementById("bubble-chart"), {
|
||||
type: 'bubble',
|
||||
data: {
|
||||
labels: "Africa",
|
||||
datasets: [
|
||||
{
|
||||
label: ["China"],
|
||||
backgroundColor: "rgba(255,221,50,0.2)",
|
||||
borderColor: "rgba(255,221,50,1)",
|
||||
data: [{
|
||||
x: 21269017,
|
||||
y: 5.245,
|
||||
r: 15
|
||||
}]
|
||||
}, {
|
||||
label: ["Denmark"],
|
||||
backgroundColor: "rgba(60,186,159,0.2)",
|
||||
borderColor: "rgba(60,186,159,1)",
|
||||
data: [{
|
||||
x: 258702,
|
||||
y: 7.526,
|
||||
r: 10
|
||||
}]
|
||||
}, {
|
||||
label: ["Germany"],
|
||||
backgroundColor: "rgba(0,0,0,0.2)",
|
||||
borderColor: "#000",
|
||||
data: [{
|
||||
x: 3979083,
|
||||
y: 6.994,
|
||||
r: 15
|
||||
}]
|
||||
}, {
|
||||
label: ["Japan"],
|
||||
backgroundColor: "rgba(193,46,12,0.2)",
|
||||
borderColor: "rgba(193,46,12,1)",
|
||||
data: [{
|
||||
x: 4931877,
|
||||
y: 5.921,
|
||||
r: 15
|
||||
}]
|
||||
}
|
||||
]
|
||||
},
|
||||
options: {
|
||||
title: {
|
||||
display: true,
|
||||
text: 'Predicted world population (millions) in 2050'
|
||||
}, scales: {
|
||||
yAxes: [{
|
||||
scaleLabel: {
|
||||
display: true,
|
||||
labelString: "Happiness"
|
||||
}
|
||||
}],
|
||||
xAxes: [{
|
||||
scaleLabel: {
|
||||
display: true,
|
||||
labelString: "GDP (PPP)"
|
||||
}
|
||||
}]
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
</script>
|
||||
</div>
|
|
@ -0,0 +1,39 @@
|
|||
<div style="background-color: CadetBlue; padding: 20px;">
|
||||
<h3>Chartist Animated Lines</h3>
|
||||
<p>Isolated mode: ⚠ mandatory. Source: <a href="https://gionkunz.github.io/chartist-js/" target="_blank">Chartist.js</a>.</p>
|
||||
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/chartist@0.11.4/dist/chartist.css">
|
||||
<script src="https://cdn.jsdelivr.net/npm/chartist@0.11.4/dist/chartist.min.js" onload="drawChart()"></script>
|
||||
<div class="ct-chart ct-perfect-fourth"></div>
|
||||
</div>
|
||||
<script>
|
||||
function drawChart() {
|
||||
var chart = new Chartist.Line('.ct-chart', {
|
||||
labels: ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'],
|
||||
series: [
|
||||
[1, 5, 2, 5, 4, 3],
|
||||
[2, 3, 4, 8, 1, 2],
|
||||
[5, 4, 3, 2, 1, 0.5]
|
||||
]
|
||||
}, {
|
||||
low: 0,
|
||||
showArea: true,
|
||||
showPoint: false,
|
||||
fullWidth: true
|
||||
});
|
||||
|
||||
chart.on('draw', function(data) {
|
||||
if(data.type === 'line' || data.type === 'area') {
|
||||
data.element.animate({
|
||||
d: {
|
||||
begin: 2000 * data.index,
|
||||
dur: 2000,
|
||||
from: data.path.clone().scale(1, 0).translate(0, data.chartRect.height()).stringify(),
|
||||
to: data.path.clone().stringify(),
|
||||
easing: Chartist.Svg.Easing.easeOutQuint
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
</script>
|
||||
</div>
|
File diff suppressed because one or more lines are too long
|
@ -0,0 +1,49 @@
|
|||
|
||||
<div style="background-color: CadetBlue; padding: 20px;">
|
||||
<h3>Agenda Custom View - Microsoft Graph Toolkit</h3>
|
||||
<p>Isolated mode: ⚠ mandatory. Find more samples on <a href="https://mgt.dev/?path=/story/overview--page" target="_blank">MGT Playground</a>. MGT components require API permissions, see the <a href="https://docs.microsoft.com/en-us/graph/toolkit/overview">Microsoft docs</a> for more info.</p>
|
||||
<script>
|
||||
function setProvider() {mgt.Providers.globalProvider = new mgt.SharePointProvider(props.context);}
|
||||
</script>
|
||||
<script src="https://unpkg.com/@microsoft/mgt/dist/bundle/wc/webcomponents-loader.js" type="text/javascript"></script>
|
||||
<script src="https://unpkg.com/@microsoft/mgt/dist/bundle/mgt.es6.js" type="text/javascript" onload="setProvider()"></script>
|
||||
<mgt-person person-query="me" view="twoLines"></mgt-person>
|
||||
<mgt-agenda></mgt-agenda>
|
||||
<!-- <mgt-agenda show-max="7" days="10">
|
||||
<template data-type="event">
|
||||
<div class="root">
|
||||
<div class="time-container">
|
||||
<div class="date">{{ dayFromDateTime(event.start.dateTime)}}</div>
|
||||
<div class="time">{{ timeRangeFromEvent(event, '12') }}</div>
|
||||
</div>
|
||||
|
||||
<div class="separator">
|
||||
<div class="vertical-line top"></div>
|
||||
<div class="circle">
|
||||
<div data-if="!event.bodyPreview.includes('Join Microsoft Teams Meeting')" class="inner-circle">
|
||||
</div>
|
||||
</div>
|
||||
<div class="vertical-line bottom"></div>
|
||||
</div>
|
||||
|
||||
<div class="details">
|
||||
<div class="subject">{{ event.subject }}</div>
|
||||
<div class="location" data-if="event.location.displayName">
|
||||
at
|
||||
<a href="https://bing.com/maps/default.aspx?where1={{event.location.displayName}}"
|
||||
target="_blank"><b>{{ event.location.displayName }}</b></a>
|
||||
</div>
|
||||
<div class="attendees" data-if="event.attendees.length">
|
||||
<span class="attendee" data-for="attendee in event.attendees">
|
||||
<mgt-person person-query="{{attendee.emailAddress.name}}"></mgt-person>
|
||||
</span>
|
||||
</div>
|
||||
<div class="online-meeting" data-if="event.bodyPreview.includes('Join Microsoft Teams Meeting')">
|
||||
<img class="online-meeting-icon" src="https://img.icons8.com/color/48/000000/microsoft-teams.png" />
|
||||
<a class="online-meeting-link" href="{{ event.onlineMeetingUrl }}">Join Teams Meeting</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
</mgt-agenda> -->
|
||||
</div>
|
|
@ -0,0 +1,70 @@
|
|||
<div style="background-color: CadetBlue; padding: 20px;">
|
||||
<h3>Emails Custom View - Microsoft Graph Toolkit</h3>
|
||||
<p>Isolated mode: ⚠ mandatory. Find more samples on <a href="https://mgt.dev/?path=/story/overview--page" target="_blank">MGT Playground</a>. MGT components require API permissions, see the <a href="https://docs.microsoft.com/en-us/graph/toolkit/overview">Microsoft docs</a> for more info.</p>
|
||||
<style>
|
||||
.email {
|
||||
box-shadow: 0 3px 7px rgba(0, 0, 0, 0.3);
|
||||
padding: 10px;
|
||||
margin: 8px 4px;
|
||||
font-family: Segoe UI, Frutiger, Frutiger Linotype, Dejavu Sans, Helvetica Neue, Arial, sans-serif;
|
||||
}
|
||||
|
||||
.email:hover {
|
||||
box-shadow: 0 3px 14px rgba(0, 0, 0, 0.3);
|
||||
padding: 10px;
|
||||
margin: 8px 4px;
|
||||
}
|
||||
|
||||
.email h3 {
|
||||
font-size: 12px;
|
||||
margin-bottom: 4px;
|
||||
}
|
||||
|
||||
.email h4 {
|
||||
font-size: 10px;
|
||||
margin-top: 0px;
|
||||
margin-bottom: 4px;
|
||||
}
|
||||
|
||||
.email mgt-person {
|
||||
--font-size: 10px;
|
||||
--avatar-size-s: 12px;
|
||||
}
|
||||
|
||||
.email .preview {
|
||||
font-size: 13px;
|
||||
text-overflow: ellipsis;
|
||||
word-wrap: break-word;
|
||||
overflow: hidden;
|
||||
max-height: 2.8em;
|
||||
line-height: 1.4em;
|
||||
}
|
||||
</style>
|
||||
<script>
|
||||
function setProvider() {mgt.Providers.globalProvider = new mgt.SharePointProvider(props.context);}
|
||||
</script>
|
||||
<script src="https://unpkg.com/@microsoft/mgt/dist/bundle/wc/webcomponents-loader.js" type="text/javascript"></script>
|
||||
<script src="https://unpkg.com/@microsoft/mgt/dist/bundle/mgt.es6.js" type="text/javascript" onload="setProvider()"></script>
|
||||
<mgt-person person-query="me" view="twoLines"></mgt-person>
|
||||
<mgt-get resource="/me/messages" version="beta" scopes="mail.read" max-pages="1">
|
||||
<template>
|
||||
<div class="email" data-for="email in value">
|
||||
<h3>{{ email.subject }}</h3>
|
||||
<h4>
|
||||
<mgt-person person-query="{{email.sender.emailAddress.address}}" view="oneline" person-card="hover">
|
||||
</mgt-person>
|
||||
</h4>
|
||||
<div data-if="email.bodyPreview" class="preview" innerHtml>{{email.bodyPreview}}</div>
|
||||
<div data-else class="preview">
|
||||
email body is empty
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<template data-type="loading">
|
||||
loading
|
||||
</template>
|
||||
<template data-type="error">
|
||||
{{ this }}
|
||||
</template>
|
||||
</mgt-get>
|
||||
</div>
|
|
@ -0,0 +1,25 @@
|
|||
<div style="background-color: CadetBlue; padding: 20px;">
|
||||
<h3>People Components - Microsoft Graph Toolkit</h3>
|
||||
<p>Isolated mode: ⚠ mandatory. Find more samples on the <a href="https://mgt.dev/?path=/story/overview--page" target="_blank">MGT Playground</a>. MGT components require API permissions, see the <a href="https://docs.microsoft.com/en-us/graph/toolkit/overview">Microsoft docs</a> for more info.</p>
|
||||
<script>
|
||||
function setProvider() {mgt.Providers.globalProvider = new mgt.SharePointProvider(props.context);}
|
||||
</script>
|
||||
<script src="https://unpkg.com/@microsoft/mgt/dist/bundle/wc/webcomponents-loader.js" type="text/javascript">
|
||||
</script>
|
||||
<script src="https://unpkg.com/@microsoft/mgt/dist/bundle/mgt.es6.js" type="text/javascript" onload="setProvider()">
|
||||
</script>
|
||||
<div style="display: flex;">
|
||||
<div style="flex: 50%;">
|
||||
<h4>mgt-login</h4>
|
||||
<mgt-login></mgt-login>
|
||||
<h4>mgt-person</h4>
|
||||
<mgt-person person-query="me" view="twoLines"></mgt-person>
|
||||
<h4>mgt-people</h4>
|
||||
<mgt-people show-max="10"></mgt-people>
|
||||
</div>
|
||||
<div style="flex: 50%;">
|
||||
<h4>mgt-person-card</h4>
|
||||
<mgt-person-card person-query="me"></mgt-person-card>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
|
@ -0,0 +1,23 @@
|
|||
<div style="background-color: CadetBlue; padding: 20px;">
|
||||
<h3>My Teams - MS Graph Client</h3>
|
||||
<p>Isolated mode: ⚠ mandatory. This sample leverages the SPFx built-in Microsoft Graph client.</p>
|
||||
<script>
|
||||
var logoURL = "https://static2.sharepointonline.com/files/fabric-cdn-prod_20200430.002/assets/brand-icons/product/svg/teams_48x1.svg";
|
||||
var currentScript = document.currentScript;
|
||||
props.context.msGraphClientFactory
|
||||
.getClient()
|
||||
.then((client) => {
|
||||
client
|
||||
.api("me/joinedTeams")
|
||||
.version("v1.0")
|
||||
.get((err, res) => {
|
||||
if (res) {
|
||||
let items = res.value.map(team => `<li><img src="${logoURL}" style="width:24px;"/> ${team.displayName}</li>`);
|
||||
const newDiv = document.createElement('div');
|
||||
newDiv.innerHTML = `<div>You are a member of ${items.length} teams:</div><ul style="list-style-type: none; columns: 3;">${items.join("")}</ul>`;
|
||||
currentScript.insertAdjacentElement('afterend', newDiv);
|
||||
}
|
||||
});
|
||||
});
|
||||
</script>
|
||||
</div>
|
|
@ -0,0 +1,16 @@
|
|||
<div style="background-color: CadetBlue; padding: 20px;">
|
||||
<h3>Site Lists - SharePoint REST API</h3>
|
||||
<p>Isolated mode: ⚠ mandatory. This sample uses the JavaScript fetch method to query the SharePoint REST API.</p>
|
||||
<script>
|
||||
var currentScript = document.currentScript;
|
||||
fetch(props.context.pageContext.web.absoluteUrl + "/_api/web/lists?$select=Title,ImageUrl", {headers: {'Accept': 'application/json'}})
|
||||
.then(response => response.json())
|
||||
.then(result => result.value)
|
||||
.then(lists => lists.map(list => `<li><img src="${list.ImageUrl}"/> ${list.Title}</li>`))
|
||||
.then(items => {
|
||||
const newDiv = document.createElement('div');
|
||||
newDiv.innerHTML = `<div>${items.length} lists found on this site:</div><ul style="list-style-type: none; columns: 2;">${items.join("")}</ul>`;
|
||||
currentScript.insertAdjacentElement('afterend', newDiv);
|
||||
});
|
||||
</script>
|
||||
</div>
|
|
@ -0,0 +1,30 @@
|
|||
<div style="background-color: CadetBlue; padding: 20px;">
|
||||
<h3>TradingView Widget</h3>
|
||||
<p>Isolated mode: recommended. Source: <a href="https://www.tradingview.com/widget/" target="_blank">TradingView widget</a>.</p>
|
||||
<!-- TradingView Widget BEGIN -->
|
||||
<div class="tradingview-widget-container">
|
||||
<div id="tradingview_cd1aa"></div>
|
||||
<div class="tradingview-widget-copyright"><a href="https://www.tradingview.com/symbols/NASDAQ-MSFT/" rel="noopener" target="_blank"><span class="blue-text">MSFT Chart</span></a> by TradingView</div>
|
||||
<script type="text/javascript" src="https://s3.tradingview.com/tv.js" onload="drawChart()"></script>
|
||||
<script type="text/javascript">
|
||||
function drawChart() {
|
||||
new TradingView.widget(
|
||||
{
|
||||
"autosize": true,
|
||||
"symbol": "NASDAQ:MSFT",
|
||||
"interval": "D",
|
||||
"timezone": "Etc/UTC",
|
||||
"theme": "light",
|
||||
"style": "1",
|
||||
"locale": "en",
|
||||
"toolbar_bg": "#f1f3f6",
|
||||
"enable_publishing": false,
|
||||
"allow_symbol_change": true,
|
||||
"container_id": "tradingview_cd1aa"
|
||||
}
|
||||
);
|
||||
}
|
||||
</script>
|
||||
</div>
|
||||
<!-- TradingView Widget END -->
|
||||
</div>
|
|
@ -0,0 +1,92 @@
|
|||
<div style="background-color: CadetBlue; padding: 20px;">
|
||||
<h3>Site Lists - pocketSOAP</h3>
|
||||
<p>Isolated mode: ⚠ mandatory. Just for fun, a minimal implementation of the deprecated SharePoint SOAP API. Do not use in production!</p>
|
||||
<script type="text/javascript">
|
||||
|
||||
/********************************************************************************
|
||||
|
||||
pocketSOAP
|
||||
|
||||
Copyright (c) 2009-2022 Christophe Humbert
|
||||
|
||||
********************************************************************************/
|
||||
|
||||
pS = {};
|
||||
|
||||
pS.cleanSpaces = function (string) { return string.replace(/^\s+|\s+$/g, "").replace(/\s+/g, " "); };
|
||||
pS.cleanBreaks = function (string) { return string.replace(/[\r\n\t]/g, ""); };
|
||||
pS.trim = function (string) { return string.replace(/^\s+|\s+$/g, ""); };
|
||||
pS.htmlUnescape = function (str) {
|
||||
return String(str)
|
||||
.replace(/"/g, '"')
|
||||
.replace(/'/g, '\'')
|
||||
.replace(/</g, '<')
|
||||
.replace(/>/g, '>')
|
||||
.replace(/&/g, '&');
|
||||
};
|
||||
|
||||
pS.getWSProperties = function (site, service, operation) {
|
||||
return fetch(site + "/" + '_vti_bin/' + service + '.asmx' + '?op=' + operation + '&_ts=' + new Date().getTime())
|
||||
.then(result => result.text())
|
||||
.then(text => {
|
||||
var regexp = /Content-Type:([\s\S]*?)Content-Length:[\s\S]*?SOAPAction:\s*"(\S*?)"([\s\S]*?)\<\/pre\>/;
|
||||
var results = regexp.exec(text);
|
||||
|
||||
let dv = document.createElement("div");
|
||||
|
||||
var spWS = {};
|
||||
|
||||
spWS.headers = {};
|
||||
spWS.headers['Content-Type'] = pS.trim(results[1]).replace(/[\r\n]/g, "");
|
||||
spWS.headers.SOAPAction = pS.trim(results[2]).replace(/[\r\n]/g, "");
|
||||
|
||||
spWS.soapEnvelope = results[3].replace(/<xsd:schema>[\s\S]+?<\/xsd:schema>/g, "");
|
||||
spWS.soapEnvelope = spWS.soapEnvelope.replace(/<([\S]+)>[^;]+<\/\1>/g, '<$1/>');
|
||||
spWS.soapEnvelope = pS.htmlUnescape(spWS.soapEnvelope);
|
||||
// Trim and remove line breaks (\n,\r), tabs (\t), etc.
|
||||
spWS.soapEnvelope = pS.trim(spWS.soapEnvelope).replace(/>\s+?</g, "><");
|
||||
|
||||
return spWS;
|
||||
}); // end then
|
||||
};
|
||||
|
||||
pS.soap = function (options) {
|
||||
return pS.getWSProperties(options.site, options.service, options.operation)
|
||||
.then(function (wsProperties) {
|
||||
var wsURL = options.site + '/_vti_bin/' + options.service + '.asmx';
|
||||
|
||||
var wsData = wsProperties.soapEnvelope.replace(/<([\S]+?)\/>/g, function (m, key) {
|
||||
if (options[key]) { return ('<' + key + '>' + options[key] + '</' + key + '>') }
|
||||
else { return ""; }
|
||||
});
|
||||
|
||||
options.method = 'POST'; // always POST for Web services
|
||||
options.headers = wsProperties.headers;
|
||||
options.body = wsData;
|
||||
|
||||
return fetch(wsURL, options);
|
||||
}); // end then
|
||||
}; // end pS.soap
|
||||
|
||||
</script>
|
||||
|
||||
<script type="text/javascript">
|
||||
var currentScript = document.currentScript;
|
||||
pS.soap({
|
||||
// Service info
|
||||
site: props.context.pageContext.web.absoluteUrl,
|
||||
service: "Lists",
|
||||
operation: "GetListCollection",
|
||||
// Service parameters
|
||||
viewFields: "<ViewFields><FieldRef Name='Title' /><FieldRef Name='ImageURL' /></ViewFields>"
|
||||
})
|
||||
.then(response => response.text())
|
||||
.then(data => new DOMParser().parseFromString(data, "application/xml"))
|
||||
.then(xml => {
|
||||
const newDiv = document.createElement('div');
|
||||
const items = [...xml.getElementsByTagName("List")].map(child => `<li><img src="${child.getAttribute("ImageUrl")}"/> ${child.getAttribute("Title")}</li>`);
|
||||
newDiv.innerHTML = `<div>${items.length} lists found on this site:</div><ul style="list-style-type: none; columns: 2;">${items.join("")}</ul>`;
|
||||
currentScript.insertAdjacentElement('afterend', newDiv);
|
||||
});
|
||||
</script>
|
||||
</div>
|
|
@ -0,0 +1,263 @@
|
|||
<div style="background-color: CadetBlue; padding: 20px;">
|
||||
<h3>Analog Clock</h3>
|
||||
<p>Isolated mode: recommended.</p>
|
||||
<p>Source: Tushar Nankani on <a href="https://github.com/tusharnankani/AnalogClock" target="_blank">Github</a>.</p>
|
||||
</div>
|
||||
<div class="clock">
|
||||
|
||||
<div class="hour">
|
||||
<div class="hr" id="hr">
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="min">
|
||||
<div class="mn" id="mn">
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="sec">
|
||||
<div class="sc" id="sc">
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
<div class="toggleClass" onclick="toggleClass()"></div>
|
||||
<style>
|
||||
|
||||
* {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
body {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
min-height: 100vh;
|
||||
background: #090909;
|
||||
background: #07141b;
|
||||
}
|
||||
|
||||
|
||||
|
||||
body.light {
|
||||
background: #d1dae3;
|
||||
}
|
||||
|
||||
|
||||
.clock {
|
||||
width: 375px;
|
||||
height: 375px;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
background: url(../clock.png);
|
||||
background-size: cover;
|
||||
border: 4px;
|
||||
/* box-shadow: 15px 15px 15px rgba(255, 255, 255, 0.5); */
|
||||
box-shadow: 0em -1.2em 1.2em rgba(255, 255, 255, 0.06),
|
||||
inset 0em -1.2em 1.2em rgba(255, 255, 255, 0.06),
|
||||
0em 1.2em 1.2em rgba(0, 0, 0, 0.3),
|
||||
inset 0em 1.2em 1.2em rgba(0, 0, 0, 0.3);
|
||||
border-radius: 50%;
|
||||
}
|
||||
|
||||
body.light .clock {
|
||||
box-shadow: 0em -1.2em 1.2em rgba(255, 255, 255, 0.3),
|
||||
inset 1em 1em -1em rgba(255, 255, 255, 0.3),
|
||||
0em -1.2em -1.2em rgba(0, 0, 0, 0.5),
|
||||
inset 1em -1em 1em rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
|
||||
.clock :hover {
|
||||
/* yet to be completed; when hovered, diplay complete information about time, `new Date().toLocaleString();` */
|
||||
cursor: pointer;
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
/* The small circle int the center */
|
||||
.clock:before {
|
||||
content: '';
|
||||
position: absolute;
|
||||
width: 15px;
|
||||
height: 15px;
|
||||
background: rgb(255, 255, 255);
|
||||
border-radius: 50%;
|
||||
|
||||
/* The z-index property specifies the stack order of an element.
|
||||
/* An element with greater stack order is always in front of an element with a lower stack order. */
|
||||
/* Note: z-index only works on positioned elements (position: absolute, position: relative, position: fixed, or position: sticky). */
|
||||
z-index: 10000;
|
||||
/* kept as a high value, since wanted at top */
|
||||
}
|
||||
|
||||
body.light .clock:before {
|
||||
background: #1a74be;
|
||||
}
|
||||
|
||||
|
||||
.clock .hour,
|
||||
.clock .min,
|
||||
.clock .sec {
|
||||
position: absolute;
|
||||
}
|
||||
|
||||
/* length of respective arms; */
|
||||
.clock .hour, .hr {
|
||||
width: 160px;
|
||||
height: 160px;
|
||||
}
|
||||
|
||||
.clock .min, .mn {
|
||||
width: 190px;
|
||||
height: 190px;
|
||||
}
|
||||
|
||||
.clock .sec, .sc {
|
||||
width: 230px;
|
||||
height: 230px;
|
||||
}
|
||||
|
||||
|
||||
.hr, .mn, .sc {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
/* align-items: center; */
|
||||
position: absolute;
|
||||
border-radius: 50%;
|
||||
|
||||
}
|
||||
|
||||
|
||||
.hr:before {
|
||||
content: '';
|
||||
position: absolute;
|
||||
width: 7.5px;
|
||||
height: 80px;
|
||||
background: #f81460;
|
||||
z-index: 10;
|
||||
/* z-index least */
|
||||
border-radius: 2.8px;
|
||||
}
|
||||
|
||||
.mn:before {
|
||||
content: '';
|
||||
position: absolute;
|
||||
width: 3.5px;
|
||||
height: 100px;
|
||||
background: #ffffff;
|
||||
z-index: 11;
|
||||
/* z-index more than hour hand */
|
||||
border-radius: 3px;
|
||||
}
|
||||
|
||||
body.light .mn:before {
|
||||
background: #091921;
|
||||
|
||||
}
|
||||
|
||||
|
||||
.sc:before {
|
||||
content: '';
|
||||
position: absolute;
|
||||
width: 2px;
|
||||
height: 150px;
|
||||
background: #0075fa80;
|
||||
z-index: 12;
|
||||
/* z-index more than hour minute hand */
|
||||
border-radius: 3px;
|
||||
}
|
||||
|
||||
|
||||
.toggleClass {
|
||||
position: absolute;
|
||||
top: 35px;
|
||||
right: 150px;
|
||||
width: 20px;
|
||||
margin: 2px;
|
||||
height: 20px;
|
||||
font-size: 18px;
|
||||
border-radius: 50%;
|
||||
background: #d1dae3;
|
||||
color: #d1dae3;
|
||||
font-family: 'Quicksand', sans-serif;
|
||||
cursor: pointer;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
|
||||
.toggleClass:before {
|
||||
position: absolute;
|
||||
content: 'Light Mode';
|
||||
white-space: nowrap;
|
||||
left: 25px;
|
||||
|
||||
}
|
||||
|
||||
body.light .toggleClass {
|
||||
background: #091921;
|
||||
color: #091921;
|
||||
content: 'Dark Mode';
|
||||
}
|
||||
|
||||
|
||||
body.light .toggleClass:before {
|
||||
content: 'Dark Mode';
|
||||
white-space: nowrap;
|
||||
}
|
||||
</style>
|
||||
|
||||
<script>// For toggle button;
|
||||
|
||||
function toggleClass()
|
||||
{
|
||||
|
||||
const body = document.querySelector('body');
|
||||
body.classList.toggle('light');
|
||||
body.style.transition = `0.3s linear`;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
// for time;
|
||||
const deg = 6;
|
||||
// 360 / (12 * 5);
|
||||
|
||||
const hr = document.querySelector('#hr');
|
||||
const mn = document.querySelector('#mn');
|
||||
const sc = document.querySelector('#sc');
|
||||
|
||||
|
||||
setInterval(() => {
|
||||
|
||||
let day = new Date();
|
||||
let hh = day.getHours() * 30;
|
||||
let mm = day.getMinutes() * deg;
|
||||
let ss = day.getSeconds() * deg;
|
||||
let msec = day.getMilliseconds();
|
||||
|
||||
|
||||
// VERY IMPORTANT STEP:
|
||||
|
||||
hr.style.transform = `rotateZ(${(hh) + (mm / 12)}deg)`;
|
||||
mn.style.transform = `rotateZ(${mm}deg)`;
|
||||
sc.style.transform = `rotateZ(${ss}deg)`;
|
||||
|
||||
// gives the smooth transitioning effect, but there's a bug here!
|
||||
// sc.style.transition = `1s`;
|
||||
|
||||
|
||||
})
|
||||
</script>
|
|
@ -0,0 +1,14 @@
|
|||
<div style="background-color: CadetBlue; padding: 20px;">
|
||||
<h3>Page Banner</h3>
|
||||
<p>Isolated mode: not recommended. Read more on the <a href="https://blog.pathtosharepoint.com/2020/10/26/a-temporary-message-on-top-of-your-sharepoint-page/" target="_blank">Path to SharePoint blog</a>.</p>
|
||||
<style type="text/css">
|
||||
body::before {
|
||||
display:block;
|
||||
width:100%;
|
||||
background-color: DarkOrange;
|
||||
font-size: 20px;
|
||||
padding: 10px;
|
||||
content: "Headed for the office? Remember to bring your badge!";
|
||||
}
|
||||
</style>
|
||||
</div>
|
|
@ -0,0 +1,187 @@
|
|||
<div style="background-color: CadetBlue; padding: 20px;">
|
||||
<h3>Flip Styled Countdown</h3>
|
||||
<p>Isolated mode: recommended. Source: FlipDown on <a href="https://github.com/PButcher/flipdown" target="_blank">Github</a>.</p>
|
||||
<div class="example">
|
||||
<p>⏰ FlipDown.js, a lightweight flip styled countdown clock</p>
|
||||
<div id="flipdown" class="flipdown"></div>
|
||||
</div>
|
||||
<link href="https://pbutcher.uk/flipdown/css/flipdown/flipdown.css" rel="stylesheet">
|
||||
<style>
|
||||
html {
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
body {
|
||||
margin: 0px;
|
||||
height: 100%;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
align-content: space-around;
|
||||
}
|
||||
|
||||
body,
|
||||
.example h1,
|
||||
.example p,
|
||||
.example .button {
|
||||
transition: all .2s ease-in-out;
|
||||
}
|
||||
|
||||
body.light-theme {
|
||||
background-color: #151515;
|
||||
}
|
||||
|
||||
body.light-theme .example h1 {
|
||||
color: #FFFFFF;
|
||||
}
|
||||
|
||||
body.light-theme .example p {
|
||||
color: #FFFFFF;
|
||||
}
|
||||
|
||||
body.light-theme .buttons .button {
|
||||
color: #FFFFFF;
|
||||
border-color: #FFFFFF;
|
||||
}
|
||||
|
||||
body.light-theme .buttons .button:hover {
|
||||
color: #151515;
|
||||
background-color: #FFFFFF;
|
||||
}
|
||||
|
||||
.example {
|
||||
font-family: 'Roboto', sans-serif;
|
||||
width: 550px;
|
||||
height: 378px;
|
||||
margin: auto;
|
||||
padding: 20px;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.example .flipdown {
|
||||
margin: auto;
|
||||
}
|
||||
|
||||
.example h1 {
|
||||
text-align: center;
|
||||
font-weight: 100;
|
||||
font-size: 3em;
|
||||
margin-top: 0;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.example p {
|
||||
text-align: center;
|
||||
font-weight: 100;
|
||||
margin-top: 0;
|
||||
margin-bottom: 35px;
|
||||
}
|
||||
|
||||
.example .buttons {
|
||||
width: 100%;
|
||||
height: 50px;
|
||||
margin: 50px auto 0px auto;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-around;
|
||||
}
|
||||
|
||||
.example .buttons p {
|
||||
height: 50px;
|
||||
line-height: 50px;
|
||||
font-weight: 400;
|
||||
padding: 0px 25px 0px 0px;
|
||||
color: #333;
|
||||
margin: 0px;
|
||||
}
|
||||
|
||||
.example .button {
|
||||
display: inline-block;
|
||||
height: 50px;
|
||||
box-sizing: border-box;
|
||||
line-height: 46px;
|
||||
text-decoration: none;
|
||||
color: #333;
|
||||
padding: 0px 20px;
|
||||
border: solid 2px #333;
|
||||
border-radius: 4px;
|
||||
text-transform: uppercase;
|
||||
font-weight: 700;
|
||||
transition: all .2s ease-in-out;
|
||||
}
|
||||
|
||||
.example .button:hover {
|
||||
background-color: #333;
|
||||
color: #FFF;
|
||||
}
|
||||
|
||||
.example .button i {
|
||||
margin-right: 5px;
|
||||
}
|
||||
|
||||
@media(max-width: 550px) {
|
||||
.example {
|
||||
width: 100%;
|
||||
height: 362px;
|
||||
}
|
||||
|
||||
.example h1 {
|
||||
font-size: 2.5em;
|
||||
}
|
||||
|
||||
.example p {
|
||||
margin-bottom: 25px;
|
||||
}
|
||||
|
||||
.example .buttons {
|
||||
width: 100%;
|
||||
margin-top: 25px;
|
||||
text-align: center;
|
||||
display: block;
|
||||
}
|
||||
|
||||
.example .buttons p,
|
||||
.example .buttons a {
|
||||
float: none;
|
||||
margin: 0 auto;
|
||||
}
|
||||
|
||||
.example .buttons p {
|
||||
padding-right: 0px;
|
||||
}
|
||||
|
||||
.example .buttons a {
|
||||
display: inline-block;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
<script>
|
||||
const runFlipDown = () => {
|
||||
|
||||
// Unix timestamp (in seconds) to count down to
|
||||
var twoDaysFromNow = (new Date().getTime() / 1000) + (86400 * 2) + 1;
|
||||
|
||||
// Set up FlipDown
|
||||
var flipdown = new FlipDown(twoDaysFromNow)
|
||||
|
||||
// Start the countdown
|
||||
.start()
|
||||
|
||||
// Do something when the countdown ends
|
||||
.ifEnded(() => {
|
||||
console.log('The countdown has ended!');
|
||||
});
|
||||
|
||||
// Toggle theme
|
||||
var interval = setInterval(() => {
|
||||
let body = document.body;
|
||||
body.classList.toggle('light-theme');
|
||||
body.querySelector('#flipdown').classList.toggle('flipdown__theme-dark');
|
||||
body.querySelector('#flipdown').classList.toggle('flipdown__theme-light');
|
||||
}, 5000);
|
||||
|
||||
var ver = document.getElementById('ver');
|
||||
ver.innerHTML = flipdown.version;
|
||||
};
|
||||
</script>
|
||||
<script src="https://pbutcher.uk/flipdown/js/flipdown/flipdown.js" onload="runFlipDown()"></script>
|
||||
</div>
|
|
@ -0,0 +1,7 @@
|
|||
<div style="background-color: CadetBlue; padding: 20px;">
|
||||
<h3>HTML Sample</h3>
|
||||
<p>Isolated mode: not recommended. Source: <a href="https://blog.pathtosharepoint.com/2021/07/29/introducing-the-property-pane-portal/" target="_blank">Path to SharePoint blog</a>.</p>
|
||||
<p><i>This got me thinking. Could we in some way avoid the redundancy? And as a bonus, directly hook a regular Reactjs component in here? That’s what I pictured in the diagram below: a generic, reusable frame that could serve as host to any Reactjs control.</i></p>
|
||||
<figure><img style="width:100%;" src="https://pathtosharepoint.files.wordpress.com/2021/07/image-3.png" /></figure>
|
||||
<p><i>It took me a couple days to come up with a workable model (the “Property Pane Portal”), weeks to test it, and then months to start writing about it 😊. In the next episodes, I’ll share some samples of the PPP in action, and I’ll provide more details on the architecture and the code that supports it. The key ingredient is <a href="https://reactjs.org/docs/portals.html">Reactjs Portals</a>, which allow to beam an element to another part of the DOM.</i></p>
|
||||
</div>
|
|
@ -0,0 +1,5 @@
|
|||
<div style="background-color: CadetBlue; padding: 20px;">
|
||||
<h3>Google Maps</h3>
|
||||
<p>Isolated mode: not recommended. Source: Google Maps embed code.</p>
|
||||
<iframe src="https://www.google.com/maps/embed?pb=!1m18!1m12!1m3!1d85972.3820324134!2d-122.17073538560777!3d47.67204903850155!2m3!1f0!2f0!3f0!3m2!1i1024!2i768!4f13.1!3m3!1m2!1s0x54900cad2000ee23%3A0x5e0390eac5d804f2!2sRedmond%2C%20WA!5e0!3m2!1sen!2sus!4v1645677983596!5m2!1sen!2sus" width="600" height="450" style="border:0;" allowfullscreen="" loading="lazy"></iframe>
|
||||
</div>
|
|
@ -0,0 +1,5 @@
|
|||
<div style="background-color: CadetBlue; padding: 20px;">
|
||||
<h3>Embedded YouTube Video</h3>
|
||||
<p>Isolated mode: not required. Source: YouTube embed code.</p>
|
||||
<iframe width="560" height="315" src="https://www.youtube.com/embed/ozhbLz1gMi0" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe>
|
||||
</div>
|
|
@ -0,0 +1 @@
|
|||
// A file is required to be in the root of the /src directory by the TypeScript compiler
|
|
@ -0,0 +1,32 @@
|
|||
{
|
||||
"$schema": "https://developer.microsoft.com/json-schemas/spfx/client-side-web-part-manifest.schema.json",
|
||||
"id": "3e86ecba-18ac-44e2-862e-fc268a929584",
|
||||
"alias": "CherryPickedContentWebPart",
|
||||
"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 approved.
|
||||
// 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": "Cherry-Picked-Content" },
|
||||
"description": { "default": "Cherry-Picked-Content description" },
|
||||
"officeFabricIconFontName": "BullseyeTargetEdit",
|
||||
"properties": {
|
||||
"description": "Cherry-Picked-Content",
|
||||
"isolated": true,
|
||||
"iframeWidth": "100%",
|
||||
"iframeHeight": "600px"
|
||||
|
||||
}
|
||||
}]
|
||||
}
|
|
@ -0,0 +1,207 @@
|
|||
import * as React from 'react';
|
||||
import * as ReactDom from 'react-dom';
|
||||
import { Version } from '@microsoft/sp-core-library';
|
||||
import {
|
||||
IPropertyPaneConfiguration,
|
||||
IPropertyPaneDropdownOption,
|
||||
PropertyPaneCheckbox,
|
||||
PropertyPaneDropdown,
|
||||
PropertyPaneTextField
|
||||
} from '@microsoft/sp-property-pane';
|
||||
import { BaseClientSideWebPart } from '@microsoft/sp-webpart-base';
|
||||
import { IReadonlyTheme } from '@microsoft/sp-component-base';
|
||||
|
||||
import * as strings from 'CherryPickedContentWebPartStrings';
|
||||
import CherryPickedContent from './components/CherryPickedContent';
|
||||
import { ICherryPickedContentProps } from './components/ICherryPickedContentProps';
|
||||
|
||||
import { SPHttpClient, SPHttpClientResponse } from '@microsoft/sp-http';
|
||||
import { approvedLibraries } from './components/ApprovedLibraries';
|
||||
|
||||
export interface ICherryPickedContentWebPartProps {
|
||||
isolated: boolean;
|
||||
iframeWidth: string;
|
||||
iframeHeight: string;
|
||||
description: string;
|
||||
libraryPicker: string;
|
||||
libraryItemPicker: string;
|
||||
}
|
||||
|
||||
export default class CherryPickedContentWebPart extends BaseClientSideWebPart<ICherryPickedContentWebPartProps> {
|
||||
|
||||
private _isDarkTheme: boolean = false;
|
||||
private _environmentMessage: string = '';
|
||||
|
||||
protected onInit(): Promise<void> {
|
||||
this._environmentMessage = this._getEnvironmentMessage();
|
||||
|
||||
return super.onInit();
|
||||
}
|
||||
|
||||
public render(): void {
|
||||
const element: React.ReactElement<ICherryPickedContentProps> = React.createElement(
|
||||
CherryPickedContent,
|
||||
{
|
||||
description: this.properties.description,
|
||||
libraryPicker: this.properties.libraryPicker,
|
||||
libraryItemPicker: this.properties.libraryItemPicker,
|
||||
approvedLibraries: this.approvedLibraries,
|
||||
isolated: this.properties.isolated,
|
||||
width: this.properties.iframeWidth,
|
||||
height: this.properties.iframeHeight,
|
||||
context: this.context,
|
||||
isDarkTheme: this._isDarkTheme,
|
||||
environmentMessage: this._environmentMessage,
|
||||
hasTeamsContext: !!this.context.sdks.microsoftTeams,
|
||||
userDisplayName: this.context.pageContext.user.displayName
|
||||
}
|
||||
);
|
||||
|
||||
ReactDom.render(element, this.domElement);
|
||||
}
|
||||
|
||||
private _getEnvironmentMessage(): string {
|
||||
if (!!this.context.sdks.microsoftTeams) { // running in Teams
|
||||
return this.context.isServedFromLocalhost ? strings.AppLocalEnvironmentTeams : strings.AppTeamsTabEnvironment;
|
||||
}
|
||||
|
||||
return this.context.isServedFromLocalhost ? strings.AppLocalEnvironmentSharePoint : strings.AppSharePointEnvironment;
|
||||
}
|
||||
|
||||
protected onThemeChanged(currentTheme: IReadonlyTheme | undefined): void {
|
||||
if (!currentTheme) {
|
||||
return;
|
||||
}
|
||||
|
||||
this._isDarkTheme = !!currentTheme.isInverted;
|
||||
const {
|
||||
semanticColors
|
||||
} = currentTheme;
|
||||
this.domElement.style.setProperty('--bodyText', semanticColors.bodyText);
|
||||
this.domElement.style.setProperty('--link', semanticColors.link);
|
||||
this.domElement.style.setProperty('--linkHovered', semanticColors.linkHovered);
|
||||
}
|
||||
|
||||
protected onDispose(): void {
|
||||
ReactDom.unmountComponentAtNode(this.domElement);
|
||||
}
|
||||
|
||||
protected get dataVersion(): Version {
|
||||
return Version.parse('1.0');
|
||||
}
|
||||
|
||||
// Only content from the approved libraries can be selected
|
||||
private approvedLibraries = approvedLibraries;
|
||||
|
||||
// Dropdown gets disabled while retrieving items asynchronously
|
||||
private itemsDropdownDisabled: boolean = true;
|
||||
|
||||
// Files in the selected library
|
||||
private libraryItemsList: IPropertyPaneDropdownOption[];
|
||||
|
||||
// Asynchronous library query
|
||||
private getLibraryItemsList = (library) => {
|
||||
// Validate approved location
|
||||
const filesLocation = this.approvedLibraries.filter(loc => loc.key == library)[0];
|
||||
const filesQuery = window.location.origin + filesLocation.siteRelativeURL + "/_api/web/lists/getbytitle('" + filesLocation.library + "')/files?$select=Name";
|
||||
|
||||
return this.context.spHttpClient.get(filesQuery, SPHttpClient.configurations.v1)
|
||||
.then((response: SPHttpClientResponse) => response.json())
|
||||
.then(data => data.value);
|
||||
}
|
||||
|
||||
// Runs before getting the Property Pane configuration
|
||||
protected onPropertyPaneConfigurationStart(): void {
|
||||
|
||||
this.itemsDropdownDisabled = true;
|
||||
|
||||
if (this.properties.libraryPicker)
|
||||
this.getLibraryItemsList(this.properties.libraryPicker)
|
||||
.then((files): void => {
|
||||
// store items
|
||||
this.libraryItemsList = files.map(file => file.Name).sort().map(name => { return { key: name, text: name }; });
|
||||
this.itemsDropdownDisabled = false;
|
||||
})
|
||||
.then(() => this.context.propertyPane.refresh());
|
||||
}
|
||||
|
||||
// This API is invoked after updating the new value of the property in the property bag (Reactive mode).
|
||||
protected onPropertyPaneFieldChanged(propertyPath: string, oldValue: any, newValue: any): void {
|
||||
super.onPropertyPaneFieldChanged(propertyPath, oldValue, newValue);
|
||||
if ((propertyPath === 'libraryPicker') && (newValue)) {
|
||||
// get previously selected item
|
||||
const previousItem: string = this.properties.libraryItemPicker;
|
||||
// reset selected item
|
||||
this.properties.libraryItemPicker = "";
|
||||
// disable item selector until new items are loaded
|
||||
this.itemsDropdownDisabled = true;
|
||||
// push new item value
|
||||
this.onPropertyPaneFieldChanged('libraryItemPicker', previousItem, this.properties.libraryItemPicker);
|
||||
// refresh the item selector control by repainting the property pane
|
||||
this.context.propertyPane.refresh();
|
||||
|
||||
this.getLibraryItemsList(newValue)
|
||||
.then((files): void => {
|
||||
if (files.length) {
|
||||
// store items
|
||||
this.libraryItemsList = files.map(file => { return { key: file.Name, text: file.Name }; });
|
||||
// enable item selector
|
||||
this.itemsDropdownDisabled = false;
|
||||
// refresh the item selector control by repainting the property pane
|
||||
this.context.propertyPane.refresh();
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
protected getPropertyPaneConfiguration(): IPropertyPaneConfiguration {
|
||||
return {
|
||||
pages: [
|
||||
{
|
||||
header: {
|
||||
description: strings.PropertyPaneDescription
|
||||
},
|
||||
groups: [
|
||||
{
|
||||
groupName: strings.BasicGroupName,
|
||||
groupFields: [
|
||||
// Web Part title
|
||||
PropertyPaneTextField('description', {
|
||||
label: strings.DescriptionFieldLabel
|
||||
}),
|
||||
// Library Picker (approved libraries only)
|
||||
PropertyPaneDropdown('libraryPicker', {
|
||||
label: strings.LibraryPickerLabel,
|
||||
options: this.approvedLibraries,
|
||||
selectedKey: this.properties.libraryPicker
|
||||
}),
|
||||
// Cascading Library Item Picker
|
||||
PropertyPaneDropdown('libraryItemPicker', {
|
||||
label: strings.LibraryItemPickerLabel,
|
||||
options: this.libraryItemsList,
|
||||
selectedKey: this.properties.libraryItemPicker,
|
||||
disabled: this.itemsDropdownDisabled
|
||||
})
|
||||
]
|
||||
},
|
||||
{
|
||||
groupName: strings.IsolatedMode,
|
||||
groupFields: [
|
||||
// Isolated options
|
||||
PropertyPaneCheckbox('isolated', {
|
||||
text: strings.Isolated,
|
||||
}),
|
||||
this.properties.isolated && PropertyPaneTextField('iframeWidth', {
|
||||
label: strings.IframeWidth
|
||||
}),
|
||||
this.properties.isolated && PropertyPaneTextField('iframeHeight', {
|
||||
label: strings.IframeHeight
|
||||
}),
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
};
|
||||
}
|
||||
}
|
Binary file not shown.
After Width: | Height: | Size: 12 KiB |
Binary file not shown.
After Width: | Height: | Size: 12 KiB |
|
@ -0,0 +1,20 @@
|
|||
export const approvedLibraries = [
|
||||
{
|
||||
key: "/sites/PublicCDN/SiteAssets",
|
||||
siteRelativeURL: "/sites/PublicCDN",
|
||||
library: "Site Assets",
|
||||
text: "Public CDN Site Assets"
|
||||
},
|
||||
{
|
||||
key: "/sites/PublicCDN/Shared%20Documents",
|
||||
siteRelativeURL: "/sites/PublicCDN",
|
||||
library: "Documents",
|
||||
text: "Public CDN Documents"
|
||||
},
|
||||
{
|
||||
key: "/sites/PrivateCDN/SiteAssets",
|
||||
siteRelativeURL: "/sites/PrivateCDN",
|
||||
library: "Site Assets",
|
||||
text: "Private CDN Site Assets"
|
||||
}
|
||||
];
|
|
@ -0,0 +1,34 @@
|
|||
@import '~office-ui-fabric-react/dist/sass/References.scss';
|
||||
|
||||
.cherryPickedContent {
|
||||
overflow: hidden;
|
||||
padding: 1em;
|
||||
color: "[theme:bodyText, default: #323130]";
|
||||
color: var(--bodyText);
|
||||
&.teams {
|
||||
font-family: $ms-font-family-fallbacks;
|
||||
}
|
||||
}
|
||||
|
||||
.welcome {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.welcomeImage {
|
||||
width: 100%;
|
||||
max-width: 420px;
|
||||
}
|
||||
|
||||
.links {
|
||||
a {
|
||||
text-decoration: none;
|
||||
color: "[theme:link, default:#03787c]";
|
||||
color: var(--link); // note: CSS Custom Properties support is limited to modern browsers only
|
||||
|
||||
&:hover {
|
||||
text-decoration: underline;
|
||||
color: "[theme:linkHovered, default: #014446]";
|
||||
color: var(--linkHovered); // note: CSS Custom Properties support is limited to modern browsers only
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,84 @@
|
|||
import * as React from 'react';
|
||||
import styles from './CherryPickedContent.module.scss';
|
||||
import { ICherryPickedContentProps } from './ICherryPickedContentProps';
|
||||
import { escape } from '@microsoft/sp-lodash-subset';
|
||||
|
||||
import { SPHttpClient, SPHttpClientResponse } from '@microsoft/sp-http';
|
||||
import PortalIframe from './PortalIframe';
|
||||
|
||||
const CherryPickedDiv = ({ htmlFragment }) =>
|
||||
<div ref={ref => { if (ref) { ref.innerHTML = ""; ref.appendChild(document.createRange().createContextualFragment(htmlFragment)); } }}>
|
||||
</div>;
|
||||
|
||||
const MemoDiv = React.memo(CherryPickedDiv);
|
||||
|
||||
const CherryPickedContent: React.FunctionComponent<ICherryPickedContentProps> = (props) => {
|
||||
|
||||
const message = "Loading...";
|
||||
const [htmlFragment, setHtmlFragment] = React.useState(message);
|
||||
|
||||
// Get the file content
|
||||
React.useEffect(() => {
|
||||
async function fetchSnippet() {
|
||||
|
||||
// Validate that the library is in the approved list
|
||||
let filteredApprovedLibraries = props.approvedLibraries.filter(lib => lib.key == props.libraryPicker);
|
||||
if ((filteredApprovedLibraries.length > 0) && (props.libraryItemPicker)) {
|
||||
|
||||
let fileURL = props.libraryPicker + "/" + props.libraryItemPicker;
|
||||
|
||||
const webURLQuery = props.context.pageContext.web.absoluteUrl + `/_api/sp.web.getweburlfrompageurl(@v)?@v=%27${window.location.origin}${fileURL}%27`;
|
||||
|
||||
let webURL = await props.context.spHttpClient.get(webURLQuery, SPHttpClient.configurations.v1)
|
||||
.then((response: SPHttpClientResponse) => response.json())
|
||||
.then(data => data.value);
|
||||
const snippetURLQuery = webURL + `/_api/web/getFileByServerRelativeUrl('${fileURL}')/$value`;
|
||||
|
||||
const fragment = await props.context.spHttpClient.get(snippetURLQuery, SPHttpClient.configurations.v1)
|
||||
.then((response: SPHttpClientResponse) => response.text());
|
||||
setHtmlFragment(fragment);
|
||||
}
|
||||
else {
|
||||
setHtmlFragment(message);
|
||||
}
|
||||
}
|
||||
fetchSnippet();
|
||||
}, [props.libraryItemPicker]);
|
||||
|
||||
if (!props.libraryItemPicker) {
|
||||
return (
|
||||
<section className={`${styles.cherryPickedContent} ${props.hasTeamsContext ? styles.teams : ''}`}>
|
||||
<div style={{ display: "flex" }}>
|
||||
<div style={{ flex: "50%" }}>
|
||||
<h3>Edit Web Part properties to select a file.</h3>
|
||||
<h3>Approved libraries:</h3>
|
||||
<p>
|
||||
<ul>
|
||||
{props.approvedLibraries.map(lib => <li>{lib.text}</li>)}
|
||||
</ul>
|
||||
</p>
|
||||
</div>
|
||||
<div style={{ flex: "50%" }} className={styles.welcome}>
|
||||
<img alt="" src={props.isDarkTheme ? require('../assets/welcome-dark.png') : require('../assets/welcome-light.png')} className={styles.welcomeImage} />
|
||||
<h2>Welcome, {escape(props.userDisplayName)}!</h2>
|
||||
<div>{props.environmentMessage}</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
);
|
||||
}
|
||||
else if (props.isolated) {
|
||||
return (
|
||||
<PortalIframe {...props}>
|
||||
<MemoDiv htmlFragment={htmlFragment} />
|
||||
</PortalIframe>
|
||||
);
|
||||
}
|
||||
else {
|
||||
return (
|
||||
<MemoDiv htmlFragment={htmlFragment} />
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
export default CherryPickedContent;
|
|
@ -0,0 +1,16 @@
|
|||
import { WebPartContext } from "@microsoft/sp-webpart-base";
|
||||
|
||||
export interface ICherryPickedContentProps {
|
||||
description: string;
|
||||
libraryPicker: string;
|
||||
libraryItemPicker: string;
|
||||
approvedLibraries: any[];
|
||||
isolated: boolean;
|
||||
width: string;
|
||||
height: string;
|
||||
context: WebPartContext;
|
||||
isDarkTheme: boolean;
|
||||
environmentMessage: string;
|
||||
hasTeamsContext: boolean;
|
||||
userDisplayName: string;
|
||||
}
|
|
@ -0,0 +1,23 @@
|
|||
import * as React from 'react';
|
||||
import { createPortal } from 'react-dom';
|
||||
|
||||
const PortalIframe = ({
|
||||
children,
|
||||
...props
|
||||
}) => {
|
||||
const [contentRef, setContentRef] = React.useState(null);
|
||||
|
||||
const mountWindow = contentRef?.contentWindow;
|
||||
const mountNode = contentRef?.contentWindow?.document?.body;
|
||||
|
||||
// Pass the props to the child iframe
|
||||
if (mountWindow) mountWindow.props = props;
|
||||
|
||||
return (
|
||||
<iframe width={props.width} height={props.height} style={{border:0}} ref={setContentRef}>
|
||||
{mountNode && createPortal(children, mountNode)}
|
||||
</iframe>
|
||||
);
|
||||
};
|
||||
|
||||
export default PortalIframe;
|
17
samples/react-cherry-picked-content/src/webparts/cherryPickedContent/loc/en-us.js
vendored
Normal file
17
samples/react-cherry-picked-content/src/webparts/cherryPickedContent/loc/en-us.js
vendored
Normal file
|
@ -0,0 +1,17 @@
|
|||
define([], function() {
|
||||
return {
|
||||
"PropertyPaneDescription": "Modern Content Editor Web Part with a twist: content can only be picked from approved locations.",
|
||||
"BasicGroupName": "Web Part Properties",
|
||||
"IsolatedMode": "Keep content isolated to prevent conflicts with other Web Parts.",
|
||||
"DescriptionFieldLabel": "Title",
|
||||
"LibraryPickerLabel": "Pick an approved library",
|
||||
"LibraryItemPickerLabel": "Pick a file",
|
||||
"Isolated": "Isolated Content",
|
||||
"IframeWidth": "Width",
|
||||
"IframeHeight": "Height",
|
||||
"AppLocalEnvironmentSharePoint": "The app is running on your local environment as SharePoint web part",
|
||||
"AppLocalEnvironmentTeams": "The app is running on your local environment as Microsoft Teams app",
|
||||
"AppSharePointEnvironment": "The app is running on SharePoint page",
|
||||
"AppTeamsTabEnvironment": "The app is running in Microsoft Teams"
|
||||
}
|
||||
});
|
20
samples/react-cherry-picked-content/src/webparts/cherryPickedContent/loc/mystrings.d.ts
vendored
Normal file
20
samples/react-cherry-picked-content/src/webparts/cherryPickedContent/loc/mystrings.d.ts
vendored
Normal file
|
@ -0,0 +1,20 @@
|
|||
declare interface ICherryPickedContentWebPartStrings {
|
||||
IsolatedMode: string;
|
||||
IframeHeight: string;
|
||||
IframeWidth: string;
|
||||
Isolated: string;
|
||||
PropertyPaneDescription: string;
|
||||
BasicGroupName: string;
|
||||
DescriptionFieldLabel: string;
|
||||
LibraryPickerLabel: string;
|
||||
LibraryItemPickerLabel: string;
|
||||
AppLocalEnvironmentSharePoint: string;
|
||||
AppLocalEnvironmentTeams: string;
|
||||
AppSharePointEnvironment: string;
|
||||
AppTeamsTabEnvironment: string;
|
||||
}
|
||||
|
||||
declare module 'CherryPickedContentWebPartStrings' {
|
||||
const strings: ICherryPickedContentWebPartStrings;
|
||||
export = strings;
|
||||
}
|
Binary file not shown.
After Width: | Height: | Size: 10 KiB |
Binary file not shown.
After Width: | Height: | Size: 542 B |
|
@ -0,0 +1,35 @@
|
|||
{
|
||||
"extends": "./node_modules/@microsoft/rush-stack-compiler-3.9/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": [
|
||||
"webpack-env"
|
||||
],
|
||||
"lib": [
|
||||
"es5",
|
||||
"dom",
|
||||
"es2015.collection",
|
||||
"es2015.promise"
|
||||
]
|
||||
},
|
||||
"include": [
|
||||
"src/**/*.ts",
|
||||
"src/**/*.tsx"
|
||||
]
|
||||
}
|
|
@ -0,0 +1,29 @@
|
|||
{
|
||||
"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
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue