Merge pull request #1791 from mhomol/advanced-page-properties

This commit is contained in:
Hugo Bernier 2021-04-04 23:24:20 -04:00 committed by GitHub
commit 40d3d66324
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
33 changed files with 18687 additions and 0 deletions

View File

@ -0,0 +1,25 @@
# EditorConfig helps developers define and maintain consistent
# coding styles between different editors and IDEs
# editorconfig.org
root = true
[*]
# change these settings to your own preference
indent_style = space
indent_size = 2
# we recommend you to keep these unchanged
end_of_line = lf
charset = utf-8
trim_trailing_whitespace = true
insert_final_newline = true
[*.md]
trim_trailing_whitespace = false
[{package,bower}.json]
indent_style = space
indent_size = 2

View File

@ -0,0 +1,35 @@
# Logs
logs
*.log
npm-debug.log*
# Dependency directories
node_modules
# Build generated files
dist
lib
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
# Talisman
.talismanrc

View File

@ -0,0 +1,12 @@
{
"@microsoft/generator-sharepoint": {
"isCreatingSolution": true,
"environment": "spo",
"version": "1.11.0",
"libraryName": "advanced-page-properties",
"libraryId": "daae06a2-8599-445c-93c0-3bd739305f56",
"packageManager": "npm",
"isDomainIsolated": false,
"componentType": "webpart"
}
}

View File

@ -0,0 +1,79 @@
# Advanced Page Properties
## Summary
Attempts to replicate the functionality of Page Properties with the following improvements:
- Support for theme variants
- Updated to standard capsule look for list options
- Support for image fields
- Support for hyperlink fields
- Support for currency
- Improved support for dates
![see the difference](./assets/diff-screencap.png)
## Compatibility
![SPFx 1.11](https://img.shields.io/badge/SPFx-1.11.0-green.svg)
![Node.js LTS 10.x](https://img.shields.io/badge/Node.js-LTS%2010.x-green.svg)
![SharePoint Online](https://img.shields.io/badge/SharePoint-Online-yellow.svg)
![Teams N/A: Untested with Microsoft Teams](https://img.shields.io/badge/Teams-N%2FA-lightgrey.svg "Untested with Microsoft Teams")
![Workbench Hosted: Does not work with local workbench](https://img.shields.io/badge/Workbench-Hosted-yellow.svg "Does not work with local workbench")
## 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
None
## Solution
Solution|Author(s)
--------|---------
src/react-advanced-page-properties | [Mike Homol](https://homol.work), Principal Consultant, [ThreeWill](https://threewill.com/)
## Version history
Version|Date|Comments
-------|----|--------
1.0|March 30, 2021|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
- Ensure that you are at the solution folder
- in the command-line run:
- **npm install**
- **gulp serve**
> Include any additional steps as needed.
## Features
> Add the part
Once the solution is installed in the app catalog and the app has been added to the site, you should see it available to a page.
![add the part](./assets/add-to-page.png)
> Familiarity is key
This web part tries to mimic the original Page Properties web part as much as possible. You should recognize the functionality of the property setup.
Basically add and remove properties until you are satisfied and you're good to go!
![configure the part](./assets/props.gif)

Binary file not shown.

After

Width:  |  Height:  |  Size: 110 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 822 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 MiB

View File

@ -0,0 +1,75 @@
[
{
"name": "pnp-sp-dev-spfx-web-parts-react-advanced-page-properties",
"source": "pnp",
"title": "Advanced Page Properties",
"shortDescription": "Replicates the functionality of Page Properties with improvements",
"url": "https://github.com/pnp/sp-dev-fx-webparts/tree/master/samples/react-advanced-page-properties",
"longDescription": [
"Attempts to replicate the functionality of Page Properties with the following improvements:",
"- Support for theme variants",
"- Updated to standard capsule look for list options",
"- Support for image fields",
"- Support for hyperlink fields",
"- Support for currency",
"- Improved support for dates"
],
"created": "2021-03-30",
"modified": "2021-03-30",
"products": [
"SharePoint",
"Office"
],
"metadata": [
{
"key": "CLIENT-SIDE-DEV",
"value": "React"
},
{
"key": "SPFX-VERSION",
"value": "1.11.0"
}
],
"thumbnails": [
{
"type": "image",
"order": 100,
"url": "https://github.com/pnp/sp-dev-fx-webparts/raw/master/samples/react-advanced-page-properties/assets/diff-screencap.png",
"alt": "Web part in action"
},
{
"type": "image",
"order": 101,
"url": "https://github.com/pnp/sp-dev-fx-webparts/raw/master/samples/react-advanced-page-properties/assets/props.gif",
"alt": "Web part in action"
},
{
"type": "image",
"order": 102,
"url": "https://github.com/pnp/sp-dev-fx-webparts/raw/master/samples/react-advanced-page-properties/assets/add-to-page.png",
"alt": "Adding to a page"
}
],
"authors": [
{
"gitHubAccount": "mhomol",
"company": "ThreeWill",
"pictureUrl": "https://github.com/mhomol.png",
"name": "Mike Homol",
"twitter": "homol"
}
],
"references": [
{
"name": "Build your first SharePoint client-side web part",
"description": "Client-side web parts are client-side components that run in the context of a SharePoint page. Client-side web parts can be deployed to SharePoint environments that support the SharePoint Framework. You can also use modern JavaScript web frameworks, tools, and libraries to build them.",
"url": "https://docs.microsoft.com/en-us/sharepoint/dev/spfx/web-parts/get-started/build-a-hello-world-web-part"
},
{
"name": "Supporting section backgrounds",
"description": "Starting with SharePoint Framework v1.8, web parts can be made aware of any section backgrounds and use these colors to improve the appearance of a web part when hosted in a section with a different background.",
"url": "https://docs.microsoft.com/en-us/sharepoint/dev/spfx/web-parts/guidance/supporting-section-backgrounds"
}
]
}
]

View File

@ -0,0 +1,19 @@
{
"$schema": "https://developer.microsoft.com/json-schemas/spfx-build/config.2.0.schema.json",
"version": "2.0",
"bundles": {
"advanced-page-properties-web-part": {
"components": [
{
"entrypoint": "./lib/webparts/advancedPageProperties/AdvancedPagePropertiesWebPart.js",
"manifest": "./src/webparts/advancedPageProperties/AdvancedPagePropertiesWebPart.manifest.json"
}
]
}
},
"externals": {},
"localizedResources": {
"AdvancedPagePropertiesWebPartStrings": "lib/webparts/advancedPageProperties/loc/{locale}.js",
"PropertyControlStrings": "node_modules/@pnp/spfx-property-controls/lib/loc/{locale}.js"
}
}

View File

@ -0,0 +1,4 @@
{
"$schema": "https://developer.microsoft.com/json-schemas/spfx-build/copy-assets.schema.json",
"deployCdnPath": "temp/deploy"
}

View File

@ -0,0 +1,7 @@
{
"$schema": "https://developer.microsoft.com/json-schemas/spfx-build/deploy-azure-storage.schema.json",
"workingDir": "./temp/deploy/",
"account": "<!-- STORAGE ACCOUNT NAME -->",
"container": "advanced-page-properties",
"accessKey": "<!-- ACCESS KEY -->"
}

View File

@ -0,0 +1,20 @@
{
"$schema": "https://developer.microsoft.com/json-schemas/spfx-build/package-solution.schema.json",
"solution": {
"name": "Advanced Page Properties",
"id": "daae06a2-8599-445c-93c0-3bd739305f56",
"version": "1.0.0.0",
"includeClientSideAssets": true,
"isDomainIsolated": false,
"developer": {
"name": "Mike Homol",
"websiteUrl": "homol.work",
"privacyUrl": "",
"termsOfUseUrl": "",
"mpnId": "990019737"
}
},
"paths": {
"zippedPackage": "solution/advanced-page-properties.sppkg"
}
}

View File

@ -0,0 +1,10 @@
{
"$schema": "https://developer.microsoft.com/json-schemas/core-build/serve.schema.json",
"port": 4321,
"https": true,
"initialPage": "https://localhost:5432/workbench",
"api": {
"port": 5432,
"entryPath": "node_modules/@microsoft/sp-webpart-workbench/lib/api/"
}
}

View File

@ -0,0 +1,4 @@
{
"$schema": "https://developer.microsoft.com/json-schemas/spfx-build/write-manifests.schema.json",
"cdnBasePath": "<!-- PATH TO CDN -->"
}

View File

@ -0,0 +1,7 @@
'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.`);
build.initialize(require('gulp'));

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,43 @@
{
"name": "advanced-page-properties",
"version": "0.0.1",
"private": true,
"main": "lib/index.js",
"engines": {
"node": ">=0.10.0"
},
"scripts": {
"build": "gulp bundle",
"clean": "gulp clean",
"test": "gulp test"
},
"dependencies": {
"@fluentui/react": "^8.7.0",
"@microsoft/sp-core-library": "1.11.0",
"@microsoft/sp-lodash-subset": "1.11.0",
"@microsoft/sp-office-ui-fabric-core": "1.11.0",
"@microsoft/sp-property-pane": "1.11.0",
"@microsoft/sp-webpart-base": "1.11.0",
"@pnp/sp": "^2.3.0",
"@pnp/spfx-property-controls": "^3.0.0",
"lodash": "^4.17.21",
"office-ui-fabric-react": "6.214.0",
"react": "16.8.5",
"react-dom": "16.8.5"
},
"devDependencies": {
"@types/react": "16.8.8",
"@types/react-dom": "16.8.3",
"@microsoft/sp-build-web": "1.11.0",
"@microsoft/sp-tslint-rules": "1.11.0",
"@microsoft/sp-module-interfaces": "1.11.0",
"@microsoft/sp-webpart-workbench": "1.11.0",
"@microsoft/rush-stack-compiler-3.3": "0.3.5",
"gulp": "~3.9.1",
"@types/chai": "3.4.34",
"@types/mocha": "2.2.38",
"ajv": "~5.2.2",
"@types/webpack-env": "1.13.1",
"@types/es6-promise": "0.0.33"
}
}

View File

@ -0,0 +1 @@
// A file is required to be in the root of the /src directory by the TypeScript compiler

View File

@ -0,0 +1,29 @@
{
"$schema": "https://developer.microsoft.com/json-schemas/spfx/client-side-web-part-manifest.schema.json",
"id": "bdbcb04e-9c90-4adb-b0f0-d025e4eb6cb5",
"alias": "AdvancedPagePropertiesWebPart",
"componentType": "WebPart",
// The "*" signifies that the version should be taken from the package.json
"version": "*",
"manifestVersion": 2,
// If true, the component can only be installed on sites where Custom Script is allowed.
// Components that allow authors to embed arbitrary script code should set this to true.
// https://support.office.com/en-us/article/Turn-scripting-capabilities-on-or-off-1f2c515f-5d7e-448a-9fd7-835da935584f
"requiresCustomScript": false,
"supportedHosts": ["SharePointWebPart"],
"supportsThemeVariants": true,
"preconfiguredEntries": [{
"groupId": "5c03119e-3074-46fd-976b-c60198311f70", // Other
"group": { "default": "Other" },
"title": { "default": "Advanced Page Properties" },
"description": { "default": "A glow-up for the standard Microsoft Page Properties web part" },
"officeFabricIconFontName": "TagSolid",
"properties": {
"title": "Page Properties",
"selectedProperties": []
}
}]
}

View File

@ -0,0 +1,169 @@
import * as React from 'react';
import * as ReactDom from 'react-dom';
import { Version } from '@microsoft/sp-core-library';
import {
IPropertyPaneConfiguration, IPropertyPaneDropdownOption, IPropertyPaneField, PropertyPaneButton, PropertyPaneButtonType, PropertyPaneDropdown, PropertyPaneHorizontalRule, PropertyPaneTextField
} from '@microsoft/sp-property-pane';
import { BaseClientSideWebPart } from '@microsoft/sp-webpart-base';
import * as strings from 'AdvancedPagePropertiesWebPartStrings';
import AdvancedPageProperties from './components/AdvancedPageProperties';
import { IAdvancedPagePropertiesProps } from './components/IAdvancedPagePropertiesProps';
//import { PropertyFieldMultiSelect } from '@pnp/spfx-property-controls/lib/PropertyFieldMultiSelect';
import "@pnp/sp/fields";
import "@pnp/sp/fields/list";
import { FieldTypes, sp } from "@pnp/sp/presets/all";
import { Log } from './utilities/Log';
import * as _ from 'lodash';
import {
ThemeProvider,
ThemeChangedEventArgs,
IReadonlyTheme
} from '@microsoft/sp-component-base';
export interface IAdvancedPagePropertiesWebPartProps {
title: string;
selectedProperties: string[];
}
export default class AdvancedPagePropertiesWebPart extends BaseClientSideWebPart<IAdvancedPagePropertiesWebPartProps> {
private availableProperties: IPropertyPaneDropdownOption[] = [];
private _themeProvider: ThemeProvider;
private _themeVariant: IReadonlyTheme | undefined;
private _handleThemeChangedEvent(args: ThemeChangedEventArgs): void {
this._themeVariant = args.theme;
this.render();
}
protected async onInit(): Promise<void> {
sp.setup({ spfxContext: this.context });
// Consume the new ThemeProvider service
this._themeProvider = this.context.serviceScope.consume(ThemeProvider.serviceKey);
// If it exists, get the theme variant
this._themeVariant = this._themeProvider.tryGetTheme();
// Register a handler to be notified if the theme variant changes
this._themeProvider.themeChangedEvent.add(this, this._handleThemeChangedEvent);
super.onInit();
}
public render(): void {
const element: React.ReactElement<IAdvancedPagePropertiesProps> = React.createElement(
AdvancedPageProperties,
{
context: this.context,
title: this.properties.title,
selectedProperties: this.properties.selectedProperties,
themeVariant: this._themeVariant
}
);
ReactDom.render(element, this.domElement);
}
private async getPageProperties(): Promise<void> {
Log.Write("Getting Site Page fields...");
const list = sp.web.lists.getByTitle("Site Pages");
const fi = await list.fields();
this.availableProperties = [];
Log.Write(`${fi.length.toString()} fields retrieved!`);
fi.forEach((f) => {
if (!f.FromBaseType && !f.Hidden && !f.Sealed && f.SchemaXml.indexOf("ShowInListSettings=\"FALSE\"") === -1
&& f.TypeAsString !== "Boolean" && f.TypeAsString !== "Note" && f.TypeAsString !== "User") {
this.availableProperties.push({ key: f.InternalName, text: f.Title });
Log.Write(f.TypeAsString);
}
});
}
protected onDispose(): void {
ReactDom.unmountComponentAtNode(this.domElement);
}
protected get dataVersion(): Version {
return Version.parse('1.0');
}
protected onAddButtonClick (value: any) {
this.properties.selectedProperties.push(this.availableProperties[0].key.toString());
}
protected onDeleteButtonClick (value: any) {
Log.Write(value.toString());
var removed = this.properties.selectedProperties.splice(value, 1);
Log.Write(`${removed[0]} removed.`);
}
protected onPropertyPaneFieldChanged(propertyPath: string, oldValue: any, newValue: any): void {
if (propertyPath.indexOf("selectedProperty") >= 0) {
Log.Write('Selected Property identified');
let index: number = _.toInteger(propertyPath.replace("selectedProperty", ""));
this.properties.selectedProperties[index] = newValue;
}
}
protected async onPropertyPaneConfigurationStart(): Promise<void> {
Log.Write(`onPropertyPaneConfigurationStart`);
await this.getPageProperties();
this.context.propertyPane.refresh();
}
protected getPropertyPaneConfiguration(): IPropertyPaneConfiguration {
Log.Write(`getPropertyPaneConfiguration`);
// Initialize with the Title entry
var propDrops: IPropertyPaneField<any>[] = [];
propDrops.push(PropertyPaneTextField('title', {
label: strings.TitleFieldLabel
}));
propDrops.push(PropertyPaneHorizontalRule());
// Determine how many page property dropdowns we currently have
this.properties.selectedProperties.forEach((prop, index) => {
propDrops.push(PropertyPaneDropdown(`selectedProperty${index.toString()}`,
{
label: strings.SelectedPropertiesFieldLabel,
options: this.availableProperties,
selectedKey: prop,
}));
// Every drop down gets its own delete button
propDrops.push(PropertyPaneButton(`deleteButton${index.toString()}`,
{
text: strings.PropPaneDeleteButtonText,
buttonType: PropertyPaneButtonType.Command,
icon: "RecycleBin",
onClick: this.onDeleteButtonClick.bind(this, index)
}));
propDrops.push(PropertyPaneHorizontalRule());
});
// Always have the Add button
propDrops.push(PropertyPaneButton('addButton',
{
text: strings.PropPaneAddButtonText,
buttonType: PropertyPaneButtonType.Command,
icon: "CirclePlus",
onClick: this.onAddButtonClick.bind(this)
}));
return {
pages: [
{
header: {
description: strings.PropertyPaneDescription
},
groups: [
{
groupName: strings.SelectionGroupName,
groupFields: propDrops
}
]
}
]
};
}
}

View File

@ -0,0 +1,57 @@
@import '~office-ui-fabric-react/dist/sass/References.scss';
.advancedPageProperties {
.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);
display: flex;
flex-direction: row;
justify-content: flex-start;
}
.propNameRow {
margin: 5px;
font-size: 15px;
font-weight: 500;
}
.propValsRow {
margin-left: 10px;
display: flex;
font-size: 12px;
flex-wrap: wrap;
}
.title {
font-size: 20px;
font-weight: 600;
margin-bottom: 20px;
}
.standardCapsule {
box-sizing: border-box;
padding: 4px 8px 5px 8px;
display: flex;
border-radius: 16px;
height: 24px;
align-items: center;
white-space: nowrap;
margin: 4px 4px 4px 4px;
width: max-content;
}
.urlValue {
margin: 4px 4px 4px 4px;
cursor: pointer;
}
.imgValue {
margin: 4px 4px 4px 4px;
width: 50px;
}
.plainValue {
margin: 4px 4px 4px 4px;
}
}

View File

@ -0,0 +1,209 @@
import * as React from 'react';
import styles from './AdvancedPageProperties.module.scss';
import { IAdvancedPagePropertiesProps } from './IAdvancedPagePropertiesProps';
import { Log } from '../utilities/Log';
import * as _ from 'lodash';
import { useState, useEffect, useRef } from 'react';
import { PageProperty } from '../models';
import { sp } from "@pnp/sp";
import { DateTimeFieldFormatType, DateTimeFieldFriendlyFormatType, FieldTypes, IField, IFieldInfo } from "@pnp/sp/fields/types";
import "@pnp/sp/webs";
import "@pnp/sp/lists";
import "@pnp/sp/fields";
import "@pnp/sp/items";
import { IReadonlyTheme } from '@microsoft/sp-component-base';
/**
* AdvancedPageProperties
* @param props Page Properties selected for display
* @returns
*/
const AdvancedPageProperties: React.FunctionComponent<IAdvancedPagePropertiesProps> = props => {
// Main state object for the life of this component - pagePropValues
const [pagePropValues, setPagePropValues] = useState<PageProperty[]>([]);
const propsRef = useRef(props);
const { semanticColors }: IReadonlyTheme = props.themeVariant;
propsRef.current = props;
sp.setup({ spfxContext: props.context });
/**
* refreshProperties
* @description Gets the actual values for any selected properties, along with critical field metadata and ultimately re-sets the pagePropValues state
*/
async function refreshProperties () {
var newSetOfValues: PageProperty[] = [];
if (props.selectedProperties !== undefined && props.selectedProperties !== null) {
Log.Write(`${props.selectedProperties.length.toString()} properties used.`);
// Get the value(s) for the field from the list item itself
var allValues: any = {};
if (props.context.pageContext.listItem !== undefined && props.context.pageContext.listItem !== null) {
allValues = await sp.web.lists.getByTitle("Site Pages").items.getById(props.context.pageContext.listItem.id).select(...props.selectedProperties).get();
console.log(allValues);
}
for (let i = 0; i < props.selectedProperties.length; i++) {
const prop = props.selectedProperties[i];
Log.Write(`Selected Property: ${prop}`);
// Get field information, in case anything is needed in conjunction with value types
const field = await sp.web.lists.getByTitle("Site Pages").fields.getByInternalNameOrTitle(prop)();
// Establish the values array
var values: any[] = [];
if (allValues.hasOwnProperty(prop)) {
switch (field.TypeAsString) {
case "TaxonomyFieldTypeMulti":
case "MultiChoice":
values = _.clone(allValues[prop]);
break;
case "Thumbnail":
values.push(JSON.parse(allValues[prop]));
break;
default:
// Default behavior is to treat it like a string
values.push(allValues[prop]);
break;
}
}
// Push the final setup of a PageProperty object
newSetOfValues.push({ info: field, values: [...values] });
}
setPagePropValues({...newSetOfValues});
}
}
/**
* @description Effects to fire whenever the properties change
*/
useEffect(() => {
refreshProperties();
return () => {
// No cleanup at this moment
};
}, [propsRef.current]);
/**
* RenderPageProperties
* @description Focuses on the 2nd row layer, which is the property names that have been chosen to be displayed (uses Title as the display name)
* @returns
*/
const RenderPageProperties = () => {
if (pagePropValues !== undefined && pagePropValues !== null) {
var retVal = _.map(pagePropValues, (prop) => {
return (
<>
<div className={styles.propNameRow}>{prop.info.Title}<span style={{display: 'none'}}> - {prop.info.TypeAsString}</span></div>
<div className={styles.propValsRow}>
{RenderPagePropValue(prop)}
</div>
</>
);
});
return retVal;
} else {
return <i>Nothing to display</i>;
}
};
/**
* RenderPagePropValue
* @description Focuses on the 3rd and final row layer, which is the actual values tied to any property displayed for the page
* @param prop
* @returns
*/
const RenderPagePropValue = (prop: PageProperty) => {
console.log(prop);
var retVal = _.map(prop.values, (val) => {
if (val !== null) {
switch (prop.info.TypeAsString) {
case "URL":
return (
<span className={styles.urlValue}><a href={val.Url} target="_blank" style={{color: semanticColors.link}}>{val.Description}</a></span>
);
case "Thumbnail":
return (
<span><img className={styles.imgValue} src={val.serverRelativeUrl} /></span>
);
case "Number":
return (
<span className={styles.plainValue}>{(prop.info["ShowAsPercentage"] === true ? Number(val).toLocaleString(undefined,{style: 'percent', minimumFractionDigits:0}) : (prop.info["CommaSeparator"] === true ? val.toLocaleString('en') : val.toString()))}</span>
);
case "Currency":
return (
<span className={styles.plainValue}>{(prop.info["CommaSeparator"] === true ? new Intl.NumberFormat('en-US', { style: 'currency', currency: 'USD' }).format(val) : Intl.NumberFormat('en-US', { style: 'currency', currency: 'USD', useGrouping: false }).format(val))}</span>
);
case "DateTime":
//,"",,
switch (prop.info["DateFormat"]) {
case "StandardUS":
return (
<span className={styles.plainValue}>{new Date(val).toLocaleDateString()}</span>
);
case "ISO8601":
const d = new Date(val);
return (
<span className={styles.plainValue}>{`${d.getFullYear().toString()}-${d.getMonth()}-${d.getDate()}`}</span>
);
case "DayOfWeek":
return (
<span className={styles.plainValue}>{new Date(val).toLocaleDateString("en-US", { weekday: 'long', month: 'long', day: 'numeric', year: 'numeric' })}</span>
);
case "MonthSpelled":
return (
<span className={styles.plainValue}>{new Date(val).toLocaleDateString("en-US", { month: 'long', day: 'numeric', year: 'numeric' })}</span>
);
default:
return (
<span className={styles.plainValue}>{new Date(val).toLocaleDateString()}</span>
);
}
case "TaxonomyFieldTypeMulti":
case "TaxonomyFieldType":
return (
<span className={styles.standardCapsule} style={{backgroundColor: semanticColors.accentButtonBackground, color: semanticColors.accentButtonText}}>{val.Label}</span>
);
default:
return (
<span className={styles.standardCapsule} style={{backgroundColor: semanticColors.accentButtonBackground, color: semanticColors.accentButtonText}}>{val}</span>
);
}
} else {
return (<span className={styles.plainValue}>N/A</span>);
}
});
return retVal;
};
/**
* RenderTitle
* @description Focuses on the 1 row layer, being the Title that has been chosen for the page
* @returns
*/
const RenderTitle = () => {
if (props.title !== '') {
return <div className={styles.title}>{props.title}</div>;
} else {
return null;
}
};
return (
<div className={`${styles.advancedPageProperties} ${styles.container}`} style={{backgroundColor: semanticColors.bodyBackground, color: semanticColors.bodyText}}>
{RenderTitle()}
{RenderPageProperties()}
</div>
);
};
export default AdvancedPageProperties;

View File

@ -0,0 +1,9 @@
import { IReadonlyTheme } from '@microsoft/sp-component-base';
import { WebPartContext } from "@microsoft/sp-webpart-base";
export interface IAdvancedPagePropertiesProps {
context: WebPartContext;
title: string;
selectedProperties: string[];
themeVariant: IReadonlyTheme | undefined;
}

View File

@ -0,0 +1,11 @@
define([], function() {
return {
"PropertyPaneDescription": "Select the page properties that you want to display on the page",
"SelectionGroupName": "",
"TitleFieldLabel": "Title",
"SelectedPropertiesFieldLabel": "Property",
"PropPaneAddButtonText": "Add",
"PropPaneDeleteButtonText": "Delete",
"LogAppName": "APP"
}
});

View File

@ -0,0 +1,14 @@
declare interface IAdvancedPagePropertiesWebPartStrings {
PropertyPaneDescription: string;
SelectionGroupName: string;
TitleFieldLabel: string;
SelectedPropertiesFieldLabel: string;
PropPaneAddButtonText: string;
PropPaneDeleteButtonText: string;
LogAppName: string;
}
declare module 'AdvancedPagePropertiesWebPartStrings' {
const strings: IAdvancedPagePropertiesWebPartStrings;
export = strings;
}

View File

@ -0,0 +1,6 @@
import { IFieldInfo } from "@pnp/sp/fields";
export interface PageProperty {
info: IFieldInfo;
values: any[];
}

View File

@ -0,0 +1 @@
export * from './PageProperty';

View File

@ -0,0 +1,17 @@
import * as strings from "AdvancedPagePropertiesWebPartStrings";
export class Log {
private static readonly logStyle: string = "background: crimson; padding: 5px; border-radius: 5px; color: white";
constructor() {
}
public static Write(s: string, always: boolean = false) {
let d = new Date();
let dateStr = d.getHours() + ':' + d.getMinutes() + ':' + d.getSeconds() + '.' + d.getMilliseconds();
if (window.location.search.indexOf("debug") >= 0 || always) {
console.log(`%c${strings.LogAppName} >> ${dateStr} >> ${s}`, Log.logStyle);
}
}
}

View File

@ -0,0 +1 @@
export * from './Log';

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 383 B

View File

@ -0,0 +1,39 @@
{
"extends": "./node_modules/@microsoft/rush-stack-compiler-3.3/includes/tsconfig-web.json",
"compilerOptions": {
"target": "es5",
"forceConsistentCasingInFileNames": true,
"module": "esnext",
"moduleResolution": "node",
"jsx": "react",
"declaration": true,
"sourceMap": true,
"experimentalDecorators": true,
"skipLibCheck": true,
"outDir": "lib",
"inlineSources": false,
"strictNullChecks": false,
"noUnusedLocals": false,
"typeRoots": [
"./node_modules/@types",
"./node_modules/@microsoft"
],
"types": [
"es6-promise",
"webpack-env"
],
"lib": [
"es5",
"dom",
"es2015.collection"
]
},
"include": [
"src/**/*.ts",
"src/**/*.tsx"
],
"exclude": [
"node_modules",
"lib"
]
}

View File

@ -0,0 +1,30 @@
{
"extends": "@microsoft/sp-tslint-rules/base-tslint.json",
"rules": {
"class-name": false,
"export-name": false,
"forin": false,
"label-position": false,
"member-access": true,
"no-arg": false,
"no-console": false,
"no-construct": false,
"no-duplicate-variable": true,
"no-eval": false,
"no-function-expression": true,
"no-internal-module": true,
"no-shadowed-variable": true,
"no-switch-case-fall-through": true,
"no-unnecessary-semicolons": true,
"no-unused-expression": true,
"no-use-before-declare": true,
"no-with-statement": true,
"semicolon": true,
"trailing-comma": false,
"typedef": false,
"typedef-whitespace": false,
"use-named-parameter": true,
"variable-name": false,
"whitespace": false
}
}