Merge remote-tracking branch 'origin/feature/block-type-create-labels' into feature/block-grid-editor-grid-foundation
This commit is contained in:
@@ -75,6 +75,7 @@ export class ExampleSorterDashboard extends UmbElementMixin(LitElement) {
|
||||
|
||||
.outer-wrapper {
|
||||
display: flex;
|
||||
gap: var(--uui-size-layout-1);
|
||||
}
|
||||
`,
|
||||
];
|
||||
|
||||
@@ -78,6 +78,9 @@ export class ExampleSorterGroup extends UmbElementMixin(LitElement) {
|
||||
:host {
|
||||
display: block;
|
||||
width: 100%;
|
||||
border: 1px dashed rgba(122, 122, 122, 0.25);
|
||||
border-radius: calc(var(--uui-border-radius) * 2);
|
||||
padding: var(--uui-size-space-1);
|
||||
}
|
||||
|
||||
.sorter-placeholder {
|
||||
@@ -87,14 +90,6 @@ export class ExampleSorterGroup extends UmbElementMixin(LitElement) {
|
||||
.sorter-container {
|
||||
min-height: 20px;
|
||||
}
|
||||
|
||||
example-sorter-group {
|
||||
display: block;
|
||||
width: 100%;
|
||||
border: 1px dashed rgba(122, 122, 122, 0.25);
|
||||
border-radius: calc(var(--uui-border-radius) * 2);
|
||||
padding: var(--uui-size-space-1);
|
||||
}
|
||||
`,
|
||||
];
|
||||
}
|
||||
|
||||
@@ -57,6 +57,7 @@ export class ExampleSorterDashboard extends UmbElementMixin(LitElement) {
|
||||
|
||||
.outer-wrapper {
|
||||
display: flex;
|
||||
gap: var(--uui-size-layout-1);
|
||||
}
|
||||
`,
|
||||
];
|
||||
|
||||
@@ -69,6 +69,9 @@ export class ExampleSorterGroup extends UmbElementMixin(LitElement) {
|
||||
:host {
|
||||
display: block;
|
||||
width: 100%;
|
||||
border: 1px dashed rgba(122, 122, 122, 0.25);
|
||||
border-radius: calc(var(--uui-border-radius) * 2);
|
||||
padding: var(--uui-size-space-1);
|
||||
}
|
||||
|
||||
.sorter-placeholder {
|
||||
|
||||
@@ -45,7 +45,7 @@
|
||||
"./tree": "./dist-cms/packages/core/tree/index.js",
|
||||
"./variant": "./dist-cms/packages/core/variant/index.js",
|
||||
"./workspace": "./dist-cms/packages/core/workspace/index.js",
|
||||
"./events": "./dist-cms/packages/core/umb-events/index.js",
|
||||
"./event": "./dist-cms/packages/core/event/index.js",
|
||||
"./repository": "./dist-cms/packages/core/repository/index.js",
|
||||
"./temporary-file": "./dist-cms/packages/core/temporary-file/index.js",
|
||||
"./block": "./dist-cms/packages/block/index.js",
|
||||
|
||||
@@ -136,6 +136,7 @@ export class UmbAppElement extends UmbLitElement {
|
||||
// Instruct all requests to use the auth flow to get and use the access_token for all subsequent requests
|
||||
OpenAPI.TOKEN = () => this.#authContext!.getLatestToken();
|
||||
OpenAPI.WITH_CREDENTIALS = true;
|
||||
OpenAPI.CREDENTIALS = 'include';
|
||||
}
|
||||
|
||||
#redirect() {
|
||||
|
||||
@@ -34,7 +34,7 @@ export class UmbPropertyEditorUIBlockGridElement extends UmbLitElement implement
|
||||
private _directRoute?: string;
|
||||
|
||||
@state()
|
||||
private _createButtonLabel = this.localize.term('content_createEmpty');
|
||||
private _createButtonLabel = this.localize.term('blockEditor_addBlock');
|
||||
|
||||
@property({ attribute: false })
|
||||
public set config(config: UmbPropertyEditorConfigCollection | undefined) {
|
||||
@@ -52,7 +52,7 @@ export class UmbPropertyEditorUIBlockGridElement extends UmbLitElement implement
|
||||
if (customCreateButtonLabel) {
|
||||
this._createButtonLabel = customCreateButtonLabel;
|
||||
} else if (this._blocks.length === 1) {
|
||||
this._createButtonLabel = `${this.localize.term('general_add')} ${this._blocks[0].label}`;
|
||||
this._createButtonLabel = this.localize.term('blockEditor_addThis', [this._blocks[0].label]);
|
||||
}
|
||||
|
||||
//const useInlineEditingAsDefault = config.getValueByAlias<boolean>('useInlineEditingAsDefault');
|
||||
|
||||
@@ -1,8 +1,4 @@
|
||||
import { UMB_BLOCK_WORKSPACE_MODAL } from '../../workspace/index.js';
|
||||
import {
|
||||
DOCUMENT_TYPE_ITEM_REPOSITORY_ALIAS,
|
||||
type UmbDocumentTypeItemModel,
|
||||
} from '@umbraco-cms/backoffice/document-type';
|
||||
import type {
|
||||
UmbBlockCatalogueModalData,
|
||||
UmbBlockCatalogueModalValue,
|
||||
@@ -10,30 +6,19 @@ import type {
|
||||
UmbBlockTypeWithGroupKey,
|
||||
} from '@umbraco-cms/backoffice/block';
|
||||
import { css, html, customElement, state, repeat, ifDefined, nothing } from '@umbraco-cms/backoffice/external/lit';
|
||||
import { groupBy } from '@umbraco-cms/backoffice/external/lodash';
|
||||
import {
|
||||
UMB_MODAL_CONTEXT,
|
||||
UmbModalBaseElement,
|
||||
UmbModalRouteRegistrationController,
|
||||
} from '@umbraco-cms/backoffice/modal';
|
||||
import { UmbRepositoryItemsManager } from '@umbraco-cms/backoffice/repository';
|
||||
|
||||
@customElement('umb-block-catalogue-modal')
|
||||
export class UmbBlockCatalogueModalElement extends UmbModalBaseElement<
|
||||
UmbBlockCatalogueModalData,
|
||||
UmbBlockCatalogueModalValue
|
||||
> {
|
||||
#itemManager = new UmbRepositoryItemsManager<UmbDocumentTypeItemModel>(
|
||||
this,
|
||||
DOCUMENT_TYPE_ITEM_REPOSITORY_ALIAS,
|
||||
(x) => x.unique,
|
||||
);
|
||||
|
||||
@state()
|
||||
private _blocks: Array<UmbBlockTypeWithGroupKey> = [];
|
||||
|
||||
@state()
|
||||
private _blockGroups: Array<UmbBlockTypeGroup> = [];
|
||||
private _groupedBlocks: Array<{ name?: string; blocks: Array<UmbBlockTypeWithGroupKey> }> = [];
|
||||
|
||||
@state()
|
||||
_openClipboard?: boolean;
|
||||
@@ -60,19 +45,6 @@ export class UmbBlockCatalogueModalElement extends UmbModalBaseElement<
|
||||
this._workspacePath = routeBuilder({});
|
||||
});
|
||||
});
|
||||
|
||||
this.observe(this.#itemManager.items, (items) => {
|
||||
this._blocks = items.map((item) => {
|
||||
const blockGroup = this._blocks.find((block) => block.contentElementTypeKey === item.unique)?.groupKey;
|
||||
const block: UmbBlockTypeWithGroupKey = {
|
||||
contentElementTypeKey: item.unique,
|
||||
label: item.name,
|
||||
icon: item.icon ?? undefined,
|
||||
groupKey: blockGroup,
|
||||
};
|
||||
return block;
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
connectedCallback() {
|
||||
@@ -80,10 +52,17 @@ export class UmbBlockCatalogueModalElement extends UmbModalBaseElement<
|
||||
if (!this.data) return;
|
||||
|
||||
this._openClipboard = this.data.openClipboard ?? false;
|
||||
this._blocks = this.data.blocks ?? [];
|
||||
this._blockGroups = this.data.blockGroups ?? [];
|
||||
|
||||
this.#itemManager.setUniques(this._blocks.map((x) => x.contentElementTypeKey));
|
||||
const blocks: Array<UmbBlockTypeWithGroupKey> = this.data.blocks ?? [];
|
||||
const blockGroups: Array<UmbBlockTypeGroup> = this.data.blockGroups ?? [];
|
||||
|
||||
const noGroupBlocks = blocks.filter((block) => !blockGroups.find((group) => group.key === block.groupKey));
|
||||
const grouped = blockGroups.map((group) => ({
|
||||
name: group.name ?? '',
|
||||
blocks: blocks.filter((block) => block.groupKey === group.key),
|
||||
}));
|
||||
|
||||
this._groupedBlocks = [{ blocks: noGroupBlocks }, ...grouped];
|
||||
}
|
||||
|
||||
render() {
|
||||
@@ -107,17 +86,10 @@ export class UmbBlockCatalogueModalElement extends UmbModalBaseElement<
|
||||
}
|
||||
|
||||
#renderCreateEmpty() {
|
||||
const blockArrays = groupBy(this._blocks, 'groupKey');
|
||||
|
||||
const mappedGroupsAndBlocks = Object.entries(blockArrays).map(([key, value]) => {
|
||||
const group = this._blockGroups.find((group) => group.key === key);
|
||||
return { name: group?.name, blocks: value };
|
||||
});
|
||||
|
||||
return html`
|
||||
${mappedGroupsAndBlocks.map(
|
||||
${this._groupedBlocks.map(
|
||||
(group) => html`
|
||||
${group.name ? html`<h2>${group.name}</h2>` : nothing}
|
||||
${group.name ? html`<h4>${group.name}</h4>` : nothing}
|
||||
<div class="blockGroup">
|
||||
${repeat(
|
||||
group.blocks,
|
||||
@@ -141,12 +113,18 @@ export class UmbBlockCatalogueModalElement extends UmbModalBaseElement<
|
||||
#renderViews() {
|
||||
return html`
|
||||
<uui-tab-group slot="navigation">
|
||||
<uui-tab label="Create Empty" ?active=${!this._openClipboard} @click=${() => (this._openClipboard = false)}>
|
||||
Create Empty
|
||||
<uui-tab
|
||||
label=${this.localize.term('blockEditor_tabCreateEmpty')}
|
||||
?active=${!this._openClipboard}
|
||||
@click=${() => (this._openClipboard = false)}>
|
||||
<umb-localize key=${this.localize.term('blockEditor_tabCreateEmpty')}>Create Empty</umb-localize>
|
||||
<uui-icon slot="icon" name="icon-add"></uui-icon>
|
||||
</uui-tab>
|
||||
<uui-tab label="Clipboard" ?active=${this._openClipboard} @click=${() => (this._openClipboard = true)}>
|
||||
Clipboard
|
||||
<uui-tab
|
||||
label=${this.localize.term('blockEditor_tabClipboard')}
|
||||
?active=${this._openClipboard}
|
||||
@click=${() => (this._openClipboard = true)}>
|
||||
<umb-localize key=${this.localize.term('blockEditor_tabClipboard')}>Clipboard</umb-localize>
|
||||
<uui-icon slot="icon" name="icon-paste-in"></uui-icon>
|
||||
</uui-tab>
|
||||
</uui-tab-group>
|
||||
|
||||
@@ -1,21 +1,25 @@
|
||||
import { UmbAuthFlow } from './auth-flow.js';
|
||||
import { UMB_AUTH_CONTEXT } from './auth.context.token.js';
|
||||
import type { UmbOpenApiConfiguration } from './models/openApiConfiguration.js';
|
||||
import type { UmbControllerHostElement } from '@umbraco-cms/backoffice/controller-api';
|
||||
import { UmbBaseController } from '@umbraco-cms/backoffice/class-api';
|
||||
import { UmbBooleanState } from '@umbraco-cms/backoffice/observable-api';
|
||||
import { OpenAPI } from '@umbraco-cms/backoffice/backend-api';
|
||||
|
||||
export class UmbAuthContext extends UmbBaseController {
|
||||
#isAuthorized = new UmbBooleanState<boolean>(false);
|
||||
readonly isAuthorized = this.#isAuthorized.asObservable();
|
||||
|
||||
#isBypassed = false;
|
||||
#backofficePath: string;
|
||||
|
||||
#serverUrl;
|
||||
#backofficePath;
|
||||
#authFlow;
|
||||
#openApi = OpenAPI;
|
||||
|
||||
constructor(host: UmbControllerHostElement, serverUrl: string, backofficePath: string, isBypassed: boolean) {
|
||||
super(host);
|
||||
this.#isBypassed = isBypassed;
|
||||
this.#serverUrl = serverUrl;
|
||||
this.#backofficePath = backofficePath;
|
||||
|
||||
this.#authFlow = new UmbAuthFlow(serverUrl, this.#getRedirectUrl());
|
||||
@@ -65,7 +69,7 @@ export class UmbAuthContext extends UmbBaseController {
|
||||
*
|
||||
* NB! The user may experience being redirected to the login screen if the token is expired.
|
||||
*
|
||||
* @example
|
||||
* @example <caption>Using the latest token</caption>
|
||||
* ```js
|
||||
* const token = await authContext.getLatestToken();
|
||||
* const result = await fetch('https://my-api.com', { headers: { Authorization: `Bearer ${token}` } });
|
||||
@@ -94,6 +98,51 @@ export class UmbAuthContext extends UmbBaseController {
|
||||
return this.#authFlow.signOut();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the server url to the Management API.
|
||||
* @memberof UmbAuthContext
|
||||
* @example <caption>Using the server url</caption>
|
||||
* ```js
|
||||
* const serverUrl = authContext.getServerUrl();
|
||||
* OpenAPI.BASE = serverUrl;
|
||||
* ```
|
||||
* @example <caption></caption>
|
||||
* ```js
|
||||
* const serverUrl = authContext.getServerUrl();
|
||||
* const token = await authContext.getLatestToken();
|
||||
* const result = await fetch(`${serverUrl}/umbraco/management/api/v1/my-resource`, { headers: { Authorization: `Bearer ${token}` } });
|
||||
* ```
|
||||
* @returns The server url to the Management API
|
||||
*/
|
||||
getServerUrl() {
|
||||
return this.#serverUrl;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the default OpenAPI configuration, which is set up to communicate with the Management API.
|
||||
* @remark This is useful if you want to communicate with your own resources generated by the [openapi-typescript-codegen](https://github.com/ferdikoomen/openapi-typescript-codegen) library.
|
||||
* @memberof UmbAuthContext
|
||||
*
|
||||
* @example <caption>Using the default OpenAPI configuration</caption>
|
||||
* ```js
|
||||
* const defaultOpenApi = authContext.getOpenApiConfiguration();
|
||||
* OpenAPI.BASE = defaultOpenApi.base;
|
||||
* OpenAPI.WITH_CREDENTIALS = defaultOpenApi.withCredentials;
|
||||
* OpenAPI.CREDENTIALS = defaultOpenApi.credentials;
|
||||
* OpenAPI.TOKEN = defaultOpenApi.token;
|
||||
* ```
|
||||
* @returns The default OpenAPI configuration
|
||||
*/
|
||||
getOpenApiConfiguration(): UmbOpenApiConfiguration {
|
||||
return {
|
||||
base: OpenAPI.BASE,
|
||||
version: OpenAPI.VERSION,
|
||||
withCredentials: OpenAPI.WITH_CREDENTIALS,
|
||||
credentials: OpenAPI.CREDENTIALS,
|
||||
token: () => this.getLatestToken(),
|
||||
};
|
||||
}
|
||||
|
||||
#getRedirectUrl() {
|
||||
return `${window.location.origin}${this.#backofficePath}`;
|
||||
}
|
||||
|
||||
@@ -1,2 +1,3 @@
|
||||
export * from './auth.context.js';
|
||||
export * from './auth.context.token.js';
|
||||
export * from './models/openApiConfiguration.js';
|
||||
|
||||
@@ -0,0 +1,32 @@
|
||||
/**
|
||||
* Configuration for the OpenAPI (Umbraco) server. This is used to communicate with the Management API.
|
||||
* This is useful if you want to configure your Fetch, Axios or other HTTP client to communicate with the Management API.
|
||||
* If you use the recommended resource generator [openapi-typescript-codegen](https://github.com/ferdikoomen/openapi-typescript-codegen) this can be used to configure the `OpenAPI` object.
|
||||
*/
|
||||
export interface UmbOpenApiConfiguration {
|
||||
/**
|
||||
* The base URL of the OpenAPI (Umbraco) server.
|
||||
*/
|
||||
readonly base: string;
|
||||
|
||||
/**
|
||||
* The configured version of the Management API to use.
|
||||
*/
|
||||
readonly version: string;
|
||||
|
||||
/**
|
||||
* The `withCredentials` option for the Fetch API.
|
||||
*/
|
||||
readonly withCredentials: boolean;
|
||||
|
||||
/**
|
||||
* The `credentials` option for the Fetch API.
|
||||
*/
|
||||
readonly credentials: 'include' | 'omit' | 'same-origin';
|
||||
|
||||
/**
|
||||
* The token to use for the Authorization header.
|
||||
* @returns A resolver for the token to use for the Authorization header.
|
||||
*/
|
||||
readonly token: () => Promise<string>;
|
||||
}
|
||||
Reference in New Issue
Block a user