diff --git a/.eslintrc.js b/.eslintrc.js index f5ce885..6ad11a9 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -189,7 +189,7 @@ module.exports = { // STANDARDIZED BY: eslint\conf\eslint-recommended.js 'no-control-regex': 2, // STANDARDIZED BY: eslint\conf\eslint-recommended.js - 'no-debugger': 1, + 'no-debugger': 0, // STANDARDIZED BY: eslint\conf\eslint-recommended.js 'no-delete-var': 2, // RATIONALE: Catches code that is likely to be incorrect diff --git a/src/webparts/betterHero/BetterHeroWebPart.module.scss b/src/webparts/betterHero/BetterHeroWebPart.module.scss index 5d73b48..8634edc 100644 --- a/src/webparts/betterHero/BetterHeroWebPart.module.scss +++ b/src/webparts/betterHero/BetterHeroWebPart.module.scss @@ -1,5 +1,7 @@ //@import '~@microsoft/sp-office-ui-fabric-core/dist/sass/SPFabricCore.scss'; - +:root { + --hero-image-opacity: .5; +} /* .betterHero { @@ -45,7 +47,7 @@ } .cardImgOverlay { - background: rgba(0, 0, 0, 0.5); + background: rgba(0, 0, 0, var(--hero-image-opacity)); bottom: 0; top: auto; } diff --git a/src/webparts/betterHero/BetterHeroWebPart.ts b/src/webparts/betterHero/BetterHeroWebPart.ts index 3c4845a..ae9b066 100644 --- a/src/webparts/betterHero/BetterHeroWebPart.ts +++ b/src/webparts/betterHero/BetterHeroWebPart.ts @@ -1,6 +1,7 @@ -import { Version } from '@microsoft/sp-core-library'; +import { Version, DisplayMode } from '@microsoft/sp-core-library'; import { type IPropertyPaneConfiguration, + PropertyPaneSlider, PropertyPaneButton } from '@microsoft/sp-property-pane'; import { BaseClientSideWebPart } from '@microsoft/sp-webpart-base'; @@ -11,14 +12,31 @@ import { Components, Helper } from 'gd-sprest-bs'; import 'bootstrap/dist/css/bootstrap.min.css'; import styles from './BetterHeroWebPart.module.scss'; + +/* +* Need to re-size the images. Maybe give options for small, medium, large? Or allow person to specify height and width of the card? +* Click and drag functionality to re-order? Or maybe create an order property? +* Need to edit an item. Need to determine current mode. If in edit mode, need edit button under each image. (done) +* Need to delete. (done) +* Need Subtitle, the card overlay part. (done) +* Tooltips. (done) +* Opacity of the text-card-overlay. (done) +* columns per row +*/ + + export interface IBetterHeroWebPartProps { images: string; //store as JSON string + opacity: number; + cardCols: number; } interface IImageInfo { image: string; title: string; url: string; + subtitle: string; + hoverText: string; } // Acceptable image file types @@ -32,9 +50,20 @@ export default class BetterHeroWebPart extends BaseClientSideWebPart= 0 && this.properties.opacity <= 100) { + const root = document.querySelector(':root') as HTMLElement; + root.style.setProperty('--hero-image-opacity', (this.properties.opacity / 100).toString()); + } + + // convert the images property to an array if (this.properties.images) { try { + // convert the JSON stringified string back to it's original form, the array this.imagesInfo = JSON.parse(this.properties.images); } catch (error) { this.imagesInfo = []; @@ -45,12 +74,76 @@ export default class BetterHeroWebPart extends BaseClientSideWebPart +
+ ${imageInfo.title} +
+
${imageInfo.title}
+

${imageInfo.subtitle || ''}

+
+
+ + + ${isInEditMode ? '
' : ''} + `; + + if (isInEditMode) { + //render buttons + Components.ButtonGroup({ + el: elCard.querySelector('.card-buttons') as HTMLElement, + isSmall: true, + buttons: [ + { + text: 'Edit', + type: Components.ButtonTypes.OutlinePrimary, + onClick: () => { + // todo + this.renderForm(idx); + } + }, + { + text: 'Delete', + type: Components.ButtonTypes.OutlineDanger, + onClick: () => { + // get element from array where image = selected one?, then delete + this.imagesInfo.splice(idx, 1); + this.properties.images = JSON.stringify(this.imagesInfo); + + this.render(); + } + } + ] + }) + } + + if (imageInfo.hoverText) { + Components.Tooltip({ + content: imageInfo.hoverText, + target: elCard.querySelector('.card') as HTMLElement, + type: Components.TooltipTypes.Info + }) + } + return elCard; } - private renderImages(): void { - let html = ''; + + private renderCards(): void { + this.domElement.innerHTML = ''; // check if images don't exist. If no images, display a message to guide the user to upload. if (this.imagesInfo.length === 0) { @@ -59,31 +152,33 @@ export default class BetterHeroWebPart extends BaseClientSideWebPart -
- `; + const elContainer = document.createElement('div'); + elContainer.classList.add('container', 'my-2'); + this.domElement.appendChild(elContainer); + + const elRows = document.createElement('div'); + elRows.classList.add('row', 'g-2', 'row-cols-md-2', 'row-cols-sm-1', `row-cols-lg-${this.properties.cardCols}`); // add row-col-{num} + elContainer.appendChild(elRows); + + + + + // debugger; // make a bootstrap card for each image in the array - for (const imageInfo of this.imagesInfo) { - html += ` - `; + //for (const imageInfo of this.imagesInfo) { + for (let i = 0; i < this.imagesInfo.length; i++) { + const elCol = document.createElement('div'); + elCol.classList.add('col-xs-1'); + elRows.appendChild(elCol); + + const elCard = this.renderCard(i); + elCol.appendChild(elCard); } - html += `
- `; - this.domElement.innerHTML = html; + //this.domElement.innerHTML = html; + // to clear out any javascript do this.domElement.innerHTML = this.domElement.innerHTML } // Determines if the image extension is valid @@ -104,28 +199,47 @@ export default class BetterHeroWebPart extends BaseClientSideWebPart { + // ensure form is valid + if (this.form.isValid()) { + // get the form values + const formValues = this.form.getValues() as IImageInfo; + + if (imageInfo) { + this.imagesInfo[idx] = formValues; + } + else { + // add object to the array + this.imagesInfo.push(formValues); + } + + + // Stringify because web part properties can only hold strings, numbers, and booleans + this.properties.images = JSON.stringify(this.imagesInfo); + + this.render(); + + Modal.hide(); + } + } + + } + }, + { + content: 'Close the dialog', + btnProps: { + text: 'Cancel', + type: Components.ButtonTypes.OutlineInfo, + onClick: () => { + Modal.hide() + } + + } + } + ] + }) + Modal.show() + } protected onThemeChanged(currentTheme: IReadonlyTheme | undefined): void { @@ -220,64 +384,24 @@ export default class BetterHeroWebPart extends BaseClientSideWebPart { - //set header - Modal.clear(); - Modal.setHeader('Add image link'); // display a form this.renderForm(); - // render footer actions : save and cancel buttons - Components.TooltipGroup({ - el: Modal.FooterElement, - tooltips: [ - { - content: 'save the image link', - btnProps: { - text: 'Save', - type: Components.ButtonTypes.OutlinePrimary, - onClick: () => { - // ensure form is valid - if (this.form.isValid()) { - // get the form values - const formValues = this.form.getValues(); - - // put the values into an IImageInfo object - const newImageInfo: IImageInfo = { - image: formValues.Image, - title: formValues.Title, - url: formValues.Link - }; - - // add object to the array - this.imagesInfo.push(newImageInfo); - - // Stringify because web part properties can only hold strings, numbers, and booleans - this.properties.images = JSON.stringify(this.imagesInfo); - - this.render(); - - Modal.hide(); - } - } - - } - }, - { - content: 'Close the dialog', - btnProps: { - text: 'Cancel', - type: Components.ButtonTypes.OutlineInfo, - onClick: () => { - Modal.hide() - } - - } - } - ] - }) - Modal.show() + } + }), + PropertyPaneSlider('opacity', { + label: strings.OpacityFieldLabel, + min: 0, + max: 100, + showValue: true + }), + PropertyPaneSlider('cardCols', { + label: strings.CardColFieldLabel, + min: 1, + max: 12, + showValue: true }) ] } diff --git a/src/webparts/betterHero/loc/en-us.js b/src/webparts/betterHero/loc/en-us.js index a388375..395a67c 100644 --- a/src/webparts/betterHero/loc/en-us.js +++ b/src/webparts/betterHero/loc/en-us.js @@ -3,6 +3,8 @@ define([], function () { "PropertyPaneDescription": "Description", "BasicGroupName": "Group Name", "AddImageFieldLabel": "Add link", + "OpacityFieldLabel": "Opacity of the text overlay", + "CardColFieldLabel": "Number of columns per row", "AppLocalEnvironmentSharePoint": "The app is running on your local environment as SharePoint web part", "AppLocalEnvironmentTeams": "The app is running on your local environment as Microsoft Teams app", "AppLocalEnvironmentOffice": "The app is running on your local environment in office.com", diff --git a/src/webparts/betterHero/loc/mystrings.d.ts b/src/webparts/betterHero/loc/mystrings.d.ts index da267c7..f590842 100644 --- a/src/webparts/betterHero/loc/mystrings.d.ts +++ b/src/webparts/betterHero/loc/mystrings.d.ts @@ -2,6 +2,8 @@ declare interface IBetterHeroWebPartStrings { PropertyPaneDescription: string; BasicGroupName: string; AddImageFieldLabel: string; + OpacityFieldLabel: string; + CardColFieldLabel: string; AppLocalEnvironmentSharePoint: string; AppLocalEnvironmentTeams: string; AppLocalEnvironmentOffice: string;