Merge branch 'main' into feature/swap-to-popover-container
This commit is contained in:
@@ -1,2 +1 @@
|
||||
@umbraco-cms:registry=https://registry.npmjs.org
|
||||
legacy-peer-deps=true
|
||||
|
||||
@@ -97,7 +97,6 @@
|
||||
"backoffice:test:e2e": "npx playwright test",
|
||||
"build-storybook": "npm run wc-analyze && storybook build",
|
||||
"build:for:cms": "npm run build && node ./devops/build/copy-to-cms.js",
|
||||
"build:for:npm": "npm run build && tsc-alias -f -p src/tsconfig.build.json && npm run generate:jsonschema:dist && npm run wc-analyze && npm run wc-analyze:vscode",
|
||||
"build:for:static": "vite build",
|
||||
"build:vite": "tsc && vite build --mode staging",
|
||||
"build": "tsc --project ./src/tsconfig.build.json && rollup -c ./src/rollup.config.js",
|
||||
@@ -116,7 +115,7 @@
|
||||
"lint:fix": "npm run lint -- --fix",
|
||||
"lint": "eslint src",
|
||||
"new-extension": "plop --plopfile ./devops/plop/plop.js",
|
||||
"prepublishOnly": "node ./devops/publish/cleanse-pkg.js",
|
||||
"prepack": "tsc-alias -f -p src/tsconfig.build.json && npm run generate:jsonschema:dist && npm run wc-analyze && npm run wc-analyze:vscode && node ./devops/publish/cleanse-pkg.js",
|
||||
"preview": "vite preview --open",
|
||||
"storybook:build": "npm run wc-analyze && storybook build",
|
||||
"storybook": "npm run wc-analyze && storybook dev -p 6006",
|
||||
|
||||
@@ -70,14 +70,18 @@ export class UmbBackofficeHeaderSectionsElement extends UmbLitElement {
|
||||
|
||||
static styles: CSSResultGroup = [
|
||||
css`
|
||||
:host {
|
||||
display: contents;
|
||||
}
|
||||
#tabs {
|
||||
color: var(--uui-color-header-contrast);
|
||||
height: 60px;
|
||||
flex-basis: 100%;
|
||||
font-size: 16px;
|
||||
--uui-tab-text: var(--uui-color-header-contrast);
|
||||
--uui-tab-text-hover: var(--uui-color-header-contrast-emphasis);
|
||||
--uui-tab-text-active: var(--uui-color-header-contrast-emphasis);
|
||||
--uui-tab-background: var(--uui-color-header-background);
|
||||
background-color: var(--uui-color-header-background);
|
||||
--uui-tab-group-dropdown-background: var(--uui-color-header-surface);
|
||||
}
|
||||
`,
|
||||
];
|
||||
|
||||
@@ -1782,8 +1782,8 @@ export default {
|
||||
inviteAnotherUser: 'Invite another user',
|
||||
inviteUserHelp:
|
||||
'Invite new users to give them access to Umbraco. An invite email will be sent to the\n user with information on how to log in to Umbraco. Invites last for 72 hours.\n ',
|
||||
language: 'Language',
|
||||
languageHelp: 'Set the language you will see in menus and dialogs',
|
||||
language: 'UI Culture',
|
||||
languageHelp: 'Set the culture you will see in menus and dialogs',
|
||||
lastLockoutDate: 'Last lockout date',
|
||||
lastLogin: 'Last login',
|
||||
lastPasswordChangeDate: 'Password last changed',
|
||||
|
||||
@@ -1,31 +1,44 @@
|
||||
import type { UmbApi } from "../models/api.interface.js";
|
||||
import type { ApiLoaderExports, ApiLoaderProperty, ClassConstructor, ElementAndApiLoaderProperty, ElementLoaderExports } from "../types/utils.js";
|
||||
import type { UmbApi } from '../models/api.interface.js';
|
||||
import type {
|
||||
ApiLoaderExports,
|
||||
ApiLoaderProperty,
|
||||
ClassConstructor,
|
||||
ElementAndApiLoaderProperty,
|
||||
ElementLoaderExports,
|
||||
} from '../types/utils.js';
|
||||
|
||||
export async function loadManifestApi<ApiType extends UmbApi>(property: ApiLoaderProperty<ApiType> | ElementAndApiLoaderProperty<any, ApiType>): Promise<ClassConstructor<ApiType> | undefined> {
|
||||
const propType = typeof property
|
||||
if(propType === 'function') {
|
||||
if((property as ClassConstructor).prototype) {
|
||||
export async function loadManifestApi<ApiType extends UmbApi>(
|
||||
property: ApiLoaderProperty<ApiType> | ElementAndApiLoaderProperty<any, ApiType>,
|
||||
): Promise<ClassConstructor<ApiType> | undefined> {
|
||||
const propType = typeof property;
|
||||
if (propType === 'function') {
|
||||
if ((property as ClassConstructor).prototype) {
|
||||
// Class Constructor
|
||||
return property as ClassConstructor<ApiType>;
|
||||
} else {
|
||||
// Promise function
|
||||
const result = await (property as (Exclude<Exclude<ApiLoaderProperty<ApiType>, string>, ClassConstructor<ApiType>>))();
|
||||
if(typeof result === 'object' && result != null) {
|
||||
const exportValue = 'api' in result ? result.api : undefined || 'default' in result ? (result as Exclude<(typeof result), ElementLoaderExports>).default : undefined;
|
||||
if(exportValue && typeof exportValue === 'function') {
|
||||
const result = await (
|
||||
property as Exclude<Exclude<ApiLoaderProperty<ApiType>, string>, ClassConstructor<ApiType>>
|
||||
)();
|
||||
if (typeof result === 'object' && result != null) {
|
||||
const exportValue =
|
||||
('api' in result ? result.api : undefined) ||
|
||||
('default' in result ? (result as Exclude<typeof result, ElementLoaderExports>).default : undefined);
|
||||
if (exportValue && typeof exportValue === 'function') {
|
||||
return exportValue;
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if(propType === 'string') {
|
||||
} else if (propType === 'string') {
|
||||
// Import string
|
||||
const result = await (import(/* @vite-ignore */ property as string) as unknown as ApiLoaderExports<ApiType>);
|
||||
if(typeof result === 'object' && result != null) {
|
||||
const exportValue = 'api' in result ? result.api : undefined || 'default' in result ? result.default : undefined;
|
||||
if(exportValue && typeof exportValue === 'function') {
|
||||
if (result && typeof result === 'object') {
|
||||
const exportValue =
|
||||
('api' in result ? result.api : undefined) || ('default' in result ? result.default : undefined);
|
||||
if (exportValue && typeof exportValue === 'function') {
|
||||
return exportValue;
|
||||
}
|
||||
}
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,28 +1,40 @@
|
||||
import type { ApiLoaderExports, ClassConstructor, ElementAndApiLoaderProperty, ElementLoaderExports, ElementLoaderProperty } from "../types/utils.js";
|
||||
import type {
|
||||
ApiLoaderExports,
|
||||
ClassConstructor,
|
||||
ElementAndApiLoaderProperty,
|
||||
ElementLoaderExports,
|
||||
ElementLoaderProperty,
|
||||
} from '../types/utils.js';
|
||||
|
||||
|
||||
export async function loadManifestElement<ElementType extends HTMLElement>(property: ElementLoaderProperty<ElementType> | ElementAndApiLoaderProperty<ElementType>): Promise<ClassConstructor<ElementType> | undefined> {
|
||||
const propType = typeof property
|
||||
if(propType === 'function') {
|
||||
if((property as ClassConstructor).prototype) {
|
||||
export async function loadManifestElement<ElementType extends HTMLElement>(
|
||||
property: ElementLoaderProperty<ElementType> | ElementAndApiLoaderProperty<ElementType>,
|
||||
): Promise<ClassConstructor<ElementType> | undefined> {
|
||||
const propType = typeof property;
|
||||
if (propType === 'function') {
|
||||
if ((property as ClassConstructor).prototype) {
|
||||
// Class Constructor
|
||||
return property as ClassConstructor<ElementType>;
|
||||
} else {
|
||||
// Promise function
|
||||
const result = await (property as (Exclude<Exclude<typeof property, string>, ClassConstructor<ElementType>>))();
|
||||
if(typeof result === 'object' && result !== null) {
|
||||
const exportValue = 'element' in result ? result.element : undefined || 'default' in result ? (result as Exclude<(typeof result), ApiLoaderExports>).default : undefined;
|
||||
if(exportValue && typeof exportValue === 'function') {
|
||||
const result = await (property as Exclude<Exclude<typeof property, string>, ClassConstructor<ElementType>>)();
|
||||
if (typeof result === 'object' && result !== null) {
|
||||
const exportValue =
|
||||
('element' in result ? result.element : undefined) ||
|
||||
('default' in result ? (result as Exclude<typeof result, ApiLoaderExports>).default : undefined);
|
||||
if (exportValue && typeof exportValue === 'function') {
|
||||
return exportValue;
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if(propType === 'string') {
|
||||
} else if (propType === 'string') {
|
||||
// Import string
|
||||
const result = await (import(/* @vite-ignore */ property as string) as unknown as ElementLoaderExports<ElementType>);
|
||||
if(typeof result === 'object' && result != null) {
|
||||
const exportValue = 'element' in result ? result.element : undefined || 'default' in result ? result.default : undefined;
|
||||
if(exportValue && typeof exportValue === 'function') {
|
||||
const result = await (import(
|
||||
/* @vite-ignore */ property as string
|
||||
) as unknown as ElementLoaderExports<ElementType>);
|
||||
if (result && typeof result === 'object') {
|
||||
const exportValue =
|
||||
('element' in result ? result.element : undefined) || ('default' in result ? result.default : undefined);
|
||||
if (exportValue && typeof exportValue === 'function') {
|
||||
return exportValue;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,18 +1,29 @@
|
||||
import type { JsLoaderProperty } from "../types/utils.js";
|
||||
import type { CssLoaderExports, CssLoaderProperty } from '../types/utils.js';
|
||||
|
||||
export async function loadManifestPlainCss<CssType = string>(property: JsLoaderProperty<CssType>): Promise<CssType | undefined> {
|
||||
export async function loadManifestPlainCss<CssType extends string>(
|
||||
property: CssLoaderProperty<CssType>,
|
||||
): Promise<CssType | undefined> {
|
||||
const propType = typeof property;
|
||||
if(propType === 'function') {
|
||||
const result = await (property as (Exclude<(typeof property), string>))();
|
||||
if(result != null) {
|
||||
return result;
|
||||
if (propType === 'function') {
|
||||
// Promise function
|
||||
const result = await (property as Exclude<typeof property, string>)();
|
||||
if (typeof result === 'object' && result != null) {
|
||||
const exportValue =
|
||||
('css' in result ? result.css : undefined) || ('default' in result ? result.default : undefined);
|
||||
if (exportValue && typeof exportValue === 'string') {
|
||||
return exportValue as CssType;
|
||||
}
|
||||
}
|
||||
} else if(propType === 'string') {
|
||||
} else if (propType === 'string') {
|
||||
// Import string
|
||||
const result = await (import(/* @vite-ignore */ property as string) as unknown as CssType);
|
||||
if(result != null) {
|
||||
return result;
|
||||
const result = await (import(/* @vite-ignore */ property as string) as unknown as CssLoaderExports<CssType>);
|
||||
if (typeof result === 'object' && result != null) {
|
||||
const exportValue =
|
||||
('css' in result ? result.css : undefined) || ('default' in result ? result.default : undefined);
|
||||
if (exportValue && typeof exportValue === 'string') {
|
||||
return exportValue;
|
||||
}
|
||||
}
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,18 +1,21 @@
|
||||
import type { JsLoaderProperty } from "../types/utils.js";
|
||||
import type { JsLoaderProperty } from '../types/utils.js';
|
||||
|
||||
export async function loadManifestPlainJs<JsType extends object>(property: JsLoaderProperty<JsType>): Promise<JsType | undefined> {
|
||||
export async function loadManifestPlainJs<JsType extends object>(
|
||||
property: JsLoaderProperty<JsType>,
|
||||
): Promise<JsType | undefined> {
|
||||
const propType = typeof property;
|
||||
if(propType === 'function') {
|
||||
const result = await (property as (Exclude<(typeof property), string>))();
|
||||
if(typeof result === 'object' && result != null) {
|
||||
if (propType === 'function') {
|
||||
// Promise function
|
||||
const result = await (property as Exclude<typeof property, string>)();
|
||||
if (typeof result === 'object' && result != null) {
|
||||
return result;
|
||||
}
|
||||
} else if(propType === 'string') {
|
||||
} else if (propType === 'string') {
|
||||
// Import string
|
||||
const result = await (import(/* @vite-ignore */ property as string) as unknown as JsType);
|
||||
if(typeof result === 'object' && result != null) {
|
||||
if (typeof result === 'object' && result != null) {
|
||||
return result;
|
||||
}
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -26,12 +26,12 @@ export interface ManifestElementWithElementName extends ManifestElement {
|
||||
elementName: string;
|
||||
}
|
||||
|
||||
export interface ManifestPlainCss<CssType = unknown> extends ManifestBase {
|
||||
export interface ManifestPlainCss extends ManifestBase {
|
||||
/**
|
||||
* The file location of the stylesheet file to load
|
||||
* @TJS-type string
|
||||
*/
|
||||
css?: CssLoaderProperty<CssType>;
|
||||
css?: CssLoaderProperty;
|
||||
}
|
||||
|
||||
export interface ManifestPlainJs<JsType> extends ManifestBase {
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
import type { UmbApi } from "../models/index.js";
|
||||
|
||||
import type { UmbApi } from '../models/index.js';
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
export type HTMLElementConstructor<T = HTMLElement> = new (...args: any[]) => T;
|
||||
@@ -7,80 +6,60 @@ export type HTMLElementConstructor<T = HTMLElement> = new (...args: any[]) => T;
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
export type ClassConstructor<T = object> = new (...args: any[]) => T;
|
||||
|
||||
|
||||
|
||||
// Module Export Types:
|
||||
|
||||
export type ElementLoaderExports<
|
||||
ElementType extends HTMLElement = HTMLElement
|
||||
> = ({default: ClassConstructor<ElementType>} | {element: ClassConstructor<ElementType>})// | Omit<Omit<object, 'element'>, 'default'>
|
||||
export type CssLoaderExports<CssType extends string = string> = { default: CssType } | { css: CssType };
|
||||
|
||||
export type ApiLoaderExports<
|
||||
ApiType extends UmbApi = UmbApi
|
||||
> = ({default: ClassConstructor<ApiType>} | {api: ClassConstructor<ApiType>})//| Omit<Omit<object, 'api'>, 'default'>
|
||||
export type ElementLoaderExports<ElementType extends HTMLElement = HTMLElement> =
|
||||
| { default: ClassConstructor<ElementType> }
|
||||
| { element: ClassConstructor<ElementType> }; // | Omit<Omit<object, 'element'>, 'default'>
|
||||
|
||||
export type ApiLoaderExports<ApiType extends UmbApi = UmbApi> =
|
||||
| { default: ClassConstructor<ApiType> }
|
||||
| { api: ClassConstructor<ApiType> }; //| Omit<Omit<object, 'api'>, 'default'>
|
||||
|
||||
export type ElementAndApiLoaderExports<
|
||||
ElementType extends HTMLElement = HTMLElement,
|
||||
ApiType extends UmbApi = UmbApi
|
||||
> = ({api: ClassConstructor<ApiType>} | {element: ClassConstructor<ElementType>} | {api: ClassConstructor<ApiType>, element: ClassConstructor<ElementType>})// | Omit<Omit<Omit<object, 'element'>, 'api'>, 'default'>
|
||||
|
||||
ApiType extends UmbApi = UmbApi,
|
||||
> =
|
||||
| { api: ClassConstructor<ApiType> }
|
||||
| { element: ClassConstructor<ElementType> }
|
||||
| { api: ClassConstructor<ApiType>; element: ClassConstructor<ElementType> }; // | Omit<Omit<Omit<object, 'element'>, 'api'>, 'default'>
|
||||
|
||||
// Promise Types:
|
||||
|
||||
export type CssLoaderPromise<
|
||||
CssType = unknown
|
||||
> = (() => Promise<CssType>)
|
||||
export type CssLoaderPromise<CssType extends string = string> = () => Promise<CssLoaderExports<CssType>>;
|
||||
|
||||
export type JsLoaderPromise<
|
||||
JsType
|
||||
> = (() => Promise<JsType>)
|
||||
export type JsLoaderPromise<JsExportType> = () => Promise<JsExportType>;
|
||||
|
||||
export type ElementLoaderPromise<
|
||||
ElementType extends HTMLElement = HTMLElement
|
||||
> = (() => Promise<ElementLoaderExports<ElementType>>)
|
||||
export type ElementLoaderPromise<ElementType extends HTMLElement = HTMLElement> = () => Promise<
|
||||
ElementLoaderExports<ElementType>
|
||||
>;
|
||||
|
||||
export type ApiLoaderPromise<
|
||||
ApiType extends UmbApi = UmbApi
|
||||
> = (() => Promise<ApiLoaderExports<ApiType>>)
|
||||
export type ApiLoaderPromise<ApiType extends UmbApi = UmbApi> = () => Promise<ApiLoaderExports<ApiType>>;
|
||||
|
||||
export type ElementAndApiLoaderPromise<
|
||||
ElementType extends HTMLElement = HTMLElement,
|
||||
ApiType extends UmbApi = UmbApi
|
||||
> = (() => Promise<ElementAndApiLoaderExports<ElementType, ApiType>>)
|
||||
|
||||
ApiType extends UmbApi = UmbApi,
|
||||
> = () => Promise<ElementAndApiLoaderExports<ElementType, ApiType>>;
|
||||
|
||||
// Property Types:
|
||||
|
||||
export type CssLoaderProperty<CssType = string> = (
|
||||
string
|
||||
|
|
||||
CssLoaderPromise<CssType>
|
||||
);
|
||||
export type JsLoaderProperty<JsType> = (
|
||||
string
|
||||
|
|
||||
JsLoaderPromise<JsType>
|
||||
);
|
||||
export type ElementLoaderProperty<ElementType extends HTMLElement = HTMLElement> = (
|
||||
string
|
||||
|
|
||||
ElementLoaderPromise<ElementType>
|
||||
|
|
||||
ClassConstructor<ElementType>
|
||||
);
|
||||
export type ApiLoaderProperty<ApiType extends UmbApi = UmbApi> = (
|
||||
string
|
||||
|
|
||||
ApiLoaderPromise<ApiType>
|
||||
|
|
||||
ClassConstructor<ApiType>
|
||||
);
|
||||
export type ElementAndApiLoaderProperty<ElementType extends HTMLElement = HTMLElement, ApiType extends UmbApi = UmbApi> = (
|
||||
string
|
||||
|
|
||||
ElementAndApiLoaderPromise<ElementType, ApiType>
|
||||
|
|
||||
ElementLoaderPromise<ElementType>
|
||||
|
|
||||
ApiLoaderPromise<ApiType>
|
||||
);
|
||||
export type CssLoaderProperty<CssType extends string = string> = string | CssLoaderPromise<CssType>;
|
||||
export type JsLoaderProperty<JsExportType> = string | JsLoaderPromise<JsExportType>;
|
||||
export type ElementLoaderProperty<ElementType extends HTMLElement = HTMLElement> =
|
||||
| string
|
||||
| ElementLoaderPromise<ElementType>
|
||||
| ClassConstructor<ElementType>;
|
||||
export type ApiLoaderProperty<ApiType extends UmbApi = UmbApi> =
|
||||
| string
|
||||
| ApiLoaderPromise<ApiType>
|
||||
| ClassConstructor<ApiType>;
|
||||
export type ElementAndApiLoaderProperty<
|
||||
ElementType extends HTMLElement = HTMLElement,
|
||||
ApiType extends UmbApi = UmbApi,
|
||||
> =
|
||||
| string
|
||||
| ElementAndApiLoaderPromise<ElementType, ApiType>
|
||||
| ElementLoaderPromise<ElementType>
|
||||
| ApiLoaderPromise<ApiType>;
|
||||
|
||||
@@ -8,7 +8,7 @@ export const data: Array<UserResponseModel & { type: string }> = [
|
||||
mediaStartNodeIds: [],
|
||||
name: 'Umbraco User',
|
||||
email: 'noreply@umbraco.com',
|
||||
languageIsoCode: 'en-US',
|
||||
languageIsoCode: 'en-us',
|
||||
state: UserStateModel.ACTIVE,
|
||||
lastLoginDate: '9/10/2022',
|
||||
lastLockoutDate: '11/23/2021',
|
||||
@@ -25,7 +25,7 @@ export const data: Array<UserResponseModel & { type: string }> = [
|
||||
mediaStartNodeIds: ['f2f81a40-c989-4b6b-84e2-057cecd3adc1'],
|
||||
name: 'Amelie Walker',
|
||||
email: 'awalker1@domain.com',
|
||||
languageIsoCode: 'Japanese',
|
||||
languageIsoCode: 'da-dk',
|
||||
state: UserStateModel.INACTIVE,
|
||||
lastLoginDate: '2023-10-12T18:30:32.879Z',
|
||||
lastLockoutDate: null,
|
||||
@@ -42,7 +42,7 @@ export const data: Array<UserResponseModel & { type: string }> = [
|
||||
mediaStartNodeIds: [],
|
||||
name: 'Oliver Kim',
|
||||
email: 'okim1@domain.com',
|
||||
languageIsoCode: 'Russian',
|
||||
languageIsoCode: 'da-dk',
|
||||
state: UserStateModel.ACTIVE,
|
||||
lastLoginDate: '2023-10-12T18:30:32.879Z',
|
||||
lastLockoutDate: null,
|
||||
@@ -59,7 +59,7 @@ export const data: Array<UserResponseModel & { type: string }> = [
|
||||
mediaStartNodeIds: [],
|
||||
name: 'Eliana Nieves',
|
||||
email: 'enieves1@domain.com',
|
||||
languageIsoCode: 'Spanish',
|
||||
languageIsoCode: 'en-us',
|
||||
state: UserStateModel.INVITED,
|
||||
lastLoginDate: '2023-10-12T18:30:32.879Z',
|
||||
lastLockoutDate: null,
|
||||
@@ -76,7 +76,7 @@ export const data: Array<UserResponseModel & { type: string }> = [
|
||||
mediaStartNodeIds: [],
|
||||
name: 'Jasmine Patel',
|
||||
email: 'jpatel1@domain.com',
|
||||
languageIsoCode: 'Hindi',
|
||||
languageIsoCode: 'en-us',
|
||||
state: UserStateModel.LOCKED_OUT,
|
||||
lastLoginDate: '2023-10-12T18:30:32.879Z',
|
||||
lastLockoutDate: '2023-10-12T18:30:32.879Z',
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { UmbTextStyles } from "@umbraco-cms/backoffice/style";
|
||||
import { UmbTextStyles } from '@umbraco-cms/backoffice/style';
|
||||
import {
|
||||
css,
|
||||
html,
|
||||
@@ -191,6 +191,7 @@ export class UmbBodyLayoutElement extends LitElement {
|
||||
height: 100%;
|
||||
align-items: center;
|
||||
box-sizing: border-box;
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
#navigation-slot,
|
||||
|
||||
@@ -2,6 +2,6 @@ import type { ManifestPlainCss } from '@umbraco-cms/backoffice/extension-api';
|
||||
/**
|
||||
* Theme manifest for styling the backoffice of Umbraco such as dark, high contrast etc
|
||||
*/
|
||||
export interface ManifestTheme extends ManifestPlainCss<string> {
|
||||
export interface ManifestTheme extends ManifestPlainCss {
|
||||
type: 'theme';
|
||||
}
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
import './ui-culture-input/ui-culture-input.element.js';
|
||||
|
||||
export { UmbUiCultureInputElement } from './ui-culture-input/ui-culture-input.element.js';
|
||||
@@ -0,0 +1,95 @@
|
||||
import { UmbChangeEvent } from '@umbraco-cms/backoffice/event';
|
||||
import { css, html, customElement, query, state, property } from '@umbraco-cms/backoffice/external/lit';
|
||||
import { FormControlMixin, UUIComboboxElement, UUIComboboxEvent } from '@umbraco-cms/backoffice/external/uui';
|
||||
import { UmbLitElement } from '@umbraco-cms/internal/lit-element';
|
||||
import { ManifestLocalization, umbExtensionsRegistry } from '@umbraco-cms/backoffice/extension-registry';
|
||||
|
||||
interface UmbCultureInputOption {
|
||||
name: string;
|
||||
value: string;
|
||||
}
|
||||
|
||||
@customElement('umb-ui-culture-input')
|
||||
export class UmbUiCultureInputElement extends FormControlMixin(UmbLitElement) {
|
||||
@state()
|
||||
private _options: Array<UmbCultureInputOption> = [];
|
||||
|
||||
@query('uui-combobox')
|
||||
private _selectElement!: HTMLInputElement;
|
||||
|
||||
@property({ type: String })
|
||||
get value() {
|
||||
return this._value;
|
||||
}
|
||||
set value(value: FormDataEntryValue | FormData) {
|
||||
if (typeof value === 'string') {
|
||||
const oldValue = this._value;
|
||||
this._value = value.toLowerCase();
|
||||
this.requestUpdate('value', oldValue);
|
||||
}
|
||||
}
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
this.#observeTranslations();
|
||||
}
|
||||
|
||||
#observeTranslations() {
|
||||
this.observe(
|
||||
umbExtensionsRegistry.extensionsOfType('localization'),
|
||||
(localizationManifests) => {
|
||||
this.#mapToOptions(localizationManifests);
|
||||
},
|
||||
'umbObserveLocalizationManifests',
|
||||
);
|
||||
}
|
||||
|
||||
#mapToOptions(manifests: Array<ManifestLocalization>) {
|
||||
this._options = manifests
|
||||
.filter((isoCode) => isoCode !== undefined)
|
||||
.map((manifest) => ({
|
||||
name: manifest.name,
|
||||
value: manifest.meta.culture.toLowerCase(),
|
||||
}));
|
||||
}
|
||||
|
||||
protected getFormElement() {
|
||||
return this._selectElement;
|
||||
}
|
||||
|
||||
#onChange(event: UUIComboboxEvent) {
|
||||
event.stopPropagation();
|
||||
const target = event.target as UUIComboboxElement;
|
||||
|
||||
if (typeof target?.value === 'string') {
|
||||
this.value = target.value;
|
||||
this.dispatchEvent(new UmbChangeEvent());
|
||||
}
|
||||
}
|
||||
|
||||
render() {
|
||||
return html` <uui-combobox value="${this._value}" @change=${this.#onChange}>
|
||||
<uui-combobox-list>
|
||||
${this._options.map(
|
||||
(option) => html`<uui-combobox-list-option value="${option.value}">${option.name}</uui-combobox-list-option>`,
|
||||
)}
|
||||
</uui-combobox-list>
|
||||
</uui-combobox>`;
|
||||
}
|
||||
|
||||
static styles = [
|
||||
css`
|
||||
:host {
|
||||
display: block;
|
||||
}
|
||||
`,
|
||||
];
|
||||
}
|
||||
|
||||
export default UmbUiCultureInputElement;
|
||||
|
||||
declare global {
|
||||
interface HTMLElementTagNameMap {
|
||||
'umb-ui-culture-input': UmbUiCultureInputElement;
|
||||
}
|
||||
}
|
||||
@@ -4,3 +4,4 @@ import './localize-number.element.js';
|
||||
import './localize-relative-time.element.js';
|
||||
|
||||
export * from './registry/localization.registry.js';
|
||||
export { UmbUiCultureInputElement } from './components/index.js';
|
||||
|
||||
@@ -102,7 +102,8 @@ export class UmbSectionMainViewElement extends UmbLitElement {
|
||||
}
|
||||
|
||||
#renderDashboards() {
|
||||
return this._dashboards.length > 0
|
||||
// Only show dashboards if there are more than one dashboard or if there are both dashboards and views
|
||||
return (this._dashboards.length > 0 && this._views.length > 0) || this._dashboards.length > 1
|
||||
? html`
|
||||
<uui-tab-group slot="header" id="dashboards">
|
||||
${this._dashboards.map((dashboard) => {
|
||||
@@ -121,7 +122,8 @@ export class UmbSectionMainViewElement extends UmbLitElement {
|
||||
}
|
||||
|
||||
#renderViews() {
|
||||
return this._views.length > 0
|
||||
// Only show views if there are more than one view or if there are both dashboards and views
|
||||
return (this._views.length > 0 && this._dashboards.length > 0) || this._views.length > 1
|
||||
? html`
|
||||
<uui-tab-group slot="navigation" id="views">
|
||||
${this._views.map((view) => {
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { UmbTextStyles } from "@umbraco-cms/backoffice/style";
|
||||
import { UmbTextStyles } from '@umbraco-cms/backoffice/style';
|
||||
import { css, html, LitElement, customElement } from '@umbraco-cms/backoffice/external/lit';
|
||||
|
||||
@customElement('umb-section-main')
|
||||
@@ -17,6 +17,7 @@ export class UmbSectionMainElement extends LitElement {
|
||||
:host {
|
||||
flex: 1 1 auto;
|
||||
height: 100%;
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
main {
|
||||
|
||||
@@ -17,14 +17,14 @@ export const themes: Array<ManifestTypes> = [
|
||||
type: 'theme',
|
||||
alias: 'umb-dark-theme',
|
||||
name: 'Dark',
|
||||
css: 'src/packages/settings/themes/themes/dark.theme.css',
|
||||
css: '/umbraco/backoffice/css/dark.theme.css',
|
||||
weight: 200,
|
||||
},
|
||||
{
|
||||
type: 'theme',
|
||||
alias: 'umb-high-contrast-theme',
|
||||
name: 'High contrast',
|
||||
css: 'src/packages/settings/themes/themes/high-contrast.theme.css',
|
||||
css: '/umbraco/backoffice/css/high-contrast.theme.css',
|
||||
weight: 100,
|
||||
},
|
||||
];
|
||||
|
||||
@@ -42,14 +42,16 @@ export class UmbThemeContext extends UmbBaseController {
|
||||
if (themes.length > 0 && themes[0].css) {
|
||||
const activeTheme = themes[0];
|
||||
if (typeof activeTheme.css === 'function') {
|
||||
const styleEl = (this.#styleElement = document.createElement('style'));
|
||||
styleEl.setAttribute('type', 'text/css');
|
||||
document.head.appendChild(styleEl);
|
||||
this.#styleElement = document.createElement('style') as HTMLStyleElement;
|
||||
// We store the current style element so we can check if it has been replaced by another theme in between.
|
||||
const currentStyleEl = this.#styleElement;
|
||||
currentStyleEl.setAttribute('type', 'text/css');
|
||||
|
||||
const result = await loadManifestPlainCss(activeTheme.css);
|
||||
// Checking that this is still our styleElement, it has not been replaced with another theme in between.
|
||||
if (result && styleEl === this.#styleElement) {
|
||||
styleEl.appendChild(document.createTextNode(result));
|
||||
if (result && currentStyleEl === this.#styleElement) {
|
||||
currentStyleEl.appendChild(document.createTextNode(result));
|
||||
document.head.appendChild(currentStyleEl);
|
||||
}
|
||||
} else if (typeof activeTheme.css === 'string') {
|
||||
this.#styleElement = document.createElement('link');
|
||||
@@ -58,16 +60,23 @@ export class UmbThemeContext extends UmbBaseController {
|
||||
document.head.appendChild(this.#styleElement);
|
||||
}
|
||||
} else {
|
||||
console.log('remove style element', this.#styleElement);
|
||||
// We could not load a theme for this alias, so we remove the theme.
|
||||
localStorage.removeItem(LOCAL_STORAGE_KEY);
|
||||
this.#styleElement?.childNodes.forEach((node) => node.remove());
|
||||
this.#styleElement?.setAttribute('href', '');
|
||||
this.#styleElement = null;
|
||||
}
|
||||
},
|
||||
);
|
||||
} else {
|
||||
// Super clean, we got a falsy value, so we remove the theme.
|
||||
|
||||
localStorage.removeItem(LOCAL_STORAGE_KEY);
|
||||
this.#styleElement?.remove();
|
||||
this.#styleElement?.childNodes.forEach((node) => node.remove());
|
||||
this.#styleElement?.setAttribute('href', '');
|
||||
this.#styleElement = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,91 +2,34 @@ import { UMB_USER_WORKSPACE_CONTEXT } from '../../user-workspace.context.js';
|
||||
import { html, customElement, state, ifDefined, css } from '@umbraco-cms/backoffice/external/lit';
|
||||
import { UmbLitElement } from '@umbraco-cms/internal/lit-element';
|
||||
import { UmbTextStyles } from '@umbraco-cms/backoffice/style';
|
||||
import { UUISelectElement } from '@umbraco-cms/backoffice/external/uui';
|
||||
import { UserResponseModel } from '@umbraco-cms/backoffice/backend-api';
|
||||
import { UMB_CURRENT_USER_CONTEXT, UmbCurrentUser } from '@umbraco-cms/backoffice/current-user';
|
||||
import { firstValueFrom } from '@umbraco-cms/backoffice/external/rxjs';
|
||||
import { umbExtensionsRegistry } from '@umbraco-cms/backoffice/extension-registry';
|
||||
import { UmbChangeEvent } from '@umbraco-cms/backoffice/event';
|
||||
import { UmbUiCultureInputElement } from '@umbraco-cms/backoffice/localization';
|
||||
|
||||
@customElement('umb-user-workspace-profile-settings')
|
||||
export class UmbUserWorkspaceProfileSettingsElement extends UmbLitElement {
|
||||
@state()
|
||||
private _user?: UserResponseModel;
|
||||
|
||||
@state()
|
||||
private _currentUser?: UmbCurrentUser;
|
||||
|
||||
@state()
|
||||
private languages: Array<{ name: string; value: string; selected: boolean }> = [];
|
||||
|
||||
#currentUserContext?: typeof UMB_CURRENT_USER_CONTEXT.TYPE;
|
||||
#userWorkspaceContext?: typeof UMB_USER_WORKSPACE_CONTEXT.TYPE;
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
|
||||
this.consumeContext(UMB_CURRENT_USER_CONTEXT, (instance) => {
|
||||
this.#currentUserContext = instance;
|
||||
this.#observeCurrentUser();
|
||||
});
|
||||
|
||||
this.consumeContext(UMB_USER_WORKSPACE_CONTEXT, (instance) => {
|
||||
this.#userWorkspaceContext = instance;
|
||||
this.observe(this.#userWorkspaceContext.data, (user) => (this._user = user), 'umbUserObserver');
|
||||
});
|
||||
}
|
||||
|
||||
#onLanguageChange(event: Event) {
|
||||
const target = event.composedPath()[0] as UUISelectElement;
|
||||
#onLanguageChange(event: UmbChangeEvent) {
|
||||
const target = event.target as UmbUiCultureInputElement;
|
||||
|
||||
if (typeof target?.value === 'string') {
|
||||
this.#userWorkspaceContext?.updateProperty('languageIsoCode', target.value);
|
||||
}
|
||||
}
|
||||
|
||||
#observeCurrentUser() {
|
||||
if (!this.#currentUserContext) return;
|
||||
this.observe(
|
||||
this.#currentUserContext.currentUser,
|
||||
async (currentUser) => {
|
||||
this._currentUser = currentUser;
|
||||
|
||||
if (!currentUser) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Find all translations and make a unique list of iso codes
|
||||
const translations = await firstValueFrom(umbExtensionsRegistry.extensionsOfType('localization'));
|
||||
|
||||
this.languages = translations
|
||||
.filter((isoCode) => isoCode !== undefined)
|
||||
.map((translation) => ({
|
||||
value: translation.meta.culture.toLowerCase(),
|
||||
name: translation.name,
|
||||
selected: false,
|
||||
}));
|
||||
|
||||
const currentUserLanguageCode = currentUser.languageIsoCode?.toLowerCase();
|
||||
|
||||
// Set the current user's language as selected
|
||||
const currentUserLanguage = this.languages.find((language) => language.value === currentUserLanguageCode);
|
||||
|
||||
if (currentUserLanguage) {
|
||||
currentUserLanguage.selected = true;
|
||||
} else {
|
||||
// If users language code did not fit any of the options. We will create an option that fits, named unknown.
|
||||
// In this way the user can keep their choice though a given language was not present at this time.
|
||||
this.languages.push({
|
||||
value: currentUserLanguageCode ?? 'en-us',
|
||||
name: currentUserLanguageCode ? `${currentUserLanguageCode} (unknown)` : 'Unknown',
|
||||
selected: true,
|
||||
});
|
||||
}
|
||||
},
|
||||
'umbUserObserver',
|
||||
);
|
||||
}
|
||||
|
||||
render() {
|
||||
return html`<uui-box>
|
||||
<div slot="headline"><umb-localize key="user_profile">Profile</umb-localize></div>
|
||||
@@ -112,13 +55,12 @@ export class UmbUserWorkspaceProfileSettingsElement extends UmbLitElement {
|
||||
<umb-workspace-property-layout
|
||||
label="${this.localize.term('user_language')}"
|
||||
description=${this.localize.term('user_languageHelp')}>
|
||||
<uui-select
|
||||
<umb-ui-culture-input
|
||||
slot="editor"
|
||||
value=${ifDefined(this._user?.languageIsoCode)}
|
||||
@change="${this.#onLanguageChange}"
|
||||
name="language"
|
||||
label="${this.localize.term('user_language')}"
|
||||
.options=${this.languages}
|
||||
@change="${this.#onLanguageChange}">
|
||||
</uui-select>
|
||||
label="${this.localize.term('user_language')}"></umb-ui-culture-input>
|
||||
</umb-workspace-property-layout>
|
||||
`;
|
||||
}
|
||||
|
||||
@@ -17,6 +17,10 @@ export const plugins: PluginOption[] = [
|
||||
src: 'public-assets/App_Plugins/custom-bundle-package/*.js',
|
||||
dest: 'App_Plugins/custom-bundle-package',
|
||||
},
|
||||
{
|
||||
src: 'src/css/*.css',
|
||||
dest: 'umbraco/backoffice/css',
|
||||
},
|
||||
{
|
||||
src: 'src/assets/*.svg',
|
||||
dest: 'umbraco/backoffice/assets',
|
||||
@@ -32,7 +36,7 @@ export const plugins: PluginOption[] = [
|
||||
{
|
||||
src: 'node_modules/msw/lib/iife/**/*',
|
||||
dest: 'umbraco/backoffice/msw',
|
||||
}
|
||||
},
|
||||
],
|
||||
}),
|
||||
viteTSConfigPaths(),
|
||||
|
||||
Reference in New Issue
Block a user