Updated readme and tslint fixes
This commit is contained in:
parent
bea779b5fc
commit
fe6481c156
|
@ -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)
|
||||
|
|
|
@ -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",
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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 (
|
||||
<span className={styles.historyItem}>
|
||||
<span className={styles.historyItemIcon}>{data.svgIcon ? <img className={styles.historyItemSvg} src={data.svgIcon} /> : <Icon iconName={data.iconName} />}</span>
|
||||
<span className={styles.historyItemIcon}>{data.svgIcon ?
|
||||
// tslint:disable-next-line: react-a11y-img-has-alt
|
||||
<img className={styles.historyItemSvg} src={data.svgIcon} /> :
|
||||
<Icon iconName={data.iconName} />}</span>
|
||||
<span className={styles.historyItemText}>{data.text}</span>
|
||||
<span className={styles.historyItemDetails}>{detailrender}</span>
|
||||
</span>
|
||||
|
|
|
@ -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<IImageManipulationProps, IImageManipulationState> {
|
||||
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<IImageManipulationProps,
|
|||
this.setManipulateRef = this.setManipulateRef.bind(this);
|
||||
this.setScale = this.setScale.bind(this);
|
||||
this.closeFilter = this.closeFilter.bind(this);
|
||||
|
||||
}
|
||||
|
||||
public componentDidMount(): void {
|
||||
this.imageChanged(this.props.src);
|
||||
}
|
||||
|
||||
public componentDidUpdate(prevProps: IImageManipulationProps): void {
|
||||
if (prevProps.src !== this.props.src) {
|
||||
this.imageChanged(this.props.src);
|
||||
|
@ -78,187 +104,10 @@ export class ImageManipulation extends React.Component<IImageManipulationProps,
|
|||
}
|
||||
}
|
||||
|
||||
private imageChanged(url: string) {
|
||||
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 = 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<IImageManipulationProps> {
|
||||
return (
|
||||
<div className={styles.imageEditor} style={{
|
||||
marginTop: this.props.displyMode === DisplayMode.Edit ? '40px' : '0px',
|
||||
marginTop: this.props.displyMode === DisplayMode.Edit ? '40px' : '0px'
|
||||
}} >
|
||||
{this.props.displyMode === DisplayMode.Edit && this.getCommandBar()}
|
||||
<div className={styles.imageplaceholder + ' ' + this.getMaxWidth()}
|
||||
|
@ -280,8 +129,189 @@ this.canvasCtx.drawImage(this.bufferRef, sourceX, sourceY, sourceWidth, sourceHe
|
|||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
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 (<ImageGrid
|
||||
width={lastset.width} height={lastset.height}
|
||||
|
@ -310,7 +340,7 @@ this.canvasCtx.drawImage(this.bufferRef, sourceX, sourceY, sourceWidth, sourceHe
|
|||
}
|
||||
return (<ImageGrid
|
||||
onChange={(size) => 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 (<div className={styles.buttonHolderPanel} >
|
||||
<IconButton
|
||||
iconProps={{ iconName: 'SwitcherStartEnd' }}
|
||||
onRenderIcon={() => { return (<img className={styles.svgbuttonPanel} src={flipVerticalIcon} />); }}
|
||||
onRenderIcon={() => {
|
||||
// tslint:disable-next-line: react-a11y-img-has-alt
|
||||
return (<img className={styles.svgbuttonPanel} src={flipVerticalIcon} />);
|
||||
}}
|
||||
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
|
|||
}}
|
||||
/>
|
||||
<IconButton
|
||||
onRenderIcon={() => { return (<img className={styles.svgbuttonPanel} src={flipHorizontalIcon} />); }}
|
||||
onRenderIcon={() => {
|
||||
// tslint:disable-next-line: react-a11y-img-has-alt
|
||||
return (<img className={styles.svgbuttonPanel} src={flipHorizontalIcon} />);
|
||||
}}
|
||||
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
|
|||
</div>);
|
||||
}
|
||||
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 (<DefaultButton
|
||||
key={'rotate' + index}
|
||||
onClick={() => {
|
||||
|
@ -522,7 +559,6 @@ this.canvasCtx.drawImage(this.bufferRef, sourceX, sourceY, sourceWidth, sourceHe
|
|||
<span className={styles.imgtext} >{'' + value}</span></DefaultButton>);
|
||||
})}
|
||||
|
||||
|
||||
</div>
|
||||
<Slider
|
||||
label=''
|
||||
|
@ -533,14 +569,17 @@ this.canvasCtx.drawImage(this.bufferRef, sourceX, sourceY, sourceWidth, sourceHe
|
|||
onChange={this.setRotate}
|
||||
showValue={true}
|
||||
componentRef={(component: ISlider | null) => {
|
||||
//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
|
||||
/>
|
||||
<IconButton
|
||||
key={'resetrotate'}
|
||||
|
@ -554,7 +593,7 @@ this.canvasCtx.drawImage(this.bufferRef, sourceX, sourceY, sourceWidth, sourceHe
|
|||
}
|
||||
|
||||
private getCropSettings(): JSX.Element {
|
||||
let crop: ICropSettings = this.getCropValues();
|
||||
const crop: ICropSettings = this.getCropValues();
|
||||
return (<div>
|
||||
<Checkbox
|
||||
label={strings.LockAspect}
|
||||
|
@ -569,16 +608,31 @@ this.canvasCtx.drawImage(this.bufferRef, sourceX, sourceY, sourceWidth, sourceHe
|
|||
}}
|
||||
|
||||
/>
|
||||
<TextField label={strings.SourceX} value={'' + crop.sx} onChanged={(x) => this.setCrop(parseInt(x), undefined, undefined, undefined, crop.aspect)} />
|
||||
<TextField label={strings.SourceY} value={'' + crop.sy} onChanged={(y) => this.setCrop(undefined, parseInt(y), undefined, undefined, crop.aspect)} />
|
||||
<TextField label={strings.Width} value={'' + crop.width} onChanged={(w) => this.setCrop(undefined, undefined, parseInt(w), undefined, crop.aspect)} />
|
||||
<TextField label={strings.Height} value={'' + crop.height} onChanged={(h) => this.setCrop(undefined, undefined, undefined, parseInt(h), crop.aspect)} />
|
||||
|
||||
<TextField
|
||||
label={strings.SourceX}
|
||||
value={'' + crop.sx}
|
||||
// tslint:disable-next-line: radix
|
||||
onChanged={(x) => this.setCrop(parseInt(x), undefined, undefined, undefined, crop.aspect)} />
|
||||
<TextField
|
||||
label={strings.SourceY}
|
||||
value={'' + crop.sy}
|
||||
// tslint:disable-next-line: radix
|
||||
onChanged={(y) => this.setCrop(undefined, parseInt(y), undefined, undefined, crop.aspect)} />
|
||||
<TextField
|
||||
label={strings.Width}
|
||||
value={'' + crop.width}
|
||||
// tslint:disable-next-line: radix
|
||||
onChanged={(w) => this.setCrop(undefined, undefined, parseInt(w), undefined, crop.aspect)} />
|
||||
<TextField
|
||||
label={strings.Height}
|
||||
value={'' + crop.height}
|
||||
// tslint:disable-next-line: radix
|
||||
onChanged={(h) => this.setCrop(undefined, undefined, undefined, parseInt(h), crop.aspect)} />
|
||||
</div>);
|
||||
}
|
||||
|
||||
private getResizeSettings(): JSX.Element {
|
||||
let resize: IResizeSettings = this.getResizeValues();
|
||||
const resize: IResizeSettings = this.getResizeValues();
|
||||
return (<div>
|
||||
|
||||
<Checkbox
|
||||
|
@ -594,8 +648,15 @@ this.canvasCtx.drawImage(this.bufferRef, sourceX, sourceY, sourceWidth, sourceHe
|
|||
}}
|
||||
|
||||
/>
|
||||
<TextField label={strings.Width} value={'' + resize.width} onChanged={(w) => this.setResize(parseInt(w), undefined, resize.aspect)} />
|
||||
<TextField label={strings.Height} value={'' + resize.height} onChanged={(h) => this.setResize(undefined, parseInt(h), resize.aspect)} />
|
||||
<TextField label={strings.Width}
|
||||
value={'' + resize.width}
|
||||
// tslint:disable-next-line: radix
|
||||
onChanged={(w) => this.setResize(parseInt(w), undefined, resize.aspect)}
|
||||
/>
|
||||
<TextField label={strings.Height} value={'' + resize.height}
|
||||
// tslint:disable-next-line: radix
|
||||
onChanged={(h) => this.setResize(undefined, parseInt(h), resize.aspect)}
|
||||
/>
|
||||
|
||||
</div>);
|
||||
}
|
||||
|
@ -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 (<div>
|
||||
|
||||
|
@ -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 (<img className={styles.svgbutton} src={options.svgIcon} />);
|
||||
}
|
||||
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)}
|
||||
/>
|
||||
<Panel
|
||||
isOpen={this.state.settingPanel != SettingPanelType.Closed}
|
||||
isOpen={this.state.settingPanel !== SettingPanelType.Closed}
|
||||
type={PanelType.smallFixedFar}
|
||||
onDismiss={this.closeFilter}
|
||||
headerText={this.getPanelHeader(this.state.settingPanel)}
|
||||
|
|
|
@ -1,8 +1,12 @@
|
|||
import * as React from 'react';
|
||||
|
||||
// tslint:disable-next-line: no-any
|
||||
const colorFilterIcon: any = require('../../svg/colorFilter.svg');
|
||||
// tslint:disable-next-line: no-any
|
||||
const cropIcon: any = require('../../svg/crop.svg');
|
||||
// tslint:disable-next-line: no-any
|
||||
const flipVerticalIcon: any = require('../../svg/flipVertical.svg');
|
||||
// tslint:disable-next-line: no-any
|
||||
const resizeIcon: any = require('../../svg/resize.svg');
|
||||
import * as strings from 'ImageManipulationStrings';
|
||||
|
||||
|
@ -28,7 +32,7 @@ export enum SettingPanelType {
|
|||
|
||||
export enum FilterType {
|
||||
Grayscale,
|
||||
Sepia,
|
||||
Sepia
|
||||
/*
|
||||
Blur,
|
||||
Emboss,
|
||||
|
@ -42,7 +46,6 @@ export enum FilterType {
|
|||
ColorOverLay*/
|
||||
}
|
||||
|
||||
|
||||
export interface IManipulationBase {
|
||||
type: ManipulationType;
|
||||
}
|
||||
|
@ -84,8 +87,13 @@ export interface IResizeSettings extends IManipulationBase, IResize {
|
|||
|
||||
}
|
||||
|
||||
export type IImageManipulationSettings = IFilterSettings | IRotateSettings | IScaleSettings | IFlipSettings | ICropSettings | IResizeSettings;
|
||||
|
||||
export type IImageManipulationSettings =
|
||||
IFilterSettings
|
||||
| IRotateSettings
|
||||
| IScaleSettings
|
||||
| IFlipSettings
|
||||
| ICropSettings
|
||||
| IResizeSettings;
|
||||
|
||||
export const filterTypeData: IFilterTypeData = {
|
||||
0: strings.FilterTypeGrayscale,
|
||||
|
@ -99,6 +107,7 @@ export interface IFilterTypeData {
|
|||
export interface IManipulationTypeDataBase {
|
||||
text: string;
|
||||
iconName?: string;
|
||||
// tslint:disable-next-line: no-any
|
||||
svgIcon?: any;
|
||||
settingPanelType: SettingPanelType;
|
||||
}
|
||||
|
@ -117,7 +126,7 @@ export const manipulationTypeData: IManipulationTypeData = {
|
|||
svgIcon: cropIcon,
|
||||
toHTML: (item: ICropSettings) => {
|
||||
return (<span></span>);
|
||||
//return (<span>{`X:${item.sx} Y:${item.sy}`}</span>);
|
||||
// return (<span>{`X:${item.sx} Y:${item.sy}`}</span>);
|
||||
},
|
||||
settingPanelType: SettingPanelType.Crop
|
||||
},
|
||||
|
@ -153,8 +162,5 @@ export const manipulationTypeData: IManipulationTypeData = {
|
|||
svgIcon: resizeIcon,
|
||||
toHTML: (item: IResizeSettings) => { return (<span></span>); },
|
||||
settingPanelType: SettingPanelType.Resize
|
||||
},
|
||||
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
|
|
|
@ -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<IImageCropProps, IImageCropState> {
|
||||
export default class ImageCrop extends
|
||||
React.Component<IImageCropProps, IImageCropState> {
|
||||
|
||||
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<IImageCropProps, IImageCr
|
|||
}
|
||||
|
||||
public componentDidMount(): void {
|
||||
const { crop, sourceHeight, sourceWidth } = this.props;
|
||||
const { crop } = this.props;
|
||||
if (crop && this.isValid(crop) &&
|
||||
(crop.sx !== 0 || crop.sy !== 0 || crop.width !== 0 && crop.height !== 0)
|
||||
) {
|
||||
this.setState({ cropIsActive: true });
|
||||
} else {
|
||||
//Requireed because first renderer has no ref
|
||||
// Requireed because first renderer has no ref
|
||||
this.setState({ reloadtimestamp: new Date().getTime().toString() });
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public componentWillUnmount(): void {
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
public render(): React.ReactElement<IImageCropProps> {
|
||||
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 (
|
||||
<div ref={this.setControlRef}
|
||||
className={styles.ImgGridShadowOverlay}
|
||||
|
@ -110,12 +98,14 @@ export default class ImageCrop extends React.Component<IImageCropProps, IImageCr
|
|||
</div>
|
||||
</div>
|
||||
);
|
||||
// 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 (
|
||||
<div
|
||||
style={style}
|
||||
|
@ -124,7 +114,6 @@ export default class ImageCrop extends React.Component<IImageCropProps, IImageCr
|
|||
onTouchStart={this.onCropMouseTouchDown}
|
||||
>
|
||||
|
||||
|
||||
<div className={styles.dragBar_n} data-ord={nodePoition.N} />
|
||||
<div className={styles.dragBar_e} data-ord={nodePoition.E} />
|
||||
<div className={styles.dragBar_s} data-ord={nodePoition.S} />
|
||||
|
@ -139,7 +128,6 @@ export default class ImageCrop extends React.Component<IImageCropProps, IImageCr
|
|||
<div className={[styles.dragHandle, styles.sw].join(' ')} data-ord={nodePoition.SW} />
|
||||
<div className={[styles.dragHandle, styles.w].join(' ')} data-ord={nodePoition.W} />
|
||||
|
||||
|
||||
{showRuler && (
|
||||
<div>
|
||||
<div className={styles.ruleOfThirdsHZ} />
|
||||
|
@ -148,6 +136,7 @@ export default class ImageCrop extends React.Component<IImageCropProps, IImageCr
|
|||
)}
|
||||
</div>
|
||||
);
|
||||
// tslint:enable
|
||||
}
|
||||
|
||||
private makeNewCrop(): ICrop {
|
||||
|
@ -155,32 +144,18 @@ export default class ImageCrop extends React.Component<IImageCropProps, IImageCr
|
|||
return crop;
|
||||
}
|
||||
|
||||
private getCropStyle() {
|
||||
const crop = this.makeNewCrop();
|
||||
const unit = 'px';
|
||||
private getCropStyle(): { top: string, left: string, width: string, height: string } {
|
||||
const crop: ICrop = this.makeNewCrop();
|
||||
const unit: string = 'px';
|
||||
return {
|
||||
top: `${crop.sy}${unit}`,
|
||||
left: `${crop.sx}${unit}`,
|
||||
width: `${crop.width}${unit}`,
|
||||
height: `${crop.height}${unit}`,
|
||||
height: `${crop.height}${unit}`
|
||||
};
|
||||
}
|
||||
|
||||
private getCurrentPosition(e: MouseEvent | any): IMousePosition {
|
||||
let { pageX, pageY } = e;
|
||||
if (e.touches) {
|
||||
[{ pageX, pageY }] = e.touches;
|
||||
}
|
||||
|
||||
let refpos = this.controlRef.getBoundingClientRect();
|
||||
let startx: number = pageX - refpos.left;
|
||||
let starty: number = pageY - refpos.top;
|
||||
return ({
|
||||
x: startx,
|
||||
y: starty
|
||||
});
|
||||
}
|
||||
|
||||
// tslint:disable-next-line: no-any
|
||||
private onDocMouseTouchMove(e: React.MouseEvent<HTMLDivElement> | any): void {
|
||||
const { crop, onChange, onDragStart } = this.props;
|
||||
if (!this.mouseDownOnCrop) {
|
||||
|
@ -191,15 +166,12 @@ export default class ImageCrop extends React.Component<IImageCropProps, IImageCr
|
|||
if (!this.dragStarted) {
|
||||
this.dragStarted = true;
|
||||
if (onDragStart) {
|
||||
// tslint:disable-next-line: no-any
|
||||
onDragStart(e as any);
|
||||
}
|
||||
}
|
||||
const pos = this.getCurrentPosition(e);
|
||||
|
||||
|
||||
|
||||
|
||||
const clientPos = this.getClientPos(e);
|
||||
const clientPos: IMousePosition = this.getClientPos(e);
|
||||
/*
|
||||
if (this.evData.isResize && this.props.aspect && this.evData.cropOffset) {
|
||||
clientPos.y = this.straightenYPath(clientPos.x);
|
||||
|
@ -209,7 +181,7 @@ export default class ImageCrop extends React.Component<IImageCropProps, IImageCr
|
|||
this.evData.xDiff = clientPos.x - this.evData.clientStartX;
|
||||
this.evData.yDiff = clientPos.y - this.evData.clientStartY;
|
||||
|
||||
let nextCrop;
|
||||
let nextCrop: ICrop;
|
||||
|
||||
if (this.evData.isResize) {
|
||||
nextCrop = this.resizeCrop();
|
||||
|
@ -226,10 +198,10 @@ export default class ImageCrop extends React.Component<IImageCropProps, IImageCr
|
|||
|
||||
}
|
||||
|
||||
private dragCrop() {
|
||||
private dragCrop(): ICrop {
|
||||
|
||||
const { evData } = this;
|
||||
let nextCrop: ICrop = this.makeNewCrop();
|
||||
const nextCrop: ICrop = this.makeNewCrop();
|
||||
const width: number = this.controlRef.clientWidth;
|
||||
const height: number = this.controlRef.clientHeight;
|
||||
nextCrop.sx = clamp(evData.cropStartX + evData.xDiff, 0, width - nextCrop.width);
|
||||
|
@ -238,9 +210,9 @@ export default class ImageCrop extends React.Component<IImageCropProps, IImageCr
|
|||
return nextCrop;
|
||||
}
|
||||
|
||||
private resizeCrop() {
|
||||
private resizeCrop(): ICrop {
|
||||
const { evData } = this;
|
||||
let nextCrop = this.makeNewCrop();
|
||||
const nextCrop: ICrop = this.makeNewCrop();
|
||||
const { pos } = evData;
|
||||
|
||||
if (evData.xInversed) {
|
||||
|
@ -251,11 +223,10 @@ export default class ImageCrop extends React.Component<IImageCropProps, IImageCr
|
|||
evData.yDiff -= evData.cropStartHeight * 2;
|
||||
|
||||
}
|
||||
const newSize = this.getNewSize();
|
||||
const newSize: { width: number, height: number } = this.getNewSize();
|
||||
|
||||
|
||||
let newX = evData.cropStartX;
|
||||
let newY = evData.cropStartY;
|
||||
let newX: number = evData.cropStartX;
|
||||
let newY: number = evData.cropStartY;
|
||||
|
||||
if (evData.xInversed) {
|
||||
newX = nextCrop.sx + (nextCrop.width - newSize.width);
|
||||
|
@ -273,7 +244,11 @@ export default class ImageCrop extends React.Component<IImageCropProps, IImageCr
|
|||
aspect: this.props.crop.aspect
|
||||
};
|
||||
|
||||
if (this.props.crop.aspect || (pos === nodePoition.NW || pos === nodePoition.SE || pos === nodePoition.SW || pos === nodePoition.NE)) {
|
||||
if (this.props.crop.aspect
|
||||
|| (pos === nodePoition.NW
|
||||
|| pos === nodePoition.SE
|
||||
|| pos === nodePoition.SW
|
||||
|| pos === nodePoition.NE)) {
|
||||
nextCrop.sx = containedCrop.sx;
|
||||
nextCrop.sy = containedCrop.sy;
|
||||
nextCrop.width = containedCrop.width;
|
||||
|
@ -288,12 +263,11 @@ export default class ImageCrop extends React.Component<IImageCropProps, IImageCr
|
|||
return nextCrop;
|
||||
}
|
||||
|
||||
|
||||
private getNewSize(): { width: number, height: number } {
|
||||
const { crop, sourceWidth, sourceHeight } = this.props;
|
||||
const { evData } = this;
|
||||
|
||||
let newWidth = evData.cropStartWidth + evData.xDiff;
|
||||
let newWidth: number = evData.cropStartWidth + evData.xDiff;
|
||||
|
||||
if (evData.xInversed) {
|
||||
newWidth = Math.abs(newWidth);
|
||||
|
@ -302,7 +276,7 @@ export default class ImageCrop extends React.Component<IImageCropProps, IImageCr
|
|||
newWidth = clamp(newWidth, 0, sourceWidth);
|
||||
|
||||
// New height.
|
||||
let newHeight;
|
||||
let newHeight: number;
|
||||
|
||||
if (crop.aspect) {
|
||||
newHeight = newWidth / crop.aspect;
|
||||
|
@ -323,16 +297,14 @@ export default class ImageCrop extends React.Component<IImageCropProps, IImageCr
|
|||
|
||||
return {
|
||||
width: newWidth,
|
||||
height: newHeight,
|
||||
height: newHeight
|
||||
};
|
||||
}
|
||||
|
||||
// tslint:disable-next-line: no-any
|
||||
private onDocMouseTouchEnd(e: MouseEvent | any): void {
|
||||
const { crop, onDragEnd, onComplete } = this.props;
|
||||
|
||||
let elecord = this.controlRef.getBoundingClientRect();
|
||||
|
||||
|
||||
if (this.mouseDownOnCrop) {
|
||||
this.mouseDownOnCrop = false;
|
||||
this.dragStarted = false;
|
||||
|
@ -347,11 +319,12 @@ export default class ImageCrop extends React.Component<IImageCropProps, IImageCr
|
|||
}
|
||||
}
|
||||
|
||||
// tslint:disable-next-line: no-any
|
||||
private onCropMouseTouchDown(e: MouseEvent | any): void {
|
||||
const { crop } = this.props;
|
||||
|
||||
e.preventDefault(); // Stop drag selection.
|
||||
const mousepos = this.getClientPos(e);
|
||||
const mousepos: IMousePosition = this.getClientPos(e);
|
||||
const { ord } = e.target.dataset;
|
||||
|
||||
let xInversed: boolean = false;
|
||||
|
@ -382,14 +355,14 @@ export default class ImageCrop extends React.Component<IImageCropProps, IImageCr
|
|||
this.setState({ cropIsActive: true });
|
||||
}
|
||||
|
||||
|
||||
private setControlRef(element: HTMLDivElement): void {
|
||||
this.controlRef = element;
|
||||
}
|
||||
|
||||
// tslint:disable-next-line: no-any
|
||||
private getClientPos(e: MouseEvent | any): IMousePosition {
|
||||
let pageX;
|
||||
let pageY;
|
||||
let pageX: number;
|
||||
let pageY: number;
|
||||
|
||||
if (e.touches) {
|
||||
[{ pageX, pageY }] = e.touches;
|
||||
|
@ -399,62 +372,30 @@ export default class ImageCrop extends React.Component<IImageCropProps, IImageCr
|
|||
|
||||
return {
|
||||
x: pageX,
|
||||
y: pageY,
|
||||
y: pageY
|
||||
};
|
||||
}
|
||||
|
||||
private isValid(crop: ICrop) {
|
||||
private isValid(crop: ICrop): boolean {
|
||||
return crop && !isNaN(crop.width) && !isNaN(crop.height);
|
||||
}
|
||||
|
||||
private makeAspectCrop(crop: ICrop) {
|
||||
if (isNaN(this.props.crop.aspect)) {
|
||||
return crop;
|
||||
}
|
||||
|
||||
const calcCrop: ICrop = crop;
|
||||
|
||||
if (crop.width) {
|
||||
calcCrop.height = calcCrop.width / this.props.crop.aspect;
|
||||
}
|
||||
|
||||
if (crop.height) {
|
||||
calcCrop.width = calcCrop.height * this.props.crop.aspect;
|
||||
}
|
||||
|
||||
if (calcCrop.sy + calcCrop.height > 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<IImageCropProps, IImageCr
|
|||
isResize: true,
|
||||
xDiff: 0,
|
||||
yDiff: 0,
|
||||
pos: nodePoition.NW,
|
||||
pos: nodePoition.NW
|
||||
};
|
||||
|
||||
this.mouseDownOnCrop = true;
|
||||
|
@ -488,5 +429,4 @@ export default class ImageCrop extends React.Component<IImageCropProps, IImageCr
|
|||
|
||||
this.setState({ cropIsActive: true, newCropIsBeingDrawn: true });
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -1,4 +1,3 @@
|
|||
import { Overlay } from 'office-ui-fabric-react';
|
||||
import * as React from 'react';
|
||||
import { IResize } from '../ImageManipulation.types';
|
||||
|
||||
|
@ -12,7 +11,9 @@ export interface IImageGridProps {
|
|||
aspect?: number;
|
||||
onChange: (size: IResize) => 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<IImageGridProps, IImageGridState> {
|
||||
|
||||
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<IImageGridProps, IImageGr
|
|||
}
|
||||
|
||||
public componentDidMount(): void {
|
||||
window.document.addEventListener("mousemove", this.onDocMouseTouchMove);
|
||||
window.document.addEventListener("touchmove", this.onDocMouseTouchMove);
|
||||
window.document.addEventListener('mousemove', this.onDocMouseTouchMove);
|
||||
window.document.addEventListener('touchmove', this.onDocMouseTouchMove);
|
||||
window.document.addEventListener('mouseup', this.onDocMouseTouchEnd);
|
||||
window.document.addEventListener('touchend', this.onDocMouseTouchEnd);
|
||||
window.document.addEventListener('touchcancel', this.onDocMouseTouchEnd);
|
||||
|
||||
}
|
||||
public componentWillUnmount(): void {
|
||||
window.document.removeEventListener("mousemove", this.onDocMouseTouchMove);
|
||||
window.document.removeEventListener("touchmove", this.onDocMouseTouchMove);
|
||||
window.document.removeEventListener('mousemove', this.onDocMouseTouchMove);
|
||||
window.document.removeEventListener('touchmove', this.onDocMouseTouchMove);
|
||||
window.document.removeEventListener('mouseup', this.onDocMouseTouchEnd);
|
||||
window.document.removeEventListener('touchend', this.onDocMouseTouchEnd);
|
||||
window.document.removeEventListener('touchcancel', this.onDocMouseTouchEnd);
|
||||
}
|
||||
|
||||
public render(): React.ReactElement<IImageGridProps> {
|
||||
// tslint:disable:react-a11y-event-has-role
|
||||
return (
|
||||
<div className={styles.ImgGridShadowOverlay}>
|
||||
<div className={styles.ImgGridVisible}
|
||||
|
@ -110,10 +112,12 @@ export default class ImageGrid extends React.Component<IImageGridProps, IImageGr
|
|||
</div>
|
||||
</div>
|
||||
);
|
||||
// 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<IImageGridProps, IImageGr
|
|||
};
|
||||
}
|
||||
|
||||
// tslint:disable-next-line: no-any
|
||||
private onDocMouseTouchMove(e: React.MouseEvent<HTMLDivElement> | 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<IImageGridProps, IImageGr
|
|||
}
|
||||
e.preventDefault();
|
||||
|
||||
const mousePos = this.getClientPos(e);
|
||||
const mousePos: IMousePosition = this.getClientPos(e);
|
||||
|
||||
let xDiff: number = 0;
|
||||
let yDiff: number = 0;
|
||||
|
||||
if (this.evData.pos == nodePoition.E || this.evData.pos == nodePoition.SE || this.evData.pos == nodePoition.NE) {
|
||||
if (this.evData.pos === nodePoition.E
|
||||
|| this.evData.pos === nodePoition.SE
|
||||
|| this.evData.pos === nodePoition.NE) {
|
||||
xDiff = mousePos.x - this.evData.clientStartX;
|
||||
} else if (this.evData.pos == nodePoition.W || this.evData.pos == nodePoition.SW || this.evData.pos == nodePoition.NW) {
|
||||
} else if (this.evData.pos === nodePoition.W
|
||||
|| this.evData.pos === nodePoition.SW
|
||||
|| this.evData.pos === nodePoition.NW) {
|
||||
xDiff = this.evData.clientStartX - mousePos.x;
|
||||
}
|
||||
|
||||
if (this.evData.pos == nodePoition.N || this.evData.pos == nodePoition.NW || this.evData.pos == nodePoition.NE) {
|
||||
if (this.evData.pos === nodePoition.N || this.evData.pos === nodePoition.NW || this.evData.pos === nodePoition.NE) {
|
||||
yDiff = this.evData.clientStartY - mousePos.y;
|
||||
} else if (this.evData.pos == nodePoition.S || this.evData.pos == nodePoition.SW || this.evData.pos == nodePoition.SE) {
|
||||
} else if (this.evData.pos === nodePoition.S
|
||||
|| this.evData.pos === nodePoition.SW
|
||||
|| this.evData.pos === nodePoition.SE) {
|
||||
yDiff = mousePos.y - this.evData.clientStartY;
|
||||
}
|
||||
|
||||
let nextsize: IResize = {
|
||||
const nextsize: IResize = {
|
||||
width: this.evData.width + xDiff,
|
||||
height: this.evData.height + yDiff
|
||||
};
|
||||
if(aspect) {
|
||||
if(this.evData.pos !== nodePoition.N && this.evData.pos !== nodePoition.S) {
|
||||
if (aspect) {
|
||||
if (this.evData.pos !== nodePoition.N && this.evData.pos !== nodePoition.S) {
|
||||
nextsize.height = nextsize.width / aspect;
|
||||
} else {
|
||||
nextsize.width = nextsize.height * aspect;
|
||||
|
@ -184,6 +195,7 @@ export default class ImageGrid extends React.Component<IImageGridProps, IImageGr
|
|||
}
|
||||
}
|
||||
|
||||
// tslint:disable-next-line: no-any
|
||||
private onDocMouseTouchEnd(e: MouseEvent | any): void {
|
||||
const { width, height, onDragEnd, onComplete } = this.props;
|
||||
if (this.dragStarted) {
|
||||
|
@ -196,14 +208,13 @@ export default class ImageGrid extends React.Component<IImageGridProps, IImageGr
|
|||
onComplete({ width: width, height: height });
|
||||
this.setState({ cropIsActive: false, newCropIsBeingDrawn: false });
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
// tslint:disable-next-line: no-any
|
||||
private getClientPos(e: MouseEvent | any): IMousePosition {
|
||||
let pageX;
|
||||
let pageY;
|
||||
let pageX: number;
|
||||
let pageY: number;
|
||||
|
||||
if (e.touches) {
|
||||
[{ pageX, pageY }] = e.touches;
|
||||
|
@ -213,8 +224,7 @@ export default class ImageGrid extends React.Component<IImageGridProps, IImageGr
|
|||
|
||||
return {
|
||||
x: pageX,
|
||||
y: pageY,
|
||||
y: pageY
|
||||
};
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -1,13 +1,10 @@
|
|||
import { nodePoition } from "./Enums";
|
||||
import { nodePoition } from './Enums';
|
||||
|
||||
export interface IMousePosition {
|
||||
x: number;
|
||||
y: number;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
export interface ICropData {
|
||||
clientStartX: number;
|
||||
clientStartY: number;
|
||||
|
@ -22,4 +19,3 @@ export interface ICropData {
|
|||
xDiff: number;
|
||||
yDiff: number;
|
||||
}
|
||||
|
||||
|
|
|
@ -1,12 +1,13 @@
|
|||
import { isEqual } from '@microsoft/sp-lodash-subset';
|
||||
import { EventGroup, IButtonStyles, IconButton, ISelection, Label } from 'office-ui-fabric-react';
|
||||
import { DragDropHelper, IDragDropContext, IDragDropHelperParams } from 'office-ui-fabric-react/lib-es2015/utilities/dragdrop';
|
||||
import { DragDropHelper, IDragDropContext } from 'office-ui-fabric-react/lib-es2015/utilities/dragdrop';
|
||||
import * as React from 'react';
|
||||
import styles from './ItemOrder.module.scss';
|
||||
|
||||
export interface IItemOrderProps {
|
||||
label: string;
|
||||
disabled: boolean;
|
||||
// tslint:disable-next-line: no-any
|
||||
items: Array<any>;
|
||||
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<any>) => 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<any>;
|
||||
}
|
||||
|
||||
|
||||
export default class ItemOrder extends React.Component<IItemOrderProps, IItemOrderState> {
|
||||
|
||||
// tslint:disable-next-line: no-any
|
||||
private _draggedItem: any;
|
||||
private _selection: ISelection;
|
||||
private _ddHelper: DragDropHelper;
|
||||
private _refs: Array<HTMLElement>;
|
||||
// tslint:disable-next-line: no-any
|
||||
private _ddSubs: Array<any>;
|
||||
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<HTMLElement>();
|
||||
// tslint:disable-next-line: no-any
|
||||
this._ddSubs = new Array<any>();
|
||||
|
||||
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<IItemOrderProps, IItemOrd
|
|||
return (
|
||||
<div className={styles.propertyFieldOrder}>
|
||||
{this.props.label && <Label>{this.props.label}</Label>}
|
||||
<ul style={{ maxHeight: this.props.maxHeight ? this.props.maxHeight + 'px' : '100%' }} className={!this.props.disabled ? styles.enabled : styles.disabled}>
|
||||
<ul
|
||||
style={{ maxHeight: this.props.maxHeight ? this.props.maxHeight + 'px' : '100%' }}
|
||||
className={!this.props.disabled ? styles.enabled : styles.disabled}>
|
||||
{
|
||||
(items && items.length > 0) && (
|
||||
// tslint:disable-next-line: no-any
|
||||
items.map((value: any, index: number) => {
|
||||
return (
|
||||
<li
|
||||
|
@ -76,69 +82,15 @@ export default class ItemOrder extends React.Component<IItemOrderProps, IItemOrd
|
|||
)
|
||||
}
|
||||
{
|
||||
(items && items.length > 0) && <div className={styles.lastBox} ref={(ref: HTMLElement) => { this._lastBox = ref; }} />
|
||||
(items && items.length > 0) && <div
|
||||
className={styles.lastBox}
|
||||
ref={(ref: HTMLElement) => { this._lastBox = ref; }} />
|
||||
}
|
||||
</ul>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
private renderItem(item: any, index: number): JSX.Element {
|
||||
return (
|
||||
<div>
|
||||
<div className={styles.itemBox}>
|
||||
{this.renderDisplayValue(item, index)}
|
||||
</div>
|
||||
{!this.props.removeArrows &&
|
||||
<div>{this.renderArrows(index)}</div>
|
||||
}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
private renderDisplayValue(item: any, index: number): JSX.Element {
|
||||
if (typeof this.props.onRenderItem === "function") {
|
||||
return this.props.onRenderItem(item, index);
|
||||
} else {
|
||||
return (
|
||||
<span>{this.props.textProperty ? item[this.props.textProperty] : item.toString()}</span>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
private renderArrows(index: number): JSX.Element {
|
||||
let arrowButtonStyles: Partial<IButtonStyles> = {
|
||||
root: {
|
||||
width: '14px',
|
||||
height: '100%',
|
||||
display: 'inline-block !important'
|
||||
},
|
||||
rootDisabled: {
|
||||
backgroundColor: 'transparent'
|
||||
},
|
||||
icon: {
|
||||
fontSize: "10px"
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<div>
|
||||
<IconButton
|
||||
disabled={this.props.disabled || index === 0}
|
||||
iconProps={{ iconName: this.props.moveUpIconName }}
|
||||
onClick={() => { this.onMoveUpClick(index); }}
|
||||
styles={arrowButtonStyles}
|
||||
/>
|
||||
<IconButton
|
||||
disabled={this.props.disabled || index === this.props.items.length - 1}
|
||||
iconProps={{ iconName: this.props.moveDownIconName }}
|
||||
onClick={() => { this.onMoveDownClick(index); }}
|
||||
styles={arrowButtonStyles}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
public componentWillMount(): void {
|
||||
this.setState({
|
||||
items: this.props.items || []
|
||||
|
@ -167,6 +119,64 @@ export default class ItemOrder extends React.Component<IItemOrderProps, IItemOrd
|
|||
this.cleanupSubscriptions();
|
||||
}
|
||||
|
||||
// tslint:disable-next-line: no-any
|
||||
private renderItem(item: any, index: number): JSX.Element {
|
||||
return (
|
||||
<div>
|
||||
<div className={styles.itemBox}>
|
||||
{this.renderDisplayValue(item, index)}
|
||||
</div>
|
||||
{!this.props.removeArrows &&
|
||||
<div>{this.renderArrows(index)}</div>
|
||||
}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
// 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 (
|
||||
<span>{this.props.textProperty ? item[this.props.textProperty] : item.toString()}</span>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
private renderArrows(index: number): JSX.Element {
|
||||
const arrowButtonStyles: Partial<IButtonStyles> = {
|
||||
root: {
|
||||
width: '14px',
|
||||
height: '100%',
|
||||
display: 'inline-block !important'
|
||||
},
|
||||
rootDisabled: {
|
||||
backgroundColor: 'transparent'
|
||||
},
|
||||
icon: {
|
||||
fontSize: '10px'
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<div>
|
||||
<IconButton
|
||||
disabled={this.props.disabled || index === 0}
|
||||
iconProps={{ iconName: this.props.moveUpIconName }}
|
||||
onClick={() => { this.onMoveUpClick(index); }}
|
||||
styles={arrowButtonStyles}
|
||||
/>
|
||||
<IconButton
|
||||
disabled={this.props.disabled || index === this.props.items.length - 1}
|
||||
iconProps={{ iconName: this.props.moveDownIconName }}
|
||||
onClick={() => { this.onMoveDownClick(index); }}
|
||||
styles={arrowButtonStyles}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
private registerRef = (ref: HTMLElement): void => {
|
||||
this._refs.push(ref);
|
||||
}
|
||||
|
@ -177,7 +187,8 @@ export default class ItemOrder extends React.Component<IItemOrderProps, IItemOrd
|
|||
this._ddSubs.push(this._ddHelper.subscribe(value, new EventGroup(value), {
|
||||
eventMap: [
|
||||
{
|
||||
callback: (context: IDragDropContext, event?: any) => {
|
||||
// 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<IItemOrderProps, IItemOrd
|
|||
],
|
||||
selectionIndex: index,
|
||||
context: { data: this.state.items[index], index: index },
|
||||
updateDropState: (isDropping: boolean, event: DragEvent) => {
|
||||
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<IItemOrderProps, IItemOrd
|
|||
//Never called for some reason, so using eventMap above
|
||||
this._draggedItem = item;
|
||||
},*/
|
||||
onDragEnd: (item?: any, event?: DragEvent) => {
|
||||
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<IItemOrderProps, IItemOrd
|
|||
this._refs[this._refs.length - 1].classList.remove(styles.dragLast);
|
||||
}
|
||||
},
|
||||
canDrop: (dropContext?: IDragDropContext, dragContext?: IDragDropContext) => {
|
||||
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<IItemOrderProps, IItemOrd
|
|||
|
||||
private cleanupSubscriptions = (): void => {
|
||||
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<IItemOrderProps, IItemOrd
|
|||
this.moveItemAtIndexToTargetIndex(itemIndex, targetIndex);
|
||||
}
|
||||
|
||||
|
||||
private onMoveUpClick = (itemIndex: number): void => {
|
||||
if (itemIndex > 0) {
|
||||
this.moveItemAtIndexToTargetIndex(itemIndex, itemIndex - 1);
|
||||
|
@ -269,8 +285,12 @@ export default class ItemOrder extends React.Component<IItemOrderProps, IItemOrd
|
|||
}
|
||||
|
||||
private moveItemAtIndexToTargetIndex = (itemIndex: number, targetIndex: number): void => {
|
||||
if (itemIndex !== targetIndex && itemIndex > -1 && targetIndex > -1 && itemIndex < this.state.items.length && targetIndex < this.state.items.length) {
|
||||
let items: Array<any> = 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<any> = this.state.items;
|
||||
items.splice(targetIndex, 0, ...items.splice(itemIndex, 1)[0]);
|
||||
|
||||
this.setState({
|
||||
|
|
|
@ -10,4 +10,3 @@ export {
|
|||
IFilterSettings, IRotateSettings, IScaleSettings, IFlipSettings, ICropSettings, IResizeSettings,
|
||||
FilterType
|
||||
} from './ImageManipulation.types';
|
||||
|
||||
|
|
|
@ -7,7 +7,6 @@ declare interface IImageManipulationStrings {
|
|||
ManipulationTypeCrop: string;
|
||||
ManipulationTypeResize: string;
|
||||
|
||||
|
||||
FilterTypeGrayscale: string;
|
||||
FilterTypeSepia: string;
|
||||
|
||||
|
|
|
@ -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" },
|
||||
|
|
|
@ -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<IReac
|
|||
url: this.properties.url,
|
||||
settings: this.properties.settings,
|
||||
|
||||
updateTitleProperty: (value: string) => {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<IReac
|
|||
return Version.parse('1.0');
|
||||
}
|
||||
|
||||
|
||||
protected getPropertyPaneConfiguration(): IPropertyPaneConfiguration {
|
||||
return {
|
||||
pages: [
|
||||
|
@ -70,10 +67,9 @@ export default class ReactImageEditorWebPart extends BaseClientSideWebPart<IReac
|
|||
{
|
||||
groupName: strings.BasicGroupName,
|
||||
groupFields: [
|
||||
PropertyPaneToggle('showTitle',{
|
||||
PropertyPaneToggle('showTitle', {
|
||||
label: strings.ShowTitleFieldLabel
|
||||
})
|
||||
|
||||
]
|
||||
}
|
||||
]
|
||||
|
|
|
@ -1,32 +1,30 @@
|
|||
import * as React from 'react';
|
||||
import styles from './ReactImageEditor.module.scss';
|
||||
|
||||
import { WebPartTitle } from "@pnp/spfx-controls-react/lib/WebPartTitle";
|
||||
import { WebPartTitle } from '@pnp/spfx-controls-react/lib/WebPartTitle';
|
||||
import { DisplayMode, Environment, EnvironmentType } from '@microsoft/sp-core-library';
|
||||
import { Placeholder } from "@pnp/spfx-controls-react/lib/Placeholder";
|
||||
import { Placeholder } from '@pnp/spfx-controls-react/lib/Placeholder';
|
||||
import { WebPartContext } from '@microsoft/sp-webpart-base';
|
||||
import { FilePicker, IFilePickerResult } from '@pnp/spfx-controls-react/lib/FilePicker';
|
||||
import { ImageManipulation, IImageManipulationSettings } from '../../../components/ImageManipulation';
|
||||
|
||||
import { ImageManipulation, IImageManipulationSettings } from '../../../components/ImageManipulation'
|
||||
export interface IReactImageEditorBaseProps {
|
||||
showTitle: boolean;
|
||||
title: string;
|
||||
url?: string;
|
||||
settings?: IImageManipulationSettings[];
|
||||
|
||||
}
|
||||
|
||||
export interface IReactImageEditorProps extends IReactImageEditorBaseProps {
|
||||
displayMode: DisplayMode;
|
||||
context: WebPartContext;
|
||||
|
||||
updateTitleProperty: (value: string) => 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<IReactImageEditorP
|
|||
this.state = {
|
||||
isFilePickerOpen: false,
|
||||
statekey: 'init'
|
||||
}
|
||||
};
|
||||
this._onConfigure = this._onConfigure.bind(this);
|
||||
this._onUrlChanged = this._onUrlChanged.bind(this);
|
||||
this._onSettingsChanged = this._onSettingsChanged.bind(this);
|
||||
|
@ -44,7 +42,7 @@ export default class ReactImageEditor extends React.Component<IReactImageEditorP
|
|||
public render(): React.ReactElement<IReactImageEditorProps> {
|
||||
const { url, settings } = this.props;
|
||||
const { isFilePickerOpen } = this.state;
|
||||
const isConfigured = !!url && url.length > 0;
|
||||
const isConfigured: boolean = !!url && url.length > 0;
|
||||
return (
|
||||
|
||||
<div className={styles.reactImageEditor}>
|
||||
|
@ -54,16 +52,16 @@ export default class ReactImageEditor extends React.Component<IReactImageEditorP
|
|||
{(isFilePickerOpen || isConfigured) && Environment.type !== EnvironmentType.Local &&
|
||||
<FilePicker
|
||||
isPanelOpen={isFilePickerOpen}
|
||||
accepts={[".gif", ".jpg", ".jpeg", ".png"]}
|
||||
buttonIcon="FileImage"
|
||||
accepts={['.gif', '.jpg', '.jpeg', '.png']}
|
||||
buttonIcon='FileImage'
|
||||
onSave={(filePickerResult: IFilePickerResult) => {
|
||||
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<IReactImageEditorP
|
|||
private _onConfigure = () => {
|
||||
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 });
|
||||
|
|
|
@ -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
|
||||
}
|
||||
}
|
||||
{
|
||||
"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
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue