diff --git a/DNN Platform/Modules/ResourceManager/App_LocalResources/ResourceManager.resx b/DNN Platform/Modules/ResourceManager/App_LocalResources/ResourceManager.resx index 48a8e50c431..1406766b1ec 100644 --- a/DNN Platform/Modules/ResourceManager/App_LocalResources/ResourceManager.resx +++ b/DNN Platform/Modules/ResourceManager/App_LocalResources/ResourceManager.resx @@ -339,4 +339,7 @@ Items + + Mapped Path + \ No newline at end of file diff --git a/DNN Platform/Modules/ResourceManager/ResourceManager.Web/src/components.d.ts b/DNN Platform/Modules/ResourceManager/ResourceManager.Web/src/components.d.ts index 8add285841a..b3018e14bcc 100644 --- a/DNN Platform/Modules/ResourceManager/ResourceManager.Web/src/components.d.ts +++ b/DNN Platform/Modules/ResourceManager/ResourceManager.Web/src/components.d.ts @@ -8,17 +8,31 @@ import { HTMLStencilElement, JSXBase } from "@stencil/core/internal"; import { FolderTreeItem } from "./services/InternalServicesClient"; import { GetFolderContentResponse } from "./services/ItemsClient"; export namespace Components { + interface DnnActionCreateFolder { + "parentFolderId": number; + } interface DnnResourceManager { + /** + * The ID of the module. + */ "moduleId": number; } interface DnnRmActionsBar { } + interface DnnRmEditFolder { + } interface DnnRmFilesPane { /** * Defines how much more pixels to load under the fold. */ "preloadOffset": number; } + interface DnnRmFolderContextMenu { + /** + * The ID of the folder onto which the context menu was triggered on. + */ + "clickedFolderId": number; + } interface DnnRmFolderList { } interface DnnRmFolderListItem { @@ -32,9 +46,15 @@ export namespace Components { "folder": FolderTreeItem; } interface DnnRmItemsCardview { + /** + * The list of current items. + */ "currentItems": GetFolderContentResponse; } interface DnnRmItemsListview { + /** + * The list of current items. + */ "currentItems": GetFolderContentResponse; } interface DnnRmLeftPane { @@ -47,6 +67,12 @@ export namespace Components { } } declare global { + interface HTMLDnnActionCreateFolderElement extends Components.DnnActionCreateFolder, HTMLStencilElement { + } + var HTMLDnnActionCreateFolderElement: { + prototype: HTMLDnnActionCreateFolderElement; + new (): HTMLDnnActionCreateFolderElement; + }; interface HTMLDnnResourceManagerElement extends Components.DnnResourceManager, HTMLStencilElement { } var HTMLDnnResourceManagerElement: { @@ -59,12 +85,24 @@ declare global { prototype: HTMLDnnRmActionsBarElement; new (): HTMLDnnRmActionsBarElement; }; + interface HTMLDnnRmEditFolderElement extends Components.DnnRmEditFolder, HTMLStencilElement { + } + var HTMLDnnRmEditFolderElement: { + prototype: HTMLDnnRmEditFolderElement; + new (): HTMLDnnRmEditFolderElement; + }; interface HTMLDnnRmFilesPaneElement extends Components.DnnRmFilesPane, HTMLStencilElement { } var HTMLDnnRmFilesPaneElement: { prototype: HTMLDnnRmFilesPaneElement; new (): HTMLDnnRmFilesPaneElement; }; + interface HTMLDnnRmFolderContextMenuElement extends Components.DnnRmFolderContextMenu, HTMLStencilElement { + } + var HTMLDnnRmFolderContextMenuElement: { + prototype: HTMLDnnRmFolderContextMenuElement; + new (): HTMLDnnRmFolderContextMenuElement; + }; interface HTMLDnnRmFolderListElement extends Components.DnnRmFolderList, HTMLStencilElement { } var HTMLDnnRmFolderListElement: { @@ -114,9 +152,12 @@ declare global { new (): HTMLDnnRmTopBarElement; }; interface HTMLElementTagNameMap { + "dnn-action-create-folder": HTMLDnnActionCreateFolderElement; "dnn-resource-manager": HTMLDnnResourceManagerElement; "dnn-rm-actions-bar": HTMLDnnRmActionsBarElement; + "dnn-rm-edit-folder": HTMLDnnRmEditFolderElement; "dnn-rm-files-pane": HTMLDnnRmFilesPaneElement; + "dnn-rm-folder-context-menu": HTMLDnnRmFolderContextMenuElement; "dnn-rm-folder-list": HTMLDnnRmFolderListElement; "dnn-rm-folder-list-item": HTMLDnnRmFolderListItemElement; "dnn-rm-items-cardview": HTMLDnnRmItemsCardviewElement; @@ -128,17 +169,35 @@ declare global { } } declare namespace LocalJSX { + interface DnnActionCreateFolder { + "parentFolderId"?: number; + } interface DnnResourceManager { + /** + * The ID of the module. + */ "moduleId": number; } interface DnnRmActionsBar { } + interface DnnRmEditFolder { + /** + * Fires when there is a possibility that some folders have changed. Can be used to force parts of the UI to refresh. + */ + "onDnnRmFoldersChanged"?: (event: CustomEvent) => void; + } interface DnnRmFilesPane { /** * Defines how much more pixels to load under the fold. */ "preloadOffset"?: number; } + interface DnnRmFolderContextMenu { + /** + * The ID of the folder onto which the context menu was triggered on. + */ + "clickedFolderId": number; + } interface DnnRmFolderList { } interface DnnRmFolderListItem { @@ -150,11 +209,21 @@ declare namespace LocalJSX { * The basic information about the folder */ "folder": FolderTreeItem; + /** + * Fires when a context menu is opened for this item. Emits the folder ID. + */ + "onDnnRmcontextMenuOpened"?: (event: CustomEvent) => void; } interface DnnRmItemsCardview { + /** + * The list of current items. + */ "currentItems": GetFolderContentResponse; } interface DnnRmItemsListview { + /** + * The list of current items. + */ "currentItems": GetFolderContentResponse; } interface DnnRmLeftPane { @@ -166,9 +235,12 @@ declare namespace LocalJSX { interface DnnRmTopBar { } interface IntrinsicElements { + "dnn-action-create-folder": DnnActionCreateFolder; "dnn-resource-manager": DnnResourceManager; "dnn-rm-actions-bar": DnnRmActionsBar; + "dnn-rm-edit-folder": DnnRmEditFolder; "dnn-rm-files-pane": DnnRmFilesPane; + "dnn-rm-folder-context-menu": DnnRmFolderContextMenu; "dnn-rm-folder-list": DnnRmFolderList; "dnn-rm-folder-list-item": DnnRmFolderListItem; "dnn-rm-items-cardview": DnnRmItemsCardview; @@ -183,9 +255,12 @@ export { LocalJSX as JSX }; declare module "@stencil/core" { export namespace JSX { interface IntrinsicElements { + "dnn-action-create-folder": LocalJSX.DnnActionCreateFolder & JSXBase.HTMLAttributes; "dnn-resource-manager": LocalJSX.DnnResourceManager & JSXBase.HTMLAttributes; "dnn-rm-actions-bar": LocalJSX.DnnRmActionsBar & JSXBase.HTMLAttributes; + "dnn-rm-edit-folder": LocalJSX.DnnRmEditFolder & JSXBase.HTMLAttributes; "dnn-rm-files-pane": LocalJSX.DnnRmFilesPane & JSXBase.HTMLAttributes; + "dnn-rm-folder-context-menu": LocalJSX.DnnRmFolderContextMenu & JSXBase.HTMLAttributes; "dnn-rm-folder-list": LocalJSX.DnnRmFolderList & JSXBase.HTMLAttributes; "dnn-rm-folder-list-item": LocalJSX.DnnRmFolderListItem & JSXBase.HTMLAttributes; "dnn-rm-items-cardview": LocalJSX.DnnRmItemsCardview & JSXBase.HTMLAttributes; diff --git a/DNN Platform/Modules/ResourceManager/ResourceManager.Web/src/components/actions/dnn-action-create-folder/dnn-action-create-folder.tsx b/DNN Platform/Modules/ResourceManager/ResourceManager.Web/src/components/actions/dnn-action-create-folder/dnn-action-create-folder.tsx new file mode 100644 index 00000000000..23e6b1a9978 --- /dev/null +++ b/DNN Platform/Modules/ResourceManager/ResourceManager.Web/src/components/actions/dnn-action-create-folder/dnn-action-create-folder.tsx @@ -0,0 +1,54 @@ +import { Component, Host, h, Prop } from '@stencil/core'; +import { ItemsClient } from '../../../services/ItemsClient'; +import state from "../../../store/store"; + +@Component({ + tag: 'dnn-action-create-folder', + styleUrl: '../dnn-action.scss', + shadow: true, +}) +export class DnnActionCreateFolder { + + @Prop() parentFolderId: number; + + private readonly itemsClient: ItemsClient; + + constructor(){ + this.itemsClient = new ItemsClient(state.moduleId); + } + + private handleClick(): void { + if (this.parentFolderId){ + this.itemsClient.getFolderContent(this.parentFolderId, 0, 0) + .then(data => { + state.currentItems = data; + this.showModal(); + }) + .catch(error => alert(error)); + return; + } + + this.showModal(); + } + + private showModal(){ + const modal = document.createElement("dnn-modal"); + modal.backdropDismiss = false; + modal.showCloseButton = false; + const editor = document.createElement("dnn-rm-edit-folder"); + modal.appendChild(editor); + document.body.appendChild(modal); + modal.show(); + } + + render() { + return ( + + + + ); + } +} diff --git a/DNN Platform/Modules/ResourceManager/ResourceManager.Web/src/components/actions/dnn-action-create-folder/readme.md b/DNN Platform/Modules/ResourceManager/ResourceManager.Web/src/components/actions/dnn-action-create-folder/readme.md new file mode 100644 index 00000000000..58351e39c3f --- /dev/null +++ b/DNN Platform/Modules/ResourceManager/ResourceManager.Web/src/components/actions/dnn-action-create-folder/readme.md @@ -0,0 +1,42 @@ +# dnn-action-create-folder + + + + + + +## Properties + +| Property | Attribute | Description | Type | Default | +| ---------------- | ------------------ | ----------- | -------- | ----------- | +| `parentFolderId` | `parent-folder-id` | | `number` | `undefined` | + + +## Dependencies + +### Used by + + - [dnn-rm-actions-bar](../../dnn-rm-actions-bar) + - [dnn-rm-folder-context-menu](../../context-menus/dnn-rm-folder-context-menu) + +### Depends on + +- dnn-modal +- [dnn-rm-edit-folder](../../dnn-rm-edit-folder) + +### Graph +```mermaid +graph TD; + dnn-action-create-folder --> dnn-modal + dnn-action-create-folder --> dnn-rm-edit-folder + dnn-rm-edit-folder --> dnn-button + dnn-button --> dnn-modal + dnn-button --> dnn-button + dnn-rm-actions-bar --> dnn-action-create-folder + dnn-rm-folder-context-menu --> dnn-action-create-folder + style dnn-action-create-folder fill:#f9f,stroke:#333,stroke-width:4px +``` + +---------------------------------------------- + +*Built with [StencilJS](https://stenciljs.com/)* diff --git a/DNN Platform/Modules/ResourceManager/ResourceManager.Web/src/components/actions/dnn-action.scss b/DNN Platform/Modules/ResourceManager/ResourceManager.Web/src/components/actions/dnn-action.scss new file mode 100644 index 00000000000..a7550b139a0 --- /dev/null +++ b/DNN Platform/Modules/ResourceManager/ResourceManager.Web/src/components/actions/dnn-action.scss @@ -0,0 +1,16 @@ +:host{ + display: inline-flex; +} + +button{ + cursor: pointer; + border: none; + background-color: transparent; + margin: 0; + padding: 0; + display: flex; + align-items: center; + svg{ + fill: var(--dnn-color-primary); + } +} \ No newline at end of file diff --git a/DNN Platform/Modules/ResourceManager/ResourceManager.Web/src/components/context-menus/context-menu.scss b/DNN Platform/Modules/ResourceManager/ResourceManager.Web/src/components/context-menus/context-menu.scss new file mode 100644 index 00000000000..2605ed33515 --- /dev/null +++ b/DNN Platform/Modules/ResourceManager/ResourceManager.Web/src/components/context-menus/context-menu.scss @@ -0,0 +1,12 @@ +:host{ + display: flex; + flex-direction: column; +} +:host(*){ + background-color: transparent; + border: none; + padding: 0.5em 1em; + margin: 0; + border-bottom: 1px solid lightgray; + white-space: nowrap; +} \ No newline at end of file diff --git a/DNN Platform/Modules/ResourceManager/ResourceManager.Web/src/components/context-menus/dnn-rm-folder-context-menu/dnn-rm-folder-context-menu.tsx b/DNN Platform/Modules/ResourceManager/ResourceManager.Web/src/components/context-menus/dnn-rm-folder-context-menu/dnn-rm-folder-context-menu.tsx new file mode 100644 index 00000000000..ccd3b93ea8b --- /dev/null +++ b/DNN Platform/Modules/ResourceManager/ResourceManager.Web/src/components/context-menus/dnn-rm-folder-context-menu/dnn-rm-folder-context-menu.tsx @@ -0,0 +1,24 @@ +import { Component, Host, h, Prop } from '@stencil/core'; +import state from '../../../store/store'; + +@Component({ + tag: 'dnn-rm-folder-context-menu', + styleUrl: '../context-menu.scss', + shadow: true, +}) +export class DnnRmFolderContextMenu { + + /** The ID of the folder onto which the context menu was triggered on. */ + @Prop() clickedFolderId!: number; + + render() { + return ( + + {state.currentItems?.hasAddFoldersPermission && + + } + + ); + } + +} diff --git a/DNN Platform/Modules/ResourceManager/ResourceManager.Web/src/components/context-menus/dnn-rm-folder-context-menu/readme.md b/DNN Platform/Modules/ResourceManager/ResourceManager.Web/src/components/context-menus/dnn-rm-folder-context-menu/readme.md new file mode 100644 index 00000000000..54c13e38d6b --- /dev/null +++ b/DNN Platform/Modules/ResourceManager/ResourceManager.Web/src/components/context-menus/dnn-rm-folder-context-menu/readme.md @@ -0,0 +1,44 @@ +# dnn-rm-folder-context-menu + + + + + + +## Properties + +| Property | Attribute | Description | Type | Default | +| ------------------------------ | ------------------- | ------------------------------------------------------------------ | -------- | ----------- | +| `clickedFolderId` _(required)_ | `clicked-folder-id` | The ID of the folder onto which the context menu was triggered on. | `number` | `undefined` | + + +## Dependencies + +### Used by + + - [dnn-rm-folder-list-item](../../dnn-rm-folder-list-item) + - [dnn-rm-items-cardview](../../dnn-rm-items-cardview) + - [dnn-rm-items-listview](../../dnn-rm-items-listview) + +### Depends on + +- [dnn-action-create-folder](../../actions/dnn-action-create-folder) + +### Graph +```mermaid +graph TD; + dnn-rm-folder-context-menu --> dnn-action-create-folder + dnn-action-create-folder --> dnn-modal + dnn-action-create-folder --> dnn-rm-edit-folder + dnn-rm-edit-folder --> dnn-button + dnn-button --> dnn-modal + dnn-button --> dnn-button + dnn-rm-folder-list-item --> dnn-rm-folder-context-menu + dnn-rm-items-cardview --> dnn-rm-folder-context-menu + dnn-rm-items-listview --> dnn-rm-folder-context-menu + style dnn-rm-folder-context-menu fill:#f9f,stroke:#333,stroke-width:4px +``` + +---------------------------------------------- + +*Built with [StencilJS](https://stenciljs.com/)* diff --git a/DNN Platform/Modules/ResourceManager/ResourceManager.Web/src/components/dnn-resource-manager/dnn-resource-manager.tsx b/DNN Platform/Modules/ResourceManager/ResourceManager.Web/src/components/dnn-resource-manager/dnn-resource-manager.tsx index fc344dd7c94..5cf0659b910 100644 --- a/DNN Platform/Modules/ResourceManager/ResourceManager.Web/src/components/dnn-resource-manager/dnn-resource-manager.tsx +++ b/DNN Platform/Modules/ResourceManager/ResourceManager.Web/src/components/dnn-resource-manager/dnn-resource-manager.tsx @@ -1,4 +1,4 @@ -import { Component, Host, h, State, Watch, Prop } from '@stencil/core'; +import { Component, Element, Host, h, State, Watch, Prop } from '@stencil/core'; import state from "../../store/store"; import { LocalizationClient } from "../../services/LocalizationClient"; import { sortField } from '../../enums/SortField'; @@ -11,6 +11,7 @@ const localStorageSplitWidthKey = "dnn-resource-manager-last-folders-width"; }) export class DnnResourceManager { + /** The ID of the module. */ @Prop() moduleId!: number; constructor() { @@ -18,6 +19,8 @@ export class DnnResourceManager { this.localizationClient = new LocalizationClient(this.moduleId); } + @Element() el: HTMLDnnResourceManagerElement; + @State() foldersExpanded = true; @Watch("foldersExpanded") async foldersExpandedChanged(expanded: boolean){ diff --git a/DNN Platform/Modules/ResourceManager/ResourceManager.Web/src/components/dnn-resource-manager/readme.md b/DNN Platform/Modules/ResourceManager/ResourceManager.Web/src/components/dnn-resource-manager/readme.md index 782c200fe14..49813ae5d3b 100644 --- a/DNN Platform/Modules/ResourceManager/ResourceManager.Web/src/components/dnn-resource-manager/readme.md +++ b/DNN Platform/Modules/ResourceManager/ResourceManager.Web/src/components/dnn-resource-manager/readme.md @@ -7,9 +7,9 @@ ## Properties -| Property | Attribute | Description | Type | Default | -| ----------------------- | ----------- | ----------- | -------- | ----------- | -| `moduleId` _(required)_ | `module-id` | | `number` | `undefined` | +| Property | Attribute | Description | Type | Default | +| ----------------------- | ----------- | --------------------- | -------- | ----------- | +| `moduleId` _(required)_ | `module-id` | The ID of the module. | `number` | `undefined` | ## Dependencies @@ -33,14 +33,27 @@ graph TD; dnn-rm-folder-list --> dnn-rm-folder-list-item dnn-rm-folder-list-item --> dnn-treeview-item dnn-rm-folder-list-item --> dnn-rm-folder-list-item + dnn-rm-folder-list-item --> dnn-collapsible + dnn-rm-folder-list-item --> dnn-rm-folder-context-menu dnn-treeview-item --> dnn-collapsible + dnn-rm-folder-context-menu --> dnn-action-create-folder + dnn-action-create-folder --> dnn-modal + dnn-action-create-folder --> dnn-rm-edit-folder + dnn-rm-edit-folder --> dnn-button + dnn-button --> dnn-modal + dnn-button --> dnn-button dnn-rm-right-pane --> dnn-rm-actions-bar dnn-rm-right-pane --> dnn-rm-files-pane dnn-rm-right-pane --> dnn-rm-status-bar dnn-rm-actions-bar --> dnn-vertical-overflow-menu + dnn-rm-actions-bar --> dnn-action-create-folder dnn-rm-actions-bar --> dnn-collapsible dnn-rm-files-pane --> dnn-rm-items-listview dnn-rm-files-pane --> dnn-rm-items-cardview + dnn-rm-items-listview --> dnn-collapsible + dnn-rm-items-listview --> dnn-rm-folder-context-menu + dnn-rm-items-cardview --> dnn-collapsible + dnn-rm-items-cardview --> dnn-rm-folder-context-menu style dnn-resource-manager fill:#f9f,stroke:#333,stroke-width:4px ``` diff --git a/DNN Platform/Modules/ResourceManager/ResourceManager.Web/src/components/dnn-rm-actions-bar/dnn-rm-actions-bar.scss b/DNN Platform/Modules/ResourceManager/ResourceManager.Web/src/components/dnn-rm-actions-bar/dnn-rm-actions-bar.scss index a6f62c226de..574e91df41f 100644 --- a/DNN Platform/Modules/ResourceManager/ResourceManager.Web/src/components/dnn-rm-actions-bar/dnn-rm-actions-bar.scss +++ b/DNN Platform/Modules/ResourceManager/ResourceManager.Web/src/components/dnn-rm-actions-bar/dnn-rm-actions-bar.scss @@ -55,4 +55,5 @@ button{ } dnn-vertical-overflow-menu{ flex-grow: 1; + margin-left:24px; } diff --git a/DNN Platform/Modules/ResourceManager/ResourceManager.Web/src/components/dnn-rm-actions-bar/dnn-rm-actions-bar.tsx b/DNN Platform/Modules/ResourceManager/ResourceManager.Web/src/components/dnn-rm-actions-bar/dnn-rm-actions-bar.tsx index d56e6e32cb6..6c484731ad8 100644 --- a/DNN Platform/Modules/ResourceManager/ResourceManager.Web/src/components/dnn-rm-actions-bar/dnn-rm-actions-bar.tsx +++ b/DNN Platform/Modules/ResourceManager/ResourceManager.Web/src/components/dnn-rm-actions-bar/dnn-rm-actions-bar.tsx @@ -1,7 +1,6 @@ import { Component, Host, h, State } from '@stencil/core'; import state from '../../store/store'; import { sortField, SortFieldInfo } from "../../enums/SortField"; - @Component({ tag: 'dnn-rm-actions-bar', styleUrl: 'dnn-rm-actions-bar.scss', @@ -49,22 +48,9 @@ export class DnnRmActionsBar { return ( - - - - + {state.selectedItems && state.selectedItems.length == 0 && state.currentItems && state.currentItems.hasAddFilesPermission && + + }
{state.selectedItems.length > 0 && diff --git a/DNN Platform/Modules/ResourceManager/ResourceManager.Web/src/components/dnn-rm-actions-bar/readme.md b/DNN Platform/Modules/ResourceManager/ResourceManager.Web/src/components/dnn-rm-actions-bar/readme.md index d92ff70403a..a0e66126c2b 100644 --- a/DNN Platform/Modules/ResourceManager/ResourceManager.Web/src/components/dnn-rm-actions-bar/readme.md +++ b/DNN Platform/Modules/ResourceManager/ResourceManager.Web/src/components/dnn-rm-actions-bar/readme.md @@ -14,13 +14,20 @@ ### Depends on - dnn-vertical-overflow-menu +- [dnn-action-create-folder](../actions/dnn-action-create-folder) - dnn-collapsible ### Graph ```mermaid graph TD; dnn-rm-actions-bar --> dnn-vertical-overflow-menu + dnn-rm-actions-bar --> dnn-action-create-folder dnn-rm-actions-bar --> dnn-collapsible + dnn-action-create-folder --> dnn-modal + dnn-action-create-folder --> dnn-rm-edit-folder + dnn-rm-edit-folder --> dnn-button + dnn-button --> dnn-modal + dnn-button --> dnn-button dnn-rm-right-pane --> dnn-rm-actions-bar style dnn-rm-actions-bar fill:#f9f,stroke:#333,stroke-width:4px ``` diff --git a/DNN Platform/Modules/ResourceManager/ResourceManager.Web/src/components/dnn-rm-edit-folder/dnn-rm-edit-folder.scss b/DNN Platform/Modules/ResourceManager/ResourceManager.Web/src/components/dnn-rm-edit-folder/dnn-rm-edit-folder.scss new file mode 100644 index 00000000000..898f5863232 --- /dev/null +++ b/DNN Platform/Modules/ResourceManager/ResourceManager.Web/src/components/dnn-rm-edit-folder/dnn-rm-edit-folder.scss @@ -0,0 +1,19 @@ +:host { + display: block; +} + +.form{ + label{ + font-weight: bold; + margin-top: 0.5em; + } + display: grid; + grid-template-columns: 1fr; +} + +.controls{ + margin-top: 2em; + display: flex; + justify-content: flex-end; + gap: 1em; +} \ No newline at end of file diff --git a/DNN Platform/Modules/ResourceManager/ResourceManager.Web/src/components/dnn-rm-edit-folder/dnn-rm-edit-folder.tsx b/DNN Platform/Modules/ResourceManager/ResourceManager.Web/src/components/dnn-rm-edit-folder/dnn-rm-edit-folder.tsx new file mode 100644 index 00000000000..64f3b26c4a1 --- /dev/null +++ b/DNN Platform/Modules/ResourceManager/ResourceManager.Web/src/components/dnn-rm-edit-folder/dnn-rm-edit-folder.tsx @@ -0,0 +1,167 @@ +import { Component, Host, h, Element, State, Event, EventEmitter } from '@stencil/core'; +import { CreateNewFolderRequest, FolderMappingInfo, ItemsClient } from '../../services/ItemsClient'; +import state from '../../store/store'; + +@Component({ + tag: 'dnn-rm-edit-folder', + styleUrl: 'dnn-rm-edit-folder.scss', + shadow: true, +}) +export class DnnRmEditFolder { + + /** + * Fires when there is a possibility that some folders have changed. + * Can be used to force parts of the UI to refresh. + */ + @Event() dnnRmFoldersChanged: EventEmitter; + + @Element() el : HTMLDnnRmEditFolderElement + + @State() folderMappings: FolderMappingInfo[]; + + @State() newFolderRequest: CreateNewFolderRequest = { + FolderMappingId: -1, + FolderName: "", + ParentFolderId: state.currentItems.folder.folderId, + } + + private readonly itemsClient: ItemsClient; + private nameField: HTMLInputElement; + + constructor(){ + this.itemsClient = new ItemsClient(state.moduleId); + } + + componentWillLoad() { + this.itemsClient.getFolderMappings() + .then(data => { + this.folderMappings = data.sort((a, b) => a.FolderMappingID - b.FolderMappingID); + this.newFolderRequest = { + ...this.newFolderRequest, + FolderMappingId: this.folderMappings[0].FolderMappingID, + ParentFolderId: state.currentItems.folder.folderId, + }; + }) + .catch(reason => console.error(reason)); + } + + componentDidLoad() { + setTimeout(() => { + this.nameField.focus(); + }, 350); + console.log({...state}); + } + + private handleCancel(): void { + const modal = this.el.parentElement as HTMLDnnModalElement; + modal.hide().then(() => { + setTimeout(() => { + document.body.removeChild(modal); + }, 300); + }); + } + + private handleTypeChanged(e: Event): void { + const select = e.target as HTMLSelectElement; + const option = select.options[select.selectedIndex] as HTMLOptionElement; + const newValue = Number.parseInt(option.value); + this.newFolderRequest = { + ...this.newFolderRequest, + FolderMappingId: newValue, + }; + } + + private handleSave(): void { + this.itemsClient.createNewFolder(this.newFolderRequest) + .then(() => { + this.dnnRmFoldersChanged.emit(); + state.currentItems = { + ...state.currentItems, + items: [], + }; + const modal = this.el.parentElement as HTMLDnnModalElement; + modal.hide().then(() => { + setTimeout(() => { + document.body.removeChild(modal); + }, 300); + }); + }) + .catch(error => alert(error)); + } + + private canChooseFolderProvider() { + return this.folderMappings && + this.folderMappings.find(f => f.FolderMappingID == state.currentItems.folder.folderMappingId).IsDefault; + } + + render() { + return ( + +

{state.localization.AddFolder}

+
+ {state.currentItems.folder.folderName.length > 0 &&[ + , + {state.currentItems.folder.folderName} + ]} + + this.nameField = el} + value={this.newFolderRequest.FolderName} + onInput={e => this.newFolderRequest = { + ...this.newFolderRequest, + FolderName: (e.target as HTMLInputElement).value, + }} + /> + {!this.newFolderRequest.FolderName && + {state.localization.FolderNameRequiredMessage} + } + {this.canChooseFolderProvider() && + [ + , + + ] + } + {this.canChooseFolderProvider() && this.folderMappings.find(m => m.FolderMappingID == this.newFolderRequest.FolderMappingId).IsDefault == false &&[ + , + this.newFolderRequest = { + ...this.newFolderRequest, + MappedName: (e.target as HTMLInputElement).value, + }} + /> + ]} +
+
+ this.handleCancel()} + > + {state.localization.Cancel} + + this.handleSave()} + > + {state.localization.Save} + +
+
+ ); + } +} diff --git a/DNN Platform/Modules/ResourceManager/ResourceManager.Web/src/components/dnn-rm-edit-folder/readme.md b/DNN Platform/Modules/ResourceManager/ResourceManager.Web/src/components/dnn-rm-edit-folder/readme.md new file mode 100644 index 00000000000..d34fa1b4186 --- /dev/null +++ b/DNN Platform/Modules/ResourceManager/ResourceManager.Web/src/components/dnn-rm-edit-folder/readme.md @@ -0,0 +1,37 @@ +# dnn-rm-edit-folder + + + + + + +## Events + +| Event | Description | Type | +| --------------------- | ------------------------------------------------------------------------------------------------------------------ | ------------------- | +| `dnnRmFoldersChanged` | Fires when there is a possibility that some folders have changed. Can be used to force parts of the UI to refresh. | `CustomEvent` | + + +## Dependencies + +### Used by + + - [dnn-action-create-folder](../actions/dnn-action-create-folder) + +### Depends on + +- dnn-button + +### Graph +```mermaid +graph TD; + dnn-rm-edit-folder --> dnn-button + dnn-button --> dnn-modal + dnn-button --> dnn-button + dnn-action-create-folder --> dnn-rm-edit-folder + style dnn-rm-edit-folder fill:#f9f,stroke:#333,stroke-width:4px +``` + +---------------------------------------------- + +*Built with [StencilJS](https://stenciljs.com/)* diff --git a/DNN Platform/Modules/ResourceManager/ResourceManager.Web/src/components/dnn-rm-files-pane/readme.md b/DNN Platform/Modules/ResourceManager/ResourceManager.Web/src/components/dnn-rm-files-pane/readme.md index ee8a012722e..700f908cd1a 100644 --- a/DNN Platform/Modules/ResourceManager/ResourceManager.Web/src/components/dnn-rm-files-pane/readme.md +++ b/DNN Platform/Modules/ResourceManager/ResourceManager.Web/src/components/dnn-rm-files-pane/readme.md @@ -28,6 +28,16 @@ graph TD; dnn-rm-files-pane --> dnn-rm-items-listview dnn-rm-files-pane --> dnn-rm-items-cardview + dnn-rm-items-listview --> dnn-collapsible + dnn-rm-items-listview --> dnn-rm-folder-context-menu + dnn-rm-folder-context-menu --> dnn-action-create-folder + dnn-action-create-folder --> dnn-modal + dnn-action-create-folder --> dnn-rm-edit-folder + dnn-rm-edit-folder --> dnn-button + dnn-button --> dnn-modal + dnn-button --> dnn-button + dnn-rm-items-cardview --> dnn-collapsible + dnn-rm-items-cardview --> dnn-rm-folder-context-menu dnn-rm-right-pane --> dnn-rm-files-pane style dnn-rm-files-pane fill:#f9f,stroke:#333,stroke-width:4px ``` diff --git a/DNN Platform/Modules/ResourceManager/ResourceManager.Web/src/components/dnn-rm-folder-list-item/dnn-rm-folder-list-item.scss b/DNN Platform/Modules/ResourceManager/ResourceManager.Web/src/components/dnn-rm-folder-list-item/dnn-rm-folder-list-item.scss index 3d26fc5c7b0..c808b568bdb 100644 --- a/DNN Platform/Modules/ResourceManager/ResourceManager.Web/src/components/dnn-rm-folder-list-item/dnn-rm-folder-list-item.scss +++ b/DNN Platform/Modules/ResourceManager/ResourceManager.Web/src/components/dnn-rm-folder-list-item/dnn-rm-folder-list-item.scss @@ -10,4 +10,12 @@ button{ background-color: transparent; gap: 0.5em; cursor: pointer; +} +dnn-collapsible{ + position: fixed; + display: none; + border: 1px solid lightgray; + box-shadow: 2px 2px 4px -2px; + background-color: white; + z-index: 1; } \ No newline at end of file diff --git a/DNN Platform/Modules/ResourceManager/ResourceManager.Web/src/components/dnn-rm-folder-list-item/dnn-rm-folder-list-item.tsx b/DNN Platform/Modules/ResourceManager/ResourceManager.Web/src/components/dnn-rm-folder-list-item/dnn-rm-folder-list-item.tsx index fbb9fc0e401..9d27ecc51ed 100644 --- a/DNN Platform/Modules/ResourceManager/ResourceManager.Web/src/components/dnn-rm-folder-list-item/dnn-rm-folder-list-item.tsx +++ b/DNN Platform/Modules/ResourceManager/ResourceManager.Web/src/components/dnn-rm-folder-list-item/dnn-rm-folder-list-item.tsx @@ -1,4 +1,4 @@ -import { Component, Host, h, Prop, State, Element } from '@stencil/core'; +import { Component, Host, h, Event, EventEmitter, Prop, State, Element, Listen } from '@stencil/core'; import { InternalServicesClient, FolderTreeItem } from '../../services/InternalServicesClient'; import { ItemsClient } from "../../services/ItemsClient"; import state from "../../store/store"; @@ -16,12 +16,32 @@ export class DnnRmFolderListItem { /** If true, this node will be expanded on load. */ @Prop({mutable: true}) expanded = false; + /** Fires when a context menu is opened for this item. Emits the folder ID. */ + @Event() dnnRmcontextMenuOpened: EventEmitter; + + @Listen("dnnRmcontextMenuOpened", {target: "body"}) + handleDnnRmContextMenuOpened(e: CustomEvent){ + if (Number.parseInt(this.folder.data.key) != e.detail){ + this.dismissContextMenu(); + } + } + + private dismissContextMenu() { + if (this.contextMenu && this.contextMenu.expanded){ + this.contextMenu.expanded = false; + requestAnimationFrame(() => { + this.contextMenu.style.display = "none"; + }); + } + } + @State() folderIconUrl: string; @Element() el!: HTMLDnnRmFolderListItemElement; private itemsClient: ItemsClient; private internalServicesClient: InternalServicesClient; + private contextMenu: HTMLDnnCollapsibleElement; constructor(){ this.itemsClient = new ItemsClient(state.moduleId); @@ -32,6 +52,11 @@ export class DnnRmFolderListItem { this.itemsClient.getFolderIconUrl(Number.parseInt(this.folder.data.key)) .then(data => this.folderIconUrl = data) .catch(error => console.error(error)); + document.addEventListener("click", this.dismissContextMenu.bind(this)); + } + + disconnectedCallback(){ + document.removeEventListener("click", this.dismissContextMenu.bind(this)); } private handleUserExpanded() { @@ -69,38 +94,50 @@ export class DnnRmFolderListItem { .catch(error => console.error(error)); } + private handleContextMenu(e: MouseEvent): void { + e.preventDefault(); + this.contextMenu.style.display = "block"; + this.contextMenu.style.left = `${e.pageX}px`; + this.contextMenu.style.top = `${e.pageY}px`; + this.contextMenu.expanded = true; + this.dnnRmcontextMenuOpened.emit(Number.parseInt(this.folder.data.key)); + } + render() { return ( - {this.folder && - this.handleUserExpanded()}> - - {this.folder.data.hasChildren && - [ -
-
- , - this.folder.children && this.folder.children.length > 0 && this.folder.children.map(child => - - ) - ]} -
- } + this.handleUserExpanded()} + > + + {this.folder.data.hasChildren && + [ +
+
+ , + this.folder.children && this.folder.children.length > 0 && this.folder.children.map(child => + + ) + ]} +
+ this.contextMenu = el}> + +
); } diff --git a/DNN Platform/Modules/ResourceManager/ResourceManager.Web/src/components/dnn-rm-folder-list-item/readme.md b/DNN Platform/Modules/ResourceManager/ResourceManager.Web/src/components/dnn-rm-folder-list-item/readme.md index f458cb9eba4..15af50e763d 100644 --- a/DNN Platform/Modules/ResourceManager/ResourceManager.Web/src/components/dnn-rm-folder-list-item/readme.md +++ b/DNN Platform/Modules/ResourceManager/ResourceManager.Web/src/components/dnn-rm-folder-list-item/readme.md @@ -13,6 +13,13 @@ | `folder` _(required)_ | -- | The basic information about the folder | `FolderTreeItem` | `undefined` | +## Events + +| Event | Description | Type | +| ------------------------ | ----------------------------------------------------------------------- | --------------------- | +| `dnnRmcontextMenuOpened` | Fires when a context menu is opened for this item. Emits the folder ID. | `CustomEvent` | + + ## Dependencies ### Used by @@ -24,12 +31,20 @@ - dnn-treeview-item - [dnn-rm-folder-list-item](.) +- dnn-collapsible +- [dnn-rm-folder-context-menu](../context-menus/dnn-rm-folder-context-menu) ### Graph ```mermaid graph TD; dnn-rm-folder-list-item --> dnn-rm-folder-list-item dnn-treeview-item --> dnn-collapsible + dnn-rm-folder-context-menu --> dnn-action-create-folder + dnn-action-create-folder --> dnn-modal + dnn-action-create-folder --> dnn-rm-edit-folder + dnn-rm-edit-folder --> dnn-button + dnn-button --> dnn-modal + dnn-button --> dnn-button dnn-rm-folder-list --> dnn-rm-folder-list-item style dnn-rm-folder-list-item fill:#f9f,stroke:#333,stroke-width:4px ``` diff --git a/DNN Platform/Modules/ResourceManager/ResourceManager.Web/src/components/dnn-rm-folder-list/dnn-rm-folder-list.tsx b/DNN Platform/Modules/ResourceManager/ResourceManager.Web/src/components/dnn-rm-folder-list/dnn-rm-folder-list.tsx index b88c666f663..fc6520ea678 100644 --- a/DNN Platform/Modules/ResourceManager/ResourceManager.Web/src/components/dnn-rm-folder-list/dnn-rm-folder-list.tsx +++ b/DNN Platform/Modules/ResourceManager/ResourceManager.Web/src/components/dnn-rm-folder-list/dnn-rm-folder-list.tsx @@ -1,4 +1,4 @@ -import { Component, Host, h, State } from '@stencil/core'; +import { Component, Host, h, State, Listen } from '@stencil/core'; import { InternalServicesClient } from "../../services/InternalServicesClient"; import { ItemsClient } from "../../services/ItemsClient"; import { GetFolderContentResponse } from "../../services/ItemsClient"; @@ -21,13 +21,27 @@ export class DnnRmFolderList { this.itemsClient = new ItemsClient(state.moduleId); } + @Listen("dnnRmFoldersChanged", {target: "document"}) + handleFoldersChanged(){ + this.getFolders(); + } + + private getFolders() { + return new Promise((resolve, reject) => { + this.internalServicesClient.getFolders() + .then(data => { + state.rootFolders = data; + resolve(data); + }) + .catch(reason => reject(reason)); + }); + } + componentWillLoad() { - this.internalServicesClient.getFolders() - .then(data => - { - state.rootFolders = data; + this.getFolders() + .then(() => { this.itemsClient.getFolderContent( - Number.parseInt(data.Tree.children[0].data.key), + Number.parseInt(state.rootFolders.Tree.children[0].data.key), 0, state.pageSize, state.sortField) @@ -37,7 +51,6 @@ export class DnnRmFolderList { .catch(error => alert(error.Message)); } - render() { return ( diff --git a/DNN Platform/Modules/ResourceManager/ResourceManager.Web/src/components/dnn-rm-folder-list/readme.md b/DNN Platform/Modules/ResourceManager/ResourceManager.Web/src/components/dnn-rm-folder-list/readme.md index 6017a9a6ea8..affba828b43 100644 --- a/DNN Platform/Modules/ResourceManager/ResourceManager.Web/src/components/dnn-rm-folder-list/readme.md +++ b/DNN Platform/Modules/ResourceManager/ResourceManager.Web/src/components/dnn-rm-folder-list/readme.md @@ -21,7 +21,15 @@ graph TD; dnn-rm-folder-list --> dnn-rm-folder-list-item dnn-rm-folder-list-item --> dnn-treeview-item dnn-rm-folder-list-item --> dnn-rm-folder-list-item + dnn-rm-folder-list-item --> dnn-collapsible + dnn-rm-folder-list-item --> dnn-rm-folder-context-menu dnn-treeview-item --> dnn-collapsible + dnn-rm-folder-context-menu --> dnn-action-create-folder + dnn-action-create-folder --> dnn-modal + dnn-action-create-folder --> dnn-rm-edit-folder + dnn-rm-edit-folder --> dnn-button + dnn-button --> dnn-modal + dnn-button --> dnn-button dnn-rm-left-pane --> dnn-rm-folder-list style dnn-rm-folder-list fill:#f9f,stroke:#333,stroke-width:4px ``` diff --git a/DNN Platform/Modules/ResourceManager/ResourceManager.Web/src/components/dnn-rm-items-cardview/dnn-rm-items-cardview.scss b/DNN Platform/Modules/ResourceManager/ResourceManager.Web/src/components/dnn-rm-items-cardview/dnn-rm-items-cardview.scss index a7bec1423c3..623e22fa6fc 100644 --- a/DNN Platform/Modules/ResourceManager/ResourceManager.Web/src/components/dnn-rm-items-cardview/dnn-rm-items-cardview.scss +++ b/DNN Platform/Modules/ResourceManager/ResourceManager.Web/src/components/dnn-rm-items-cardview/dnn-rm-items-cardview.scss @@ -41,4 +41,12 @@ margin: auto; } } +} +dnn-collapsible{ + position: fixed; + display: none; + border: 1px solid lightgray; + box-shadow: 2px 2px 4px -2px; + background-color: white; + z-index: 1; } \ No newline at end of file diff --git a/DNN Platform/Modules/ResourceManager/ResourceManager.Web/src/components/dnn-rm-items-cardview/dnn-rm-items-cardview.tsx b/DNN Platform/Modules/ResourceManager/ResourceManager.Web/src/components/dnn-rm-items-cardview/dnn-rm-items-cardview.tsx index e719a81c0a9..1bed17459fd 100644 --- a/DNN Platform/Modules/ResourceManager/ResourceManager.Web/src/components/dnn-rm-items-cardview/dnn-rm-items-cardview.tsx +++ b/DNN Platform/Modules/ResourceManager/ResourceManager.Web/src/components/dnn-rm-items-cardview/dnn-rm-items-cardview.tsx @@ -1,6 +1,7 @@ -import { Component, Host, h, Prop } from '@stencil/core'; -import { GetFolderContentResponse } from '../../services/ItemsClient'; -import { isItemSelected, toggleItemSelected } from '../../utilities/selection-utilities'; +import { Component, Host, h, Prop, Element } from '@stencil/core'; +import { GetFolderContentResponse, Item } from '../../services/ItemsClient'; +import state from '../../store/store'; +import { selectionUtilities } from '../../utilities/selection-utilities'; @Component({ tag: 'dnn-rm-items-cardview', @@ -9,8 +10,44 @@ import { isItemSelected, toggleItemSelected } from '../../utilities/selection-ut }) export class DnnRmItemsCardview { + /** The list of current items. */ @Prop() currentItems!: GetFolderContentResponse; + @Element() el: HTMLDnnRmItemsCardviewElement; + + componentWillLoad() { + document.addEventListener("click", this.dismissContextMenu.bind(this)); + } + + disconnectedCallback() { + document.removeEventListener("click", this.disconnectedCallback.bind(this)); + } + + private dismissContextMenu() { + const existingMenus = this.el.shadowRoot.querySelectorAll("dnn-collapsible"); + existingMenus?.forEach(existingMenu => this.el.shadowRoot.removeChild(existingMenu)); + } + + private handleContextMenu(e: MouseEvent, item: Item): void { + e.preventDefault(); + state.selectedItems = []; + this.dismissContextMenu(); + if (item.isFolder){ + const collapsible = document.createElement("dnn-collapsible"); + const folderContextMenu = document.createElement("dnn-rm-folder-context-menu"); + collapsible.appendChild(folderContextMenu); + folderContextMenu.clickedFolderId = item.itemId; + collapsible.style.left = `${e.pageX}px`; + collapsible.style.top = `${e.pageY}px`; + collapsible.style.display = "block"; + this.el.shadowRoot.appendChild(collapsible); + setTimeout(() => { + collapsible.expanded = true; + }, 100); + return; + } + } + render() { return ( @@ -19,13 +56,14 @@ export class DnnRmItemsCardview { {this.currentItems.items?.map(item =>