Added theme aware components

This commit is contained in:
Kalle Mansikkaniemi 2023-04-21 15:18:31 +03:00
parent da1b5b0932
commit c822d2aada
9 changed files with 1207 additions and 167 deletions

File diff suppressed because it is too large Load Diff

View File

@ -22,7 +22,7 @@
"@microsoft/sp-webpart-base": "1.11.0", "@microsoft/sp-webpart-base": "1.11.0",
"@pnp/common": "1.2.6", "@pnp/common": "1.2.6",
"@pnp/logging": "1.2.6", "@pnp/logging": "1.2.6",
"@pnp/spfx-controls-react": "1.10.0", "@pnp/spfx-controls-react": "^2.10.0",
"@pnp/spfx-property-controls": "1.12.0", "@pnp/spfx-property-controls": "1.12.0",
"@types/handlebars": "4.0.39", "@types/handlebars": "4.0.39",
"common-tags": "1.8.0", "common-tags": "1.8.0",

View File

@ -4,6 +4,8 @@
"alias": "RssReaderWebPart", "alias": "RssReaderWebPart",
"componentType": "WebPart", "componentType": "WebPart",
"supportsThemeVariants": true,
// The "*" signifies that the version should be taken from the package.json // The "*" signifies that the version should be taken from the package.json
"version": "*", "version": "*",
"manifestVersion": 2, "manifestVersion": 2,

View File

@ -34,6 +34,12 @@ import {
MockTemplateService } from '../../services/TemplateService'; MockTemplateService } from '../../services/TemplateService';
import { FeedLayoutOption, FeedServiceOption } from '../../models'; import { FeedLayoutOption, FeedServiceOption } from '../../models';
import {
ThemeProvider,
ThemeChangedEventArgs,
IReadonlyTheme
} from '@microsoft/sp-component-base';
export interface IRssReaderWebPartProps { export interface IRssReaderWebPartProps {
title: string; title: string;
@ -72,11 +78,15 @@ export interface IRssReaderWebPartProps {
dateFormatLang: string; dateFormatLang: string;
backgroundColor: string; backgroundColor: string;
fontColor: string; fontColor: string;
useThemeFontColor: boolean;
useThemeBackgroundColor: boolean;
} }
export default class RssReaderWebPart extends BaseClientSideWebPart<IRssReaderWebPartProps> { export default class RssReaderWebPart extends BaseClientSideWebPart<IRssReaderWebPartProps> {
private _templateService: BaseTemplateService; private _templateService: BaseTemplateService;
private _propertyPage = null; private _propertyPage = null;
private _themeProvider: ThemeProvider;
private _themeVariant: IReadonlyTheme | undefined;
/** /**
* The template to display at render time * The template to display at render time
@ -85,6 +95,15 @@ export default class RssReaderWebPart extends BaseClientSideWebPart<IRssReaderWe
public onInit(): Promise<void> { public onInit(): Promise<void> {
// 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);
//Initialize a redux store that uses our custom Reducer & state //Initialize a redux store that uses our custom Reducer & state
RssHttpClientService.init(this.context); RssHttpClientService.init(this.context);
@ -141,6 +160,8 @@ export default class RssReaderWebPart extends BaseClientSideWebPart<IRssReaderWe
backgroundColor: this.properties.backgroundColor, backgroundColor: this.properties.backgroundColor,
fontColor: this.properties.fontColor, fontColor: this.properties.fontColor,
useThemeFontColor: this.properties.useThemeFontColor,
useThemeBackgroundColor: this.properties.useThemeBackgroundColor,
propertyPane: this.context.propertyPane, propertyPane: this.context.propertyPane,
@ -152,7 +173,9 @@ export default class RssReaderWebPart extends BaseClientSideWebPart<IRssReaderWe
updateProperty: (value: string) => { updateProperty: (value: string) => {
this.properties.title = value; this.properties.title = value;
} },
themeVariant: this._themeVariant
} }
); );
@ -516,6 +539,11 @@ export default class RssReaderWebPart extends BaseClientSideWebPart<IRssReaderWe
iconName: 'Precipitation', iconName: 'Precipitation',
key: 'rssReaderFontColorField' key: 'rssReaderFontColorField'
})); }));
layoutFields.push(PropertyPaneToggle('useThemeFontColor', {
label: strings.UseThemeFontColor
}));
layoutFields.push(PropertyFieldColorPicker('backgroundColor', { layoutFields.push(PropertyFieldColorPicker('backgroundColor', {
label: strings.BackgroundColorLabel, label: strings.BackgroundColorLabel,
selectedColor: this.properties.backgroundColor, selectedColor: this.properties.backgroundColor,
@ -528,6 +556,10 @@ export default class RssReaderWebPart extends BaseClientSideWebPart<IRssReaderWe
key: 'rssReaderBgColorField' key: 'rssReaderBgColorField'
})); }));
layoutFields.push(PropertyPaneToggle('useThemeBackgroundColor', {
label: strings.UseThemeBackgroundColor
}));
/* /*
dateFormatLang: string; dateFormatLang: string;
*/ */
@ -604,4 +636,14 @@ export default class RssReaderWebPart extends BaseClientSideWebPart<IRssReaderWe
this._templateContentToDisplay = templateContent; this._templateContentToDisplay = templateContent;
} }
/**
* Update the current theme variant reference and re-render.
*
* @param args The new theme
*/
private _handleThemeChangedEvent(args: ThemeChangedEventArgs): void {
this._themeVariant = args.theme;
this.render();
}
} }

View File

@ -5,6 +5,8 @@ import { FeedServiceOption, FeedLayoutOption } from '../../../../models';
import { BaseTemplateService } from '../../../../services/TemplateService'; import { BaseTemplateService } from '../../../../services/TemplateService';
import { IReadonlyTheme } from '@microsoft/sp-component-base';
export interface IRssReaderProps { export interface IRssReaderProps {
feedUrl: string; feedUrl: string;
feedService: FeedServiceOption; feedService: FeedServiceOption;
@ -38,6 +40,8 @@ export interface IRssReaderProps {
backgroundColor: string; backgroundColor: string;
fontColor: string; fontColor: string;
useThemeBackgroundColor: boolean;
useThemeFontColor: boolean;
propertyPane?: IPropertyPaneAccessor; propertyPane?: IPropertyPaneAccessor;
@ -55,4 +59,9 @@ export interface IRssReaderProps {
templateContent: string; templateContent: string;
updateProperty: (value: string) => void; updateProperty: (value: string) => void;
/**
* Theme awareness
*/
themeVariant: IReadonlyTheme | undefined;
} }

View File

@ -3,18 +3,6 @@
.rssReader { .rssReader {
.rssReaderHeader { .rssReaderHeader {
position: relative; position: relative;
.rssReaderListViewAll {
position: absolute;
top: 7px;
right: 0px;
padding: 0px;
A {
color: $ms-color-themePrimary;
}
}
} }
.rssReaderList { .rssReaderList {
@ -59,7 +47,6 @@
.itemDate { .itemDate {
font-size:11px; font-size:11px;
color:$ms-color-neutralDark;
} }
.itemContent { .itemContent {

View File

@ -24,6 +24,8 @@ import {
IRssReaderRequest, IRssReaderRequest,
FeedLayoutOption } from '../../../../models'; FeedLayoutOption } from '../../../../models';
import { IReadonlyTheme } from '@microsoft/sp-component-base';
export default class RssReader extends React.Component<IRssReaderProps, IRssReaderState> { export default class RssReader extends React.Component<IRssReaderProps, IRssReaderState> {
private viewAllLinkLabel: string = strings.DefaultFeedViewAllLinkLabel; private viewAllLinkLabel: string = strings.DefaultFeedViewAllLinkLabel;
@ -51,18 +53,17 @@ export default class RssReader extends React.Component<IRssReaderProps, IRssRead
} }
public render(): React.ReactElement<IRssReaderProps> { public render(): React.ReactElement<IRssReaderProps> {
const { semanticColors }: IReadonlyTheme = this.props.themeVariant;
return ( return (
<div className={ styles.rssReader }> <div className={ styles.rssReader } style={{backgroundColor: semanticColors.bodyBackground}}>
<div className={styles.rssReaderHeader}> <div className={styles.rssReaderHeader}>
<WebPartTitle displayMode={this.props.displayMode} <WebPartTitle displayMode={this.props.displayMode}
title={this.props.title} title={this.props.title}
updateProperty={this.props.updateProperty} /> updateProperty={this.props.updateProperty}
themeVariant={this.props.themeVariant}
{this.state.rssFeedReady && this.state.rssFeed && this.props.feedViewAllLink && this.props.feedViewAllLink.length > 0 && ( moreLink={this.state.rssFeedReady && this.state.rssFeed && this.props.feedViewAllLink && this.props.feedViewAllLink.length > 0 && (
<div className={styles.rssReaderListViewAll}> <Link style={{color: semanticColors.link}} href={this.props.feedViewAllLink}>{this.viewAllLinkLabel}</Link>
<a href={this.props.feedViewAllLink}>{this.viewAllLinkLabel}</a> )}/>
</div>
)}
</div> </div>
{!this.props.feedUrl || this.props.feedUrl.length < 1 ? ( {!this.props.feedUrl || this.props.feedUrl.length < 1 ? (
@ -97,7 +98,7 @@ export default class RssReader extends React.Component<IRssReaderProps, IRssRead
className={styles.rssReaderList + (this.props.backgroundColor ? " " + styles.rssReaderListPadding : "")} className={styles.rssReaderList + (this.props.backgroundColor ? " " + styles.rssReaderListPadding : "")}
items={this.state.rssFeed.query.results.rss} items={this.state.rssFeed.query.results.rss}
onRenderCell={this._onRenderListRow} onRenderCell={this._onRenderListRow}
style={this.props.backgroundColor ? {backgroundColor: this.props.backgroundColor} : {}} style={this.props.useThemeBackgroundColor ? {backgroundColor: semanticColors.bodyBackground} : this.props.backgroundColor ? {backgroundColor: this.props.backgroundColor} : {}}
/> />
)} )}
@ -154,7 +155,10 @@ export default class RssReader extends React.Component<IRssReaderProps, IRssRead
this.props.titleLinkTarget != nextProps.titleLinkTarget || this.props.titleLinkTarget != nextProps.titleLinkTarget ||
this.props.dateFormat != nextProps.dateFormat || this.props.dateFormat != nextProps.dateFormat ||
this.props.backgroundColor != nextProps.backgroundColor || this.props.backgroundColor != nextProps.backgroundColor ||
this.props.fontColor != nextProps.fontColor this.props.useThemeBackgroundColor != nextProps.useThemeBackgroundColor ||
this.props.useThemeFontColor != nextProps.useThemeFontColor ||
this.props.fontColor != nextProps.fontColor ||
this.props.themeVariant != nextProps.themeVariant
) { ) {
this.loadRssFeed(); this.loadRssFeed();
} }
@ -190,6 +194,7 @@ export default class RssReader extends React.Component<IRssReaderProps, IRssRead
let div = document.createElement("div"); let div = document.createElement("div");
div.innerHTML = displayDesc; div.innerHTML = displayDesc;
displayDesc = (div.textContent || div.innerText || "").replace(/\&nbsp;/ig, "").trim(); displayDesc = (div.textContent || div.innerText || "").replace(/\&nbsp;/ig, "").trim();
const { semanticColors }: IReadonlyTheme = this.props.themeVariant;
return ( return (
<div className={styles.rssReaderListItem} data-is-focusable={true}> <div className={styles.rssReaderListItem} data-is-focusable={true}>
@ -197,7 +202,7 @@ export default class RssReader extends React.Component<IRssReaderProps, IRssRead
<Link <Link
href={thisItem.link} href={thisItem.link}
target={this.props.titleLinkTarget ? this.props.titleLinkTarget : "_self"} target={this.props.titleLinkTarget ? this.props.titleLinkTarget : "_self"}
style={this.props.fontColor ? {color: this.props.fontColor} : {}} style={this.props.useThemeFontColor ? {color: semanticColors.link} : this.props.fontColor ? {color: this.props.fontColor} : {}}
> >
{this.decodeHtml(thisItem.title)} {this.decodeHtml(thisItem.title)}
</Link> </Link>
@ -205,7 +210,7 @@ export default class RssReader extends React.Component<IRssReaderProps, IRssRead
</div> </div>
{this.props.showPubDate && ( {this.props.showPubDate && (
<div className={styles.itemDate}> <div className={styles.itemDate} style={{color: semanticColors.bodySubtext}}>
{this.props.dateFormat && this.props.dateFormat.length > 0 ? ( {this.props.dateFormat && this.props.dateFormat.length > 0 ? (
<Moment <Moment
format={this.props.dateFormat} format={this.props.dateFormat}
@ -218,7 +223,7 @@ export default class RssReader extends React.Component<IRssReaderProps, IRssRead
)} )}
{this.props.showDesc && ( {this.props.showDesc && (
<div className={styles.itemContent}> <div className={styles.itemContent} style={{color: semanticColors.bodyText}}>
{this.props.descCharacterLimit && (displayDesc.length > this.props.descCharacterLimit) ? ( {this.props.descCharacterLimit && (displayDesc.length > this.props.descCharacterLimit) ? (
<div> <div>
{displayDesc.substring(0, this.props.descCharacterLimit) + '...'} {displayDesc.substring(0, this.props.descCharacterLimit) + '...'}

View File

@ -46,6 +46,8 @@ define([], function() {
"DateFormatLabel": "Date Format", "DateFormatLabel": "Date Format",
"BackgroundColorLabel": "Background Color", "BackgroundColorLabel": "Background Color",
"FontColorLabel": "Title Color", "FontColorLabel": "Title Color",
"UseThemeBackgroundColor": "Use background color from theme",
"UseThemeFontColor": "Use title color from theme",
//feed service options //feed service options

View File

@ -42,7 +42,8 @@ declare interface IRssReaderWebPartStrings {
DateFormatLabel: string; DateFormatLabel: string;
BackgroundColorLabel: string; BackgroundColorLabel: string;
FontColorLabel: string; FontColorLabel: string;
UseThemeBackgroundColor: string;
UseThemeFontColor: string;
//feed service options //feed service options