Merge pull request #5278 from ishaisagi-hns/image-editor-webpart-enhancements
This commit is contained in:
commit
88310656fe
|
@ -66,6 +66,7 @@ References to office-ui-fabric-react version 5.x because of SharePoint 2019 Supp
|
|||
## Contributors
|
||||
|
||||
* [Peter Paul Kirschner](https://github.com/petkir)
|
||||
* [Ishai Sagi] (http://github.com/ishaisagi-hns)
|
||||
|
||||
Thanks to [celum](https://www.celum.com/) and [cubido](https://www.cubido.at/) to allow to share this code.
|
||||
|
||||
|
@ -73,6 +74,7 @@ Thanks to [celum](https://www.celum.com/) and [cubido](https://www.cubido.at/) t
|
|||
|
||||
Version|Date|Comments
|
||||
-------|----|--------
|
||||
1.1.0.0|Sep 09, 2024|Added properties and accessibility
|
||||
1.0.0.0|Mar 17, 2021|Initial release
|
||||
|
||||
## Minimal Path to Awesome
|
||||
|
|
|
@ -59,11 +59,14 @@ export interface IImageManipulationProps {
|
|||
editMode?: (mode: boolean) => void;
|
||||
configSettings: IImageManipulationConfig;
|
||||
displayMode: DisplayMode;
|
||||
altText: string;
|
||||
}
|
||||
|
||||
export interface IImageManipulationState {
|
||||
settingPanel: SettingPanelType;
|
||||
redosettings: IImageManipulationSettings[];
|
||||
lockAspectCrop: boolean;
|
||||
lockAspectResize: boolean;
|
||||
}
|
||||
|
||||
export class ImageManipulation extends React.Component<IImageManipulationProps, IImageManipulationState> {
|
||||
|
@ -81,7 +84,9 @@ export class ImageManipulation extends React.Component<IImageManipulationProps,
|
|||
|
||||
this.state = {
|
||||
settingPanel: SettingPanelType.Closed,
|
||||
redosettings: []
|
||||
redosettings: [],
|
||||
lockAspectCrop: true,
|
||||
lockAspectResize: true
|
||||
};
|
||||
this.openPanel = this.openPanel.bind(this);
|
||||
this.setRotate = this.setRotate.bind(this);
|
||||
|
@ -109,6 +114,7 @@ export class ImageManipulation extends React.Component<IImageManipulationProps,
|
|||
this.img = new Image();
|
||||
this.img.src = url;
|
||||
this.img.crossOrigin = 'Anonymous';
|
||||
this.img.alt = this.props.altText ? this.props.altText : 'Untitled image';
|
||||
this.img.onload = () => {
|
||||
|
||||
this.applySettings();
|
||||
|
@ -305,7 +311,7 @@ export class ImageManipulation extends React.Component<IImageManipulationProps,
|
|||
<canvas className={this.getMaxWidth()}
|
||||
style={{ display: 'none' }}
|
||||
ref={this.setManipulateRef}></canvas>
|
||||
<canvas className={this.getMaxWidth()} ref={this.setCanvasRef} ></canvas>
|
||||
<canvas className={this.getMaxWidth()} ref={this.setCanvasRef} aria-label={this.props.altText} role='img' >Your browser does not support displaying canvas elements.</canvas>
|
||||
{this.state.settingPanel === SettingPanelType.Crop && (this.getCropGrid())}
|
||||
{this.state.settingPanel === SettingPanelType.Resize && (this.getResizeGrid())}
|
||||
|
||||
|
@ -600,17 +606,20 @@ export class ImageManipulation extends React.Component<IImageManipulationProps,
|
|||
return (<div>
|
||||
<Checkbox
|
||||
label={strings.LockAspect}
|
||||
checked={!isNaN(crop.aspect)}
|
||||
onChange={() => {
|
||||
if (isNaN(crop.aspect)) {
|
||||
this.setCrop(undefined, undefined, undefined, undefined, this.getAspect());
|
||||
} else {
|
||||
this.setCrop(undefined, undefined, undefined, undefined, undefined);
|
||||
}
|
||||
|
||||
checked={this.state.lockAspectCrop}
|
||||
onChange={(e, checked) => {
|
||||
// Toggle the lockAspect state when checkbox is checked/unchecked
|
||||
this.setState({ lockAspectCrop: checked }, () => {
|
||||
// Call the setCrop function with appropriate arguments based on the new state
|
||||
if (this.state.lockAspectCrop) {
|
||||
this.setCrop(undefined, undefined, undefined, undefined, this.getAspect());
|
||||
} else {
|
||||
this.setCrop(undefined, undefined, undefined, undefined, undefined);
|
||||
}
|
||||
});
|
||||
}}
|
||||
|
||||
/>
|
||||
|
||||
<TextField
|
||||
label={strings.SourceX}
|
||||
value={'' + crop.sx}
|
||||
|
@ -640,13 +649,16 @@ export class ImageManipulation extends React.Component<IImageManipulationProps,
|
|||
|
||||
<Checkbox
|
||||
label={strings.LockAspect}
|
||||
checked={!isNaN(resize.aspect)}
|
||||
checked={this.state.lockAspectResize}
|
||||
onChange={() => {
|
||||
if (isNaN(resize.aspect)) {
|
||||
this.setResize(undefined, undefined, this.getAspect());
|
||||
} else {
|
||||
this.setResize(undefined, undefined, undefined);
|
||||
}
|
||||
this.setState({ lockAspectResize: !this.state.lockAspectResize }, () => {
|
||||
if (isNaN(resize.aspect)) {
|
||||
this.setResize(undefined, undefined, this.getAspect());
|
||||
} else {
|
||||
this.setResize(undefined, undefined, undefined);
|
||||
}
|
||||
})
|
||||
|
||||
|
||||
}}
|
||||
|
||||
|
|
|
@ -12,12 +12,13 @@
|
|||
"preconfiguredEntries": [{
|
||||
"groupId": "5c03119e-3074-46fd-976b-c60198311f70",
|
||||
"group": { "default": "Other" },
|
||||
"title": { "default": "react-image-editor" },
|
||||
"description": { "default": "react-image-editor description" },
|
||||
"title": { "default": "Advanced Image Editor" },
|
||||
"description": { "default": "Add an image to the page, resize and edit the image." },
|
||||
"officeFabricIconFontName": "Page",
|
||||
"properties": {
|
||||
"title": "react-image-editor Sample",
|
||||
"showTitle":true,
|
||||
"title": "Advanced Image Editor",
|
||||
"showTitle":false,
|
||||
"showEditIcon":false,
|
||||
"settings":[],
|
||||
"url":""
|
||||
}
|
||||
|
|
|
@ -4,6 +4,8 @@ import { Version } from '@microsoft/sp-core-library';
|
|||
import {
|
||||
BaseClientSideWebPart,
|
||||
IPropertyPaneConfiguration,
|
||||
PropertyPaneLabel,
|
||||
PropertyPaneTextField,
|
||||
PropertyPaneToggle
|
||||
} from '@microsoft/sp-webpart-base';
|
||||
|
||||
|
@ -28,6 +30,10 @@ export default class ReactImageEditorWebPart extends BaseClientSideWebPart<IReac
|
|||
title: this.properties.title,
|
||||
url: this.properties.url,
|
||||
settings: this.properties.settings,
|
||||
showEditIcon:this.properties.showEditIcon,
|
||||
altText:this.properties.altText,
|
||||
|
||||
|
||||
|
||||
updateTitleProperty: (value: string) => { this.properties.title = value; },
|
||||
updateUrlProperty: (value: string) => {
|
||||
|
@ -67,9 +73,16 @@ export default class ReactImageEditorWebPart extends BaseClientSideWebPart<IReac
|
|||
{
|
||||
groupName: strings.BasicGroupName,
|
||||
groupFields: [
|
||||
PropertyPaneTextField('altText',{label:strings.AltTextFieldLabel}),
|
||||
PropertyPaneToggle('showTitle', {
|
||||
label: strings.ShowTitleFieldLabel
|
||||
})
|
||||
,
|
||||
PropertyPaneToggle('showEditIcon', {
|
||||
label: strings.ShowEditIconFieldLabel
|
||||
}),
|
||||
PropertyPaneLabel('urlInfo',{text:`The selected image is at ${this.properties.url?this.properties.url:'Not yet selected'} `})
|
||||
|
||||
]
|
||||
}
|
||||
]
|
||||
|
|
|
@ -9,9 +9,11 @@ import { ImageManipulation, IImageManipulationSettings } from '../../../componen
|
|||
|
||||
export interface IReactImageEditorBaseProps {
|
||||
showTitle: boolean;
|
||||
showEditIcon: boolean;
|
||||
title: string;
|
||||
url?: string;
|
||||
settings?: IImageManipulationSettings[];
|
||||
altText?: string;
|
||||
|
||||
}
|
||||
|
||||
|
@ -21,6 +23,7 @@ export interface IReactImageEditorProps extends IReactImageEditorBaseProps {
|
|||
updateTitleProperty: (value: string) => void;
|
||||
updateUrlProperty: (value: string) => void;
|
||||
updateManipulationSettingsProperty: (value: IImageManipulationSettings[]) => void;
|
||||
|
||||
}
|
||||
|
||||
export interface IReactImageEditorState {
|
||||
|
@ -46,14 +49,16 @@ export default class ReactImageEditor extends React.Component<IReactImageEditorP
|
|||
return (
|
||||
|
||||
<div className={styles.reactImageEditor}>
|
||||
<WebPartTitle displayMode={this.props.displayMode}
|
||||
title={this.props.title}
|
||||
updateProperty={this.props.updateTitleProperty} />
|
||||
{(isFilePickerOpen || isConfigured) && Environment.type !== EnvironmentType.Local &&
|
||||
{this.props.showTitle &&
|
||||
<WebPartTitle displayMode={this.props.displayMode}
|
||||
title={this.props.title}
|
||||
updateProperty={this.props.updateTitleProperty} />
|
||||
}
|
||||
{(isFilePickerOpen || (isConfigured && this.props.displayMode === DisplayMode.Edit)) && Environment.type !== EnvironmentType.Local &&
|
||||
<FilePicker
|
||||
isPanelOpen={isFilePickerOpen}
|
||||
accepts={['.gif', '.jpg', '.jpeg', '.png']}
|
||||
buttonIcon='FileImage'
|
||||
buttonIcon={'FileImage'}
|
||||
onSave={(filePickerResult: IFilePickerResult) => {
|
||||
this.setState({ isFilePickerOpen: false }, () => this._onUrlChanged(filePickerResult.fileAbsoluteUrl));
|
||||
}}
|
||||
|
@ -65,6 +70,7 @@ export default class ReactImageEditor extends React.Component<IReactImageEditorP
|
|||
|
||||
}}
|
||||
context={this.props.context}
|
||||
|
||||
/>}
|
||||
|
||||
{!isConfigured ? (<Placeholder iconName='Edit'
|
||||
|
@ -73,16 +79,16 @@ export default class ReactImageEditor extends React.Component<IReactImageEditorP
|
|||
buttonLabel='Configure'
|
||||
onConfigure={this._onConfigure} />) :
|
||||
(
|
||||
<ImageManipulation
|
||||
settings={this.props.settings}
|
||||
configSettings={{
|
||||
rotateButtons: [-90, -45, -30, 0, 30, 45, 90]
|
||||
}
|
||||
}
|
||||
displayMode={this.props.displayMode}
|
||||
settingsChanged={this._onSettingsChanged}
|
||||
src={this.props.url}
|
||||
/>
|
||||
<ImageManipulation
|
||||
settings={this.props.settings}
|
||||
configSettings={{
|
||||
rotateButtons: [-90, -45, -30, 0, 30, 45, 90]
|
||||
}
|
||||
}
|
||||
displayMode={this.props.displayMode}
|
||||
settingsChanged={this._onSettingsChanged}
|
||||
src={this.props.url} altText={this.props.altText}
|
||||
/>
|
||||
)}
|
||||
|
||||
</div >
|
||||
|
@ -95,7 +101,7 @@ export default class ReactImageEditor extends React.Component<IReactImageEditorP
|
|||
this._onUrlChanged(
|
||||
'https://media.gettyimages.com/photos/'
|
||||
+ 'whitewater-paddlers-descend-vertical-waterfall-in-kayak-picture-id1256321293?s=2048x2048'
|
||||
);
|
||||
);
|
||||
});
|
||||
} else {
|
||||
this.setState({ isFilePickerOpen: true });
|
||||
|
|
|
@ -1,7 +1,10 @@
|
|||
define([], function() {
|
||||
return {
|
||||
"PropertyPaneDescription": "Description",
|
||||
"BasicGroupName": "Group Name",
|
||||
"ShowTitleFieldLabel": "Show webpart title"
|
||||
"PropertyPaneDescription": "",
|
||||
"BasicGroupName": "Web part configuration",
|
||||
"ShowTitleFieldLabel": "Show webpart title",
|
||||
"ShowEditIconFieldLabel": "Show edit icon",
|
||||
"AltTextFieldLabel":"Alt Text"
|
||||
|
||||
}
|
||||
});
|
||||
|
|
|
@ -2,6 +2,8 @@ declare interface IReactImageEditorWebPartStrings {
|
|||
PropertyPaneDescription: string;
|
||||
BasicGroupName: string;
|
||||
ShowTitleFieldLabel: string;
|
||||
ShowEditIconFieldLabel: string;
|
||||
AltTextFieldLabel: string;
|
||||
}
|
||||
|
||||
declare module 'ReactImageEditorWebPartStrings' {
|
||||
|
|
Loading…
Reference in New Issue