Initial release of the 'react-fluentui-theme-variant' sample
This commit is contained in:
parent
ec8162e8ea
commit
2f145ee1ba
|
@ -1,73 +1,68 @@
|
||||||
# react-fluentui-theme-variant
|
# React Fluent UI Theme Variant
|
||||||
|
|
||||||
## Summary
|
## Summary
|
||||||
|
This web part provides an example of how to apply a custom theme or a variation of the current SharePoint theme directly to the web part.
|
||||||
|
In this way it is possible to implement the same mechanism that is currently implemented by default by the SharePoint page sections
|
||||||
|
|
||||||
Short summary on functionality and used technologies.
|
![picture of the web part in action](assets/preview.gif)
|
||||||
|
|
||||||
[picture of the solution in action, if possible]
|
## Compatibility
|
||||||
|
|
||||||
## Used SharePoint Framework Version
|
![SPFx 1.12.1](https://img.shields.io/badge/SPFx-1.12.1-green.svg)
|
||||||
|
![Node.js LTS v14 | LTS v12 | LTS v10](https://img.shields.io/badge/Node.js-LTS%20v14%20%7C%20LTS%20v12%20%7C%20LTS%20v10-green.svg)
|
||||||
![version](https://img.shields.io/npm/v/@microsoft/sp-component-base/latest?color=green)
|
![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 Local | Hosted](https://img.shields.io/badge/Workbench-Local%20%7C%20Hosted-green.svg)
|
||||||
|
|
||||||
## Applies to
|
## Applies to
|
||||||
|
|
||||||
- [SharePoint Framework](https://aka.ms/spfx)
|
* [SharePoint Framework](https://docs.microsoft.com/sharepoint/dev/spfx/sharepoint-framework-overview)
|
||||||
- [Microsoft 365 tenant](https://docs.microsoft.com/en-us/sharepoint/dev/spfx/set-up-your-developer-tenant)
|
* [Microsoft 365 tenant](https://docs.microsoft.com/sharepoint/dev/spfx/set-up-your-development-environment)
|
||||||
|
|
||||||
> Get your own free development tenant by subscribing to [Microsoft 365 developer program](http://aka.ms/o365devprogram)
|
|
||||||
|
|
||||||
## Prerequisites
|
|
||||||
|
|
||||||
> Any special pre-requisites?
|
|
||||||
|
|
||||||
## Solution
|
## Solution
|
||||||
|
|
||||||
Solution|Author(s)
|
Solution|Author(s)
|
||||||
--------|---------
|
--------|---------
|
||||||
folder name | Author details (name, company, twitter alias with link)
|
react-fluentui-theme-variant | [Fabio Franzini](https://www.linkedin.com/in/fabiofranzini/) ([@franzinifabio](https://twitter.com/franzinifabio)), fabiofranzini.com
|
||||||
|
|
||||||
## Version history
|
## Version history
|
||||||
|
|
||||||
Version|Date|Comments
|
Version|Date|Comments
|
||||||
-------|----|--------
|
-------|----|--------
|
||||||
1.1|March 10, 2021|Update comment
|
1.0|August 9, 2021|Initial release
|
||||||
1.0|January 29, 2021|Initial release
|
|
||||||
|
## Minimal Path to Awesome
|
||||||
|
|
||||||
|
* Clone this repository
|
||||||
|
* in the command line run:
|
||||||
|
* `npm install`
|
||||||
|
* `gulp serve`
|
||||||
|
|
||||||
|
## Features
|
||||||
|
|
||||||
|
This example was born with the idea of overcoming the present default limit regarding the colors of the sections of the SharePoint pages.
|
||||||
|
|
||||||
|
Specifically, by default, it is only possible to change the color (also called Section Background Shading) of the sections but not of the individual Web Parts.
|
||||||
|
|
||||||
|
In this implementation it is instead possible to vary the "Background Shading" of the single Web Part in 3 ways:
|
||||||
|
* Use the colors applied to the section where the Web Part is present
|
||||||
|
* Select the color variations based on the theme applied at the Site level.
|
||||||
|
* Apply variations set to the json of a custom theme, created through the Fluent UI theme designer.
|
||||||
|
|
||||||
|
In all these cases, the component variation works automatically as much as the Fluent UI react controls are used, otherwise the variation will only work on the background and some HTML elements.
|
||||||
|
|
||||||
## Disclaimer
|
## 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.**
|
**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.**
|
||||||
|
|
||||||
---
|
## Help
|
||||||
|
|
||||||
## Minimal Path to Awesome
|
We do not support samples, but we 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.
|
||||||
|
|
||||||
- Clone this repository
|
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&template=bug-report.yml&sample=react-htm-templating&authors=@fabiofranzini&title=react-htm-templating%20-%20).
|
||||||
- Ensure that you are at the solution folder
|
|
||||||
- in the command-line run:
|
|
||||||
- **npm install**
|
|
||||||
- **gulp serve**
|
|
||||||
|
|
||||||
> Include any additional steps as needed.
|
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%3Abug-suspected&template=question.yml&sample=react-htm-templating&authors=@fabiofranzini&title=react-htm-templating%20-%20).
|
||||||
|
|
||||||
## Features
|
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%3Abug-suspected&template=suggestion.yml&sample=react-htm-templating&authors=@fabiofranzini&title=react-htm-templating%20-%20).
|
||||||
|
|
||||||
Description of the extension that expands upon high-level summary above.
|
<img src="https://telemetry.sharepointpnp.com/sp-dev-fx-webparts/samples/react-htm-templating" />
|
||||||
|
|
||||||
This extension illustrates the following concepts:
|
|
||||||
|
|
||||||
- topic 1
|
|
||||||
- topic 2
|
|
||||||
- topic 3
|
|
||||||
|
|
||||||
> Notice that better pictures and documentation will increase the sample usage and the value you are providing for others. Thanks for your submissions advance.
|
|
||||||
|
|
||||||
> Share your web part with others through Microsoft 365 Patterns and Practices program to get visibility and exposure. More details on the community, open-source projects and other activities from http://aka.ms/m365pnp.
|
|
||||||
|
|
||||||
## 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
|
|
Binary file not shown.
After Width: | Height: | Size: 44 MiB |
|
@ -2,13 +2,7 @@ import { Theme } from "@fluentui/react-theme-provider";
|
||||||
import { getNeutralVariant, getSoftVariant, getStrongVariant } from "@fluentui/scheme-utilities/lib/variants";
|
import { getNeutralVariant, getSoftVariant, getStrongVariant } from "@fluentui/scheme-utilities/lib/variants";
|
||||||
import { IReadonlyTheme } from '@microsoft/sp-component-base';
|
import { IReadonlyTheme } from '@microsoft/sp-component-base';
|
||||||
import { ServiceKey, ServiceScope } from "@microsoft/sp-core-library";
|
import { ServiceKey, ServiceScope } from "@microsoft/sp-core-library";
|
||||||
import { BaseSlots, createTheme, FabricSlots, getColorFromString, getContrastRatio, getTheme, IColor, isDark, ThemeGenerator, themeRulesStandardCreator } from "office-ui-fabric-react";
|
import { createTheme, getTheme, IPalette } from "office-ui-fabric-react";
|
||||||
|
|
||||||
export interface IContrastRatioPair {
|
|
||||||
contrastRatioValue: string;
|
|
||||||
contrastRatioPair: string;
|
|
||||||
colorPair: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
export enum ThemeType {
|
export enum ThemeType {
|
||||||
current = "current",
|
current = "current",
|
||||||
|
@ -24,9 +18,7 @@ export enum BackgroundShadingType {
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface IThemeService {
|
export interface IThemeService {
|
||||||
setCustomColors(primaryColor: string, textColor: string, backgroundColor: string): void;
|
generateTheme(themeType: ThemeType, backgroundShadingType: BackgroundShadingType, themeVariant: IReadonlyTheme | Theme, palette: Partial<IPalette>): IReadonlyTheme | Theme;
|
||||||
setThemeVariant(themeVariant: IReadonlyTheme | Theme): void;
|
|
||||||
generateTheme(themeType: ThemeType, backgroundShadingType: BackgroundShadingType): IReadonlyTheme | Theme;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const ThemeService_ServiceKey = 'ReactFluentUIThemeVariant:ThemeService';
|
const ThemeService_ServiceKey = 'ReactFluentUIThemeVariant:ThemeService';
|
||||||
|
@ -34,49 +26,29 @@ const ThemeService_ServiceKey = 'ReactFluentUIThemeVariant:ThemeService';
|
||||||
export class ThemeService implements IThemeService {
|
export class ThemeService implements IThemeService {
|
||||||
public static ServiceKey: ServiceKey<IThemeService> = ServiceKey.create(ThemeService_ServiceKey, ThemeService);
|
public static ServiceKey: ServiceKey<IThemeService> = ServiceKey.create(ThemeService_ServiceKey, ThemeService);
|
||||||
private serviceScope: ServiceScope;
|
private serviceScope: ServiceScope;
|
||||||
private themeRules = themeRulesStandardCreator();
|
|
||||||
|
|
||||||
private themeVariant: IReadonlyTheme | Theme;
|
|
||||||
private primaryColor: string;
|
|
||||||
private textColor: string;
|
|
||||||
private backgroundColor: string;
|
|
||||||
|
|
||||||
public constructor(serviceScope: ServiceScope) {
|
public constructor(serviceScope: ServiceScope) {
|
||||||
this.serviceScope = serviceScope;
|
this.serviceScope = serviceScope;
|
||||||
}
|
}
|
||||||
|
|
||||||
public setCustomColors(primaryColor: string, textColor: string, backgroundColor: string): void {
|
public generateTheme(themeType: ThemeType,
|
||||||
if (!primaryColor)
|
backgroundShadingType: BackgroundShadingType,
|
||||||
throw 'primaryColor == null or undefined';
|
themeVariant: IReadonlyTheme | Theme,
|
||||||
else
|
palette: Partial<IPalette>): IReadonlyTheme | Theme {
|
||||||
this.primaryColor = primaryColor;
|
|
||||||
|
|
||||||
if (!textColor)
|
|
||||||
throw 'textColor == null or undefined';
|
|
||||||
else
|
|
||||||
this.textColor = textColor;
|
|
||||||
|
|
||||||
if (!backgroundColor)
|
|
||||||
throw 'backgroundColor == null or undefined';
|
|
||||||
this.backgroundColor = backgroundColor;
|
|
||||||
}
|
|
||||||
|
|
||||||
public setThemeVariant(themeVariant: IReadonlyTheme | Theme): void {
|
|
||||||
this.themeVariant = themeVariant;
|
|
||||||
}
|
|
||||||
|
|
||||||
public generateTheme(themeType: ThemeType, backgroundShadingType: BackgroundShadingType): IReadonlyTheme | Theme {
|
|
||||||
let currentTheme: IReadonlyTheme | Theme;
|
let currentTheme: IReadonlyTheme | Theme;
|
||||||
|
|
||||||
switch (themeType) {
|
switch (themeType) {
|
||||||
case ThemeType.current: currentTheme = this.getDefaultTheme();
|
case ThemeType.current: currentTheme = this.getDefaultTheme();
|
||||||
break;
|
break;
|
||||||
case ThemeType.section: currentTheme = this.themeVariant;
|
case ThemeType.section: currentTheme = themeVariant;
|
||||||
break;
|
break;
|
||||||
case ThemeType.custom: currentTheme = this.generateThemeFromColors();
|
case ThemeType.custom: currentTheme = this.generateThemeFromPalette(palette);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (themeType == ThemeType.section)
|
||||||
|
return currentTheme;
|
||||||
|
|
||||||
switch (backgroundShadingType) {
|
switch (backgroundShadingType) {
|
||||||
case BackgroundShadingType.none: currentTheme = currentTheme;
|
case BackgroundShadingType.none: currentTheme = currentTheme;
|
||||||
break;
|
break;
|
||||||
|
@ -105,17 +77,14 @@ export class ThemeService implements IThemeService {
|
||||||
return currentTheme;
|
return currentTheme;
|
||||||
}
|
}
|
||||||
|
|
||||||
private generateThemeFromColors(): Theme {
|
private generateThemeFromPalette(palette: Partial<IPalette>): Theme {
|
||||||
ThemeGenerator.setSlot(this.themeRules[BaseSlots[BaseSlots.primaryColor]], getColorFromString(this.primaryColor), false, true, true);
|
|
||||||
ThemeGenerator.setSlot(this.themeRules[BaseSlots[BaseSlots.foregroundColor]], getColorFromString(this.textColor), false, true, true);
|
|
||||||
ThemeGenerator.setSlot(this.themeRules[BaseSlots[BaseSlots.backgroundColor]], getColorFromString(this.backgroundColor), false, true, true);
|
|
||||||
ThemeGenerator.insureSlots(this.themeRules, false);
|
|
||||||
|
|
||||||
let generatedTheme = createTheme({
|
let generatedTheme = createTheme({
|
||||||
...{ palette: ThemeGenerator.getThemeAsJson(this.themeRules) },
|
...{ palette: palette }
|
||||||
isInverted: isDark(this.themeRules[BaseSlots[BaseSlots.backgroundColor]].color!),
|
|
||||||
});
|
});
|
||||||
|
|
||||||
return generatedTheme;
|
return generatedTheme;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -15,8 +15,6 @@
|
||||||
"description": { "default": "Example on how to apply Fluent UI theme variant not only at Section level but at Web Part level" },
|
"description": { "default": "Example on how to apply Fluent UI theme variant not only at Section level but at Web Part level" },
|
||||||
"officeFabricIconFontName": "BackgroundColor",
|
"officeFabricIconFontName": "BackgroundColor",
|
||||||
"properties": {
|
"properties": {
|
||||||
"themeType": "current",
|
|
||||||
"backgroundShadingType": "none"
|
|
||||||
}
|
}
|
||||||
}]
|
}]
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,28 +7,29 @@ import {
|
||||||
PropertyPaneDropdown
|
PropertyPaneDropdown
|
||||||
} from '@microsoft/sp-property-pane';
|
} from '@microsoft/sp-property-pane';
|
||||||
import { BaseClientSideWebPart } from '@microsoft/sp-webpart-base';
|
import { BaseClientSideWebPart } from '@microsoft/sp-webpart-base';
|
||||||
import { PropertyFieldColorPicker, PropertyFieldColorPickerStyle } from '@pnp/spfx-property-controls/lib/PropertyFieldColorPicker';
|
|
||||||
import { PropertyFieldMessage } from '@pnp/spfx-property-controls/lib/PropertyFieldMessage';
|
|
||||||
import * as strings from 'FluentUiThemeVariantWebPartStrings';
|
import * as strings from 'FluentUiThemeVariantWebPartStrings';
|
||||||
import { MessageBarType } from 'office-ui-fabric-react/lib/MessageBar';
|
import { MessageBarType } from 'office-ui-fabric-react/lib/components/MessageBar';
|
||||||
import * as React from 'react';
|
import * as React from 'react';
|
||||||
import * as ReactDom from 'react-dom';
|
import * as ReactDom from 'react-dom';
|
||||||
import { BackgroundShadingType, IContrastRatioPair, IThemeService, ThemeService, ThemeType } from '../../services/ThemeService';
|
import { BackgroundShadingType, IThemeService, ThemeService, ThemeType } from '../../services/ThemeService';
|
||||||
import FluentUiThemeVariant, { IFluentUiThemeVariantProps } from './components/FluentUiThemeVariant';
|
import FluentUiThemeVariant, { IFluentUiThemeVariantProps } from './components/FluentUiThemeVariant';
|
||||||
|
|
||||||
export interface IFluentUiThemeVariantWebPartProps {
|
export interface IFluentUiThemeVariantWebPartProps {
|
||||||
themeType: ThemeType;
|
themeType: ThemeType;
|
||||||
backgroundShadingType: BackgroundShadingType;
|
backgroundShadingType: BackgroundShadingType;
|
||||||
primaryColor: string;
|
customPalette: string;
|
||||||
textColor: string;
|
|
||||||
backgroundColor: string;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export default class FluentUiThemeVariantWebPart extends BaseClientSideWebPart<IFluentUiThemeVariantWebPartProps> {
|
export default class FluentUiThemeVariantWebPart extends BaseClientSideWebPart<IFluentUiThemeVariantWebPartProps> {
|
||||||
private themeService: IThemeService;
|
private themeService: IThemeService;
|
||||||
|
|
||||||
protected themeProvider: ThemeProvider;
|
protected themeProvider: ThemeProvider;
|
||||||
protected themeVariant: IReadonlyTheme;
|
protected themeVariant: IReadonlyTheme;
|
||||||
|
|
||||||
|
protected propertyFieldCodeEditor;
|
||||||
|
protected propertyFieldCodeEditorLanguages;
|
||||||
|
protected propertyFieldMessage;
|
||||||
|
|
||||||
protected onInit(): Promise<void> {
|
protected onInit(): Promise<void> {
|
||||||
this.themeService = this.context.serviceScope.consume<IThemeService>(ThemeService.ServiceKey);
|
this.themeService = this.context.serviceScope.consume<IThemeService>(ThemeService.ServiceKey);
|
||||||
|
|
||||||
|
@ -37,43 +38,67 @@ export default class FluentUiThemeVariantWebPart extends BaseClientSideWebPart<I
|
||||||
this.themeProvider.themeChangedEvent.add(this, (args: ThemeChangedEventArgs): void => {
|
this.themeProvider.themeChangedEvent.add(this, (args: ThemeChangedEventArgs): void => {
|
||||||
if (!isEqual(this.themeVariant, args.theme)) {
|
if (!isEqual(this.themeVariant, args.theme)) {
|
||||||
this.themeVariant = args.theme;
|
this.themeVariant = args.theme;
|
||||||
this.themeService.setThemeVariant(this.themeVariant);
|
|
||||||
this.render();
|
this.render();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
if (!this.properties.primaryColor)
|
if (!this.properties.themeType)
|
||||||
this.properties.primaryColor = "#0078d4";
|
this.properties.themeType = ThemeType.section;
|
||||||
|
|
||||||
if (!this.properties.textColor)
|
if (!this.properties.backgroundShadingType)
|
||||||
this.properties.textColor = "#323130";
|
this.properties.backgroundShadingType = BackgroundShadingType.none;
|
||||||
|
|
||||||
if (!this.properties.backgroundColor)
|
if (!this.properties.customPalette)
|
||||||
this.properties.backgroundColor = "#ffffff";
|
this.properties.customPalette =
|
||||||
|
JSON.stringify(
|
||||||
|
JSON.parse(
|
||||||
this.themeService.setThemeVariant(this.themeVariant);
|
`{
|
||||||
this.themeService.setCustomColors(this.properties.primaryColor, this.properties.textColor, this.properties.backgroundColor);
|
"themePrimary": "#0078d4",
|
||||||
|
"themeLighterAlt": "#eff6fc",
|
||||||
|
"themeLighter": "#deecf9",
|
||||||
|
"themeLight": "#c7e0f4",
|
||||||
|
"themeTertiary": "#71afe5",
|
||||||
|
"themeSecondary": "#2b88d8",
|
||||||
|
"themeDarkAlt": "#106ebe",
|
||||||
|
"themeDark": "#005a9e",
|
||||||
|
"themeDarker": "#004578",
|
||||||
|
"neutralLighterAlt": "#faf9f8",
|
||||||
|
"neutralLighter": "#f3f2f1",
|
||||||
|
"neutralLight": "#edebe9",
|
||||||
|
"neutralQuaternaryAlt": "#e1dfdd",
|
||||||
|
"neutralQuaternary": "#d0d0d0",
|
||||||
|
"neutralTertiaryAlt": "#c8c6c4",
|
||||||
|
"neutralTertiary": "#a19f9d",
|
||||||
|
"neutralSecondary": "#605e5c",
|
||||||
|
"neutralPrimaryAlt": "#3b3a39",
|
||||||
|
"neutralPrimary": "#323130",
|
||||||
|
"neutralDark": "#201f1e",
|
||||||
|
"black": "#000000",
|
||||||
|
"white": "#ffffff"
|
||||||
|
}`
|
||||||
|
), null, 2);
|
||||||
|
|
||||||
return super.onInit();
|
return super.onInit();
|
||||||
}
|
}
|
||||||
|
|
||||||
public render(): void {
|
public render(): void {
|
||||||
|
|
||||||
|
const currentTheme = this.themeService.generateTheme(
|
||||||
|
this.properties.themeType,
|
||||||
|
this.properties.backgroundShadingType,
|
||||||
|
this.themeVariant,
|
||||||
|
JSON.parse(this.properties.customPalette));
|
||||||
|
|
||||||
const element: React.ReactElement<IFluentUiThemeVariantProps> = React.createElement(
|
const element: React.ReactElement<IFluentUiThemeVariantProps> = React.createElement(
|
||||||
FluentUiThemeVariant,
|
FluentUiThemeVariant,
|
||||||
{
|
{
|
||||||
themeVariant: this.themeService.generateTheme(
|
themeVariant: currentTheme
|
||||||
this.properties.themeType,
|
|
||||||
this.properties.backgroundShadingType)
|
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
ReactDom.render(element, this.domElement);
|
ReactDom.render(element, this.domElement);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
protected onDispose(): void {
|
protected onDispose(): void {
|
||||||
ReactDom.unmountComponentAtNode(this.domElement);
|
ReactDom.unmountComponentAtNode(this.domElement);
|
||||||
}
|
}
|
||||||
|
@ -82,6 +107,21 @@ export default class FluentUiThemeVariantWebPart extends BaseClientSideWebPart<I
|
||||||
return Version.parse('1.0');
|
return Version.parse('1.0');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected async loadPropertyPaneResources(): Promise<void> {
|
||||||
|
const { PropertyFieldCodeEditor, PropertyFieldCodeEditorLanguages } = await import(
|
||||||
|
/* webpackChunkName: 'pnp-controls-property-field-code-editor' */
|
||||||
|
'@pnp/spfx-property-controls/lib/PropertyFieldCodeEditor'
|
||||||
|
);
|
||||||
|
this.propertyFieldCodeEditor = PropertyFieldCodeEditor;
|
||||||
|
this.propertyFieldCodeEditorLanguages = PropertyFieldCodeEditorLanguages;
|
||||||
|
|
||||||
|
const { PropertyFieldMessage } = await import(
|
||||||
|
/* webpackChunkName: 'pnp-controls-property-field-message' */
|
||||||
|
'@pnp/spfx-property-controls/lib/PropertyFieldMessage'
|
||||||
|
);
|
||||||
|
this.propertyFieldMessage = PropertyFieldMessage;
|
||||||
|
}
|
||||||
|
|
||||||
protected getPropertyPaneConfiguration(): IPropertyPaneConfiguration {
|
protected getPropertyPaneConfiguration(): IPropertyPaneConfiguration {
|
||||||
|
|
||||||
let inlineSvgNone = this.generateInlineSvgForBackgroundShadingType(BackgroundShadingType.none);
|
let inlineSvgNone = this.generateInlineSvgForBackgroundShadingType(BackgroundShadingType.none);
|
||||||
|
@ -89,9 +129,6 @@ export default class FluentUiThemeVariantWebPart extends BaseClientSideWebPart<I
|
||||||
let inlineSvgSoft = this.generateInlineSvgForBackgroundShadingType(BackgroundShadingType.soft);
|
let inlineSvgSoft = this.generateInlineSvgForBackgroundShadingType(BackgroundShadingType.soft);
|
||||||
let inlineSvgStrong = this.generateInlineSvgForBackgroundShadingType(BackgroundShadingType.strong);
|
let inlineSvgStrong = this.generateInlineSvgForBackgroundShadingType(BackgroundShadingType.strong);
|
||||||
|
|
||||||
console.log(inlineSvgNone);
|
|
||||||
console.log(inlineSvgStrong);
|
|
||||||
|
|
||||||
return {
|
return {
|
||||||
pages: [
|
pages: [
|
||||||
{
|
{
|
||||||
|
@ -105,55 +142,29 @@ export default class FluentUiThemeVariantWebPart extends BaseClientSideWebPart<I
|
||||||
PropertyPaneDropdown('themeType', {
|
PropertyPaneDropdown('themeType', {
|
||||||
label: strings.ThemeTypeField,
|
label: strings.ThemeTypeField,
|
||||||
options: [
|
options: [
|
||||||
{ key: ThemeType.current, text: strings.Texts.Current },
|
|
||||||
{ key: ThemeType.section, text: strings.Texts.Section },
|
{ key: ThemeType.section, text: strings.Texts.Section },
|
||||||
|
{ key: ThemeType.current, text: strings.Texts.Current },
|
||||||
{ key: ThemeType.custom, text: strings.Texts.Custom }
|
{ key: ThemeType.custom, text: strings.Texts.Custom }
|
||||||
]
|
]
|
||||||
}),
|
}),
|
||||||
// PropertyFieldMessage("", {
|
this.properties.themeType == ThemeType.custom && this.propertyFieldMessage("", {
|
||||||
// key: "colorPaletteAccessibilityErrors",
|
key: "fluentUiThemeDesignerMessage",
|
||||||
// text: this.getNonAccessiblePairsMessage(this.themeService.getNonAccessiblePairs()),
|
text: strings.CustomPaletteMessageField,
|
||||||
// multiline: true,
|
multiline: true,
|
||||||
// messageType: MessageBarType.warning,
|
messageType: MessageBarType.info,
|
||||||
// isVisible: this.properties.themeType == ThemeType.custom && this.themeService.getNonAccessiblePairs().length > 0,
|
isVisible: true,
|
||||||
// }),
|
}),
|
||||||
PropertyFieldColorPicker('primaryColor', {
|
this.properties.themeType == ThemeType.custom && this.propertyFieldCodeEditor('customPalette', {
|
||||||
label: strings.PrimaryColorField,
|
label: strings.CustomPaletteField,
|
||||||
selectedColor: this.properties.primaryColor,
|
panelTitle: strings.CustomPaletteField,
|
||||||
onPropertyChange: this.onCustomPropertyPaneFieldChanged,
|
initialValue: this.properties.customPalette,
|
||||||
|
onPropertyChange: this.onPropertyPaneFieldChanged,
|
||||||
properties: this.properties,
|
properties: this.properties,
|
||||||
disabled: false,
|
disabled: false,
|
||||||
isHidden: this.properties.themeType != ThemeType.custom,
|
key: 'customPaletteEditorFieldId',
|
||||||
alphaSliderHidden: false,
|
language: this.propertyFieldCodeEditorLanguages.JSON
|
||||||
style: PropertyFieldColorPickerStyle.Inline,
|
|
||||||
iconName: 'Color',
|
|
||||||
key: 'primaryColorFieldId'
|
|
||||||
}),
|
}),
|
||||||
PropertyFieldColorPicker('textColor', {
|
this.properties.themeType != ThemeType.section && PropertyPaneChoiceGroup('backgroundShadingType', {
|
||||||
label: strings.TextColorField,
|
|
||||||
selectedColor: this.properties.textColor,
|
|
||||||
onPropertyChange: this.onCustomPropertyPaneFieldChanged,
|
|
||||||
properties: this.properties,
|
|
||||||
disabled: false,
|
|
||||||
isHidden: this.properties.themeType != ThemeType.custom,
|
|
||||||
alphaSliderHidden: false,
|
|
||||||
style: PropertyFieldColorPickerStyle.Inline,
|
|
||||||
iconName: 'Color',
|
|
||||||
key: 'textColorFieldId'
|
|
||||||
}),
|
|
||||||
PropertyFieldColorPicker('backgroundColor', {
|
|
||||||
label: strings.BackgroundColorField,
|
|
||||||
selectedColor: this.properties.backgroundColor,
|
|
||||||
onPropertyChange: this.onCustomPropertyPaneFieldChanged,
|
|
||||||
properties: this.properties,
|
|
||||||
disabled: false,
|
|
||||||
isHidden: this.properties.themeType != ThemeType.custom,
|
|
||||||
alphaSliderHidden: false,
|
|
||||||
style: PropertyFieldColorPickerStyle.Inline,
|
|
||||||
iconName: 'Color',
|
|
||||||
key: 'backgroundColorFieldId'
|
|
||||||
}),
|
|
||||||
PropertyPaneChoiceGroup('backgroundShadingType', {
|
|
||||||
label: strings.BackgroundShadingTypeField,
|
label: strings.BackgroundShadingTypeField,
|
||||||
options: [{
|
options: [{
|
||||||
key: BackgroundShadingType.none,
|
key: BackgroundShadingType.none,
|
||||||
|
@ -205,30 +216,11 @@ export default class FluentUiThemeVariantWebPart extends BaseClientSideWebPart<I
|
||||||
}
|
}
|
||||||
|
|
||||||
private generateInlineSvgForBackgroundShadingType(type: BackgroundShadingType): string {
|
private generateInlineSvgForBackgroundShadingType(type: BackgroundShadingType): string {
|
||||||
let currentTheme = this.themeService.generateTheme(this.properties.themeType, type);
|
let currentTheme = this.themeService.generateTheme(this.properties.themeType, type, this.themeVariant, JSON.parse(this.properties.customPalette));
|
||||||
let backgroundColor = currentTheme.semanticColors.bodyBackground.replace("#", "%23");
|
let backgroundColor = currentTheme.semanticColors.bodyBackground.replace("#", "%23");
|
||||||
let textColor = currentTheme.semanticColors.bodyText.replace("#", "%23");
|
let textColor = currentTheme.semanticColors.bodyText.replace("#", "%23");
|
||||||
|
|
||||||
return `data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='45' height='45'%3E%3Crect x='0' y='0' width='45' height='45' stroke='black' stroke-width='2px' fill='${backgroundColor}'/%3E%3Ctext fill='${textColor}' font-family='Segoe UI, sans-serif' x='50%25' y='55%25' dominant-baseline='middle' text-anchor='middle'%3EAa%3C/text%3E%3C/svg%3E`;
|
return `data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='45' height='45'%3E%3Crect x='0' y='0' width='45' height='45' stroke='black' stroke-width='2px' fill='${backgroundColor}'/%3E%3Ctext fill='${textColor}' font-family='Segoe UI, sans-serif' x='50%25' y='55%25' dominant-baseline='middle' text-anchor='middle'%3EAa%3C/text%3E%3C/svg%3E`;
|
||||||
}
|
}
|
||||||
|
|
||||||
private onCustomPropertyPaneFieldChanged = (targetProperty: string, oldValue: string, newValue: string) => {
|
|
||||||
if (targetProperty == "primaryColor" || targetProperty == "textColor" || targetProperty == "backgroundColor") {
|
|
||||||
this.themeService.setCustomColors(this.properties.primaryColor, this.properties.textColor, this.properties.backgroundColor);
|
|
||||||
//this.themeService.calculateContrastRatioPairs();
|
|
||||||
}
|
|
||||||
|
|
||||||
this.onPropertyPaneFieldChanged(targetProperty, oldValue, newValue);
|
|
||||||
}
|
|
||||||
|
|
||||||
// private getNonAccessiblePairsMessage(contrastRatios: IContrastRatioPair[]): string {
|
|
||||||
// let message = `Your color palette has ${this.themeService.getNonAccessiblePairs().length} accessibility errors:`;
|
|
||||||
|
|
||||||
// contrastRatios.forEach((element, index) => {
|
|
||||||
// message += (index > 0) ? `, ${element.colorPair}` : ` ${element.colorPair}`;
|
|
||||||
// });
|
|
||||||
|
|
||||||
// return message;
|
|
||||||
// }
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,9 +1,8 @@
|
||||||
|
|
||||||
import { ThemeProvider } from '@fluentui/react-theme-provider';
|
import { PartialTheme, Theme, ThemeProvider } from '@fluentui/react-theme-provider';
|
||||||
import { DefaultButton, DetailsList, DetailsListLayoutMode, Label, Link, PrimaryButton, Stack } from 'office-ui-fabric-react';
|
import { DefaultButton, DetailsList, DetailsListLayoutMode, Label, Link, PrimaryButton, Stack } from 'office-ui-fabric-react';
|
||||||
import * as React from 'react';
|
import * as React from 'react';
|
||||||
import { useEffect } from 'react';
|
import { useEffect } from 'react';
|
||||||
import { PartialTheme, Theme } from '@fluentui/react-theme-provider';
|
|
||||||
|
|
||||||
export interface IFluentUiThemeVariantProps {
|
export interface IFluentUiThemeVariantProps {
|
||||||
themeVariant: PartialTheme | Theme;
|
themeVariant: PartialTheme | Theme;
|
||||||
|
|
|
@ -3,10 +3,9 @@ define([], function() {
|
||||||
"PropertyPaneDescription": "Web Part Configuration",
|
"PropertyPaneDescription": "Web Part Configuration",
|
||||||
"BasicGroupName": "Core Settings",
|
"BasicGroupName": "Core Settings",
|
||||||
"ThemeTypeField": "Theme Type",
|
"ThemeTypeField": "Theme Type",
|
||||||
"PrimaryColorField": "Primary Color",
|
|
||||||
"TextColorField": "Text Color",
|
|
||||||
"BackgroundColorField": "Background Color",
|
|
||||||
"BackgroundShadingTypeField": "Background Shading",
|
"BackgroundShadingTypeField": "Background Shading",
|
||||||
|
"CustomPaletteField": "Fluent UI Custom Palette",
|
||||||
|
"CustomPaletteMessageField": "Please use the 'Fluent UI Theme Designer' available at https://aka.ms/themedesigner to export the Theme you want to apply",
|
||||||
"Texts": {
|
"Texts": {
|
||||||
"Current": "Current SharePoint Theme",
|
"Current": "Current SharePoint Theme",
|
||||||
"Section": "Current Section Variation",
|
"Section": "Current Section Variation",
|
||||||
|
|
|
@ -2,10 +2,9 @@ declare interface IFluentUiThemeVariantWebPartStrings {
|
||||||
PropertyPaneDescription: string;
|
PropertyPaneDescription: string;
|
||||||
BasicGroupName: string;
|
BasicGroupName: string;
|
||||||
ThemeTypeField: string;
|
ThemeTypeField: string;
|
||||||
PrimaryColorField: string;
|
|
||||||
TextColorField: string;
|
|
||||||
BackgroundColorField: string;
|
|
||||||
BackgroundShadingTypeField: string;
|
BackgroundShadingTypeField: string;
|
||||||
|
CustomPaletteField: string;
|
||||||
|
CustomPaletteMessageField: string;
|
||||||
Texts: {
|
Texts: {
|
||||||
Current: string;
|
Current: string;
|
||||||
Section: string;
|
Section: string;
|
||||||
|
|
Loading…
Reference in New Issue