diff --git a/src/Umbraco.Web.UI.Client/src/mocks/data/stylesheet.data.ts b/src/Umbraco.Web.UI.Client/src/mocks/data/stylesheet.data.ts index f23c5d19f9..2893fbe822 100644 --- a/src/Umbraco.Web.UI.Client/src/mocks/data/stylesheet.data.ts +++ b/src/Umbraco.Web.UI.Client/src/mocks/data/stylesheet.data.ts @@ -1,17 +1,19 @@ import { UmbEntityData } from './entity.data.js'; -import { createFileSystemTreeItem, createTextFileItem } from './utils.js'; +import { createFileSystemTreeItem, createItem, createTextFileItem } from './utils.js'; import { CreateTextFileViewModelBaseModel, FileSystemTreeItemPresentationModel, PagedFileSystemTreeItemPresentationModel, + PagedStylesheetOverviewResponseModel, StylesheetResponseModel, } from '@umbraco-cms/backoffice/backend-api'; -type StylesheetDBItem = StylesheetResponseModel & FileSystemTreeItemPresentationModel; +type StylesheetDBItem = StylesheetResponseModel & FileSystemTreeItemPresentationModel & { icon?: string }; export const data: Array = [ { path: 'Stylesheet File 1.css', + icon: 'style', isFolder: false, name: 'Stylesheet File 1.css', type: 'stylesheet', @@ -40,6 +42,8 @@ export const data: Array = [ { path: 'Stylesheet File 2.css', isFolder: false, + icon: 'style', + name: 'Stylesheet File 2.css', type: 'stylesheet', hasChildren: false, @@ -65,6 +69,8 @@ export const data: Array = [ { path: 'Folder 1', isFolder: true, + icon: 'folder', + name: 'Folder 1', type: 'stylesheet', hasChildren: true, @@ -90,6 +96,8 @@ export const data: Array = [ { path: 'Folder 1/Stylesheet File 3.css', isFolder: false, + icon: 'style', + name: 'Stylesheet File 3.css', type: 'stylesheet', hasChildren: false, @@ -142,17 +150,47 @@ class UmbStylesheetData extends UmbEntityData { return items.map((item) => createFileSystemTreeItem(item)); } - getStylesheet(path: string): StylesheetDBItem | undefined { + getStylesheetItem(path: string): StylesheetDBItem | undefined { + return createItem(this.data.find((item) => item.path === path)); + } + + getStylesheet(path: string): StylesheetResponseModel | undefined { return createTextFileItem(this.data.find((item) => item.path === path)); } + getAllStylesheets(): PagedStylesheetOverviewResponseModel { + return { + items: this.data.map((item) => createTextFileItem(item)), + total: this.data.map((item) => !item.isFolder).length, + }; + } + + getFolder(path: string): StylesheetDBItem | undefined { + return this.data.find((item) => item.path === path && item.isFolder === true); + } + + insertFolder(item: CreateTextFileViewModelBaseModel) { + const newItem: StylesheetDBItem = { + ...item, + path: `${item.parentPath}/${item.name}`, + isFolder: true, + hasChildren: false, + type: 'stylesheet', + icon: 'folder', + }; + + this.insert(newItem); + return newItem; + } + insertStyleSheet(item: CreateTextFileViewModelBaseModel) { const newItem: StylesheetDBItem = { ...item, - path: `${item.parentPath}/${item.name}.cshtml}`, + path: `${item.parentPath}/${item.name}.css`, isFolder: false, hasChildren: false, - type: 'partial-view', + type: 'stylesheet', + icon: 'style', }; this.insert(newItem); diff --git a/src/Umbraco.Web.UI.Client/src/mocks/data/utils.ts b/src/Umbraco.Web.UI.Client/src/mocks/data/utils.ts index df924c2e71..32caed7acf 100644 --- a/src/Umbraco.Web.UI.Client/src/mocks/data/utils.ts +++ b/src/Umbraco.Web.UI.Client/src/mocks/data/utils.ts @@ -80,3 +80,9 @@ export const createTextFileItem = (item: any): TextFileResponseModelBaseModel => name: item.name, content: item.content, }); + +export const createItem = (item: any): any => ({ + path: item.path, + name: item.name, + icon: item.icon, +}); diff --git a/src/Umbraco.Web.UI.Client/src/mocks/handlers/stylesheet.handlers.ts b/src/Umbraco.Web.UI.Client/src/mocks/handlers/stylesheet.handlers.ts index c657affed5..daa04b5623 100644 --- a/src/Umbraco.Web.UI.Client/src/mocks/handlers/stylesheet.handlers.ts +++ b/src/Umbraco.Web.UI.Client/src/mocks/handlers/stylesheet.handlers.ts @@ -1,6 +1,6 @@ const { rest } = window.MockServiceWorker; -import { CreateTextFileViewModelBaseModel } from '@umbraco-cms/backoffice/backend-api'; import { umbStylesheetData } from '../data/stylesheet.data.js'; +import { CreateTextFileViewModelBaseModel } from '@umbraco-cms/backoffice/backend-api'; import { umbracoPath } from '@umbraco-cms/backoffice/utils'; const treeHandlers = [ @@ -27,32 +27,68 @@ const treeHandlers = [ ]; const detailHandlers = [ - rest.get(umbracoPath('/v1/stylesheet'), (req, res, ctx) => { + rest.get(umbracoPath('/stylesheet'), (req, res, ctx) => { const path = req.url.searchParams.get('path'); if (!path) return; const response = umbStylesheetData.getStylesheet(path); return res(ctx.status(200), ctx.json(response)); }), - rest.post(umbracoPath('/partial-view'), (req, res, ctx) => { + rest.post(umbracoPath('/stylesheet'), (req, res, ctx) => { const requestBody = req.json() as CreateTextFileViewModelBaseModel; if (!requestBody) return res(ctx.status(400, 'no body found')); const response = umbStylesheetData.insertStyleSheet(requestBody); return res(ctx.status(200), ctx.json(response)); }), - rest.delete(umbracoPath('/partial-view'), (req, res, ctx) => { + rest.delete(umbracoPath('/stylesheet'), (req, res, ctx) => { const path = req.url.searchParams.get('path'); if (!path) return res(ctx.status(400)); const response = umbStylesheetData.delete([path]); return res(ctx.status(200), ctx.json(response)); }), - rest.put(umbracoPath('/partial-view'), (req, res, ctx) => { + rest.put(umbracoPath('/stylesheet'), (req, res, ctx) => { const requestBody = req.json() as CreateTextFileViewModelBaseModel; if (!requestBody) return res(ctx.status(400, 'no body found')); const response = umbStylesheetData.updateData(requestBody); return res(ctx.status(200)); }), + rest.get(umbracoPath('/v1/stylesheet/all'), (req, res, ctx) => { + const path = req.url.searchParams.get('path'); + if (!path) return; + + const response = umbStylesheetData.getAllStylesheets(); + return res(ctx.status(200), ctx.json(response)); + }), + rest.get(umbracoPath('/v1/stylesheet/item'), (req, res, ctx) => { + const paths = req.url.searchParams.getAll('path'); + if (!paths) return; + + const items = umbStylesheetData.getStylesheetItem(paths[0]); + return res(ctx.status(200), ctx.json(items)); + }), ]; -export const handlers = [...treeHandlers, ...detailHandlers]; +const folderHandlers = [ + rest.get(umbracoPath('/v1/stylesheet/all'), (req, res, ctx) => { + const path = req.url.searchParams.get('path'); + if (!path) return; + + const response = umbStylesheetData.getFolder(path); + return res(ctx.status(200), ctx.json(response)); + }), + rest.post(umbracoPath('/stylesheet/folder'), (req, res, ctx) => { + const requestBody = req.json() as CreateTextFileViewModelBaseModel; + if (!requestBody) return res(ctx.status(400, 'no body found')); + const response = umbStylesheetData.insertFolder(requestBody); + return res(ctx.status(200), ctx.json(response)); + }), + rest.delete(umbracoPath('/stylesheet/folder'), (req, res, ctx) => { + const path = req.url.searchParams.get('path'); + if (!path) return res(ctx.status(400)); + const response = umbStylesheetData.delete([path]); + return res(ctx.status(200), ctx.json(response)); + }), +]; + +export const handlers = [...treeHandlers, ...detailHandlers, ...folderHandlers]; diff --git a/src/Umbraco.Web.UI.Client/src/packages/templating/stylesheets/workspace/manifests.ts b/src/Umbraco.Web.UI.Client/src/packages/templating/stylesheets/workspace/manifests.ts index f341fbee8f..f2238b5937 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/templating/stylesheets/workspace/manifests.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/templating/stylesheets/workspace/manifests.ts @@ -14,7 +14,38 @@ const workspace: ManifestWorkspace = { }, }; -const workspaceViews: Array = []; +const workspaceEditorViews: Array = [ + { + type: 'workspaceEditorView', + alias: 'Umb.WorkspaceView.Stylesheet.CodeEditor', + name: 'Stylesheet Workspace Code Editor View', + loader: () => import('./views/code-editor/stylesheet-workspace-view-code-editor.element.js'), + weight: 700, + meta: { + label: 'Code', + pathname: 'code', + icon: 'umb:brackets', + }, + conditions: { + workspaces: ['Umb.Workspace.StyleSheet'], + }, + }, + { + type: 'workspaceEditorView', + alias: 'Umb.WorkspaceView.Stylesheet.RichTextEditor', + name: 'Stylesheet Workspace Rich Text Editor View', + loader: () => import('./views/rich-text-editor/stylesheet-workspace-view-rich-text-editor.element.js'), + weight: 800, + meta: { + label: 'Rich Text Editor', + pathname: 'rich-text-editor', + icon: 'umb:font', + }, + conditions: { + workspaces: ['Umb.Workspace.StyleSheet'], + }, + }, +]; const workspaceActions: Array = []; -export const manifests = [workspace, ...workspaceViews, ...workspaceActions]; +export const manifests = [workspace, ...workspaceEditorViews, ...workspaceActions]; diff --git a/src/Umbraco.Web.UI.Client/src/packages/templating/stylesheets/workspace/stylesheet-workspace-edit.element.ts b/src/Umbraco.Web.UI.Client/src/packages/templating/stylesheets/workspace/stylesheet-workspace-edit.element.ts index a9d15b63d8..eae185aa4a 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/templating/stylesheets/workspace/stylesheet-workspace-edit.element.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/templating/stylesheets/workspace/stylesheet-workspace-edit.element.ts @@ -1,10 +1,68 @@ -import { UUITextStyles } from '@umbraco-cms/backoffice/external/uui'; -import { css, html, LitElement, customElement } from '@umbraco-cms/backoffice/external/lit'; +import { UUIInputElement, UUIInputEvent, UUITextStyles } from '@umbraco-cms/backoffice/external/uui'; +import { css, html, LitElement, customElement, state } from '@umbraco-cms/backoffice/external/lit'; +import { UmbStylesheetWorkspaceContext } from './stylesheet-workspace.context.js'; +import { UMB_MODAL_MANAGER_CONTEXT_TOKEN, UmbModalManagerContext } from '@umbraco-cms/backoffice/modal'; +import { UMB_ENTITY_WORKSPACE_CONTEXT } from '@umbraco-cms/backoffice/workspace'; +import { UmbLitElement } from '@umbraco-cms/internal/lit-element'; @customElement('umb-stylesheet-workspace-edit') -export class UmbStylesheetWorkspaceEditElement extends LitElement { +export class UmbStylesheetWorkspaceEditElement extends UmbLitElement { + #workspaceContext?: UmbStylesheetWorkspaceContext; + + @state() + private _name?: string; + + @state() + private _path?: string; + + private _modalContext?: UmbModalManagerContext; + + constructor() { + super(); + + this.consumeContext(UMB_ENTITY_WORKSPACE_CONTEXT, (instance) => { + this.#workspaceContext = instance as UmbStylesheetWorkspaceContext; + }); + + this.consumeContext(UMB_MODAL_MANAGER_CONTEXT_TOKEN, (instance) => { + this._modalContext = instance; + }); + } + + #onNameChange(event: UUIInputEvent) { + if (event instanceof UUIInputEvent) { + const target = event.composedPath()[0] as UUIInputElement; + + if (typeof target?.value === 'string') { + const oldName = this._name; + const newName = event.target.value.toString(); + this.#workspaceContext?.setName(target.value); + } + } + } + render() { - return html` Stylesheet workspace `; + return html` + + + +
+ + + Keyboard Shortcuts + + ALT + + + shift + + + k + + +
+
+ `; } static styles = [ diff --git a/src/Umbraco.Web.UI.Client/src/packages/templating/stylesheets/workspace/views/code-editor/stylesheet-workspace-view-code-editor.element.ts b/src/Umbraco.Web.UI.Client/src/packages/templating/stylesheets/workspace/views/code-editor/stylesheet-workspace-view-code-editor.element.ts new file mode 100644 index 0000000000..fee430117a --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/packages/templating/stylesheets/workspace/views/code-editor/stylesheet-workspace-view-code-editor.element.ts @@ -0,0 +1,21 @@ +import { UUITextStyles } from '@umbraco-ui/uui-css'; +import { css, html } from 'lit'; +import { customElement } from 'lit/decorators.js'; +import { UmbLitElement } from '@umbraco-cms/internal/lit-element'; + +@customElement('umb-stylesheet-workspace-view-code-editor') +export class UmbStylesheetWorkspaceViewCodeEditorElement extends UmbLitElement { + render() { + return html`umb-stylesheet-workspace-view-code-editor`; + } + + static styles = [UUITextStyles, css``]; +} + +export default UmbStylesheetWorkspaceViewCodeEditorElement; + +declare global { + interface HTMLElementTagNameMap { + 'umb-stylesheet-workspace-view-code-editor': UmbStylesheetWorkspaceViewCodeEditorElement; + } +} diff --git a/src/Umbraco.Web.UI.Client/src/packages/templating/stylesheets/workspace/views/rich-text-editor/stylesheet-workspace-view-rich-text-editor.element.ts b/src/Umbraco.Web.UI.Client/src/packages/templating/stylesheets/workspace/views/rich-text-editor/stylesheet-workspace-view-rich-text-editor.element.ts new file mode 100644 index 0000000000..8ebb040a0f --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/packages/templating/stylesheets/workspace/views/rich-text-editor/stylesheet-workspace-view-rich-text-editor.element.ts @@ -0,0 +1,21 @@ +import { UUITextStyles } from '@umbraco-ui/uui-css'; +import { css, html } from 'lit'; +import { customElement } from 'lit/decorators.js'; +import { UmbLitElement } from '@umbraco-cms/internal/lit-element'; + +@customElement('umb-stylesheet-workspace-view-rich-text-editor') +export class UmbStylesheetWorkspaceViewRichTextEditorElement extends UmbLitElement { + render() { + return html`umb-stylesheet-workspace-view-RICH_TEXT-editor`; + } + + static styles = [UUITextStyles, css``]; +} + +export default UmbStylesheetWorkspaceViewRichTextEditorElement; + +declare global { + interface HTMLElementTagNameMap { + 'umb-stylesheet-workspace-view-code-editor': UmbStylesheetWorkspaceViewRichTextEditorElement; + } +}