diff --git a/docs/documentation/docs/assets/FolderPicker-no-selection.png b/docs/documentation/docs/assets/FolderPicker-no-selection.png new file mode 100644 index 000000000..847d187a5 Binary files /dev/null and b/docs/documentation/docs/assets/FolderPicker-no-selection.png differ diff --git a/docs/documentation/docs/assets/FolderPicker-selected.png b/docs/documentation/docs/assets/FolderPicker-selected.png new file mode 100644 index 000000000..8ecfd60fd Binary files /dev/null and b/docs/documentation/docs/assets/FolderPicker-selected.png differ diff --git a/docs/documentation/docs/assets/FolderPicker-selection.png b/docs/documentation/docs/assets/FolderPicker-selection.png new file mode 100644 index 000000000..1362eee60 Binary files /dev/null and b/docs/documentation/docs/assets/FolderPicker-selection.png differ diff --git a/docs/documentation/docs/assets/FolderPicker.png b/docs/documentation/docs/assets/FolderPicker.png new file mode 100644 index 000000000..5ce0a6d72 Binary files /dev/null and b/docs/documentation/docs/assets/FolderPicker.png differ diff --git a/docs/documentation/docs/controls/FolderPicker.md b/docs/documentation/docs/controls/FolderPicker.md new file mode 100644 index 000000000..35d0fa5f8 --- /dev/null +++ b/docs/documentation/docs/controls/FolderPicker.md @@ -0,0 +1,68 @@ +# FolderPicker control + +This control allows you to explore and select a folder. +It also allows the user to create a new folder at the current level being explored. + +Here is an example of the control: + +![FolderPicker](../assets/FolderPicker.png) + +`FolderPicker` no selection: + +![FolderPicker no selection](../assets/FolderPicker-no-selection.png) + +`FolderPicker` selection: + +![FolderPicker selection](../assets/FolderPicker-selection.png) + +`FolderPicker` selected: + +![FolderPicker selected](../assets/FolderPicker-selected.png) + +## How to use this control in your solutions + +- Check that you installed the `@pnp/spfx-controls-react` dependency. Check out the [getting started](../../#getting-started) page for more information about installing the dependency. +- Import the control into your component: + +```TypeScript +import { FolderPicker, IFolder } from "@pnp/spfx-controls-react/lib/FolderPicker"; +``` + +- Use the `FolderPicker` control in your code as follows: + +```TypeScript + +``` + +- The `onSelect` change event returns the selected folder and can be implemented as follows: + +```TypeScript +private _onFolderSelect = (folder: IFolder): void => { + console.log('selected folder', folder); +} +``` + +## Implementation + +The `FolderPicker` control can be configured with the following properties: + +| Property | Type | Required | Description | +| ---- | ---- | ---- | ---- | +| context | WebPartContext \| ExtensionContext | yes | The context object of the SPFx loaded webpart or customizer. | +| label | string | yes | The label for the control. | +| rootFolder | IFolder | yes | The lowest level folder that can be explored. This can be the root folder of a library. | +| defaultFolder | IFolder | no | The default folder to be selected or explored. | +| required | boolean | no | Is selection required. | +| disabled | boolean | no | Is the control disabled. | +| canCreateFolders | boolean | no | Allow current user to create folders on the target location. If enabled, you need to ensure that the user has the required permissions. | +| onSelect | (folder: IFolder): void | no | Callback function called after a folder is selected. | + +![](https://telemetry.sharepointpnp.com/sp-dev-fx-controls-react/wiki/controls/FolderPicker) diff --git a/docs/documentation/docs/index.md b/docs/documentation/docs/index.md index 8ba297ed8..5f4696ec4 100644 --- a/docs/documentation/docs/index.md +++ b/docs/documentation/docs/index.md @@ -54,6 +54,7 @@ The following controls are currently available: - [FilePicker](./controls/FilePicker) (control that allows to browse and select a file from various places) - [FileTypeIcon](./controls/FileTypeIcon) (Control that shows the icon of a specified file path or application) - [FolderExplorer](./controls/FolderExplorer) (Control that allows to browse the folders and sub-folders from a root folder) +- [FolderPicker](./controls/FolderPicker) (Control that allows to browse and select a folder) - [GridLayout](./controls/GridLayout) (control that renders a responsive grid layout for your web parts) - [IconPicker](./controls/IconPicker) (control that allows to search and select an icon from office-ui-fabric icons) - [IFrameDialog](./controls/IFrameDialog) (renders a Dialog with an iframe as a content) diff --git a/docs/documentation/mkdocs.yml b/docs/documentation/mkdocs.yml index a8d8f557d..9c0637d93 100644 --- a/docs/documentation/mkdocs.yml +++ b/docs/documentation/mkdocs.yml @@ -18,6 +18,7 @@ nav: - FilePicker: 'controls/FilePicker.md' - FileTypeIcon: 'controls/FileTypeIcon.md' - FolderExplorer: 'controls/FolderExplorer.md' + - FolderPicker: 'controls/FolderPicker.md' - GridLayout: 'controls/GridLayout.md' - IconPicker: 'controls/IconPicker.md' - IFrameDialog: 'controls/IFrameDialog.md' diff --git a/src/FolderPicker.ts b/src/FolderPicker.ts new file mode 100644 index 000000000..a10850940 --- /dev/null +++ b/src/FolderPicker.ts @@ -0,0 +1 @@ +export * from './controls/folderPicker/index'; diff --git a/src/controls/folderPicker/FolderPicker.module.scss b/src/controls/folderPicker/FolderPicker.module.scss new file mode 100644 index 000000000..413fa398f --- /dev/null +++ b/src/controls/folderPicker/FolderPicker.module.scss @@ -0,0 +1,46 @@ +// @import "~@microsoft/sp-office-ui-fabric-core/dist/sass/SPFabricCore.scss"; +@import '~office-ui-fabric-react/dist/sass/References.scss'; + + +.folderPicker { + display: flex; + align-items: center; + + .selection { + width: 90%; + } + + .selectFolderLabel { + color: $ms-color-neutralSecondary; + } + + .selectFolder { + align-items: center; + display: flex; + margin-right: 5px; + max-width: 90%; + span { + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; + } + } + + & .selectButton { + width: 10%; + display: flex; + justify-content: center; + } +} + +.actions { + button { + margin-right: 15px; + } +} + +label.required::after { + content: " *"; + color: rgb(168, 0, 0); + padding-right: 12px; +} diff --git a/src/controls/folderPicker/FolderPicker.tsx b/src/controls/folderPicker/FolderPicker.tsx new file mode 100644 index 000000000..e779ac757 --- /dev/null +++ b/src/controls/folderPicker/FolderPicker.tsx @@ -0,0 +1,141 @@ +import * as React from 'react'; +import styles from './FolderPicker.module.scss'; +import { IFolderPickerProps, IFolderPickerState } from '.'; +import { IFolder } from '../../services/IFolderExplorerService'; +import { IconButton, PrimaryButton, DefaultButton } from 'office-ui-fabric-react/lib/Button'; +import { Label } from 'office-ui-fabric-react/lib/Label'; +import { Link } from 'office-ui-fabric-react/lib/Link'; +import { getId } from 'office-ui-fabric-react/lib/Utilities'; +import { Panel, PanelType } from 'office-ui-fabric-react/lib/Panel'; +import { FolderExplorer } from '../folderExplorer/FolderExplorer'; + + +export default class FolderPicker extends React.Component { + + private _folderLinkId = getId('folderLink'); + private _selectedFolder: IFolder; + + constructor(props: IFolderPickerProps) { + super(props); + + this.state = { + showPanel: false, + selectedFolder: this.props.defaultFolder + }; + } + + public componentWillReceiveProps(nextProps: IFolderPickerProps) { + + this.setState({ + selectedFolder: nextProps.defaultFolder, + }); + + } + + public render(): React.ReactElement { + return ( +
+ {this.props.label && + + } +
+
+ {!this.state.selectedFolder && + Select a folder + } + {this.state.selectedFolder && +
+ + {this.state.selectedFolder.Name} + + +
+ } +
+
+ +
+
+ + +
+ +
+
+ +
+ ); + } + + private _showPanel = () => { + this.setState({ showPanel: true }); + } + + private _hidePanel = () => { + this.setState({ showPanel: false }); + } + + private _onRenderFooterContent = () => { + return ( +
+ + Save + + + Cancel + +
+ ); + } + + private _onFolderSelect = (folder: IFolder): void => { + this._selectedFolder = folder; + } + + private _onFolderSave = (): void => { + this.setState({ + selectedFolder: this._selectedFolder, + showPanel: false, + }); + + this.props.onSelect(this._selectedFolder); + } + + private _resetSelection = (): void => { + this._selectedFolder = null; + + this.setState({ + selectedFolder: this._selectedFolder, + }); + + this.props.onSelect(this._selectedFolder); + } + + + +} diff --git a/src/controls/folderPicker/IFolderPickerProps.ts b/src/controls/folderPicker/IFolderPickerProps.ts new file mode 100644 index 000000000..58a3330db --- /dev/null +++ b/src/controls/folderPicker/IFolderPickerProps.ts @@ -0,0 +1,46 @@ +import { WebPartContext } from '@microsoft/sp-webpart-base'; +import { ExtensionContext } from '@microsoft/sp-extension-base'; +import { IFolder } from '../../services/IFolderExplorerService'; + +export interface IFolderPickerProps { + /** + * Current context + */ + context: WebPartContext | ExtensionContext; + + /** + * The label for the control + */ + label: string; + + /** + * The lowest level folder that can be explored. This can be the root folder of a library. + */ + rootFolder: IFolder; + + /** + * The default folder to be explored + */ + defaultFolder?: IFolder; + + /** + * Is selection required + */ + required?: boolean; + + /** + * Is the control disabled + */ + disabled?: boolean; + + /** + * Allow current user to create folders on the target location. If enabled, you need to ensure that the user has the required permissions + */ + canCreateFolders?: boolean; + + /** + * Callback function called after a folder is selected + * @argument folder The selected folder + */ + onSelect: (folder: IFolder) => void; +} diff --git a/src/controls/folderPicker/IFolderPickerState.ts b/src/controls/folderPicker/IFolderPickerState.ts new file mode 100644 index 000000000..382e6e28f --- /dev/null +++ b/src/controls/folderPicker/IFolderPickerState.ts @@ -0,0 +1,6 @@ +import { IFolder } from '../../services/IFolderExplorerService'; + +export interface IFolderPickerState { + showPanel: boolean; + selectedFolder: IFolder; +} diff --git a/src/controls/folderPicker/index.ts b/src/controls/folderPicker/index.ts new file mode 100644 index 000000000..60bd6b8a7 --- /dev/null +++ b/src/controls/folderPicker/index.ts @@ -0,0 +1,3 @@ +export * from './FolderPicker'; +export * from './IFolderPickerProps'; +export * from './IFolderPickerState'; diff --git a/src/index.ts b/src/index.ts index 3c97f4178..abdc4894e 100644 --- a/src/index.ts +++ b/src/index.ts @@ -12,6 +12,7 @@ export * from './Progress'; export * from './DateTimePicker'; export * from './FilePicker'; export * from './FolderExplorer'; +export * from './FolderPicker'; export * from './IconPicker'; export * from './Pagination'; diff --git a/src/webparts/controlsTest/components/ControlsTest.tsx b/src/webparts/controlsTest/components/ControlsTest.tsx index c1c984fe7..ac735c732 100644 --- a/src/webparts/controlsTest/components/ControlsTest.tsx +++ b/src/webparts/controlsTest/components/ControlsTest.tsx @@ -55,6 +55,7 @@ import { } from 'office-ui-fabric-react/lib/DocumentCard'; import { ImageFit } from 'office-ui-fabric-react/lib/Image'; import { FilePicker, IFilePickerResult } from '../../../FilePicker'; +import FolderPicker from '../../../controls/folderPicker/FolderPicker'; import { FolderExplorer, IFolder, IBreadcrumbItem } from '../../../FolderExplorer'; import { Pagination } from '../../../controls/pagination'; @@ -425,6 +426,11 @@ export default class ControlsTest extends React.Component { + console.log('selected folder', folder); + + } + private _onRenderGridItem = (item: any, _finalSize: ISize, isCompact: boolean): JSX.Element => { const previewProps: IDocumentCardPreviewProps = { previewImages: [ @@ -946,6 +952,18 @@ export default class ControlsTest extends React.Component { console.log('iframe loaded'); }} /> +
+ +
@@ -1029,21 +1047,19 @@ export default class ControlsTest extends React.Component - +

Tree View