Merge branch 'main' into poc/package-modules-v2
This commit is contained in:
@@ -70,7 +70,7 @@ export class UmbExtensionRegistry {
|
||||
this._extensions.next([...extensionsValues, manifest as ManifestTypes]);
|
||||
}
|
||||
|
||||
registerMany(manifests: Array<ManifestTypes>): void {
|
||||
registerMany(manifests: Array<ManifestTypes | ManifestKind>): void {
|
||||
manifests.forEach((manifest) => this.register(manifest));
|
||||
}
|
||||
|
||||
|
||||
@@ -1,7 +1,13 @@
|
||||
import type { ManifestModal } from '../models';
|
||||
import type { UmbModalHandler } from '@umbraco-cms/backoffice/modal';
|
||||
|
||||
export interface UmbModalExtensionElement<UmbModalData extends object = object, UmbModalResult = unknown>
|
||||
extends HTMLElement {
|
||||
export interface UmbModalExtensionElement<
|
||||
UmbModalData extends object = object,
|
||||
UmbModalResult = unknown,
|
||||
ModalManifestType extends ManifestModal = ManifestModal
|
||||
> extends HTMLElement {
|
||||
manifest?: ModalManifestType;
|
||||
|
||||
modalHandler?: UmbModalHandler<UmbModalData, UmbModalResult>;
|
||||
|
||||
data?: UmbModalData;
|
||||
|
||||
@@ -8,7 +8,7 @@ import type { ManifestHeaderApp, ManifestHeaderAppButtonKind } from './header-ap
|
||||
import type { ManifestHealthCheck } from './health-check.model';
|
||||
import type { ManifestMenu } from './menu.model';
|
||||
import type { ManifestMenuItem, ManifestMenuItemTreeKind } from './menu-item.model';
|
||||
import type { ManifestModal } from './modal.model';
|
||||
import type { ManifestModal, ManifestModalTreePickerKind } from './modal.model';
|
||||
import type { ManifestPackageView } from './package-view.model';
|
||||
import type { ManifestPropertyAction } from './property-action.model';
|
||||
import type { ManifestPropertyEditorUI, ManifestPropertyEditorModel } from './property-editor.model';
|
||||
@@ -72,6 +72,7 @@ export type ManifestTypes =
|
||||
| ManifestMenuItem
|
||||
| ManifestMenuItemTreeKind
|
||||
| ManifestModal
|
||||
| ManifestModalTreePickerKind
|
||||
| ManifestPackageView
|
||||
| ManifestPropertyAction
|
||||
| ManifestPropertyEditorModel
|
||||
|
||||
@@ -3,3 +3,13 @@ import type { ManifestElement } from '.';
|
||||
export interface ManifestModal extends ManifestElement {
|
||||
type: 'modal';
|
||||
}
|
||||
|
||||
export interface ManifestModalTreePickerKind extends ManifestModal {
|
||||
type: 'modal';
|
||||
kind: 'treePicker';
|
||||
meta: MetaModalTreePickerKind;
|
||||
}
|
||||
|
||||
export interface MetaModalTreePickerKind {
|
||||
treeAlias: string;
|
||||
}
|
||||
|
||||
@@ -72,6 +72,9 @@ export class UmbModalHandlerClass<ModalData extends object = object, ModalResult
|
||||
this.type = config?.type || this.type;
|
||||
this.size = config?.size || this.size;
|
||||
|
||||
const defaultData = modalAlias instanceof UmbModalToken ? modalAlias.getDefaultData() : undefined;
|
||||
const combinedData = { ...defaultData, ...data } as ModalData;
|
||||
|
||||
// TODO: Consider if its right to use Promises, or use another event based system? Would we need to be able to cancel an event, to then prevent the closing..?
|
||||
this._submitPromise = new Promise((resolve, reject) => {
|
||||
this._submitResolver = resolve;
|
||||
@@ -79,7 +82,7 @@ export class UmbModalHandlerClass<ModalData extends object = object, ModalResult
|
||||
});
|
||||
|
||||
this.modalElement = this.#createContainerElement();
|
||||
this.#observeModal(modalAlias.toString(), data);
|
||||
this.#observeModal(modalAlias.toString(), combinedData);
|
||||
}
|
||||
|
||||
#createContainerElement() {
|
||||
@@ -109,6 +112,7 @@ export class UmbModalHandlerClass<ModalData extends object = object, ModalResult
|
||||
innerElement.data = data;
|
||||
//innerElement.observable = this.#dataObservable;
|
||||
innerElement.modalHandler = this;
|
||||
innerElement.manifest = manifest;
|
||||
}
|
||||
|
||||
return innerElement;
|
||||
|
||||
@@ -1,10 +1,13 @@
|
||||
export interface UmbPickerModalData<T> {
|
||||
export interface UmbPickerModalData<ItemType> {
|
||||
multiple?: boolean;
|
||||
selection?: Array<string | null>;
|
||||
filter?: (item: T) => boolean;
|
||||
pickableFilter?: (item: T) => boolean;
|
||||
filter?: (item: ItemType) => boolean;
|
||||
pickableFilter?: (item: ItemType) => boolean;
|
||||
}
|
||||
|
||||
export interface UmbPickerModalResult {
|
||||
selection: Array<string | null>;
|
||||
}
|
||||
|
||||
export interface UmbTreePickerModalData<TreeItemType> extends UmbPickerModalData<TreeItemType> {
|
||||
treeAlias?: string;
|
||||
}
|
||||
|
||||
@@ -1,13 +1,16 @@
|
||||
import { FolderTreeItemResponseModel } from '@umbraco-cms/backoffice/backend-api';
|
||||
import { UmbModalToken, UmbPickerModalData, UmbPickerModalResult } from '@umbraco-cms/backoffice/modal';
|
||||
import { UmbModalToken, UmbTreePickerModalData, UmbPickerModalResult } from '@umbraco-cms/backoffice/modal';
|
||||
|
||||
export type UmbDataTypePickerModalData = UmbPickerModalData<FolderTreeItemResponseModel>;
|
||||
export type UmbDataTypePickerModalData = UmbTreePickerModalData<FolderTreeItemResponseModel>;
|
||||
export type UmbDataTypePickerModalResult = UmbPickerModalResult;
|
||||
|
||||
export const UMB_DATA_TYPE_PICKER_MODAL = new UmbModalToken<UmbDataTypePickerModalData, UmbDataTypePickerModalResult>(
|
||||
'Umb.Modal.DataTypePicker',
|
||||
'Umb.Modal.TreePicker',
|
||||
{
|
||||
type: 'sidebar',
|
||||
size: 'small',
|
||||
},
|
||||
{
|
||||
treeAlias: 'Umb.Tree.DataTypes',
|
||||
}
|
||||
);
|
||||
|
||||
@@ -0,0 +1,20 @@
|
||||
import { UmbModalToken } from '@umbraco-cms/backoffice/modal';
|
||||
|
||||
export interface UmbDictionaryItemPickerModalData {
|
||||
multiple: boolean;
|
||||
selection: string[];
|
||||
}
|
||||
|
||||
export interface UmbDictionaryItemPickerModalResult {
|
||||
selection: Array<string | null>;
|
||||
}
|
||||
|
||||
export const UMB_DICTIONARY_ITEM_PICKER_MODAL_ALIAS = 'Umb.Modal.DictionaryItemPicker';
|
||||
|
||||
export const UMB_DICTIONARY_ITEM_PICKER_MODAL = new UmbModalToken<
|
||||
UmbDictionaryItemPickerModalData,
|
||||
UmbDictionaryItemPickerModalResult
|
||||
>(UMB_DICTIONARY_ITEM_PICKER_MODAL_ALIAS, {
|
||||
type: 'sidebar',
|
||||
size: 'small',
|
||||
});
|
||||
@@ -1,18 +1,16 @@
|
||||
import { UmbModalToken } from '@umbraco-cms/backoffice/modal';
|
||||
import { DocumentTreeItemResponseModel } from '@umbraco-cms/backoffice/backend-api';
|
||||
import { UmbModalToken, UmbPickerModalResult, UmbTreePickerModalData } from '@umbraco-cms/backoffice/modal';
|
||||
|
||||
export interface UmbDocumentPickerModalData {
|
||||
multiple?: boolean;
|
||||
selection?: Array<string | null>;
|
||||
}
|
||||
|
||||
export interface UmbDocumentPickerModalResult {
|
||||
selection: Array<string | null>;
|
||||
}
|
||||
export type UmbDocumentPickerModalData = UmbTreePickerModalData<DocumentTreeItemResponseModel>;
|
||||
export type UmbDocumentPickerModalResult = UmbPickerModalResult;
|
||||
|
||||
export const UMB_DOCUMENT_PICKER_MODAL = new UmbModalToken<UmbDocumentPickerModalData, UmbDocumentPickerModalResult>(
|
||||
'Umb.Modal.DocumentPicker',
|
||||
'Umb.Modal.TreePicker',
|
||||
{
|
||||
type: 'sidebar',
|
||||
size: 'small',
|
||||
},
|
||||
{
|
||||
treeAlias: 'Umb.Tree.Documents',
|
||||
}
|
||||
);
|
||||
|
||||
@@ -1,18 +1,19 @@
|
||||
import { UmbModalToken } from '@umbraco-cms/backoffice/modal';
|
||||
import { EntityTreeItemResponseModel } from '@umbraco-cms/backoffice/backend-api';
|
||||
import { UmbModalToken, UmbPickerModalResult, UmbTreePickerModalData } from '@umbraco-cms/backoffice/modal';
|
||||
|
||||
export interface UmbDocumentTypePickerModalData {
|
||||
multiple?: boolean;
|
||||
selection?: Array<string>;
|
||||
}
|
||||
|
||||
export interface UmbDocumentTypePickerModalResult {
|
||||
selection: Array<string | null>;
|
||||
}
|
||||
export type UmbDocumentTypePickerModalData = UmbTreePickerModalData<EntityTreeItemResponseModel>;
|
||||
export type UmbDocumentTypePickerModalResult = UmbPickerModalResult;
|
||||
|
||||
export const UMB_DOCUMENT_TYPE_PICKER_MODAL = new UmbModalToken<
|
||||
UmbDocumentTypePickerModalData,
|
||||
UmbDocumentTypePickerModalResult
|
||||
>('Umb.Modal.DocumentTypePicker', {
|
||||
type: 'sidebar',
|
||||
size: 'small',
|
||||
});
|
||||
>(
|
||||
'Umb.Modal.TreePicker',
|
||||
{
|
||||
type: 'sidebar',
|
||||
size: 'small',
|
||||
},
|
||||
{
|
||||
treeAlias: 'Umb.Tree.DocumentTypes',
|
||||
}
|
||||
);
|
||||
|
||||
@@ -16,7 +16,7 @@ export * from './import-dictionary-modal.token';
|
||||
export * from './invite-user-modal.token';
|
||||
export * from './language-picker-modal.token';
|
||||
export * from './link-picker-modal.token';
|
||||
export * from './media-picker-modal.token';
|
||||
export * from './media-tree-picker-modal.token';
|
||||
export * from './property-editor-ui-picker-modal.token';
|
||||
export * from './property-settings-modal.token';
|
||||
export * from './search-modal.token';
|
||||
@@ -26,4 +26,6 @@ export * from './template-picker-modal.token';
|
||||
export * from './user-group-picker-modal.token';
|
||||
export * from './user-picker-modal.token';
|
||||
export * from './folder-modal.token';
|
||||
export * from './partial-view-picker-modal.token';
|
||||
export * from './dictionary-item-picker-modal.token';
|
||||
export * from './data-type-picker-modal.token';
|
||||
|
||||
@@ -1,18 +0,0 @@
|
||||
import { UmbModalToken } from '@umbraco-cms/backoffice/modal';
|
||||
|
||||
export interface UmbMediaPickerModalData {
|
||||
multiple?: boolean;
|
||||
selection: Array<string>;
|
||||
}
|
||||
|
||||
export interface UmbMediaPickerModalResult {
|
||||
selection: Array<string | null>;
|
||||
}
|
||||
|
||||
export const UMB_MEDIA_PICKER_MODAL = new UmbModalToken<UmbMediaPickerModalData, UmbMediaPickerModalResult>(
|
||||
'Umb.Modal.MediaPicker',
|
||||
{
|
||||
type: 'sidebar',
|
||||
size: 'small',
|
||||
}
|
||||
);
|
||||
@@ -0,0 +1,19 @@
|
||||
import { UmbModalToken, UmbPickerModalResult, UmbTreePickerModalData } from '@umbraco-cms/backoffice/modal';
|
||||
import { ContentTreeItemResponseModel } from '@umbraco-cms/backoffice/backend-api';
|
||||
|
||||
export type UmbMediaTreePickerModalData = UmbTreePickerModalData<ContentTreeItemResponseModel>;
|
||||
export type UmbMediaTreePickerModalResult = UmbPickerModalResult;
|
||||
|
||||
export const UMB_MEDIA_TREE_PICKER_MODAL = new UmbModalToken<
|
||||
UmbMediaTreePickerModalData,
|
||||
UmbMediaTreePickerModalResult
|
||||
>(
|
||||
'Umb.Modal.TreePicker',
|
||||
{
|
||||
type: 'sidebar',
|
||||
size: 'small',
|
||||
},
|
||||
{
|
||||
treeAlias: 'Umb.Tree.Media',
|
||||
}
|
||||
);
|
||||
@@ -1,36 +1,38 @@
|
||||
import { UmbModalConfig } from '../modal.context';
|
||||
|
||||
export class UmbModalToken<Data extends object = object, Result = unknown> {
|
||||
export class UmbModalToken<ModalDataType extends object = object, ModalResultType = unknown> {
|
||||
/**
|
||||
* Get the data type of the token's data.
|
||||
*
|
||||
* @public
|
||||
* @type {Data}
|
||||
* @type {ModalDataType}
|
||||
* @memberOf UmbModalToken
|
||||
* @example `typeof MyModal.TYPE`
|
||||
* @returns undefined
|
||||
*/
|
||||
readonly DATA: Data = undefined as never;
|
||||
readonly DATA: ModalDataType = undefined as never;
|
||||
|
||||
/**
|
||||
* Get the result type of the token
|
||||
*
|
||||
* @public
|
||||
* @type {Result}
|
||||
* @type {ModalResultType}
|
||||
* @memberOf UmbModalToken
|
||||
* @example `typeof MyModal.RESULT`
|
||||
* @returns undefined
|
||||
*/
|
||||
readonly RESULT: Result = undefined as never;
|
||||
readonly RESULT: ModalResultType = undefined as never;
|
||||
|
||||
/**
|
||||
* @param alias Unique identifier for the token,
|
||||
* @param defaultConfig Default configuration for the modal,
|
||||
* @param _desc Description for the token,
|
||||
* used only for debugging purposes,
|
||||
* it should but does not need to be unique
|
||||
* @param defaultData Default data for the modal,
|
||||
*/
|
||||
constructor(protected alias: string, protected defaultConfig?: UmbModalConfig, protected _desc?: string) {}
|
||||
constructor(
|
||||
protected alias: string,
|
||||
protected defaultConfig?: UmbModalConfig,
|
||||
protected defaultData?: ModalDataType
|
||||
) {}
|
||||
|
||||
/**
|
||||
* This method must always return the unique alias of the token since that
|
||||
@@ -45,4 +47,8 @@ export class UmbModalToken<Data extends object = object, Result = unknown> {
|
||||
public getDefaultConfig(): UmbModalConfig | undefined {
|
||||
return this.defaultConfig;
|
||||
}
|
||||
|
||||
public getDefaultData(): ModalDataType | undefined {
|
||||
return this.defaultData;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,20 @@
|
||||
import { UmbModalToken } from '@umbraco-cms/backoffice/modal';
|
||||
|
||||
export interface UmbPartialViewPickerModalData {
|
||||
multiple: boolean;
|
||||
selection: string[];
|
||||
}
|
||||
|
||||
export interface UmbPartialViewPickerModalResult {
|
||||
selection: Array<string | null> | undefined;
|
||||
}
|
||||
|
||||
export const UMB_PARTIAL_VIEW_PICKER_MODAL_ALIAS = 'Umb.Modal.PartialViewPicker';
|
||||
|
||||
export const UMB_PARTIAL_VIEW_PICKER_MODAL = new UmbModalToken<
|
||||
UmbPartialViewPickerModalData,
|
||||
UmbPartialViewPickerModalResult
|
||||
>(UMB_PARTIAL_VIEW_PICKER_MODAL_ALIAS, {
|
||||
type: 'sidebar',
|
||||
size: 'small',
|
||||
});
|
||||
@@ -1,18 +1,16 @@
|
||||
import { UmbModalToken } from '@umbraco-cms/backoffice/modal';
|
||||
import { EntityTreeItemResponseModel } from '@umbraco-cms/backoffice/backend-api';
|
||||
import { UmbModalToken, UmbPickerModalResult, UmbTreePickerModalData } from '@umbraco-cms/backoffice/modal';
|
||||
|
||||
export interface UmbTemplatePickerModalData {
|
||||
multiple: boolean;
|
||||
selection: Array<string | null>;
|
||||
}
|
||||
|
||||
export interface UmbTemplatePickerModalResult {
|
||||
selection: Array<string | null>;
|
||||
}
|
||||
export type UmbTemplatePickerModalData = UmbTreePickerModalData<EntityTreeItemResponseModel>;
|
||||
export type UmbTemplatePickerModalResult = UmbPickerModalResult;
|
||||
|
||||
export const UMB_TEMPLATE_PICKER_MODAL = new UmbModalToken<UmbTemplatePickerModalData, UmbTemplatePickerModalResult>(
|
||||
'Umb.Modal.TemplatePicker',
|
||||
'Umb.Modal.TreePicker',
|
||||
{
|
||||
type: 'sidebar',
|
||||
size: 'small',
|
||||
},
|
||||
{
|
||||
treeAlias: 'Umb.Tree.Templates',
|
||||
}
|
||||
);
|
||||
|
||||
@@ -26,7 +26,7 @@
|
||||
],
|
||||
"peerDependencies": {
|
||||
"@types/uuid": "^9.0.1",
|
||||
"@umbraco-ui/uui": "^1.2.0-rc.0",
|
||||
"@umbraco-ui/uui": "1.2.1",
|
||||
"rxjs": "^7.8.0"
|
||||
},
|
||||
"customElements": "custom-elements.json"
|
||||
|
||||
@@ -9,7 +9,6 @@ import { InterfaceColor, InterfaceLook } from '@umbraco-ui/uui-base/lib/types';
|
||||
@customElement('umb-button-with-dropdown')
|
||||
export class UmbButtonWithDropdownElement extends LitElement {
|
||||
|
||||
|
||||
@property()
|
||||
label = '';
|
||||
|
||||
@@ -25,6 +24,9 @@ export class UmbButtonWithDropdownElement extends LitElement {
|
||||
@property()
|
||||
placement: PopoverPlacement = 'bottom-start';
|
||||
|
||||
@property({ type: Boolean })
|
||||
compact = false;
|
||||
|
||||
@query('#symbol-expand')
|
||||
symbolExpand!: UUISymbolExpandElement;
|
||||
|
||||
@@ -55,6 +57,7 @@ export class UmbButtonWithDropdownElement extends LitElement {
|
||||
.look=${this.look}
|
||||
.color=${this.color}
|
||||
.label=${this.label}
|
||||
.compact=${this.compact}
|
||||
id="myPopoverBtn"
|
||||
@click=${this.#togglePopover}>
|
||||
<slot></slot>
|
||||
|
||||
@@ -73,5 +73,7 @@ import './variant-selector/variant-selector.element';
|
||||
import './code-editor';
|
||||
|
||||
export * from './table';
|
||||
export * from './tree/tree.element';
|
||||
export * from './code-editor';
|
||||
|
||||
export const manifests = [...debugManifests];
|
||||
|
||||
@@ -45,7 +45,7 @@ export class UmbInputListBaseElement extends UmbLitElement {
|
||||
|
||||
modalHandler?.onSubmit().then((data: UmbPickerModalResult) => {
|
||||
if (data) {
|
||||
this.value = data.selection.filter((id) => id !== null && id !== undefined) as Array<string>;
|
||||
this.value = data.selection?.filter((id) => id !== null && id !== undefined) as Array<string>;
|
||||
this.selectionUpdated();
|
||||
}
|
||||
});
|
||||
|
||||
@@ -10,14 +10,27 @@ import { UmbNotificationContext, UMB_NOTIFICATION_CONTEXT_TOKEN } from '@umbraco
|
||||
import { UmbModalContext, UMB_MODAL_CONTEXT_TOKEN } from '@umbraco-cms/backoffice/modal';
|
||||
import { UmbContextProviderController } from '@umbraco-cms/backoffice/context-api';
|
||||
import type { UmbEntrypointOnInit } from '@umbraco-cms/backoffice/extensions-api';
|
||||
import { ManifestKind, ManifestTypes } from '@umbraco-cms/backoffice/extensions-registry';
|
||||
|
||||
import './notification';
|
||||
|
||||
export const manifests = [
|
||||
export const manifests: Array<ManifestTypes | ManifestKind> = [
|
||||
...componentManifests,
|
||||
...propertyActionManifests,
|
||||
...propertyEditorManifests,
|
||||
...modalManifests,
|
||||
// TODO: where should these live?
|
||||
{
|
||||
type: 'kind',
|
||||
alias: 'Umb.Kind.TreePickerModal',
|
||||
matchKind: 'treePicker',
|
||||
matchType: 'modal',
|
||||
manifest: {
|
||||
type: 'modal',
|
||||
kind: 'treePicker',
|
||||
elementName: 'umb-tree-picker-modal',
|
||||
},
|
||||
},
|
||||
];
|
||||
|
||||
export const onInit: UmbEntrypointOnInit = (host, extensionRegistry) => {
|
||||
|
||||
@@ -37,12 +37,6 @@ const modals: Array<ManifestModal> = [
|
||||
name: 'Section Picker Modal',
|
||||
loader: () => import('./section-picker/section-picker-modal.element'),
|
||||
},
|
||||
{
|
||||
type: 'modal',
|
||||
alias: 'Umb.Modal.TemplatePicker',
|
||||
name: 'Template Picker Modal',
|
||||
loader: () => import('./template-picker/template-picker-modal.element'),
|
||||
},
|
||||
{
|
||||
type: 'modal',
|
||||
alias: 'Umb.Modal.Template',
|
||||
@@ -55,6 +49,12 @@ const modals: Array<ManifestModal> = [
|
||||
name: 'Embedded Media Modal',
|
||||
loader: () => import('./embedded-media/embedded-media-modal.element'),
|
||||
},
|
||||
{
|
||||
type: 'modal',
|
||||
alias: 'Umb.Modal.TreePicker',
|
||||
name: 'Tree Picker Modal',
|
||||
loader: () => import('./tree-picker/tree-picker-modal.element'),
|
||||
},
|
||||
];
|
||||
|
||||
export const manifests = [...modals];
|
||||
|
||||
@@ -1,105 +0,0 @@
|
||||
import { css, html } from 'lit';
|
||||
import { UUITextStyles } from '@umbraco-ui/uui-css/lib';
|
||||
import { customElement, state } from 'lit/decorators.js';
|
||||
import { UmbTreeElement } from '../../components/tree/tree.element';
|
||||
import { UmbTemplatePickerModalData, UmbTemplatePickerModalResult } from '@umbraco-cms/backoffice/modal';
|
||||
import { UmbModalBaseElement } from '@umbraco-cms/internal/modal';
|
||||
|
||||
//TODO: make a default tree-picker that can be used across multiple pickers
|
||||
// TODO: make use of UmbPickerLayoutBase
|
||||
@customElement('umb-template-picker-modal')
|
||||
export class UmbTemplatePickerModalElement extends UmbModalBaseElement<
|
||||
UmbTemplatePickerModalData,
|
||||
UmbTemplatePickerModalResult
|
||||
> {
|
||||
@state()
|
||||
_selection: Array<string | null> = [];
|
||||
|
||||
@state()
|
||||
_multiple = true;
|
||||
|
||||
connectedCallback() {
|
||||
super.connectedCallback();
|
||||
this._selection = this.data?.selection ?? [];
|
||||
this._multiple = this.data?.multiple ?? true;
|
||||
}
|
||||
|
||||
private _handleSelectionChange(e: CustomEvent) {
|
||||
e.stopPropagation();
|
||||
const element = e.target as UmbTreeElement;
|
||||
this._selection = this._multiple ? element.selection : [element.selection[element.selection.length - 1]];
|
||||
}
|
||||
|
||||
private _submit() {
|
||||
this.modalHandler?.submit({ selection: this._selection });
|
||||
}
|
||||
|
||||
private _close() {
|
||||
this.modalHandler?.reject();
|
||||
}
|
||||
|
||||
// TODO: implement search
|
||||
// TODO: make umb-tree have a disabled option (string array like selection)?
|
||||
render() {
|
||||
return html`
|
||||
<umb-workspace-editor headline="Select Content">
|
||||
<uui-box>
|
||||
<uui-input></uui-input>
|
||||
<hr />
|
||||
<umb-tree
|
||||
alias="Umb.Tree.Templates"
|
||||
@selected=${this._handleSelectionChange}
|
||||
.selection=${this._selection}
|
||||
selectable></umb-tree>
|
||||
</uui-box>
|
||||
<div slot="actions">
|
||||
<uui-button label="Close" @click=${this._close}></uui-button>
|
||||
<uui-button label="Submit" look="primary" color="positive" @click=${this._submit}></uui-button>
|
||||
</div>
|
||||
</umb-workspace-editor>
|
||||
`;
|
||||
}
|
||||
|
||||
static styles = [
|
||||
UUITextStyles,
|
||||
css`
|
||||
h3 {
|
||||
margin-left: var(--uui-size-space-5);
|
||||
margin-right: var(--uui-size-space-5);
|
||||
}
|
||||
|
||||
uui-input {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
hr {
|
||||
border: none;
|
||||
border-bottom: 1px solid var(--uui-color-divider);
|
||||
margin: 16px 0;
|
||||
}
|
||||
|
||||
#content-list {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: var(--uui-size-space-3);
|
||||
}
|
||||
|
||||
.content-item {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.content-item.selected {
|
||||
background-color: var(--uui-color-selected);
|
||||
color: var(--uui-color-selected-contrast);
|
||||
}
|
||||
`,
|
||||
];
|
||||
}
|
||||
|
||||
export default UmbTemplatePickerModalElement;
|
||||
|
||||
declare global {
|
||||
interface HTMLElementTagNameMap {
|
||||
'umb-template-picker-modal': UmbTemplatePickerModalElement;
|
||||
}
|
||||
}
|
||||
@@ -1,23 +1,18 @@
|
||||
import { css, html } from 'lit';
|
||||
import { UUITextStyles } from '@umbraco-ui/uui-css/lib';
|
||||
import { customElement, property, state } from 'lit/decorators.js';
|
||||
import type { UmbTreeElement } from '../../../../core/components/tree/tree.element';
|
||||
import {
|
||||
UmbDataTypePickerModalData,
|
||||
UmbDataTypePickerModalResult,
|
||||
UmbModalHandler,
|
||||
} from '@umbraco-cms/backoffice/modal';
|
||||
import { UmbLitElement } from '@umbraco-cms/internal/lit-element';
|
||||
|
||||
// TODO: make use of UmbPickerLayoutBase
|
||||
@customElement('umb-data-type-picker-modal')
|
||||
export class UmbDataTypePickerModalElement extends UmbLitElement {
|
||||
@property({ attribute: false })
|
||||
modalHandler?: UmbModalHandler<UmbDataTypePickerModalData, UmbDataTypePickerModalResult>;
|
||||
|
||||
@property({ type: Object, attribute: false })
|
||||
data?: UmbDataTypePickerModalData;
|
||||
import { customElement, state } from 'lit/decorators.js';
|
||||
import type { UmbTreeElement } from '../../components/tree/tree.element';
|
||||
import { ManifestModalTreePickerKind } from '@umbraco-cms/backoffice/extensions-registry';
|
||||
import { UmbTreePickerModalData, UmbPickerModalResult } from '@umbraco-cms/backoffice/modal';
|
||||
import { UmbModalBaseElement } from '@umbraco-cms/internal/modal';
|
||||
import { TreeItemPresentationModel } from '@umbraco-cms/backoffice/backend-api';
|
||||
|
||||
@customElement('umb-tree-picker-modal')
|
||||
export class UmbTreePickerModalElement<TreeItemType extends TreeItemPresentationModel> extends UmbModalBaseElement<
|
||||
UmbTreePickerModalData<TreeItemType>,
|
||||
UmbPickerModalResult,
|
||||
ManifestModalTreePickerKind
|
||||
> {
|
||||
@state()
|
||||
_selection: Array<string | null> = [];
|
||||
|
||||
@@ -26,6 +21,7 @@ export class UmbDataTypePickerModalElement extends UmbLitElement {
|
||||
|
||||
connectedCallback() {
|
||||
super.connectedCallback();
|
||||
|
||||
this._selection = this.data?.selection ?? [];
|
||||
this._multiple = this.data?.multiple ?? false;
|
||||
}
|
||||
@@ -49,7 +45,7 @@ export class UmbDataTypePickerModalElement extends UmbLitElement {
|
||||
<umb-body-layout headline="Select">
|
||||
<uui-box>
|
||||
<umb-tree
|
||||
alias="Umb.Tree.DataTypes"
|
||||
alias=${this.data?.treeAlias}
|
||||
@selected=${this.#onSelectionChange}
|
||||
.selection=${this._selection}
|
||||
selectable
|
||||
@@ -67,10 +63,10 @@ export class UmbDataTypePickerModalElement extends UmbLitElement {
|
||||
static styles = [UUITextStyles, css``];
|
||||
}
|
||||
|
||||
export default UmbDataTypePickerModalElement;
|
||||
export default UmbTreePickerModalElement;
|
||||
|
||||
declare global {
|
||||
interface HTMLElementTagNameMap {
|
||||
'umb-data-type-picker-modal': UmbDataTypePickerModalElement;
|
||||
'umb-tree-picker-modal': UmbTreePickerModalElement<TreeItemPresentationModel>;
|
||||
}
|
||||
}
|
||||
@@ -11,7 +11,7 @@ import {
|
||||
UmbModalContext,
|
||||
UMB_MODAL_CONTEXT_TOKEN,
|
||||
UMB_CONFIRM_MODAL,
|
||||
UMB_DOCUMENT_TYPE_PICKER_MODAL,
|
||||
UMB_DOCUMENT_PICKER_MODAL,
|
||||
} from '@umbraco-cms/backoffice/modal';
|
||||
import { UmbLitElement } from '@umbraco-cms/internal/lit-element';
|
||||
import { DocumentTypeResponseModel, EntityTreeItemResponseModel } from '@umbraco-cms/backoffice/backend-api';
|
||||
@@ -73,7 +73,7 @@ export class UmbInputDocumentTypePickerElement extends FormControlMixin(UmbLitEl
|
||||
|
||||
private _openPicker() {
|
||||
// We send a shallow copy(good enough as its just an array of ids) of our this._selectedIds, as we don't want the modal to manipulate our data:
|
||||
const modalHandler = this._modalContext?.open(UMB_DOCUMENT_TYPE_PICKER_MODAL, {
|
||||
const modalHandler = this._modalContext?.open(UMB_DOCUMENT_PICKER_MODAL, {
|
||||
multiple: true,
|
||||
selection: [...this._selectedIds],
|
||||
});
|
||||
|
||||
@@ -1,9 +1,11 @@
|
||||
import { DOCUMENT_TYPE_REPOSITORY_ALIAS } from '../repository/manifests';
|
||||
import type { ManifestTree, ManifestTreeItem } from '@umbraco-cms/backoffice/extensions-registry';
|
||||
|
||||
export const DOCUMENT_TYPE_TREE_ALIAS = 'Umb.Tree.DocumentTypes';
|
||||
|
||||
const tree: ManifestTree = {
|
||||
type: 'tree',
|
||||
alias: 'Umb.Tree.DocumentTypes',
|
||||
alias: DOCUMENT_TYPE_TREE_ALIAS,
|
||||
name: 'Document Types Tree',
|
||||
meta: {
|
||||
repositoryAlias: DOCUMENT_TYPE_REPOSITORY_ALIAS,
|
||||
|
||||
@@ -5,7 +5,6 @@ import { manifests as treeManifests } from './tree/manifests';
|
||||
import { manifests as workspaceManifests } from './workspace/manifests';
|
||||
import { manifests as entityActionManifests } from './entity-actions/manifests';
|
||||
import { manifests as entityBulkActionManifests } from './entity-bulk-actions/manifests';
|
||||
import { manifests as modalManifests } from './modals/manifests';
|
||||
import { manifests as propertyEditorManifests } from './property-editors/manifests';
|
||||
|
||||
export const manifests = [
|
||||
@@ -16,6 +15,5 @@ export const manifests = [
|
||||
...workspaceManifests,
|
||||
...entityActionManifests,
|
||||
...entityBulkActionManifests,
|
||||
...modalManifests,
|
||||
...propertyEditorManifests,
|
||||
];
|
||||
|
||||
@@ -1,103 +0,0 @@
|
||||
import { css, html } from 'lit';
|
||||
import { UUITextStyles } from '@umbraco-ui/uui-css/lib';
|
||||
import { customElement, state } from 'lit/decorators.js';
|
||||
import type { UmbTreeElement } from '../../../../core/components/tree/tree.element';
|
||||
import { UmbDocumentPickerModalData, UmbDocumentPickerModalResult } from '@umbraco-cms/backoffice/modal';
|
||||
import { UmbModalBaseElement } from '@umbraco-cms/internal/modal';
|
||||
|
||||
// TODO: make use of UmbPickerLayoutBase
|
||||
@customElement('umb-document-picker-modal')
|
||||
export class UmbDocumentPickerModalElement extends UmbModalBaseElement<
|
||||
UmbDocumentPickerModalData,
|
||||
UmbDocumentPickerModalResult
|
||||
> {
|
||||
@state()
|
||||
_selection: Array<string | null> = [];
|
||||
|
||||
@state()
|
||||
_multiple = true;
|
||||
|
||||
connectedCallback() {
|
||||
super.connectedCallback();
|
||||
this._selection = this.data?.selection ?? [];
|
||||
this._multiple = this.data?.multiple ?? true;
|
||||
}
|
||||
|
||||
private _handleSelectionChange(e: CustomEvent) {
|
||||
e.stopPropagation();
|
||||
const element = e.target as UmbTreeElement;
|
||||
//TODO: Should multiple property be implemented here or be passed down into umb-tree?
|
||||
this._selection = this._multiple ? element.selection : [element.selection[element.selection.length - 1]];
|
||||
}
|
||||
|
||||
private _submit() {
|
||||
this.modalHandler?.submit({ selection: this._selection });
|
||||
}
|
||||
|
||||
private _close() {
|
||||
this.modalHandler?.reject();
|
||||
}
|
||||
|
||||
render() {
|
||||
return html`
|
||||
<umb-workspace-editor headline="Select Content">
|
||||
<uui-box>
|
||||
<uui-input></uui-input>
|
||||
<hr />
|
||||
<umb-tree
|
||||
alias="Umb.Tree.Documents"
|
||||
@selected=${this._handleSelectionChange}
|
||||
.selection=${this._selection}
|
||||
selectable></umb-tree>
|
||||
</uui-box>
|
||||
<div slot="actions">
|
||||
<uui-button label="Close" @click=${this._close}></uui-button>
|
||||
<uui-button label="Submit" look="primary" color="positive" @click=${this._submit}></uui-button>
|
||||
</div>
|
||||
</umb-workspace-editor>
|
||||
`;
|
||||
}
|
||||
|
||||
static styles = [
|
||||
UUITextStyles,
|
||||
css`
|
||||
h3 {
|
||||
margin-left: var(--uui-size-space-5);
|
||||
margin-right: var(--uui-size-space-5);
|
||||
}
|
||||
|
||||
uui-input {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
hr {
|
||||
border: none;
|
||||
border-bottom: 1px solid var(--uui-color-divider);
|
||||
margin: 16px 0;
|
||||
}
|
||||
|
||||
#content-list {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: var(--uui-size-space-3);
|
||||
}
|
||||
|
||||
.content-item {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.content-item.selected {
|
||||
background-color: var(--uui-color-selected);
|
||||
color: var(--uui-color-selected-contrast);
|
||||
}
|
||||
`,
|
||||
];
|
||||
}
|
||||
|
||||
export default UmbDocumentPickerModalElement;
|
||||
|
||||
declare global {
|
||||
interface HTMLElementTagNameMap {
|
||||
'umb-document-picker-modal': UmbDocumentPickerModalElement;
|
||||
}
|
||||
}
|
||||
@@ -1,26 +0,0 @@
|
||||
import '../../../../core/components/body-layout/body-layout.element';
|
||||
import './document-picker-modal.element';
|
||||
|
||||
import { Meta, Story } from '@storybook/web-components';
|
||||
import { html } from 'lit';
|
||||
|
||||
import type { UmbDocumentPickerModalElement } from './document-picker-modal.element';
|
||||
import type { UmbDocumentPickerModalData } from '@umbraco-cms/backoffice/modal';
|
||||
|
||||
export default {
|
||||
title: 'API/Modals/Layouts/Content Picker',
|
||||
component: 'umb-document-picker-modal',
|
||||
id: 'umb-document-picker-modal',
|
||||
} as Meta;
|
||||
|
||||
const data: UmbDocumentPickerModalData = {
|
||||
multiple: true,
|
||||
selection: [],
|
||||
};
|
||||
|
||||
export const Overview: Story<UmbDocumentPickerModalElement> = () => html`
|
||||
<!-- TODO: figure out if generics are allowed for properties:
|
||||
https://github.com/runem/lit-analyzer/issues/149
|
||||
https://github.com/runem/lit-analyzer/issues/163 -->
|
||||
<umb-document-picker-modal .data=${data as any}></umb-document-picker-modal>
|
||||
`;
|
||||
@@ -1,103 +0,0 @@
|
||||
import { css, html } from 'lit';
|
||||
import { UUITextStyles } from '@umbraco-ui/uui-css/lib';
|
||||
import { customElement, state } from 'lit/decorators.js';
|
||||
import type { UmbTreeElement } from '../../../../core/components/tree/tree.element';
|
||||
import { UmbDocumentTypePickerModalData, UmbDocumentTypePickerModalResult } from '@umbraco-cms/backoffice/modal';
|
||||
import { UmbModalBaseElement } from '@umbraco-cms/internal/modal';
|
||||
|
||||
// TODO: make use of UmbPickerLayoutBase
|
||||
@customElement('umb-document-type-picker-modal')
|
||||
export class UmbDocumentTypePickerModalElement extends UmbModalBaseElement<
|
||||
UmbDocumentTypePickerModalData,
|
||||
UmbDocumentTypePickerModalResult
|
||||
> {
|
||||
@state()
|
||||
_selection: Array<string | null> = [];
|
||||
|
||||
@state()
|
||||
_multiple = true;
|
||||
|
||||
connectedCallback() {
|
||||
super.connectedCallback();
|
||||
this._selection = this.data?.selection ?? [];
|
||||
this._multiple = this.data?.multiple ?? true;
|
||||
}
|
||||
|
||||
private _handleSelectionChange(e: CustomEvent) {
|
||||
e.stopPropagation();
|
||||
const element = e.target as UmbTreeElement;
|
||||
this._selection = element.selection;
|
||||
}
|
||||
|
||||
private _submit() {
|
||||
this.modalHandler?.submit({ selection: this._selection });
|
||||
}
|
||||
|
||||
private _close() {
|
||||
this.modalHandler?.reject();
|
||||
}
|
||||
|
||||
render() {
|
||||
return html`
|
||||
<umb-workspace-editor headline="Select Content">
|
||||
<uui-box>
|
||||
<uui-input></uui-input>
|
||||
<hr />
|
||||
<umb-tree
|
||||
alias="Umb.Tree.DocumentTypes"
|
||||
@selected=${this._handleSelectionChange}
|
||||
.selection=${this._selection}
|
||||
selectable
|
||||
?multiple=${this._multiple}></umb-tree>
|
||||
</uui-box>
|
||||
<div slot="actions">
|
||||
<uui-button label="Close" @click=${this._close}></uui-button>
|
||||
<uui-button label="Submit" look="primary" color="positive" @click=${this._submit}></uui-button>
|
||||
</div>
|
||||
</umb-workspace-editor>
|
||||
`;
|
||||
}
|
||||
|
||||
static styles = [
|
||||
UUITextStyles,
|
||||
css`
|
||||
h3 {
|
||||
margin-left: var(--uui-size-space-5);
|
||||
margin-right: var(--uui-size-space-5);
|
||||
}
|
||||
|
||||
uui-input {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
hr {
|
||||
border: none;
|
||||
border-bottom: 1px solid var(--uui-color-divider);
|
||||
margin: 16px 0;
|
||||
}
|
||||
|
||||
#content-list {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: var(--uui-size-space-3);
|
||||
}
|
||||
|
||||
.content-item {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.content-item.selected {
|
||||
background-color: var(--uui-color-selected);
|
||||
color: var(--uui-color-selected-contrast);
|
||||
}
|
||||
`,
|
||||
];
|
||||
}
|
||||
|
||||
export default UmbDocumentTypePickerModalElement;
|
||||
|
||||
declare global {
|
||||
interface HTMLElementTagNameMap {
|
||||
'umb-document-type-picker-modal': UmbDocumentTypePickerModalElement;
|
||||
}
|
||||
}
|
||||
@@ -1,26 +0,0 @@
|
||||
import '../../../../core/components/body-layout/body-layout.element';
|
||||
import './document-type-picker-modal.element';
|
||||
|
||||
import { Meta, Story } from '@storybook/web-components';
|
||||
import { html } from 'lit';
|
||||
|
||||
import type { UmbDocumentTypePickerModalElement } from './document-type-picker-modal.element';
|
||||
import type { UmbDocumentTypePickerModalData } from '@umbraco-cms/backoffice/modal';
|
||||
|
||||
export default {
|
||||
title: 'API/Modals/Layouts/Content Picker',
|
||||
component: 'umb-document-type-picker-modal',
|
||||
id: 'umb-document-type-picker-modal',
|
||||
} as Meta;
|
||||
|
||||
const data: UmbDocumentTypePickerModalData = {
|
||||
multiple: true,
|
||||
selection: [],
|
||||
};
|
||||
|
||||
export const Overview: Story<UmbDocumentTypePickerModalElement> = () => html`
|
||||
<!-- TODO: figure out if generics are allowed for properties:
|
||||
https://github.com/runem/lit-analyzer/issues/149
|
||||
https://github.com/runem/lit-analyzer/issues/163 -->
|
||||
<umb-document-picker-modal .data=${data as any}></umb-document-picker-modal>
|
||||
`;
|
||||
@@ -1,18 +0,0 @@
|
||||
import type { ManifestModal } from '@umbraco-cms/backoffice/extensions-registry';
|
||||
|
||||
const modals: Array<ManifestModal> = [
|
||||
{
|
||||
type: 'modal',
|
||||
alias: 'Umb.Modal.DocumentPicker',
|
||||
name: 'Document Picker Modal',
|
||||
loader: () => import('./document-picker/document-picker-modal.element'),
|
||||
},
|
||||
{
|
||||
type: 'modal',
|
||||
alias: 'Umb.Modal.DocumentTypePicker',
|
||||
name: 'Document Type Picker Modal',
|
||||
loader: () => import('./document-type-picker/document-type-picker-modal.element'),
|
||||
},
|
||||
];
|
||||
|
||||
export const manifests = [...modals];
|
||||
@@ -1,11 +1,11 @@
|
||||
import { DOCUMENT_REPOSITORY_ALIAS } from '../repository/manifests';
|
||||
import type { ManifestTree, ManifestTreeItem } from '@umbraco-cms/backoffice/extensions-registry';
|
||||
|
||||
const treeAlias = 'Umb.Tree.Documents';
|
||||
export const DOCUMENT_TREE_ALIAS = 'Umb.Tree.Documents';
|
||||
|
||||
const tree: ManifestTree = {
|
||||
type: 'tree',
|
||||
alias: treeAlias,
|
||||
alias: DOCUMENT_TREE_ALIAS,
|
||||
name: 'Documents Tree',
|
||||
meta: {
|
||||
repositoryAlias: DOCUMENT_REPOSITORY_ALIAS,
|
||||
|
||||
@@ -8,7 +8,7 @@ import {
|
||||
UmbModalContext,
|
||||
UMB_MODAL_CONTEXT_TOKEN,
|
||||
UMB_CONFIRM_MODAL,
|
||||
UMB_MEDIA_PICKER_MODAL,
|
||||
UMB_MEDIA_TREE_PICKER_MODAL,
|
||||
} from '@umbraco-cms/backoffice/modal';
|
||||
import { UmbLitElement } from '@umbraco-cms/internal/lit-element';
|
||||
import type { EntityTreeItemResponseModel } from '@umbraco-cms/backoffice/backend-api';
|
||||
@@ -120,7 +120,7 @@ export class UmbInputMediaPickerElement extends FormControlMixin(UmbLitElement)
|
||||
|
||||
private _openPicker() {
|
||||
// We send a shallow copy(good enough as its just an array of ids) of our this._selectedIds, as we don't want the modal to manipulate our data:
|
||||
const modalHandler = this._modalContext?.open(UMB_MEDIA_PICKER_MODAL, {
|
||||
const modalHandler = this._modalContext?.open(UMB_MEDIA_TREE_PICKER_MODAL, {
|
||||
multiple: this.max === 1 ? false : true,
|
||||
selection: [...this._selectedIds],
|
||||
});
|
||||
|
||||
@@ -2,7 +2,7 @@ import type { UmbMediaRepository } from '../../repository/media.repository';
|
||||
import { UmbEntityBulkActionBase } from '@umbraco-cms/backoffice/entity-action';
|
||||
import { UmbControllerHostElement } from '@umbraco-cms/backoffice/controller';
|
||||
import { UmbContextConsumerController } from '@umbraco-cms/backoffice/context-api';
|
||||
import { UmbModalContext, UMB_MODAL_CONTEXT_TOKEN, UMB_MEDIA_PICKER_MODAL } from '@umbraco-cms/backoffice/modal';
|
||||
import { UmbModalContext, UMB_MODAL_CONTEXT_TOKEN, UMB_MEDIA_TREE_PICKER_MODAL } from '@umbraco-cms/backoffice/modal';
|
||||
|
||||
export class UmbMediaMoveEntityBulkAction extends UmbEntityBulkActionBase<UmbMediaRepository> {
|
||||
#modalContext?: UmbModalContext;
|
||||
@@ -17,7 +17,7 @@ export class UmbMediaMoveEntityBulkAction extends UmbEntityBulkActionBase<UmbMed
|
||||
|
||||
async execute() {
|
||||
// TODO: the picker should be single picker by default
|
||||
const modalHandler = this.#modalContext?.open(UMB_MEDIA_PICKER_MODAL, {
|
||||
const modalHandler = this.#modalContext?.open(UMB_MEDIA_TREE_PICKER_MODAL, {
|
||||
selection: [],
|
||||
multiple: false,
|
||||
});
|
||||
|
||||
@@ -5,7 +5,6 @@ import { manifests as treeManifests } from './tree/manifests';
|
||||
import { manifests as workspaceManifests } from './workspace/manifests';
|
||||
import { manifests as entityActionsManifests } from './entity-actions/manifests';
|
||||
import { manifests as entityBulkActionsManifests } from './entity-bulk-actions/manifests';
|
||||
import { manifests as modalManifests } from './modals/manifests';
|
||||
|
||||
export const manifests = [
|
||||
...collectionViewManifests,
|
||||
@@ -15,5 +14,4 @@ export const manifests = [
|
||||
...workspaceManifests,
|
||||
...entityActionsManifests,
|
||||
...entityBulkActionsManifests,
|
||||
...modalManifests,
|
||||
];
|
||||
|
||||
@@ -1,12 +0,0 @@
|
||||
import type { ManifestModal } from '@umbraco-cms/backoffice/extensions-registry';
|
||||
|
||||
const modals: Array<ManifestModal> = [
|
||||
{
|
||||
type: 'modal',
|
||||
alias: 'Umb.Modal.MediaPicker',
|
||||
name: 'Media Picker Modal',
|
||||
loader: () => import('./media-picker/media-picker-modal.element'),
|
||||
},
|
||||
];
|
||||
|
||||
export const manifests = [...modals];
|
||||
@@ -1,102 +0,0 @@
|
||||
import { css, html } from 'lit';
|
||||
import { UUITextStyles } from '@umbraco-ui/uui-css/lib';
|
||||
import { customElement, state } from 'lit/decorators.js';
|
||||
import { UmbTreeElement } from '../../../../core/components/tree/tree.element';
|
||||
import { UmbMediaPickerModalData, UmbMediaPickerModalResult } from '@umbraco-cms/backoffice/modal';
|
||||
import { UmbModalBaseElement } from '@umbraco-cms/internal/modal';
|
||||
|
||||
@customElement('umb-media-picker-modal')
|
||||
export class UmbMediaPickerModalElement extends UmbModalBaseElement<
|
||||
UmbMediaPickerModalData,
|
||||
UmbMediaPickerModalResult
|
||||
> {
|
||||
@state()
|
||||
_selection: Array<string | null> = [];
|
||||
|
||||
@state()
|
||||
_multiple = true;
|
||||
|
||||
connectedCallback() {
|
||||
super.connectedCallback();
|
||||
this._selection = this.data?.selection ?? [];
|
||||
this._multiple = this.data?.multiple ?? true;
|
||||
}
|
||||
|
||||
private _handleSelectionChange(e: CustomEvent) {
|
||||
e.stopPropagation();
|
||||
const element = e.target as UmbTreeElement;
|
||||
this._selection = element.selection;
|
||||
}
|
||||
|
||||
private _submit() {
|
||||
this.modalHandler?.submit({ selection: this._selection });
|
||||
}
|
||||
|
||||
private _close() {
|
||||
this.modalHandler?.reject();
|
||||
}
|
||||
|
||||
render() {
|
||||
return html`
|
||||
<umb-workspace-editor headline="Select Content">
|
||||
<uui-box>
|
||||
<uui-input></uui-input>
|
||||
<hr />
|
||||
<umb-tree
|
||||
alias="Umb.Tree.Media"
|
||||
@selected=${this._handleSelectionChange}
|
||||
.selection=${this._selection}
|
||||
selectable
|
||||
?multiple=${this._multiple}></umb-tree>
|
||||
</uui-box>
|
||||
<div slot="actions">
|
||||
<uui-button label="Close" @click=${this._close}></uui-button>
|
||||
<uui-button label="Submit" look="primary" color="positive" @click=${this._submit}></uui-button>
|
||||
</div>
|
||||
</umb-workspace-editor>
|
||||
`;
|
||||
}
|
||||
|
||||
static styles = [
|
||||
UUITextStyles,
|
||||
css`
|
||||
h3 {
|
||||
margin-left: var(--uui-size-space-5);
|
||||
margin-right: var(--uui-size-space-5);
|
||||
}
|
||||
|
||||
uui-input {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
hr {
|
||||
border: none;
|
||||
border-bottom: 1px solid var(--uui-color-divider);
|
||||
margin: 16px 0;
|
||||
}
|
||||
|
||||
#content-list {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: var(--uui-size-space-3);
|
||||
}
|
||||
|
||||
.content-item {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.content-item.selected {
|
||||
background-color: var(--uui-color-selected);
|
||||
color: var(--uui-color-selected-contrast);
|
||||
}
|
||||
`,
|
||||
];
|
||||
}
|
||||
|
||||
export default UmbMediaPickerModalElement;
|
||||
|
||||
declare global {
|
||||
interface HTMLElementTagNameMap {
|
||||
'umb-media-picker-modal': UmbMediaPickerModalElement;
|
||||
}
|
||||
}
|
||||
@@ -3,7 +3,6 @@ import { manifests as repositoryManifests } from './repository/manifests';
|
||||
import { manifests as menuItemManifests } from './menu-item/manifests';
|
||||
import { manifests as treeManifests } from './tree/manifests';
|
||||
import { manifests as workspaceManifests } from './workspace/manifests';
|
||||
import { manifests as modalManifests } from './modal/manifests';
|
||||
|
||||
export const manifests = [
|
||||
...entityActions,
|
||||
@@ -11,5 +10,4 @@ export const manifests = [
|
||||
...menuItemManifests,
|
||||
...treeManifests,
|
||||
...workspaceManifests,
|
||||
...modalManifests,
|
||||
];
|
||||
|
||||
@@ -1,12 +0,0 @@
|
||||
import type { ManifestModal } from '@umbraco-cms/backoffice/extensions-registry';
|
||||
|
||||
const modals: Array<ManifestModal> = [
|
||||
{
|
||||
type: 'modal',
|
||||
alias: 'Umb.Modal.DataTypePicker',
|
||||
name: 'Data Type Picker Modal',
|
||||
loader: () => import('./data-type-picker/data-type-picker-modal.element'),
|
||||
},
|
||||
];
|
||||
|
||||
export const manifests = [...modals];
|
||||
@@ -1 +1,2 @@
|
||||
import './file-system-tree-item/file-system-tree-item.element';
|
||||
import './insert-menu/templating-insert-menu.element';
|
||||
@@ -0,0 +1,220 @@
|
||||
import { UUITextStyles } from '@umbraco-ui/uui-css';
|
||||
import { css, html } from 'lit';
|
||||
import { customElement, property } from 'lit/decorators.js';
|
||||
import { UMB_MODAL_TEMPLATING_INSERT_CHOOSE_TYPE_SIDEBAR_ALIAS } from '../../modals/manifests';
|
||||
import { UmbDictionaryRepository } from '../../../translation/dictionary/repository/dictionary.repository';
|
||||
import { getInsertDictionarySnippet, getInsertPartialSnippet } from '../../utils';
|
||||
import {
|
||||
ChooseInsertTypeModalResult,
|
||||
CodeSnippetType,
|
||||
UMB_MODAL_TEMPLATING_INSERT_FIELD_SIDEBAR_MODAL,
|
||||
} from '../../modals/insert-choose-type-sidebar.element';
|
||||
import {
|
||||
UMB_DICTIONARY_ITEM_PICKER_MODAL,
|
||||
UMB_MODAL_CONTEXT_TOKEN,
|
||||
UMB_PARTIAL_VIEW_PICKER_MODAL,
|
||||
UmbDictionaryItemPickerModalResult,
|
||||
UmbModalContext,
|
||||
UmbModalHandler,
|
||||
UmbModalToken,
|
||||
UmbPartialViewPickerModalResult,
|
||||
} from '@umbraco-cms/backoffice/modal';
|
||||
import { UmbLitElement } from '@umbraco-cms/internal/lit-element';
|
||||
|
||||
export const UMB_MODAL_TEMPLATING_INSERT_CHOOSE_TYPE_SIDEBAR_MODAL = new UmbModalToken<{ hidePartialView: boolean }>(
|
||||
UMB_MODAL_TEMPLATING_INSERT_CHOOSE_TYPE_SIDEBAR_ALIAS,
|
||||
{
|
||||
type: 'sidebar',
|
||||
size: 'small',
|
||||
}
|
||||
);
|
||||
|
||||
@customElement('umb-templating-insert-menu')
|
||||
export class UmbTemplatingInsertMenuElement extends UmbLitElement {
|
||||
@property()
|
||||
value = '';
|
||||
|
||||
private _modalContext?: UmbModalContext;
|
||||
|
||||
#openModal?: UmbModalHandler;
|
||||
|
||||
#dictionaryRepository = new UmbDictionaryRepository(this);
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
this.consumeContext(UMB_MODAL_CONTEXT_TOKEN, (instance) => {
|
||||
this._modalContext = instance;
|
||||
});
|
||||
}
|
||||
|
||||
async determineInsertValue(modalResult: ChooseInsertTypeModalResult) {
|
||||
const { type, value } = modalResult;
|
||||
|
||||
switch (type) {
|
||||
case CodeSnippetType.umbracoField: {
|
||||
this.#getUmbracoFieldValueSnippet(value as string);
|
||||
break;
|
||||
}
|
||||
case CodeSnippetType.partialView: {
|
||||
this.#getPartialViewSnippet(value as UmbPartialViewPickerModalResult);
|
||||
break;
|
||||
}
|
||||
case CodeSnippetType.dictionaryItem: {
|
||||
this.#getDictionaryItemSnippet(value as UmbDictionaryItemPickerModalResult);
|
||||
break;
|
||||
}
|
||||
case CodeSnippetType.macro: {
|
||||
throw new Error('Not implemented');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#getDictionaryItemSnippet = async (modalResult: UmbDictionaryItemPickerModalResult) => {
|
||||
const id = modalResult.selection[0];
|
||||
if (id === null) return;
|
||||
const { data } = await this.#dictionaryRepository.requestById(id);
|
||||
this.value = getInsertDictionarySnippet(data?.name ?? '');
|
||||
};
|
||||
|
||||
#getUmbracoFieldValueSnippet = async (value: string) => {
|
||||
this.value = value;
|
||||
};
|
||||
|
||||
#getPartialViewSnippet = async (modalResult: UmbPartialViewPickerModalResult) => {
|
||||
this.value = getInsertPartialSnippet(modalResult.selection?.[0] ?? '');
|
||||
};
|
||||
|
||||
#openChooseTypeModal = () => {
|
||||
this.#openModal = this._modalContext?.open(UMB_MODAL_TEMPLATING_INSERT_CHOOSE_TYPE_SIDEBAR_MODAL, {
|
||||
hidePartialView: this.hidePartialView,
|
||||
});
|
||||
this.#openModal?.onSubmit().then((closedModal: ChooseInsertTypeModalResult) => {
|
||||
this.determineInsertValue(closedModal);
|
||||
});
|
||||
};
|
||||
|
||||
#openInsertValueSidebar() {
|
||||
this.#openModal = this._modalContext?.open(UMB_MODAL_TEMPLATING_INSERT_FIELD_SIDEBAR_MODAL);
|
||||
this.#openModal?.onSubmit().then((value) => {
|
||||
this.value = value;
|
||||
this.#dispatchInsertEvent();
|
||||
});
|
||||
}
|
||||
|
||||
#openInsertPartialViewSidebar() {
|
||||
this.#openModal = this._modalContext?.open(UMB_PARTIAL_VIEW_PICKER_MODAL);
|
||||
this.#openModal?.onSubmit().then((value) => {
|
||||
this.#getPartialViewSnippet(value).then(() => {
|
||||
this.#dispatchInsertEvent();
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
#openInsertDictionaryItemModal() {
|
||||
this.#openModal = this._modalContext?.open(UMB_DICTIONARY_ITEM_PICKER_MODAL);
|
||||
this.#openModal?.onSubmit().then((value) => {
|
||||
this.#getDictionaryItemSnippet(value).then(() => {
|
||||
this.#dispatchInsertEvent();
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
#dispatchInsertEvent() {
|
||||
this.dispatchEvent(new CustomEvent('insert', { bubbles: true, cancelable: true, composed: false }));
|
||||
}
|
||||
|
||||
@property()
|
||||
hidePartialView = false;
|
||||
|
||||
render() {
|
||||
return html`
|
||||
<uui-button-group>
|
||||
<uui-button look="secondary" @click=${this.#openChooseTypeModal}>
|
||||
<uui-icon name="umb:add"></uui-icon>Insert</uui-button
|
||||
>
|
||||
<umb-button-with-dropdown
|
||||
look="secondary"
|
||||
compact
|
||||
placement="bottom-start"
|
||||
id="insert-button"
|
||||
label="open insert menu">
|
||||
<ul id="insert-menu" slot="dropdown">
|
||||
<li>
|
||||
<uui-menu-item
|
||||
class="insert-menu-item"
|
||||
target="_blank"
|
||||
label="Value"
|
||||
title="Value"
|
||||
@click=${this.#openInsertValueSidebar}>
|
||||
</uui-menu-item>
|
||||
</li>
|
||||
${this.hidePartialView
|
||||
? ''
|
||||
: html` <li>
|
||||
<uui-menu-item
|
||||
class="insert-menu-item"
|
||||
label="Partial view"
|
||||
title="Partial view"
|
||||
@click=${this.#openInsertPartialViewSidebar}>
|
||||
</uui-menu-item>
|
||||
</li>`}
|
||||
<li>
|
||||
<uui-menu-item
|
||||
class="insert-menu-item"
|
||||
label="Dictionary item"
|
||||
title="Dictionary item"
|
||||
@click=${this.#openInsertDictionaryItemModal}>
|
||||
</uui-menu-item>
|
||||
</li>
|
||||
<li>
|
||||
<uui-menu-item class="insert-menu-item" label="Macro" title="Macro"> </uui-menu-item>
|
||||
</li>
|
||||
</ul>
|
||||
</umb-button-with-dropdown>
|
||||
</uui-button-group>
|
||||
`;
|
||||
}
|
||||
|
||||
static styles = [
|
||||
UUITextStyles,
|
||||
css`
|
||||
#insert-menu {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
margin-top: var(--uui-size-space-3);
|
||||
background-color: var(--uui-color-surface);
|
||||
box-shadow: var(--uui-shadow-depth-3);
|
||||
min-width: 150px;
|
||||
}
|
||||
|
||||
#insert-menu > li,
|
||||
ul {
|
||||
padding: 0;
|
||||
width: 100%;
|
||||
list-style: none;
|
||||
}
|
||||
|
||||
ul {
|
||||
transform: translateX(-100px);
|
||||
}
|
||||
|
||||
.insert-menu-item {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
umb-button-with-dropdown {
|
||||
--umb-button-with-dropdown-symbol-expand-margin-left: 0;
|
||||
}
|
||||
|
||||
uui-icon[name='umb:add'] {
|
||||
margin-right: var(--uui-size-4);
|
||||
}
|
||||
`,
|
||||
];
|
||||
}
|
||||
|
||||
declare global {
|
||||
interface HTMLElementTagNameMap {
|
||||
'umb-templating-insert-menu': UmbTemplatingInsertMenuElement;
|
||||
}
|
||||
}
|
||||
@@ -1,12 +1,20 @@
|
||||
import { manifests as menuManifests } from './menu.manifests';
|
||||
import { manifests as templateManifests } from './templates/manifests';
|
||||
import { manifests as stylesheetManifests } from './stylesheets/manifests';
|
||||
import { manifests as partialManifests } from './partial-views/manifests';
|
||||
import { manifests as modalManifests } from './modals/manifests';
|
||||
import type { UmbEntrypointOnInit } from '@umbraco-cms/backoffice/extensions-api';
|
||||
|
||||
import './components';
|
||||
import './templates/components';
|
||||
|
||||
export const manifests = [...menuManifests, ...templateManifests, ...stylesheetManifests];
|
||||
export const manifests = [
|
||||
...menuManifests,
|
||||
...templateManifests,
|
||||
...stylesheetManifests,
|
||||
...partialManifests,
|
||||
...modalManifests,
|
||||
];
|
||||
|
||||
export const onInit: UmbEntrypointOnInit = (_host, extensionRegistry) => {
|
||||
extensionRegistry.registerMany(manifests);
|
||||
|
||||
@@ -0,0 +1,159 @@
|
||||
import { UUITextStyles } from '@umbraco-ui/uui-css';
|
||||
import { css, html } from 'lit';
|
||||
import { customElement } from 'lit/decorators.js';
|
||||
import { UMB_MODAL_TEMPLATING_INSERT_FIELD_SIDEBAR_ALIAS } from './manifests';
|
||||
import { UmbModalBaseElement } from '@umbraco-cms/internal/modal';
|
||||
import {
|
||||
UMB_MODAL_CONTEXT_TOKEN,
|
||||
UmbModalContext,
|
||||
UmbModalToken,
|
||||
UMB_PARTIAL_VIEW_PICKER_MODAL,
|
||||
UmbModalHandler,
|
||||
UMB_DICTIONARY_ITEM_PICKER_MODAL,
|
||||
UmbDictionaryItemPickerModalResult,
|
||||
} from '@umbraco-cms/backoffice/modal';
|
||||
|
||||
export const UMB_MODAL_TEMPLATING_INSERT_FIELD_SIDEBAR_MODAL = new UmbModalToken(
|
||||
UMB_MODAL_TEMPLATING_INSERT_FIELD_SIDEBAR_ALIAS,
|
||||
{
|
||||
type: 'sidebar',
|
||||
size: 'small',
|
||||
}
|
||||
);
|
||||
|
||||
export interface ChooseInsertTypeModalData {
|
||||
hidePartialViews?: boolean;
|
||||
}
|
||||
|
||||
export enum CodeSnippetType {
|
||||
partialView = 'partialView',
|
||||
umbracoField = 'umbracoField',
|
||||
dictionaryItem = 'dictionaryItem',
|
||||
macro = 'macro',
|
||||
}
|
||||
export interface ChooseInsertTypeModalResult {
|
||||
value: string | UmbDictionaryItemPickerModalResult;
|
||||
type: CodeSnippetType;
|
||||
}
|
||||
|
||||
@customElement('umb-templating-choose-insert-type-modal')
|
||||
export default class UmbChooseInsertTypeModalElement extends UmbModalBaseElement<
|
||||
ChooseInsertTypeModalData,
|
||||
ChooseInsertTypeModalResult
|
||||
> {
|
||||
private _close() {
|
||||
this.modalHandler?.reject();
|
||||
}
|
||||
|
||||
private _modalContext?: UmbModalContext;
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
this.consumeContext(UMB_MODAL_CONTEXT_TOKEN, (instance) => {
|
||||
this._modalContext = instance;
|
||||
});
|
||||
}
|
||||
|
||||
#openModal?: UmbModalHandler;
|
||||
|
||||
#openInsertValueSidebar() {
|
||||
this.#openModal = this._modalContext?.open(UMB_MODAL_TEMPLATING_INSERT_FIELD_SIDEBAR_MODAL);
|
||||
this.#openModal?.onSubmit().then((chosenValue) => {
|
||||
if (chosenValue) this.modalHandler?.submit({ value: chosenValue, type: CodeSnippetType.umbracoField });
|
||||
});
|
||||
}
|
||||
|
||||
#openInsertPartialViewSidebar() {
|
||||
this.#openModal = this._modalContext?.open(UMB_PARTIAL_VIEW_PICKER_MODAL);
|
||||
this.#openModal?.onSubmit().then((partialViewPickerModalResult) => {
|
||||
if (partialViewPickerModalResult)
|
||||
this.modalHandler?.submit({
|
||||
type: CodeSnippetType.partialView,
|
||||
value: partialViewPickerModalResult.selection[0],
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
#openInsertDictionaryItemModal() {
|
||||
this.#openModal = this._modalContext?.open(UMB_DICTIONARY_ITEM_PICKER_MODAL);
|
||||
this.#openModal?.onSubmit().then((dictionaryItemPickerModalResult) => {
|
||||
if (dictionaryItemPickerModalResult) this.modalHandler?.submit({ value: dictionaryItemPickerModalResult, type: CodeSnippetType.dictionaryItem });
|
||||
});
|
||||
}
|
||||
|
||||
render() {
|
||||
return html`
|
||||
<umb-body-layout headline="Insert">
|
||||
<div id="main">
|
||||
<uui-box>
|
||||
<uui-button @click=${this.#openInsertValueSidebar} look="placeholder" label="Insert value"
|
||||
><h3>Value</h3>
|
||||
<p>
|
||||
Displays the value of a named field from the current page, with options to modify the value or fallback
|
||||
to alternative values.
|
||||
</p></uui-button
|
||||
>
|
||||
${this.data?.hidePartialViews
|
||||
? ''
|
||||
: html`<uui-button @click=${this.#openInsertPartialViewSidebar} look="placeholder" label="Insert value"
|
||||
><h3>Partial view</h3>
|
||||
<p>
|
||||
A partial view is a separate template file which can be rendered inside another template, it's great
|
||||
for reusing markup or for separating complex templates into separate files.
|
||||
</p></uui-button
|
||||
>`}
|
||||
<uui-button @click=${this._close} look="placeholder" label="Insert Macro"
|
||||
><h3>Macro</h3>
|
||||
<p>
|
||||
A Macro is a configurable component which is great for reusable parts of your design, where you need the
|
||||
option to provide parameters, such as galleries, forms and lists.
|
||||
</p></uui-button
|
||||
>
|
||||
<uui-button @click=${this.#openInsertDictionaryItemModal} look="placeholder" label="Insert Dictionary item"
|
||||
><h3>Dictionary item</h3>
|
||||
<p>
|
||||
A dictionary item is a placeholder for a translatable piece of text, which makes it easy to create
|
||||
designs for multilingual websites.
|
||||
</p></uui-button
|
||||
>
|
||||
</uui-box>
|
||||
</div>
|
||||
<div slot="actions">
|
||||
<uui-button @click=${this._close} look="secondary">Close</uui-button>
|
||||
</div>
|
||||
</umb-body-layout>
|
||||
`;
|
||||
}
|
||||
|
||||
static styles = [
|
||||
UUITextStyles,
|
||||
css`
|
||||
:host {
|
||||
display: block;
|
||||
color: var(--uui-color-text);
|
||||
}
|
||||
|
||||
#main {
|
||||
box-sizing: border-box;
|
||||
padding: var(--uui-size-space-5);
|
||||
height: calc(100vh - 124px);
|
||||
}
|
||||
|
||||
#main uui-button:not(:last-of-type) {
|
||||
display: block;
|
||||
margin-bottom: var(--uui-size-space-5);
|
||||
}
|
||||
|
||||
h3,
|
||||
p {
|
||||
text-align: left;
|
||||
}
|
||||
`,
|
||||
];
|
||||
}
|
||||
|
||||
declare global {
|
||||
interface HTMLElementTagNameMap {
|
||||
'umb-templating-choose-insert-type-modal': UmbChooseInsertTypeModalElement;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,139 @@
|
||||
import { UUITextStyles } from '@umbraco-ui/uui-css';
|
||||
import { css, html } from 'lit';
|
||||
import { customElement, property, query } from 'lit/decorators.js';
|
||||
import { UUIBooleanInputElement, UUIInputElement } from '@umbraco-ui/uui';
|
||||
import { getAddSectionSnippet, getRenderBodySnippet, getRenderSectionSnippet } from '../../utils';
|
||||
|
||||
@customElement('umb-insert-section-checkbox')
|
||||
export class UmbInsertSectionCheckboxElement extends UUIBooleanInputElement {
|
||||
renderCheckbox() {
|
||||
return html``;
|
||||
}
|
||||
|
||||
@property({ type: Boolean, attribute: 'show-mandatory' })
|
||||
showMandatory = false;
|
||||
|
||||
@property({ type: Boolean, attribute: 'show-input' })
|
||||
showInput = false;
|
||||
|
||||
@query('uui-input')
|
||||
input?: UUIInputElement;
|
||||
|
||||
@query('form')
|
||||
form?: HTMLFormElement;
|
||||
|
||||
@query('uui-checkbox')
|
||||
checkbox?: HTMLFormElement;
|
||||
|
||||
validate() {
|
||||
if (!this.form) return true;
|
||||
|
||||
this.form.requestSubmit();
|
||||
return this.form.checkValidity();
|
||||
}
|
||||
|
||||
#preventDefault(event: Event) {
|
||||
event.preventDefault();
|
||||
}
|
||||
|
||||
get inputValue() {
|
||||
return this.input?.value;
|
||||
}
|
||||
|
||||
get isMandatory() {
|
||||
return this.checkbox?.checked;
|
||||
}
|
||||
|
||||
/* eslint-disable lit-a11y/click-events-have-key-events */
|
||||
render() {
|
||||
return html`
|
||||
${super.render()}
|
||||
<h3 @click=${this.click}>${this.checked ? html`<uui-icon name="umb:check"></uui-icon>` : ''}${this.label}</h3>
|
||||
<div @click=${this.click}>
|
||||
<slot name="description"><p>here goes some description</p></slot>
|
||||
</div>
|
||||
${this.checked && this.showInput
|
||||
? html`<uui-form>
|
||||
<form @submit=${this.#preventDefault}>
|
||||
<uui-form-layout-item>
|
||||
<uui-label slot="label" for="section-name-input" required>Section name</uui-label>
|
||||
<uui-input
|
||||
required
|
||||
placeholder="Enter section name"
|
||||
id="section-name-input"></uui-input> </uui-form-layout-item
|
||||
>${this.showMandatory
|
||||
? html`<p slot="if-checked">
|
||||
<uui-checkbox label="Section is mandatory">Section is mandatory </uui-checkbox><br />
|
||||
<small
|
||||
>If mandatory, the child template must contain a <code>@section</code> definition, otherwise an
|
||||
error is shown.</small
|
||||
>
|
||||
</p>`
|
||||
: ''}
|
||||
</form>
|
||||
</uui-form>`
|
||||
: ''}
|
||||
`;
|
||||
}
|
||||
/* eslint-enable lit-a11y/click-events-have-key-events */
|
||||
|
||||
static styles = [
|
||||
...UUIBooleanInputElement.styles,
|
||||
UUITextStyles,
|
||||
css`
|
||||
:host {
|
||||
display: block;
|
||||
border-style: dashed;
|
||||
background-color: transparent;
|
||||
color: var(--uui-color-default-standalone, rgb(28, 35, 59));
|
||||
border-color: var(--uui-color-border-standalone, #c2c2c2);
|
||||
border-radius: var(--uui-border-radius, 3px);
|
||||
border-width: 1px;
|
||||
line-height: normal;
|
||||
padding: 6px 18px;
|
||||
}
|
||||
|
||||
:host(:hover),
|
||||
:host(:focus),
|
||||
:host(:focus-within) {
|
||||
background-color: var(--uui-button-background-color-hover, transparent);
|
||||
color: var(--uui-color-default-emphasis, #3544b1);
|
||||
border-color: var(--uui-color-default-emphasis, #3544b1);
|
||||
}
|
||||
|
||||
uui-icon {
|
||||
background-color: var(--uui-color-positive-emphasis);
|
||||
border-radius: 50%;
|
||||
padding: 0.2em;
|
||||
margin-right: 1ch;
|
||||
color: var(--uui-color-positive-contrast);
|
||||
font-size: 0.7em;
|
||||
}
|
||||
|
||||
::slotted(*) {
|
||||
line-height: normal;
|
||||
}
|
||||
|
||||
.label {
|
||||
display: none;
|
||||
}
|
||||
|
||||
h3,
|
||||
p {
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
uui-input {
|
||||
width: 100%;
|
||||
}
|
||||
`,
|
||||
];
|
||||
}
|
||||
|
||||
export default UmbInsertSectionCheckboxElement;
|
||||
|
||||
declare global {
|
||||
interface HTMLElementTagNameMap {
|
||||
'umb-insert-section-input': UmbInsertSectionCheckboxElement;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,131 @@
|
||||
import { UUITextStyles } from '@umbraco-ui/uui-css';
|
||||
import { css, html } from 'lit';
|
||||
import { customElement, queryAll, state } from 'lit/decorators.js';
|
||||
import { UMB_MODAL_TEMPLATING_INSERT_SECTION_SIDEBAR_ALIAS } from '../manifests';
|
||||
import { UmbModalBaseElement } from '@umbraco-cms/internal/modal';
|
||||
import { UmbModalToken } from '@umbraco-cms/backoffice/modal';
|
||||
|
||||
import './insert-section-input.element';
|
||||
import UmbInsertSectionCheckboxElement from './insert-section-input.element';
|
||||
import { getAddSectionSnippet, getRenderBodySnippet, getRenderSectionSnippet } from '../../utils';
|
||||
|
||||
export const UMB_MODAL_TEMPLATING_INSERT_SECTION_MODAL = new UmbModalToken(
|
||||
UMB_MODAL_TEMPLATING_INSERT_SECTION_SIDEBAR_ALIAS,
|
||||
{
|
||||
type: 'sidebar',
|
||||
size: 'small',
|
||||
}
|
||||
);
|
||||
|
||||
export interface InsertSectionModalModalResult {
|
||||
value?: string;
|
||||
}
|
||||
|
||||
@customElement('umb-templating-insert-section-modal')
|
||||
export default class UmbTemplatingInsertSectionModalElement extends UmbModalBaseElement<
|
||||
object,
|
||||
InsertSectionModalModalResult
|
||||
> {
|
||||
@queryAll('umb-insert-section-checkbox')
|
||||
checkboxes!: NodeListOf<UmbInsertSectionCheckboxElement>;
|
||||
|
||||
@state()
|
||||
selectedCheckbox?: UmbInsertSectionCheckboxElement | null = null;
|
||||
|
||||
@state()
|
||||
snippet = '';
|
||||
|
||||
#chooseSection(event: Event) {
|
||||
event.stopPropagation();
|
||||
const target = event.target as UmbInsertSectionCheckboxElement;
|
||||
const checkboxes = Array.from(this.checkboxes);
|
||||
if (checkboxes.every((checkbox) => checkbox.checked === false)) {
|
||||
this.selectedCheckbox = null;
|
||||
return;
|
||||
}
|
||||
if (target.checked) {
|
||||
this.selectedCheckbox = target;
|
||||
this.snippet = this.snippetMethods[checkboxes.indexOf(target)](target?.inputValue as string, target?.isMandatory);
|
||||
checkboxes.forEach((checkbox) => {
|
||||
if (checkbox !== target) {
|
||||
checkbox.checked = false;
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
firstUpdated() {
|
||||
this.selectedCheckbox = this.checkboxes[0];
|
||||
}
|
||||
|
||||
snippetMethods = [getRenderBodySnippet, getRenderSectionSnippet, getAddSectionSnippet];
|
||||
|
||||
#close() {
|
||||
this.modalHandler?.reject();
|
||||
}
|
||||
|
||||
#submit() {
|
||||
if (this.selectedCheckbox?.validate()) this.modalHandler?.submit({ value: this.snippet ?? '' });
|
||||
}
|
||||
|
||||
render() {
|
||||
return html`
|
||||
<umb-body-layout headline="Insert">
|
||||
<div id="main">
|
||||
<uui-box @change=${this.#chooseSection}>
|
||||
<umb-insert-section-checkbox label="Render child template" checked>
|
||||
<p slot="description">
|
||||
Renders the contents of a child template, by inserting a <code>@RenderBody()</code> placeholder.
|
||||
</p>
|
||||
</umb-insert-section-checkbox>
|
||||
|
||||
<umb-insert-section-checkbox label="Render a named section" show-mandatory show-input>
|
||||
<p slot="description">
|
||||
Renders a named area of a child template, by inserting a <code>@RenderSection(name)</code> placeholder.
|
||||
This renders an area of a child template which is wrapped in a corresponding
|
||||
<code>@section [name]{ ... }</code> definition.
|
||||
</p>
|
||||
</umb-insert-section-checkbox>
|
||||
|
||||
<umb-insert-section-checkbox label="Define a named section" show-input>
|
||||
<p slot="description">
|
||||
Defines a part of your template as a named section by wrapping it in <code>@section { ... }</code>. This
|
||||
can be rendered in a specific area of the parent of this template, by using <code>@RenderSection</code>.
|
||||
</p>
|
||||
</umb-insert-section-checkbox>
|
||||
</uui-box>
|
||||
</div>
|
||||
<div slot="actions">
|
||||
<uui-button @click=${this.#close} look="secondary">Close</uui-button>
|
||||
<uui-button @click=${this.#submit} look="primary" color="positive">Submit</uui-button>
|
||||
</div>
|
||||
</umb-body-layout>
|
||||
`;
|
||||
}
|
||||
|
||||
static styles = [
|
||||
UUITextStyles,
|
||||
css`
|
||||
:host {
|
||||
display: block;
|
||||
color: var(--uui-color-text);
|
||||
}
|
||||
|
||||
#main {
|
||||
box-sizing: border-box;
|
||||
padding: var(--uui-size-space-5);
|
||||
height: calc(100vh - 124px);
|
||||
}
|
||||
|
||||
#main umb-insert-section-checkbox:not(:last-of-type) {
|
||||
margin-bottom: var(--uui-size-space-5);
|
||||
}
|
||||
`,
|
||||
];
|
||||
}
|
||||
|
||||
declare global {
|
||||
interface HTMLElementTagNameMap {
|
||||
'umb-templating-insert-section-modal': UmbTemplatingInsertSectionModalElement;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,132 @@
|
||||
import { UUITextStyles } from '@umbraco-ui/uui-css';
|
||||
import { css, html } from 'lit';
|
||||
import { customElement, state } from 'lit/decorators.js';
|
||||
import { UUIComboboxElement, UUIInputElement } from '@umbraco-ui/uui';
|
||||
import { getUmbracoFieldSnippet } from '../utils';
|
||||
import { UmbModalBaseElement } from '@umbraco-cms/internal/modal';
|
||||
|
||||
@customElement('umb-insert-value-sidebar')
|
||||
export default class UmbInsertValueSidebarElement extends UmbModalBaseElement<object, string> {
|
||||
private _close() {
|
||||
this.modalHandler?.submit();
|
||||
}
|
||||
|
||||
private _submit() {
|
||||
this.modalHandler?.submit(this.output);
|
||||
}
|
||||
|
||||
@state()
|
||||
showDefaultValueInput = false;
|
||||
|
||||
@state()
|
||||
recursive = false;
|
||||
|
||||
@state()
|
||||
defaultValue: string | null = null;
|
||||
|
||||
@state()
|
||||
field: string | null = null;
|
||||
|
||||
@state()
|
||||
output = '';
|
||||
|
||||
protected willUpdate(): void {
|
||||
this.output = this.field ? getUmbracoFieldSnippet(this.field, this.defaultValue, this.recursive) : '';
|
||||
}
|
||||
|
||||
#setField(event: Event) {
|
||||
const target = event.target as UUIComboboxElement;
|
||||
this.field = target.value as string;
|
||||
}
|
||||
|
||||
#setDefaultValue(event: Event) {
|
||||
const target = event.target as UUIInputElement;
|
||||
this.defaultValue = target.value === '' ? null : (target.value as string);
|
||||
}
|
||||
|
||||
render() {
|
||||
return html`
|
||||
<umb-body-layout headline="Insert">
|
||||
<div id="main">
|
||||
<uui-box>
|
||||
<uui-form-layout-item>
|
||||
<uui-label slot="label" for="field-selector">Choose field</uui-label>
|
||||
<uui-combobox id="field-selector" @change=${this.#setField}>
|
||||
<uui-combobox-list>
|
||||
<uui-combobox-list-option style="padding: 8px"> apple </uui-combobox-list-option>
|
||||
<uui-combobox-list-option style="padding: 8px"> orange </uui-combobox-list-option>
|
||||
<uui-combobox-list-option style="padding: 8px"> lemon </uui-combobox-list-option>
|
||||
</uui-combobox-list>
|
||||
</uui-combobox>
|
||||
</uui-form-layout-item>
|
||||
${this.showDefaultValueInput
|
||||
? html` <uui-form-layout-item>
|
||||
<uui-label slot="label" for="default-value">Default value</uui-label>
|
||||
<uui-input
|
||||
id="default-value"
|
||||
type="text"
|
||||
name="default-value"
|
||||
label="default value"
|
||||
@input=${this.#setDefaultValue}>
|
||||
</uui-input>
|
||||
</uui-form-layout-item>`
|
||||
: html` <uui-button
|
||||
@click=${() => (this.showDefaultValueInput = true)}
|
||||
look="placeholder"
|
||||
label="Add default value "
|
||||
>Add default value</uui-button
|
||||
>`}
|
||||
<uui-form-layout-item>
|
||||
<uui-label slot="label" for="default-value">Fallback</uui-label>
|
||||
<uui-checkbox @change=${() => (this.recursive = !this.recursive)}>From ancestors</uui-checkbox>
|
||||
</uui-form-layout-item>
|
||||
<uui-form-layout-item>
|
||||
<uui-label slot="label">Output sample</uui-label>
|
||||
<uui-code-block>${this.output}</uui-code-block>
|
||||
</uui-form-layout-item>
|
||||
</uui-box>
|
||||
</div>
|
||||
<div slot="actions">
|
||||
<uui-button @click=${this._close} look="secondary">Close</uui-button>
|
||||
<uui-button @click=${this._submit} look="primary">Submit</uui-button>
|
||||
</div>
|
||||
</umb-body-layout>
|
||||
`;
|
||||
}
|
||||
|
||||
static styles = [
|
||||
UUITextStyles,
|
||||
css`
|
||||
:host {
|
||||
display: block;
|
||||
color: var(--uui-color-text);
|
||||
}
|
||||
|
||||
#main {
|
||||
box-sizing: border-box;
|
||||
padding: var(--uui-size-space-5);
|
||||
height: calc(100vh - 124px);
|
||||
}
|
||||
|
||||
#main uui-button {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
h3,
|
||||
p {
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
uui-combobox,
|
||||
uui-input {
|
||||
width: 100%;
|
||||
}
|
||||
`,
|
||||
];
|
||||
}
|
||||
|
||||
declare global {
|
||||
interface HTMLElementTagNameMap {
|
||||
'umb-insert-value-sidebar': UmbInsertValueSidebarElement;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
import { ManifestModal } from '@umbraco-cms/backoffice/extensions-registry';
|
||||
import { UMB_PARTIAL_VIEW_PICKER_MODAL_ALIAS } from '@umbraco-cms/backoffice/modal';
|
||||
|
||||
export const UMB_MODAL_TEMPLATING_INSERT_CHOOSE_TYPE_SIDEBAR_ALIAS = 'Umb.Modal.Templating.Insert.ChooseType.Sidebar';
|
||||
export const UMB_MODAL_TEMPLATING_INSERT_FIELD_SIDEBAR_ALIAS = 'Umb.Modal.Templating.Insert.Value.Sidebar';
|
||||
export const UMB_MODAL_TEMPLATING_INSERT_SECTION_SIDEBAR_ALIAS = 'Umb.Modal.Templating.Insert.Section.Sidebar';
|
||||
|
||||
const modals: Array<ManifestModal> = [
|
||||
{
|
||||
type: 'modal',
|
||||
alias: UMB_MODAL_TEMPLATING_INSERT_CHOOSE_TYPE_SIDEBAR_ALIAS,
|
||||
name: 'Choose insert type sidebar',
|
||||
loader: () => import('./insert-choose-type-sidebar.element'),
|
||||
},
|
||||
{
|
||||
type: 'modal',
|
||||
alias: UMB_MODAL_TEMPLATING_INSERT_FIELD_SIDEBAR_ALIAS,
|
||||
name: 'Insert value type sidebar',
|
||||
loader: () => import('./insert-value-sidebar.element'),
|
||||
},
|
||||
{
|
||||
type: 'modal',
|
||||
alias: UMB_PARTIAL_VIEW_PICKER_MODAL_ALIAS,
|
||||
name: 'Partial View Picker Modal',
|
||||
loader: () => import('../../templating/modals/partial-view-picker-modal.element'),
|
||||
},
|
||||
{
|
||||
type: 'modal',
|
||||
alias: UMB_MODAL_TEMPLATING_INSERT_SECTION_SIDEBAR_ALIAS,
|
||||
name: 'Partial Insert Section Picker Modal',
|
||||
loader: () => import('./insert-section-modal/insert-section-modal.element'),
|
||||
},
|
||||
];
|
||||
|
||||
export const manifests = [...modals];
|
||||
@@ -0,0 +1 @@
|
||||
//TODO: move tokens here nad import this file somewhere
|
||||
@@ -0,0 +1,89 @@
|
||||
import { UUITextStyles } from '@umbraco-ui/uui-css';
|
||||
import { css, html } from 'lit';
|
||||
import { customElement, state } from 'lit/decorators.js';
|
||||
import { UmbPartialViewPickerModalData, UmbPartialViewPickerModalResult } from '@umbraco-cms/backoffice/modal';
|
||||
import { UmbModalBaseElement } from '@umbraco-cms/internal/modal';
|
||||
import { UmbTreeElement } from '@umbraco-cms/backoffice/core/components';
|
||||
|
||||
@customElement('umb-partial-view-picker-modal')
|
||||
export default class UmbPartialViewPickerModalElement extends UmbModalBaseElement<
|
||||
UmbPartialViewPickerModalData,
|
||||
UmbPartialViewPickerModalResult
|
||||
> {
|
||||
@state()
|
||||
_selection: Array<string | null> = [];
|
||||
|
||||
@state()
|
||||
_multiple = false;
|
||||
|
||||
connectedCallback() {
|
||||
super.connectedCallback();
|
||||
this._selection = this.data?.selection ?? [];
|
||||
this._multiple = this.data?.multiple ?? true;
|
||||
}
|
||||
|
||||
private _handleSelectionChange(e: CustomEvent) {
|
||||
e.stopPropagation();
|
||||
const element = e.target as UmbTreeElement;
|
||||
this._selection = this._multiple ? element.selection : [element.selection[element.selection.length - 1]];
|
||||
this._submit();
|
||||
}
|
||||
|
||||
private _submit() {
|
||||
this.modalHandler?.submit({ selection: this._selection });
|
||||
}
|
||||
|
||||
private _close() {
|
||||
this.modalHandler?.reject();
|
||||
}
|
||||
|
||||
render() {
|
||||
return html`
|
||||
<umb-body-layout headline="Insert Partial view">
|
||||
<div id="main">
|
||||
<uui-box>
|
||||
<umb-tree
|
||||
alias="Umb.Tree.PartialViews"
|
||||
@selected=${this._handleSelectionChange}
|
||||
.selection=${this._selection}
|
||||
selectable></umb-tree>
|
||||
</uui-box>
|
||||
</div>
|
||||
<div slot="actions">
|
||||
<uui-button @click=${this._close} look="secondary">Close</uui-button>
|
||||
</div>
|
||||
</umb-body-layout>
|
||||
`;
|
||||
}
|
||||
|
||||
static styles = [
|
||||
UUITextStyles,
|
||||
css`
|
||||
:host {
|
||||
display: block;
|
||||
color: var(--uui-color-text);
|
||||
}
|
||||
|
||||
#main {
|
||||
box-sizing: border-box;
|
||||
padding: var(--uui-size-space-5);
|
||||
height: calc(100vh - 124px);
|
||||
}
|
||||
|
||||
#main uui-button {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
h3,
|
||||
p {
|
||||
text-align: left;
|
||||
}
|
||||
`,
|
||||
];
|
||||
}
|
||||
|
||||
declare global {
|
||||
interface HTMLElementTagNameMap {
|
||||
'umb-partial-view-picker-modal': UmbPartialViewPickerModalElement;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
import { FileSystemTreeItemPresentationModel } from '@umbraco-cms/backoffice/backend-api';
|
||||
|
||||
// TODO: temp until we have a proper stylesheet model
|
||||
export interface PartialViewDetails extends FileSystemTreeItemPresentationModel {
|
||||
content: string;
|
||||
}
|
||||
|
||||
export const PARTIAL_VIEW_ENTITY_TYPE = 'partial-view';
|
||||
export const PARTIAL_VIEW_FOLDER_ENTITY_TYPE = 'partial-view';
|
||||
|
||||
export const PARTIAL_VIEW_REPOSITORY_ALIAS = 'Umb.Repository.PartialViews';
|
||||
|
||||
export const PARTIAL_VIEW_TREE_ALIAS = 'Umb.Tree.PartialViews';
|
||||
|
||||
export const UMB_PARTIAL_VIEW_TREE_STORE_CONTEXT_TOKEN_ALIAS = 'Umb.Store.PartialViews.Tree';
|
||||
export const UMB_PARTIAL_VIEW_STORE_CONTEXT_TOKEN_ALIAS = 'Umb.Store.PartialViews';
|
||||
@@ -0,0 +1,12 @@
|
||||
import { UmbEntityActionBase } from '@umbraco-cms/backoffice/entity-action';
|
||||
import { UmbControllerHostElement } from '@umbraco-cms/backoffice/controller';
|
||||
|
||||
export class UmbCreateEmptyPartialViewAction<T extends { copy(): Promise<void> }> extends UmbEntityActionBase<T> {
|
||||
constructor(host: UmbControllerHostElement, repositoryAlias: string, unique: string) {
|
||||
super(host, repositoryAlias, unique);
|
||||
}
|
||||
|
||||
async execute() {
|
||||
throw new Error('Method not implemented.');
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
import { UmbEntityActionBase } from '@umbraco-cms/backoffice/entity-action';
|
||||
import { UmbControllerHostElement } from '@umbraco-cms/backoffice/controller';
|
||||
|
||||
export class UmbCreateFromSnippetPartialViewAction<T extends { copy(): Promise<void> }> extends UmbEntityActionBase<T> {
|
||||
constructor(host: UmbControllerHostElement, repositoryAlias: string, unique: string) {
|
||||
super(host, repositoryAlias, unique);
|
||||
}
|
||||
|
||||
async execute() {
|
||||
throw new Error('Method not implemented.');
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,60 @@
|
||||
import { PARTIAL_VIEW_ENTITY_TYPE, PARTIAL_VIEW_FOLDER_ENTITY_TYPE, PARTIAL_VIEW_REPOSITORY_ALIAS } from '../config';
|
||||
import { UmbCreateFromSnippetPartialViewAction } from './create/create-from-snippet.action';
|
||||
import { UmbCreateEmptyPartialViewAction } from './create/create-empty.action';
|
||||
import { UmbDeleteEntityAction } from '@umbraco-cms/backoffice/entity-action';
|
||||
import { ManifestEntityAction } from '@umbraco-cms/backoffice/extensions-registry';
|
||||
|
||||
//TODO: this is temporary until we have a proper way of registering actions for folder types in a specific tree
|
||||
|
||||
//Actions for partial view files
|
||||
const partialViewActions: Array<ManifestEntityAction> = [
|
||||
{
|
||||
type: 'entityAction',
|
||||
alias: 'Umb.EntityAction.PartialView.Delete',
|
||||
name: 'Delete PartialView Entity Action',
|
||||
meta: {
|
||||
icon: 'umb:trash',
|
||||
label: 'Delete',
|
||||
api: UmbDeleteEntityAction,
|
||||
repositoryAlias: PARTIAL_VIEW_REPOSITORY_ALIAS,
|
||||
},
|
||||
conditions: {
|
||||
entityTypes: [PARTIAL_VIEW_ENTITY_TYPE],
|
||||
},
|
||||
},
|
||||
];
|
||||
|
||||
//TODO: add create folder action when the generic folder action is implemented
|
||||
//Actions for directories
|
||||
const partialViewFolderActions: Array<ManifestEntityAction> = [
|
||||
{
|
||||
type: 'entityAction',
|
||||
alias: 'Umb.EntityAction.PartialViewFolder.Create.New',
|
||||
name: 'Create PartialView Entity Under Directory Action',
|
||||
meta: {
|
||||
icon: 'umb:article',
|
||||
label: 'New empty partial view',
|
||||
api: UmbCreateEmptyPartialViewAction,
|
||||
repositoryAlias: PARTIAL_VIEW_REPOSITORY_ALIAS,
|
||||
},
|
||||
conditions: {
|
||||
entityTypes: [PARTIAL_VIEW_FOLDER_ENTITY_TYPE],
|
||||
},
|
||||
},
|
||||
{
|
||||
type: 'entityAction',
|
||||
alias: 'Umb.EntityAction.PartialViewFolder.Create.From.Snippet',
|
||||
name: 'Create PartialView Entity From Snippet Under Directory Action',
|
||||
meta: {
|
||||
icon: 'umb:article',
|
||||
label: 'New partial view from snippet...',
|
||||
api: UmbCreateFromSnippetPartialViewAction,
|
||||
repositoryAlias: PARTIAL_VIEW_REPOSITORY_ALIAS,
|
||||
},
|
||||
conditions: {
|
||||
entityTypes: [PARTIAL_VIEW_FOLDER_ENTITY_TYPE],
|
||||
},
|
||||
},
|
||||
];
|
||||
|
||||
export const manifests = [...partialViewActions, ...partialViewFolderActions];
|
||||
@@ -0,0 +1,13 @@
|
||||
import { manifests as repositoryManifests } from './repository/manifests';
|
||||
import { manifests as menuItemManifests } from './menu-item/manifests';
|
||||
import { manifests as treeManifests } from './tree/manifests';
|
||||
import { manifests as entityActionsManifests } from './entity-actions/manifests';
|
||||
import { manifests as workspaceManifests } from './workspace/manifests';
|
||||
|
||||
export const manifests = [
|
||||
...repositoryManifests,
|
||||
...menuItemManifests,
|
||||
...treeManifests,
|
||||
...entityActionsManifests,
|
||||
...workspaceManifests,
|
||||
];
|
||||
@@ -0,0 +1,21 @@
|
||||
import { PARTIAL_VIEW_TREE_ALIAS } from '../config';
|
||||
import type { ManifestTypes } from '@umbraco-cms/backoffice/extensions-registry';
|
||||
|
||||
const menuItem: ManifestTypes = {
|
||||
type: 'menuItem',
|
||||
kind: 'tree',
|
||||
alias: 'Umb.MenuItem.PartialViews',
|
||||
name: 'Partial View Menu Item',
|
||||
weight: 40,
|
||||
meta: {
|
||||
label: 'Partial Views',
|
||||
icon: 'umb:folder',
|
||||
entityType: 'partial-view',
|
||||
treeAlias: PARTIAL_VIEW_TREE_ALIAS,
|
||||
},
|
||||
conditions: {
|
||||
menus: ['Umb.Menu.Templating'],
|
||||
},
|
||||
};
|
||||
|
||||
export const manifests = [menuItem];
|
||||
@@ -0,0 +1,32 @@
|
||||
import { UmbTemplateRepository } from '../repository/partial-views.repository';
|
||||
import { PARTIAL_VIEW_REPOSITORY_ALIAS } from '../config';
|
||||
import { UmbPartialViewsTreeStore } from './partial-views.tree.store';
|
||||
import { UmbPartialViewsStore } from './partial-views.store';
|
||||
import { ManifestRepository, ManifestStore, ManifestTreeStore } from '@umbraco-cms/backoffice/extensions-registry';
|
||||
|
||||
|
||||
const repository: ManifestRepository = {
|
||||
type: 'repository',
|
||||
alias: PARTIAL_VIEW_REPOSITORY_ALIAS,
|
||||
name: 'Partial Views Repository',
|
||||
class: UmbTemplateRepository,
|
||||
};
|
||||
|
||||
export const PARTIAL_VIEW_STORE_ALIAS = 'Umb.Store.PartialViews';
|
||||
export const PARTIAL_VIEW_TREE_STORE_ALIAS = 'Umb.Store.PartialViewsTree';
|
||||
|
||||
const store: ManifestStore = {
|
||||
type: 'store',
|
||||
alias: PARTIAL_VIEW_STORE_ALIAS,
|
||||
name: 'Partial Views Store',
|
||||
class: UmbPartialViewsStore,
|
||||
};
|
||||
|
||||
const treeStore: ManifestTreeStore = {
|
||||
type: 'treeStore',
|
||||
alias: PARTIAL_VIEW_TREE_STORE_ALIAS,
|
||||
name: 'Partial Views Tree Store',
|
||||
class: UmbPartialViewsTreeStore,
|
||||
};
|
||||
|
||||
export const manifests = [repository, store, treeStore];
|
||||
@@ -0,0 +1,153 @@
|
||||
import { UmbPartialViewDetailServerDataSource } from './sources/partial-views.detail.server.data';
|
||||
import { UmbPartialViewsTreeServerDataSource } from './sources/partial-views.tree.server.data';
|
||||
import { UmbPartialViewsStore, UMB_PARTIAL_VIEWS_STORE_CONTEXT_TOKEN } from './partial-views.store';
|
||||
import { UmbPartialViewsTreeStore, UMB_PARTIAL_VIEW_TREE_STORE_CONTEXT_TOKEN } from './partial-views.tree.store';
|
||||
import { UmbControllerHostElement } from '@umbraco-cms/backoffice/controller';
|
||||
import { UmbNotificationContext, UMB_NOTIFICATION_CONTEXT_TOKEN } from '@umbraco-cms/backoffice/notification';
|
||||
import { UmbContextConsumerController } from '@umbraco-cms/backoffice/context-api';
|
||||
import { ProblemDetailsModel } from '@umbraco-cms/backoffice/backend-api';
|
||||
import { UmbDetailRepository, UmbTreeRepository } from '@umbraco-cms/backoffice/repository';
|
||||
import { UmbTreeRootEntityModel } from '@umbraco-cms/backoffice/tree';
|
||||
import { Observable } from 'rxjs';
|
||||
|
||||
export class UmbTemplateRepository implements UmbTreeRepository<any>, UmbDetailRepository<any> {
|
||||
#init;
|
||||
#host: UmbControllerHostElement;
|
||||
|
||||
#treeDataSource: UmbPartialViewsTreeServerDataSource;
|
||||
#detailDataSource: UmbPartialViewDetailServerDataSource;
|
||||
|
||||
#treeStore?: UmbPartialViewsTreeStore;
|
||||
#store?: UmbPartialViewsStore;
|
||||
|
||||
#notificationContext?: UmbNotificationContext;
|
||||
|
||||
constructor(host: UmbControllerHostElement) {
|
||||
this.#host = host;
|
||||
|
||||
this.#treeDataSource = new UmbPartialViewsTreeServerDataSource(this.#host);
|
||||
this.#detailDataSource = new UmbPartialViewDetailServerDataSource(this.#host);
|
||||
|
||||
this.#init = Promise.all([
|
||||
new UmbContextConsumerController(this.#host, UMB_PARTIAL_VIEW_TREE_STORE_CONTEXT_TOKEN, (instance) => {
|
||||
this.#treeStore = instance;
|
||||
}),
|
||||
|
||||
new UmbContextConsumerController(this.#host, UMB_PARTIAL_VIEWS_STORE_CONTEXT_TOKEN, (instance) => {
|
||||
this.#store = instance;
|
||||
}),
|
||||
|
||||
new UmbContextConsumerController(this.#host, UMB_NOTIFICATION_CONTEXT_TOKEN, (instance) => {
|
||||
this.#notificationContext = instance;
|
||||
}),
|
||||
]);
|
||||
}
|
||||
|
||||
requestTreeRoot(): Promise<{ data?: UmbTreeRootEntityModel | undefined; error?: ProblemDetailsModel | undefined }> {
|
||||
throw new Error('Method not implemented.');
|
||||
}
|
||||
|
||||
requestItemsLegacy?:
|
||||
| ((
|
||||
uniques: string[]
|
||||
) => Promise<{
|
||||
data?: any[] | undefined;
|
||||
error?: ProblemDetailsModel | undefined;
|
||||
asObservable?: (() => Observable<any[]>) | undefined;
|
||||
}>)
|
||||
| undefined;
|
||||
|
||||
itemsLegacy?: ((uniques: string[]) => Promise<Observable<any[]>>) | undefined;
|
||||
|
||||
byId(id: string): Promise<Observable<any>> {
|
||||
throw new Error('Method not implemented.');
|
||||
}
|
||||
|
||||
requestById(id: string): Promise<{ data?: any; error?: ProblemDetailsModel | undefined }> {
|
||||
throw new Error('Method not implemented.');
|
||||
}
|
||||
|
||||
// TREE:
|
||||
|
||||
async requestRootTreeItems() {
|
||||
await this.#init;
|
||||
|
||||
const { data, error } = await this.#treeDataSource.getRootItems();
|
||||
|
||||
if (data) {
|
||||
this.#treeStore?.appendItems(data.items);
|
||||
}
|
||||
|
||||
return { data, error, asObservable: () => this.#treeStore!.rootItems };
|
||||
}
|
||||
|
||||
async requestTreeItemsOf(path: string | null) {
|
||||
if (!path) throw new Error('Cannot request tree item with missing path');
|
||||
|
||||
await this.#init;
|
||||
|
||||
const { data, error } = await this.#treeDataSource.getChildrenOf({ path });
|
||||
|
||||
if (data) {
|
||||
this.#treeStore!.appendItems(data.items);
|
||||
}
|
||||
|
||||
return { data, error, asObservable: () => this.#treeStore!.childrenOf(path) };
|
||||
}
|
||||
|
||||
async requestTreeItems(keys: Array<string>) {
|
||||
await this.#init;
|
||||
|
||||
if (!keys) {
|
||||
const error: ProblemDetailsModel = { title: 'Keys are missing' };
|
||||
return { data: undefined, error };
|
||||
}
|
||||
|
||||
const { data, error } = await this.#treeDataSource.getItem(keys);
|
||||
|
||||
return { data, error, asObservable: () => this.#treeStore!.items(keys) };
|
||||
}
|
||||
|
||||
async rootTreeItems() {
|
||||
await this.#init;
|
||||
return this.#treeStore!.rootItems;
|
||||
}
|
||||
|
||||
async treeItemsOf(parentPath: string | null) {
|
||||
if (!parentPath) throw new Error('Parent Path is missing');
|
||||
await this.#init;
|
||||
return this.#treeStore!.childrenOf(parentPath);
|
||||
}
|
||||
|
||||
async treeItems(paths: Array<string>) {
|
||||
if (!paths) throw new Error('Paths are missing');
|
||||
await this.#init;
|
||||
return this.#treeStore!.items(paths);
|
||||
}
|
||||
|
||||
// DETAILS
|
||||
async requestByKey(path: string) {
|
||||
if (!path) throw new Error('Path is missing');
|
||||
await this.#init;
|
||||
const { data, error } = await this.#detailDataSource.get(path);
|
||||
return { data, error };
|
||||
}
|
||||
|
||||
// DETAILS:
|
||||
|
||||
async createScaffold(parentKey: string | null) {
|
||||
return Promise.reject(new Error('Not implemented'));
|
||||
}
|
||||
|
||||
async create(patrial: any) {
|
||||
return Promise.reject(new Error('Not implemented'));
|
||||
}
|
||||
|
||||
async save(patrial: any) {
|
||||
return Promise.reject(new Error('Not implemented'));
|
||||
}
|
||||
|
||||
async delete(key: string) {
|
||||
return Promise.reject(new Error('Not implemented'));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,49 @@
|
||||
import { UmbContextToken } from '@umbraco-cms/backoffice/context-api';
|
||||
import { UmbArrayState } from '@umbraco-cms/backoffice/observable-api';
|
||||
import { UmbStoreBase } from '@umbraco-cms/backoffice/store';
|
||||
import type { TemplateResponseModel } from '@umbraco-cms/backoffice/backend-api';
|
||||
import { UmbControllerHostElement } from '@umbraco-cms/backoffice/controller';
|
||||
import { UMB_PARTIAL_VIEW_STORE_CONTEXT_TOKEN_ALIAS } from '../config';
|
||||
|
||||
/**
|
||||
* @export
|
||||
* @class UmbPartialViewsStore
|
||||
* @extends {UmbStoreBase}
|
||||
* @description - Data Store for partial views
|
||||
*/
|
||||
export class UmbPartialViewsStore extends UmbStoreBase {
|
||||
/**
|
||||
* Creates an instance of UmbPartialViewsStore.
|
||||
* @param {UmbControllerHostInterface} host
|
||||
* @memberof UmbPartialViewsStore
|
||||
*/
|
||||
constructor(host: UmbControllerHostElement) {
|
||||
super(
|
||||
host,
|
||||
UMB_PARTIAL_VIEWS_STORE_CONTEXT_TOKEN.toString(),
|
||||
new UmbArrayState<TemplateResponseModel>([], (x) => x.id)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Append a partial view to the store
|
||||
* @param {Template} template
|
||||
* @memberof UmbPartialViewsStore
|
||||
*/
|
||||
append(template: TemplateResponseModel) {
|
||||
this._data.append([template]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes partial views in the store with the given uniques
|
||||
* @param {string[]} uniques
|
||||
* @memberof UmbPartialViewsStore
|
||||
*/
|
||||
remove(uniques: string[]) {
|
||||
this._data.remove(uniques);
|
||||
}
|
||||
}
|
||||
|
||||
export const UMB_PARTIAL_VIEWS_STORE_CONTEXT_TOKEN = new UmbContextToken<UmbPartialViewsStore>(
|
||||
UMB_PARTIAL_VIEW_STORE_CONTEXT_TOKEN_ALIAS
|
||||
);
|
||||
@@ -0,0 +1,26 @@
|
||||
import { UMB_PARTIAL_VIEW_TREE_STORE_CONTEXT_TOKEN_ALIAS } from '../config';
|
||||
import { UmbContextToken } from '@umbraco-cms/backoffice/context-api';
|
||||
import { UmbFileSystemTreeStore } from '@umbraco-cms/backoffice/store';
|
||||
import type { UmbControllerHostElement } from '@umbraco-cms/backoffice/controller';
|
||||
|
||||
export const UMB_PARTIAL_VIEW_TREE_STORE_CONTEXT_TOKEN = new UmbContextToken<UmbPartialViewsTreeStore>(
|
||||
UMB_PARTIAL_VIEW_TREE_STORE_CONTEXT_TOKEN_ALIAS
|
||||
);
|
||||
|
||||
/**
|
||||
* Tree Store for partial views
|
||||
*
|
||||
* @export
|
||||
* @class UmbPartialViewsTreeStore
|
||||
* @extends {UmbEntityTreeStore}
|
||||
*/
|
||||
export class UmbPartialViewsTreeStore extends UmbFileSystemTreeStore {
|
||||
/**
|
||||
* Creates an instance of UmbPartialViewsTreeStore.
|
||||
* @param {UmbControllerHostInterface} host
|
||||
* @memberof UmbPartialViewsTreeStore
|
||||
*/
|
||||
constructor(host: UmbControllerHostElement) {
|
||||
super(host, UMB_PARTIAL_VIEW_TREE_STORE_CONTEXT_TOKEN.toString());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
import {
|
||||
FileSystemTreeItemPresentationModel,
|
||||
PagedFileSystemTreeItemPresentationModel,
|
||||
} from '@umbraco-cms/backoffice/backend-api';
|
||||
import type { DataSourceResponse } from '@umbraco-cms/backoffice/repository';
|
||||
|
||||
export interface PartialViewsTreeDataSource {
|
||||
getRootItems(): Promise<DataSourceResponse<PagedFileSystemTreeItemPresentationModel>>;
|
||||
getChildrenOf({
|
||||
path,
|
||||
skip,
|
||||
take,
|
||||
}: {
|
||||
path?: string | undefined;
|
||||
skip?: number | undefined;
|
||||
take?: number | undefined;
|
||||
}): Promise<DataSourceResponse<PagedFileSystemTreeItemPresentationModel>>;
|
||||
getItem(ids: Array<string>): Promise<DataSourceResponse<FileSystemTreeItemPresentationModel[]>>;
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
import { PartialViewDetails } from '../../config';
|
||||
import type { UmbControllerHostElement } from '@umbraco-cms/backoffice/controller';
|
||||
import { DataSourceResponse, UmbDataSource } from '@umbraco-cms/backoffice/repository';
|
||||
|
||||
//TODO Pass proper models
|
||||
export class UmbPartialViewDetailServerDataSource
|
||||
implements UmbDataSource<PartialViewDetails, PartialViewDetails, PartialViewDetails, PartialViewDetails>
|
||||
{
|
||||
#host: UmbControllerHostElement;
|
||||
|
||||
/**
|
||||
* Creates an instance of UmbPartialViewDetailServerDataSource.
|
||||
* @param {UmbControllerHostInterface} host
|
||||
* @memberof UmbPartialViewDetailServerDataSource
|
||||
*/
|
||||
constructor(host: UmbControllerHostElement) {
|
||||
this.#host = host;
|
||||
}
|
||||
|
||||
createScaffold(parentKey: string | null): Promise<DataSourceResponse<PartialViewDetails>> {
|
||||
throw new Error('Method not implemented.');
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetches a Stylesheet with the given path from the server
|
||||
* @param {string} path
|
||||
* @return {*}
|
||||
* @memberof UmbStylesheetServerDataSource
|
||||
*/
|
||||
async get(path: string) {
|
||||
if (!path) throw new Error('Path is missing');
|
||||
console.log('GET PATRIAL WITH PATH', path);
|
||||
return { data: undefined, error: undefined };
|
||||
}
|
||||
|
||||
insert(data: any): Promise<DataSourceResponse<PartialViewDetails>> {
|
||||
throw new Error('Method not implemented.');
|
||||
}
|
||||
update(unique: string, data: PartialViewDetails): Promise<DataSourceResponse<PartialViewDetails>> {
|
||||
throw new Error('Method not implemented.');
|
||||
}
|
||||
delete(unique: string): Promise<DataSourceResponse> {
|
||||
throw new Error('Method not implemented.');
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
import { PartialViewsTreeDataSource } from '.';
|
||||
import { PartialViewResource, ProblemDetailsModel } from '@umbraco-cms/backoffice/backend-api';
|
||||
import { UmbControllerHostElement } from '@umbraco-cms/backoffice/controller';
|
||||
import { tryExecuteAndNotify } from '@umbraco-cms/backoffice/resources';
|
||||
|
||||
export class UmbPartialViewsTreeServerDataSource implements PartialViewsTreeDataSource {
|
||||
#host: UmbControllerHostElement;
|
||||
|
||||
constructor(host: UmbControllerHostElement) {
|
||||
this.#host = host;
|
||||
}
|
||||
|
||||
async getRootItems() {
|
||||
return tryExecuteAndNotify(this.#host, PartialViewResource.getTreePartialViewRoot({}));
|
||||
}
|
||||
|
||||
async getChildrenOf({
|
||||
path,
|
||||
skip,
|
||||
take,
|
||||
}: {
|
||||
path?: string | undefined;
|
||||
skip?: number | undefined;
|
||||
take?: number | undefined;
|
||||
}) {
|
||||
if (!path) {
|
||||
const error: ProblemDetailsModel = { title: 'Path is missing' };
|
||||
return error ;
|
||||
}
|
||||
|
||||
return tryExecuteAndNotify(
|
||||
this.#host,
|
||||
PartialViewResource.getTreePartialViewChildren({
|
||||
path,
|
||||
skip,
|
||||
take,
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
async getItem(id: Array<string>) {
|
||||
if (!id) {
|
||||
const error: ProblemDetailsModel = { title: 'Paths are missing' };
|
||||
return error ;
|
||||
}
|
||||
|
||||
return tryExecuteAndNotify(
|
||||
this.#host,
|
||||
PartialViewResource.getPartialViewItem({
|
||||
id,
|
||||
})
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
import { PARTIAL_VIEW_ENTITY_TYPE, PARTIAL_VIEW_REPOSITORY_ALIAS, PARTIAL_VIEW_TREE_ALIAS } from '../config';
|
||||
import type { ManifestTree, ManifestTreeItem } from '@umbraco-cms/backoffice/extensions-registry';
|
||||
|
||||
const tree: ManifestTree = {
|
||||
type: 'tree',
|
||||
alias: PARTIAL_VIEW_TREE_ALIAS,
|
||||
name: 'Partial Views Tree',
|
||||
meta: {
|
||||
repositoryAlias: PARTIAL_VIEW_REPOSITORY_ALIAS,
|
||||
},
|
||||
};
|
||||
|
||||
const treeItem: ManifestTreeItem = {
|
||||
type: 'treeItem',
|
||||
kind: 'fileSystem',
|
||||
alias: 'Umb.TreeItem.PartialViews',
|
||||
name: 'Partial Views Tree Item',
|
||||
conditions: {
|
||||
entityTypes: [PARTIAL_VIEW_ENTITY_TYPE],
|
||||
},
|
||||
};
|
||||
|
||||
export const manifests = [tree, treeItem];
|
||||
@@ -0,0 +1,32 @@
|
||||
import { UmbSaveWorkspaceAction } from '@umbraco-cms/backoffice/workspace';
|
||||
import type { ManifestWorkspace, ManifestWorkspaceAction } from '@umbraco-cms/backoffice/extensions-registry';
|
||||
|
||||
const workspace: ManifestWorkspace = {
|
||||
type: 'workspace',
|
||||
alias: 'Umb.Workspace.PartialView',
|
||||
name: 'Partial View Workspace',
|
||||
loader: () => import('./partial-views-workspace.element'),
|
||||
meta: {
|
||||
entityType: 'partial-view',
|
||||
},
|
||||
};
|
||||
|
||||
const workspaceActions: Array<ManifestWorkspaceAction> = [
|
||||
{
|
||||
type: 'workspaceAction',
|
||||
alias: 'Umb.WorkspaceAction.PartialView.Save',
|
||||
name: 'Save Partial View',
|
||||
weight: 70,
|
||||
meta: {
|
||||
look: 'primary',
|
||||
color: 'positive',
|
||||
label: 'Save',
|
||||
api: UmbSaveWorkspaceAction,
|
||||
},
|
||||
conditions: {
|
||||
workspaces: ['Umb.Workspace.PartialView'],
|
||||
},
|
||||
},
|
||||
];
|
||||
|
||||
export const manifests = [workspace, ...workspaceActions];
|
||||
@@ -0,0 +1,114 @@
|
||||
import { UUITextStyles } from '@umbraco-ui/uui-css/lib';
|
||||
import { css, html } from 'lit';
|
||||
import { customElement, query, state } from 'lit/decorators.js';
|
||||
import { UUIInputElement } from '@umbraco-ui/uui';
|
||||
import { UmbCodeEditorElement } from '../../../core/components/code-editor';
|
||||
import { UmbPartialViewsWorkspaceContext } from './partial-views-workspace.context';
|
||||
import { UmbLitElement } from '@umbraco-cms/internal/lit-element';
|
||||
|
||||
@customElement('umb-partial-views-workspace-edit')
|
||||
export class UmbPartialViewsWorkspaceEditElement extends UmbLitElement {
|
||||
@state()
|
||||
private _name?: string | null = '';
|
||||
|
||||
@state()
|
||||
private _content?: string | null = '';
|
||||
|
||||
@query('umb-code-editor')
|
||||
private _codeEditor?: UmbCodeEditorElement;
|
||||
|
||||
#partialViewsWorkspaceContext?: UmbPartialViewsWorkspaceContext;
|
||||
#isNew = false;
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
|
||||
this.consumeContext('umbWorkspaceContext', (workspaceContext: UmbPartialViewsWorkspaceContext) => {
|
||||
this.#partialViewsWorkspaceContext = workspaceContext;
|
||||
this.observe(this.#partialViewsWorkspaceContext.name, (name) => {
|
||||
this._name = name;
|
||||
});
|
||||
|
||||
this.observe(this.#partialViewsWorkspaceContext.content, (content) => {
|
||||
this._content = content;
|
||||
});
|
||||
|
||||
// this.observe(this.#partialViewsWorkspaceContext.isNew, (isNew) => {
|
||||
// this.#isNew = !!isNew;
|
||||
// console.log(this.#isNew);
|
||||
// });
|
||||
});
|
||||
}
|
||||
|
||||
// TODO: temp code for testing create and save
|
||||
#onNameInput(event: Event) {
|
||||
const target = event.target as UUIInputElement;
|
||||
const value = target.value as string;
|
||||
this.#partialViewsWorkspaceContext?.setName(value);
|
||||
}
|
||||
|
||||
//TODO - debounce that
|
||||
#onCodeEditorInput(event: Event) {
|
||||
const target = event.target as UmbCodeEditorElement;
|
||||
const value = target.code as string;
|
||||
this.#partialViewsWorkspaceContext?.setContent(value);
|
||||
}
|
||||
|
||||
#insertCode(event: Event) {
|
||||
const target = event.target as UUIInputElement;
|
||||
const value = target.value as string;
|
||||
|
||||
this._codeEditor?.insert(`My hovercraft is full of eels`);
|
||||
}
|
||||
|
||||
render() {
|
||||
// TODO: add correct UI elements
|
||||
return html`<umb-body-layout alias="Umb.Workspace.Template">
|
||||
<uui-input slot="header" .value=${this._name} @input=${this.#onNameInput}></uui-input>
|
||||
<uui-box>
|
||||
<uui-button color="danger" look="primary" slot="header" @click=${this.#insertCode}
|
||||
>Insert "My hovercraft is full of eels"</uui-button
|
||||
>
|
||||
|
||||
<umb-code-editor
|
||||
language="razor"
|
||||
id="content"
|
||||
.code=${this._content ?? ''}
|
||||
@input=${this.#onCodeEditorInput}></umb-code-editor>
|
||||
</uui-box>
|
||||
</umb-body-layout>`;
|
||||
}
|
||||
|
||||
static styles = [
|
||||
UUITextStyles,
|
||||
css`
|
||||
:host {
|
||||
display: block;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
umb-code-editor {
|
||||
--editor-height: calc(100vh - 300px);
|
||||
}
|
||||
|
||||
uui-box {
|
||||
margin: 1em;
|
||||
--uui-box-default-padding: 0;
|
||||
}
|
||||
|
||||
uui-input {
|
||||
width: 100%;
|
||||
margin: 1em;
|
||||
}
|
||||
`,
|
||||
];
|
||||
}
|
||||
|
||||
export default UmbPartialViewsWorkspaceEditElement;
|
||||
|
||||
declare global {
|
||||
interface HTMLElementTagNameMap {
|
||||
'umb-partial-views-workspace-edit': UmbPartialViewsWorkspaceEditElement;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,55 @@
|
||||
import { UmbTemplateRepository } from '../repository/partial-views.repository';
|
||||
import { createObservablePart, UmbDeepState } from '@umbraco-cms/backoffice/observable-api';
|
||||
import { TemplateResponseModel } from '@umbraco-cms/backoffice/backend-api';
|
||||
import { UmbControllerHostElement } from '@umbraco-cms/backoffice/controller';
|
||||
import { UmbWorkspaceContext } from '@umbraco-cms/backoffice/workspace';
|
||||
|
||||
export class UmbPartialViewsWorkspaceContext extends UmbWorkspaceContext<UmbTemplateRepository, TemplateResponseModel> {
|
||||
getEntityId(): string | undefined {
|
||||
throw new Error('Method not implemented.');
|
||||
}
|
||||
getEntityType(): string {
|
||||
throw new Error('Method not implemented.');
|
||||
}
|
||||
save(): Promise<void> {
|
||||
throw new Error('Method not implemented.');
|
||||
}
|
||||
destroy(): void {
|
||||
throw new Error('Method not implemented.');
|
||||
}
|
||||
#data = new UmbDeepState<TemplateResponseModel | undefined>(undefined);
|
||||
data = this.#data.asObservable();
|
||||
name = createObservablePart(this.#data, (data) => data?.name);
|
||||
content = createObservablePart(this.#data, (data) => data?.content);
|
||||
|
||||
constructor(host: UmbControllerHostElement) {
|
||||
super(host, new UmbTemplateRepository(host));
|
||||
}
|
||||
|
||||
getData() {
|
||||
return this.#data.getValue();
|
||||
}
|
||||
|
||||
setName(value: string) {
|
||||
this.#data.next({ ...this.#data.value, $type: this.#data.value?.$type || '', name: value });
|
||||
}
|
||||
|
||||
setContent(value: string) {
|
||||
this.#data.next({ ...this.#data.value, $type: this.#data.value?.$type || '', content: value });
|
||||
}
|
||||
|
||||
async load(entityKey: string) {
|
||||
const { data } = await this.repository.requestByKey(entityKey);
|
||||
if (data) {
|
||||
this.setIsNew(false);
|
||||
this.#data.next(data);
|
||||
}
|
||||
}
|
||||
|
||||
async createScaffold(parentKey: string | null) {
|
||||
const { data } = await this.repository.createScaffold(parentKey);
|
||||
if (!data) return;
|
||||
this.setIsNew(true);
|
||||
this.#data.next(data);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,57 @@
|
||||
import { UUITextStyles } from '@umbraco-ui/uui-css';
|
||||
import { css, html } from 'lit';
|
||||
import { customElement, state } from 'lit/decorators.js';
|
||||
import { UmbPartialViewsWorkspaceContext } from './partial-views-workspace.context';
|
||||
import { UmbRouterSlotInitEvent } from '@umbraco-cms/internal/router';
|
||||
import { UmbLitElement } from '@umbraco-cms/internal/lit-element';
|
||||
|
||||
import './partial-views-workspace-edit.element';
|
||||
import { UmbRoute, IRoutingInfo, PageComponent } from '@umbraco-cms/backoffice/router';
|
||||
|
||||
@customElement('umb-partial-views-workspace')
|
||||
export class UmbPartialViewsWorkspaceElement extends UmbLitElement {
|
||||
#partialViewsWorkspaceContext = new UmbPartialViewsWorkspaceContext(this);
|
||||
|
||||
#routerPath? = '';
|
||||
|
||||
#element = document.createElement('umb-partial-views-workspace-edit');
|
||||
#key = '';
|
||||
|
||||
@state()
|
||||
_routes: UmbRoute[] = [
|
||||
{
|
||||
path: 'create/:parentKey',
|
||||
component: () => this.#element,
|
||||
setup: async (component: PageComponent, info: IRoutingInfo) => {
|
||||
const parentKey = info.match.params.parentKey;
|
||||
this.#partialViewsWorkspaceContext.createScaffold(parentKey);
|
||||
},
|
||||
},
|
||||
{
|
||||
path: 'edit/:key',
|
||||
component: () => this.#element,
|
||||
setup: (component: PageComponent, info: IRoutingInfo) => {
|
||||
const key = info.match.params.key;
|
||||
this.#partialViewsWorkspaceContext.load(key);
|
||||
},
|
||||
},
|
||||
];
|
||||
|
||||
render() {
|
||||
return html`<umb-router-slot
|
||||
.routes=${this._routes}
|
||||
@init=${(event: UmbRouterSlotInitEvent) => {
|
||||
this.#routerPath = event.target.absoluteRouterPath;
|
||||
}}></umb-router-slot>`;
|
||||
}
|
||||
|
||||
static styles = [UUITextStyles, css``];
|
||||
}
|
||||
|
||||
export default UmbPartialViewsWorkspaceElement;
|
||||
|
||||
declare global {
|
||||
interface HTMLElementTagNameMap {
|
||||
'umb-partial-views-workspace': UmbPartialViewsWorkspaceElement;
|
||||
}
|
||||
}
|
||||
@@ -9,7 +9,7 @@ export class UmbCreateEntityAction<T extends { copy(): Promise<void> }> extends
|
||||
// TODO: can we make this a generic create action
|
||||
async execute() {
|
||||
// TODO: get entity type from repository?
|
||||
const url = `section/settings/template/create/${this.unique || 'root'}`;
|
||||
const url = `section/settings/workspace/template/create/${this.unique || 'root'}`;
|
||||
// TODO: how do we handle this with a href?
|
||||
history.pushState(null, '', url);
|
||||
}
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
import { TEMPLATE_REPOSITORY_ALIAS } from '../repository/manifests';
|
||||
import { UmbSaveWorkspaceAction } from '@umbraco-cms/backoffice/workspace';
|
||||
import type {
|
||||
ManifestWorkspace,
|
||||
|
||||
@@ -0,0 +1,178 @@
|
||||
import { UUITextStyles } from '@umbraco-ui/uui-css/lib';
|
||||
import { css, html } from 'lit';
|
||||
import { customElement, query, state } from 'lit/decorators.js';
|
||||
import { UUIInputElement } from '@umbraco-ui/uui';
|
||||
import { UmbTemplatingInsertMenuElement } from '../../components/insert-menu/templating-insert-menu.element';
|
||||
import { UMB_MODAL_TEMPLATING_INSERT_SECTION_MODAL } from '../../modals/insert-section-modal/insert-section-modal.element';
|
||||
import { UmbTemplateWorkspaceContext } from './template-workspace.context';
|
||||
import { UmbLitElement } from '@umbraco-cms/internal/lit-element';
|
||||
import { UMB_MODAL_CONTEXT_TOKEN, UmbModalContext } from '@umbraco-cms/backoffice/modal';
|
||||
import { UmbCodeEditorElement } from '@umbraco-cms/backoffice/core/components';
|
||||
|
||||
@customElement('umb-template-workspace-edit')
|
||||
export class UmbTemplateWorkspaceEditElement extends UmbLitElement {
|
||||
@state()
|
||||
private _name?: string | null = '';
|
||||
|
||||
@state()
|
||||
private _content?: string | null = '';
|
||||
|
||||
@query('umb-code-editor')
|
||||
private _codeEditor?: UmbCodeEditorElement;
|
||||
|
||||
#templateWorkspaceContext?: UmbTemplateWorkspaceContext;
|
||||
#isNew = false;
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
|
||||
this.consumeContext(UMB_MODAL_CONTEXT_TOKEN, (instance) => {
|
||||
this._modalContext = instance;
|
||||
});
|
||||
|
||||
this.consumeContext('UmbEntityWorkspaceContext', (workspaceContext: UmbTemplateWorkspaceContext) => {
|
||||
this.#templateWorkspaceContext = workspaceContext;
|
||||
this.observe(this.#templateWorkspaceContext.name, (name) => {
|
||||
this._name = name;
|
||||
});
|
||||
|
||||
this.observe(this.#templateWorkspaceContext.content, (content) => {
|
||||
this._content = content;
|
||||
});
|
||||
|
||||
this.observe(this.#templateWorkspaceContext.isNew, (isNew) => {
|
||||
this.#isNew = !!isNew;
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
// TODO: temp code for testing create and save
|
||||
#onNameInput(event: Event) {
|
||||
const target = event.target as UUIInputElement;
|
||||
const value = target.value as string;
|
||||
this.#templateWorkspaceContext?.setName(value);
|
||||
}
|
||||
|
||||
//TODO - debounce that
|
||||
#onCodeEditorInput(event: Event) {
|
||||
const target = event.target as UmbCodeEditorElement;
|
||||
const value = target.code as string;
|
||||
this.#templateWorkspaceContext?.setContent(value);
|
||||
}
|
||||
|
||||
#insertCode(event: Event) {
|
||||
const target = event.target as UmbTemplatingInsertMenuElement;
|
||||
const value = target.value as string;
|
||||
|
||||
this._codeEditor?.insert(value);
|
||||
}
|
||||
|
||||
private _modalContext?: UmbModalContext;
|
||||
|
||||
#openInsertSectionModal() {
|
||||
const sectionModal = this._modalContext?.open(UMB_MODAL_TEMPLATING_INSERT_SECTION_MODAL);
|
||||
sectionModal?.onSubmit().then((insertSectionModalResult) => {
|
||||
console.log(insertSectionModalResult);
|
||||
});
|
||||
}
|
||||
|
||||
render() {
|
||||
// TODO: add correct UI elements
|
||||
return html`<umb-body-layout alias="Umb.Workspace.Template">
|
||||
<uui-input slot="header" .value=${this._name} @input=${this.#onNameInput}></uui-input>
|
||||
<uui-box>
|
||||
<div slot="header" id="code-editor-menu-container">
|
||||
<uui-button-group>
|
||||
<uui-button look="secondary" id="master-template-button" label="Change Master template"
|
||||
>Master template: something</uui-button
|
||||
>
|
||||
<uui-button look="secondary" id="save-button" label="Remove master template" compact
|
||||
><uui-icon name="umb:delete"></uui-icon
|
||||
></uui-button>
|
||||
</uui-button-group>
|
||||
<umb-templating-insert-menu @insert=${this.#insertCode}></umb-templating-insert-menu>
|
||||
<uui-button look="secondary" id="query-builder-button" label="Query builder">
|
||||
<uui-icon name="umb:wand"></uui-icon>Query builder
|
||||
</uui-button>
|
||||
|
||||
<uui-button
|
||||
look="secondary"
|
||||
id="sections-button"
|
||||
label="Query builder"
|
||||
@click=${this.#openInsertSectionModal}>
|
||||
<uui-icon name="umb:indent"></uui-icon>Sections
|
||||
</uui-button>
|
||||
</div>
|
||||
|
||||
<umb-code-editor
|
||||
language="razor"
|
||||
id="content"
|
||||
.code=${this._content ?? ''}
|
||||
@input=${this.#onCodeEditorInput}></umb-code-editor>
|
||||
</uui-box>
|
||||
</umb-body-layout>`;
|
||||
}
|
||||
|
||||
static styles = [
|
||||
UUITextStyles,
|
||||
css`
|
||||
:host {
|
||||
display: block;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
umb-code-editor {
|
||||
--editor-height: calc(100vh - 300px);
|
||||
}
|
||||
|
||||
uui-box {
|
||||
margin: 1em;
|
||||
--uui-box-default-padding: 0;
|
||||
}
|
||||
|
||||
uui-input {
|
||||
width: 100%;
|
||||
margin: 1em;
|
||||
}
|
||||
|
||||
#code-editor-menu-container uui-icon {
|
||||
margin-right: var(--uui-size-space-3);
|
||||
}
|
||||
|
||||
#insert-menu {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
margin-top: var(--uui-size-space-3);
|
||||
background-color: var(--uui-color-surface);
|
||||
box-shadow: var(--uui-shadow-depth-3);
|
||||
min-width: calc(100% + var(--uui-size-8, 24px));
|
||||
}
|
||||
|
||||
#insert-menu > li,
|
||||
ul {
|
||||
padding: 0;
|
||||
width: 100%;
|
||||
list-style: none;
|
||||
}
|
||||
|
||||
.insert-menu-item {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
#code-editor-menu-container {
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
gap: var(--uui-size-space-3);
|
||||
}
|
||||
`,
|
||||
];
|
||||
}
|
||||
|
||||
export default UmbTemplateWorkspaceEditElement;
|
||||
|
||||
declare global {
|
||||
interface HTMLElementTagNameMap {
|
||||
'umb-template-workspace-edit': UmbTemplateWorkspaceEditElement;
|
||||
}
|
||||
}
|
||||
@@ -1,83 +1,58 @@
|
||||
import { UUITextStyles } from '@umbraco-ui/uui-css/lib';
|
||||
import { UUITextStyles } from '@umbraco-ui/uui-css';
|
||||
import { css, html } from 'lit';
|
||||
import { customElement, query, state } from 'lit/decorators.js';
|
||||
import { UUIInputElement } from '@umbraco-ui/uui';
|
||||
import type { UmbCodeEditorElement } from '../../../core/components/code-editor/code-editor.element';
|
||||
import { customElement, state } from 'lit/decorators.js';
|
||||
import { UmbTemplateWorkspaceContext } from './template-workspace.context';
|
||||
import { UmbRouterSlotInitEvent } from '@umbraco-cms/internal/router';
|
||||
import type { IRoutingInfo, PageComponent, UmbRoute } from '@umbraco-cms/backoffice/router';
|
||||
import { UmbLitElement } from '@umbraco-cms/internal/lit-element';
|
||||
|
||||
import './template-workspace-edit.element';
|
||||
|
||||
@customElement('umb-template-workspace')
|
||||
export class UmbTemplateWorkspaceElement extends UmbLitElement {
|
||||
public load(entityId: string) {
|
||||
this.#templateWorkspaceContext.load(entityId);
|
||||
}
|
||||
|
||||
public create(parentId: string | null) {
|
||||
this.#isNew = true;
|
||||
this.#templateWorkspaceContext.createScaffold(parentId);
|
||||
}
|
||||
|
||||
@state()
|
||||
private _name?: string | null = '';
|
||||
|
||||
@state()
|
||||
private _content?: string | null = '';
|
||||
|
||||
@query('umb-code-editor')
|
||||
private _codeEditor?: UmbCodeEditorElement;
|
||||
|
||||
#templateWorkspaceContext = new UmbTemplateWorkspaceContext(this);
|
||||
#isNew = false;
|
||||
|
||||
async connectedCallback() {
|
||||
super.connectedCallback();
|
||||
#routerPath? = '';
|
||||
|
||||
this.observe(this.#templateWorkspaceContext.name, (name) => {
|
||||
this._name = name;
|
||||
});
|
||||
#element = document.createElement('umb-template-workspace-edit');
|
||||
#key = '';
|
||||
|
||||
this.observe(this.#templateWorkspaceContext.content, (content) => {
|
||||
this._content = content;
|
||||
});
|
||||
}
|
||||
|
||||
// TODO: temp code for testing create and save
|
||||
#onNameInput(event: Event) {
|
||||
const target = event.target as UUIInputElement;
|
||||
const value = target.value as string;
|
||||
this.#templateWorkspaceContext.setName(value);
|
||||
}
|
||||
|
||||
//TODO - debounce that
|
||||
#onCodeEditorInput(event: Event) {
|
||||
const target = event.target as UmbCodeEditorElement;
|
||||
const value = target.code as string;
|
||||
this.#templateWorkspaceContext.setContent(value);
|
||||
}
|
||||
|
||||
#insertCode(event: Event) {
|
||||
const target = event.target as UUIInputElement;
|
||||
const value = target.value as string;
|
||||
|
||||
this._codeEditor?.insert(`My hovercraft is full of eels`);
|
||||
}
|
||||
@state()
|
||||
_routes: UmbRoute[] = [
|
||||
{
|
||||
path: 'create/:parentKey',
|
||||
component: () => this.#element,
|
||||
setup: (component: PageComponent, info: IRoutingInfo) => {
|
||||
const parentKey = info.match.params.parentKey;
|
||||
this.#templateWorkspaceContext.createScaffold(parentKey);
|
||||
},
|
||||
},
|
||||
{
|
||||
path: 'edit/:key',
|
||||
component: () => this.#element,
|
||||
setup: (component: PageComponent, info: IRoutingInfo): void => {
|
||||
const key = info.match.params.key;
|
||||
this.#templateWorkspaceContext.load(key);
|
||||
},
|
||||
},
|
||||
];
|
||||
|
||||
render() {
|
||||
// TODO: add correct UI elements
|
||||
return html`<umb-workspace-editor alias="Umb.Workspace.Template">
|
||||
<uui-input slot="header" .value=${this._name} @input=${this.#onNameInput}></uui-input>
|
||||
<uui-box>
|
||||
<uui-button color="danger" look="primary" slot="header" @click=${this.#insertCode}
|
||||
>Insert "My hovercraft is full of eels"</uui-button
|
||||
>
|
||||
|
||||
<umb-code-editor
|
||||
language="razor"
|
||||
id="content"
|
||||
.code=${this._content ?? ''}
|
||||
@input=${this.#onCodeEditorInput}></umb-code-editor>
|
||||
</uui-box>
|
||||
</umb-workspace-editor>`;
|
||||
return html`<umb-router-slot
|
||||
.routes=${this._routes}
|
||||
@init=${(event: UmbRouterSlotInitEvent) => {
|
||||
this.#routerPath = event.target.absoluteRouterPath;
|
||||
}}></umb-router-slot>`;
|
||||
}
|
||||
|
||||
static styles = [
|
||||
|
||||
@@ -3,3 +3,56 @@ export const urlFriendlyPathFromServerFilePath = (path: string) => encodeURIComp
|
||||
|
||||
// TODO: we can try and make pretty urls if we want to
|
||||
export const serverFilePathFromUrlFriendlyPath = (unique: string) => decodeURIComponent(unique.replace('-', '.'));
|
||||
|
||||
//Below are a copy of
|
||||
export const getInsertDictionarySnippet = (nodeName: string) => {
|
||||
return `@Umbraco.GetDictionaryValue("${nodeName}")`;
|
||||
}
|
||||
|
||||
export const getInsertPartialSnippet = (nodeName: string) =>
|
||||
`@await Html.PartialAsync("${nodeName.replace('.cshtml', '')}")`;
|
||||
|
||||
export const getQuerySnippet = (queryExpression: string) => {
|
||||
let code = '\n@{\n' + '\tvar selection = ' + queryExpression + ';\n}\n';
|
||||
code +=
|
||||
'<ul>\n' +
|
||||
'\t@foreach (var item in selection)\n' +
|
||||
'\t{\n' +
|
||||
'\t\t<li>\n' +
|
||||
'\t\t\t<a href="@item.Url()">@item.Name()</a>\n' +
|
||||
'\t\t</li>\n' +
|
||||
'\t}\n' +
|
||||
'</ul>\n\n';
|
||||
return code;
|
||||
};
|
||||
|
||||
export const getRenderBodySnippet = () => '@RenderBody()';
|
||||
|
||||
export const getRenderSectionSnippet = (sectionName: string, isMandatory: boolean) =>
|
||||
`@RenderSection("${sectionName}", ${isMandatory})`;
|
||||
|
||||
export const getAddSectionSnippet = (sectionName: string) => `@section ${sectionName}
|
||||
{
|
||||
|
||||
|
||||
|
||||
}`;
|
||||
|
||||
export const getUmbracoFieldSnippet = (field: string, defaultValue: string | null = null, recursive = false) => {
|
||||
let fallback = null;
|
||||
|
||||
if (recursive !== false && defaultValue !== null) {
|
||||
fallback = 'Fallback.To(Fallback.Ancestors, Fallback.DefaultValue)';
|
||||
} else if (recursive !== false) {
|
||||
fallback = 'Fallback.ToAncestors';
|
||||
} else if (defaultValue !== null) {
|
||||
fallback = 'Fallback.ToDefaultValue';
|
||||
}
|
||||
|
||||
const value = `${field !== null ? `@Model.Value("${field}"` : ''}${
|
||||
fallback !== null ? `, fallback: ${fallback}` : ''
|
||||
}${defaultValue !== null ? `, defaultValue: new HtmlString("${defaultValue}")` : ''}${field ? ')' : ''}`;
|
||||
|
||||
return value;
|
||||
};
|
||||
|
||||
|
||||
@@ -1,8 +1,10 @@
|
||||
import { manifests as translationSectionManifests } from './section.manifest';
|
||||
import { manifests as dictionaryManifests } from './dictionary/manifests';
|
||||
import { manifests as modalManifests } from './modals/manifests';
|
||||
|
||||
import { UmbEntrypointOnInit } from '@umbraco-cms/backoffice/extensions-api';
|
||||
|
||||
export const manifests = [...translationSectionManifests, ...dictionaryManifests];
|
||||
export const manifests = [...modalManifests, ...translationSectionManifests, ...dictionaryManifests];
|
||||
|
||||
export const onInit: UmbEntrypointOnInit = (_host, extensionRegistry) => {
|
||||
extensionRegistry.registerMany(manifests);
|
||||
|
||||
@@ -0,0 +1,89 @@
|
||||
import { UUITextStyles } from '@umbraco-ui/uui-css';
|
||||
import { css, html } from 'lit';
|
||||
import { customElement, state } from 'lit/decorators.js';
|
||||
import { UmbTreeElement } from '../../../core/components/tree/tree.element';
|
||||
import { UmbModalBaseElement } from '@umbraco-cms/internal/modal';
|
||||
import { UmbDictionaryItemPickerModalData, UmbDictionaryItemPickerModalResult } from '@umbraco-cms/backoffice/modal';
|
||||
|
||||
@customElement('umb-dictionary-item-picker-modal')
|
||||
export default class UmbDictionaryItemPickerModalElement extends UmbModalBaseElement<
|
||||
UmbDictionaryItemPickerModalData,
|
||||
UmbDictionaryItemPickerModalResult
|
||||
> {
|
||||
@state()
|
||||
_selection: Array<string | null> = [];
|
||||
|
||||
@state()
|
||||
_multiple = false;
|
||||
|
||||
connectedCallback() {
|
||||
super.connectedCallback();
|
||||
this._selection = this.data?.selection ?? [];
|
||||
this._multiple = this.data?.multiple ?? true;
|
||||
}
|
||||
|
||||
private _handleSelectionChange(e: CustomEvent) {
|
||||
e.stopPropagation();
|
||||
const element = e.target as UmbTreeElement;
|
||||
this._selection = this._multiple ? element.selection : [element.selection[element.selection.length - 1]];
|
||||
this._submit();
|
||||
}
|
||||
|
||||
private _submit() {
|
||||
this.modalHandler?.submit({ selection: this._selection });
|
||||
}
|
||||
|
||||
private _close() {
|
||||
this.modalHandler?.reject();
|
||||
}
|
||||
|
||||
render() {
|
||||
return html`
|
||||
<umb-body-layout headline="Dictionary item">
|
||||
<div id="main">
|
||||
<uui-box>
|
||||
<umb-tree
|
||||
alias="Umb.Tree.Dictionary"
|
||||
@selected=${this._handleSelectionChange}
|
||||
.selection=${this._selection}
|
||||
selectable></umb-tree>
|
||||
</uui-box>
|
||||
</div>
|
||||
<div slot="actions">
|
||||
<uui-button @click=${this._close} look="secondary">Close</uui-button>
|
||||
</div>
|
||||
</umb-body-layout>
|
||||
`;
|
||||
}
|
||||
|
||||
static styles = [
|
||||
UUITextStyles,
|
||||
css`
|
||||
:host {
|
||||
display: block;
|
||||
color: var(--uui-color-text);
|
||||
}
|
||||
|
||||
#main {
|
||||
box-sizing: border-box;
|
||||
padding: var(--uui-size-space-5);
|
||||
height: calc(100vh - 124px);
|
||||
}
|
||||
|
||||
#main uui-button {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
h3,
|
||||
p {
|
||||
text-align: left;
|
||||
}
|
||||
`,
|
||||
];
|
||||
}
|
||||
|
||||
declare global {
|
||||
interface HTMLElementTagNameMap {
|
||||
'umb-dictionary-item-picker-modal': UmbDictionaryItemPickerModalElement;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
import { ManifestModal } from '@umbraco-cms/backoffice/extensions-registry';
|
||||
import { UMB_DICTIONARY_ITEM_PICKER_MODAL_ALIAS } from '@umbraco-cms/backoffice/modal';
|
||||
|
||||
const modals: Array<ManifestModal> = [
|
||||
{
|
||||
type: 'modal',
|
||||
alias: UMB_DICTIONARY_ITEM_PICKER_MODAL_ALIAS,
|
||||
name: 'Dictionary Item Picker Modal',
|
||||
loader: () => import('./dictionary-item-picker/dictionary-item-picker-modal.element'),
|
||||
},
|
||||
];
|
||||
|
||||
export const manifests = [...modals];
|
||||
@@ -29,6 +29,7 @@ import { handlers as logViewerHandlers } from './domains/log-viewer.handlers';
|
||||
import { handlers as packageHandlers } from './domains/package.handlers';
|
||||
import { handlers as rteEmbedHandlers } from './domains/rte-embed.handlers';
|
||||
import { handlers as stylesheetHandlers } from './domains/stylesheet.handlers';
|
||||
import { handlers as partialViewsHandlers } from './domains/partial-views.handlers';
|
||||
import { handlers as tagHandlers } from './domains/tag-handlers';
|
||||
|
||||
const handlers = [
|
||||
@@ -62,6 +63,7 @@ const handlers = [
|
||||
...packageHandlers,
|
||||
...rteEmbedHandlers,
|
||||
...stylesheetHandlers,
|
||||
...partialViewsHandlers,
|
||||
...tagHandlers,
|
||||
];
|
||||
|
||||
|
||||
@@ -0,0 +1,104 @@
|
||||
import { UmbEntityData } from './entity.data';
|
||||
import { createFileSystemTreeItem } from './utils';
|
||||
import {
|
||||
FileSystemTreeItemPresentationModel,
|
||||
PagedFileSystemTreeItemPresentationModel,
|
||||
} from '@umbraco-cms/backoffice/backend-api';
|
||||
|
||||
export const data: Array<FileSystemTreeItemPresentationModel> = [
|
||||
{
|
||||
path: 'blockgrid',
|
||||
isFolder: true,
|
||||
name: 'blockgrid',
|
||||
type: 'partial-view',
|
||||
icon: 'umb:folder',
|
||||
hasChildren: true,
|
||||
},
|
||||
{
|
||||
path: 'blocklist',
|
||||
isFolder: true,
|
||||
name: 'blocklist',
|
||||
type: 'partial-view',
|
||||
icon: 'umb:folder',
|
||||
hasChildren: true,
|
||||
},
|
||||
{
|
||||
path: 'grid',
|
||||
isFolder: true,
|
||||
name: 'grid',
|
||||
type: 'partial-view',
|
||||
icon: 'umb:folder',
|
||||
hasChildren: true,
|
||||
},
|
||||
{
|
||||
path: 'blockgrid/area.cshtml',
|
||||
isFolder: false,
|
||||
name: 'area.cshtml',
|
||||
type: 'partial-view',
|
||||
icon: 'umb:article',
|
||||
hasChildren: false,
|
||||
},
|
||||
{
|
||||
path: 'blockgrid/items.cshtml',
|
||||
isFolder: false,
|
||||
name: 'items.cshtml',
|
||||
type: 'partial-view',
|
||||
icon: 'umb:article',
|
||||
hasChildren: false,
|
||||
},
|
||||
{
|
||||
path: 'blocklist/default.cshtml',
|
||||
isFolder: false,
|
||||
name: 'default.cshtml',
|
||||
type: 'partial-view',
|
||||
icon: 'umb:article',
|
||||
hasChildren: false,
|
||||
},
|
||||
{
|
||||
path: 'grid/editors',
|
||||
isFolder: false,
|
||||
name: 'editors',
|
||||
type: 'partial-view',
|
||||
icon: 'umb:folder',
|
||||
hasChildren: false,
|
||||
},
|
||||
{
|
||||
path: 'grid/default.cshtml',
|
||||
isFolder: false,
|
||||
name: 'items.cshtml',
|
||||
type: 'partial-view',
|
||||
icon: 'umb:article',
|
||||
hasChildren: false,
|
||||
},
|
||||
];
|
||||
|
||||
// Temp mocked database
|
||||
// TODO: all properties are optional in the server schema. I don't think this is correct.
|
||||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||
// @ts-ignore
|
||||
class UmbPartialViewsData extends UmbEntityData<FileSystemTreeItemPresentationModel> {
|
||||
constructor() {
|
||||
super(data);
|
||||
}
|
||||
|
||||
getTreeRoot(): PagedFileSystemTreeItemPresentationModel {
|
||||
const items = this.data.filter((item) => item.path?.includes('/') === false);
|
||||
const treeItems = items.map((item) => createFileSystemTreeItem(item));
|
||||
const total = items.length;
|
||||
return { items: treeItems, total };
|
||||
}
|
||||
|
||||
getTreeItemChildren(parentPath: string): PagedFileSystemTreeItemPresentationModel {
|
||||
const items = this.data.filter((item) => item.path?.startsWith(parentPath + '/'));
|
||||
const treeItems = items.map((item) => createFileSystemTreeItem(item));
|
||||
const total = items.length;
|
||||
return { items: treeItems, total };
|
||||
}
|
||||
|
||||
getTreeItem(paths: Array<string>): Array<FileSystemTreeItemPresentationModel> {
|
||||
const items = this.data.filter((item) => paths.includes(item.path ?? ''));
|
||||
return items.map((item) => createFileSystemTreeItem(item));
|
||||
}
|
||||
}
|
||||
|
||||
export const umbPartialViewsData = new UmbPartialViewsData();
|
||||
@@ -29,7 +29,7 @@ export const data: Array<TemplateDBItem> = [
|
||||
parentId: null,
|
||||
name: 'Doc 1',
|
||||
type: 'template',
|
||||
icon: 'icon-layout',
|
||||
icon: 'umb:layout',
|
||||
hasChildren: false,
|
||||
alias: 'Doc1',
|
||||
content: `@using Umbraco.Extensions
|
||||
@@ -53,7 +53,7 @@ export const data: Array<TemplateDBItem> = [
|
||||
parentId: null,
|
||||
name: 'Test',
|
||||
type: 'template',
|
||||
icon: 'icon-layout',
|
||||
icon: 'umb:layout',
|
||||
hasChildren: true,
|
||||
alias: 'Test',
|
||||
content:
|
||||
@@ -66,7 +66,7 @@ export const data: Array<TemplateDBItem> = [
|
||||
parentId: '9a84c0b3-03b4-4dd4-84ac-706740ac0f71',
|
||||
name: 'Child',
|
||||
type: 'template',
|
||||
icon: 'icon-layout',
|
||||
icon: 'umb:layout',
|
||||
hasChildren: false,
|
||||
alias: 'Test',
|
||||
content:
|
||||
|
||||
@@ -0,0 +1,26 @@
|
||||
import { rest } from 'msw';
|
||||
import { umbPartialViewsData } from '../data/partial-views.data';
|
||||
import { umbracoPath } from '@umbraco-cms/backoffice/utils';
|
||||
|
||||
export const handlers = [
|
||||
rest.get(umbracoPath('/tree/partial-view/root'), (req, res, ctx) => {
|
||||
const response = umbPartialViewsData.getTreeRoot();
|
||||
return res(ctx.status(200), ctx.json(response));
|
||||
}),
|
||||
|
||||
rest.get(umbracoPath('/tree/partial-view/children'), (req, res, ctx) => {
|
||||
const path = req.url.searchParams.get('path');
|
||||
if (!path) return;
|
||||
|
||||
const response = umbPartialViewsData.getTreeItemChildren(path);
|
||||
return res(ctx.status(200), ctx.json(response));
|
||||
}),
|
||||
|
||||
rest.get(umbracoPath('/tree/partial-view/item'), (req, res, ctx) => {
|
||||
const paths = req.url.searchParams.getAll('paths');
|
||||
if (!paths) return;
|
||||
|
||||
const items = umbPartialViewsData.getTreeItem(paths);
|
||||
return res(ctx.status(200), ctx.json(items));
|
||||
}),
|
||||
];
|
||||
@@ -1,15 +1,22 @@
|
||||
import { property } from 'lit/decorators.js';
|
||||
import { UmbLitElement } from '@umbraco-cms/internal/lit-element';
|
||||
import { UmbModalHandler } from '@umbraco-cms/backoffice/modal';
|
||||
import type { UmbModalExtensionElement } from '@umbraco-cms/backoffice/extensions-registry';
|
||||
import type { ManifestModal, UmbModalExtensionElement } from '@umbraco-cms/backoffice/extensions-registry';
|
||||
|
||||
export abstract class UmbModalBaseElement<UmbModalData extends object = object, UmbModalResult = unknown>
|
||||
export abstract class UmbModalBaseElement<
|
||||
ModalDataType extends object = object,
|
||||
ModalResultType = unknown,
|
||||
ModalManifestType extends ManifestModal = ManifestModal
|
||||
>
|
||||
extends UmbLitElement
|
||||
implements UmbModalExtensionElement<UmbModalData, UmbModalResult>
|
||||
implements UmbModalExtensionElement<ModalDataType, ModalResultType, ModalManifestType>
|
||||
{
|
||||
@property({ type: Array, attribute: false })
|
||||
public manifest?: ModalManifestType;
|
||||
|
||||
@property({ attribute: false })
|
||||
modalHandler?: UmbModalHandler<UmbModalData, UmbModalResult>;
|
||||
public modalHandler?: UmbModalHandler<ModalDataType, ModalResultType>;
|
||||
|
||||
@property({ type: Object, attribute: false })
|
||||
data?: UmbModalData;
|
||||
public data?: ModalDataType;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user