Merge branch 'main' into chore/move-extension-components
This commit is contained in:
24
src/Umbraco.Web.UI.Client/package-lock.json
generated
24
src/Umbraco.Web.UI.Client/package-lock.json
generated
@@ -15833,18 +15833,6 @@
|
||||
"node": ">=8.6"
|
||||
}
|
||||
},
|
||||
"node_modules/mime": {
|
||||
"version": "1.6.0",
|
||||
"resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz",
|
||||
"integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==",
|
||||
"dev": true,
|
||||
"bin": {
|
||||
"mime": "cli.js"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=4"
|
||||
}
|
||||
},
|
||||
"node_modules/mime-db": {
|
||||
"version": "1.52.0",
|
||||
"resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz",
|
||||
@@ -18415,6 +18403,18 @@
|
||||
"integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/send/node_modules/mime": {
|
||||
"version": "1.6.0",
|
||||
"resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz",
|
||||
"integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==",
|
||||
"dev": true,
|
||||
"bin": {
|
||||
"mime": "cli.js"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=4"
|
||||
}
|
||||
},
|
||||
"node_modules/send/node_modules/ms": {
|
||||
"version": "2.1.3",
|
||||
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
|
||||
|
||||
@@ -7,6 +7,7 @@ import { UMB_MODAL_MANAGER_CONTEXT } from '@umbraco-cms/backoffice/modal';
|
||||
|
||||
export class UmbAppAuthController extends UmbControllerBase {
|
||||
#authContext?: typeof UMB_AUTH_CONTEXT.TYPE;
|
||||
#isFirstCheck = true;
|
||||
|
||||
constructor(host: UmbControllerHost) {
|
||||
super(host);
|
||||
@@ -37,7 +38,18 @@ export class UmbAppAuthController extends UmbControllerBase {
|
||||
const isAuthorized = this.#authContext.getIsAuthorized();
|
||||
|
||||
if (isAuthorized) {
|
||||
return true;
|
||||
// If this is the first time we are checking the authorization state (i.e. on first load), we need to make sure
|
||||
// that the token is still valid. If it is not, we need to start the authorization flow.
|
||||
// If the token is still valid, we can return true.
|
||||
if (this.#isFirstCheck) {
|
||||
this.#isFirstCheck = false;
|
||||
const isValid = await this.#authContext.validateToken();
|
||||
if (isValid) {
|
||||
return true;
|
||||
}
|
||||
} else {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
// Make a request to the auth server to start the auth flow
|
||||
|
||||
@@ -62,21 +62,36 @@ export class UmbAppElement extends UmbLitElement {
|
||||
path: 'oauth_complete',
|
||||
component: () => import('./app-error.element.js'),
|
||||
setup: (component) => {
|
||||
if (!this.#authContext) {
|
||||
throw new Error('[Fatal] Auth context is not available');
|
||||
}
|
||||
|
||||
const searchParams = new URLSearchParams(window.location.search);
|
||||
const hasCode = searchParams.has('code');
|
||||
(component as UmbAppErrorElement).hideBackButton = true;
|
||||
(component as UmbAppErrorElement).errorHeadline = this.localize.term('general_login');
|
||||
(component as UmbAppErrorElement).errorMessage = hasCode
|
||||
? this.localize.term('errors_externalLoginSuccess')
|
||||
: this.localize.term('errors_externalLoginFailed');
|
||||
|
||||
// Complete the authorization request
|
||||
this.#authContext?.completeAuthorizationRequest().finally(() => {
|
||||
// If we don't have an opener, redirect to the root
|
||||
if (!window.opener) {
|
||||
history.replaceState(null, '', '');
|
||||
}
|
||||
});
|
||||
// If there is an opener, we are in a popup window, and we should show a different message
|
||||
// than if we are in the main window. If we are in the main window, we should redirect to the root.
|
||||
// The authorization request will be completed in the active window (main or popup) and the authorization signal will be sent.
|
||||
// If we are in a popup window, the storage event in UmbAuthContext will catch the signal and close the window.
|
||||
// If we are in the main window, the signal will be caught right here and the user will be redirected to the root.
|
||||
if (window.opener) {
|
||||
(component as UmbAppErrorElement).errorMessage = hasCode
|
||||
? this.localize.term('errors_externalLoginSuccess')
|
||||
: this.localize.term('errors_externalLoginFailed');
|
||||
} else {
|
||||
(component as UmbAppErrorElement).errorMessage = hasCode
|
||||
? this.localize.term('errors_externalLoginRedirectSuccess')
|
||||
: this.localize.term('errors_externalLoginFailed');
|
||||
|
||||
this.observe(this.#authContext.authorizationSignal, () => {
|
||||
window.location.href = '/';
|
||||
});
|
||||
}
|
||||
|
||||
// Complete the authorization request, which will send the authorization signal
|
||||
this.#authContext.completeAuthorizationRequest();
|
||||
},
|
||||
},
|
||||
{
|
||||
|
||||
@@ -711,6 +711,7 @@ export default {
|
||||
externalLoginFailed:
|
||||
'Serveren mislykkedes i at logge ind med den eksterne loginudbyder. Luk dette vindue og prøv igen.',
|
||||
externalLoginSuccess: 'Du er nu logget ind. Du kan nu lukke dette vindue.',
|
||||
externalLoginRedirectSuccess: 'Du er nu logget ind. Du vil blive omdirigeret om et øjeblik.',
|
||||
},
|
||||
openidErrors: {
|
||||
accessDenied: 'Access denied',
|
||||
|
||||
@@ -720,6 +720,7 @@ export default {
|
||||
externalLoginFailed:
|
||||
'The server failed to authorize you against the external login provider. Please close the window and try again.',
|
||||
externalLoginSuccess: 'You have successfully logged in. You may now close this window.',
|
||||
externalLoginRedirectSuccess: 'You have successfully logged in. You will be redirected shortly.',
|
||||
},
|
||||
openidErrors: {
|
||||
accessDenied: 'Access denied',
|
||||
@@ -1243,8 +1244,8 @@ export default {
|
||||
openMediaPicker: 'Open media picker',
|
||||
},
|
||||
propertyEditorPicker: {
|
||||
title: 'Select Property Editor',
|
||||
openPropertyEditorPicker: 'Select Property Editor',
|
||||
title: 'Select a property editor',
|
||||
openPropertyEditorPicker: 'Select a property editor UI',
|
||||
},
|
||||
relatedlinks: {
|
||||
enterExternal: 'enter external link',
|
||||
|
||||
@@ -731,6 +731,7 @@ export default {
|
||||
externalLoginFailed:
|
||||
'The server failed to authorize you against the external login provider. Please close the window and try again.',
|
||||
externalLoginSuccess: 'You have successfully logged in. You may now close this window.',
|
||||
externalLoginRedirectSuccess: 'You have successfully logged in. You will be redirected shortly.',
|
||||
},
|
||||
openidErrors: {
|
||||
accessDenied: 'Access denied',
|
||||
@@ -2542,8 +2543,8 @@ export default {
|
||||
searchResults: 'items returned',
|
||||
},
|
||||
propertyEditorPicker: {
|
||||
title: 'Select Property Editor',
|
||||
openPropertyEditorPicker: 'Select Property Editor',
|
||||
title: 'Select a property editor',
|
||||
openPropertyEditorPicker: 'Select a property editor UI',
|
||||
},
|
||||
analytics: {
|
||||
consentForAnalytics: 'Consent for telemetry data',
|
||||
|
||||
@@ -5,10 +5,10 @@
|
||||
* @param contexts This is a map of the collected contexts from umb-debug
|
||||
* @returns An array of simplified context data
|
||||
*/
|
||||
export function contextData(contexts: Map<any, any>): Array<DebugContextData> {
|
||||
const contextData = new Array<DebugContextData>();
|
||||
export function contextData(contexts: Map<any, any>): Array<UmbDebugContextData> {
|
||||
const contextData = new Array<UmbDebugContextData>();
|
||||
for (const [alias, instance] of contexts) {
|
||||
const data: DebugContextItemData = contextItemData(instance);
|
||||
const data = contextItemData(instance);
|
||||
contextData.push({ alias: alias, type: typeof instance, data });
|
||||
}
|
||||
return contextData;
|
||||
@@ -20,8 +20,8 @@ export function contextData(contexts: Map<any, any>): Array<DebugContextData> {
|
||||
* @param contextInstance The instance of the context
|
||||
* @returns A simplied object contain the properties and methods of the context
|
||||
*/
|
||||
function contextItemData(contextInstance: any): DebugContextItemData {
|
||||
let contextItemData: DebugContextItemData = { type: 'unknown' };
|
||||
function contextItemData(contextInstance: any): UmbDebugContextItemData {
|
||||
let contextItemData: UmbDebugContextItemData = { type: 'unknown' };
|
||||
|
||||
if (typeof contextInstance === 'function') {
|
||||
contextItemData = { ...contextItemData, type: 'function' };
|
||||
@@ -59,7 +59,7 @@ function contextItemData(contextInstance: any): DebugContextItemData {
|
||||
|
||||
valueToDisplay = `Web Component <${tagName}>`;
|
||||
} else if (isSubscribeLike) {
|
||||
valueToDisplay = 'Subscribable';
|
||||
valueToDisplay = 'Observable';
|
||||
}
|
||||
|
||||
props.push({ key: key, type: typeof value, value: valueToDisplay });
|
||||
@@ -71,7 +71,7 @@ function contextItemData(contextInstance: any): DebugContextItemData {
|
||||
}
|
||||
}
|
||||
|
||||
contextItemData = { ...contextItemData, properties: props };
|
||||
contextItemData = { ...contextItemData, properties: props.sort((a, b) => a.key.localeCompare(b.key)) };
|
||||
}
|
||||
} else {
|
||||
contextItemData = { ...contextItemData, type: 'primitive', value: contextInstance };
|
||||
@@ -83,7 +83,7 @@ function contextItemData(contextInstance: any): DebugContextItemData {
|
||||
/**
|
||||
* Gets a list of methods from a class
|
||||
*
|
||||
* @param klass The class to get the methods from
|
||||
* @param class The class to get the methods from
|
||||
* @returns An array of method names as strings
|
||||
*/
|
||||
function getClassMethodNames(klass: any) {
|
||||
@@ -98,15 +98,15 @@ function getClassMethodNames(klass: any) {
|
||||
|
||||
const allMethods =
|
||||
typeof klass.prototype === 'undefined' ? distinctDeepFunctions(klass) : Object.getOwnPropertyNames(klass.prototype);
|
||||
return allMethods.filter((name: any) => name !== 'constructor' && !name.startsWith('_'));
|
||||
return allMethods.filter((name: any) => name !== 'constructor' && !name.startsWith('_')).sort();
|
||||
}
|
||||
|
||||
export interface DebugContextData {
|
||||
export interface UmbDebugContextData {
|
||||
/**
|
||||
* The alias of the context
|
||||
*
|
||||
* @type {string}
|
||||
* @memberof DebugContextData
|
||||
* @memberof UmbDebugContextData
|
||||
*/
|
||||
alias: string;
|
||||
|
||||
@@ -114,32 +114,32 @@ export interface DebugContextData {
|
||||
* The type of the context such as object or string
|
||||
*
|
||||
* @type {("string" | "number" | "bigint" | "boolean" | "symbol" | "undefined" | "object" | "function")}
|
||||
* @memberof DebugContextData
|
||||
* @memberof UmbDebugContextData
|
||||
*/
|
||||
type: 'string' | 'number' | 'bigint' | 'boolean' | 'symbol' | 'undefined' | 'object' | 'function';
|
||||
|
||||
/**
|
||||
* Data about the context that includes method and property names
|
||||
*
|
||||
* @type {DebugContextItemData}
|
||||
* @memberof DebugContextData
|
||||
* @type {UmbDebugContextItemData}
|
||||
* @memberof UmbDebugContextData
|
||||
*/
|
||||
data: DebugContextItemData;
|
||||
data: UmbDebugContextItemData;
|
||||
}
|
||||
|
||||
export interface DebugContextItemData {
|
||||
export interface UmbDebugContextItemData {
|
||||
type: string;
|
||||
methods?: Array<unknown>;
|
||||
properties?: Array<DebugContextItemPropertyData>;
|
||||
properties?: Array<UmbDebugContextItemPropertyData>;
|
||||
value?: unknown;
|
||||
}
|
||||
|
||||
export interface DebugContextItemPropertyData {
|
||||
export interface UmbDebugContextItemPropertyData {
|
||||
/**
|
||||
* The name of the property
|
||||
*
|
||||
* @type {string}
|
||||
* @memberof DebugContextItemPropertyData
|
||||
* @memberof UmbDebugContextItemPropertyData
|
||||
*/
|
||||
key: string;
|
||||
|
||||
@@ -147,7 +147,7 @@ export interface DebugContextItemPropertyData {
|
||||
* The type of the property's value such as string or number
|
||||
*
|
||||
* @type {("string" | "number" | "bigint" | "boolean" | "symbol" | "undefined" | "object" | "function")}
|
||||
* @memberof DebugContextItemPropertyData
|
||||
* @memberof UmbDebugContextItemPropertyData
|
||||
*/
|
||||
type: 'string' | 'number' | 'bigint' | 'boolean' | 'symbol' | 'undefined' | 'object' | 'function';
|
||||
|
||||
@@ -155,7 +155,7 @@ export interface DebugContextItemPropertyData {
|
||||
* Simple types such as string or number can have their value displayed stored inside the property
|
||||
*
|
||||
* @type {("string" | "number" | "bigint" | "boolean" | "symbol" | "undefined" | "object" | "function")}
|
||||
* @memberof DebugContextItemPropertyData
|
||||
* @memberof UmbDebugContextItemPropertyData
|
||||
*/
|
||||
value?: unknown;
|
||||
}
|
||||
|
||||
@@ -1,10 +1,7 @@
|
||||
import type { UmbContextRequestEvent } from '../consume/context-request.event.js';
|
||||
import { UMB_CONTENT_REQUEST_EVENT_TYPE, UMB_DEBUG_CONTEXT_EVENT_TYPE } from '../consume/context-request.event.js';
|
||||
import type { UmbContextToken } from '../token/index.js';
|
||||
import {
|
||||
UmbContextProvideEventImplementation,
|
||||
//UmbContextUnprovidedEventImplementation,
|
||||
} from './context-provide.event.js';
|
||||
import { UMB_CONTENT_REQUEST_EVENT_TYPE, UMB_DEBUG_CONTEXT_EVENT_TYPE } from '../consume/context-request.event.js';
|
||||
import { UmbContextProvideEventImplementation } from './context-provide.event.js';
|
||||
|
||||
/**
|
||||
* @export
|
||||
@@ -76,7 +73,7 @@ export class UmbContextProvider<BaseType = unknown, ResultType extends BaseType
|
||||
this.#eventTarget.dispatchEvent(new UmbContextProvideEventImplementation(this.#contextAlias));
|
||||
|
||||
// Listen to our debug event 'umb:debug-contexts'
|
||||
this.#eventTarget.addEventListener(UMB_DEBUG_CONTEXT_EVENT_TYPE, this._handleDebugContextRequest);
|
||||
this.#eventTarget.addEventListener(UMB_DEBUG_CONTEXT_EVENT_TYPE, this.#handleDebugContextRequest);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -88,10 +85,10 @@ export class UmbContextProvider<BaseType = unknown, ResultType extends BaseType
|
||||
//window.dispatchEvent(new UmbContextUnprovidedEventImplementation(this._contextAlias, this.#instance));
|
||||
|
||||
// Stop listen to our debug event 'umb:debug-contexts'
|
||||
this.#eventTarget?.removeEventListener(UMB_DEBUG_CONTEXT_EVENT_TYPE, this._handleDebugContextRequest);
|
||||
this.#eventTarget?.removeEventListener(UMB_DEBUG_CONTEXT_EVENT_TYPE, this.#handleDebugContextRequest);
|
||||
}
|
||||
|
||||
private _handleDebugContextRequest = (event: any): void => {
|
||||
#handleDebugContextRequest = (event: any): void => {
|
||||
// If the event doesn't have an instances property, create it.
|
||||
if (!event.instances) {
|
||||
event.instances = new Map();
|
||||
|
||||
@@ -1,6 +1,10 @@
|
||||
import { UmbMockDBBase } from './utils/mock-db-base.js';
|
||||
import { UmbId } from '@umbraco-cms/backoffice/id';
|
||||
import type { UmbEntityBase } from '@umbraco-cms/backoffice/models';
|
||||
|
||||
type UmbEntityBase = {
|
||||
id?: string;
|
||||
name?: string;
|
||||
};
|
||||
|
||||
// Temp mocked database
|
||||
export class UmbEntityData<T extends UmbEntityBase> extends UmbMockDBBase<T> {
|
||||
|
||||
@@ -307,29 +307,17 @@ export class UmbAuthFlow {
|
||||
return Promise.resolve(this.#tokenResponse.accessToken);
|
||||
}
|
||||
|
||||
// if the refresh token is not set (maybe the provider doesn't support them)
|
||||
if (!this.#tokenResponse?.refreshToken) {
|
||||
this.#timeoutSignal.next();
|
||||
return Promise.reject('Missing refreshToken.');
|
||||
}
|
||||
const success = await this.makeRefreshTokenRequest();
|
||||
|
||||
const request = new TokenRequest({
|
||||
client_id: this.#clientId,
|
||||
redirect_uri: this.#redirectUri,
|
||||
grant_type: GRANT_TYPE_REFRESH_TOKEN,
|
||||
code: undefined,
|
||||
refresh_token: this.#tokenResponse.refreshToken,
|
||||
extras: undefined,
|
||||
});
|
||||
|
||||
await this.#performTokenRequest(request);
|
||||
|
||||
if (!this.#tokenResponse) {
|
||||
if (!success) {
|
||||
this.clearTokenStorage();
|
||||
this.#timeoutSignal.next();
|
||||
return Promise.reject('Missing tokenResponse.');
|
||||
}
|
||||
|
||||
return Promise.resolve(this.#tokenResponse.accessToken);
|
||||
return this.#tokenResponse
|
||||
? Promise.resolve(this.#tokenResponse.accessToken)
|
||||
: Promise.reject('Missing tokenResponse.');
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -364,18 +352,36 @@ export class UmbAuthFlow {
|
||||
await this.#performTokenRequest(request);
|
||||
}
|
||||
|
||||
async makeRefreshTokenRequest(): Promise<boolean> {
|
||||
if (!this.#tokenResponse?.refreshToken) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const request = new TokenRequest({
|
||||
client_id: this.#clientId,
|
||||
redirect_uri: this.#redirectUri,
|
||||
grant_type: GRANT_TYPE_REFRESH_TOKEN,
|
||||
code: undefined,
|
||||
refresh_token: this.#tokenResponse.refreshToken,
|
||||
extras: undefined,
|
||||
});
|
||||
|
||||
return this.#performTokenRequest(request);
|
||||
}
|
||||
|
||||
/**
|
||||
* This method will make a token request to the server using the refresh token.
|
||||
* If the request fails, it will sign the user out (clear the token state).
|
||||
*/
|
||||
async #performTokenRequest(request: TokenRequest): Promise<void> {
|
||||
async #performTokenRequest(request: TokenRequest): Promise<boolean> {
|
||||
try {
|
||||
this.#tokenResponse = await this.#tokenHandler.performTokenRequest(this.#configuration, request);
|
||||
this.#saveTokenState();
|
||||
return true;
|
||||
} catch (error) {
|
||||
// If the token request fails, it means the code or refresh token is invalid
|
||||
this.clearTokenStorage();
|
||||
console.error('Token request error', error);
|
||||
this.clearTokenStorage();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -174,6 +174,15 @@ export class UmbAuthContext extends UmbContextBase<UmbAuthContext> {
|
||||
return this.#authFlow.performWithFreshTokens();
|
||||
}
|
||||
|
||||
/**
|
||||
* Validates the token against the server and returns true if the token is valid.
|
||||
* @memberof UmbAuthContext
|
||||
* @returns True if the token is valid, otherwise false
|
||||
*/
|
||||
async validateToken(): Promise<boolean> {
|
||||
return this.#isBypassed || this.#authFlow.makeRefreshTokenRequest();
|
||||
}
|
||||
|
||||
/**
|
||||
* Clears the token storage.
|
||||
* @memberof UmbAuthContext
|
||||
@@ -188,7 +197,6 @@ export class UmbAuthContext extends UmbContextBase<UmbAuthContext> {
|
||||
* @memberof UmbAuthContext
|
||||
*/
|
||||
timeOut() {
|
||||
this.clearTokenStorage();
|
||||
this.#isAuthorized.setValue(false);
|
||||
this.#isTimeout.next();
|
||||
}
|
||||
|
||||
@@ -95,14 +95,16 @@ export class UmbAppAuthModalElement extends UmbModalBaseElement<UmbModalAppAuthC
|
||||
const providerName =
|
||||
typeof providerOrManifest === 'string' ? providerOrManifest : providerOrManifest.forProviderName;
|
||||
|
||||
await authContext.makeAuthorizationRequest(providerName, false, loginHint, manifest);
|
||||
// If the user is timed out, we do not want to lose the state, so avoid redirecting to the provider
|
||||
// and instead just make the authorization request. In all other cases, we want to redirect to the provider.
|
||||
const isTimedOut = this.data?.userLoginState === 'timedOut';
|
||||
|
||||
await authContext.makeAuthorizationRequest(providerName, isTimedOut ? false : true, loginHint, manifest);
|
||||
|
||||
const isAuthed = authContext.getIsAuthorized();
|
||||
this.value = { success: isAuthed };
|
||||
if (isAuthed) {
|
||||
this._submitModal();
|
||||
} else {
|
||||
this._error = 'Failed to authenticate';
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('[AuthModal] Error submitting auth request', error);
|
||||
|
||||
@@ -8,7 +8,7 @@ import type { UmbRoute } from '@umbraco-cms/backoffice/router';
|
||||
|
||||
export interface UmbCollectionViewManagerConfig {
|
||||
defaultViewAlias?: string;
|
||||
manifestFilter?: (manifest: ManifestCollectionView) => boolean
|
||||
manifestFilter?: (manifest: ManifestCollectionView) => boolean;
|
||||
}
|
||||
|
||||
export class UmbCollectionViewManager extends UmbControllerBase {
|
||||
|
||||
@@ -35,6 +35,5 @@ export const Datetimelocal: Story = {
|
||||
args: {
|
||||
type: 'datetime-local',
|
||||
value: '2023-04-01T10:00:00',
|
||||
displayValue: '',
|
||||
},
|
||||
};
|
||||
|
||||
@@ -29,6 +29,7 @@ export class UmbInputEntityElement extends UUIFormControlMixin(UmbLitElement, ''
|
||||
protected getFormElement() {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
@property({ type: Number })
|
||||
public set min(value: number) {
|
||||
this.#min = value;
|
||||
@@ -125,6 +126,10 @@ export class UmbInputEntityElement extends UUIFormControlMixin(UmbLitElement, ''
|
||||
|
||||
#openPicker() {
|
||||
this.#pickerContext?.openPicker({
|
||||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||
// @ts-ignore
|
||||
// TODO: ignoring this for now to prevent breaking existing functionality.
|
||||
// if we want a very generic input it should be possible to pass in picker config
|
||||
hideTreeRoot: true,
|
||||
});
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { customElement, html, property } from '@umbraco-cms/backoffice/external/lit';
|
||||
import { customElement, html, property, when } from '@umbraco-cms/backoffice/external/lit';
|
||||
import { UmbChangeEvent } from '@umbraco-cms/backoffice/event';
|
||||
import { UmbLitElement } from '@umbraco-cms/backoffice/lit-element';
|
||||
import { UUIFormControlMixin } from '@umbraco-cms/backoffice/external/uui';
|
||||
@@ -19,22 +19,42 @@ export class UmbInputEyeDropperElement extends UUIFormControlMixin(UmbLitElement
|
||||
@property({ type: Boolean })
|
||||
opacity = false;
|
||||
|
||||
@property({ type: Array })
|
||||
swatches: string[] = [];
|
||||
@property({ type: Boolean })
|
||||
showPalette = false;
|
||||
|
||||
//TODO if empty swatches, the color picker still shows the area where they are supposed to be rendered.
|
||||
// BTW in the old backoffice "palette" seemed to be true/false setting, but here its an array.
|
||||
@property({ type: Array })
|
||||
swatches?: string[];
|
||||
|
||||
// HACK: Since `uui-color-picker` doesn't have an option to hide the swatches, we had to get creative.
|
||||
// Based on UUI v1.8.0-rc3, the value of `swatches` must be a falsey value to hide them.
|
||||
// https://github.com/umbraco/Umbraco.UI/blob/v1.8.0-rc.3/packages/uui-color-picker/lib/uui-color-picker.element.ts#L517
|
||||
// However, the object-type for `swatches` is a `string[]` (non-nullable).
|
||||
// https://github.com/umbraco/Umbraco.UI/blob/v1.8.0-rc.3/packages/uui-color-picker/lib/uui-color-picker.element.ts#L157
|
||||
// To do this, we must omit the `.swatches` attribute, otherwise the default swatches can't be used.
|
||||
// So, we've use a `when()` render both configurations. [LK]
|
||||
|
||||
render() {
|
||||
return html`
|
||||
<uui-color-picker
|
||||
label="Eye dropper"
|
||||
.opacity=${this.opacity}
|
||||
.swatches=${this.swatches}
|
||||
.value=${this.value as string}
|
||||
@change=${this.#onChange}>
|
||||
</uui-color-picker>
|
||||
`;
|
||||
const swatches = this.showPalette ? this.swatches : undefined;
|
||||
return when(
|
||||
this.showPalette && !swatches,
|
||||
() => html`
|
||||
<uui-color-picker
|
||||
label="Eye dropper"
|
||||
.opacity=${this.opacity}
|
||||
.value=${this.value as string}
|
||||
@change=${this.#onChange}>
|
||||
</uui-color-picker>
|
||||
`,
|
||||
() => html`
|
||||
<uui-color-picker
|
||||
label="Eye dropper"
|
||||
.opacity=${this.opacity}
|
||||
.swatches=${swatches!}
|
||||
.value=${this.value as string}
|
||||
@change=${this.#onChange}>
|
||||
</uui-color-picker>
|
||||
`,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -22,6 +22,13 @@ export const WithOpacity: Story = {
|
||||
|
||||
export const WithSwatches: Story = {
|
||||
args: {
|
||||
showPalette: true,
|
||||
swatches: ['#000000', '#ffffff', '#ff0000', '#00ff00', '#0000ff'],
|
||||
},
|
||||
};
|
||||
|
||||
export const ShowPalette: Story = {
|
||||
args: {
|
||||
showPalette: true,
|
||||
},
|
||||
};
|
||||
|
||||
@@ -4,6 +4,8 @@ import { UmbChangeEvent } from '@umbraco-cms/backoffice/event';
|
||||
import { UmbLitElement } from '@umbraco-cms/backoffice/lit-element';
|
||||
import type { UUIRadioEvent } from '@umbraco-cms/backoffice/external/uui';
|
||||
|
||||
type UmbRadioButtonItem = { label: string; value: string };
|
||||
|
||||
@customElement('umb-input-radio-button-list')
|
||||
export class UmbInputRadioButtonListElement extends UUIFormControlMixin(UmbLitElement, '') {
|
||||
#value: string = '';
|
||||
@@ -17,7 +19,7 @@ export class UmbInputRadioButtonListElement extends UUIFormControlMixin(UmbLitEl
|
||||
}
|
||||
|
||||
@property({ type: Array })
|
||||
public list: Array<{ label: string; value: string }> = [];
|
||||
public list: Array<UmbRadioButtonItem> = [];
|
||||
|
||||
protected getFormElement() {
|
||||
return undefined;
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import { html, customElement, property } from '@umbraco-cms/backoffice/external/lit';
|
||||
import { UUIFormControlMixin } from '@umbraco-cms/backoffice/external/uui';
|
||||
import { UmbLitElement } from '@umbraco-cms/backoffice/lit-element';
|
||||
import type { UUISliderEvent } from '@umbraco-cms/backoffice/external/uui';
|
||||
import { customElement, html, property } from '@umbraco-cms/backoffice/external/lit';
|
||||
import { UmbChangeEvent } from '@umbraco-cms/backoffice/event';
|
||||
import { UmbLitElement } from '@umbraco-cms/backoffice/lit-element';
|
||||
import { UUIFormControlMixin } from '@umbraco-cms/backoffice/external/uui';
|
||||
import type { UUISliderEvent } from '@umbraco-cms/backoffice/external/uui';
|
||||
|
||||
@customElement('umb-input-slider')
|
||||
export class UmbInputSliderElement extends UUIFormControlMixin(UmbLitElement, '') {
|
||||
@@ -28,9 +28,9 @@ export class UmbInputSliderElement extends UUIFormControlMixin(UmbLitElement, ''
|
||||
return undefined;
|
||||
}
|
||||
|
||||
#onChange(e: UUISliderEvent) {
|
||||
e.stopPropagation();
|
||||
this.value = e.target.value as string;
|
||||
#onChange(event: UUISliderEvent) {
|
||||
event.stopPropagation();
|
||||
this.value = event.target.value as string;
|
||||
this.dispatchEvent(new UmbChangeEvent());
|
||||
}
|
||||
|
||||
@@ -39,20 +39,27 @@ export class UmbInputSliderElement extends UUIFormControlMixin(UmbLitElement, ''
|
||||
}
|
||||
|
||||
#renderSlider() {
|
||||
return html`<uui-slider
|
||||
.min="${this.min}"
|
||||
.max="${this.max}"
|
||||
.step="${this.step}"
|
||||
.value="${this.valueLow.toString()}"
|
||||
@change="${this.#onChange}"></uui-slider>`;
|
||||
return html`
|
||||
<uui-slider
|
||||
.min=${this.min}
|
||||
.max=${this.max}
|
||||
.step=${this.step}
|
||||
.value=${this.valueLow.toString()}
|
||||
@change=${this.#onChange}>
|
||||
</uui-slider>
|
||||
`;
|
||||
}
|
||||
|
||||
#renderRangeSlider() {
|
||||
return html`<uui-range-slider
|
||||
.min="${this.min}"
|
||||
.max="${this.max}"
|
||||
.step="${this.step}"
|
||||
.value="${this.valueLow},${this.valueHigh}"
|
||||
@change="${this.#onChange}"></uui-range-slider>`;
|
||||
return html`
|
||||
<uui-range-slider
|
||||
.min=${this.min}
|
||||
.max=${this.max}
|
||||
.step=${this.step}
|
||||
.value="${this.valueLow},${this.valueHigh}"
|
||||
@change=${this.#onChange}>
|
||||
</uui-range-slider>
|
||||
`;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -23,23 +23,22 @@ export class UmbInputUploadFieldFileElement extends UmbLitElement {
|
||||
label = '';
|
||||
|
||||
#serverUrl = '';
|
||||
#serverUrlPromise;
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
constructor() {
|
||||
super();
|
||||
this.#serverUrlPromise = this.consumeContext(UMB_APP_CONTEXT, (instance) => {
|
||||
this.consumeContext(UMB_APP_CONTEXT, (instance) => {
|
||||
this.#serverUrl = instance.getServerUrl();
|
||||
}).asPromise();
|
||||
}
|
||||
|
||||
protected updated(_changedProperties: PropertyValueMap<any> | Map<PropertyKey, unknown>): void {
|
||||
super.updated(_changedProperties);
|
||||
if (_changedProperties.has('file')) {
|
||||
this.extension = this.file?.name.split('.').pop() || '';
|
||||
this.label = this.file?.name || 'loading...';
|
||||
if (_changedProperties.has('file') && this.file) {
|
||||
this.extension = this.#getExtensionFromMime(this.file.type) ?? '';
|
||||
this.label = this.file.name || 'loading...';
|
||||
}
|
||||
|
||||
if (_changedProperties.has('path')) {
|
||||
@@ -52,8 +51,23 @@ export class UmbInputUploadFieldFileElement extends UmbLitElement {
|
||||
}
|
||||
}
|
||||
|
||||
#getExtensionFromMime(mime: string): string {
|
||||
//TODO Temporary solution.
|
||||
if (!mime) return ''; //folders
|
||||
const extension = mime.split('/')[1];
|
||||
switch (extension) {
|
||||
case 'svg+xml':
|
||||
return 'svg';
|
||||
default:
|
||||
return extension;
|
||||
}
|
||||
}
|
||||
|
||||
#renderLabel() {
|
||||
if (this.path) return html`<a id="label" href=${this.path}>${this.label}</a>`;
|
||||
if (this.path) {
|
||||
// Don't make it a link if it's a temp file upload.
|
||||
return this.file ? this.label : html`<a id="label" href=${this.path} target="_blank">${this.label}</a>`;
|
||||
}
|
||||
|
||||
return html`<span id="label">${this.label}</span>`;
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import type { MediaValueType } from '../../../property-editors/upload-field/property-editor-ui-upload-field.element.js';
|
||||
import type { UmbTemporaryFileModel } from '../../temporary-file/temporary-file-manager.class.js';
|
||||
import { UmbTemporaryFileManager } from '../../temporary-file/temporary-file-manager.class.js';
|
||||
import { TemporaryFileStatus, UmbTemporaryFileManager } from '../../temporary-file/temporary-file-manager.class.js';
|
||||
import { UmbId } from '@umbraco-cms/backoffice/id';
|
||||
import {
|
||||
css,
|
||||
@@ -10,62 +11,41 @@ import {
|
||||
property,
|
||||
query,
|
||||
state,
|
||||
repeat,
|
||||
} from '@umbraco-cms/backoffice/external/lit';
|
||||
import { UUIFormControlMixin } from '@umbraco-cms/backoffice/external/uui';
|
||||
import type { UUIFileDropzoneElement, UUIFileDropzoneEvent } from '@umbraco-cms/backoffice/external/uui';
|
||||
import { UmbLitElement } from '@umbraco-cms/backoffice/lit-element';
|
||||
|
||||
import './input-upload-field-file.element.js';
|
||||
import { UmbChangeEvent } from '@umbraco-cms/backoffice/event';
|
||||
import { UMB_APP_CONTEXT } from '@umbraco-cms/backoffice/app';
|
||||
import './input-upload-field-file.element.js';
|
||||
|
||||
@customElement('umb-input-upload-field')
|
||||
export class UmbInputUploadFieldElement extends UUIFormControlMixin(UmbLitElement, '') {
|
||||
private _keys: Array<string> = [];
|
||||
/**
|
||||
* @description Keys to the files that belong to this upload field.
|
||||
* @type {Array<String>}
|
||||
* @default []
|
||||
*/
|
||||
@property({ type: Array })
|
||||
public set keys(fileKeys: Array<string>) {
|
||||
this._keys = fileKeys;
|
||||
super.value = this._keys.join(',');
|
||||
this.#setFilePaths();
|
||||
export class UmbInputUploadFieldElement extends UmbLitElement {
|
||||
@property({ type: Object })
|
||||
set value(value: MediaValueType) {
|
||||
if (!value?.src) return;
|
||||
this._src = value.src;
|
||||
}
|
||||
public get keys(): Array<string> {
|
||||
return this._keys;
|
||||
get value(): MediaValueType {
|
||||
return !this.temporaryFile ? { src: this._src } : { temporaryFileId: this.temporaryFile.unique };
|
||||
}
|
||||
|
||||
/**
|
||||
* @description Allowed file extensions. If left empty, all are allowed.
|
||||
* @description Allowed file extensions. Allow all if empty.
|
||||
* @type {Array<String>}
|
||||
* @default undefined
|
||||
*/
|
||||
@property({ type: Array })
|
||||
set fileExtensions(value: Array<string>) {
|
||||
set allowedFileExtensions(value: Array<string>) {
|
||||
this.#setExtensions(value);
|
||||
}
|
||||
get fileExtensions(): Array<string> | undefined {
|
||||
get allowedFileExtensions(): Array<string> | undefined {
|
||||
return this._extensions;
|
||||
}
|
||||
|
||||
/**
|
||||
* @description Allows the user to upload multiple files.
|
||||
* @default false
|
||||
* @attr
|
||||
*/
|
||||
@property({ type: Boolean })
|
||||
public multiple = false;
|
||||
@state()
|
||||
public temporaryFile?: UmbTemporaryFileModel;
|
||||
|
||||
@state()
|
||||
private _files: Array<{
|
||||
path: string;
|
||||
unique: string;
|
||||
queueItem?: UmbTemporaryFileModel;
|
||||
file?: File;
|
||||
}> = [];
|
||||
private _src = '';
|
||||
|
||||
@state()
|
||||
private _extensions?: string[];
|
||||
@@ -73,110 +53,36 @@ export class UmbInputUploadFieldElement extends UUIFormControlMixin(UmbLitElemen
|
||||
@query('#dropzone')
|
||||
private _dropzone?: UUIFileDropzoneElement;
|
||||
|
||||
#manager;
|
||||
#serverUrl = '';
|
||||
#serverUrlPromise;
|
||||
#manager = new UmbTemporaryFileManager(this);
|
||||
|
||||
protected getFormElement() {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
this.#manager = new UmbTemporaryFileManager(this);
|
||||
|
||||
/*this.consumeContext(UMB_PROPERTY_DATASET_CONTEXT, async (context) => {
|
||||
this.observe(await context.propertyValueByAlias('umbracoExtension'), (value) => {
|
||||
//const test = value;
|
||||
});
|
||||
});*/
|
||||
|
||||
this.#serverUrlPromise = this.consumeContext(UMB_APP_CONTEXT, (instance) => {
|
||||
this.#serverUrl = instance.getServerUrl();
|
||||
}).asPromise();
|
||||
|
||||
this.observe(this.#manager.queue, (value) => {
|
||||
this.error = !value.length;
|
||||
this._files = this._files.map((file) => {
|
||||
const queueItem = value.find((item) => item.unique === file.unique);
|
||||
if (queueItem) {
|
||||
file.queueItem = queueItem;
|
||||
}
|
||||
return file;
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
async #setFilePaths() {
|
||||
await this.#serverUrlPromise;
|
||||
|
||||
this.keys.forEach((key) => {
|
||||
if (!UmbId.validate(key) && key.startsWith('/')) {
|
||||
this._files.push({
|
||||
path: this.#serverUrl + key,
|
||||
unique: UmbId.new(),
|
||||
});
|
||||
this.requestUpdate();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
#setExtensions(value: Array<string>) {
|
||||
if (!value) {
|
||||
#setExtensions(extensions: Array<string>) {
|
||||
if (!extensions?.length) {
|
||||
this._extensions = undefined;
|
||||
return;
|
||||
}
|
||||
|
||||
// TODO: The dropzone uui component does not support file extensions without a dot. Remove this when it does.
|
||||
this._extensions = value?.map((extension) => {
|
||||
return `.${extension}`;
|
||||
});
|
||||
this._extensions = extensions?.map((extension) => `.${extension}`);
|
||||
}
|
||||
|
||||
#onUpload(e: UUIFileDropzoneEvent) {
|
||||
const files: File[] = e.detail.files;
|
||||
async #onUpload(e: UUIFileDropzoneEvent) {
|
||||
//Property Editor for Upload field will always only have one file.
|
||||
const item: UmbTemporaryFileModel = {
|
||||
unique: UmbId.new(),
|
||||
file: e.detail.files[0],
|
||||
};
|
||||
const upload = this.#manager.uploadOne(item);
|
||||
|
||||
if (!files?.length) return;
|
||||
const reader = new FileReader();
|
||||
reader.onload = () => {
|
||||
this._src = reader.result as string;
|
||||
};
|
||||
reader.readAsDataURL(item.file);
|
||||
|
||||
// TODO: Should we validate the mimetype some how?
|
||||
this.#setFiles(files);
|
||||
}
|
||||
|
||||
#setFiles(files: File[]) {
|
||||
const items = files.map(
|
||||
(file): UmbTemporaryFileModel => ({
|
||||
unique: UmbId.new(),
|
||||
file,
|
||||
status: 'waiting',
|
||||
}),
|
||||
);
|
||||
this.#manager.upload(items);
|
||||
|
||||
this.keys = items.map((item) => item.unique);
|
||||
this.value = this.keys.join(',');
|
||||
|
||||
this.dispatchEvent(new UmbChangeEvent());
|
||||
|
||||
// Read files to get their paths and add them to the file paths array.
|
||||
items.forEach((item) => {
|
||||
this._files.push({
|
||||
path: '',
|
||||
unique: item.unique,
|
||||
queueItem: item,
|
||||
file: item.file,
|
||||
});
|
||||
const reader = new FileReader();
|
||||
reader.onload = () => {
|
||||
this._files = this._files.map((file) => {
|
||||
if (file.unique === item.unique) {
|
||||
file.path = reader.result as string;
|
||||
}
|
||||
return file;
|
||||
});
|
||||
this.requestUpdate();
|
||||
};
|
||||
reader.readAsDataURL(item.file);
|
||||
});
|
||||
const uploaded = await upload;
|
||||
if (uploaded.status === TemporaryFileStatus.SUCCESS) {
|
||||
this.temporaryFile = { unique: item.unique, file: item.file };
|
||||
this.dispatchEvent(new UmbChangeEvent());
|
||||
}
|
||||
}
|
||||
|
||||
#handleBrowse() {
|
||||
@@ -185,67 +91,53 @@ export class UmbInputUploadFieldElement extends UUIFormControlMixin(UmbLitElemen
|
||||
}
|
||||
|
||||
render() {
|
||||
return html`
|
||||
<div id="wrapper">${this.#renderFiles()}</div>
|
||||
${this.#renderDropzone()} ${this.#renderButtonRemove()}
|
||||
`;
|
||||
return html`${this._src ? this.#renderFile(this._src, this.temporaryFile?.file) : this.#renderDropzone()}`;
|
||||
}
|
||||
|
||||
//TODO When the property editor gets saved, it seems that the property editor gets the file path from the server rather than key/id.
|
||||
// This however does not work when there is multiple files. Can the server not handle multiple files uploaded into one property editor?
|
||||
#renderDropzone() {
|
||||
if (!this.multiple && this._files.length) return nothing;
|
||||
|
||||
return html`
|
||||
<uui-file-dropzone
|
||||
id="dropzone"
|
||||
label="dropzone"
|
||||
@change="${this.#onUpload}"
|
||||
accept="${ifDefined(this._extensions?.join(', '))}"
|
||||
?multiple="${this.multiple}">
|
||||
accept="${ifDefined(this._extensions?.join(', '))}">
|
||||
<uui-button label=${this.localize.term('media_clickToUpload')} @click="${this.#handleBrowse}"></uui-button>
|
||||
</uui-file-dropzone>
|
||||
`;
|
||||
}
|
||||
|
||||
#renderFiles() {
|
||||
return repeat(
|
||||
this._files,
|
||||
(path) => path,
|
||||
(path) => this.#renderFile(path),
|
||||
);
|
||||
}
|
||||
|
||||
#renderFile(file: { path: string; unique: string; queueItem?: UmbTemporaryFileModel; file?: File }) {
|
||||
// TODO: Get the mime type from the server and use that to determine the file type.
|
||||
const type = this.#getFileTypeFromPath(file.path);
|
||||
#renderFile(src: string, file?: File) {
|
||||
const extension = this.#getFileExtensionFromPath(src);
|
||||
|
||||
return html`
|
||||
<div style="position:relative; display: flex; width: fit-content; max-width: 100%">
|
||||
${getElementTemplate()}
|
||||
${file.queueItem?.status === 'waiting' ? html`<umb-temporary-file-badge></umb-temporary-file-badge>` : nothing}
|
||||
<div id="wrapper">
|
||||
<div style="position:relative; display: flex; width: fit-content; max-width: 100%">
|
||||
${getElementTemplate()}
|
||||
${this.temporaryFile?.status === TemporaryFileStatus.WAITING
|
||||
? html`<umb-temporary-file-badge></umb-temporary-file-badge>`
|
||||
: nothing}
|
||||
</div>
|
||||
</div>
|
||||
${this.#renderButtonRemove()}
|
||||
`;
|
||||
|
||||
function getElementTemplate() {
|
||||
switch (type) {
|
||||
switch (extension) {
|
||||
case 'audio':
|
||||
return html`<umb-input-upload-field-audio .path=${file.path}></umb-input-upload-field-audio>`;
|
||||
return html`<umb-input-upload-field-audio .path=${src}></umb-input-upload-field-audio>`;
|
||||
case 'video':
|
||||
return html`<umb-input-upload-field-video .path=${file.path}></umb-input-upload-field-video>`;
|
||||
return html`<umb-input-upload-field-video .path=${src}></umb-input-upload-field-video>`;
|
||||
case 'image':
|
||||
return html`<umb-input-upload-field-image .path=${file.path}></umb-input-upload-field-image>`;
|
||||
return html`<umb-input-upload-field-image .path=${src}></umb-input-upload-field-image>`;
|
||||
case 'svg':
|
||||
return html`<umb-input-upload-field-svg .path=${file.path}></umb-input-upload-field-svg>`;
|
||||
case 'file':
|
||||
return html`<umb-input-upload-field-file
|
||||
.path=${file.path}
|
||||
.file=${file.file as any}></umb-input-upload-field-file>`;
|
||||
return html`<umb-input-upload-field-svg .path=${src}></umb-input-upload-field-svg>`;
|
||||
default:
|
||||
return html`<umb-input-upload-field-file .path=${src} .file=${file}></umb-input-upload-field-file>`;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#getFileTypeFromPath(path: string): 'audio' | 'video' | 'image' | 'svg' | 'file' {
|
||||
#getFileExtensionFromPath(path: string): 'audio' | 'video' | 'image' | 'svg' | 'file' {
|
||||
// Extract the MIME type from the data URL
|
||||
if (path.startsWith('data:')) {
|
||||
const mimeType = path.substring(5, path.indexOf(';'));
|
||||
@@ -267,20 +159,14 @@ export class UmbInputUploadFieldElement extends UUIFormControlMixin(UmbLitElemen
|
||||
}
|
||||
|
||||
#renderButtonRemove() {
|
||||
if (!this._files.length) return;
|
||||
|
||||
return html`<uui-button compact @click=${this.#handleRemove} label=${this.localize.term('content_uploadClear')}>
|
||||
<uui-icon name="icon-trash"></uui-icon>${this.localize.term('content_uploadClear')}
|
||||
</uui-button>`;
|
||||
}
|
||||
|
||||
#handleRemove() {
|
||||
const uniques = this._files.map((file) => file.unique);
|
||||
this.#manager.remove(uniques);
|
||||
this._files = [];
|
||||
this.value = '';
|
||||
this.keys = [];
|
||||
|
||||
this._src = '';
|
||||
this.temporaryFile = undefined;
|
||||
this.dispatchEvent(new UmbChangeEvent());
|
||||
}
|
||||
|
||||
@@ -297,6 +183,7 @@ export class UmbInputUploadFieldElement extends UUIFormControlMixin(UmbLitElemen
|
||||
gap: var(--uui-size-space-4);
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
#wrapper:has(umb-input-upload-field-file) {
|
||||
padding: var(--uui-size-space-4);
|
||||
border: 1px solid var(--uui-color-border);
|
||||
|
||||
@@ -11,7 +11,5 @@ export default meta;
|
||||
type Story = StoryObj<UmbInputUploadFieldElement>;
|
||||
|
||||
export const Overview: Story = {
|
||||
args: {
|
||||
multiple: false,
|
||||
},
|
||||
args: {},
|
||||
};
|
||||
|
||||
@@ -102,7 +102,7 @@ export class UmbMultipleColorPickerItemInputElement extends UUIFormControlMixin(
|
||||
this.dispatchEvent(new UmbInputEvent());
|
||||
}
|
||||
|
||||
#onColorInput(event: InputEvent) {
|
||||
#onColorChange(event: Event) {
|
||||
event.stopPropagation();
|
||||
this.value = this._colorPicker.value;
|
||||
this.dispatchEvent(new UmbChangeEvent());
|
||||
@@ -153,7 +153,7 @@ export class UmbMultipleColorPickerItemInputElement extends UUIFormControlMixin(
|
||||
value=${this._valueHex}
|
||||
@click=${this.#onColorClick}></uui-color-swatch>
|
||||
</uui-input>
|
||||
<input aria-hidden="${true}" type="color" id="color" value=${this.value} @input=${this.#onColorInput} />
|
||||
<input aria-hidden="${true}" type="color" id="color" value=${this.value} @change=${this.#onColorChange} />
|
||||
</div>
|
||||
${when(
|
||||
this.showLabels,
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { UmbLitElement } from "@umbraco-cms/backoffice/lit-element";
|
||||
import { customElement, html, css, property, classMap } from "@umbraco-cms/backoffice/external/lit";
|
||||
import { UmbLitElement } from '@umbraco-cms/backoffice/lit-element';
|
||||
import { classMap, customElement, css, html, property } from '@umbraco-cms/backoffice/external/lit';
|
||||
|
||||
/**
|
||||
* @element umb-stack
|
||||
@@ -7,72 +7,73 @@ import { customElement, html, css, property, classMap } from "@umbraco-cms/backo
|
||||
* @extends LitElement
|
||||
*/
|
||||
@customElement('umb-stack')
|
||||
export class UmbStackElement extends UmbLitElement
|
||||
{
|
||||
/**
|
||||
* Look
|
||||
* @type {String}
|
||||
* @memberof UmbStackElement
|
||||
*/
|
||||
@property({ type:String })
|
||||
look: 'compact' | 'default' = 'default';
|
||||
export class UmbStackElement extends UmbLitElement {
|
||||
/**
|
||||
* Look
|
||||
* @type {String}
|
||||
* @memberof UmbStackElement
|
||||
*/
|
||||
@property({ type: String })
|
||||
look: 'compact' | 'default' = 'default';
|
||||
|
||||
/**
|
||||
* Divide
|
||||
* @type {Boolean}
|
||||
* @memberof UmbStackElement
|
||||
*/
|
||||
@property({ type:Boolean })
|
||||
divide: boolean = false;
|
||||
/**
|
||||
* Divide
|
||||
* @type {Boolean}
|
||||
* @memberof UmbStackElement
|
||||
*/
|
||||
@property({ type: Boolean })
|
||||
divide: boolean = false;
|
||||
|
||||
render() {
|
||||
return html`<div class=${classMap({ divide: this.divide, compact: this.look === 'compact' })}>
|
||||
<slot></slot>
|
||||
</div>`;
|
||||
}
|
||||
render() {
|
||||
return html`
|
||||
<div class=${classMap({ divide: this.divide, compact: this.look === 'compact' })}>
|
||||
<slot></slot>
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
|
||||
static styles = [
|
||||
css`
|
||||
div {
|
||||
display: block;
|
||||
position: relative;
|
||||
}
|
||||
static styles = [
|
||||
css`
|
||||
div {
|
||||
display: block;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
::slotted(*) {
|
||||
position: relative;
|
||||
margin-top: var(--uui-size-space-6);
|
||||
}
|
||||
::slotted(*) {
|
||||
position: relative;
|
||||
margin-top: var(--uui-size-space-6);
|
||||
}
|
||||
|
||||
.divide ::slotted(*)::before {
|
||||
content: '';
|
||||
position: absolute;
|
||||
top: calc((var(--uui-size-space-6) / 2) * -1);
|
||||
height: 0;
|
||||
width: 100%;
|
||||
border-top: solid 1px var(--uui-color-divider-standalone);
|
||||
}
|
||||
.divide ::slotted(*)::before {
|
||||
content: '';
|
||||
position: absolute;
|
||||
top: calc((var(--uui-size-space-6) / 2) * -1);
|
||||
height: 0;
|
||||
width: 100%;
|
||||
border-top: solid 1px var(--uui-color-divider-standalone);
|
||||
}
|
||||
|
||||
::slotted(*:first-child) {
|
||||
margin-top: 0;
|
||||
}
|
||||
::slotted(*:first-child) {
|
||||
margin-top: 0;
|
||||
}
|
||||
|
||||
.divide ::slotted(*:first-child)::before {
|
||||
display: none;
|
||||
}
|
||||
.divide ::slotted(*:first-child)::before {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.compact ::slotted(*) {
|
||||
margin-top: var(--uui-size-space-3);
|
||||
}
|
||||
.compact ::slotted(*) {
|
||||
margin-top: var(--uui-size-space-3);
|
||||
}
|
||||
|
||||
.compact ::slotted(*:first-child) {
|
||||
margin-top: 0;
|
||||
}
|
||||
.compact ::slotted(*:first-child) {
|
||||
margin-top: 0;
|
||||
}
|
||||
|
||||
.compact.divide ::slotted(*)::before {
|
||||
display: none;
|
||||
}
|
||||
`
|
||||
];
|
||||
.compact.divide ::slotted(*)::before {
|
||||
display: none;
|
||||
}
|
||||
`,
|
||||
];
|
||||
}
|
||||
|
||||
export default UmbStackElement;
|
||||
|
||||
@@ -1,23 +1,20 @@
|
||||
import { UmbTextStyles } from '@umbraco-cms/backoffice/style';
|
||||
import type { TemplateResult } from '@umbraco-cms/backoffice/external/lit';
|
||||
import { css, html, nothing, customElement, property, state, repeat } from '@umbraco-cms/backoffice/external/lit';
|
||||
|
||||
import type { DebugContextData, DebugContextItemData } from '@umbraco-cms/backoffice/context-api';
|
||||
import { css, customElement, html, map, nothing, property, state, when } from '@umbraco-cms/backoffice/external/lit';
|
||||
import { contextData, UmbContextDebugRequest } from '@umbraco-cms/backoffice/context-api';
|
||||
import { UmbLitElement } from '@umbraco-cms/backoffice/lit-element';
|
||||
import type { UmbModalManagerContext } from '@umbraco-cms/backoffice/modal';
|
||||
import { UMB_CONTEXT_DEBUGGER_MODAL, UMB_MODAL_MANAGER_CONTEXT } from '@umbraco-cms/backoffice/modal';
|
||||
import type { UmbDebugContextData, UmbDebugContextItemData } from '@umbraco-cms/backoffice/context-api';
|
||||
import type { UmbModalManagerContext } from '@umbraco-cms/backoffice/modal';
|
||||
|
||||
@customElement('umb-debug')
|
||||
export class UmbDebugElement extends UmbLitElement {
|
||||
@property({ reflect: true, type: Boolean })
|
||||
@property({ type: Boolean })
|
||||
visible = false;
|
||||
|
||||
@property({ reflect: true, type: Boolean })
|
||||
@property({ type: Boolean })
|
||||
dialog = false;
|
||||
|
||||
@state()
|
||||
contextData = Array<DebugContextData>();
|
||||
private _contextData = Array<UmbDebugContextData>();
|
||||
|
||||
@state()
|
||||
private _debugPaneOpen = false;
|
||||
@@ -31,160 +28,144 @@ export class UmbDebugElement extends UmbLitElement {
|
||||
});
|
||||
}
|
||||
|
||||
render() {
|
||||
if (this.visible) {
|
||||
return this.dialog ? this._renderDialog() : this._renderPanel();
|
||||
} else {
|
||||
return nothing;
|
||||
}
|
||||
}
|
||||
|
||||
private _update() {
|
||||
// Dispatch it
|
||||
#update() {
|
||||
this.dispatchEvent(
|
||||
new UmbContextDebugRequest((contexts: Map<any, any>) => {
|
||||
// The Contexts are collected
|
||||
// When travelling up through the DOM from this element
|
||||
// to the root of <umb-app> which then uses the callback prop
|
||||
// of the this event tha has been raised to assign the contexts
|
||||
// of this event that has been raised to assign the contexts
|
||||
// back to this property of the WebComponent
|
||||
|
||||
// Massage the data into a simplier array of objects
|
||||
// From a function in the context-api '
|
||||
this.contextData = contextData(contexts);
|
||||
this.requestUpdate('contextData');
|
||||
// from a function in the context-api.
|
||||
this._contextData = contextData(contexts);
|
||||
this.requestUpdate('_contextData');
|
||||
}),
|
||||
);
|
||||
}
|
||||
|
||||
private _toggleDebugPane() {
|
||||
#toggleDebugPane() {
|
||||
this._debugPaneOpen = !this._debugPaneOpen;
|
||||
if (this._debugPaneOpen) {
|
||||
this._update();
|
||||
this.#update();
|
||||
}
|
||||
}
|
||||
|
||||
private _openDialog() {
|
||||
|
||||
this._update();
|
||||
#openDialog() {
|
||||
this.#update();
|
||||
|
||||
this._modalContext?.open(this, UMB_CONTEXT_DEBUGGER_MODAL, {
|
||||
data: {
|
||||
content: html`${this._renderContextAliases()}`,
|
||||
content: this.#renderContextAliases(),
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
private _renderDialog() {
|
||||
render() {
|
||||
if (!this.visible) return nothing;
|
||||
return this.dialog ? this.#renderDialog() : this.#renderPanel();
|
||||
}
|
||||
|
||||
#renderDialog() {
|
||||
return html`
|
||||
<div id="container">
|
||||
<uui-badge color="danger" look="primary" attention @click="${this._openDialog}">
|
||||
<uui-icon name="icon-bug"></uui-icon> Debug
|
||||
</uui-badge>
|
||||
</div>`;
|
||||
}
|
||||
|
||||
private _renderPanel() {
|
||||
return html` <div id="container">
|
||||
<uui-button color="danger" look="primary" @click="${this._toggleDebugPane}">
|
||||
<uui-icon name="icon-bug"></uui-icon>
|
||||
Debug
|
||||
</uui-button>
|
||||
|
||||
<div class="events ${this._debugPaneOpen ? 'open' : ''}">
|
||||
<div>
|
||||
<ul>
|
||||
${this._renderContextAliases()}
|
||||
</ul>
|
||||
</div>
|
||||
<div>
|
||||
<uui-badge color="danger" look="primary" @click=${this.#openDialog}>
|
||||
<uui-icon name="icon-bug"></uui-icon>
|
||||
<span>Debug</span>
|
||||
</uui-badge>
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
|
||||
#renderPanel() {
|
||||
return html`
|
||||
<div id="container">
|
||||
<uui-button color="danger" look="primary" @click=${this.#toggleDebugPane}>
|
||||
<uui-icon name="icon-bug"></uui-icon>
|
||||
<span>Debug</span>
|
||||
</uui-button>
|
||||
${when(this._debugPaneOpen, () => this.#renderContextAliases())}
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
|
||||
#renderContextAliases() {
|
||||
return html`<div class="events">
|
||||
${map(this._contextData, (context) => {
|
||||
return html`
|
||||
<details>
|
||||
<summary><strong>${context.alias}</strong></summary>
|
||||
${this.#renderInstance(context.data)}
|
||||
</details>
|
||||
`;
|
||||
})}
|
||||
</div>`;
|
||||
}
|
||||
|
||||
private _renderContextAliases() {
|
||||
return repeat(
|
||||
this.contextData,
|
||||
(contextData) => contextData.alias,
|
||||
(contextData) => {
|
||||
return html` <li>
|
||||
Context: <strong>${contextData.alias}</strong>
|
||||
<em>(${contextData.type})</em>
|
||||
<ul>
|
||||
${this._renderInstance(contextData.data)}
|
||||
</ul>
|
||||
</li>`;
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
private _renderInstance(instance: DebugContextItemData) {
|
||||
const instanceTemplates: TemplateResult[] = [];
|
||||
|
||||
if (instance.type === 'function') {
|
||||
return instanceTemplates.push(html`<li>Callable Function</li>`);
|
||||
} else if (instance.type === 'object') {
|
||||
if (instance.methods?.length) {
|
||||
instanceTemplates.push(html`
|
||||
<li>
|
||||
<strong>Methods</strong>
|
||||
<ul>
|
||||
${instance.methods?.map((methodName) => html`<li>${methodName}</li>`)}
|
||||
</ul>
|
||||
</li>
|
||||
`);
|
||||
#renderInstance(instance: UmbDebugContextItemData) {
|
||||
switch (instance.type) {
|
||||
case 'function': {
|
||||
return html`<h3>Callable Function</h3>`;
|
||||
}
|
||||
|
||||
const props: TemplateResult[] = [];
|
||||
instance.properties?.forEach((property) => {
|
||||
switch (property.type) {
|
||||
case 'string':
|
||||
case 'number':
|
||||
case 'boolean':
|
||||
case 'object':
|
||||
props.push(html`<li>${property.key} <em>(${property.type})</em> = ${property.value}</li>`);
|
||||
break;
|
||||
case 'object': {
|
||||
return html`
|
||||
<details>
|
||||
<summary>Methods</summary>
|
||||
<ul>
|
||||
${map(instance.methods, (methodName) => html`<li>${methodName}</li>`)}
|
||||
</ul>
|
||||
</details>
|
||||
|
||||
default:
|
||||
props.push(html`<li>${property.key} <em>(${property.type})</em></li>`);
|
||||
break;
|
||||
}
|
||||
});
|
||||
<details>
|
||||
<summary>Properties</summary>
|
||||
<ul>
|
||||
${map(instance.properties, (property) => {
|
||||
switch (property.type) {
|
||||
case 'string':
|
||||
case 'number':
|
||||
case 'boolean':
|
||||
case 'object':
|
||||
return html`<li>${property.key} <em>(${property.type})</em> = ${property.value}</li>`;
|
||||
|
||||
instanceTemplates.push(html`
|
||||
<li>
|
||||
<strong>Properties</strong>
|
||||
<ul>
|
||||
${props}
|
||||
</ul>
|
||||
</li>
|
||||
`);
|
||||
} else if (instance.type === 'primitive') {
|
||||
instanceTemplates.push(html`<li>Context is a primitive with value: ${instance.value}</li>`);
|
||||
default:
|
||||
return html`<li>${property.key} <em>(${property.type})</em></li>`;
|
||||
}
|
||||
})}
|
||||
</ul>
|
||||
</details>
|
||||
`;
|
||||
}
|
||||
|
||||
case 'primitive': {
|
||||
return html`<p>Context is a primitive with value: ${instance.value}</p>`;
|
||||
}
|
||||
|
||||
default: {
|
||||
return html`<p>Unknown type: ${instance.type}</p>`;
|
||||
}
|
||||
}
|
||||
|
||||
return instanceTemplates;
|
||||
}
|
||||
|
||||
static styles = [
|
||||
UmbTextStyles,
|
||||
css`
|
||||
:host {
|
||||
float: right;
|
||||
font-family: monospace;
|
||||
position: relative;
|
||||
z-index: 10000;
|
||||
}
|
||||
|
||||
#container {
|
||||
display: block;
|
||||
font-family: monospace;
|
||||
|
||||
z-index: 10000;
|
||||
|
||||
position: relative;
|
||||
width: 100%;
|
||||
padding: 10px 0;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: flex-end;
|
||||
}
|
||||
|
||||
uui-badge {
|
||||
cursor: pointer;
|
||||
gap: 0.5rem;
|
||||
}
|
||||
|
||||
uui-icon {
|
||||
@@ -194,22 +175,19 @@ export class UmbDebugElement extends UmbLitElement {
|
||||
.events {
|
||||
background-color: var(--uui-color-danger);
|
||||
color: var(--uui-color-selected-contrast);
|
||||
max-height: 0;
|
||||
transition: max-height 0.25s ease-out;
|
||||
overflow: hidden;
|
||||
padding: 1rem;
|
||||
}
|
||||
|
||||
.events.open {
|
||||
max-height: 500px;
|
||||
overflow: auto;
|
||||
summary {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.events > div {
|
||||
padding: 10px;
|
||||
details > details {
|
||||
margin-left: 1rem;
|
||||
}
|
||||
|
||||
h4 {
|
||||
margin: 0;
|
||||
ul {
|
||||
margin-top: 0;
|
||||
}
|
||||
`,
|
||||
];
|
||||
|
||||
@@ -1,66 +1,34 @@
|
||||
import { css, html, customElement } from '@umbraco-cms/backoffice/external/lit';
|
||||
import { UmbTextStyles } from '@umbraco-cms/backoffice/style';
|
||||
import type { UmbContextDebuggerModalData} from '@umbraco-cms/backoffice/modal';
|
||||
import { css, customElement, html } from '@umbraco-cms/backoffice/external/lit';
|
||||
import { UmbModalBaseElement } from '@umbraco-cms/backoffice/modal';
|
||||
import { UmbTextStyles } from '@umbraco-cms/backoffice/style';
|
||||
import type { UmbContextDebuggerModalData } from '@umbraco-cms/backoffice/modal';
|
||||
|
||||
@customElement('umb-context-debugger-modal')
|
||||
export default class UmbContextDebuggerModalElement extends UmbModalBaseElement<UmbContextDebuggerModalData> {
|
||||
private _handleClose() {
|
||||
#close() {
|
||||
this.modalContext?.reject();
|
||||
}
|
||||
|
||||
render() {
|
||||
return html`
|
||||
<uui-dialog-layout>
|
||||
<span slot="headline"> <uui-icon name="icon-bug"></uui-icon> Debug: Contexts </span>
|
||||
<uui-scroll-container id="field-settings">
|
||||
${this.data?.content}
|
||||
</uui-scroll-container>
|
||||
<uui-button slot="actions" look="primary" label="Close sidebar" @click="${this._handleClose}">Close</uui-button>
|
||||
</uui-dialog-layout>
|
||||
<umb-body-layout headline="Debug: Contexts">
|
||||
<div id="main">${this.data?.content}</div>
|
||||
<div slot="actions">
|
||||
<uui-button @click=${this.#close} label=${this.localize.term('general_close')}></uui-button>
|
||||
</div>
|
||||
</umb-body-layout>
|
||||
`;
|
||||
}
|
||||
|
||||
static styles = [
|
||||
UmbTextStyles,
|
||||
css`
|
||||
uui-dialog-layout {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
height: 100%;
|
||||
|
||||
padding: var(--uui-size-space-5);
|
||||
box-sizing: border-box;
|
||||
summary {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
uui-scroll-container {
|
||||
overflow-y: scroll;
|
||||
max-height: 100%;
|
||||
min-height: 0;
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
uui-icon {
|
||||
vertical-align: text-top;
|
||||
color: var(--uui-color-danger);
|
||||
}
|
||||
|
||||
.context {
|
||||
padding: 15px 0;
|
||||
border-bottom: 1px solid var(--uui-color-danger-emphasis);
|
||||
}
|
||||
|
||||
h3 {
|
||||
margin-top: 0;
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
h3 > span {
|
||||
border-radius: var(--uui-size-4);
|
||||
background-color: var(--uui-color-danger);
|
||||
color: var(--uui-color-danger-contrast);
|
||||
padding: 8px;
|
||||
font-size: 12px;
|
||||
details > details {
|
||||
margin-left: 1rem;
|
||||
}
|
||||
|
||||
ul {
|
||||
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 226 KiB After Width: | Height: | Size: 116 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 174 KiB After Width: | Height: | Size: 91 KiB |
@@ -1,9 +1,8 @@
|
||||
import { UMB_DUPLICATE_TO_MODAL_ALIAS } from './constants.js';
|
||||
import type { UmbEntityModel } from '@umbraco-cms/backoffice/entity';
|
||||
import { UmbModalToken } from '@umbraco-cms/backoffice/modal';
|
||||
|
||||
export interface UmbDuplicateToModalData {
|
||||
unique: string | null;
|
||||
entityType: string;
|
||||
export interface UmbDuplicateToModalData extends UmbEntityModel {
|
||||
treeAlias: string;
|
||||
}
|
||||
|
||||
|
||||
@@ -5,7 +5,7 @@ import { UmbTextStyles } from '@umbraco-cms/backoffice/style';
|
||||
import { UmbModalBaseElement } from '@umbraco-cms/backoffice/modal';
|
||||
import { UmbSorterController } from '@umbraco-cms/backoffice/sorter';
|
||||
import { createExtensionApiByAlias } from '@umbraco-cms/backoffice/extension-registry';
|
||||
import type { UmbTreeRepository, UmbUniqueTreeItemModel } from '@umbraco-cms/backoffice/tree';
|
||||
import type { UmbTreeRepository, UmbTreeItemModel } from '@umbraco-cms/backoffice/tree';
|
||||
import { UmbPaginationManager } from '@umbraco-cms/backoffice/utils';
|
||||
import { observeMultiple } from '@umbraco-cms/backoffice/observable-api';
|
||||
|
||||
@@ -17,7 +17,7 @@ export class UmbSortChildrenOfModalElement extends UmbModalBaseElement<
|
||||
UmbSortChildrenOfModalValue
|
||||
> {
|
||||
@state()
|
||||
_children: Array<UmbUniqueTreeItemModel> = [];
|
||||
_children: Array<UmbTreeItemModel> = [];
|
||||
|
||||
@state()
|
||||
_currentPage = 1;
|
||||
@@ -27,7 +27,7 @@ export class UmbSortChildrenOfModalElement extends UmbModalBaseElement<
|
||||
|
||||
#pagination = new UmbPaginationManager();
|
||||
#sortedUniques = new Set<string>();
|
||||
#sorter?: UmbSorterController<UmbUniqueTreeItemModel>;
|
||||
#sorter?: UmbSorterController<UmbTreeItemModel>;
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
@@ -52,13 +52,16 @@ export class UmbSortChildrenOfModalElement extends UmbModalBaseElement<
|
||||
if (!this.data?.unique === undefined) throw new Error('unique is required');
|
||||
if (!this.data?.treeRepositoryAlias) throw new Error('treeRepositoryAlias is required');
|
||||
|
||||
const treeRepository = await createExtensionApiByAlias<UmbTreeRepository<UmbUniqueTreeItemModel>>(
|
||||
const treeRepository = await createExtensionApiByAlias<UmbTreeRepository<UmbTreeItemModel>>(
|
||||
this,
|
||||
this.data.treeRepositoryAlias,
|
||||
);
|
||||
|
||||
const { data } = await treeRepository.requestTreeItemsOf({
|
||||
parentUnique: this.data.unique,
|
||||
parent: {
|
||||
unique: this.data.unique,
|
||||
entityType: this.data.entityType,
|
||||
},
|
||||
skip: this.#pagination.getSkip(),
|
||||
take: this.#pagination.getPageSize(),
|
||||
});
|
||||
@@ -77,7 +80,7 @@ export class UmbSortChildrenOfModalElement extends UmbModalBaseElement<
|
||||
#initSorter() {
|
||||
if (this.#sorter) return;
|
||||
|
||||
this.#sorter = new UmbSorterController<UmbUniqueTreeItemModel>(this, {
|
||||
this.#sorter = new UmbSorterController<UmbTreeItemModel>(this, {
|
||||
getUniqueOfElement: (element) => {
|
||||
return element.dataset.unique;
|
||||
},
|
||||
@@ -174,7 +177,7 @@ export class UmbSortChildrenOfModalElement extends UmbModalBaseElement<
|
||||
`;
|
||||
}
|
||||
|
||||
#renderChild(item: UmbUniqueTreeItemModel) {
|
||||
#renderChild(item: UmbTreeItemModel) {
|
||||
return html`<uui-ref-node .name=${item.name} data-unique=${item.unique}></uui-ref-node>`;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,9 +1,8 @@
|
||||
import { UMB_SORT_CHILDREN_OF_MODAL_ALIAS } from './constants.js';
|
||||
import type { UmbEntityModel } from '@umbraco-cms/backoffice/entity';
|
||||
import { UmbModalToken } from '@umbraco-cms/backoffice/modal';
|
||||
|
||||
export interface UmbSortChildrenOfModalData {
|
||||
unique: string | null;
|
||||
entityType: string;
|
||||
export interface UmbSortChildrenOfModalData extends UmbEntityModel {
|
||||
treeRepositoryAlias: string;
|
||||
sortChildrenOfRepositoryAlias: string;
|
||||
}
|
||||
|
||||
@@ -1,9 +1,7 @@
|
||||
import { UmbControllerEvent } from '@umbraco-cms/backoffice/controller-api';
|
||||
import type { UmbEntityModel } from '@umbraco-cms/backoffice/entity';
|
||||
|
||||
export interface UmbEntityActionEventArgs {
|
||||
unique: string | null;
|
||||
entityType: string;
|
||||
}
|
||||
export interface UmbEntityActionEventArgs extends UmbEntityModel {}
|
||||
|
||||
export class UmbEntityActionEvent extends UmbControllerEvent {
|
||||
#args: UmbEntityActionEventArgs;
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
export interface UmbEntityActionArgs<MetaArgsType> {
|
||||
entityType: string;
|
||||
unique: string | null;
|
||||
import type { UmbEntityModel } from '@umbraco-cms/backoffice/entity';
|
||||
|
||||
export interface UmbEntityActionArgs<MetaArgsType> extends UmbEntityModel {
|
||||
meta: MetaArgsType;
|
||||
}
|
||||
|
||||
@@ -1,2 +1,3 @@
|
||||
export { UMB_ENTITY_CONTEXT } from './entity.context-token.js';
|
||||
export { UmbEntityContext } from './entity.context.js';
|
||||
export * from './types.js';
|
||||
|
||||
@@ -0,0 +1,6 @@
|
||||
export type UmbEntityUnique = string | null;
|
||||
|
||||
export interface UmbEntityModel {
|
||||
unique: UmbEntityUnique;
|
||||
entityType: string;
|
||||
}
|
||||
@@ -1,10 +1,10 @@
|
||||
import type { UmbTreeItemModelBase } from '../../tree/types.js';
|
||||
import type { UmbTreeItemModel } from '../../tree/types.js';
|
||||
import type { UmbTreeItemContext } from '../../tree/tree-item/index.js';
|
||||
import type { UmbControllerHostElement } from '@umbraco-cms/backoffice/controller-api';
|
||||
import type { ManifestElementAndApi } from '@umbraco-cms/backoffice/extension-api';
|
||||
|
||||
export interface ManifestTreeItem
|
||||
extends ManifestElementAndApi<UmbControllerHostElement, UmbTreeItemContext<UmbTreeItemModelBase>> {
|
||||
extends ManifestElementAndApi<UmbControllerHostElement, UmbTreeItemContext<UmbTreeItemModel>> {
|
||||
type: 'treeItem';
|
||||
forEntityTypes: Array<string>;
|
||||
}
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
import type { UmbStructureItemModel } from './types.js';
|
||||
import type { UmbTreeRepository, UmbUniqueTreeItemModel, UmbUniqueTreeRootModel } from '@umbraco-cms/backoffice/tree';
|
||||
import type { UmbTreeRepository, UmbTreeItemModel, UmbTreeRootModel } from '@umbraco-cms/backoffice/tree';
|
||||
import { createExtensionApiByAlias } from '@umbraco-cms/backoffice/extension-registry';
|
||||
import { UmbContextBase } from '@umbraco-cms/backoffice/class-api';
|
||||
import { UMB_WORKSPACE_CONTEXT } from '@umbraco-cms/backoffice/workspace';
|
||||
import { UmbArrayState } from '@umbraco-cms/backoffice/observable-api';
|
||||
import { UmbArrayState, UmbObjectState } from '@umbraco-cms/backoffice/observable-api';
|
||||
import type { UmbControllerHost } from '@umbraco-cms/backoffice/controller-api';
|
||||
|
||||
interface UmbMenuTreeStructureWorkspaceContextBaseArgs {
|
||||
@@ -17,6 +17,9 @@ export abstract class UmbMenuTreeStructureWorkspaceContextBase extends UmbContex
|
||||
#structure = new UmbArrayState<UmbStructureItemModel>([], (x) => x.unique);
|
||||
public readonly structure = this.#structure.asObservable();
|
||||
|
||||
#parent = new UmbObjectState<UmbStructureItemModel | undefined>(undefined);
|
||||
public readonly parent = this.#parent.asObservable();
|
||||
|
||||
constructor(host: UmbControllerHost, args: UmbMenuTreeStructureWorkspaceContextBaseArgs) {
|
||||
// TODO: set up context token
|
||||
super(host, 'UmbMenuStructureWorkspaceContext');
|
||||
@@ -36,9 +39,10 @@ export abstract class UmbMenuTreeStructureWorkspaceContextBase extends UmbContex
|
||||
async #requestStructure() {
|
||||
let structureItems: Array<UmbStructureItemModel> = [];
|
||||
|
||||
const treeRepository = await createExtensionApiByAlias<
|
||||
UmbTreeRepository<UmbUniqueTreeItemModel, UmbUniqueTreeRootModel>
|
||||
>(this, this.#args.treeRepositoryAlias);
|
||||
const treeRepository = await createExtensionApiByAlias<UmbTreeRepository<UmbTreeItemModel, UmbTreeRootModel>>(
|
||||
this,
|
||||
this.#args.treeRepositoryAlias,
|
||||
);
|
||||
|
||||
const { data: root } = await treeRepository.requestTreeRoot();
|
||||
|
||||
@@ -55,11 +59,15 @@ export abstract class UmbMenuTreeStructureWorkspaceContextBase extends UmbContex
|
||||
|
||||
const isNew = this.#workspaceContext?.getIsNew();
|
||||
const uniqueObservable = isNew ? this.#workspaceContext?.parentUnique : this.#workspaceContext?.unique;
|
||||
const entityTypeObservable = isNew ? this.#workspaceContext?.parentEntityType : this.#workspaceContext?.entityType;
|
||||
|
||||
const unique = (await this.observe(uniqueObservable, () => {})?.asPromise()) as string;
|
||||
if (!unique) throw new Error('Unique is not available');
|
||||
|
||||
const { data } = await treeRepository.requestTreeItemAncestors({ descendantUnique: unique });
|
||||
const entityType = (await this.observe(entityTypeObservable, () => {})?.asPromise()) as string;
|
||||
if (!entityType) throw new Error('Entity type is not available');
|
||||
|
||||
const { data } = await treeRepository.requestTreeItemAncestors({ treeItem: { unique, entityType } });
|
||||
|
||||
if (data) {
|
||||
const ancestorItems = data.map((treeItem) => {
|
||||
@@ -70,9 +78,12 @@ export abstract class UmbMenuTreeStructureWorkspaceContextBase extends UmbContex
|
||||
isFolder: treeItem.isFolder,
|
||||
};
|
||||
});
|
||||
|
||||
structureItems.push(...ancestorItems);
|
||||
}
|
||||
|
||||
const parent = structureItems[structureItems.length - 2];
|
||||
this.#parent.setValue(parent);
|
||||
this.#structure.setValue(structureItems);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
import type { UmbVariantStructureItemModel } from './types.js';
|
||||
import type { UmbTreeRepository } from '@umbraco-cms/backoffice/tree';
|
||||
import type { UmbTreeItemModel, UmbTreeRepository, UmbTreeRootModel } from '@umbraco-cms/backoffice/tree';
|
||||
import { createExtensionApiByAlias } from '@umbraco-cms/backoffice/extension-registry';
|
||||
import { UmbContextBase } from '@umbraco-cms/backoffice/class-api';
|
||||
import { UMB_VARIANT_WORKSPACE_CONTEXT } from '@umbraco-cms/backoffice/workspace';
|
||||
import { UmbArrayState } from '@umbraco-cms/backoffice/observable-api';
|
||||
import { UmbArrayState, UmbObjectState } from '@umbraco-cms/backoffice/observable-api';
|
||||
import type { UmbControllerHost } from '@umbraco-cms/backoffice/controller-api';
|
||||
|
||||
interface UmbMenuVariantTreeStructureWorkspaceContextBaseArgs {
|
||||
@@ -18,6 +18,9 @@ export abstract class UmbMenuVariantTreeStructureWorkspaceContextBase extends Um
|
||||
#structure = new UmbArrayState<UmbVariantStructureItemModel>([], (x) => x.unique);
|
||||
public readonly structure = this.#structure.asObservable();
|
||||
|
||||
#parent = new UmbObjectState<UmbVariantStructureItemModel | undefined>(undefined);
|
||||
public readonly parent = this.#parent.asObservable();
|
||||
|
||||
constructor(host: UmbControllerHost, args: UmbMenuVariantTreeStructureWorkspaceContextBaseArgs) {
|
||||
// TODO: set up context token
|
||||
super(host, 'UmbMenuStructureWorkspaceContext');
|
||||
@@ -37,18 +40,38 @@ export abstract class UmbMenuVariantTreeStructureWorkspaceContextBase extends Um
|
||||
async #requestStructure() {
|
||||
const isNew = this.#workspaceContext?.getIsNew();
|
||||
const uniqueObservable = isNew ? this.#workspaceContext?.parentUnique : this.#workspaceContext?.unique;
|
||||
const entityTypeObservable = isNew ? this.#workspaceContext?.parentEntityType : this.#workspaceContext?.entityType;
|
||||
|
||||
let structureItems: Array<UmbVariantStructureItemModel> = [];
|
||||
|
||||
const unique = (await this.observe(uniqueObservable, () => {})?.asPromise()) as string;
|
||||
if (!unique) throw new Error('Unique is not available');
|
||||
|
||||
const treeRepository = await createExtensionApiByAlias<UmbTreeRepository<any>>(
|
||||
const entityType = (await this.observe(entityTypeObservable, () => {})?.asPromise()) as string;
|
||||
if (!entityType) throw new Error('Entity type is not available');
|
||||
|
||||
// TODO: add correct tree variant item model
|
||||
const treeRepository = await createExtensionApiByAlias<UmbTreeRepository<any, UmbTreeRootModel>>(
|
||||
this,
|
||||
this.#args.treeRepositoryAlias,
|
||||
);
|
||||
const { data } = await treeRepository.requestTreeItemAncestors({ descendantUnique: unique });
|
||||
|
||||
const { data: root } = await treeRepository.requestTreeRoot();
|
||||
|
||||
if (root) {
|
||||
structureItems = [
|
||||
{
|
||||
unique: root.unique,
|
||||
entityType: root.entityType,
|
||||
variants: [{ name: root.name, culture: null, segment: null }],
|
||||
},
|
||||
];
|
||||
}
|
||||
|
||||
const { data } = await treeRepository.requestTreeItemAncestors({ treeItem: { unique, entityType } });
|
||||
|
||||
if (data) {
|
||||
const structureItems = data.map((treeItem) => {
|
||||
const ancestorItems = data.map((treeItem) => {
|
||||
return {
|
||||
unique: treeItem.unique,
|
||||
entityType: treeItem.entityType,
|
||||
@@ -62,6 +85,10 @@ export abstract class UmbMenuVariantTreeStructureWorkspaceContextBase extends Um
|
||||
};
|
||||
});
|
||||
|
||||
structureItems.push(...ancestorItems);
|
||||
|
||||
const parent = structureItems[structureItems.length - 2];
|
||||
this.#parent.setValue(parent);
|
||||
this.#structure.setValue(structureItems);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
export interface UmbStructureItemModelBase {
|
||||
unique: string | null;
|
||||
entityType: string;
|
||||
}
|
||||
import type { UmbEntityModel } from '@umbraco-cms/backoffice/entity';
|
||||
|
||||
export interface UmbStructureItemModelBase extends UmbEntityModel {}
|
||||
|
||||
export interface UmbStructureItemModel extends UmbStructureItemModelBase {
|
||||
name: string;
|
||||
|
||||
@@ -1,13 +1,11 @@
|
||||
import type { UUIColorSwatchesEvent } from '@umbraco-cms/backoffice/external/uui';
|
||||
|
||||
import { css, html, customElement, state, repeat, query, nothing } from '@umbraco-cms/backoffice/external/lit';
|
||||
import { UmbTextStyles } from '@umbraco-cms/backoffice/style';
|
||||
|
||||
import type { UmbIconPickerModalData, UmbIconPickerModalValue } from '@umbraco-cms/backoffice/modal';
|
||||
import { UmbModalBaseElement } from '@umbraco-cms/backoffice/modal';
|
||||
import { css, customElement, html, nothing, query, repeat, state } from '@umbraco-cms/backoffice/external/lit';
|
||||
import { extractUmbColorVariable, umbracoColors } from '@umbraco-cms/backoffice/resources';
|
||||
import { umbFocus } from '@umbraco-cms/backoffice/lit-element';
|
||||
import { UmbModalBaseElement } from '@umbraco-cms/backoffice/modal';
|
||||
import { UmbTextStyles } from '@umbraco-cms/backoffice/style';
|
||||
import { UMB_ICON_REGISTRY_CONTEXT, type UmbIconDefinition } from '@umbraco-cms/backoffice/icon';
|
||||
import type { UmbIconPickerModalData, UmbIconPickerModalValue } from '@umbraco-cms/backoffice/modal';
|
||||
import type { UUIColorSwatchesEvent } from '@umbraco-cms/backoffice/external/uui';
|
||||
|
||||
@customElement('umb-icon-picker-modal')
|
||||
export class UmbIconPickerModalElement extends UmbModalBaseElement<UmbIconPickerModalData, UmbIconPickerModalValue> {
|
||||
@@ -106,12 +104,12 @@ export class UmbIconPickerModalElement extends UmbModalBaseElement<UmbIconPicker
|
||||
<uui-button
|
||||
slot="actions"
|
||||
label=${this.localize.term('general_close')}
|
||||
@click="${this._rejectModal}"></uui-button>
|
||||
@click=${this._rejectModal}></uui-button>
|
||||
<uui-button
|
||||
slot="actions"
|
||||
color="positive"
|
||||
look="primary"
|
||||
@click="${this._submitModal}"
|
||||
@click=${this._submitModal}
|
||||
label=${this.localize.term('general_submit')}></uui-button>
|
||||
</umb-body-layout>
|
||||
`;
|
||||
@@ -123,7 +121,7 @@ export class UmbIconPickerModalElement extends UmbModalBaseElement<UmbIconPicker
|
||||
placeholder=${this.localize.term('placeholders_filter')}
|
||||
label=${this.localize.term('placeholders_filter')}
|
||||
id="search"
|
||||
@keyup="${this.#filterIcons}"
|
||||
@keyup=${this.#filterIcons}
|
||||
${umbFocus()}>
|
||||
<uui-icon name="search" slot="prepend" id="search_icon"></uui-icon>
|
||||
</uui-input>`;
|
||||
@@ -136,15 +134,14 @@ export class UmbIconPickerModalElement extends UmbModalBaseElement<UmbIconPicker
|
||||
(icon) => icon.name,
|
||||
(icon) => html`
|
||||
<uui-button
|
||||
label="${icon.name}"
|
||||
title="${icon.name}"
|
||||
class="${icon.name === this._currentIcon ? 'selected' : ''}"
|
||||
label=${icon.name}
|
||||
title=${icon.name}
|
||||
class=${icon.name === this._currentIcon ? 'selected' : ''}
|
||||
@click=${(e: InputEvent) => this.#changeIcon(e, icon.name)}
|
||||
@keyup=${(e: KeyboardEvent) => this.#changeIcon(e, icon.name)}>
|
||||
<uui-icon
|
||||
style="--uui-icon-color: var(${extractUmbColorVariable(this._currentColor)})"
|
||||
name="${icon.name}">
|
||||
</uui-icon>
|
||||
name=${icon.name}></uui-icon>
|
||||
</uui-button>
|
||||
`,
|
||||
)
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
export interface UmbPickerModalData<ItemType> {
|
||||
multiple?: boolean;
|
||||
hideTreeRoot?: boolean; // TODO: this should be moved to a tree picker modal data interface
|
||||
filter?: (item: ItemType) => boolean;
|
||||
pickableFilter?: (item: ItemType) => boolean;
|
||||
}
|
||||
|
||||
@@ -1,11 +1,3 @@
|
||||
export type UmbEntityUnique = string | null;
|
||||
|
||||
/** Tried to find a common base of our entities — used by Entity Workspace Context */
|
||||
export type UmbEntityBase = {
|
||||
id?: string;
|
||||
name?: string;
|
||||
};
|
||||
|
||||
export interface UmbSwatchDetails {
|
||||
label: string;
|
||||
value: string;
|
||||
|
||||
@@ -8,13 +8,14 @@ import type { UmbModalToken, UmbPickerModalData, UmbPickerModalValue } from '@um
|
||||
|
||||
type PickerItemBaseType = { name: string; unique: string };
|
||||
export class UmbPickerInputContext<
|
||||
ItemType extends PickerItemBaseType,
|
||||
TreeItemType extends PickerItemBaseType = ItemType,
|
||||
PickedItemType extends PickerItemBaseType,
|
||||
PickerItemType extends PickerItemBaseType = PickedItemType,
|
||||
PickerModalConfigType extends UmbPickerModalData<PickerItemType> = UmbPickerModalData<PickerItemType>,
|
||||
PickerModalValueType extends UmbPickerModalValue = UmbPickerModalValue,
|
||||
> extends UmbControllerBase {
|
||||
// TODO: We are way too unsecure about the requirements for the Modal Token, as we have certain expectation for the data and value.
|
||||
modalAlias: string | UmbModalToken<UmbPickerModalData<TreeItemType>, UmbPickerModalValue>;
|
||||
repository?: UmbItemRepository<ItemType>;
|
||||
#getUnique: (entry: ItemType) => string | undefined;
|
||||
modalAlias: string | UmbModalToken<UmbPickerModalData<PickerItemType>, PickerModalValueType>;
|
||||
repository?: UmbItemRepository<PickedItemType>;
|
||||
#getUnique: (entry: PickedItemType) => string | undefined;
|
||||
|
||||
#itemManager;
|
||||
|
||||
@@ -48,14 +49,14 @@ export class UmbPickerInputContext<
|
||||
constructor(
|
||||
host: UmbControllerHost,
|
||||
repositoryAlias: string,
|
||||
modalAlias: string | UmbModalToken<UmbPickerModalData<TreeItemType>, UmbPickerModalValue>,
|
||||
getUniqueMethod?: (entry: ItemType) => string | undefined,
|
||||
modalAlias: string | UmbModalToken<UmbPickerModalData<PickerItemType>, PickerModalValueType>,
|
||||
getUniqueMethod?: (entry: PickedItemType) => string | undefined,
|
||||
) {
|
||||
super(host);
|
||||
this.modalAlias = modalAlias;
|
||||
this.#getUnique = getUniqueMethod || ((entry) => entry.unique);
|
||||
|
||||
this.#itemManager = new UmbRepositoryItemsManager<ItemType>(this, repositoryAlias, this.#getUnique);
|
||||
this.#itemManager = new UmbRepositoryItemsManager<PickedItemType>(this, repositoryAlias, this.#getUnique);
|
||||
|
||||
this.selection = this.#itemManager.uniques;
|
||||
this.selectedItems = this.#itemManager.items;
|
||||
@@ -70,7 +71,7 @@ export class UmbPickerInputContext<
|
||||
this.#itemManager.setUniques(selection.filter((value) => value !== null) as Array<string>);
|
||||
}
|
||||
|
||||
async openPicker(pickerData?: Partial<UmbPickerModalData<TreeItemType>>) {
|
||||
async openPicker(pickerData?: Partial<PickerModalConfigType>) {
|
||||
await this.#itemManager.init;
|
||||
const modalManager = await this.getContext(UMB_MODAL_MANAGER_CONTEXT);
|
||||
const modalContext = modalManager.open(this, this.modalAlias, {
|
||||
@@ -80,7 +81,7 @@ export class UmbPickerInputContext<
|
||||
},
|
||||
value: {
|
||||
selection: this.getSelection(),
|
||||
},
|
||||
} as PickerModalValueType,
|
||||
});
|
||||
|
||||
const modalValue = await modalContext?.onSubmit();
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import type { UmbVariantId } from '../../variant/variant-id.class.js';
|
||||
import type { UmbContext } from '@umbraco-cms/backoffice/class-api';
|
||||
import type { Observable } from '@umbraco-cms/backoffice/external/rxjs';
|
||||
import type { UmbEntityUnique } from '@umbraco-cms/backoffice/models';
|
||||
import type { UmbEntityUnique } from '@umbraco-cms/backoffice/entity';
|
||||
|
||||
/**
|
||||
* A property dataset context, represents the data of a set of properties.
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import type { UmbEntityModel } from '@umbraco-cms/backoffice/entity';
|
||||
import type { UmbPickerModalData, UmbPickerModalValue } from '@umbraco-cms/backoffice/modal';
|
||||
import { UmbModalToken } from '@umbraco-cms/backoffice/modal';
|
||||
|
||||
@@ -10,12 +11,7 @@ export interface UmbRestoreFromRecycleBinModalData {
|
||||
}
|
||||
|
||||
export interface UmbRestoreFromRecycleBinModalValue {
|
||||
destination:
|
||||
| {
|
||||
unique: string | null;
|
||||
entityType: string;
|
||||
}
|
||||
| undefined;
|
||||
destination: UmbEntityModel | undefined;
|
||||
}
|
||||
|
||||
export const UMB_RESTORE_FROM_RECYCLE_BIN_MODAL = new UmbModalToken<
|
||||
|
||||
@@ -4,22 +4,26 @@ import type { UmbControllerHost } from '@umbraco-cms/backoffice/controller-api';
|
||||
import { UmbControllerBase } from '@umbraco-cms/backoffice/class-api';
|
||||
import { UmbId } from '@umbraco-cms/backoffice/id';
|
||||
|
||||
export type TemporaryFileStatus = 'success' | 'waiting' | 'error';
|
||||
///export type TemporaryFileStatus = 'success' | 'waiting' | 'error';
|
||||
|
||||
export enum TemporaryFileStatus {
|
||||
SUCCESS = 'success',
|
||||
WAITING = 'waiting',
|
||||
ERROR = 'error',
|
||||
}
|
||||
|
||||
export interface UmbTemporaryFileModel {
|
||||
file: File;
|
||||
unique: string;
|
||||
status: TemporaryFileStatus;
|
||||
status?: TemporaryFileStatus;
|
||||
}
|
||||
|
||||
export interface UmbTemporaryFileQueueModel extends Partial<UmbTemporaryFileModel> {
|
||||
file: File;
|
||||
}
|
||||
|
||||
export class UmbTemporaryFileManager extends UmbControllerBase {
|
||||
export class UmbTemporaryFileManager<
|
||||
UploadableItem extends UmbTemporaryFileModel = UmbTemporaryFileModel,
|
||||
> extends UmbControllerBase {
|
||||
#temporaryFileRepository;
|
||||
|
||||
#queue = new UmbArrayState<UmbTemporaryFileModel>([], (item) => item.unique);
|
||||
#queue = new UmbArrayState<UploadableItem>([], (item) => item.unique);
|
||||
public readonly queue = this.#queue.asObservable();
|
||||
|
||||
constructor(host: UmbControllerHost) {
|
||||
@@ -27,28 +31,24 @@ export class UmbTemporaryFileManager extends UmbControllerBase {
|
||||
this.#temporaryFileRepository = new UmbTemporaryFileRepository(host);
|
||||
}
|
||||
|
||||
async uploadOne(queueItem: UmbTemporaryFileQueueModel): Promise<Array<UmbTemporaryFileModel>> {
|
||||
async uploadOne(uploadableItem: UploadableItem): Promise<UploadableItem> {
|
||||
this.#queue.setValue([]);
|
||||
const item: UmbTemporaryFileModel = {
|
||||
file: queueItem.file,
|
||||
unique: queueItem.unique ?? UmbId.new(),
|
||||
status: queueItem.status ?? 'waiting',
|
||||
|
||||
const item: UploadableItem = {
|
||||
status: TemporaryFileStatus.WAITING,
|
||||
...uploadableItem,
|
||||
};
|
||||
|
||||
this.#queue.appendOne(item);
|
||||
return this.handleQueue();
|
||||
return (await this.#handleQueue())[0];
|
||||
}
|
||||
|
||||
async upload(queueItems: Array<UmbTemporaryFileQueueModel>): Promise<Array<UmbTemporaryFileModel>> {
|
||||
async upload(queueItems: Array<UploadableItem>): Promise<Array<UploadableItem>> {
|
||||
this.#queue.setValue([]);
|
||||
const items = queueItems.map(
|
||||
(item): UmbTemporaryFileModel => ({
|
||||
file: item.file,
|
||||
unique: item.unique ?? UmbId.new(),
|
||||
status: item.status ?? 'waiting',
|
||||
}),
|
||||
);
|
||||
|
||||
const items = queueItems.map((item): UploadableItem => ({ status: TemporaryFileStatus.WAITING, ...item }));
|
||||
this.#queue.append(items);
|
||||
return this.handleQueue();
|
||||
return this.#handleQueue();
|
||||
}
|
||||
|
||||
removeOne(unique: string) {
|
||||
@@ -59,8 +59,8 @@ export class UmbTemporaryFileManager extends UmbControllerBase {
|
||||
this.#queue.remove(uniques);
|
||||
}
|
||||
|
||||
private async handleQueue() {
|
||||
const filesCompleted: Array<UmbTemporaryFileModel> = [];
|
||||
async #handleQueue() {
|
||||
const filesCompleted: Array<UploadableItem> = [];
|
||||
const queue = this.#queue.getValue();
|
||||
|
||||
if (!queue.length) return filesCompleted;
|
||||
@@ -69,14 +69,14 @@ export class UmbTemporaryFileManager extends UmbControllerBase {
|
||||
if (!item.unique) throw new Error(`Unique is missing for item ${item}`);
|
||||
|
||||
const { error } = await this.#temporaryFileRepository.upload(item.unique, item.file);
|
||||
await new Promise((resolve) => setTimeout(resolve, (Math.random() + 0.5) * 1000)); // simulate small delay so that the upload badge is properly shown
|
||||
//await new Promise((resolve) => setTimeout(resolve, (Math.random() + 0.5) * 1000)); // simulate small delay so that the upload badge is properly shown
|
||||
|
||||
let status: TemporaryFileStatus;
|
||||
if (error) {
|
||||
status = 'error';
|
||||
status = TemporaryFileStatus.ERROR;
|
||||
this.#queue.updateOne(item.unique, { ...item, status });
|
||||
} else {
|
||||
status = 'success';
|
||||
status = TemporaryFileStatus.SUCCESS;
|
||||
this.#queue.updateOne(item.unique, { ...item, status });
|
||||
}
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import type { UmbTreeItemModelBase } from '../types.js';
|
||||
import type { UmbTreeItemModel } from '../types.js';
|
||||
import type {
|
||||
UmbTreeAncestorsOfRequestArgs,
|
||||
UmbTreeChildrenOfRequestArgs,
|
||||
@@ -13,7 +13,7 @@ import type { UmbControllerHost } from '@umbraco-cms/backoffice/controller-api';
|
||||
* @interface UmbTreeDataSourceConstructor
|
||||
* @template TreeItemType
|
||||
*/
|
||||
export interface UmbTreeDataSourceConstructor<TreeItemType extends UmbTreeItemModelBase> {
|
||||
export interface UmbTreeDataSourceConstructor<TreeItemType extends UmbTreeItemModel> {
|
||||
new (host: UmbControllerHost): UmbTreeDataSource<TreeItemType>;
|
||||
}
|
||||
|
||||
@@ -23,7 +23,7 @@ export interface UmbTreeDataSourceConstructor<TreeItemType extends UmbTreeItemMo
|
||||
* @interface UmbTreeDataSource
|
||||
* @template TreeItemType
|
||||
*/
|
||||
export interface UmbTreeDataSource<TreeItemType extends UmbTreeItemModelBase> {
|
||||
export interface UmbTreeDataSource<TreeItemType extends UmbTreeItemModel> {
|
||||
/**
|
||||
* Gets the root items of the tree.
|
||||
* @return {*} {Promise<UmbDataSourceResponse<UmbPagedModel<TreeItemType>>>}
|
||||
|
||||
@@ -1,8 +1,12 @@
|
||||
import type { UmbUniqueTreeItemModel, UmbUniqueTreeRootModel } from '../types.js';
|
||||
import type { UmbTreeItemModel, UmbTreeRootModel } from '../types.js';
|
||||
import type { UmbTreeStore } from './tree-store.interface.js';
|
||||
import type { UmbTreeRepository } from './tree-repository.interface.js';
|
||||
import type { UmbTreeDataSource, UmbTreeDataSourceConstructor } from './tree-data-source.interface.js';
|
||||
import type { UmbTreeAncestorsOfRequestArgs } from './types.js';
|
||||
import type {
|
||||
UmbTreeAncestorsOfRequestArgs,
|
||||
UmbTreeChildrenOfRequestArgs,
|
||||
UmbTreeRootItemsRequestArgs,
|
||||
} from './types.js';
|
||||
import { UmbRepositoryBase } from '@umbraco-cms/backoffice/repository';
|
||||
import type { ProblemDetails } from '@umbraco-cms/backoffice/external/backend-api';
|
||||
import type { UmbControllerHost } from '@umbraco-cms/backoffice/controller-api';
|
||||
@@ -21,8 +25,8 @@ import type { UmbContextToken } from '@umbraco-cms/backoffice/context-api';
|
||||
* @template TreeRootType
|
||||
*/
|
||||
export abstract class UmbTreeRepositoryBase<
|
||||
TreeItemType extends UmbUniqueTreeItemModel,
|
||||
TreeRootType extends UmbUniqueTreeRootModel,
|
||||
TreeItemType extends UmbTreeItemModel,
|
||||
TreeRootType extends UmbTreeRootModel,
|
||||
>
|
||||
extends UmbRepositoryBase
|
||||
implements UmbTreeRepository<TreeItemType, TreeRootType>, UmbApi
|
||||
@@ -63,7 +67,7 @@ export abstract class UmbTreeRepositoryBase<
|
||||
* @return {*}
|
||||
* @memberof UmbTreeRepositoryBase
|
||||
*/
|
||||
async requestRootTreeItems(args: any) {
|
||||
async requestRootTreeItems(args: UmbTreeRootItemsRequestArgs) {
|
||||
await this._init;
|
||||
|
||||
const { data, error: _error } = await this._treeSource.getRootItems(args);
|
||||
@@ -81,8 +85,10 @@ export abstract class UmbTreeRepositoryBase<
|
||||
* @return {*}
|
||||
* @memberof UmbTreeRepositoryBase
|
||||
*/
|
||||
async requestTreeItemsOf(args: any) {
|
||||
if (args.parentUnique === undefined) throw new Error('Parent unique is missing');
|
||||
async requestTreeItemsOf(args: UmbTreeChildrenOfRequestArgs) {
|
||||
if (!args.parent) throw new Error('Parent is missing');
|
||||
if (args.parent.unique === undefined) throw new Error('Parent unique is missing');
|
||||
if (args.parent.entityType === null) throw new Error('Parent entity type is missing');
|
||||
await this._init;
|
||||
|
||||
const { data, error: _error } = await this._treeSource.getChildrenOf(args);
|
||||
@@ -91,7 +97,7 @@ export abstract class UmbTreeRepositoryBase<
|
||||
this._treeStore!.appendItems(data.items);
|
||||
}
|
||||
|
||||
return { data, error, asObservable: () => this._treeStore!.childrenOf(args.parentUnique) };
|
||||
return { data, error, asObservable: () => this._treeStore!.childrenOf(args.parent.unique) };
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -101,7 +107,7 @@ export abstract class UmbTreeRepositoryBase<
|
||||
* @memberof UmbTreeRepositoryBase
|
||||
*/
|
||||
async requestTreeItemAncestors(args: UmbTreeAncestorsOfRequestArgs) {
|
||||
if (args.descendantUnique === undefined) throw new Error('Descendant unique is missing');
|
||||
if (args.treeItem.unique === undefined) throw new Error('Descendant unique is missing');
|
||||
await this._init;
|
||||
|
||||
const { data, error: _error } = await this._treeSource.getAncestorsOf(args);
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import type { UmbTreeItemModelBase } from '../types.js';
|
||||
import type { UmbTreeItemModel, UmbTreeRootModel } from '../types.js';
|
||||
import type {
|
||||
UmbTreeChildrenOfRequestArgs,
|
||||
UmbTreeAncestorsOfRequestArgs,
|
||||
@@ -18,8 +18,8 @@ import type { UmbApi } from '@umbraco-cms/backoffice/extension-api';
|
||||
* @template TreeRootType
|
||||
*/
|
||||
export interface UmbTreeRepository<
|
||||
TreeItemType extends UmbTreeItemModelBase = UmbTreeItemModelBase,
|
||||
TreeRootType extends UmbTreeItemModelBase = UmbTreeItemModelBase,
|
||||
TreeItemType extends UmbTreeItemModel = UmbTreeItemModel,
|
||||
TreeRootType extends UmbTreeRootModel = UmbTreeRootModel,
|
||||
> extends UmbApi {
|
||||
/**
|
||||
* Requests the root of the tree.
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import type { UmbTreeItemModelBase } from '../types.js';
|
||||
import type { UmbTreeItemModelBase, UmbTreeItemModel } from '../types.js';
|
||||
import type { UmbTreeDataSource } from './tree-data-source.interface.js';
|
||||
import type {
|
||||
UmbTreeAncestorsOfRequestArgs,
|
||||
@@ -27,7 +27,7 @@ export interface UmbTreeServerDataSourceBaseArgs<
|
||||
*/
|
||||
export abstract class UmbTreeServerDataSourceBase<
|
||||
ServerTreeItemType extends { hasChildren: boolean },
|
||||
ClientTreeItemType extends UmbTreeItemModelBase,
|
||||
ClientTreeItemType extends UmbTreeItemModel,
|
||||
> implements UmbTreeDataSource<ClientTreeItemType>
|
||||
{
|
||||
#host;
|
||||
@@ -73,7 +73,7 @@ export abstract class UmbTreeServerDataSourceBase<
|
||||
* @memberof UmbTreeServerDataSourceBase
|
||||
*/
|
||||
async getChildrenOf(args: UmbTreeChildrenOfRequestArgs) {
|
||||
if (args.parentUnique === undefined) throw new Error('Parent unique is missing');
|
||||
if (args.parent.unique === undefined) throw new Error('Parent unique is missing');
|
||||
|
||||
const { data, error } = await tryExecuteAndNotify(this.#host, this.#getChildrenOf(args));
|
||||
|
||||
@@ -92,7 +92,7 @@ export abstract class UmbTreeServerDataSourceBase<
|
||||
* @memberof UmbTreeServerDataSourceBase
|
||||
*/
|
||||
async getAncestorsOf(args: UmbTreeAncestorsOfRequestArgs) {
|
||||
if (!args.descendantUnique) throw new Error('Parent unique is missing');
|
||||
if (!args.treeItem.entityType) throw new Error('Parent unique is missing');
|
||||
|
||||
const { data, error } = await tryExecuteAndNotify(this.#host, this.#getAncestorsOf(args));
|
||||
|
||||
|
||||
@@ -1,14 +1,19 @@
|
||||
import type { UmbEntityModel } from '@umbraco-cms/backoffice/entity';
|
||||
|
||||
export interface UmbTreeRootItemsRequestArgs {
|
||||
skip: number;
|
||||
take: number;
|
||||
skip?: number;
|
||||
take?: number;
|
||||
}
|
||||
|
||||
export interface UmbTreeChildrenOfRequestArgs {
|
||||
parentUnique: string | null;
|
||||
skip: number;
|
||||
take: number;
|
||||
parent: UmbEntityModel;
|
||||
skip?: number;
|
||||
take?: number;
|
||||
}
|
||||
|
||||
export interface UmbTreeAncestorsOfRequestArgs {
|
||||
descendantUnique: string;
|
||||
treeItem: {
|
||||
unique: string;
|
||||
entityType: string;
|
||||
};
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import type { UmbUniqueTreeItemModel } from '../types.js';
|
||||
import type { UmbTreeItemModel } from '../types.js';
|
||||
import type { UmbTreeStore } from './tree-store.interface.js';
|
||||
import { UmbStoreBase } from '@umbraco-cms/backoffice/store';
|
||||
import { UmbArrayState } from '@umbraco-cms/backoffice/observable-api';
|
||||
@@ -11,19 +11,16 @@ import type { UmbControllerHost } from '@umbraco-cms/backoffice/controller-api';
|
||||
* @extends {UmbStoreBase}
|
||||
* @description - Entity Tree Store
|
||||
*/
|
||||
export class UmbUniqueTreeStore
|
||||
extends UmbStoreBase<UmbUniqueTreeItemModel>
|
||||
implements UmbTreeStore<UmbUniqueTreeItemModel>
|
||||
{
|
||||
export class UmbUniqueTreeStore extends UmbStoreBase<UmbTreeItemModel> implements UmbTreeStore<UmbTreeItemModel> {
|
||||
constructor(host: UmbControllerHost, storeAlias: string) {
|
||||
super(host, storeAlias, new UmbArrayState<UmbUniqueTreeItemModel>([], (x) => x.unique));
|
||||
super(host, storeAlias, new UmbArrayState<UmbTreeItemModel>([], (x) => x.unique));
|
||||
}
|
||||
|
||||
/**
|
||||
* An observable to observe the root items
|
||||
* @memberof UmbUniqueTreeStore
|
||||
*/
|
||||
rootItems = this._data.asObservablePart((items) => items.filter((item) => item.parentUnique === null));
|
||||
rootItems = this._data.asObservablePart((items) => items.filter((item) => item.parent.unique === null));
|
||||
|
||||
/**
|
||||
* Returns an observable to observe the children of a given parent
|
||||
@@ -32,16 +29,6 @@ export class UmbUniqueTreeStore
|
||||
* @memberof UmbUniqueTreeStore
|
||||
*/
|
||||
childrenOf(parentUnique: string | null) {
|
||||
return this._data.asObservablePart((items) => items.filter((item) => item.parentUnique === parentUnique));
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an observable to observe the items with the given uniques
|
||||
* @param {Array<string>} uniques
|
||||
* @return {*}
|
||||
* @memberof UmbUniqueTreeStore
|
||||
*/
|
||||
items(uniques: Array<string | null>) {
|
||||
return this._data.asObservablePart((items) => items.filter((item) => uniques.includes(item.unique)));
|
||||
return this._data.asObservablePart((items) => items.filter((item) => item.parent.unique === parentUnique));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { UmbRequestReloadTreeItemChildrenEvent } from '../reload-tree-item-children/index.js';
|
||||
import type { UmbTreeItemModelBase } from '../types.js';
|
||||
import type { UmbTreeItemModel, UmbTreeRootModel, UmbTreeStartNode } from '../types.js';
|
||||
import type { UmbTreeRepository } from '../data/tree-repository.interface.js';
|
||||
import type { UmbTreeContext } from '../tree-context.interface.js';
|
||||
import { type UmbActionEventContext, UMB_ACTION_EVENT_CONTEXT } from '@umbraco-cms/backoffice/action';
|
||||
@@ -11,21 +11,19 @@ import {
|
||||
import { UmbContextBase } from '@umbraco-cms/backoffice/class-api';
|
||||
import type { UmbControllerHost } from '@umbraco-cms/backoffice/controller-api';
|
||||
import { UmbExtensionApiInitializer } from '@umbraco-cms/backoffice/extension-api';
|
||||
import { UmbPaginationManager, UmbSelectionManager } from '@umbraco-cms/backoffice/utils';
|
||||
import { UmbPaginationManager, UmbSelectionManager, debounce } from '@umbraco-cms/backoffice/utils';
|
||||
import type { UmbEntityActionEvent } from '@umbraco-cms/backoffice/entity-action';
|
||||
import { UmbArrayState, UmbObjectState } from '@umbraco-cms/backoffice/observable-api';
|
||||
import { UmbArrayState, UmbBooleanState, UmbObjectState } from '@umbraco-cms/backoffice/observable-api';
|
||||
import { UmbContextToken } from '@umbraco-cms/backoffice/context-api';
|
||||
import { UmbChangeEvent } from '@umbraco-cms/backoffice/event';
|
||||
|
||||
export class UmbDefaultTreeContext<TreeItemType extends UmbTreeItemModelBase>
|
||||
extends UmbContextBase<UmbDefaultTreeContext<TreeItemType>>
|
||||
export class UmbDefaultTreeContext<TreeItemType extends UmbTreeItemModel, TreeRootType extends UmbTreeRootModel>
|
||||
extends UmbContextBase<UmbDefaultTreeContext<TreeItemType, TreeRootType>>
|
||||
implements UmbTreeContext
|
||||
{
|
||||
#treeRoot = new UmbObjectState<TreeItemType | undefined>(undefined);
|
||||
#treeRoot = new UmbObjectState<TreeRootType | undefined>(undefined);
|
||||
treeRoot = this.#treeRoot.asObservable();
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||
// @ts-ignore
|
||||
#rootItems = new UmbArrayState<TreeItemType>([], (x) => x.unique);
|
||||
rootItems = this.#rootItems.asObservable();
|
||||
|
||||
@@ -34,8 +32,14 @@ export class UmbDefaultTreeContext<TreeItemType extends UmbTreeItemModelBase>
|
||||
public readonly selection = new UmbSelectionManager(this._host);
|
||||
public readonly pagination = new UmbPaginationManager();
|
||||
|
||||
#hideTreeRoot = new UmbBooleanState(false);
|
||||
hideTreeRoot = this.#hideTreeRoot.asObservable();
|
||||
|
||||
#startNode = new UmbObjectState<UmbTreeStartNode | undefined>(undefined);
|
||||
startNode = this.#startNode.asObservable();
|
||||
|
||||
#manifest?: ManifestTree;
|
||||
#repository?: UmbTreeRepository<TreeItemType>;
|
||||
#repository?: UmbTreeRepository<TreeItemType, TreeRootType>;
|
||||
#actionEventContext?: UmbActionEventContext;
|
||||
|
||||
#paging = {
|
||||
@@ -51,6 +55,8 @@ export class UmbDefaultTreeContext<TreeItemType extends UmbTreeItemModelBase>
|
||||
});
|
||||
|
||||
constructor(host: UmbControllerHost) {
|
||||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||
// @ts-ignore
|
||||
super(host, UMB_DEFAULT_TREE_CONTEXT);
|
||||
this.pagination.setPageSize(this.#paging.take);
|
||||
this.#consumeContexts();
|
||||
@@ -67,16 +73,16 @@ export class UmbDefaultTreeContext<TreeItemType extends UmbTreeItemModelBase>
|
||||
// @ts-ignore
|
||||
hostElement.addEventListener('temp-reload-tree-item-parent', (event: CustomEvent) => {
|
||||
const treeRoot = this.#treeRoot.getValue();
|
||||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||
// @ts-ignore
|
||||
const unique = treeRoot.unique;
|
||||
const unique = treeRoot?.unique;
|
||||
|
||||
if (event.detail.unique === unique) {
|
||||
event.stopPropagation();
|
||||
this.loadRootItems();
|
||||
this.loadTree();
|
||||
}
|
||||
});
|
||||
|
||||
this.loadTreeRoot();
|
||||
// always load the tree root because we need the root entity to reload the entire tree
|
||||
this.#loadTreeRoot();
|
||||
}
|
||||
|
||||
// TODO: find a generic way to do this
|
||||
@@ -115,31 +121,127 @@ export class UmbDefaultTreeContext<TreeItemType extends UmbTreeItemModelBase>
|
||||
return this.#repository;
|
||||
}
|
||||
|
||||
public async loadTreeRoot() {
|
||||
await this.#init;
|
||||
const { data } = await this.#repository!.requestTreeRoot();
|
||||
/**
|
||||
* Loads the tree
|
||||
* @memberof UmbDefaultTreeContext
|
||||
*/
|
||||
// TODO: debouncing the load tree method because multiple props can be set at the same time
|
||||
// that would trigger multiple loadTree calls. This is a temporary solution to avoid that.
|
||||
public loadTree = debounce(() => this.#debouncedLoadTree(), 100);
|
||||
|
||||
if (data) {
|
||||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||
// @ts-ignore
|
||||
this.#treeRoot.setValue(data);
|
||||
/**
|
||||
* Reloads the tree
|
||||
* @memberof UmbDefaultTreeContext
|
||||
*/
|
||||
public loadMore = () => this.#debouncedLoadTree(true);
|
||||
|
||||
#debouncedLoadTree(reload = false) {
|
||||
if (this.getStartFrom()) {
|
||||
this.#loadRootItems(reload);
|
||||
return;
|
||||
}
|
||||
|
||||
const hideTreeRoot = this.getHideTreeRoot();
|
||||
if (hideTreeRoot) {
|
||||
this.#loadRootItems(reload);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
public async loadRootItems() {
|
||||
async #loadTreeRoot() {
|
||||
await this.#init;
|
||||
|
||||
const { data } = await this.#repository!.requestRootTreeItems({
|
||||
skip: this.#paging.skip,
|
||||
take: this.#paging.take,
|
||||
});
|
||||
const { data } = await this.#repository!.requestTreeRoot();
|
||||
|
||||
if (data) {
|
||||
this.#rootItems.setValue(data.items);
|
||||
this.#treeRoot.setValue(data);
|
||||
this.pagination.setTotalItems(1);
|
||||
}
|
||||
}
|
||||
|
||||
async #loadRootItems(loadMore = false) {
|
||||
await this.#init;
|
||||
|
||||
const skip = loadMore ? this.#paging.skip : 0;
|
||||
const take = loadMore ? this.#paging.take : this.pagination.getCurrentPageNumber() * this.#paging.take;
|
||||
|
||||
// If we have a start node get children of that instead of the root
|
||||
const startNode = this.getStartFrom();
|
||||
|
||||
const { data } = startNode?.unique
|
||||
? await this.#repository!.requestTreeItemsOf({
|
||||
parent: {
|
||||
unique: startNode.unique,
|
||||
entityType: startNode.entityType,
|
||||
},
|
||||
skip,
|
||||
take,
|
||||
})
|
||||
: await this.#repository!.requestRootTreeItems({
|
||||
skip,
|
||||
take,
|
||||
});
|
||||
|
||||
if (data) {
|
||||
if (loadMore) {
|
||||
const currentItems = this.#rootItems.getValue();
|
||||
this.#rootItems.setValue([...currentItems, ...data.items]);
|
||||
} else {
|
||||
this.#rootItems.setValue(data.items);
|
||||
}
|
||||
|
||||
this.pagination.setTotalItems(data.total);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the hideTreeRoot config
|
||||
* @param {boolean} hideTreeRoot
|
||||
* @memberof UmbDefaultTreeContext
|
||||
*/
|
||||
setHideTreeRoot(hideTreeRoot: boolean) {
|
||||
this.#hideTreeRoot.setValue(hideTreeRoot);
|
||||
// we need to reset the tree if this config changes
|
||||
this.#resetTree();
|
||||
this.loadTree();
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the hideTreeRoot config
|
||||
* @return {boolean}
|
||||
* @memberof UmbDefaultTreeContext
|
||||
*/
|
||||
getHideTreeRoot() {
|
||||
return this.#hideTreeRoot.getValue();
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the startNode config
|
||||
* @param {UmbTreeStartNode} startNode
|
||||
* @memberof UmbDefaultTreeContext
|
||||
*/
|
||||
setStartFrom(startNode: UmbTreeStartNode | undefined) {
|
||||
this.#startNode.setValue(startNode);
|
||||
// we need to reset the tree if this config changes
|
||||
this.#resetTree();
|
||||
this.loadTree();
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the startNode config
|
||||
* @return {UmbTreeStartNode}
|
||||
* @memberof UmbDefaultTreeContext
|
||||
*/
|
||||
getStartFrom() {
|
||||
return this.#startNode.getValue();
|
||||
}
|
||||
|
||||
#resetTree() {
|
||||
this.#treeRoot.setValue(undefined);
|
||||
this.#rootItems.setValue([]);
|
||||
this.pagination.clear();
|
||||
}
|
||||
|
||||
#consumeContexts() {
|
||||
this.consumeContext(UMB_ACTION_EVENT_CONTEXT, (instance) => {
|
||||
this.#actionEventContext = instance;
|
||||
@@ -157,7 +259,7 @@ export class UmbDefaultTreeContext<TreeItemType extends UmbTreeItemModelBase>
|
||||
#onPageChange = (event: UmbChangeEvent) => {
|
||||
const target = event.target as UmbPaginationManager;
|
||||
this.#paging.skip = target.getSkip();
|
||||
this.loadRootItems();
|
||||
this.loadMore();
|
||||
};
|
||||
|
||||
#observeRepository(repositoryAlias?: string) {
|
||||
@@ -179,11 +281,9 @@ export class UmbDefaultTreeContext<TreeItemType extends UmbTreeItemModelBase>
|
||||
// Only handle root request here. Items are handled by the tree item context
|
||||
const treeRoot = this.#treeRoot.getValue();
|
||||
if (treeRoot === undefined) return;
|
||||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||
// @ts-ignore
|
||||
if (event.getUnique() !== treeRoot.unique) return;
|
||||
if (event.getEntityType() !== treeRoot.entityType) return;
|
||||
this.loadRootItems();
|
||||
this.loadTree();
|
||||
};
|
||||
|
||||
destroy(): void {
|
||||
@@ -197,4 +297,6 @@ export class UmbDefaultTreeContext<TreeItemType extends UmbTreeItemModelBase>
|
||||
|
||||
export default UmbDefaultTreeContext;
|
||||
|
||||
export const UMB_DEFAULT_TREE_CONTEXT = new UmbContextToken<UmbDefaultTreeContext<any>>('UmbTreeContext');
|
||||
export const UMB_DEFAULT_TREE_CONTEXT = new UmbContextToken<UmbDefaultTreeContext<UmbTreeItemModel, UmbTreeRootModel>>(
|
||||
'UmbTreeContext',
|
||||
);
|
||||
|
||||
@@ -1,4 +1,10 @@
|
||||
import type { UmbTreeItemModelBase, UmbTreeSelectionConfiguration } from '../types.js';
|
||||
import type {
|
||||
UmbTreeItemModel,
|
||||
UmbTreeItemModelBase,
|
||||
UmbTreeRootModel,
|
||||
UmbTreeSelectionConfiguration,
|
||||
UmbTreeStartNode,
|
||||
} from '../types.js';
|
||||
import type { UmbDefaultTreeContext } from './default-tree.context.js';
|
||||
import { UMB_DEFAULT_TREE_CONTEXT } from './default-tree.context.js';
|
||||
import type { PropertyValueMap } from '@umbraco-cms/backoffice/external/lit';
|
||||
@@ -22,6 +28,9 @@ export class UmbDefaultTreeElement extends UmbLitElement {
|
||||
@property({ type: Boolean, attribute: false })
|
||||
hideTreeRoot: boolean = false;
|
||||
|
||||
@property({ type: Object, attribute: false })
|
||||
startNode?: UmbTreeStartNode;
|
||||
|
||||
@property({ attribute: false })
|
||||
selectableFilter: (item: UmbTreeItemModelBase) => boolean = () => true;
|
||||
|
||||
@@ -29,10 +38,10 @@ export class UmbDefaultTreeElement extends UmbLitElement {
|
||||
filter: (item: UmbTreeItemModelBase) => boolean = () => true;
|
||||
|
||||
@state()
|
||||
private _rootItems: UmbTreeItemModelBase[] = [];
|
||||
private _rootItems: UmbTreeItemModel[] = [];
|
||||
|
||||
@state()
|
||||
private _treeRoot?: UmbTreeItemModelBase;
|
||||
private _treeRoot?: UmbTreeRootModel;
|
||||
|
||||
@state()
|
||||
private _currentPage = 1;
|
||||
@@ -40,7 +49,7 @@ export class UmbDefaultTreeElement extends UmbLitElement {
|
||||
@state()
|
||||
private _totalPages = 1;
|
||||
|
||||
#treeContext?: UmbDefaultTreeContext<UmbTreeItemModelBase>;
|
||||
#treeContext?: UmbDefaultTreeContext<UmbTreeItemModel, UmbTreeRootModel>;
|
||||
#init: Promise<unknown>;
|
||||
|
||||
constructor() {
|
||||
@@ -70,11 +79,12 @@ export class UmbDefaultTreeElement extends UmbLitElement {
|
||||
this.#treeContext!.selection.setSelection(this._selectionConfiguration.selection ?? []);
|
||||
}
|
||||
|
||||
if (_changedProperties.has('startNode')) {
|
||||
this.#treeContext!.setStartFrom(this.startNode);
|
||||
}
|
||||
|
||||
if (_changedProperties.has('hideTreeRoot')) {
|
||||
if (this.hideTreeRoot === true) {
|
||||
await this.#init;
|
||||
this.#treeContext!.loadRootItems();
|
||||
}
|
||||
this.#treeContext!.setHideTreeRoot(this.hideTreeRoot);
|
||||
}
|
||||
|
||||
if (_changedProperties.has('selectableFilter')) {
|
||||
@@ -104,7 +114,7 @@ export class UmbDefaultTreeElement extends UmbLitElement {
|
||||
}
|
||||
|
||||
#renderRootItems() {
|
||||
// only shot the root items directly if the tree root is hidden
|
||||
// only show the root items directly if the tree root is hidden
|
||||
if (this.hideTreeRoot === true) {
|
||||
return html`
|
||||
${repeat(
|
||||
|
||||
@@ -1,12 +1,10 @@
|
||||
import type { UmbEntityModel } from '@umbraco-cms/backoffice/entity';
|
||||
import { UmbModalToken } from '@umbraco-cms/backoffice/modal';
|
||||
import type { UmbFolderModel } from '@umbraco-cms/backoffice/tree';
|
||||
|
||||
export interface UmbFolderCreateModalData {
|
||||
folderRepositoryAlias: string;
|
||||
parent: {
|
||||
unique: string | null;
|
||||
entityType: string;
|
||||
};
|
||||
parent: UmbEntityModel;
|
||||
}
|
||||
|
||||
export interface UmbFolderCreateModalValue {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import type { UmbTreeItemContext } from '../tree-item-context.interface.js';
|
||||
import { UMB_DEFAULT_TREE_CONTEXT, type UmbDefaultTreeContext } from '../../default/default-tree.context.js';
|
||||
import type { UmbTreeItemModelBase } from '../../types.js';
|
||||
import type { UmbTreeItemModel, UmbTreeRootModel } from '../../types.js';
|
||||
import { UmbRequestReloadTreeItemChildrenEvent } from '../../reload-tree-item-children/index.js';
|
||||
import { map } from '@umbraco-cms/backoffice/external/rxjs';
|
||||
import { UMB_SECTION_CONTEXT, UMB_SECTION_SIDEBAR_CONTEXT } from '@umbraco-cms/backoffice/section';
|
||||
@@ -17,11 +17,10 @@ import type { UmbEntityActionEvent } from '@umbraco-cms/backoffice/entity-action
|
||||
import { UmbPaginationManager, debounce } from '@umbraco-cms/backoffice/utils';
|
||||
import { UmbChangeEvent } from '@umbraco-cms/backoffice/event';
|
||||
|
||||
export type UmbTreeItemUniqueFunction<TreeItemType extends UmbTreeItemModelBase> = (
|
||||
x: TreeItemType,
|
||||
) => string | null | undefined;
|
||||
|
||||
export abstract class UmbTreeItemContextBase<TreeItemType extends UmbTreeItemModelBase>
|
||||
export abstract class UmbTreeItemContextBase<
|
||||
TreeItemType extends UmbTreeItemModel,
|
||||
TreeRootType extends UmbTreeRootModel,
|
||||
>
|
||||
extends UmbContextBase<UmbTreeItemContext<TreeItemType>>
|
||||
implements UmbTreeItemContext<TreeItemType>
|
||||
{
|
||||
@@ -63,11 +62,10 @@ export abstract class UmbTreeItemContextBase<TreeItemType extends UmbTreeItemMod
|
||||
#path = new UmbStringState('');
|
||||
readonly path = this.#path.asObservable();
|
||||
|
||||
treeContext?: UmbDefaultTreeContext<TreeItemType>;
|
||||
treeContext?: UmbDefaultTreeContext<TreeItemType, TreeRootType>;
|
||||
#sectionContext?: UmbSectionContext;
|
||||
#sectionSidebarContext?: UmbSectionSidebarContext;
|
||||
#actionEventContext?: UmbActionEventContext;
|
||||
#getUniqueFunction: UmbTreeItemUniqueFunction<TreeItemType>;
|
||||
|
||||
// TODO: get this from the tree context
|
||||
#paging = {
|
||||
@@ -75,10 +73,9 @@ export abstract class UmbTreeItemContextBase<TreeItemType extends UmbTreeItemMod
|
||||
take: 50,
|
||||
};
|
||||
|
||||
constructor(host: UmbControllerHost, getUniqueFunction: UmbTreeItemUniqueFunction<TreeItemType>) {
|
||||
constructor(host: UmbControllerHost) {
|
||||
super(host, UMB_TREE_ITEM_CONTEXT);
|
||||
this.pagination.setPageSize(this.#paging.take);
|
||||
this.#getUniqueFunction = getUniqueFunction;
|
||||
this.#consumeContexts();
|
||||
|
||||
// listen for page changes on the pagination manager
|
||||
@@ -134,10 +131,9 @@ export abstract class UmbTreeItemContextBase<TreeItemType extends UmbTreeItemMod
|
||||
return;
|
||||
}
|
||||
|
||||
const unique = this.#getUniqueFunction(treeItem);
|
||||
// Only check for undefined. The tree root has null as unique
|
||||
if (unique === undefined) throw new Error('Could not create tree item context, unique key is missing');
|
||||
this.unique = unique;
|
||||
if (treeItem.unique === undefined) throw new Error('Could not create tree item context, unique is missing');
|
||||
this.unique = treeItem.unique;
|
||||
|
||||
if (!treeItem.entityType) throw new Error('Could not create tree item context, tree item type is missing');
|
||||
this.entityType = treeItem.entityType;
|
||||
@@ -152,22 +148,48 @@ export abstract class UmbTreeItemContextBase<TreeItemType extends UmbTreeItemMod
|
||||
this.#observeSectionPath();
|
||||
}
|
||||
|
||||
public async loadChildren() {
|
||||
/**
|
||||
* Load children of the tree item
|
||||
* @memberof UmbTreeItemContextBase
|
||||
*/
|
||||
public loadChildren = () => this.#loadChildren();
|
||||
|
||||
/**
|
||||
* Load more children of the tree item
|
||||
* @memberof UmbTreeItemContextBase
|
||||
*/
|
||||
public loadMore = () => this.#loadChildren(true);
|
||||
|
||||
async #loadChildren(loadMore = false) {
|
||||
if (this.unique === undefined) throw new Error('Could not request children, unique key is missing');
|
||||
if (this.entityType === undefined) throw new Error('Could not request children, entity type is missing');
|
||||
|
||||
// TODO: wait for tree context to be ready
|
||||
const repository = this.treeContext?.getRepository();
|
||||
if (!repository) throw new Error('Could not request children, repository is missing');
|
||||
|
||||
this.#isLoading.setValue(true);
|
||||
|
||||
const skip = loadMore ? this.#paging.skip : 0;
|
||||
const take = loadMore ? this.#paging.take : this.pagination.getCurrentPageNumber() * this.#paging.take;
|
||||
|
||||
const { data } = await repository.requestTreeItemsOf({
|
||||
parentUnique: this.unique,
|
||||
skip: this.#paging.skip,
|
||||
take: this.#paging.take,
|
||||
parent: {
|
||||
unique: this.unique,
|
||||
entityType: this.entityType,
|
||||
},
|
||||
skip,
|
||||
take,
|
||||
});
|
||||
|
||||
if (data) {
|
||||
this.#childItems.setValue(data.items);
|
||||
if (loadMore) {
|
||||
const currentItems = this.#childItems.getValue();
|
||||
this.#childItems.setValue([...currentItems, ...data.items]);
|
||||
} else {
|
||||
this.#childItems.setValue(data.items);
|
||||
}
|
||||
|
||||
this.#hasChildren.setValue(data.total > 0);
|
||||
this.pagination.setTotalItems(data.total);
|
||||
}
|
||||
@@ -207,7 +229,9 @@ export abstract class UmbTreeItemContextBase<TreeItemType extends UmbTreeItemMod
|
||||
this.#sectionSidebarContext = instance;
|
||||
});
|
||||
|
||||
this.consumeContext(UMB_DEFAULT_TREE_CONTEXT, (treeContext: UmbDefaultTreeContext<TreeItemType>) => {
|
||||
this.consumeContext(UMB_DEFAULT_TREE_CONTEXT, (treeContext) => {
|
||||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||
// @ts-ignore
|
||||
this.treeContext = treeContext;
|
||||
this.#observeIsSelectable();
|
||||
this.#observeIsSelected();
|
||||
@@ -329,7 +353,7 @@ export abstract class UmbTreeItemContextBase<TreeItemType extends UmbTreeItemMod
|
||||
#onPageChange = (event: UmbChangeEvent) => {
|
||||
const target = event.target as UmbPaginationManager;
|
||||
this.#paging.skip = target.getSkip();
|
||||
this.loadChildren();
|
||||
this.loadMore();
|
||||
};
|
||||
|
||||
#debouncedCheckIsActive = debounce(() => this.#checkIsActive(), 100);
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
import type { UmbTreeItemContext } from '../index.js';
|
||||
import type { UmbTreeItemModelBase } from '../../types.js';
|
||||
import type { UmbTreeItemModel } from '../../types.js';
|
||||
import { UMB_TREE_ITEM_CONTEXT } from './tree-item-context-base.js';
|
||||
import { html, nothing, state, ifDefined, repeat, property } from '@umbraco-cms/backoffice/external/lit';
|
||||
import { UmbLitElement } from '@umbraco-cms/backoffice/lit-element';
|
||||
|
||||
// eslint-disable-next-line local-rules/enforce-element-suffix-on-element-class-name
|
||||
export abstract class UmbTreeItemElementBase<TreeItemModelType extends UmbTreeItemModelBase> extends UmbLitElement {
|
||||
export abstract class UmbTreeItemElementBase<TreeItemModelType extends UmbTreeItemModel> extends UmbLitElement {
|
||||
_item?: TreeItemModelType;
|
||||
@property({ type: Object, attribute: false })
|
||||
get item(): TreeItemModelType | undefined {
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
import type { UmbTreeItemModelBase } from '../types.js';
|
||||
import type { UmbTreeItemModel } from '../types.js';
|
||||
import type { UmbPaginationManager } from '../../utils/pagination-manager/pagination.manager.js';
|
||||
import type { Observable } from '@umbraco-cms/backoffice/external/rxjs';
|
||||
import type { UmbApi } from '@umbraco-cms/backoffice/extension-api';
|
||||
|
||||
export interface UmbTreeItemContext<TreeItemType extends UmbTreeItemModelBase> extends UmbApi {
|
||||
export interface UmbTreeItemContext<TreeItemType extends UmbTreeItemModel> extends UmbApi {
|
||||
unique?: string | null;
|
||||
entityType?: string;
|
||||
treeItem: Observable<TreeItemType | undefined>;
|
||||
|
||||
@@ -1,12 +1,13 @@
|
||||
import { UmbTreeItemContextBase } from '../tree-item-base/index.js';
|
||||
import type { UmbUniqueTreeItemModel } from '../../types.js';
|
||||
import type { UmbTreeItemModel, UmbTreeRootModel } from '../../types.js';
|
||||
import type { UmbControllerHost } from '@umbraco-cms/backoffice/controller-api';
|
||||
|
||||
export class UmbDefaultTreeItemContext<
|
||||
TreeItemModelType extends UmbUniqueTreeItemModel,
|
||||
> extends UmbTreeItemContextBase<TreeItemModelType> {
|
||||
TreeItemType extends UmbTreeItemModel,
|
||||
TreeRootType extends UmbTreeRootModel,
|
||||
> extends UmbTreeItemContextBase<TreeItemType, TreeRootType> {
|
||||
constructor(host: UmbControllerHost) {
|
||||
super(host, (x: UmbUniqueTreeItemModel) => x.unique);
|
||||
super(host);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
import { UmbTreeItemElementBase } from '../tree-item-base/index.js';
|
||||
import type { UmbUniqueTreeItemModel } from '../../types.js';
|
||||
import type { UmbTreeItemModel } from '../../types.js';
|
||||
import { customElement } from '@umbraco-cms/backoffice/external/lit';
|
||||
|
||||
@customElement('umb-default-tree-item')
|
||||
export class UmbDefaultTreeItemElement extends UmbTreeItemElementBase<UmbUniqueTreeItemModel> {}
|
||||
export class UmbDefaultTreeItemElement extends UmbTreeItemElementBase<UmbTreeItemModel> {}
|
||||
|
||||
export default UmbDefaultTreeItemElement;
|
||||
|
||||
|
||||
@@ -103,6 +103,7 @@ export class UmbTreePickerModalElement<TreeItemType extends UmbTreeItemModelBase
|
||||
selectionConfiguration: this._selectionConfiguration,
|
||||
filter: this.data?.filter,
|
||||
selectableFilter: this.data?.pickableFilter,
|
||||
startNode: this.data?.startNode,
|
||||
}}
|
||||
@selection-change=${this.#onSelectionChange}
|
||||
@selected=${this.#onSelected}
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import type { UmbTreeStartNode } from '../types.js';
|
||||
import { UMB_TREE_PICKER_MODAL_ALIAS } from './constants.js';
|
||||
import type { UmbPickerModalData, UmbPickerModalValue, UmbWorkspaceModalData } from '@umbraco-cms/backoffice/modal';
|
||||
import { UmbModalToken } from '@umbraco-cms/backoffice/modal';
|
||||
@@ -15,9 +16,11 @@ export interface UmbTreePickerModalData<
|
||||
TreeItemType,
|
||||
PathPatternParamsType extends UmbPathPatternParamsType = UmbPathPatternParamsType,
|
||||
> extends UmbPickerModalData<TreeItemType> {
|
||||
hideTreeRoot?: boolean;
|
||||
treeAlias?: string;
|
||||
// Consider if it makes sense to move this into the UmbPickerModalData interface, but for now this is a TreePicker feature. [NL]
|
||||
createAction?: UmbTreePickerModalCreateActionData<PathPatternParamsType>;
|
||||
startNode?: UmbTreeStartNode;
|
||||
}
|
||||
|
||||
export interface UmbTreePickerModalValue extends UmbPickerModalValue {}
|
||||
|
||||
@@ -1,17 +1,18 @@
|
||||
export interface UmbTreeItemModelBase {
|
||||
import type { UmbEntityModel } from '@umbraco-cms/backoffice/entity';
|
||||
|
||||
export interface UmbTreeItemModelBase extends UmbEntityModel {
|
||||
name: string;
|
||||
entityType: string;
|
||||
hasChildren: boolean;
|
||||
isFolder: boolean;
|
||||
icon?: string | null;
|
||||
}
|
||||
|
||||
export interface UmbUniqueTreeItemModel extends UmbTreeItemModelBase {
|
||||
export interface UmbTreeItemModel extends UmbTreeItemModelBase {
|
||||
unique: string;
|
||||
parentUnique: string | null;
|
||||
parent: UmbEntityModel;
|
||||
}
|
||||
|
||||
export interface UmbUniqueTreeRootModel extends UmbTreeItemModelBase {
|
||||
export interface UmbTreeRootModel extends UmbTreeItemModelBase {
|
||||
unique: null;
|
||||
}
|
||||
|
||||
@@ -20,3 +21,8 @@ export type UmbTreeSelectionConfiguration = {
|
||||
selectable?: boolean;
|
||||
selection?: Array<string | null>;
|
||||
};
|
||||
|
||||
export interface UmbTreeStartNode {
|
||||
unique: string;
|
||||
entityType: string;
|
||||
}
|
||||
|
||||
@@ -13,7 +13,7 @@ export function umbDeepMerge<
|
||||
|
||||
for (const key in source) {
|
||||
if (Object.prototype.hasOwnProperty.call(source, key) && source[key] !== undefined) {
|
||||
if (source[key]?.constructor === Object && fallback[key].constructor === Object) {
|
||||
if (source[key]?.constructor === Object && fallback[key]?.constructor === Object) {
|
||||
result[key] = umbDeepMerge(source[key] as any, fallback[key]);
|
||||
} else {
|
||||
result[key] = source[key] as any;
|
||||
|
||||
@@ -2,16 +2,22 @@ import { UmbChangeEvent } from '@umbraco-cms/backoffice/event';
|
||||
import { UmbNumberState } from '@umbraco-cms/backoffice/observable-api';
|
||||
|
||||
export class UmbPaginationManager extends EventTarget {
|
||||
#defaultValues = {
|
||||
totalItems: 0,
|
||||
totalPages: 1,
|
||||
currentPage: 1,
|
||||
};
|
||||
|
||||
#pageSize = new UmbNumberState(10);
|
||||
public readonly pageSize = this.#pageSize.asObservable();
|
||||
|
||||
#totalItems = new UmbNumberState(0);
|
||||
#totalItems = new UmbNumberState(this.#defaultValues.totalItems);
|
||||
public readonly totalItems = this.#totalItems.asObservable();
|
||||
|
||||
#totalPages = new UmbNumberState(1);
|
||||
#totalPages = new UmbNumberState(this.#defaultValues.totalPages);
|
||||
public readonly totalPages = this.#totalPages.asObservable();
|
||||
|
||||
#currentPage = new UmbNumberState(1);
|
||||
#currentPage = new UmbNumberState(this.#defaultValues.currentPage);
|
||||
public readonly currentPage = this.#currentPage.asObservable();
|
||||
|
||||
#skip = new UmbNumberState(0);
|
||||
@@ -101,6 +107,17 @@ export class UmbPaginationManager extends EventTarget {
|
||||
return this.#skip.getValue();
|
||||
}
|
||||
|
||||
/**
|
||||
* Clears the pagination manager values and resets them to their default values
|
||||
* @memberof UmbPaginationManager
|
||||
*/
|
||||
public clear() {
|
||||
this.#totalItems.setValue(this.#defaultValues.totalItems);
|
||||
this.#totalPages.setValue(this.#defaultValues.totalPages);
|
||||
this.#currentPage.setValue(this.#defaultValues.currentPage);
|
||||
this.#skip.setValue(0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculates the total number of pages
|
||||
* @memberof UmbPaginationManager
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import { html, customElement, state, ifDefined, map } from '@umbraco-cms/backoffice/external/lit';
|
||||
import { UmbTextStyles } from '@umbraco-cms/backoffice/style';
|
||||
import { css, customElement, html, ifDefined, map, state } from '@umbraco-cms/backoffice/external/lit';
|
||||
import { UmbLitElement } from '@umbraco-cms/backoffice/lit-element';
|
||||
import { UMB_WORKSPACE_CONTEXT } from '@umbraco-cms/backoffice/workspace';
|
||||
import { UmbTextStyles } from '@umbraco-cms/backoffice/style';
|
||||
import { UMB_SECTION_CONTEXT } from '@umbraco-cms/backoffice/section';
|
||||
import { UMB_WORKSPACE_CONTEXT } from '@umbraco-cms/backoffice/workspace';
|
||||
import type { UmbMenuStructureWorkspaceContext, UmbStructureItemModel } from '@umbraco-cms/backoffice/menu';
|
||||
|
||||
@customElement('umb-workspace-breadcrumb')
|
||||
@@ -84,7 +84,14 @@ export class UmbWorkspaceBreadcrumbElement extends UmbLitElement {
|
||||
`;
|
||||
}
|
||||
|
||||
static styles = [UmbTextStyles];
|
||||
static styles = [
|
||||
UmbTextStyles,
|
||||
css`
|
||||
:host {
|
||||
margin-left: var(--uui-size-layout-1);
|
||||
}
|
||||
`,
|
||||
];
|
||||
}
|
||||
|
||||
export default UmbWorkspaceBreadcrumbElement;
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
import { html, customElement, state, ifDefined } from '@umbraco-cms/backoffice/external/lit';
|
||||
import { UmbTextStyles } from '@umbraco-cms/backoffice/style';
|
||||
import { css, customElement, html, ifDefined, state } from '@umbraco-cms/backoffice/external/lit';
|
||||
import { UmbLitElement } from '@umbraco-cms/backoffice/lit-element';
|
||||
import type { UmbVariantDatasetWorkspaceContext } from '@umbraco-cms/backoffice/workspace';
|
||||
import { UMB_VARIANT_WORKSPACE_CONTEXT } from '@umbraco-cms/backoffice/workspace';
|
||||
import { UmbTextStyles } from '@umbraco-cms/backoffice/style';
|
||||
import { UmbVariantId } from '@umbraco-cms/backoffice/variant';
|
||||
import type { UmbAppLanguageContext } from '@umbraco-cms/backoffice/language';
|
||||
import { UMB_APP_LANGUAGE_CONTEXT } from '@umbraco-cms/backoffice/language';
|
||||
import { UMB_SECTION_CONTEXT } from '@umbraco-cms/backoffice/section';
|
||||
import { UMB_VARIANT_WORKSPACE_CONTEXT } from '@umbraco-cms/backoffice/workspace';
|
||||
import type { UmbAppLanguageContext } from '@umbraco-cms/backoffice/language';
|
||||
import type { UmbVariantDatasetWorkspaceContext } from '@umbraco-cms/backoffice/workspace';
|
||||
import type { UmbVariantStructureItemModel } from '@umbraco-cms/backoffice/menu';
|
||||
|
||||
@customElement('umb-workspace-variant-menu-breadcrumb')
|
||||
@@ -116,7 +116,7 @@ export class UmbWorkspaceVariantMenuBreadcrumbElement extends UmbLitElement {
|
||||
${this._structure.map(
|
||||
(structureItem) =>
|
||||
html`<uui-breadcrumb-item href="${ifDefined(this.#getHref(structureItem))}"
|
||||
>${this.#getItemVariantName(structureItem)}</uui-breadcrumb-item
|
||||
>${this.localize.string(this.#getItemVariantName(structureItem))}</uui-breadcrumb-item
|
||||
>`,
|
||||
)}
|
||||
<uui-breadcrumb-item>${this._name}</uui-breadcrumb-item>
|
||||
@@ -124,7 +124,14 @@ export class UmbWorkspaceVariantMenuBreadcrumbElement extends UmbLitElement {
|
||||
`;
|
||||
}
|
||||
|
||||
static styles = [UmbTextStyles];
|
||||
static styles = [
|
||||
UmbTextStyles,
|
||||
css`
|
||||
:host {
|
||||
margin-left: var(--uui-size-layout-1);
|
||||
}
|
||||
`,
|
||||
];
|
||||
}
|
||||
|
||||
export default UmbWorkspaceVariantMenuBreadcrumbElement;
|
||||
|
||||
@@ -1,11 +1,10 @@
|
||||
import { UmbTextStyles } from '@umbraco-cms/backoffice/style';
|
||||
import { css, html, nothing, customElement, property, state, repeat } from '@umbraco-cms/backoffice/external/lit';
|
||||
import type { UmbRoute, UmbRouterSlotInitEvent, UmbRouterSlotChangeEvent } from '@umbraco-cms/backoffice/router';
|
||||
import type { ManifestWorkspaceView } from '@umbraco-cms/backoffice/extension-registry';
|
||||
import { css, customElement, html, nothing, property, repeat, state, when } from '@umbraco-cms/backoffice/external/lit';
|
||||
import { createExtensionElement, UmbExtensionsManifestInitializer } from '@umbraco-cms/backoffice/extension-api';
|
||||
import { umbExtensionsRegistry } from '@umbraco-cms/backoffice/extension-registry';
|
||||
import { UmbExtensionsManifestInitializer, createExtensionElement } from '@umbraco-cms/backoffice/extension-api';
|
||||
|
||||
import { UmbLitElement } from '@umbraco-cms/backoffice/lit-element';
|
||||
import { UmbTextStyles } from '@umbraco-cms/backoffice/style';
|
||||
import type { ManifestWorkspaceView } from '@umbraco-cms/backoffice/extension-registry';
|
||||
import type { UmbRoute, UmbRouterSlotInitEvent, UmbRouterSlotChangeEvent } from '@umbraco-cms/backoffice/router';
|
||||
|
||||
/**
|
||||
* @element umb-workspace-editor
|
||||
@@ -92,14 +91,15 @@ export class UmbWorkspaceEditorElement extends UmbLitElement {
|
||||
<slot name="action-menu" slot="action-menu"></slot>
|
||||
${this.#renderRoutes()}
|
||||
<slot></slot>
|
||||
${this.enforceNoFooter
|
||||
? ''
|
||||
: html`
|
||||
<umb-workspace-footer slot="footer">
|
||||
<slot name="footer-info"></slot>
|
||||
<slot name="actions" slot="actions"></slot>
|
||||
</umb-workspace-footer>
|
||||
`}
|
||||
${when(
|
||||
!this.enforceNoFooter,
|
||||
() => html`
|
||||
<umb-workspace-footer slot="footer">
|
||||
<slot name="footer-info"></slot>
|
||||
<slot name="actions" slot="actions"></slot>
|
||||
</umb-workspace-footer>
|
||||
`,
|
||||
)}
|
||||
</umb-body-layout>
|
||||
`;
|
||||
}
|
||||
@@ -114,10 +114,10 @@ export class UmbWorkspaceEditorElement extends UmbLitElement {
|
||||
(view) => view.alias,
|
||||
(view) => html`
|
||||
<uui-tab
|
||||
.label="${view.meta.label ? this.localize.string(view.meta.label) : view.name}"
|
||||
href="${this._routerPath}/view/${view.meta.pathname}"
|
||||
?active="${'view/' + view.meta.pathname === this._activePath}">
|
||||
<umb-icon slot="icon" name="${view.meta.icon}"></umb-icon>
|
||||
.label="${view.meta.label ? this.localize.string(view.meta.label) : view.name}"
|
||||
?active=${'view/' + view.meta.pathname === this._activePath}>
|
||||
<umb-icon slot="icon" name=${view.meta.icon}></umb-icon>
|
||||
${view.meta.label ? this.localize.string(view.meta.label) : view.name}
|
||||
</uui-tab>
|
||||
`,
|
||||
@@ -132,8 +132,8 @@ export class UmbWorkspaceEditorElement extends UmbLitElement {
|
||||
if (!this.backPath) return nothing;
|
||||
return html`
|
||||
<uui-button
|
||||
class="back-button"
|
||||
slot="header"
|
||||
class="back-button"
|
||||
compact
|
||||
href=${this.backPath}
|
||||
label=${this.localize.term('general_back')}>
|
||||
@@ -143,20 +143,17 @@ export class UmbWorkspaceEditorElement extends UmbLitElement {
|
||||
}
|
||||
|
||||
#renderRoutes() {
|
||||
if (!this._routes || this._routes.length === 0) return nothing;
|
||||
return html`
|
||||
${this._routes && this._routes.length > 0
|
||||
? html`
|
||||
<umb-router-slot
|
||||
id="router-slot"
|
||||
.routes="${this._routes}"
|
||||
@init=${(event: UmbRouterSlotInitEvent) => {
|
||||
this._routerPath = event.target.absoluteRouterPath;
|
||||
}}
|
||||
@change=${(event: UmbRouterSlotChangeEvent) => {
|
||||
this._activePath = event.target.localActiveViewPath;
|
||||
}}></umb-router-slot>
|
||||
`
|
||||
: nothing}
|
||||
<umb-router-slot
|
||||
id="router-slot"
|
||||
.routes=${this._routes}
|
||||
@init=${(event: UmbRouterSlotInitEvent) => {
|
||||
this._routerPath = event.target.absoluteRouterPath;
|
||||
}}
|
||||
@change=${(event: UmbRouterSlotChangeEvent) => {
|
||||
this._activePath = event.target.localActiveViewPath;
|
||||
}}></umb-router-slot>
|
||||
`;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,13 +1,17 @@
|
||||
import { UMB_DATA_TYPE_ITEM_REPOSITORY_ALIAS } from '../../repository/index.js';
|
||||
import type { UmbDataTypeItemModel } from '../../repository/item/types.js';
|
||||
import type { UmbDataTypePickerModalData } from '../../modals/index.js';
|
||||
import { UMB_DATA_TYPE_PICKER_MODAL } from '../../modals/index.js';
|
||||
import type { UmbDataTypeTreeItemModel } from '../../tree/types.js';
|
||||
import { UmbPickerInputContext } from '@umbraco-cms/backoffice/picker-input';
|
||||
import type { UmbControllerHost } from '@umbraco-cms/backoffice/controller-api';
|
||||
|
||||
export class UmbDataTypePickerContext extends UmbPickerInputContext<UmbDataTypeItemModel> {
|
||||
export class UmbDataTypePickerContext extends UmbPickerInputContext<
|
||||
UmbDataTypeItemModel,
|
||||
UmbDataTypeTreeItemModel,
|
||||
UmbDataTypePickerModalData
|
||||
> {
|
||||
constructor(host: UmbControllerHost) {
|
||||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||
// @ts-ignore
|
||||
super(host, UMB_DATA_TYPE_ITEM_REPOSITORY_ALIAS, UMB_DATA_TYPE_PICKER_MODAL);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,10 +1,8 @@
|
||||
import type { UmbEntityModel } from '@umbraco-cms/backoffice/entity';
|
||||
import { UmbModalToken } from '@umbraco-cms/backoffice/modal';
|
||||
|
||||
export interface UmbDataTypeCreateOptionsModalData {
|
||||
parent: {
|
||||
entityType: string;
|
||||
unique: string | null;
|
||||
};
|
||||
parent: UmbEntityModel;
|
||||
}
|
||||
|
||||
export const UMB_DATA_TYPE_CREATE_OPTIONS_MODAL = new UmbModalToken<UmbDataTypeCreateOptionsModalData>(
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
import {
|
||||
type UmbTreePickerModalValue,
|
||||
type UmbTreePickerModalData,
|
||||
type UmbUniqueTreeItemModel,
|
||||
type UmbTreeItemModel,
|
||||
UMB_TREE_PICKER_MODAL_ALIAS,
|
||||
} from '@umbraco-cms/backoffice/tree';
|
||||
import { UmbModalToken } from '@umbraco-cms/backoffice/modal';
|
||||
|
||||
export type UmbDataTypePickerModalData = UmbTreePickerModalData<UmbUniqueTreeItemModel>;
|
||||
export type UmbDataTypePickerModalData = UmbTreePickerModalData<UmbTreeItemModel>;
|
||||
export type UmbDataTypePickerModalValue = UmbTreePickerModalValue;
|
||||
|
||||
export const UMB_DATA_TYPE_PICKER_MODAL = new UmbModalToken<UmbDataTypePickerModalData, UmbDataTypePickerModalValue>(
|
||||
|
||||
@@ -84,7 +84,7 @@ export class UmbPropertyEditorUIPickerModalElement extends UmbModalBaseElement<
|
||||
|
||||
render() {
|
||||
return html`
|
||||
<umb-body-layout headline="Select Property Editor UI">
|
||||
<umb-body-layout headline=${this.localize.term('propertyEditorPicker_openPropertyEditorPicker')}>
|
||||
<uui-box> ${this._renderFilter()} ${this._renderGrid()} </uui-box>
|
||||
<div slot="actions">
|
||||
<uui-button label="Close" @click=${this._rejectModal}></uui-button>
|
||||
|
||||
@@ -1,3 +1,8 @@
|
||||
import {
|
||||
UMB_DATA_TYPE_ENTITY_TYPE,
|
||||
UMB_DATA_TYPE_FOLDER_ENTITY_TYPE,
|
||||
UMB_DATA_TYPE_ROOT_ENTITY_TYPE,
|
||||
} from '../entity.js';
|
||||
import type { UmbDataTypeTreeItemModel } from './types.js';
|
||||
import type {
|
||||
UmbTreeChildrenOfRequestArgs,
|
||||
@@ -49,12 +54,12 @@ const getRootItems = async (args: UmbTreeRootItemsRequestArgs) => {
|
||||
};
|
||||
|
||||
const getChildrenOf = (args: UmbTreeChildrenOfRequestArgs) => {
|
||||
if (args.parentUnique === null) {
|
||||
if (args.parent.unique === null) {
|
||||
return getRootItems(args);
|
||||
} else {
|
||||
// eslint-disable-next-line local-rules/no-direct-api-import
|
||||
return DataTypeService.getTreeDataTypeChildren({
|
||||
parentId: args.parentUnique,
|
||||
parentId: args.parent.unique,
|
||||
skip: args.skip,
|
||||
take: args.take,
|
||||
});
|
||||
@@ -64,16 +69,19 @@ const getChildrenOf = (args: UmbTreeChildrenOfRequestArgs) => {
|
||||
const getAncestorsOf = (args: UmbTreeAncestorsOfRequestArgs) =>
|
||||
// eslint-disable-next-line local-rules/no-direct-api-import
|
||||
DataTypeService.getTreeDataTypeAncestors({
|
||||
descendantId: args.descendantUnique,
|
||||
descendantId: args.treeItem.unique,
|
||||
});
|
||||
|
||||
const mapper = (item: DataTypeTreeItemResponseModel): UmbDataTypeTreeItemModel => {
|
||||
return {
|
||||
unique: item.id,
|
||||
parentUnique: item.parent?.id || null,
|
||||
parent: {
|
||||
unique: item.parent?.id || null,
|
||||
entityType: item.parent ? UMB_DATA_TYPE_ENTITY_TYPE : UMB_DATA_TYPE_ROOT_ENTITY_TYPE,
|
||||
},
|
||||
icon: manifestPropertyEditorUis.find((ui) => ui.alias === item.editorUiAlias)?.meta.icon,
|
||||
name: item.name,
|
||||
entityType: item.isFolder ? 'data-type-folder' : 'data-type',
|
||||
entityType: item.isFolder ? UMB_DATA_TYPE_FOLDER_ENTITY_TYPE : UMB_DATA_TYPE_ENTITY_TYPE,
|
||||
isFolder: item.isFolder,
|
||||
hasChildren: item.hasChildren,
|
||||
};
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
import type { UmbDataTypeEntityType, UmbDataTypeFolderEntityType, UmbDataTypeRootEntityType } from '../entity.js';
|
||||
import type { UmbUniqueTreeItemModel, UmbUniqueTreeRootModel } from '@umbraco-cms/backoffice/tree';
|
||||
import type { UmbTreeItemModel, UmbTreeRootModel } from '@umbraco-cms/backoffice/tree';
|
||||
|
||||
export interface UmbDataTypeTreeItemModel extends UmbUniqueTreeItemModel {
|
||||
export interface UmbDataTypeTreeItemModel extends UmbTreeItemModel {
|
||||
entityType: UmbDataTypeEntityType | UmbDataTypeFolderEntityType;
|
||||
}
|
||||
|
||||
export interface UmbDataTypeTreeRootModel extends UmbUniqueTreeRootModel {
|
||||
export interface UmbDataTypeTreeRootModel extends UmbTreeRootModel {
|
||||
entityType: UmbDataTypeRootEntityType;
|
||||
}
|
||||
|
||||
@@ -40,6 +40,7 @@ export class UmbDataTypeWorkspaceContext
|
||||
|
||||
#parent = new UmbObjectState<{ entityType: string; unique: string | null } | undefined>(undefined);
|
||||
readonly parentUnique = this.#parent.asObservablePart((parent) => (parent ? parent.unique : undefined));
|
||||
readonly parentEntityType = this.#parent.asObservablePart((parent) => (parent ? parent.entityType : undefined));
|
||||
|
||||
#persistedData = new UmbObjectState<EntityType | undefined>(undefined);
|
||||
#currentData = new UmbObjectState<EntityType | undefined>(undefined);
|
||||
@@ -52,6 +53,7 @@ export class UmbDataTypeWorkspaceContext
|
||||
|
||||
readonly name = this.#currentData.asObservablePart((data) => data?.name);
|
||||
readonly unique = this.#currentData.asObservablePart((data) => data?.unique);
|
||||
readonly entityType = this.#currentData.asObservablePart((data) => data?.entityType);
|
||||
|
||||
readonly propertyEditorUiAlias = this.#currentData.asObservablePart((data) => data?.editorUiAlias);
|
||||
readonly propertyEditorSchemaAlias = this.#currentData.asObservablePart((data) => data?.editorAlias);
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import type { UmbUniqueTreeItemModel } from '../../core/tree/types.js';
|
||||
import type { UmbTreeItemModel } from '../../core/tree/types.js';
|
||||
import { UmbModalToken } from '../../core/modal/token/modal-token.js';
|
||||
import {
|
||||
type UmbTreePickerModalValue,
|
||||
@@ -6,7 +6,7 @@ import {
|
||||
UMB_TREE_PICKER_MODAL_ALIAS,
|
||||
} from '@umbraco-cms/backoffice/tree';
|
||||
|
||||
export type UmbDictionaryPickerModalData = UmbTreePickerModalData<UmbUniqueTreeItemModel>;
|
||||
export type UmbDictionaryPickerModalData = UmbTreePickerModalData<UmbTreeItemModel>;
|
||||
export type UmbDictionaryPickerModalValue = UmbTreePickerModalValue;
|
||||
|
||||
export const UMB_DICTIONARY_PICKER_MODAL = new UmbModalToken<
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { UMB_DICTIONARY_ENTITY_TYPE } from '../entity.js';
|
||||
import { UMB_DICTIONARY_ENTITY_TYPE, UMB_DICTIONARY_ROOT_ENTITY_TYPE } from '../entity.js';
|
||||
import type { UmbDictionaryTreeItemModel } from './types.js';
|
||||
import type {
|
||||
UmbTreeAncestorsOfRequestArgs,
|
||||
@@ -40,12 +40,12 @@ const getRootItems = (args: UmbTreeRootItemsRequestArgs) =>
|
||||
DictionaryService.getTreeDictionaryRoot({ skip: args.skip, take: args.take });
|
||||
|
||||
const getChildrenOf = (args: UmbTreeChildrenOfRequestArgs) => {
|
||||
if (args.parentUnique === null) {
|
||||
if (args.parent.unique === null) {
|
||||
return getRootItems(args);
|
||||
} else {
|
||||
// eslint-disable-next-line local-rules/no-direct-api-import
|
||||
return DictionaryService.getTreeDictionaryChildren({
|
||||
parentId: args.parentUnique,
|
||||
parentId: args.parent.unique,
|
||||
skip: args.skip,
|
||||
take: args.take,
|
||||
});
|
||||
@@ -55,13 +55,16 @@ const getChildrenOf = (args: UmbTreeChildrenOfRequestArgs) => {
|
||||
const getAncestorsOf = (args: UmbTreeAncestorsOfRequestArgs) =>
|
||||
// eslint-disable-next-line local-rules/no-direct-api-import
|
||||
DictionaryService.getTreeDictionaryAncestors({
|
||||
descendantId: args.descendantUnique,
|
||||
descendantId: args.treeItem.unique,
|
||||
});
|
||||
|
||||
const mapper = (item: NamedEntityTreeItemResponseModel): UmbDictionaryTreeItemModel => {
|
||||
return {
|
||||
unique: item.id,
|
||||
parentUnique: item.parent?.id || null,
|
||||
parent: {
|
||||
unique: item.parent?.id || null,
|
||||
entityType: item.parent ? UMB_DICTIONARY_ENTITY_TYPE : UMB_DICTIONARY_ROOT_ENTITY_TYPE,
|
||||
},
|
||||
name: item.name,
|
||||
entityType: UMB_DICTIONARY_ENTITY_TYPE,
|
||||
hasChildren: item.hasChildren,
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
import type { UmbDictionaryEntityType, UmbDictionaryRootEntityType } from '../entity.js';
|
||||
import type { UmbUniqueTreeItemModel, UmbUniqueTreeRootModel } from '@umbraco-cms/backoffice/tree';
|
||||
import type { UmbTreeItemModel, UmbTreeRootModel } from '@umbraco-cms/backoffice/tree';
|
||||
|
||||
export interface UmbDictionaryTreeItemModel extends UmbUniqueTreeItemModel {
|
||||
export interface UmbDictionaryTreeItemModel extends UmbTreeItemModel {
|
||||
entityType: UmbDictionaryEntityType;
|
||||
}
|
||||
|
||||
export interface UmbDictionaryTreeRootModel extends UmbUniqueTreeRootModel {
|
||||
export interface UmbDictionaryTreeRootModel extends UmbTreeRootModel {
|
||||
entityType: UmbDictionaryRootEntityType;
|
||||
}
|
||||
|
||||
@@ -23,11 +23,14 @@ export class UmbDictionaryWorkspaceContext
|
||||
|
||||
#parent = new UmbObjectState<{ entityType: string; unique: string | null } | undefined>(undefined);
|
||||
readonly parentUnique = this.#parent.asObservablePart((parent) => (parent ? parent.unique : undefined));
|
||||
readonly parentEntityType = this.#parent.asObservablePart((parent) => (parent ? parent.entityType : undefined));
|
||||
|
||||
#data = new UmbObjectState<UmbDictionaryDetailModel | undefined>(undefined);
|
||||
readonly data = this.#data.asObservable();
|
||||
|
||||
readonly unique = this.#data.asObservablePart((data) => data?.unique);
|
||||
readonly entityType = this.#data.asObservablePart((data) => data?.entityType);
|
||||
|
||||
readonly name = this.#data.asObservablePart((data) => data?.name);
|
||||
readonly dictionary = this.#data.asObservablePart((data) => data);
|
||||
|
||||
|
||||
@@ -1,10 +1,8 @@
|
||||
import type { UmbEntityModel } from '@umbraco-cms/backoffice/entity';
|
||||
import { UmbModalToken } from '@umbraco-cms/backoffice/modal';
|
||||
|
||||
export interface UmbDocumentBlueprintOptionsCreateModalData {
|
||||
parent: {
|
||||
unique: string | null;
|
||||
entityType: string;
|
||||
};
|
||||
parent: UmbEntityModel;
|
||||
}
|
||||
|
||||
export interface UmbDocumentBlueprintOptionsCreateModalValue {
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
export const UMB_DOCUMENT_BLUEPRINT_ROOT_ENTITY_TYPE = 'document-blueprint-root';
|
||||
export const UMB_DOCUMENT_BLUEPRINT_ENTITY_TYPE = 'document-blueprint';
|
||||
export const UMB_DOCUMENT_BLUEPRINT_FOLDER_ENTITY_TYPE = 'document-blueprint-folder';
|
||||
|
||||
export type UmbDocumentBlueprintRootEntityType = typeof UMB_DOCUMENT_BLUEPRINT_ROOT_ENTITY_TYPE;
|
||||
export type UmbDocumentBlueprintEntityType = typeof UMB_DOCUMENT_BLUEPRINT_ENTITY_TYPE;
|
||||
export type UmbDocumentBlueprintFolderEntityType = typeof UMB_DOCUMENT_BLUEPRINT_FOLDER_ENTITY_TYPE;
|
||||
|
||||
@@ -1,4 +1,8 @@
|
||||
import { UMB_DOCUMENT_BLUEPRINT_ENTITY_TYPE, UMB_DOCUMENT_BLUEPRINT_FOLDER_ENTITY_TYPE } from '../entity.js';
|
||||
import {
|
||||
UMB_DOCUMENT_BLUEPRINT_ENTITY_TYPE,
|
||||
UMB_DOCUMENT_BLUEPRINT_FOLDER_ENTITY_TYPE,
|
||||
UMB_DOCUMENT_BLUEPRINT_ROOT_ENTITY_TYPE,
|
||||
} from '../entity.js';
|
||||
import type { UmbDocumentBlueprintTreeItemModel } from './types.js';
|
||||
import { UmbTreeServerDataSourceBase } from '@umbraco-cms/backoffice/tree';
|
||||
import { DocumentBlueprintService } from '@umbraco-cms/backoffice/external/backend-api';
|
||||
@@ -40,12 +44,12 @@ const getRootItems = (args: UmbTreeRootItemsRequestArgs) =>
|
||||
DocumentBlueprintService.getTreeDocumentBlueprintRoot({ skip: args.skip, take: args.take });
|
||||
|
||||
const getChildrenOf = (args: UmbTreeChildrenOfRequestArgs) => {
|
||||
if (args.parentUnique === null) {
|
||||
if (args.parent.unique === null) {
|
||||
return getRootItems(args);
|
||||
} else {
|
||||
// eslint-disable-next-line local-rules/no-direct-api-import
|
||||
return DocumentBlueprintService.getTreeDocumentBlueprintChildren({
|
||||
parentId: args.parentUnique,
|
||||
parentId: args.parent.unique,
|
||||
});
|
||||
}
|
||||
};
|
||||
@@ -58,7 +62,10 @@ const getAncestorsOf = (args: UmbTreeAncestorsOfRequestArgs) => {
|
||||
const mapper = (item: DocumentBlueprintTreeItemResponseModel): UmbDocumentBlueprintTreeItemModel => {
|
||||
return {
|
||||
unique: item.id,
|
||||
parentUnique: item.parent?.id || null,
|
||||
parent: {
|
||||
unique: item.parent ? item.parent.id : null,
|
||||
entityType: item.parent ? UMB_DOCUMENT_BLUEPRINT_ENTITY_TYPE : UMB_DOCUMENT_BLUEPRINT_ROOT_ENTITY_TYPE,
|
||||
},
|
||||
name: (item as any).variants?.[0].name ?? item.name,
|
||||
entityType: item.isFolder ? UMB_DOCUMENT_BLUEPRINT_FOLDER_ENTITY_TYPE : UMB_DOCUMENT_BLUEPRINT_ENTITY_TYPE,
|
||||
isFolder: item.isFolder,
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import type { UmbDocumentBlueprintEntityType, UmbDocumentBlueprintFolderEntityType } from '../entity.js';
|
||||
import type { UmbUniqueTreeItemModel, UmbUniqueTreeRootModel } from '@umbraco-cms/backoffice/tree';
|
||||
import type { UmbTreeItemModel, UmbTreeRootModel } from '@umbraco-cms/backoffice/tree';
|
||||
|
||||
export interface UmbDocumentBlueprintTreeRootModel extends UmbUniqueTreeRootModel {}
|
||||
export interface UmbDocumentBlueprintTreeRootModel extends UmbTreeRootModel {}
|
||||
|
||||
export interface UmbDocumentBlueprintTreeItemModel extends UmbUniqueTreeItemModel {
|
||||
export interface UmbDocumentBlueprintTreeItemModel extends UmbTreeItemModel {
|
||||
entityType: UmbDocumentBlueprintEntityType | UmbDocumentBlueprintFolderEntityType;
|
||||
}
|
||||
|
||||
@@ -45,6 +45,7 @@ export class UmbDocumentBlueprintWorkspaceContext
|
||||
|
||||
#parent = new UmbObjectState<{ entityType: string; unique: string | null } | undefined>(undefined);
|
||||
readonly parentUnique = this.#parent.asObservablePart((parent) => (parent ? parent.unique : undefined));
|
||||
readonly parentEntityType = this.#parent.asObservablePart((parent) => (parent ? parent.entityType : undefined));
|
||||
|
||||
/**
|
||||
*/
|
||||
@@ -62,6 +63,8 @@ export class UmbDocumentBlueprintWorkspaceContext
|
||||
}
|
||||
|
||||
readonly unique = this.#currentData.asObservablePart((data) => data?.unique);
|
||||
readonly entityType = this.#currentData.asObservablePart((data) => data?.entityType);
|
||||
|
||||
readonly contentTypeUnique = this.#currentData.asObservablePart((data) => data?.documentType.unique);
|
||||
|
||||
readonly variants = this.#currentData.asObservablePart((data) => data?.variants || []);
|
||||
@@ -427,20 +430,6 @@ export class UmbDocumentBlueprintWorkspaceContext
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
concept notes:
|
||||
|
||||
public saveAndPreview() {
|
||||
|
||||
}
|
||||
*/
|
||||
|
||||
/*public createPropertyDatasetContext(host: UmbControllerHost, variantId: UmbVariantId) {
|
||||
// TODO: [LK] Temporary workaround/hack to get the workspace to load.
|
||||
const docCxt = new UmbDocumentWorkspaceContext(host);
|
||||
return new UmbDocumentPropertyDataContext(host, docCxt, variantId);
|
||||
}*/
|
||||
|
||||
public createPropertyDatasetContext(host: UmbControllerHost, variantId: UmbVariantId) {
|
||||
return new UmbDocumentBlueprintPropertyDataContext(host, this, variantId);
|
||||
}
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import type { UmbDocumentTypePickerModalData, UmbDocumentTypePickerModalValue } from '../../modals/index.js';
|
||||
import { UMB_DOCUMENT_TYPE_PICKER_MODAL } from '../../modals/index.js';
|
||||
import type { UmbDocumentTypeItemModel } from '../../repository/index.js';
|
||||
import type { UmbDocumentTypeTreeItemModel } from '../../tree/types.js';
|
||||
@@ -7,11 +8,11 @@ import type { UmbControllerHost } from '@umbraco-cms/backoffice/controller-api';
|
||||
|
||||
export class UmbDocumentTypePickerContext extends UmbPickerInputContext<
|
||||
UmbDocumentTypeItemModel,
|
||||
UmbDocumentTypeTreeItemModel
|
||||
UmbDocumentTypeTreeItemModel,
|
||||
UmbDocumentTypePickerModalData,
|
||||
UmbDocumentTypePickerModalValue
|
||||
> {
|
||||
constructor(host: UmbControllerHost) {
|
||||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||
// @ts-ignore
|
||||
super(host, UMB_DOCUMENT_TYPE_ITEM_REPOSITORY_ALIAS, UMB_DOCUMENT_TYPE_PICKER_MODAL);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,10 +1,8 @@
|
||||
import type { UmbEntityModel } from '@umbraco-cms/backoffice/entity';
|
||||
import { UmbModalToken } from '@umbraco-cms/backoffice/modal';
|
||||
|
||||
export interface UmbDocumentTypeCreateOptionsModalData {
|
||||
parent: {
|
||||
unique: string | null;
|
||||
entityType: string;
|
||||
};
|
||||
parent: UmbEntityModel;
|
||||
}
|
||||
|
||||
export const UMB_DOCUMENT_TYPE_CREATE_OPTIONS_MODAL = new UmbModalToken<UmbDocumentTypeCreateOptionsModalData>(
|
||||
|
||||
@@ -7,9 +7,6 @@ import {
|
||||
UMB_TREE_PICKER_MODAL_ALIAS,
|
||||
} from '@umbraco-cms/backoffice/tree';
|
||||
|
||||
/*export interface UmbDocumentTypePickerModalData
|
||||
extends UmbTreePickerModalData<UmbDocumentTypeTreeItemModel, typeof umbCreateDocumentTypeWorkspacePathGenerator> {}
|
||||
*/
|
||||
export type UmbDocumentTypePickerModalData = UmbTreePickerModalData<
|
||||
UmbDocumentTypeTreeItemModel,
|
||||
typeof UMB_CREATE_DOCUMENT_TYPE_WORKSPACE_PATH_PATTERN.PARAMS
|
||||
|
||||
@@ -1,4 +1,8 @@
|
||||
import { UMB_DOCUMENT_TYPE_ENTITY_TYPE, UMB_DOCUMENT_TYPE_FOLDER_ENTITY_TYPE } from '../entity.js';
|
||||
import {
|
||||
UMB_DOCUMENT_TYPE_ENTITY_TYPE,
|
||||
UMB_DOCUMENT_TYPE_FOLDER_ENTITY_TYPE,
|
||||
UMB_DOCUMENT_TYPE_ROOT_ENTITY_TYPE,
|
||||
} from '../entity.js';
|
||||
import type { UmbDocumentTypeTreeItemModel } from './types.js';
|
||||
import type {
|
||||
UmbTreeAncestorsOfRequestArgs,
|
||||
@@ -40,12 +44,12 @@ const getRootItems = (args: UmbTreeRootItemsRequestArgs) =>
|
||||
DocumentTypeService.getTreeDocumentTypeRoot({ skip: args.skip, take: args.take });
|
||||
|
||||
const getChildrenOf = (args: UmbTreeChildrenOfRequestArgs) => {
|
||||
if (args.parentUnique === null) {
|
||||
if (args.parent.unique === null) {
|
||||
return getRootItems({ skip: args.skip, take: args.take });
|
||||
} else {
|
||||
// eslint-disable-next-line local-rules/no-direct-api-import
|
||||
return DocumentTypeService.getTreeDocumentTypeChildren({
|
||||
parentId: args.parentUnique,
|
||||
parentId: args.parent.unique,
|
||||
skip: args.skip,
|
||||
take: args.take,
|
||||
});
|
||||
@@ -55,13 +59,16 @@ const getChildrenOf = (args: UmbTreeChildrenOfRequestArgs) => {
|
||||
const getAncestorsOf = (args: UmbTreeAncestorsOfRequestArgs) =>
|
||||
// eslint-disable-next-line local-rules/no-direct-api-import
|
||||
DocumentTypeService.getTreeDocumentTypeAncestors({
|
||||
descendantId: args.descendantUnique,
|
||||
descendantId: args.treeItem.unique,
|
||||
});
|
||||
|
||||
const mapper = (item: DocumentTypeTreeItemResponseModel): UmbDocumentTypeTreeItemModel => {
|
||||
return {
|
||||
unique: item.id,
|
||||
parentUnique: item.parent ? item.parent.id : null,
|
||||
parent: {
|
||||
unique: item.parent ? item.parent.id : null,
|
||||
entityType: item.parent ? UMB_DOCUMENT_TYPE_ENTITY_TYPE : UMB_DOCUMENT_TYPE_ROOT_ENTITY_TYPE,
|
||||
},
|
||||
name: item.name,
|
||||
entityType: item.isFolder ? UMB_DOCUMENT_TYPE_FOLDER_ENTITY_TYPE : UMB_DOCUMENT_TYPE_ENTITY_TYPE,
|
||||
hasChildren: item.hasChildren,
|
||||
|
||||
@@ -3,13 +3,13 @@ import type {
|
||||
UmbDocumentTypeFolderEntityType,
|
||||
UmbDocumentTypeRootEntityType,
|
||||
} from '../entity.js';
|
||||
import type { UmbUniqueTreeItemModel, UmbUniqueTreeRootModel } from '@umbraco-cms/backoffice/tree';
|
||||
import type { UmbTreeItemModel, UmbTreeRootModel } from '@umbraco-cms/backoffice/tree';
|
||||
|
||||
export interface UmbDocumentTypeTreeItemModel extends UmbUniqueTreeItemModel {
|
||||
export interface UmbDocumentTypeTreeItemModel extends UmbTreeItemModel {
|
||||
entityType: UmbDocumentTypeEntityType | UmbDocumentTypeFolderEntityType;
|
||||
isElement: boolean;
|
||||
}
|
||||
|
||||
export interface UmbDocumentTypeTreeRootModel extends UmbUniqueTreeRootModel {
|
||||
export interface UmbDocumentTypeTreeRootModel extends UmbTreeRootModel {
|
||||
entityType: UmbDocumentTypeRootEntityType;
|
||||
}
|
||||
|
||||
@@ -42,12 +42,14 @@ export class UmbDocumentTypeWorkspaceContext
|
||||
|
||||
#parent = new UmbObjectState<{ entityType: string; unique: string | null } | undefined>(undefined);
|
||||
readonly parentUnique = this.#parent.asObservablePart((parent) => (parent ? parent.unique : undefined));
|
||||
readonly parentEntityType = this.#parent.asObservablePart((parent) => (parent ? parent.entityType : undefined));
|
||||
|
||||
#persistedData = new UmbObjectState<EntityType | undefined>(undefined);
|
||||
|
||||
// General for content types:
|
||||
//readonly data;
|
||||
readonly unique;
|
||||
readonly entityType;
|
||||
readonly name;
|
||||
getName(): string | undefined {
|
||||
return this.structure.getOwnerContentType()?.name;
|
||||
@@ -81,6 +83,8 @@ export class UmbDocumentTypeWorkspaceContext
|
||||
//this.data = this.structure.ownerContentType;
|
||||
|
||||
this.unique = this.structure.ownerContentTypeObservablePart((data) => data?.unique);
|
||||
this.entityType = this.structure.ownerContentTypeObservablePart((data) => data?.entityType);
|
||||
|
||||
this.name = this.structure.ownerContentTypeObservablePart((data) => data?.name);
|
||||
this.alias = this.structure.ownerContentTypeObservablePart((data) => data?.alias);
|
||||
this.description = this.structure.ownerContentTypeObservablePart((data) => data?.description);
|
||||
|
||||
@@ -83,10 +83,8 @@ export class UmbDocumentTypeWorkspaceViewStructureElement extends UmbLitElement
|
||||
<umb-input-document-type
|
||||
.documentTypesOnly=${true}
|
||||
.selection=${this._allowedContentTypeUniques ?? []}
|
||||
@change="${(e: CustomEvent) => {
|
||||
const sortedContentTypesList: Array<UmbContentTypeSortModel> = (
|
||||
e.target as UmbInputDocumentTypeElement
|
||||
).selection.map((id, index) => ({
|
||||
@change="${(e: CustomEvent & { target: UmbInputDocumentTypeElement }) => {
|
||||
const sortedContentTypesList: Array<UmbContentTypeSortModel> = e.target.selection.map((id, index) => ({
|
||||
contentType: { unique: id },
|
||||
sortOrder: index,
|
||||
}));
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { html, customElement, property, state, map } from '@umbraco-cms/backoffice/external/lit';
|
||||
import { css, customElement, html, map, property, state } from '@umbraco-cms/backoffice/external/lit';
|
||||
import { UmbDocumentTypeStructureRepository } from '@umbraco-cms/backoffice/document-type';
|
||||
import { UmbLitElement } from '@umbraco-cms/backoffice/lit-element';
|
||||
import { UMB_COLLECTION_CONTEXT } from '@umbraco-cms/backoffice/collection';
|
||||
@@ -8,9 +8,9 @@ import {
|
||||
UMB_DOCUMENT_ROOT_ENTITY_TYPE,
|
||||
UMB_DOCUMENT_WORKSPACE_CONTEXT,
|
||||
} from '@umbraco-cms/backoffice/document';
|
||||
import { UMB_WORKSPACE_MODAL, UmbModalRouteRegistrationController } from '@umbraco-cms/backoffice/modal';
|
||||
import type { ManifestCollectionAction } from '@umbraco-cms/backoffice/extension-registry';
|
||||
import type { UmbAllowedDocumentTypeModel } from '@umbraco-cms/backoffice/document-type';
|
||||
import { UMB_WORKSPACE_MODAL, UmbModalRouteRegistrationController } from '@umbraco-cms/backoffice/modal';
|
||||
|
||||
@customElement('umb-create-document-collection-action')
|
||||
export class UmbCreateDocumentCollectionActionElement extends UmbLitElement {
|
||||
@@ -20,6 +20,9 @@ export class UmbCreateDocumentCollectionActionElement extends UmbLitElement {
|
||||
@state()
|
||||
private _createDocumentPath = '';
|
||||
|
||||
@state()
|
||||
private _currentView?: string;
|
||||
|
||||
@state()
|
||||
private _documentUnique?: string;
|
||||
|
||||
@@ -29,6 +32,9 @@ export class UmbCreateDocumentCollectionActionElement extends UmbLitElement {
|
||||
@state()
|
||||
private _popoverOpen = false;
|
||||
|
||||
@state()
|
||||
private _rootPathName?: string;
|
||||
|
||||
@state()
|
||||
private _useInfiniteEditor = false;
|
||||
|
||||
@@ -59,6 +65,12 @@ export class UmbCreateDocumentCollectionActionElement extends UmbLitElement {
|
||||
});
|
||||
|
||||
this.consumeContext(UMB_COLLECTION_CONTEXT, (collectionContext) => {
|
||||
this.observe(collectionContext.view.currentView, (currentView) => {
|
||||
this._currentView = currentView?.meta.pathName;
|
||||
});
|
||||
this.observe(collectionContext.view.rootPathName, (rootPathName) => {
|
||||
this._rootPathName = rootPathName;
|
||||
});
|
||||
this.observe(collectionContext.filter, (filter) => {
|
||||
this._useInfiniteEditor = filter.useInfiniteEditor == true;
|
||||
});
|
||||
@@ -87,20 +99,22 @@ export class UmbCreateDocumentCollectionActionElement extends UmbLitElement {
|
||||
}
|
||||
|
||||
#getCreateUrl(item: UmbAllowedDocumentTypeModel) {
|
||||
// TODO: [LK] I need help with this. I don't know what the infinity editor URL should be.
|
||||
// TODO: Yes, revisit the path extension of the routable modal, cause this is not pretty...? [NL]
|
||||
return this._useInfiniteEditor
|
||||
? this._createDocumentPath +
|
||||
UMB_CREATE_DOCUMENT_WORKSPACE_PATH_PATTERN.generateLocal({
|
||||
parentEntityType: this._documentUnique ? UMB_DOCUMENT_ENTITY_TYPE : UMB_DOCUMENT_ROOT_ENTITY_TYPE,
|
||||
parentUnique: this._documentUnique ?? 'null',
|
||||
documentTypeUnique: item.unique,
|
||||
})
|
||||
: UMB_CREATE_DOCUMENT_WORKSPACE_PATH_PATTERN.generateAbsolute({
|
||||
if (this._useInfiniteEditor) {
|
||||
return (
|
||||
this._createDocumentPath.replace(`${this._rootPathName}`, `${this._rootPathName}/${this._currentView}`) +
|
||||
UMB_CREATE_DOCUMENT_WORKSPACE_PATH_PATTERN.generateLocal({
|
||||
parentEntityType: this._documentUnique ? UMB_DOCUMENT_ENTITY_TYPE : UMB_DOCUMENT_ROOT_ENTITY_TYPE,
|
||||
parentUnique: this._documentUnique ?? 'null',
|
||||
documentTypeUnique: item.unique,
|
||||
});
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
return UMB_CREATE_DOCUMENT_WORKSPACE_PATH_PATTERN.generateAbsolute({
|
||||
parentEntityType: this._documentUnique ? UMB_DOCUMENT_ENTITY_TYPE : UMB_DOCUMENT_ROOT_ENTITY_TYPE,
|
||||
parentUnique: this._documentUnique ?? 'null',
|
||||
documentTypeUnique: item.unique,
|
||||
});
|
||||
}
|
||||
|
||||
render() {
|
||||
@@ -113,11 +127,9 @@ export class UmbCreateDocumentCollectionActionElement extends UmbLitElement {
|
||||
const item = this._allowedDocumentTypes[0];
|
||||
const label = (this.manifest?.meta.label ?? this.localize.term('general_create')) + ' ' + item.name;
|
||||
|
||||
return html`<uui-button
|
||||
color="default"
|
||||
href=${this.#getCreateUrl(item)}
|
||||
label=${label}
|
||||
look="outline"></uui-button>`;
|
||||
return html`
|
||||
<uui-button color="default" href=${this.#getCreateUrl(item)} label=${label} look="outline"></uui-button>
|
||||
`;
|
||||
}
|
||||
|
||||
#renderDropdown() {
|
||||
@@ -149,6 +161,14 @@ export class UmbCreateDocumentCollectionActionElement extends UmbLitElement {
|
||||
</uui-popover-container>
|
||||
`;
|
||||
}
|
||||
|
||||
static styles = [
|
||||
css`
|
||||
uui-scroll-container {
|
||||
max-height: 500px;
|
||||
}
|
||||
`,
|
||||
];
|
||||
}
|
||||
|
||||
export default UmbCreateDocumentCollectionActionElement;
|
||||
|
||||
@@ -1,10 +1,17 @@
|
||||
import type { UmbDocumentPickerModalData, UmbDocumentPickerModalValue } from '../../modals/index.js';
|
||||
import { UMB_DOCUMENT_PICKER_MODAL } from '../../modals/index.js';
|
||||
import { UMB_DOCUMENT_ITEM_REPOSITORY_ALIAS } from '../../repository/index.js';
|
||||
import type { UmbDocumentItemModel } from '../../repository/index.js';
|
||||
import type { UmbDocumentTreeItemModel } from '../../tree/types.js';
|
||||
import { UmbPickerInputContext } from '@umbraco-cms/backoffice/picker-input';
|
||||
import type { UmbControllerHost } from '@umbraco-cms/backoffice/controller-api';
|
||||
|
||||
export class UmbDocumentPickerContext extends UmbPickerInputContext<UmbDocumentItemModel> {
|
||||
export class UmbDocumentPickerContext extends UmbPickerInputContext<
|
||||
UmbDocumentItemModel,
|
||||
UmbDocumentTreeItemModel,
|
||||
UmbDocumentPickerModalData,
|
||||
UmbDocumentPickerModalValue
|
||||
> {
|
||||
constructor(host: UmbControllerHost) {
|
||||
super(host, UMB_DOCUMENT_ITEM_REPOSITORY_ALIAS, UMB_DOCUMENT_PICKER_MODAL, (entry) => entry.unique);
|
||||
}
|
||||
|
||||
@@ -7,6 +7,7 @@ import { UmbSorterController } from '@umbraco-cms/backoffice/sorter';
|
||||
import { UMB_WORKSPACE_MODAL, UmbModalRouteRegistrationController } from '@umbraco-cms/backoffice/modal';
|
||||
import { UUIFormControlMixin } from '@umbraco-cms/backoffice/external/uui';
|
||||
import type { UmbDocumentItemModel } from '@umbraco-cms/backoffice/document';
|
||||
import type { UmbTreeStartNode } from '@umbraco-cms/backoffice/tree';
|
||||
|
||||
@customElement('umb-input-document')
|
||||
export class UmbInputDocumentElement extends UUIFormControlMixin(UmbLitElement, '') {
|
||||
@@ -80,8 +81,8 @@ export class UmbInputDocumentElement extends UUIFormControlMixin(UmbLitElement,
|
||||
return this.#pickerContext.getSelection();
|
||||
}
|
||||
|
||||
@property({ type: String })
|
||||
startNodeId?: string;
|
||||
@property({ type: Object, attribute: false })
|
||||
startNode?: UmbTreeStartNode;
|
||||
|
||||
@property({ type: Array })
|
||||
allowedContentTypeIds?: string[] | undefined;
|
||||
@@ -156,6 +157,7 @@ export class UmbInputDocumentElement extends UUIFormControlMixin(UmbLitElement,
|
||||
this.#pickerContext.openPicker({
|
||||
hideTreeRoot: true,
|
||||
pickableFilter: this.#pickableFilter,
|
||||
startNode: this.startNode,
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -1,10 +1,8 @@
|
||||
import type { UmbEntityModel } from '@umbraco-cms/backoffice/entity';
|
||||
import { UmbModalToken } from '@umbraco-cms/backoffice/modal';
|
||||
|
||||
export interface UmbDocumentCreateOptionsModalData {
|
||||
parent: {
|
||||
unique: string | null;
|
||||
entityType: string;
|
||||
};
|
||||
parent: UmbEntityModel;
|
||||
documentType: {
|
||||
unique: string;
|
||||
} | null;
|
||||
|
||||
@@ -1,10 +1,8 @@
|
||||
import { UMB_DUPLICATE_DOCUMENT_MODAL_ALIAS } from './constants.js';
|
||||
import type { UmbEntityModel } from '@umbraco-cms/backoffice/entity';
|
||||
import { UmbModalToken } from '@umbraco-cms/backoffice/modal';
|
||||
|
||||
export interface UmbDuplicateDocumentModalData {
|
||||
unique: string | null;
|
||||
entityType: string;
|
||||
}
|
||||
export interface UmbDuplicateDocumentModalData extends UmbEntityModel {}
|
||||
|
||||
export interface UmbDuplicateDocumentModalValue {
|
||||
destination: {
|
||||
|
||||
@@ -1,10 +1,12 @@
|
||||
import type { UmbInputDocumentElement } from '../../components/input-document/input-document.element.js';
|
||||
import { UMB_DOCUMENT_ENTITY_TYPE } from '../../entity.js';
|
||||
import { html, customElement, property, state } from '@umbraco-cms/backoffice/external/lit';
|
||||
import { UmbLitElement } from '@umbraco-cms/backoffice/lit-element';
|
||||
import { UmbPropertyValueChangeEvent } from '@umbraco-cms/backoffice/property-editor';
|
||||
import type { NumberRangeValueType } from '@umbraco-cms/backoffice/models';
|
||||
import type { UmbPropertyEditorConfigCollection } from '@umbraco-cms/backoffice/property-editor';
|
||||
import type { UmbPropertyEditorUiElement } from '@umbraco-cms/backoffice/extension-registry';
|
||||
import type { UmbTreeStartNode } from '@umbraco-cms/backoffice/tree';
|
||||
|
||||
@customElement('umb-property-editor-ui-document-picker')
|
||||
export class UmbPropertyEditorUIDocumentPickerElement extends UmbLitElement implements UmbPropertyEditorUiElement {
|
||||
@@ -15,28 +17,30 @@ export class UmbPropertyEditorUIDocumentPickerElement extends UmbLitElement impl
|
||||
if (!config) return;
|
||||
|
||||
const minMax = config.getValueByAlias<NumberRangeValueType>('validationLimit');
|
||||
this.min = minMax?.min ?? 0;
|
||||
this.max = minMax?.max ?? Infinity;
|
||||
if (minMax) {
|
||||
this._min = minMax.min && minMax.min > 0 ? minMax.min : 0;
|
||||
this._max = minMax.max && minMax.max > 0 ? minMax.max : Infinity;
|
||||
}
|
||||
|
||||
this.ignoreUserStartNodes = config.getValueByAlias('ignoreUserStartNodes') ?? false;
|
||||
this.startNodeId = config.getValueByAlias('startNodeId');
|
||||
this.showOpenButton = config.getValueByAlias('showOpenButton') ?? false;
|
||||
this._ignoreUserStartNodes = config.getValueByAlias('ignoreUserStartNodes') ?? false;
|
||||
this._startNodeId = config.getValueByAlias('startNodeId');
|
||||
this._showOpenButton = config.getValueByAlias('showOpenButton') ?? false;
|
||||
}
|
||||
|
||||
@state()
|
||||
min = 0;
|
||||
private _min = 0;
|
||||
|
||||
@state()
|
||||
max = Infinity;
|
||||
private _max = Infinity;
|
||||
|
||||
@state()
|
||||
startNodeId?: string;
|
||||
private _startNodeId?: string;
|
||||
|
||||
@state()
|
||||
showOpenButton?: boolean;
|
||||
private _showOpenButton?: boolean;
|
||||
|
||||
@state()
|
||||
ignoreUserStartNodes?: boolean;
|
||||
private _ignoreUserStartNodes?: boolean;
|
||||
|
||||
#onChange(event: CustomEvent & { target: UmbInputDocumentElement }) {
|
||||
this.value = event.target.selection.join(',');
|
||||
@@ -44,14 +48,18 @@ export class UmbPropertyEditorUIDocumentPickerElement extends UmbLitElement impl
|
||||
}
|
||||
|
||||
render() {
|
||||
const startNode: UmbTreeStartNode | undefined = this._startNodeId
|
||||
? { unique: this._startNodeId, entityType: UMB_DOCUMENT_ENTITY_TYPE }
|
||||
: undefined;
|
||||
|
||||
return html`
|
||||
<umb-input-document
|
||||
.min=${this.min}
|
||||
.max=${this.max}
|
||||
.startNodeId=${this.startNodeId ?? ''}
|
||||
.min=${this._min}
|
||||
.max=${this._max}
|
||||
.startNode=${startNode}
|
||||
.value=${this.value ?? ''}
|
||||
?showOpenButton=${this.showOpenButton}
|
||||
?ignoreUserStartNodes=${this.ignoreUserStartNodes}
|
||||
?ignoreUserStartNodes=${this._ignoreUserStartNodes}
|
||||
?showOpenButton=${this._showOpenButton}
|
||||
@change=${this.#onChange}>
|
||||
</umb-input-document>
|
||||
`;
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { UMB_DOCUMENT_ENTITY_TYPE } from '../../entity.js';
|
||||
import { UMB_DOCUMENT_RECYCLE_BIN_ROOT_ENTITY_TYPE } from '../entity.js';
|
||||
import type { UmbDocumentRecycleBinTreeItemModel } from './types.js';
|
||||
import type { DocumentRecycleBinItemResponseModel } from '@umbraco-cms/backoffice/external/backend-api';
|
||||
import { DocumentService } from '@umbraco-cms/backoffice/external/backend-api';
|
||||
@@ -40,12 +41,12 @@ const getRootItems = (args: UmbTreeRootItemsRequestArgs) =>
|
||||
DocumentService.getRecycleBinDocumentRoot({ skip: args.skip, take: args.take });
|
||||
|
||||
const getChildrenOf = (args: UmbTreeChildrenOfRequestArgs) => {
|
||||
if (args.parentUnique === null) {
|
||||
if (args.parent.unique === null) {
|
||||
return getRootItems(args);
|
||||
} else {
|
||||
// eslint-disable-next-line local-rules/no-direct-api-import
|
||||
return DocumentService.getRecycleBinDocumentChildren({
|
||||
parentId: args.parentUnique,
|
||||
parentId: args.parent.unique,
|
||||
skip: args.skip,
|
||||
take: args.take,
|
||||
});
|
||||
@@ -55,13 +56,16 @@ const getChildrenOf = (args: UmbTreeChildrenOfRequestArgs) => {
|
||||
const getAncestorsOf = (args: UmbTreeAncestorsOfRequestArgs) =>
|
||||
// eslint-disable-next-line local-rules/no-direct-api-import
|
||||
DocumentService.getTreeDocumentAncestors({
|
||||
descendantId: args.descendantUnique,
|
||||
descendantId: args.treeItem.unique,
|
||||
});
|
||||
|
||||
const mapper = (item: DocumentRecycleBinItemResponseModel): UmbDocumentRecycleBinTreeItemModel => {
|
||||
return {
|
||||
unique: item.id,
|
||||
parentUnique: item.parent ? item.parent.id : null,
|
||||
parent: {
|
||||
unique: item.parent ? item.parent.id : null,
|
||||
entityType: item.parent ? UMB_DOCUMENT_ENTITY_TYPE : UMB_DOCUMENT_RECYCLE_BIN_ROOT_ENTITY_TYPE,
|
||||
},
|
||||
entityType: UMB_DOCUMENT_ENTITY_TYPE,
|
||||
noAccess: false,
|
||||
isTrashed: true,
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import type { UmbDocumentTreeItemModel } from '../../tree/index.js';
|
||||
import type { UmbUniqueTreeRootModel } from '@umbraco-cms/backoffice/tree';
|
||||
import type { UmbTreeRootModel } from '@umbraco-cms/backoffice/tree';
|
||||
|
||||
export interface UmbDocumentRecycleBinTreeItemModel extends UmbDocumentTreeItemModel {}
|
||||
|
||||
export interface UmbDocumentRecycleBinTreeRootModel extends UmbUniqueTreeRootModel {}
|
||||
export interface UmbDocumentRecycleBinTreeRootModel extends UmbTreeRootModel {}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { UMB_DOCUMENT_ENTITY_TYPE } from '../entity.js';
|
||||
import { UMB_DOCUMENT_ENTITY_TYPE, UMB_DOCUMENT_ROOT_ENTITY_TYPE } from '../entity.js';
|
||||
import type { UmbDocumentTreeItemModel } from './types.js';
|
||||
import type {
|
||||
UmbTreeAncestorsOfRequestArgs,
|
||||
@@ -40,12 +40,12 @@ const getRootItems = (args: UmbTreeRootItemsRequestArgs) =>
|
||||
DocumentService.getTreeDocumentRoot({ skip: args.skip, take: args.take });
|
||||
|
||||
const getChildrenOf = (args: UmbTreeChildrenOfRequestArgs) => {
|
||||
if (args.parentUnique === null) {
|
||||
if (args.parent.unique === null) {
|
||||
return getRootItems(args);
|
||||
} else {
|
||||
// eslint-disable-next-line local-rules/no-direct-api-import
|
||||
return DocumentService.getTreeDocumentChildren({
|
||||
parentId: args.parentUnique,
|
||||
parentId: args.parent.unique,
|
||||
skip: args.skip,
|
||||
take: args.take,
|
||||
});
|
||||
@@ -55,13 +55,16 @@ const getChildrenOf = (args: UmbTreeChildrenOfRequestArgs) => {
|
||||
const getAncestorsOf = (args: UmbTreeAncestorsOfRequestArgs) =>
|
||||
// eslint-disable-next-line local-rules/no-direct-api-import
|
||||
DocumentService.getTreeDocumentAncestors({
|
||||
descendantId: args.descendantUnique,
|
||||
descendantId: args.treeItem.unique,
|
||||
});
|
||||
|
||||
const mapper = (item: DocumentTreeItemResponseModel): UmbDocumentTreeItemModel => {
|
||||
return {
|
||||
unique: item.id,
|
||||
parentUnique: item.parent ? item.parent.id : null,
|
||||
parent: {
|
||||
unique: item.parent ? item.parent.id : null,
|
||||
entityType: item.parent ? UMB_DOCUMENT_ENTITY_TYPE : UMB_DOCUMENT_ROOT_ENTITY_TYPE,
|
||||
},
|
||||
entityType: UMB_DOCUMENT_ENTITY_TYPE,
|
||||
noAccess: item.noAccess,
|
||||
isTrashed: item.isTrashed,
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user