From fe6481c156cf4d7b02a3bc7e03614450f84321d4 Mon Sep 17 00:00:00 2001 From: Hugo Bernier Date: Sun, 21 Mar 2021 00:32:07 -0400 Subject: [PATCH 1/2] Updated readme and tslint fixes --- samples/react-image-editor/README.md | 13 +- samples/react-image-editor/assets/sample.json | 6 +- .../Filter/GrayscaleFilter.ts | 24 +- .../ImageManipulation/Filter/IImageFilter.ts | 6 +- .../ImageManipulation/Filter/SepiaFilter.ts | 30 +- .../ImageManipulation/HistoryItem.tsx | 19 +- .../ImageManipulation/ImageManipulation.tsx | 559 ++++++++++-------- .../ImageManipulation.types.tsx | 24 +- .../components/ImageCrop.tsx | 170 ++---- .../components/ImageGrid.tsx | 54 +- .../components/Interfaces.ts | 6 +- .../components/ItemOrder.tsx | 184 +++--- .../src/components/ImageManipulation/index.ts | 1 - .../ImageManipulation/loc/mystrings.d.ts | 1 - .../ReactImageEditorWebPart.manifest.json | 6 +- .../ReactImageEditorWebPart.ts | 14 +- .../components/ReactImageEditor.tsx | 41 +- samples/react-image-editor/tslint.json | 61 +- 18 files changed, 632 insertions(+), 587 deletions(-) diff --git a/samples/react-image-editor/README.md b/samples/react-image-editor/README.md index bcc29a8c0..941e671c3 100644 --- a/samples/react-image-editor/README.md +++ b/samples/react-image-editor/README.md @@ -19,9 +19,10 @@ extensions: ## Summary -This solution contains an SPFx webpart that shows an HTML Image Editor based on canvas and [Office UI Fabric](https://developer.microsoft.com/fluentui/). +This solution contains an SPFx web part that shows an HTML Image Editor based on canvas and [Office UI Fabric](https://developer.microsoft.com/fluentui/). Key features of the Editor + * Resize * Crop * Flip @@ -36,10 +37,13 @@ The Placeholder and FilePicker are components from the [sp-dev-fx-controls-react ![react-image-editor in action](assets/react-image-editor.gif) -## Used SharePoint Framework Version +## Compatibility -![version](https://img.shields.io/badge/version-1.4.0-green.svg) -> SharePoint 2019 and SharePoint Online +![SPFx 1.4.0](https://img.shields.io/badge/SPFx-1.4.0-green.svg) +![Node.js LTS 6.x](https://img.shields.io/badge/Node.js-LTS%206.x-green.svg) +![SharePoint 2016 | 2019 | Online](https://img.shields.io/badge/SharePoint-2016%20%7C%202019%20%7C%20Online-green.svg) +![Teams No: Not designed for Microsoft Teams](https://img.shields.io/badge/Teams-No-red.svg "Not designed for Microsoft Teams") +![Workbench Local | Hosted](https://img.shields.io/badge/Workbench-Local%20%7C%20Hosted-green.svg) References to office-ui-fabric-react version 5.x because of SharePoint 2019 Support @@ -86,6 +90,7 @@ Version|Date|Comments > Include any additional steps as needed. ## Usage + * PNP Placeholder control if not Configured * PNP WebpartTitle control (toggle Show/Hide in property pane) * PNP FilePicker control to pick Images (is mocked on localworkbench) diff --git a/samples/react-image-editor/assets/sample.json b/samples/react-image-editor/assets/sample.json index ec9fcdc6e..211529385 100644 --- a/samples/react-image-editor/assets/sample.json +++ b/samples/react-image-editor/assets/sample.json @@ -2,11 +2,11 @@ { "name": "pnp-sp-dev-spfx-web-parts-react-image-editor", "source": "pnp", - "title": "React Image Editor", - "shortDescription": "This solution contains an SPFx webpart that shows an HTML Image Editor based on canvas and Office UI Fabric", + "title": "Image Editor", + "shortDescription": "This solution contains an SPFx web part that shows an HTML Image Editor based on canvas and Office UI Fabric", "url": "https://github.com/pnp/sp-dev-fx-webparts/tree/master/samples/react-image-editor", "longDescription": [ - "This solution contains an SPFx webpart that shows an HTML Image Editor based on canvas and Office UI Fabric ", + "This solution contains an SPFx web part that shows an HTML Image Editor based on canvas and Office UI Fabric ", "Key features of the Editor", "* Resize", "* Crop", diff --git a/samples/react-image-editor/src/components/ImageManipulation/Filter/GrayscaleFilter.ts b/samples/react-image-editor/src/components/ImageManipulation/Filter/GrayscaleFilter.ts index 44d46babb..9a1ec1902 100644 --- a/samples/react-image-editor/src/components/ImageManipulation/Filter/GrayscaleFilter.ts +++ b/samples/react-image-editor/src/components/ImageManipulation/Filter/GrayscaleFilter.ts @@ -1,25 +1,23 @@ -import { IImageFilter } from "./IImageFilter"; - +import { IImageFilter } from './IImageFilter'; export class GrayscaleFilter implements IImageFilter { public process(imageData: ImageData, width: number, height: number, nvalue?: number, svalue?: string): ImageData { - var data = imageData.data; + const data: Uint8ClampedArray = imageData.data; - //Get length of all pixels in image each pixel made up of 4 elements for each pixel, one for Red, Green, Blue and Alpha - var arraylength = width * height * 4; - //Common formula for converting to grayscale. - //gray = 0.3*R + 0.59*G + 0.11*B - for (var i = arraylength - 1; i > 0; i -= 4) { - //R= i-3, G = i-2 and B = i-1 - //Get our gray shade using the formula - var gray = 0.3 * data[i - 3] + 0.59 * data[i - 2] + 0.11 * data[i - 1]; + // Get length of all pixels in image each pixel made up of + // 4 elements for each pixel, one for Red, Green, Blue and Alpha + const arraylength: number = width * height * 4; + // Common formula for converting to grayscale. + // gray = 0.3*R + 0.59*G + 0.11*B + for (let i: number = arraylength - 1; i > 0; i -= 4) { + // R= i-3, G = i-2 and B = i-1 + // Get our gray shade using the formula + const gray: number = 0.3 * data[i - 3] + 0.59 * data[i - 2] + 0.11 * data[i - 1]; data[i - 3] = gray; data[i - 2] = gray; data[i - 1] = gray; - } return (imageData); } - } diff --git a/samples/react-image-editor/src/components/ImageManipulation/Filter/IImageFilter.ts b/samples/react-image-editor/src/components/ImageManipulation/Filter/IImageFilter.ts index 74166c0f7..0a766e64f 100644 --- a/samples/react-image-editor/src/components/ImageManipulation/Filter/IImageFilter.ts +++ b/samples/react-image-editor/src/components/ImageManipulation/Filter/IImageFilter.ts @@ -1,6 +1,6 @@ export interface IImageFilter { - //describing function, - //parameters are in left side parenthesis, - //right side 'string' is return type + // describing function, + // parameters are in left side parenthesis, + // right side 'string' is return type process(imageData: ImageData, width: number, height: number, nvalue?: number, svalue?: string): ImageData; } diff --git a/samples/react-image-editor/src/components/ImageManipulation/Filter/SepiaFilter.ts b/samples/react-image-editor/src/components/ImageManipulation/Filter/SepiaFilter.ts index 484701e8c..49ab5a02f 100644 --- a/samples/react-image-editor/src/components/ImageManipulation/Filter/SepiaFilter.ts +++ b/samples/react-image-editor/src/components/ImageManipulation/Filter/SepiaFilter.ts @@ -1,14 +1,25 @@ -import { IImageFilter } from "./IImageFilter"; +import { IImageFilter } from './IImageFilter'; export class SepiaFilter implements IImageFilter { - private r: number[] = [0, 0, 0, 1, 1, 2, 3, 3, 3, 4, 4, 4, 5, 5, 5, 6, 6, 7, 7, 7, 7, 8, 8, 8, 9, 9, 9, 9, 10, 10, 10, 10, 11, 11, 12, 12, 12, 12, 13, 13, 13, 14, 14, 15, 15, 16, 16, 17, 17, 17, 18, 19, 19, 20, 21, 22, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 39, 40, 41, 42, 44, 45, 47, 48, 49, 52, 54, 55, 57, 59, 60, 62, 65, 67, 69, 70, 72, 74, 77, 79, 81, 83, 86, 88, 90, 92, 94, 97, 99, 101, 103, 107, 109, 111, 112, 116, 118, 120, 124, 126, 127, 129, 133, 135, 136, 140, 142, 143, 145, 149, 150, 152, 155, 157, 159, 162, 163, 165, 167, 170, 171, 173, 176, 177, 178, 180, 183, 184, 185, 188, 189, 190, 192, 194, 195, 196, 198, 200, 201, 202, 203, 204, 206, 207, 208, 209, 211, 212, 213, 214, 215, 216, 218, 219, 219, 220, 221, 222, 223, 224, 225, 226, 227, 227, 228, 229, 229, 230, 231, 232, 232, 233, 234, 234, 235, 236, 236, 237, 238, 238, 239, 239, 240, 241, 241, 242, 242, 243, 244, 244, 245, 245, 245, 246, 247, 247, 248, 248, 249, 249, 249, 250, 251, 251, 252, 252, 252, 253, 254, 254, 254, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255]; - private g: number[] = [0, 0, 1, 2, 2, 3, 5, 5, 6, 7, 8, 8, 10, 11, 11, 12, 13, 15, 15, 16, 17, 18, 18, 19, 21, 22, 22, 23, 24, 26, 26, 27, 28, 29, 31, 31, 32, 33, 34, 35, 35, 37, 38, 39, 40, 41, 43, 44, 44, 45, 46, 47, 48, 50, 51, 52, 53, 54, 56, 57, 58, 59, 60, 61, 63, 64, 65, 66, 67, 68, 69, 71, 72, 73, 74, 75, 76, 77, 79, 80, 81, 83, 84, 85, 86, 88, 89, 90, 92, 93, 94, 95, 96, 97, 100, 101, 102, 103, 105, 106, 107, 108, 109, 111, 113, 114, 115, 117, 118, 119, 120, 122, 123, 124, 126, 127, 128, 129, 131, 132, 133, 135, 136, 137, 138, 140, 141, 142, 144, 145, 146, 148, 149, 150, 151, 153, 154, 155, 157, 158, 159, 160, 162, 163, 164, 166, 167, 168, 169, 171, 172, 173, 174, 175, 176, 177, 178, 179, 181, 182, 183, 184, 186, 186, 187, 188, 189, 190, 192, 193, 194, 195, 195, 196, 197, 199, 200, 201, 202, 202, 203, 204, 205, 206, 207, 208, 208, 209, 210, 211, 212, 213, 214, 214, 215, 216, 217, 218, 219, 219, 220, 221, 222, 223, 223, 224, 225, 226, 226, 227, 228, 228, 229, 230, 231, 232, 232, 232, 233, 234, 235, 235, 236, 236, 237, 238, 238, 239, 239, 240, 240, 241, 242, 242, 242, 243, 244, 245, 245, 246, 246, 247, 247, 248, 249, 249, 249, 250, 251, 251, 252, 252, 252, 253, 254, 255]; - private b: number[] = [53, 53, 53, 54, 54, 54, 55, 55, 55, 56, 57, 57, 57, 58, 58, 58, 59, 59, 59, 60, 61, 61, 61, 62, 62, 63, 63, 63, 64, 65, 65, 65, 66, 66, 67, 67, 67, 68, 69, 69, 69, 70, 70, 71, 71, 72, 73, 73, 73, 74, 74, 75, 75, 76, 77, 77, 78, 78, 79, 79, 80, 81, 81, 82, 82, 83, 83, 84, 85, 85, 86, 86, 87, 87, 88, 89, 89, 90, 90, 91, 91, 93, 93, 94, 94, 95, 95, 96, 97, 98, 98, 99, 99, 100, 101, 102, 102, 103, 104, 105, 105, 106, 106, 107, 108, 109, 109, 110, 111, 111, 112, 113, 114, 114, 115, 116, 117, 117, 118, 119, 119, 121, 121, 122, 122, 123, 124, 125, 126, 126, 127, 128, 129, 129, 130, 131, 132, 132, 133, 134, 134, 135, 136, 137, 137, 138, 139, 140, 140, 141, 142, 142, 143, 144, 145, 145, 146, 146, 148, 148, 149, 149, 150, 151, 152, 152, 153, 153, 154, 155, 156, 156, 157, 157, 158, 159, 160, 160, 161, 161, 162, 162, 163, 164, 164, 165, 165, 166, 166, 167, 168, 168, 169, 169, 170, 170, 171, 172, 172, 173, 173, 174, 174, 175, 176, 176, 177, 177, 177, 178, 178, 179, 180, 180, 181, 181, 181, 182, 182, 183, 184, 184, 184, 185, 185, 186, 186, 186, 187, 188, 188, 188, 189, 189, 189, 190, 190, 191, 191, 192, 192, 193, 193, 193, 194, 194, 194, 195, 196, 196, 196, 197, 197, 197, 198, 199]; + // tslint:disable-next-line: max-line-length + private r: number[] = [ + 0, 0, 0, 1, 1, 2, 3, 3, 3, 4, 4, 4, 5, 5, 5, 6, 6, 7, 7, 7, 7, 8, 8, 8, 9, 9, 9, 9, 10, 10, 10, 10, 11, 11, 12, 12, 12, 12, 13, 13, 13, 14, 14, 15, 15, 16, 16, 17, 17, 17, 18, 19, 19, 20, 21, 22, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 39, 40, 41, 42, 44, 45, 47, 48, 49, 52, 54, 55, 57, 59, 60, 62, 65, 67, 69, 70, 72, 74, 77, 79, 81, 83, 86, 88, 90, 92, 94, 97, 99, 101, 103, 107, 109, 111, 112, 116, 118, 120, 124, 126, 127, 129, 133, 135, 136, 140, 142, 143, 145, 149, 150, 152, 155, 157, 159, 162, 163, 165, 167, 170, 171, 173, 176, 177, 178, 180, 183, 184, 185, 188, 189, 190, 192, 194, 195, 196, 198, 200, 201, 202, 203, 204, 206, 207, 208, 209, 211, 212, 213, 214, 215, 216, 218, 219, 219, 220, 221, 222, 223, 224, 225, 226, 227, 227, 228, 229, 229, 230, 231, 232, 232, 233, 234, 234, 235, 236, 236, 237, 238, 238, 239, 239, 240, 241, 241, 242, 242, 243, 244, 244, 245, 245, 245, 246, 247, 247, 248, 248, 249, 249, 249, 250, 251, 251, 252, 252, 252, 253, 254, 254, 254, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 + ]; + + // tslint:disable-next-line: max-line-length + private g: number[] = [ + 0, 0, 1, 2, 2, 3, 5, 5, 6, 7, 8, 8, 10, 11, 11, 12, 13, 15, 15, 16, 17, 18, 18, 19, 21, 22, 22, 23, 24, 26, 26, 27, 28, 29, 31, 31, 32, 33, 34, 35, 35, 37, 38, 39, 40, 41, 43, 44, 44, 45, 46, 47, 48, 50, 51, 52, 53, 54, 56, 57, 58, 59, 60, 61, 63, 64, 65, 66, 67, 68, 69, 71, 72, 73, 74, 75, 76, 77, 79, 80, 81, 83, 84, 85, 86, 88, 89, 90, 92, 93, 94, 95, 96, 97, 100, 101, 102, 103, 105, 106, 107, 108, 109, 111, 113, 114, 115, 117, 118, 119, 120, 122, 123, 124, 126, 127, 128, 129, 131, 132, 133, 135, 136, 137, 138, 140, 141, 142, 144, 145, 146, 148, 149, 150, 151, 153, 154, 155, 157, 158, 159, 160, 162, 163, 164, 166, 167, 168, 169, 171, 172, 173, 174, 175, 176, 177, 178, 179, 181, 182, 183, 184, 186, 186, 187, 188, 189, 190, 192, 193, 194, 195, 195, 196, 197, 199, 200, 201, 202, 202, 203, 204, 205, 206, 207, 208, 208, 209, 210, 211, 212, 213, 214, 214, 215, 216, 217, 218, 219, 219, 220, 221, 222, 223, 223, 224, 225, 226, 226, 227, 228, 228, 229, 230, 231, 232, 232, 232, 233, 234, 235, 235, 236, 236, 237, 238, 238, 239, 239, 240, 240, 241, 242, 242, 242, 243, 244, 245, 245, 246, 246, 247, 247, 248, 249, 249, 249, 250, 251, 251, 252, 252, 252, 253, 254, 255 + ]; + + // tslint:disable-next-line: max-line-length + private b: number[] = [ + 53, 53, 53, 54, 54, 54, 55, 55, 55, 56, 57, 57, 57, 58, 58, 58, 59, 59, 59, 60, 61, 61, 61, 62, 62, 63, 63, 63, 64, 65, 65, 65, 66, 66, 67, 67, 67, 68, 69, 69, 69, 70, 70, 71, 71, 72, 73, 73, 73, 74, 74, 75, 75, 76, 77, 77, 78, 78, 79, 79, 80, 81, 81, 82, 82, 83, 83, 84, 85, 85, 86, 86, 87, 87, 88, 89, 89, 90, 90, 91, 91, 93, 93, 94, 94, 95, 95, 96, 97, 98, 98, 99, 99, 100, 101, 102, 102, 103, 104, 105, 105, 106, 106, 107, 108, 109, 109, 110, 111, 111, 112, 113, 114, 114, 115, 116, 117, 117, 118, 119, 119, 121, 121, 122, 122, 123, 124, 125, 126, 126, 127, 128, 129, 129, 130, 131, 132, 132, 133, 134, 134, 135, 136, 137, 137, 138, 139, 140, 140, 141, 142, 142, 143, 144, 145, 145, 146, 146, 148, 148, 149, 149, 150, 151, 152, 152, 153, 153, 154, 155, 156, 156, 157, 157, 158, 159, 160, 160, 161, 161, 162, 162, 163, 164, 164, 165, 165, 166, 166, 167, 168, 168, 169, 169, 170, 170, 171, 172, 172, 173, 173, 174, 174, 175, 176, 176, 177, 177, 177, 178, 178, 179, 180, 180, 181, 181, 181, 182, 182, 183, 184, 184, 184, 185, 185, 186, 186, 186, 187, 188, 188, 188, 189, 189, 189, 190, 190, 191, 191, 192, 192, 193, 193, 193, 194, 194, 194, 195, 196, 196, 196, 197, 197, 197, 198, 199 + ]; private noise: number = 0; - public process(imageData: ImageData, width: number, height: number, nvalue?: number, svalue?: string): ImageData { - //var data: Uint8ClampedArray = imageData.data; + public process(imageData: ImageData, _width: number, _height: number, nvalue?: number, _svalue?: string): ImageData { + // var data: Uint8ClampedArray = imageData.data; this.noise = nvalue; - for (var i = 0; i < imageData.data.length; i += 4) { + for (let i: number = 0; i < imageData.data.length; i += 4) { // change image colors imageData.data[i] = this.r[imageData.data[i]]; imageData.data[i + 1] = this.g[imageData.data[i + 1]]; @@ -16,14 +27,13 @@ export class SepiaFilter implements IImageFilter { if (this.noise > 0) { this.noise = Math.round(this.noise - Math.random() * this.noise); - for (var j = 0; j < 3; j++) { - var iPN = this.noise + imageData.data[i + j]; + for (let j: number = 0; j < 3; j++) { + const iPN: number = this.noise + imageData.data[i + j]; imageData.data[i + j] = (iPN > 255) ? 255 : iPN; } } } - return (imageData); } } diff --git a/samples/react-image-editor/src/components/ImageManipulation/HistoryItem.tsx b/samples/react-image-editor/src/components/ImageManipulation/HistoryItem.tsx index 304428091..cf864a67d 100644 --- a/samples/react-image-editor/src/components/ImageManipulation/HistoryItem.tsx +++ b/samples/react-image-editor/src/components/ImageManipulation/HistoryItem.tsx @@ -2,18 +2,25 @@ import { Icon } from 'office-ui-fabric-react'; import * as React from 'react'; import styles from './ImageManipulation.module.scss'; -import { IImageManipulationSettings, manipulationTypeData } from './ImageManipulation.types'; +import { + IImageManipulationSettings, + manipulationTypeData, + IManipulationTypeDataDetails } from './ImageManipulation.types'; -export const historyItem = (item:IImageManipulationSettings, index:number): JSX.Element => { - if(!item) { +// tslint:disable-next-line: typedef +export const historyItem = (item: IImageManipulationSettings, _index: number): JSX.Element => { + if (!item) { return undefined; } - const data=manipulationTypeData[item.type]; + const data: IManipulationTypeDataDetails = manipulationTypeData[item.type]; - const detailrender = data.toHTML(item); + const detailrender: JSX.Element = data.toHTML(item); return ( - {data.svgIcon ? : } + {data.svgIcon ? + // tslint:disable-next-line: react-a11y-img-has-alt + : + } {data.text} {detailrender} diff --git a/samples/react-image-editor/src/components/ImageManipulation/ImageManipulation.tsx b/samples/react-image-editor/src/components/ImageManipulation/ImageManipulation.tsx index 824ca6e24..d11d3c031 100644 --- a/samples/react-image-editor/src/components/ImageManipulation/ImageManipulation.tsx +++ b/samples/react-image-editor/src/components/ImageManipulation/ImageManipulation.tsx @@ -1,6 +1,17 @@ import { DisplayMode } from '@microsoft/sp-core-library'; import { clone } from '@microsoft/sp-lodash-subset'; -import { Checkbox, DefaultButton, findIndex, Icon, IconButton, IsFocusVisibleClassName, ISlider, Panel, PanelType, Slider, TextField } from 'office-ui-fabric-react'; +import { + Checkbox, + DefaultButton, + findIndex, + Icon, + IconButton, + ISlider, + Panel, + PanelType, + Slider, + TextField +} from 'office-ui-fabric-react'; import * as React from 'react'; import ImageCrop from './components/ImageCrop'; @@ -9,12 +20,28 @@ import ItemOrder from './components/ItemOrder'; import { GrayscaleFilter } from './Filter/GrayscaleFilter'; import { SepiaFilter } from './Filter/SepiaFilter'; -import { historyItem } from './HistoryItem'; +import { historyItem } from './historyItem'; import styles from './ImageManipulation.module.scss'; -import { FilterType, filterTypeData, ICrop, ICropSettings, IFilterSettings, IFlipSettings, IImageManipulationSettings, IManipulationTypeDataDetails, IResizeSettings, IRotateSettings, IScaleSettings, ManipulationType, manipulationTypeData, SettingPanelType } from './ImageManipulation.types'; - +import { + FilterType, + filterTypeData, + ICrop, + ICropSettings, + IFilterSettings, + IFlipSettings, + IImageManipulationSettings, + IManipulationTypeDataDetails, + IResizeSettings, + IRotateSettings, + IScaleSettings, + ManipulationType, + manipulationTypeData, + SettingPanelType +} from './ImageManipulation.types'; +// tslint:disable-next-line: no-any const flipVerticalIcon: any = require('../../svg/flipVertical.svg'); +// tslint:disable-next-line: no-any const flipHorizontalIcon: any = require('../../svg/flipHorizontal.svg'); import * as strings from 'ImageManipulationStrings'; @@ -39,16 +66,14 @@ export interface IImageManipulationState { } export class ImageManipulation extends React.Component { - private img: HTMLImageElement = null; - private wrapperRef: HTMLDivElement = null; - private bufferRef: HTMLCanvasElement = null; - private bufferCtx: CanvasRenderingContext2D = null; - private canvasRef: HTMLCanvasElement = null; - private canvasCtx: CanvasRenderingContext2D = null; - private manipulateRef: HTMLCanvasElement = null; - private manipulateCtx: CanvasRenderingContext2D = null; - - + private img: HTMLImageElement = undefined; + private wrapperRef: HTMLDivElement = undefined; + private bufferRef: HTMLCanvasElement = undefined; + private bufferCtx: CanvasRenderingContext2D = undefined; + private canvasRef: HTMLCanvasElement = undefined; + private canvasCtx: CanvasRenderingContext2D = undefined; + private manipulateRef: HTMLCanvasElement = undefined; + private manipulateCtx: CanvasRenderingContext2D = undefined; constructor(props: IImageManipulationProps) { super(props); @@ -65,11 +90,12 @@ export class ImageManipulation extends React.Component { - - this.applySettings(); - }; - this.img.onerror = (event: Event | string) => { - if (this.props.imgLoadError) { - this.props.imgLoadError(); - } - }; - } - private applySettings(): void { - this.canvasRef.width = this.img.width; - this.canvasRef.height = this.img.height; - this.bufferRef.width = this.img.width; - this.bufferRef.height = this.img.height; - this.manipulateRef.width = this.img.width; - this.manipulateRef.height = this.img.height; - - let currentwidth = this.img.width; - let currentheight = this.img.height; - let newwidth = currentwidth; - let newheight = currentheight; - this.bufferCtx.clearRect(0, 0, currentwidth, currentheight); - this.bufferCtx.drawImage(this.img, 0, 0); - - - if (this.props.settings) { - this.props.settings.forEach((element, index) => { - this.manipulateCtx.clearRect(0, 0, currentwidth, currentheight); - this.manipulateRef.width = currentwidth; - this.manipulateRef.height = currentheight; - this.manipulateCtx.save(); - let nothingToDo: boolean = false; - switch (element.type) { - case ManipulationType.Flip: - const filp = element as IFlipSettings; - if (filp.flipY) { - this.manipulateCtx.translate(0, currentheight); - this.manipulateCtx.scale(1, -1); - } - if (filp.flipX) { - this.manipulateCtx.translate(currentwidth, 0); - this.manipulateCtx.scale(-1, 1); - } - this.manipulateCtx.drawImage(this.bufferRef, 0, 0); - break; - case ManipulationType.Rotate: - const rotate = element as IRotateSettings; - if (!rotate.rotate || rotate.rotate === 0 || isNaN(rotate.rotate)) { - nothingToDo = true; - break; - } - if (rotate.rotate) { - const angelcalc = rotate.rotate * Math.PI / 180; - const oldwidth = currentwidth; - const oldheight = currentheight; - let offsetwidth = 0; - let offsetheight = 0; - - var a = oldwidth * Math.abs(Math.cos(angelcalc)); - var b = oldheight * Math.abs(Math.sin(angelcalc)); - - var p = oldwidth * Math.abs(Math.sin(angelcalc)); - var q = oldheight * Math.abs(Math.cos(angelcalc)); - newwidth = a + b; - newheight = p + q; - - offsetwidth = (newwidth - oldwidth) / 2; - offsetheight = (newheight - oldheight) / 2; - - this.manipulateRef.width = newwidth; - this.manipulateRef.height = newheight; - - this.manipulateCtx.translate(newwidth / 2, newheight / 2); - this.manipulateCtx.rotate(angelcalc); - this.manipulateCtx.translate(newwidth / 2 * -1, newheight / 2 * -1); - - this.manipulateCtx.drawImage(this.bufferRef, offsetwidth, offsetheight); - - } - break; - case ManipulationType.Scale: - const scale = element as IScaleSettings; - if (scale.scale) { - this.manipulateCtx.translate(currentwidth / 2, currentheight / 2); - this.manipulateCtx.scale(scale.scale, scale.scale); - this.manipulateCtx.translate(currentwidth / 2 * -1, currentheight / 2 * -1); - - this.manipulateCtx.drawImage(this.bufferRef, 0, 0); - } - break; - case ManipulationType.Filter: - nothingToDo = true; - const filter = element as IFilterSettings; - var imageData = this.bufferCtx.getImageData(0, 0, currentwidth, currentheight); - switch (filter.filterType) { - case FilterType.Grayscale: - imageData = new GrayscaleFilter().process(imageData, currentwidth, currentheight, undefined, undefined); - break; - case FilterType.Sepia: - imageData = new SepiaFilter().process(imageData, currentwidth, currentheight, undefined, undefined); - break; - } - this.bufferCtx.putImageData(imageData, 0, 0); - break; - case ManipulationType.Crop: - const last = this.props.settings.length === index + 1; - if (last && this.state.settingPanel === SettingPanelType.Crop) { - //Do nothingis last and current edit - nothingToDo = true; - } else { - const crop = element as ICropSettings; - const sourceX = crop.sx; - const sourceY = crop.sy; - newwidth = crop.width; - newheight = crop.height; - this.manipulateRef.width = newwidth; - this.manipulateRef.height = newheight; - this.manipulateCtx.drawImage(this.bufferRef, sourceX, sourceY, newwidth, newheight, 0, 0, newwidth, newheight); - } - break; - - case ManipulationType.Resize: - const resize = element as IResizeSettings; - newwidth = resize.width; - newheight = resize.height; - this.manipulateCtx.drawImage(this.bufferRef, 0, 0); - } - this.manipulateCtx.restore(); - - if (!nothingToDo) { - this.bufferCtx.clearRect(0, 0, currentwidth, currentheight); - - this.bufferRef.width = newwidth; - this.bufferRef.height = newheight; - - this.bufferCtx.clearRect(0, 0, newwidth, newheight); - this.bufferCtx.drawImage(this.manipulateRef, 0, 0, newwidth, newheight); - - - currentwidth = newwidth; - currentheight = newheight; - } - - }); - - - } - - /*this.canvasCtx.clearRect(0, 0, this.canvasRef.width, this.canvasRef.height) - // this.canvasCtx.drawImage(this.bufferRef, 0, 0); - const sourceX = 400; - const sourceY = 200; - const sourceWidth = 1200; - const sourceHeight = 600; -this.canvasCtx.drawImage(this.bufferRef, sourceX, sourceY, sourceWidth, sourceHeight, 0, 0, this.canvasRef.width, this.canvasRef.height); -*/ - this.canvasCtx.clearRect(0, 0, currentwidth, currentheight); - this.canvasRef.width = currentwidth; - this.canvasRef.height = currentheight; - this.canvasCtx.drawImage(this.bufferRef, 0, 0); - // this.canvasCtx.drawImage(this.bufferRef, 0, 0, currentwidth, currentheight); - this.wrapperRef.style.width = currentwidth + 'px'; - //this.wrapperRef.style.height = currentheight + 'px'; - // let height = this.canvasRef.height; - // let width = this.canvasRef.width; - - //this.canvasctx.translate(this.canvasRef.width / 2 * -1, this.canvasRef.height / 2 * -1); - } - - - - public render(): React.ReactElement { return (
{this.props.displyMode === DisplayMode.Edit && this.getCommandBar()}
); } + + private imageChanged(url: string): void { + this.img = new Image(); + this.img.src = url; + this.img.crossOrigin = 'Anonymous'; + this.img.onload = () => { + + this.applySettings(); + }; + this.img.onerror = (event: Event | string) => { + if (this.props.imgLoadError) { + this.props.imgLoadError(); + } + }; + } + + private applySettings(): void { + this.canvasRef.width = this.img.width; + this.canvasRef.height = this.img.height; + this.bufferRef.width = this.img.width; + this.bufferRef.height = this.img.height; + this.manipulateRef.width = this.img.width; + this.manipulateRef.height = this.img.height; + + let currentwidth: number = this.img.width; + let currentheight: number = this.img.height; + let newwidth: number = currentwidth; + let newheight: number = currentheight; + this.bufferCtx.clearRect(0, 0, currentwidth, currentheight); + this.bufferCtx.drawImage(this.img, 0, 0); + + if (this.props.settings) { + this.props.settings.forEach((element, index) => { + this.manipulateCtx.clearRect(0, 0, currentwidth, currentheight); + this.manipulateRef.width = currentwidth; + this.manipulateRef.height = currentheight; + this.manipulateCtx.save(); + let nothingToDo: boolean = false; + switch (element.type) { + case ManipulationType.Flip: + const filp: IFlipSettings = element as IFlipSettings; + if (filp.flipY) { + this.manipulateCtx.translate(0, currentheight); + this.manipulateCtx.scale(1, -1); + } + if (filp.flipX) { + this.manipulateCtx.translate(currentwidth, 0); + this.manipulateCtx.scale(-1, 1); + } + this.manipulateCtx.drawImage(this.bufferRef, 0, 0); + break; + case ManipulationType.Rotate: + const rotate: IRotateSettings = element as IRotateSettings; + if (!rotate.rotate || rotate.rotate === 0 || isNaN(rotate.rotate)) { + nothingToDo = true; + break; + } + if (rotate.rotate) { + const angelcalc: number = rotate.rotate * Math.PI / 180; + const oldwidth: number = currentwidth; + const oldheight: number = currentheight; + let offsetwidth: number = 0; + let offsetheight: number = 0; + + const a: number = oldwidth * Math.abs(Math.cos(angelcalc)); + const b: number = oldheight * Math.abs(Math.sin(angelcalc)); + + const p: number = oldwidth * Math.abs(Math.sin(angelcalc)); + const q: number = oldheight * Math.abs(Math.cos(angelcalc)); + newwidth = a + b; + newheight = p + q; + + offsetwidth = (newwidth - oldwidth) / 2; + offsetheight = (newheight - oldheight) / 2; + + this.manipulateRef.width = newwidth; + this.manipulateRef.height = newheight; + + this.manipulateCtx.translate(newwidth / 2, newheight / 2); + this.manipulateCtx.rotate(angelcalc); + this.manipulateCtx.translate(newwidth / 2 * -1, newheight / 2 * -1); + + this.manipulateCtx.drawImage(this.bufferRef, offsetwidth, offsetheight); + } + break; + case ManipulationType.Scale: + const scale: IScaleSettings = element as IScaleSettings; + if (scale.scale) { + this.manipulateCtx.translate(currentwidth / 2, currentheight / 2); + this.manipulateCtx.scale(scale.scale, scale.scale); + this.manipulateCtx.translate(currentwidth / 2 * -1, currentheight / 2 * -1); + + this.manipulateCtx.drawImage(this.bufferRef, 0, 0); + } + break; + case ManipulationType.Filter: + nothingToDo = true; + const filter: IFilterSettings = element as IFilterSettings; + let imageData: ImageData = this.bufferCtx.getImageData(0, 0, currentwidth, currentheight); + switch (filter.filterType) { + case FilterType.Grayscale: + imageData = new GrayscaleFilter().process(imageData, currentwidth, currentheight, undefined, undefined); + break; + case FilterType.Sepia: + imageData = new SepiaFilter().process(imageData, currentwidth, currentheight, undefined, undefined); + break; + } + this.bufferCtx.putImageData(imageData, 0, 0); + break; + case ManipulationType.Crop: + const last: boolean = this.props.settings.length === index + 1; + if (last && this.state.settingPanel === SettingPanelType.Crop) { + // Do nothing is last and current edit + nothingToDo = true; + } else { + const crop: ICropSettings = element as ICropSettings; + const sourceX: number = crop.sx; + const sourceY: number = crop.sy; + newwidth = crop.width; + newheight = crop.height; + this.manipulateRef.width = newwidth; + this.manipulateRef.height = newheight; + this.manipulateCtx.drawImage( + this.bufferRef, + sourceX, + sourceY, + newwidth, + newheight, + 0, + 0, + newwidth, + newheight); + } + break; + + case ManipulationType.Resize: + const resize: IResizeSettings = element as IResizeSettings; + newwidth = resize.width; + newheight = resize.height; + this.manipulateCtx.drawImage(this.bufferRef, 0, 0); + } + this.manipulateCtx.restore(); + + if (!nothingToDo) { + this.bufferCtx.clearRect(0, 0, currentwidth, currentheight); + + this.bufferRef.width = newwidth; + this.bufferRef.height = newheight; + + this.bufferCtx.clearRect(0, 0, newwidth, newheight); + this.bufferCtx.drawImage(this.manipulateRef, 0, 0, newwidth, newheight); + + currentwidth = newwidth; + currentheight = newheight; + } + }); + } + + /*this.canvasCtx.clearRect(0, 0, this.canvasRef.width, this.canvasRef.height) + // this.canvasCtx.drawImage(this.bufferRef, 0, 0); + const sourceX = 400; + const sourceY = 200; + const sourceWidth = 1200; + const sourceHeight = 600; + this.canvasCtx.drawImage( + this.bufferRef, sourceX, sourceY, sourceWidth, + sourceHeight, 0, 0, this.canvasRef.width, this.canvasRef.height); +*/ + this.canvasCtx.clearRect(0, 0, currentwidth, currentheight); + this.canvasRef.width = currentwidth; + this.canvasRef.height = currentheight; + this.canvasCtx.drawImage(this.bufferRef, 0, 0); + // this.canvasCtx.drawImage(this.bufferRef, 0, 0, currentwidth, currentheight); + this.wrapperRef.style.width = currentwidth + 'px'; + // this.wrapperRef.style.height = currentheight + 'px'; + // let height = this.canvasRef.height; + // let width = this.canvasRef.width; + + // this.canvasctx.translate(this.canvasRef.width / 2 * -1, this.canvasRef.height / 2 * -1); + } + private getCropGrid(): JSX.Element { - const lastset = this.getLastManipulation() as ICropSettings; + const lastset: ICropSettings = this.getLastManipulation() as ICropSettings; let lastdata: ICrop = { sx: 0, sy: 0, width: 0, height: 0 }; if (lastset && lastset.type === ManipulationType.Crop) { @@ -300,7 +330,7 @@ this.canvasCtx.drawImage(this.bufferRef, sourceX, sourceY, sourceWidth, sourceHe } private getResizeGrid(): JSX.Element { - const lastset = this.getLastManipulation() as IResizeSettings; + const lastset: IResizeSettings = this.getLastManipulation() as IResizeSettings; if (lastset && lastset.type === ManipulationType.Resize) { return ( this.setResize(size.width, size.height, undefined)} - //aspect={this.getAspect()} + // aspect={this.getAspect()} width={this.canvasRef.width} height={this.canvasRef.height} />); } @@ -323,7 +353,9 @@ this.canvasCtx.drawImage(this.bufferRef, sourceX, sourceY, sourceWidth, sourceHe } private isFilterActive(type: FilterType): boolean { - return (this.props.settings && this.props.settings.filter((f) => f.type === ManipulationType.Filter && (f as IFilterSettings).filterType === type).length > 0); + return (this.props.settings && this.props.settings.filter( + (f) => f.type === ManipulationType.Filter + && (f as IFilterSettings).filterType === type).length > 0); } private closeFilter(): void { this.setState({ @@ -379,7 +411,7 @@ this.canvasCtx.drawImage(this.bufferRef, sourceX, sourceY, sourceWidth, sourceHe }, () => this.toggleEditMode(true)); } - private toggleEditMode(mode: boolean) { + private toggleEditMode(mode: boolean): void { if (this.props.editMode) { this.props.editMode(mode); } @@ -408,8 +440,6 @@ this.canvasCtx.drawImage(this.bufferRef, sourceX, sourceY, sourceWidth, sourceHe this.props.settingschanged(newhist); } } - - }} onRenderItem={historyItem} />); @@ -431,10 +461,12 @@ this.canvasCtx.drawImage(this.bufferRef, sourceX, sourceY, sourceWidth, sourceHe } private toggleFilter(type: FilterType, nvalue: number = undefined, svalue: string = undefined): void { - let tmpsettings = clone(this.props.settings); + let tmpsettings: IImageManipulationSettings[] = clone(this.props.settings); if (!tmpsettings) { tmpsettings = []; } - if (tmpsettings.filter((f) => f.type === ManipulationType.Filter && (f as IFilterSettings).filterType === type).length > 0) { - const removeindex = findIndex(tmpsettings, (f) => f.type === ManipulationType.Filter && (f as IFilterSettings).filterType === type); + if (tmpsettings.filter( + (f) => f.type === ManipulationType.Filter && (f as IFilterSettings).filterType === type).length > 0) { + const removeindex: number = findIndex(tmpsettings, + (f) => f.type === ManipulationType.Filter && (f as IFilterSettings).filterType === type); tmpsettings.splice(removeindex, 1); } else { tmpsettings.push({ @@ -453,12 +485,15 @@ this.canvasCtx.drawImage(this.bufferRef, sourceX, sourceY, sourceWidth, sourceHe return (
{ return (); }} + onRenderIcon={() => { + // tslint:disable-next-line: react-a11y-img-has-alt + return (); + }} title={strings.FlipHorizontal} ariaLabel={strings.FlipHorizontal} onClick={() => { - let last = this.getLastManipulation(); + const last: IImageManipulationSettings = this.getLastManipulation(); if (last && last.type === ManipulationType.Flip) { (last as IFlipSettings).flipX = !(last as IFlipSettings).flipX; if ((last as IFlipSettings).flipX === false && @@ -473,11 +508,14 @@ this.canvasCtx.drawImage(this.bufferRef, sourceX, sourceY, sourceWidth, sourceHe }} /> { return (); }} + onRenderIcon={() => { + // tslint:disable-next-line: react-a11y-img-has-alt + return (); + }} title={strings.FlipVertical} ariaLabel={strings.FlipVertical} onClick={() => { - let last = this.getLastManipulation(); + const last: IImageManipulationSettings = this.getLastManipulation(); if (last && last.type === ManipulationType.Flip) { (last as IFlipSettings).flipY = !(last as IFlipSettings).flipY; if ((last as IFlipSettings).flipX === false && @@ -495,8 +533,8 @@ this.canvasCtx.drawImage(this.bufferRef, sourceX, sourceY, sourceWidth, sourceHe
); } private getRotateSettings(): JSX.Element { - const lastvalue = this.getLastManipulation(); - let rotatevalue = 0; + const lastvalue: IImageManipulationSettings = this.getLastManipulation(); + let rotatevalue: number = 0; if (lastvalue && lastvalue.type === ManipulationType.Rotate) { rotatevalue = (lastvalue as IRotateSettings).rotate ? (lastvalue as IRotateSettings).rotate : 0; } @@ -506,7 +544,6 @@ this.canvasCtx.drawImage(this.bufferRef, sourceX, sourceY, sourceWidth, sourceHe let icon: string = 'CompassNW'; if (value !== 0) { icon = 'Rotate'; } - return ( { @@ -522,7 +559,6 @@ this.canvasCtx.drawImage(this.bufferRef, sourceX, sourceY, sourceWidth, sourceHe {'' + value}); })} -
{ - //Initial Value has a bug 0 is min value only min value is negative - const correctBugComponent = component as any; - if (correctBugComponent && correctBugComponent.state && correctBugComponent.value != correctBugComponent.props.value) { + // Initial Value has a bug 0 is min value only min value is negative + // tslint:disable-next-line: no-any + const correctBugComponent: any = component as any; + if (correctBugComponent + && correctBugComponent.state + && correctBugComponent.value !== correctBugComponent.props.value) { correctBugComponent.setState({ value: 0, renderedValue: 0 }); } }} - //originFromZero + // originFromZero /> - this.setCrop(parseInt(x), undefined, undefined, undefined, crop.aspect)} /> - this.setCrop(undefined, parseInt(y), undefined, undefined, crop.aspect)} /> - this.setCrop(undefined, undefined, parseInt(w), undefined, crop.aspect)} /> - this.setCrop(undefined, undefined, undefined, parseInt(h), crop.aspect)} /> - + this.setCrop(parseInt(x), undefined, undefined, undefined, crop.aspect)} /> + this.setCrop(undefined, parseInt(y), undefined, undefined, crop.aspect)} /> + this.setCrop(undefined, undefined, parseInt(w), undefined, crop.aspect)} /> + this.setCrop(undefined, undefined, undefined, parseInt(h), crop.aspect)} />
); } private getResizeSettings(): JSX.Element { - let resize: IResizeSettings = this.getResizeValues(); + const resize: IResizeSettings = this.getResizeValues(); return (
- this.setResize(parseInt(w), undefined, resize.aspect)} /> - this.setResize(undefined, parseInt(h), resize.aspect)} /> + this.setResize(parseInt(w), undefined, resize.aspect)} + /> + this.setResize(undefined, parseInt(h), resize.aspect)} + />
); } @@ -604,10 +665,11 @@ this.canvasCtx.drawImage(this.bufferRef, sourceX, sourceY, sourceWidth, sourceHe } private getScaleSettings(): JSX.Element { - const lastvalue = this.getLastManipulation(); - let scalevalue = 1; + const lastvalue: IImageManipulationSettings = this.getLastManipulation(); + let scalevalue: number = 1; if (lastvalue && lastvalue.type === ManipulationType.Scale) { - scalevalue = (lastvalue as IScaleSettings).scale ? (lastvalue as IScaleSettings).scale : 1; + scalevalue = (lastvalue as IScaleSettings).scale ? + (lastvalue as IScaleSettings).scale : 1; } return (
@@ -633,7 +695,7 @@ this.canvasCtx.drawImage(this.bufferRef, sourceX, sourceY, sourceWidth, sourceHe } private getResizeValues(): IResizeSettings { - let state: IImageManipulationSettings = this.getLastManipulation(); + const state: IImageManipulationSettings = this.getLastManipulation(); let values: IResizeSettings = { type: ManipulationType.Resize, height: this.bufferRef.height, @@ -646,7 +708,7 @@ this.canvasCtx.drawImage(this.bufferRef, sourceX, sourceY, sourceWidth, sourceHe } private setResize(width: number, height: number, aspect: number): void { - let values: IResizeSettings = this.getResizeValues(); + const values: IResizeSettings = this.getResizeValues(); if (width) { values.width = width; if (aspect) { @@ -664,7 +726,7 @@ this.canvasCtx.drawImage(this.bufferRef, sourceX, sourceY, sourceWidth, sourceHe } private getCropValues(): ICropSettings { - let state: IImageManipulationSettings = this.getLastManipulation(); + const state: IImageManipulationSettings = this.getLastManipulation(); let values: ICropSettings = { type: ManipulationType.Crop, sx: 0, @@ -679,7 +741,7 @@ this.canvasCtx.drawImage(this.bufferRef, sourceX, sourceY, sourceWidth, sourceHe } private setCrop(sx: number, sy: number, width: number, height: number, aspect: number): void { - let values = this.getCropValues(); + const values: ICropSettings = this.getCropValues(); const currentheight: number = this.bufferRef.height; const currentwidth: number = this.bufferRef.width; if (!isNaN(sx) && sx >= 0) { @@ -722,22 +784,21 @@ this.canvasCtx.drawImage(this.bufferRef, sourceX, sourceY, sourceWidth, sourceHe } } if (isNaN(values.aspect) && !isNaN(aspect)) { - //aspect added + // aspect added - //limit w + // limit w if ((values.width + values.sx) > currentwidth) { values.width = currentwidth - values.sx; } values.height = values.width / aspect; - //limit h adn recalulate w + // limit h adn recalulate w if ((values.height + values.sy) > currentheight) { values.height = currentheight - values.sy; values.width = values.height * aspect; } - - } + values.aspect = aspect; if (aspect && (!isNaN(sx) || !isNaN(width))) { values.height = values.width / aspect; @@ -762,8 +823,8 @@ this.canvasCtx.drawImage(this.bufferRef, sourceX, sourceY, sourceWidth, sourceHe }); } private calcRotate(value: number): void { - const lastVal = this.getLastManipulation(); - let cvalue = 0; + const lastVal: IImageManipulationSettings = this.getLastManipulation(); + let cvalue: number = 0; if (lastVal && lastVal.type === ManipulationType.Rotate) { cvalue = (lastVal as IRotateSettings).rotate; } @@ -810,18 +871,17 @@ this.canvasCtx.drawImage(this.bufferRef, sourceX, sourceY, sourceWidth, sourceHe return undefined; } private addOrUpdateLastManipulation(changed: IImageManipulationSettings): void { - let state = clone(this.props.settings); + let state: IImageManipulationSettings[] = clone(this.props.settings); if (!state) { state = []; } if (state.length > 0 && state[state.length - 1].type === changed.type) { state[state.length - 1] = changed; - } else { state.push(changed); - } + if (this.state.redosettings && this.state.redosettings.length > 0) { this.setState({ redosettings: [] }, () => { if (this.props.settingschanged) { @@ -837,7 +897,7 @@ this.canvasCtx.drawImage(this.bufferRef, sourceX, sourceY, sourceWidth, sourceHe private removeLastManipulation(): void { if (this.props.settings && this.props.settings.length > 0) { - let state = clone(this.props.settings); + const state: IImageManipulationSettings[] = clone(this.props.settings); state.splice(state.length - 1, 1); if (this.props.settingschanged) { this.props.settingschanged(clone(state)); @@ -850,6 +910,7 @@ this.canvasCtx.drawImage(this.bufferRef, sourceX, sourceY, sourceWidth, sourceHe iconProps={{ iconName: options.iconName }} onRenderIcon={(p, defaultrenderer) => { if (options.svgIcon) { + // tslint:disable-next-line: react-a11y-img-has-alt return (); } return defaultrenderer(p); @@ -875,9 +936,9 @@ this.canvasCtx.drawImage(this.bufferRef, sourceX, sourceY, sourceWidth, sourceHe ariaLabel={strings.CommandBarUndo} disabled={!this.props.settings || this.props.settings.length < 1} onClick={() => { - const settings = clone(this.props.settings); - const last = settings.pop(); - const redo = clone(this.state.redosettings); + const settings: IImageManipulationSettings[] = clone(this.props.settings); + const last: IImageManipulationSettings = settings.pop(); + const redo: IImageManipulationSettings[] = clone(this.state.redosettings); redo.push(last); this.setState({ redosettings: redo }, () => { @@ -894,9 +955,9 @@ this.canvasCtx.drawImage(this.bufferRef, sourceX, sourceY, sourceWidth, sourceHe ariaLabel={strings.CommandBarRedo} disabled={!this.state.redosettings || this.state.redosettings.length < 1} onClick={() => { - const redosettings = clone(this.state.redosettings); - const redo = redosettings.pop(); - const settings = clone(this.props.settings); + const redosettings: IImageManipulationSettings[] = clone(this.state.redosettings); + const redo: IImageManipulationSettings = redosettings.pop(); + const settings: IImageManipulationSettings[] = clone(this.props.settings); settings.push(redo); this.setState({ redosettings: redosettings }, () => { @@ -930,7 +991,7 @@ this.canvasCtx.drawImage(this.bufferRef, sourceX, sourceY, sourceWidth, sourceHe onClick={() => this.openPanel(SettingPanelType.History)} /> { return (); - //return ({`X:${item.sx} Y:${item.sy}`}); + // return ({`X:${item.sx} Y:${item.sy}`}); }, settingPanelType: SettingPanelType.Crop }, @@ -153,8 +162,5 @@ export const manipulationTypeData: IManipulationTypeData = { svgIcon: resizeIcon, toHTML: (item: IResizeSettings) => { return (); }, settingPanelType: SettingPanelType.Resize - }, - + } }; - - diff --git a/samples/react-image-editor/src/components/ImageManipulation/components/ImageCrop.tsx b/samples/react-image-editor/src/components/ImageManipulation/components/ImageCrop.tsx index bd14ae797..05a854413 100644 --- a/samples/react-image-editor/src/components/ImageManipulation/components/ImageCrop.tsx +++ b/samples/react-image-editor/src/components/ImageManipulation/components/ImageCrop.tsx @@ -1,5 +1,3 @@ -import { noWrap } from 'office-ui-fabric-react'; -import { IPosition } from 'office-ui-fabric-react/lib-es2015/utilities/positioning'; import * as React from 'react'; import { ICrop } from '../ImageManipulation.types'; @@ -7,11 +5,10 @@ import { nodePoition } from './Enums'; import styles from './ImageCrop.module.scss'; import { ICropData, IMousePosition } from './Interfaces'; -function clamp(num, min, max) { +function clamp(num: number, min: number, max: number): number { return Math.min(Math.max(num, min), max); } - export interface IImageCropProps { crop: ICrop; @@ -21,7 +18,8 @@ export interface IImageCropProps { onDragStart?: (e: MouseEvent) => void; onComplete?: (crop: ICrop) => void; onChange?: (crop: ICrop) => void; - onDragEnd?: (e) => void; + // tslint:disable-next-line: no-any + onDragEnd?: (e: any) => void; } export interface IImageCropState { @@ -30,17 +28,14 @@ export interface IImageCropState { reloadtimestamp: string; } - - - - // Feature detection +// tslint:disable-next-line: max-line-length // https://developer.mozilla.org/en-US/docs/Web/API/EventTarget/addEventListener#Improving_scrolling_performance_with_passive_listeners -let passiveSupported = false; -export default class ImageCrop extends React.Component { +export default class ImageCrop extends + React.Component { - private controlRef: HTMLDivElement = null; + private controlRef: HTMLDivElement = undefined; private dragStarted: boolean = false; private mouseDownOnCrop: boolean = false; @@ -61,31 +56,24 @@ export default class ImageCrop extends React.Component { const { crop } = this.props; - const { cropIsActive, newCropIsBeingDrawn } = this.state; - const cropSelection = this.isValid(crop) && this.controlRef ? this.createSelectionGrid() : null; + const cropSelection: JSX.Element = this.isValid(crop) && this.controlRef ? this.createSelectionGrid() : undefined; + // tslint:disable:react-a11y-event-has-role return (
); + // tslint: } private createSelectionGrid(): JSX.Element { const { showRuler } = this.props; - const style = this.getCropStyle(); + const style: { top: string, left: string, width: string, height: string } = this.getCropStyle(); + // tslint:disable:react-a11y-event-has-role return (
-
@@ -139,7 +128,6 @@ export default class ImageCrop extends React.Component
- {showRuler && (
@@ -148,6 +136,7 @@ export default class ImageCrop extends React.Component ); + // tslint:enable } private makeNewCrop(): ICrop { @@ -155,32 +144,18 @@ export default class ImageCrop extends React.Component | any): void { const { crop, onChange, onDragStart } = this.props; if (!this.mouseDownOnCrop) { @@ -191,15 +166,12 @@ export default class ImageCrop extends React.Component this.props.sourceHeight) { - calcCrop.height = this.props.sourceHeight - calcCrop.sy; - calcCrop.width = calcCrop.height * this.props.crop.aspect; - } - - if (calcCrop.sx + calcCrop.width > this.props.sourceWidth) { - calcCrop.width = this.props.sourceWidth - calcCrop.sx; - calcCrop.height = calcCrop.width / this.props.crop.aspect; - } - - return calcCrop; - } - private resolveCrop(pixelCrop: ICrop) { - if (this.props.crop.aspect && (!pixelCrop.width || !pixelCrop.height)) { - return this.makeAspectCrop(pixelCrop); - } - return pixelCrop; - } - + // tslint:disable-next-line: no-any private onMouseTouchDown(e: MouseEvent | any): void { const { crop, onChange } = this.props; e.preventDefault(); // Stop drag selection. - const mousepos = this.getClientPos(e); + const mousepos: IMousePosition = this.getClientPos(e); - let refpos = this.controlRef.getBoundingClientRect(); - let startx: number = mousepos.x - refpos.left; - let starty: number = mousepos.y - refpos.top; - //is mousePos in current pos + // tslint:disable-next-line: no-any + const refpos: any = this.controlRef.getBoundingClientRect(); + const startx: number = mousepos.x - refpos.left; + const starty: number = mousepos.y - refpos.top; + // is mousePos in current pos if (crop) { if (crop.sx - 5 <= startx && crop.sx + crop.width + 5 >= startx && crop.sy - 5 <= starty && crop.sy + crop.height + 5 >= starty ) { - //Position in current crop do Nothing + // Position in current crop do Nothing return; } } @@ -479,7 +420,7 @@ export default class ImageCrop extends React.Component void; onComplete?: (size: IResize) => void; + // tslint:disable-next-line: no-any onDragEnd?: (e: MouseEvent | any) => void; + // tslint:disable-next-line: no-any onDragStart?: (e: MouseEvent | any) => void; } @@ -30,7 +31,7 @@ export interface IResizeData { export default class ImageGrid extends React.Component { - private evData: IResizeData = null; + private evData: IResizeData = undefined; private dragStarted: boolean = false; constructor(props: IImageGridProps) { super(props); @@ -43,22 +44,23 @@ export default class ImageGrid extends React.Component { + // tslint:disable:react-a11y-event-has-role return (
); + // tslint:enable } + // tslint:disable-next-line: no-any private onStartResizing(e: MouseEvent | any): void { - const mousePos = this.getClientPos(e); + const mousePos: IMousePosition = this.getClientPos(e); let xInversed: boolean = false; let yInversed: boolean = false; const { ord } = e.target.dataset; @@ -141,8 +145,9 @@ export default class ImageGrid extends React.Component | any): void { - const { aspect ,onChange } = this.props; + const { aspect, onChange } = this.props; if (!this.dragStarted) { return; } @@ -151,29 +156,35 @@ export default class ImageGrid extends React.Component; textProperty?: string; moveUpIconName: string; @@ -14,45 +15,47 @@ export interface IItemOrderProps { disableDragAndDrop: boolean; removeArrows: boolean; maxHeight?: number; + // tslint:disable-next-line: no-any valueChanged: (newValue: Array) => void; + // tslint:disable-next-line: no-any onRenderItem?: (item: any, index: number) => JSX.Element; } export interface IItemOrderState { + // tslint:disable-next-line: no-any items: Array; } - export default class ItemOrder extends React.Component { + // tslint:disable-next-line: no-any private _draggedItem: any; private _selection: ISelection; private _ddHelper: DragDropHelper; private _refs: Array; + // tslint:disable-next-line: no-any private _ddSubs: Array; private _lastBox: HTMLElement; - constructor(props: IItemOrderProps) { super(props); - this._selection = null; + this._selection = undefined; this._ddHelper = new DragDropHelper({ selection: this._selection }); this._refs = new Array(); + // tslint:disable-next-line: no-any this._ddSubs = new Array(); - this._draggedItem = null; + this._draggedItem = undefined; this.state = { items: [] }; } - - public render(): JSX.Element { const { items @@ -60,9 +63,12 @@ export default class ItemOrder extends React.Component {this.props.label && } -
    +
      { (items && items.length > 0) && ( + // tslint:disable-next-line: no-any items.map((value: any, index: number) => { return (
    • 0) &&
      { this._lastBox = ref; }} /> + (items && items.length > 0) &&
      { this._lastBox = ref; }} /> }
); } - private renderItem(item: any, index: number): JSX.Element { - return ( -
-
- {this.renderDisplayValue(item, index)} -
- {!this.props.removeArrows && -
{this.renderArrows(index)}
- } -
- ); - } - - private renderDisplayValue(item: any, index: number): JSX.Element { - if (typeof this.props.onRenderItem === "function") { - return this.props.onRenderItem(item, index); - } else { - return ( - {this.props.textProperty ? item[this.props.textProperty] : item.toString()} - ); - } - } - - private renderArrows(index: number): JSX.Element { - let arrowButtonStyles: Partial = { - root: { - width: '14px', - height: '100%', - display: 'inline-block !important' - }, - rootDisabled: { - backgroundColor: 'transparent' - }, - icon: { - fontSize: "10px" - } - }; - - return ( -
- { this.onMoveUpClick(index); }} - styles={arrowButtonStyles} - /> - { this.onMoveDownClick(index); }} - styles={arrowButtonStyles} - /> -
- ); - } - public componentWillMount(): void { this.setState({ items: this.props.items || [] @@ -167,6 +119,64 @@ export default class ItemOrder extends React.Component +
+ {this.renderDisplayValue(item, index)} +
+ {!this.props.removeArrows && +
{this.renderArrows(index)}
+ } +
+ ); + } + + // tslint:disable-next-line: no-any + private renderDisplayValue(item: any, index: number): JSX.Element { + if (typeof this.props.onRenderItem === 'function') { + return this.props.onRenderItem(item, index); + } else { + return ( + {this.props.textProperty ? item[this.props.textProperty] : item.toString()} + ); + } + } + + private renderArrows(index: number): JSX.Element { + const arrowButtonStyles: Partial = { + root: { + width: '14px', + height: '100%', + display: 'inline-block !important' + }, + rootDisabled: { + backgroundColor: 'transparent' + }, + icon: { + fontSize: '10px' + } + }; + + return ( +
+ { this.onMoveUpClick(index); }} + styles={arrowButtonStyles} + /> + { this.onMoveDownClick(index); }} + styles={arrowButtonStyles} + /> +
+ ); + } + private registerRef = (ref: HTMLElement): void => { this._refs.push(ref); } @@ -177,7 +187,8 @@ export default class ItemOrder extends React.Component { + // tslint:disable-next-line: no-any + callback: (context: IDragDropContext, _event?: any) => { this._draggedItem = context.data; }, eventName: 'dragstart' @@ -185,20 +196,22 @@ export default class ItemOrder extends React.Component { + updateDropState: (isDropping: boolean, _event: DragEvent) => { if (isDropping) { value.classList.add(styles.dragEnter); } else { value.classList.remove(styles.dragEnter); } }, - canDrop: (dropContext?: IDragDropContext, dragContext?: IDragDropContext) => { + canDrop: (_dropContext?: IDragDropContext, _dragContext?: IDragDropContext) => { return true; }, - canDrag: (item?: any) => { + // tslint:disable-next-line: no-any + canDrag: (_item?: any) => { return true; }, - onDrop: (item?: any, event?: DragEvent) => { + // tslint:disable-next-line: no-any + onDrop: (item?: any, _event?: DragEvent) => { if (this._draggedItem) { this.insertBeforeItem(item); } @@ -207,14 +220,15 @@ export default class ItemOrder extends React.Component { - this._draggedItem = null; + // tslint:disable-next-line: no-any + onDragEnd: (_item?: any, _event?: DragEvent) => { + this._draggedItem = undefined; } })); }); - //Create dropable area below list to allow items to be dragged to the bottom - if (this._refs.length && typeof this._lastBox !== "undefined") { + // Create droppable area below list to allow items to be dragged to the bottom + if (this._refs.length && typeof this._lastBox !== 'undefined') { this._ddSubs.push(this._ddHelper.subscribe(this._lastBox, new EventGroup(this._lastBox), { selectionIndex: this._refs.length, context: { data: {}, index: this._refs.length }, @@ -225,12 +239,13 @@ export default class ItemOrder extends React.Component { + canDrop: (_dropContext?: IDragDropContext, _dragContext?: IDragDropContext) => { return true; }, - onDrop: (item?: any, event?: DragEvent) => { + // tslint:disable-next-line: no-any + onDrop: (_item?: any, _event?: DragEvent) => { if (this._draggedItem) { - let itemIndex: number = this.state.items.indexOf(this._draggedItem); + const itemIndex: number = this.state.items.indexOf(this._draggedItem); this.moveItemAtIndexToTargetIndex(itemIndex, this.state.items.length - 1); } } @@ -241,13 +256,15 @@ export default class ItemOrder extends React.Component { while (this._ddSubs.length) { - let sub: any = this._ddSubs.pop(); + // tslint:disable-next-line: no-any + const sub: any = this._ddSubs.pop(); sub.dispose(); } } + // tslint:disable-next-line: no-any private insertBeforeItem = (item: any) => { - let itemIndex: number = this.state.items.indexOf(this._draggedItem); + const itemIndex: number = this.state.items.indexOf(this._draggedItem); let targetIndex: number = this.state.items.indexOf(item); if (itemIndex < targetIndex) { targetIndex -= 1; @@ -255,7 +272,6 @@ export default class ItemOrder extends React.Component { if (itemIndex > 0) { this.moveItemAtIndexToTargetIndex(itemIndex, itemIndex - 1); @@ -269,8 +285,12 @@ export default class ItemOrder extends React.Component { - if (itemIndex !== targetIndex && itemIndex > -1 && targetIndex > -1 && itemIndex < this.state.items.length && targetIndex < this.state.items.length) { - let items: Array = this.state.items; + if (itemIndex !== targetIndex + && itemIndex > -1 && targetIndex > -1 + && itemIndex < this.state.items.length + && targetIndex < this.state.items.length) { + // tslint:disable-next-line: no-any + const items: Array = this.state.items; items.splice(targetIndex, 0, ...items.splice(itemIndex, 1)[0]); this.setState({ diff --git a/samples/react-image-editor/src/components/ImageManipulation/index.ts b/samples/react-image-editor/src/components/ImageManipulation/index.ts index 4f9d2e702..75a1788e0 100644 --- a/samples/react-image-editor/src/components/ImageManipulation/index.ts +++ b/samples/react-image-editor/src/components/ImageManipulation/index.ts @@ -10,4 +10,3 @@ export { IFilterSettings, IRotateSettings, IScaleSettings, IFlipSettings, ICropSettings, IResizeSettings, FilterType } from './ImageManipulation.types'; - diff --git a/samples/react-image-editor/src/components/ImageManipulation/loc/mystrings.d.ts b/samples/react-image-editor/src/components/ImageManipulation/loc/mystrings.d.ts index df65f3b6a..0bbb598ed 100644 --- a/samples/react-image-editor/src/components/ImageManipulation/loc/mystrings.d.ts +++ b/samples/react-image-editor/src/components/ImageManipulation/loc/mystrings.d.ts @@ -7,7 +7,6 @@ declare interface IImageManipulationStrings { ManipulationTypeCrop: string; ManipulationTypeResize: string; - FilterTypeGrayscale: string; FilterTypeSepia: string; diff --git a/samples/react-image-editor/src/webparts/reactImageEditor/ReactImageEditorWebPart.manifest.json b/samples/react-image-editor/src/webparts/reactImageEditor/ReactImageEditorWebPart.manifest.json index 4d4a254ef..768987eae 100644 --- a/samples/react-image-editor/src/webparts/reactImageEditor/ReactImageEditorWebPart.manifest.json +++ b/samples/react-image-editor/src/webparts/reactImageEditor/ReactImageEditorWebPart.manifest.json @@ -4,17 +4,13 @@ "alias": "ReactImageEditorWebPart", "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, "preconfiguredEntries": [{ - "groupId": "5c03119e-3074-46fd-976b-c60198311f70", // Other + "groupId": "5c03119e-3074-46fd-976b-c60198311f70", "group": { "default": "Other" }, "title": { "default": "react-image-editor" }, "description": { "default": "react-image-editor description" }, diff --git a/samples/react-image-editor/src/webparts/reactImageEditor/ReactImageEditorWebPart.ts b/samples/react-image-editor/src/webparts/reactImageEditor/ReactImageEditorWebPart.ts index 41a9993c9..89971ca2e 100644 --- a/samples/react-image-editor/src/webparts/reactImageEditor/ReactImageEditorWebPart.ts +++ b/samples/react-image-editor/src/webparts/reactImageEditor/ReactImageEditorWebPart.ts @@ -4,7 +4,6 @@ import { Version } from '@microsoft/sp-core-library'; import { BaseClientSideWebPart, IPropertyPaneConfiguration, - PropertyPaneTextField, PropertyPaneToggle } from '@microsoft/sp-webpart-base'; @@ -12,8 +11,6 @@ import * as strings from 'ReactImageEditorWebPartStrings'; import ReactImageEditor, { IReactImageEditorBaseProps, IReactImageEditorProps } from './components/ReactImageEditor'; import { IImageManipulationSettings } from '../../components'; - - export interface IReactImageEditorWebPartProps extends IReactImageEditorBaseProps { } @@ -32,11 +29,12 @@ export default class ReactImageEditorWebPart extends BaseClientSideWebPart {this.properties.title = value;}, + updateTitleProperty: (value: string) => { this.properties.title = value; }, updateUrlProperty: (value: string) => { - if(this.properties.url !== value) + // tslint:disable-next-line: curly + if (this.properties.url !== value) this.properties.url = value; - this.properties.settings=[]; + this.properties.settings = []; this.render(); }, updateManipulationSettingsProperty: (value: IImageManipulationSettings[]) => { @@ -58,7 +56,6 @@ export default class ReactImageEditorWebPart extends BaseClientSideWebPart void; updateUrlProperty: (value: string) => void; updateManipulationSettingsProperty: (value: IImageManipulationSettings[]) => void; } -export interface IReactImageEditorBaseProps { - showTitle: boolean; - title: string; - url?: string - settings?: IImageManipulationSettings[]; -} export interface IReactImageEditorState { - isFilePickerOpen: boolean, + isFilePickerOpen: boolean; statekey: string; } @@ -36,7 +34,7 @@ export default class ReactImageEditor extends React.Component { const { url, settings } = this.props; const { isFilePickerOpen } = this.state; - const isConfigured = !!url && url.length > 0; + const isConfigured: boolean = !!url && url.length > 0; return (
@@ -54,16 +52,16 @@ export default class ReactImageEditor extends React.Component { - this.setState({ isFilePickerOpen: false }, () => this._onUrlChanged(filePickerResult.fileAbsoluteUrl)) + this.setState({ isFilePickerOpen: false }, () => this._onUrlChanged(filePickerResult.fileAbsoluteUrl)); }} onCancel={() => { - this.setState({ isFilePickerOpen: false }) + this.setState({ isFilePickerOpen: false }); }} onChanged={(filePickerResult: IFilePickerResult) => { - this.setState({ isFilePickerOpen: false }, () => this._onUrlChanged(filePickerResult.fileAbsoluteUrl)) + this.setState({ isFilePickerOpen: false }, () => this._onUrlChanged(filePickerResult.fileAbsoluteUrl)); }} context={this.props.context} @@ -92,7 +90,10 @@ export default class ReactImageEditor extends React.Component { if (Environment.type === EnvironmentType.Local) { this.setState({ isFilePickerOpen: false }, () => { - this._onUrlChanged('https://media.gettyimages.com/photos/whitewater-paddlers-descend-vertical-waterfall-in-kayak-picture-id1256321293?s=2048x2048'); + this._onUrlChanged( + 'https://media.gettyimages.com/photos/' + + 'whitewater-paddlers-descend-vertical-waterfall-in-kayak-picture-id1256321293?s=2048x2048' + ); }); } else { this.setState({ isFilePickerOpen: true }); diff --git a/samples/react-image-editor/tslint.json b/samples/react-image-editor/tslint.json index 6bfc75a4c..4fa8d07b3 100644 --- a/samples/react-image-editor/tslint.json +++ b/samples/react-image-editor/tslint.json @@ -1,30 +1,31 @@ -{ - "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 - } -} \ No newline at end of file +{ + "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, + "react-a11y-event-has-role": true + } +} From d57fa123194fddcd5c6a58defbeabaa5de26ead4 Mon Sep 17 00:00:00 2001 From: Hugo Bernier Date: Sun, 21 Mar 2021 00:32:39 -0400 Subject: [PATCH 2/2] Fixed typo --- samples/react-image-editor/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/samples/react-image-editor/README.md b/samples/react-image-editor/README.md index 941e671c3..0304b606a 100644 --- a/samples/react-image-editor/README.md +++ b/samples/react-image-editor/README.md @@ -93,7 +93,7 @@ Version|Date|Comments * PNP Placeholder control if not Configured * PNP WebpartTitle control (toggle Show/Hide in property pane) -* PNP FilePicker control to pick Images (is mocked on localworkbench) +* PNP FilePicker control to pick Images (is mocked on local workbench) * Office UI Fabric