Skip to content

Commit c84e2e7

Browse files
committed
commit changes new imagePicker
1 parent e52931f commit c84e2e7

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

45 files changed

+2913
-235
lines changed

.hintrc

+15
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
{
2+
"extends": [
3+
"development"
4+
],
5+
"hints": {
6+
"axe/aria": "off",
7+
"no-inline-styles": "off",
8+
"axe/text-alternatives": [
9+
"default",
10+
{
11+
"frame-title": "off"
12+
}
13+
]
14+
}
15+
}

package-lock.json

+41
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

+1
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,7 @@
7979
"react": "17.0.1",
8080
"react-accessible-accordion": "^5.0.0",
8181
"react-dom": "17.0.1",
82+
"react-dropzone": "^14.2.3",
8283
"react-mentions": "^4.3.0",
8384
"react-quill": "2.0.0",
8485
"regexify-string": "^1.0.16",

src/DynamicForm.ts

+2-1
Original file line numberDiff line numberDiff line change
@@ -1 +1,2 @@
1-
export * from './controls/dynamicForm/index';
1+
export * from './controls/dynamicForm/index';
2+
export * from './controls/dynamicForm/index';

src/ImagePicker.ts

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
export * from './controls/imagePicker/index';
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
export interface IFilePickerResult {
2+
/**
3+
* Selected file name with extension.
4+
*/
5+
fileName: string;
6+
/**
7+
* Selected file name without extension.
8+
*/
9+
fileNameWithoutExtension: string;
10+
/**
11+
* Absolute file URL. Undefined in case of file upload.
12+
*/
13+
fileAbsoluteUrl: string;
14+
15+
/**
16+
* Size of a selected file (in bytes). Undefined in all cases but file upload
17+
*/
18+
fileSize?: number;
19+
20+
/**
21+
* Absolute not modified file SharePoint URL.
22+
*/
23+
spItemUrl?: string;
24+
25+
/**
26+
* Downloads file picker result content.
27+
*/
28+
downloadFileContent: () => Promise<File>;
29+
30+
/**
31+
* Preview
32+
*/
33+
previewDataUrl?: string;
34+
}
35+
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
import { IImageFile } from './models/IImageFile';
2+
3+
export interface IImagePickerProps {
4+
onImageSelected: (image: IImageFile) => void;
5+
isOpen: boolean;
6+
onDismiss: () => void;
7+
8+
}
+141
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,141 @@
1+
import * as React from "react";
2+
3+
import strings from "ControlStrings";
4+
import { useAtom } from "jotai";
5+
6+
import {
7+
Button,
8+
Image,
9+
} from "@fluentui/react-components";
10+
import {
11+
Delete16Regular,
12+
Image20Regular,
13+
} from "@fluentui/react-icons";
14+
import { BaseComponentContext } from "@microsoft/sp-component-base";
15+
16+
import { contextState } from "./atoms/contextState";
17+
import { IFilePickerResult } from "./IFilePickerResult";
18+
/* import { RenderSpinner } from "./RenderSpninner/RenderSpinner"; */
19+
import { SelectFromSharePoint } from "./selectFromSharePoint";
20+
import { useImagePickerStyles } from "./useImagePickerStyles";
21+
22+
export interface IImagePickerProps {
23+
onFileSelected: (file: IFilePickerResult) => void;
24+
onDeleteFile: () => void;
25+
selectedFileUrl: string;
26+
context: BaseComponentContext;
27+
}
28+
29+
/**
30+
* Renders the preview image component.
31+
*
32+
* @param props - The component props.
33+
* @param props.selectedImageFileUrl - The URL of the selected image file.
34+
* @returns The JSX element representing the preview image component.
35+
*/
36+
const RenderPreviewImage = (props: { selectedImageFileUrl: string }): JSX.Element => {
37+
const { selectedImageFileUrl } = props;
38+
39+
const maxWidth = 200;
40+
const maxHeight = 200;
41+
const styles = useImagePickerStyles();
42+
43+
if (!selectedImageFileUrl) {
44+
return null;
45+
}
46+
47+
return (
48+
<>
49+
<div className={styles.renderImageContainer}>
50+
<Image
51+
src={selectedImageFileUrl}
52+
fit="cover"
53+
style={{ minWidth: maxWidth, maxWidth: maxWidth, height: maxHeight }}
54+
alt="Selected Image"
55+
/>
56+
</div>
57+
</>
58+
);
59+
};
60+
61+
/**
62+
* Renders an image picker component.
63+
*
64+
* @component
65+
* @example
66+
* ```tsx
67+
* <ImagePicker
68+
* onFileSelected={handleFileSelected}
69+
* onDeleteFile={handleDeleteFile}
70+
* selectedFileUrl={selectedImageUrl}
71+
* context={appContext}
72+
* />
73+
* ```
74+
*/
75+
76+
export const ImagePicker: React.FunctionComponent<IImagePickerProps> = (
77+
props: React.PropsWithChildren<IImagePickerProps>
78+
) => {
79+
const { onFileSelected, onDeleteFile, selectedFileUrl, context } = props;
80+
const [isOpen, setIsOpen] = React.useState<boolean>(false);
81+
const styles = useImagePickerStyles();
82+
const ref = React.useRef<HTMLDivElement>(null);
83+
const [appContext, setAppContext] = useAtom(contextState);
84+
85+
React.useEffect(() => {
86+
setAppContext({
87+
...appContext,
88+
context: context,
89+
});
90+
}, []);
91+
92+
const [selectedImageFileUrl, setSelectedImageFileUrl] = React.useState<string>(selectedFileUrl);
93+
94+
const onDismiss = (): void => {
95+
setIsOpen(false);
96+
};
97+
98+
const isFileSelected = React.useMemo(() => {
99+
return !!selectedImageFileUrl;
100+
}, [selectedImageFileUrl]);
101+
102+
const onDeleteFileCLick = React.useCallback(() => {
103+
setSelectedImageFileUrl(undefined);
104+
onDeleteFile();
105+
}, []);
106+
107+
const styleButtonDelete: React.CSSProperties = { display: !isFileSelected ? "none" : "inline-flex" };
108+
109+
if (!context) return null;
110+
111+
return (
112+
<>
113+
<div className={styles.root} ref={ref}>
114+
<div className={styles.buttonContainer}>
115+
<Button icon={<Image20Regular />} shape="circular" onClick={() => setIsOpen(true)}>
116+
{strings.ImagePickderSelectLabel}
117+
</Button>
118+
<Button
119+
icon={<Delete16Regular />}
120+
shape="circular"
121+
style={styleButtonDelete}
122+
onClick={() => onDeleteFileCLick()}
123+
>
124+
{strings.ImagePickerDeleteImageLabel}
125+
</Button>
126+
</div>
127+
<SelectFromSharePoint
128+
isOpen={isOpen}
129+
onDismiss={onDismiss}
130+
onFileSelected={async (file: IFilePickerResult) => {
131+
onFileSelected(file);
132+
setSelectedImageFileUrl(file.previewDataUrl);
133+
134+
onDismiss();
135+
}}
136+
/>
137+
{<RenderPreviewImage selectedImageFileUrl={selectedImageFileUrl} />}
138+
</div>
139+
</>
140+
);
141+
};
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
import * as React from 'react';
2+
3+
import {
4+
makeStyles,
5+
mergeClasses,
6+
Spinner,
7+
} from '@fluentui/react-components';
8+
9+
const useStyles = makeStyles({
10+
root: {
11+
display: "flex",
12+
justifyContent: "center",
13+
alignItems: "center",
14+
15+
height: "100%",
16+
width: "100%",
17+
},
18+
spinner: {
19+
width: "100px",
20+
height: "100px",
21+
},
22+
});
23+
24+
export interface IRenderSpinnerProps {
25+
size: "medium" | "small" | "extra-tiny" | "tiny" | "extra-small" | "large" | "extra-large" | "huge";
26+
label?: string;
27+
labelPosition?: "above" | "below" | "before" | "after";
28+
style?: React.CSSProperties;
29+
className?: string;
30+
}
31+
32+
export const RenderSpinner: React.FunctionComponent<IRenderSpinnerProps> = (
33+
props: React.PropsWithChildren<IRenderSpinnerProps>
34+
) => {
35+
const { size, label, labelPosition, style, className } = props;
36+
37+
const styles = useStyles();
38+
return (
39+
<div className={styles.root}>
40+
<Spinner
41+
style={style}
42+
className={mergeClasses(styles.spinner, className)}
43+
size={size}
44+
label={label}
45+
labelPosition={labelPosition}
46+
/>
47+
</div>
48+
);
49+
};

0 commit comments

Comments
 (0)