From 6020d008218214fffc86d99c3a9572ff1103d792 Mon Sep 17 00:00:00 2001 From: Jeremy Coleman Date: Mon, 13 Mar 2017 03:43:31 -0700 Subject: [PATCH] New Sample Added, Modern Calendar (#140) * Initial Commit * Update README.md * Updated README --- samples/js-modern-calendar/LICENSE | 21 + samples/js-modern-calendar/README.md | 56 +++ samples/js-modern-calendar/config/config.json | 14 + .../config/copy-assets.json | 3 + .../config/deploy-azure-storage.json | 6 + .../config/package-solution.json | 10 + samples/js-modern-calendar/config/serve.json | 9 + samples/js-modern-calendar/config/tslint.json | 46 +++ .../config/write-manifests.json | 3 + samples/js-modern-calendar/gulpfile.js | 6 + samples/js-modern-calendar/package.json | 31 ++ .../CalendarTemplate.module.scss | 52 +++ .../modernCalendar/CalendarTemplate.ts | 82 ++++ .../IModernCalendarWebPartProps.ts | 12 + .../modernCalendar/ModernCalendar.module.scss | 60 +++ .../ModernCalendarWebPart.manifest.json | 21 + .../modernCalendar/ModernCalendarWebPart.ts | 376 ++++++++++++++++++ .../src/webparts/modernCalendar/loc/en-us.js | 7 + .../modernCalendar/loc/mystrings.d.ts | 10 + .../tests/ModernCalendar.test.ts | 9 + samples/js-modern-calendar/tsconfig.json | 15 + .../js-modern-calendar/typings/@ms/odsp.d.ts | 8 + samples/js-modern-calendar/typings/tsd.d.ts | 1 + 23 files changed, 858 insertions(+) create mode 100644 samples/js-modern-calendar/LICENSE create mode 100644 samples/js-modern-calendar/README.md create mode 100644 samples/js-modern-calendar/config/config.json create mode 100644 samples/js-modern-calendar/config/copy-assets.json create mode 100644 samples/js-modern-calendar/config/deploy-azure-storage.json create mode 100644 samples/js-modern-calendar/config/package-solution.json create mode 100644 samples/js-modern-calendar/config/serve.json create mode 100644 samples/js-modern-calendar/config/tslint.json create mode 100644 samples/js-modern-calendar/config/write-manifests.json create mode 100644 samples/js-modern-calendar/gulpfile.js create mode 100644 samples/js-modern-calendar/package.json create mode 100644 samples/js-modern-calendar/src/webparts/modernCalendar/CalendarTemplate.module.scss create mode 100644 samples/js-modern-calendar/src/webparts/modernCalendar/CalendarTemplate.ts create mode 100644 samples/js-modern-calendar/src/webparts/modernCalendar/IModernCalendarWebPartProps.ts create mode 100644 samples/js-modern-calendar/src/webparts/modernCalendar/ModernCalendar.module.scss create mode 100644 samples/js-modern-calendar/src/webparts/modernCalendar/ModernCalendarWebPart.manifest.json create mode 100644 samples/js-modern-calendar/src/webparts/modernCalendar/ModernCalendarWebPart.ts create mode 100644 samples/js-modern-calendar/src/webparts/modernCalendar/loc/en-us.js create mode 100644 samples/js-modern-calendar/src/webparts/modernCalendar/loc/mystrings.d.ts create mode 100644 samples/js-modern-calendar/src/webparts/modernCalendar/tests/ModernCalendar.test.ts create mode 100644 samples/js-modern-calendar/tsconfig.json create mode 100644 samples/js-modern-calendar/typings/@ms/odsp.d.ts create mode 100644 samples/js-modern-calendar/typings/tsd.d.ts diff --git a/samples/js-modern-calendar/LICENSE b/samples/js-modern-calendar/LICENSE new file mode 100644 index 000000000..5991938b4 --- /dev/null +++ b/samples/js-modern-calendar/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2017 jcoleman-pcprofessional + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/samples/js-modern-calendar/README.md b/samples/js-modern-calendar/README.md new file mode 100644 index 000000000..89f63a4ce --- /dev/null +++ b/samples/js-modern-calendar/README.md @@ -0,0 +1,56 @@ +# Modern Calendar + +## Summary +This is a modern webpart built on the GA version of the [SharePoint Framework](https://dev.office.com/sharepoint/docs/spfx/sharepoint-framework-overview). The ready to deploy SharePoint Add-in package is available under the sharepoint/solution folder. + +![SS1](https://cloud.githubusercontent.com/assets/13068139/23584809/14c4333e-0121-11e7-9bf1-3117651222d3.png) +![SS2](https://cloud.githubusercontent.com/assets/13068139/23584808/14c3ec26-0121-11e7-8be8-65fbcca32b62.png) +![SS3](https://cloud.githubusercontent.com/assets/13068139/23584807/14b88f34-0121-11e7-8c91-56ecff9343e1.png) + +## Used SharePoint Framework Version +![drop](https://img.shields.io/badge/version-GA-green.svg) + +## Applies to + +* [SharePoint Framework](https://blogs.office.com/2017/02/23/sharepoint-framework-reaches-general-availability-build-and-deploy-engaging-web-parts-today/) +* [Office 365 tenant](https://dev.office.com/sharepoint/docs/spfx/set-up-your-development-environment) + +> Update accordingly as needed. + +## Prerequisites + +> Any special pre-requisites? + +## Solution + +Solution|Author(s) +--------|--------- +folder name | Author details + +## Version history + +Version|Date|Comments +-------|----|-------- +1.1|September 2, 2025|Update comment +1.0|August 29, 2025|Initial release + +## Disclaimer +**THIS CODE IS PROVIDED *AS IS* WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING ANY IMPLIED WARRANTIES OF FITNESS FOR A PARTICULAR PURPOSE, MERCHANTABILITY, OR NON-INFRINGEMENT.** + +--- + +## Minimal Path to Awesome + +- Clone this repository +- in the command line run: + - `npm install` + - `gulp serve` + +> Include any additional steps as needed. + +## Features +Renders a calendar from any list available on the selected site. Site, List, Start, End, Event Title,Event Details and Calendar Theme are user-definable in the web part properties. + +![](https://telemetry.sharepointpnp.com/sp-dev-fx-webparts/samples/js-modern-calendar) + +", + "container": "document-card-web-part", + "accessKey": "" +} \ No newline at end of file diff --git a/samples/js-modern-calendar/config/package-solution.json b/samples/js-modern-calendar/config/package-solution.json new file mode 100644 index 000000000..0bf6f31db --- /dev/null +++ b/samples/js-modern-calendar/config/package-solution.json @@ -0,0 +1,10 @@ +{ + "solution": { + "name": "spFX Modern Calendar", + "id": "3d593a2f-73f1-486f-9dae-555c6f6b584d", + "version": "1.0.0.1" + }, + "paths": { + "zippedPackage": "solution/modern-calendar.sppkg" + } +} diff --git a/samples/js-modern-calendar/config/serve.json b/samples/js-modern-calendar/config/serve.json new file mode 100644 index 000000000..438d3f74e --- /dev/null +++ b/samples/js-modern-calendar/config/serve.json @@ -0,0 +1,9 @@ +{ + "port": 4321, + "initialPage": "https://localhost:5432/workbench", + "https": true, + "api": { + "port": 5432, + "entryPath": "node_modules/@microsoft/sp-webpart-workbench/lib/api/" + } +} diff --git a/samples/js-modern-calendar/config/tslint.json b/samples/js-modern-calendar/config/tslint.json new file mode 100644 index 000000000..2a152a5df --- /dev/null +++ b/samples/js-modern-calendar/config/tslint.json @@ -0,0 +1,46 @@ +{ + // Display errors as warnings + "displayAsWarning": true, + // The TSLint task may have been configured with several custom lint rules + // before this config file is read (for example lint rules from the tslint-microsoft-contrib + // project). If true, this flag will deactivate any of these rules. + "removeExistingRules": true, + // When true, the TSLint task is configured with some default TSLint "rules.": + "useDefaultConfigAsBase": false, + // Since removeExistingRules=true and useDefaultConfigAsBase=false, there will be no lint rules + // which are active, other than the list of rules below. + "lintConfig": { + // Opt-in to Lint rules which help to eliminate bugs in JavaScript + "rules": { + "class-name": false, + "export-name": false, + "forin": false, + "label-position": false, + "member-access": true, + "no-arg": false, + "no-console": false, + "no-construct": false, + "no-duplicate-case": true, + "no-duplicate-variable": true, + "no-eval": false, + "no-function-expression": true, + "no-internal-module": true, + "no-shadowed-variable": true, + "no-switch-case-fall-through": true, + "no-unnecessary-semicolons": true, + "no-unused-expression": true, + "no-unused-imports": true, + "no-use-before-declare": true, + "no-with-statement": true, + "semicolon": true, + "trailing-comma": false, + "typedef": false, + "typedef-whitespace": false, + "use-named-parameter": true, + "valid-typeof": true, + "variable-name": false, + "whitespace": false, + "prefer-const": true + } + } +} \ No newline at end of file diff --git a/samples/js-modern-calendar/config/write-manifests.json b/samples/js-modern-calendar/config/write-manifests.json new file mode 100644 index 000000000..8c6020586 --- /dev/null +++ b/samples/js-modern-calendar/config/write-manifests.json @@ -0,0 +1,3 @@ +{ + "cdnBasePath": "https://publiccdn.sharepointonline.com/pcpro365.sharepoint.com/163800463de5f9285ad4f6f9cc0a97985b96b057e062d4733849cc0fc90eeffbd6d72bb8/calendar/" +} \ No newline at end of file diff --git a/samples/js-modern-calendar/gulpfile.js b/samples/js-modern-calendar/gulpfile.js new file mode 100644 index 000000000..de5b70ed8 --- /dev/null +++ b/samples/js-modern-calendar/gulpfile.js @@ -0,0 +1,6 @@ +'use strict'; + +const gulp = require('gulp'); +const build = require('@microsoft/sp-build-web'); + +build.initialize(gulp); diff --git a/samples/js-modern-calendar/package.json b/samples/js-modern-calendar/package.json new file mode 100644 index 000000000..633caef6d --- /dev/null +++ b/samples/js-modern-calendar/package.json @@ -0,0 +1,31 @@ +{ + "name": "modern-calendar", + "version": "0.0.1", + "private": true, + "engines": { + "node": ">=0.10.0" + }, + "dependencies": { + "@microsoft/sp-client-base": "~1.0.0", + "@microsoft/sp-core-library": "~1.0.0", + "@microsoft/sp-webpart-base": "~1.0.0", + "@types/fullcalendar": "^2.7.38", + "@types/moment": "^2.13.0", + "@types/webpack-env": ">=1.12.1 <1.14.0", + "fullcalendar": "^3.2.0", + "sweetalert2": "^6.4.2" + }, + "devDependencies": { + "@microsoft/sp-build-web": "~1.0.0", + "@microsoft/sp-module-interfaces": "~1.0.0", + "@microsoft/sp-webpart-workbench": "~1.0.0", + "gulp": "~3.9.1", + "@types/chai": ">=3.4.34 <3.6.0", + "@types/mocha": ">=2.2.33 <2.6.0" + }, + "scripts": { + "build": "gulp bundle", + "clean": "gulp clean", + "test": "gulp test" + } +} diff --git a/samples/js-modern-calendar/src/webparts/modernCalendar/CalendarTemplate.module.scss b/samples/js-modern-calendar/src/webparts/modernCalendar/CalendarTemplate.module.scss new file mode 100644 index 000000000..f243f56df --- /dev/null +++ b/samples/js-modern-calendar/src/webparts/modernCalendar/CalendarTemplate.module.scss @@ -0,0 +1,52 @@ +.EmptyCalendar { + .container { + max-width: 700px; + margin: 0px auto; + box-shadow: 0 2px 4px 0 rgba(0, 0, 0, 0.2), 0 25px 50px 0 rgba(0, 0, 0, 0.1); + } + + .row { + padding: 20px; + } + + .listItem { + max-width: 715px; + margin: 5px auto 5px auto; + box-shadow: 0 0 4px 0 rgba(0, 0, 0, 0.2), 0 25px 50px 0 rgba(0, 0, 0, 0.1); + } + + .button { + // Our button + text-decoration: none; + height: 32px; + + // Primary Button + min-width: 80px; + background-color: #0078d7; + border-color: #0078d7; + color: #ffffff; + + // 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: 14px; + font-weight: 400; + border-width: 0; + text-align: center; + cursor: pointer; + display: inline-block; + padding: 0 16px; + + .label { + font-weight: 600; + font-size: 14px; + height: 32px; + line-height: 32px; + margin: 0 4px; + vertical-align: top; + display: inline-block; + } + } +} \ No newline at end of file diff --git a/samples/js-modern-calendar/src/webparts/modernCalendar/CalendarTemplate.ts b/samples/js-modern-calendar/src/webparts/modernCalendar/CalendarTemplate.ts new file mode 100644 index 000000000..f102c99e9 --- /dev/null +++ b/samples/js-modern-calendar/src/webparts/modernCalendar/CalendarTemplate.ts @@ -0,0 +1,82 @@ +import { + IPropertyPaneDropdownOption +} from '@microsoft/sp-webpart-base'; +import styles from './CalendarTemplate.module.scss'; +import { escape } from '@microsoft/sp-lodash-subset'; + +export interface ISPLists { + value: ISPList[]; +} + +export interface ISPList { + Title: string; + Id: string; +} +export default class CalendarTemplate { + public static templateHtml: string = ` +
+ `; + + public static emptyHtml(title: string): string { + return `
+
+
+
+ ${title} +

Edit this web part to continue

+ + View More Modern Web Parts + +
+
+
+
+ `; + } + + public static themeBase: string = `https://publiccdn.sharepointonline.com/pcpro365.sharepoint.com/163800463de5f9285ad4f6f9cc0a97985b96b057e062d4733849cc0fc90eeffbd6d72bb8/calendar/themes/`; + + public static themeNames: Array = [ + 'default', + 'black-tie', + 'blitzer', + 'cupertino', + 'dark-hive', + 'dot-luv', + 'eggplant', + 'excite-bike', + 'flick', + 'hot-sneaks', + 'humanity', + 'le-frog', + 'mint-choc', + 'overcast', + 'pcpro', + 'pepper-grinder', + 'redmond', + 'smoothness', + 'south-street', + 'start', + 'sunny', + 'swanky-purse', + 'trontastic', + 'ui-darkness', + 'ui-lightness', + 'vader' + ] + + public static theme(): IPropertyPaneDropdownOption[] { + var themes: IPropertyPaneDropdownOption[] = []; + CalendarTemplate.themeNames.forEach(function(name,index) { + themes.push({key: CalendarTemplate.themeBase + name + '/jquery-ui.min.css', text: name.toLocaleUpperCase()}) + }); + return themes; + } + + public static themes: IPropertyPaneDropdownOption[] = [ + { key: CalendarTemplate.themeBase + 'jquery-ui.theme.min.css', text: 'Default' }, + { key: CalendarTemplate.themeBase + 'ui-lightness/jquery-ui.min.css', text: 'Light' }, + { key: '//cdnjs.cloudflare.com/ajax/libs/jqueryui/1.12.1/themes/redmond/jquery-ui.min.css', text: 'Redmond' }, + { key: '//cdnjs.cloudflare.com/ajax/libs/jqueryui/1.12.1/themes/overcast/jquery-ui.min.css', text: 'Overcast' } + ]; +} \ No newline at end of file diff --git a/samples/js-modern-calendar/src/webparts/modernCalendar/IModernCalendarWebPartProps.ts b/samples/js-modern-calendar/src/webparts/modernCalendar/IModernCalendarWebPartProps.ts new file mode 100644 index 000000000..bb1d6d55c --- /dev/null +++ b/samples/js-modern-calendar/src/webparts/modernCalendar/IModernCalendarWebPartProps.ts @@ -0,0 +1,12 @@ +export interface IModernCalendarWebPartProps { + description: string; + site: string; + siteOther: string; + other: boolean; + listTitle: string; + theme: string; + start: string; + end: string; + title: string; + detail: string; +} diff --git a/samples/js-modern-calendar/src/webparts/modernCalendar/ModernCalendar.module.scss b/samples/js-modern-calendar/src/webparts/modernCalendar/ModernCalendar.module.scss new file mode 100644 index 000000000..b5c483a2f --- /dev/null +++ b/samples/js-modern-calendar/src/webparts/modernCalendar/ModernCalendar.module.scss @@ -0,0 +1,60 @@ +.helloWorld { + .container { + max-width: 700px; + margin: 0px auto; + box-shadow: 0 2px 4px 0 rgba(0, 0, 0, 0.2), 0 25px 50px 0 rgba(0, 0, 0, 0.1); + } + + .row { + padding: 20px; + } + + .listItem { + max-width: 715px; + margin: 5px auto 5px auto; + box-shadow: 0 0 4px 0 rgba(0, 0, 0, 0.2), 0 25px 50px 0 rgba(0, 0, 0, 0.1); + } + + .button { + // Our button + text-decoration: none; + height: 32px; + + // Primary Button + min-width: 80px; + background-color: #0078d7; + border-color: #0078d7; + color: #ffffff; + + // 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: 14px; + font-weight: 400; + border-width: 0; + text-align: center; + cursor: pointer; + display: inline-block; + padding: 0 16px; + + .label { + font-weight: 600; + font-size: 14px; + height: 32px; + line-height: 32px; + margin: 0 4px; + vertical-align: top; + display: inline-block; + } + } +} + +input[aria-label='hide-col'] { + display:none; + } + +#spPropertyPaneContainer input[aria-label='hide-col'] { + display:none; + } \ No newline at end of file diff --git a/samples/js-modern-calendar/src/webparts/modernCalendar/ModernCalendarWebPart.manifest.json b/samples/js-modern-calendar/src/webparts/modernCalendar/ModernCalendarWebPart.manifest.json new file mode 100644 index 000000000..0eada18b9 --- /dev/null +++ b/samples/js-modern-calendar/src/webparts/modernCalendar/ModernCalendarWebPart.manifest.json @@ -0,0 +1,21 @@ +{ + "$schema": "../../../node_modules/@microsoft/sp-module-interfaces/lib/manifestSchemas/jsonSchemas/clientSideComponentManifestSchema.json", + + "id": "c2a397d3-8c8f-47ab-b731-897178313c15", + "alias": "ModernCalendarWebPart", + "componentType": "WebPart", + "version": "0.0.1", + "manifestVersion": 2, + + "preconfiguredEntries": [{ + "groupId": "c2a397d3-8c8f-47ab-b731-897178313c15", + "group": { "default": "Modern Web Parts" }, + "title": { "default": "ModernCalendar" }, + "description": { "default": "SFPx Modern Calendar" }, + "officeFabricIconFontName": "Calendar", + "properties": { + "description": "SPFx Modern Calendar", + "other": false + } + }] +} diff --git a/samples/js-modern-calendar/src/webparts/modernCalendar/ModernCalendarWebPart.ts b/samples/js-modern-calendar/src/webparts/modernCalendar/ModernCalendarWebPart.ts new file mode 100644 index 000000000..3f7ed3ac4 --- /dev/null +++ b/samples/js-modern-calendar/src/webparts/modernCalendar/ModernCalendarWebPart.ts @@ -0,0 +1,376 @@ +import { Version } from '@microsoft/sp-core-library'; +import { + BaseClientSideWebPart, + IPropertyPaneConfiguration, + IPropertyPaneDropdownOption, + IPropertyPaneTextFieldProps, + IWebPartContext, + PropertyPaneTextField, + PropertyPaneDropdown, + IPropertyPaneDropdownProps +} from '@microsoft/sp-webpart-base'; +import { escape } from '@microsoft/sp-lodash-subset'; +import styles from './ModernCalendar.module.scss'; +import * as strings from 'modernCalendarStrings'; +import { IModernCalendarWebPartProps } from './IModernCalendarWebPartProps'; +import CalendarTemplate from './CalendarTemplate'; +import * as jQuery from 'jquery'; +import 'fullcalendar'; +import * as moment from 'moment'; +import * as swal2 from 'sweetalert2'; +import { SPComponentLoader } from '@microsoft/sp-loader'; +import { + SPHttpClient +} from '@microsoft/sp-http'; +import { + Environment, + EnvironmentType +} from '@microsoft/sp-core-library'; + +export interface ISPLists { + value: ISPList[]; +} + +export interface ISPList { + Title: string; + Id: string; +} + +export interface EventObjects { + value: FC.EventObject[]; +} + +export default class ModernCalendarWebPart extends BaseClientSideWebPart { + + public constructor() { + super(); + //Modify with your a CDN or local path + SPComponentLoader.loadCss('//publiccdn.sharepointonline.com/pcpro365.sharepoint.com/163800463de5f9285ad4f6f9cc0a97985b96b057e062d4733849cc0fc90eeffbd6d72bb8/calendar/sweetalert2.min.css'); + SPComponentLoader.loadCss('//publiccdn.sharepointonline.com/pcpro365.sharepoint.com/163800463de5f9285ad4f6f9cc0a97985b96b057e062d4733849cc0fc90eeffbd6d72bb8/calendar/fullcalendar.min.css'); + } + + public render(): void { + + if (this.properties.theme != null){ + SPComponentLoader.loadCss(this.properties.theme); + } + + if (!this.properties.other){ + jQuery('input[aria-label=hide-col]').parent().hide(); + } + //Check required properties before rendering list + if (this.properties.listTitle == null || this.properties.start == null || this.properties.end == null || this.properties.title == null || this.properties.detail == null) { + this.domElement.innerHTML = CalendarTemplate.emptyHtml(this.properties.description); + } else { + this.domElement.innerHTML = CalendarTemplate.templateHtml; + this._renderListAsync(); + } + } + + protected get dataVersion(): Version { + return Version.parse('1.0'); + } + + protected onPropertyPaneConfigurationStart(): void { + //Set a default theme + + if (this.properties.theme == null){ + this.properties.theme = CalendarTemplate.theme()[0].key.toString(); + } + if (this.properties.site){ + this.listDisabled = false; + } + if (this.properties.listTitle && (!this.properties.start || !this.properties.end || !this.properties.title || !this.properties.detail)){ + //this._getColumnsAsync(); + } + + if (!this.properties.other){ + jQuery('input[aria-label=hide-col]').parent().hide(); + } + + if (this.properties.site && this.properties.listTitle && this.properties.start && this.properties.start && this.properties.end && this.properties.title && this.properties.detail){ + this.context.statusRenderer.displayLoadingIndicator(this.domElement, 'Configuration'); + this._getSiteRootWeb() + .then((response0) => { + this._getSites(response0['Url']) + .then((response) => { + var sites: IPropertyPaneDropdownOption[] = []; + sites.push({key:this.context.pageContext.web.absoluteUrl, text:'This Site'}); + sites.push({key:'other', text:'Other Site (Specify Url)'}); + for (var _key in response.value) { + if (this.context.pageContext.web.absoluteUrl != response.value[_key]['Url']){ + sites.push({key: response.value[_key]['Url'], text: response.value[_key]['Title']}); + } + } + this._siteOptions = sites; + if (this.properties.site){ + this._getListTitles(this.properties.site) + .then((response2) => { + this._dropdownOptions = response2.value.map((list: ISPList) => { + return { + key: list.Title, + text: list.Title + }; + }); + this._getListColumns(this.properties.listTitle,this.properties.site) + .then((response3) => { + var col: IPropertyPaneDropdownOption[] = []; + for (var _key in response3.value) { + col.push({key: response3.value[_key]['InternalName'], text: response3.value[_key]['Title']}); + } + this._columnOptions = col; + this.colsDisabled = false; + this.listDisabled = false; + this.context.propertyPane.refresh(); + this.context.statusRenderer.clearLoadingIndicator(this.domElement); + this.render(); + }) + }); + } + }) + }) + } else { + this._getSitesAsync(); + } + } + + protected onPropertyPaneFieldChanged(propertyPath: string, oldValue: any, newValue: any): void { + if (newValue == 'other' ){ + this.properties.other = true; + this.properties.listTitle = null; + jQuery('input[aria-label=hide-col]').parent().show(); + } else if (oldValue === 'other' && newValue != 'other') { + this.properties.other = false; + this.properties.siteOther = null; + this.properties.listTitle = null; + jQuery('input[aria-label=hide-col]').parent().hide(); + } + this.context.statusRenderer.displayLoadingIndicator(this.domElement, 'Configuration'); + if ((propertyPath === 'site' || propertyPath === 'siteOther') && newValue) { + this.colsDisabled = true; + this.listDisabled = true; + var siteUrl = newValue; + if (this.properties.other) { siteUrl = this.properties.siteOther; } else { jQuery('input[aria-label=hide-col]').parent().hide(); } + if ((this.properties.other && this.properties.siteOther.length > 25) || !this.properties.other){ + this._getListTitles(siteUrl) + .then((response) => { + this._dropdownOptions = response.value.map((list: ISPList) => { + return { + key: list.Title, + text: list.Title + }; + }); + this.listDisabled = false; + this.context.propertyPane.refresh(); + this.context.statusRenderer.clearLoadingIndicator(this.domElement); + this.render(); + }); + } + } else if (propertyPath === 'listTitle' && newValue) { + var siteUrl = newValue; + if (this.properties.other) { siteUrl = this.properties.siteOther; } + this._getListColumns(newValue,siteUrl) + .then((response) => { + var col: IPropertyPaneDropdownOption[] = []; + for (var _key in response.value) { + col.push({key: response.value[_key]['InternalName'], text: response.value[_key]['Title']}); + } + this._columnOptions = col; + this.colsDisabled = false; + this.context.propertyPane.refresh(); + this.context.statusRenderer.clearLoadingIndicator(this.domElement); + this.render(); + }); + } else { + //Handle other fields here + this.render(); + } + } + + private colsDisabled: boolean = true; + private listDisabled: boolean = true; + + protected getPropertyPaneConfiguration(): IPropertyPaneConfiguration { + var otherSiteAria = 'hide-col'; + if (this.properties.other) { otherSiteAria = ''; } + return { + pages: [ + { + header: { + description: strings.PropertyPaneDescription + }, + groups: [ + { + groupName: strings.BasicGroupName, + groupFields: [ + PropertyPaneTextField('description', { + label: strings.DescriptionFieldLabel + }), + PropertyPaneDropdown('theme', { + label: 'Theme', + options: CalendarTemplate.theme() + }), + PropertyPaneDropdown('site', { + label: 'Site', + options: this._siteOptions + }), + PropertyPaneTextField('siteOther', { + label: 'Other Site Url (i.e. https://contoso.sharepoint.com/path)', + ariaLabel: otherSiteAria + }), + PropertyPaneDropdown('listTitle', { + label: 'List Title', + options: this._dropdownOptions, + disabled: this.listDisabled + }), + PropertyPaneDropdown('start', { + label: 'Start Date Field', + options: this._columnOptions, + disabled: this.colsDisabled + }), + PropertyPaneDropdown('end', { + label: 'End Date Field', + options: this._columnOptions, + disabled: this.colsDisabled + }), + PropertyPaneDropdown('title', { + label: 'Event Title Field', + options: this._columnOptions, + disabled: this.colsDisabled + }), + PropertyPaneDropdown('detail', { + label: 'Event Details', + options: this._columnOptions, + disabled: this.colsDisabled + }) + ] + } + ] + } + ] + }; + } + + private _siteOptions: IPropertyPaneDropdownOption[] = []; + private _dropdownOptions: IPropertyPaneDropdownOption[] = []; + private _columnOptions: IPropertyPaneDropdownOption[] = []; + + public onInit(): Promise { + //this._siteOptions.push({key:this.context.pageContext.web.absoluteUrl, text:'This Site'}); + return Promise.resolve(); + } + + private _getSiteRootWeb(): Promise { + return this.context.spHttpClient.get(this.context.pageContext.web.absoluteUrl + `/_api/Site/RootWeb?$select=Title,Url`, SPHttpClient.configurations.v1) + .then((response: Response) => { + return response.json(); + }); + } + + private _getSites(rootWebUrl: string): Promise { + return this.context.spHttpClient.get(rootWebUrl + `/_api/web/webs?$select=Title,Url`, SPHttpClient.configurations.v1) + .then((response: Response) => { + return response.json(); + }); + } + + private _getListTitles(site: string): Promise { + return this.context.spHttpClient.get(site + `/_api/web/lists?$filter=Hidden eq false and BaseType eq 0`, SPHttpClient.configurations.v1) + .then((response: Response) => { + return response.json(); + }); + } + + private _getListColumns(listNameColumns: string,listsite: string): Promise { + return this.context.spHttpClient.get(listsite + `/_api/web/lists/GetByTitle('${listNameColumns}')/Fields?$filter=Hidden eq false and ReadOnlyField eq false`, SPHttpClient.configurations.v1) + .then((response: Response) => { + return response.json(); + }); + } + + private _getListData(listName: string, site: string): Promise { + return this.context.spHttpClient.get(site + `/_api/web/lists/GetByTitle('${listName}')/items?$select=${encodeURIComponent(this.properties.title)},${encodeURIComponent(this.properties.start)},${encodeURIComponent(this.properties.end)},${encodeURIComponent(this.properties.detail)},Created,Author/ID,Author/Title&$expand=Author/ID,Author/Title&$orderby=Id desc&$limit=500`,SPHttpClient.configurations.v1) + .then((response: Response) => { + return response.json(); + }); + } + + private _renderList(items: any[]): void { + var calItems: FC.EventObject[] = items.map((list: any) => { + return { + title: list[this.properties.title], + start: list[this.properties.start], + end: list[this.properties.end], + id: list['Id'], + detail: list[this.properties.detail] + }; + }); + this.context.statusRenderer.clearLoadingIndicator(this.domElement); + const calendarOptions:FC.Options = { + theme: true, + events: calItems, + eventClick: function(_event) { + var eventDetail = moment(_event['start']).format('MM/DD/YYYY hh:mm') + ' - ' + moment(_event['end']).format('MM/DD/YYYY hh:mm') + '
' + _event['detail']; + swal2.default(_event.title,eventDetail,'info'); + } + }; + jQuery('.spfxcalendar', this.domElement).fullCalendar(calendarOptions); + } + + private _getSitesAsync(): void { + this._getSiteRootWeb() + .then((response) => { + this._getSites(response['Url']) + .then((response1) => { + var sites: IPropertyPaneDropdownOption[] = []; + sites.push({key:this.context.pageContext.web.absoluteUrl, text:'This Site'}); + sites.push({key:'other', text:'Other Site (Specify Url)'}); + for (var _key in response1.value) { + sites.push({key: response1.value[_key]['Url'], text: response1.value[_key]['Title']}); + } + this._siteOptions = sites; + this.context.propertyPane.refresh(); + var siteUrl = this.properties.site; + if (this.properties.other) { siteUrl = this.properties.siteOther; } + this._getListTitles(siteUrl) + .then((response2) => { + this._dropdownOptions = response2.value.map((list: ISPList) => { + return { + key: list.Title, + text: list.Title + }; + }); + this.context.propertyPane.refresh(); + if (this.properties.listTitle) { + this._getListColumns(this.properties.listTitle,this.properties.site) + .then((response3) => { + var col: IPropertyPaneDropdownOption[] = []; + for (var _key in response3.value) { + col.push({key: response3.value[_key]['InternalName'], text: response3.value[_key]['Title']}); + } + this._columnOptions = col; + this.colsDisabled = false; + this.listDisabled = false; + this.context.propertyPane.refresh(); + this.context.statusRenderer.clearLoadingIndicator(this.domElement); + this.render(); + }) + } + }); + }) + }); + } + + + private _renderListAsync(): void { + var siteUrl = this.properties.site; + if (this.properties.other) { siteUrl = this.properties.siteOther; } + this._getListData(this.properties.listTitle, siteUrl).then((response) => { + this._renderList(response.value); + }).catch((err) => { + this.context.statusRenderer.clearLoadingIndicator(this.domElement); + this.context.statusRenderer.renderError(this.domElement,"There was an error loading your list, please verify the selected list has Calendar Events or choose a new list."); + }); + } + +} diff --git a/samples/js-modern-calendar/src/webparts/modernCalendar/loc/en-us.js b/samples/js-modern-calendar/src/webparts/modernCalendar/loc/en-us.js new file mode 100644 index 000000000..e5f5a6025 --- /dev/null +++ b/samples/js-modern-calendar/src/webparts/modernCalendar/loc/en-us.js @@ -0,0 +1,7 @@ +define([], function() { + return { + "PropertyPaneDescription": "Description", + "BasicGroupName": "Group Name", + "DescriptionFieldLabel": "Description Field" + } +}); \ No newline at end of file diff --git a/samples/js-modern-calendar/src/webparts/modernCalendar/loc/mystrings.d.ts b/samples/js-modern-calendar/src/webparts/modernCalendar/loc/mystrings.d.ts new file mode 100644 index 000000000..fb61d6754 --- /dev/null +++ b/samples/js-modern-calendar/src/webparts/modernCalendar/loc/mystrings.d.ts @@ -0,0 +1,10 @@ +declare interface IModernCalendarStrings { + PropertyPaneDescription: string; + BasicGroupName: string; + DescriptionFieldLabel: string; +} + +declare module 'modernCalendarStrings' { + const strings: IModernCalendarStrings; + export = strings; +} diff --git a/samples/js-modern-calendar/src/webparts/modernCalendar/tests/ModernCalendar.test.ts b/samples/js-modern-calendar/src/webparts/modernCalendar/tests/ModernCalendar.test.ts new file mode 100644 index 000000000..c42c8e401 --- /dev/null +++ b/samples/js-modern-calendar/src/webparts/modernCalendar/tests/ModernCalendar.test.ts @@ -0,0 +1,9 @@ +/// + +import { assert } from 'chai'; + +describe('ModernCalendarWebPart', () => { + it('should do something', () => { + assert.ok(true); + }); +}); diff --git a/samples/js-modern-calendar/tsconfig.json b/samples/js-modern-calendar/tsconfig.json new file mode 100644 index 000000000..c0a88ca86 --- /dev/null +++ b/samples/js-modern-calendar/tsconfig.json @@ -0,0 +1,15 @@ +{ + "compilerOptions": { + "target": "es5", + "forceConsistentCasingInFileNames": true, + "module": "commonjs", + "jsx": "react", + "declaration": true, + "sourceMap": true, + "types": [ + "es6-promise", + "es6-collections", + "webpack-env" + ] + } +} diff --git a/samples/js-modern-calendar/typings/@ms/odsp.d.ts b/samples/js-modern-calendar/typings/@ms/odsp.d.ts new file mode 100644 index 000000000..3a4eb25f7 --- /dev/null +++ b/samples/js-modern-calendar/typings/@ms/odsp.d.ts @@ -0,0 +1,8 @@ +// Type definitions for Microsoft ODSP projects +// Project: ODSP + +/* Global definition for UNIT_TEST builds + Code that is wrapped inside an if(UNIT_TEST) {...} + block will not be included in the final bundle when the + --ship flag is specified */ +declare const UNIT_TEST: boolean; \ No newline at end of file diff --git a/samples/js-modern-calendar/typings/tsd.d.ts b/samples/js-modern-calendar/typings/tsd.d.ts new file mode 100644 index 000000000..a248cbd2c --- /dev/null +++ b/samples/js-modern-calendar/typings/tsd.d.ts @@ -0,0 +1 @@ +///