Merge remote-tracking branch 'origin/main' into v14/bugfix/correct-settings-data-depending-on-config
This commit is contained in:
@@ -48,6 +48,7 @@
|
||||
"local-rules/prefer-import-aliases": "error",
|
||||
"local-rules/prefer-static-styles-last": "warn",
|
||||
"local-rules/umb-class-prefix": "error",
|
||||
"local-rules/no-relative-import-to-import-map-module": "error",
|
||||
"local-rules/enforce-umbraco-external-imports": [
|
||||
"error",
|
||||
{
|
||||
|
||||
@@ -0,0 +1,56 @@
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
|
||||
const getDirectories = (source) =>
|
||||
fs
|
||||
.readdirSync(source, { withFileTypes: true })
|
||||
.filter((dirent) => dirent.isDirectory())
|
||||
.map((dirent) => dirent.name);
|
||||
|
||||
// TODO: get the correct list of modules. This is a temporary solution where we assume that a directory is equivalent to a module
|
||||
// TODO: include package modules in this list
|
||||
const coreRoot = path.join(__dirname, '../../../', 'src/packages/core');
|
||||
const externalRoot = path.join(__dirname, '../../../', 'src/external');
|
||||
const libsRoot = path.join(__dirname, '../../../', 'src/libs');
|
||||
const coreModules = getDirectories(coreRoot).map((dir) => `/core/${dir}/`);
|
||||
const externalModules = getDirectories(externalRoot).map((dir) => `/${dir}/`);
|
||||
const libsModules = getDirectories(libsRoot).map((dir) => `/${dir}/`);
|
||||
|
||||
const modulePathIdentifiers = [...coreModules, ...externalModules, ...libsModules];
|
||||
|
||||
/** @type {import('eslint').Rule.RuleModule} */
|
||||
module.exports = {
|
||||
meta: {
|
||||
type: 'problem',
|
||||
docs: {
|
||||
description: 'Prevent relative import to a module that is in the import map.',
|
||||
category: 'Best Practices',
|
||||
recommended: true,
|
||||
},
|
||||
schema: [],
|
||||
messages: {
|
||||
unexpectedValue: 'Relative import paths should include "{{value}}".',
|
||||
},
|
||||
},
|
||||
create: function (context) {
|
||||
return {
|
||||
ImportDeclaration(node) {
|
||||
// exclude test and story files
|
||||
if (context.filename.endsWith('.test.ts') || context.filename.endsWith('.stories.ts')) {
|
||||
return {};
|
||||
}
|
||||
|
||||
const importPath = node.source.value;
|
||||
|
||||
if (importPath.startsWith('./') || importPath.startsWith('../')) {
|
||||
if (modulePathIdentifiers.some((moduleName) => importPath.includes(moduleName))) {
|
||||
context.report({
|
||||
node,
|
||||
message: 'Use the correct import map alias instead of a relative import path: ' + importPath,
|
||||
});
|
||||
}
|
||||
}
|
||||
},
|
||||
};
|
||||
},
|
||||
};
|
||||
@@ -26,7 +26,7 @@ const mainMap = buildMap(mainKeys);
|
||||
const keys = Array.from(mainMap.keys());
|
||||
const usedKeys = new Set();
|
||||
|
||||
const elementAndControllerFiles = await glob(`${__dirname}/../../src/**/*.ts`);
|
||||
const elementAndControllerFiles = await glob(`${__dirname}/../../src/**/*.ts`, { filesOnly: true });
|
||||
|
||||
console.log(`Checking ${elementAndControllerFiles.length} files for unused keys`);
|
||||
|
||||
|
||||
@@ -10,6 +10,7 @@ const noDirectApiImportRule = require('./devops/eslint/rules/no-direct-api-impor
|
||||
const preferImportAliasesRule = require('./devops/eslint/rules/prefer-import-aliases.cjs');
|
||||
const preferStaticStylesLastRule = require('./devops/eslint/rules/prefer-static-styles-last.cjs');
|
||||
const umbClassPrefixRule = require('./devops/eslint/rules/umb-class-prefix.cjs');
|
||||
const noRelativeImportToImportMapModule = require('./devops/eslint/rules/no-relative-import-to-import-map-module.cjs');
|
||||
|
||||
module.exports = {
|
||||
'bad-type-import': badTypeImportRule,
|
||||
@@ -22,4 +23,5 @@ module.exports = {
|
||||
'prefer-import-aliases': preferImportAliasesRule,
|
||||
'prefer-static-styles-last': preferStaticStylesLastRule,
|
||||
'umb-class-prefix': umbClassPrefixRule,
|
||||
'no-relative-import-to-import-map-module': noRelativeImportToImportMapModule,
|
||||
};
|
||||
|
||||
@@ -3,7 +3,7 @@ import type {
|
||||
UmbContextConsumerController,
|
||||
UmbContextProviderController,
|
||||
UmbContextToken,
|
||||
} from '../context-api/index.js';
|
||||
} from '@umbraco-cms/backoffice/context-api';
|
||||
import type { UmbControllerAlias, UmbControllerHost } from '@umbraco-cms/backoffice/controller-api';
|
||||
import type { ObserverCallback, UmbObserverController } from '@umbraco-cms/backoffice/observable-api';
|
||||
import type { Observable } from '@umbraco-cms/backoffice/external/rxjs';
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import type { UmbContextToken } from '../context-api/index.js';
|
||||
import type { UmbControllerHost } from '../controller-api/index.js';
|
||||
import type { UmbContext } from './context.interface.js';
|
||||
import { UmbControllerBase } from './controller-base.class.js';
|
||||
import type { UmbContextToken } from '@umbraco-cms/backoffice/context-api';
|
||||
import type { UmbControllerHost } from '@umbraco-cms/backoffice/controller-api';
|
||||
|
||||
/**
|
||||
* This base provides the necessary for a class to become a context-api controller.
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
import type { UmbController } from '../controller-api/controller.interface.js';
|
||||
import type { UmbController } from '@umbraco-cms/backoffice/controller-api';
|
||||
|
||||
export interface UmbContext extends UmbController {}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import type { UmbController } from '../controller-api/controller.interface.js';
|
||||
import { UmbClassMixin } from './class.mixin.js';
|
||||
import type { UmbController } from '@umbraco-cms/backoffice/controller-api';
|
||||
import type { ClassConstructor } from '@umbraco-cms/backoffice/extension-api';
|
||||
|
||||
/**
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import type { ClassConstructor } from '../extension-api/types/utils.js';
|
||||
import type { UmbControllerHost } from './controller-host.interface.js';
|
||||
import type { UmbController } from './controller.interface.js';
|
||||
import type { ClassConstructor } from '@umbraco-cms/backoffice/extension-api';
|
||||
|
||||
interface UmbControllerHostBaseDeclaration extends Omit<UmbControllerHost, 'getHostElement'> {
|
||||
hostConnected(): void;
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import type { UmbControllerHostElement } from '../controller-api/controller-host-element.interface.js';
|
||||
import type { UmbControllerHostElement } from '@umbraco-cms/backoffice/controller-api';
|
||||
import type { UmbLocalizationController } from '@umbraco-cms/backoffice/localization-api';
|
||||
import type { UmbClassInterface } from '@umbraco-cms/backoffice/class-api';
|
||||
|
||||
|
||||
@@ -153,15 +153,15 @@ export abstract class UmbBlockEntryContext<
|
||||
// Consume block manager:
|
||||
this.consumeContext(blockManagerContextToken, (manager) => {
|
||||
this._manager = manager;
|
||||
this.#gotManager();
|
||||
this._gotManager();
|
||||
this.#gotManager();
|
||||
});
|
||||
|
||||
// Consume block entries:
|
||||
this.consumeContext(blockEntriesContextToken, (entries) => {
|
||||
this._entries = entries;
|
||||
this.#gotEntries();
|
||||
this._gotEntries();
|
||||
this.#gotEntries();
|
||||
});
|
||||
|
||||
// Observe UDI:
|
||||
|
||||
@@ -6,7 +6,7 @@ import { type UUIInputElement, UUIInputEvent } from '@umbraco-cms/backoffice/ext
|
||||
import { generateAlias } from '@umbraco-cms/backoffice/utils';
|
||||
|
||||
@customElement('umb-input-with-alias')
|
||||
export class UmbInputWithAliasElement extends UmbFormControlMixin<string>(UmbLitElement) {
|
||||
export class UmbInputWithAliasElement extends UmbFormControlMixin<string, typeof UmbLitElement>(UmbLitElement) {
|
||||
@property({ type: String })
|
||||
label: string = '';
|
||||
|
||||
@@ -31,36 +31,37 @@ export class UmbInputWithAliasElement extends UmbFormControlMixin<string>(UmbLit
|
||||
}
|
||||
|
||||
#onNameChange(e: UUIInputEvent) {
|
||||
if (e instanceof UUIInputEvent) {
|
||||
const target = e.composedPath()[0] as UUIInputElement;
|
||||
if (!(e instanceof UUIInputEvent)) return;
|
||||
|
||||
if (typeof target?.value === 'string') {
|
||||
const oldName = this.value;
|
||||
const oldAlias = this.alias ?? '';
|
||||
this.value = e.target.value.toString();
|
||||
if (this.autoGenerateAlias && this._aliasLocked) {
|
||||
// If locked we will update the alias, but only if it matches the generated alias of the old name [NL]
|
||||
const expectedOldAlias = generateAlias(oldName ?? '');
|
||||
// Only update the alias if the alias matches a generated alias of the old name (otherwise the alias is considered one written by the user.) [NL]
|
||||
if (expectedOldAlias === oldAlias) {
|
||||
this.alias = generateAlias(this.value);
|
||||
}
|
||||
const target = e.composedPath()[0] as UUIInputElement;
|
||||
|
||||
if (typeof target?.value === 'string') {
|
||||
const oldName = this.value;
|
||||
const oldAlias = this.alias ?? '';
|
||||
this.value = e.target.value.toString();
|
||||
if (this.autoGenerateAlias && this._aliasLocked) {
|
||||
// If locked we will update the alias, but only if it matches the generated alias of the old name [NL]
|
||||
const expectedOldAlias = generateAlias(oldName ?? '');
|
||||
// Only update the alias if the alias matches a generated alias of the old name (otherwise the alias is considered one written by the user.) [NL]
|
||||
if (expectedOldAlias === oldAlias) {
|
||||
this.alias = generateAlias(this.value);
|
||||
}
|
||||
this.dispatchEvent(new UmbChangeEvent());
|
||||
}
|
||||
|
||||
this.dispatchEvent(new UmbChangeEvent());
|
||||
}
|
||||
}
|
||||
|
||||
#onAliasChange(e: UUIInputEvent) {
|
||||
if (e instanceof UUIInputEvent) {
|
||||
const target = e.composedPath()[0] as UUIInputElement;
|
||||
if (typeof target?.value === 'string') {
|
||||
this.alias = target.value;
|
||||
console.log(this.alias);
|
||||
this.dispatchEvent(new UmbChangeEvent());
|
||||
}
|
||||
}
|
||||
e.stopPropagation();
|
||||
if (!(e instanceof UUIInputEvent)) return;
|
||||
|
||||
const target = e.composedPath()[0] as UUIInputElement;
|
||||
|
||||
if (typeof target?.value === 'string') {
|
||||
this.alias = target.value;
|
||||
this.dispatchEvent(new UmbChangeEvent());
|
||||
}
|
||||
}
|
||||
|
||||
#onToggleAliasLock() {
|
||||
@@ -68,25 +69,35 @@ export class UmbInputWithAliasElement extends UmbFormControlMixin<string>(UmbLit
|
||||
}
|
||||
|
||||
render() {
|
||||
// Localizations: [NL]
|
||||
const nameLabel = this.label ?? this.localize.term('placeholders_entername');
|
||||
const aliasLabel = this.localize.term('placeholders_enterAlias');
|
||||
|
||||
return html`
|
||||
<uui-input id="name" placeholder="Enter a name..." label=${this.label} .value=${this.value} @input="${this.#onNameChange}">
|
||||
<uui-input
|
||||
id="name"
|
||||
placeholder=${nameLabel}
|
||||
label=${nameLabel}
|
||||
.value=${this.value}
|
||||
@input=${this.#onNameChange}>
|
||||
<!-- TODO: should use UUI-LOCK-INPUT, but that does not fire an event when its locked/unlocked -->
|
||||
<uui-input
|
||||
auto-width
|
||||
name="alias"
|
||||
slot="append"
|
||||
label="alias"
|
||||
@input=${this.#onAliasChange}
|
||||
label=${aliasLabel}
|
||||
.value=${this.alias}
|
||||
placeholder="Enter alias..."
|
||||
placeholder=${aliasLabel}
|
||||
?disabled=${this._aliasLocked && !this.aliasReadonly}
|
||||
?readonly=${this.aliasReadonly}>
|
||||
?readonly=${this.aliasReadonly}
|
||||
@input=${this.#onAliasChange}>
|
||||
<!-- TODO: validation for bad characters -->
|
||||
${this.aliasReadonly
|
||||
? nothing
|
||||
: html`<div @click=${this.#onToggleAliasLock} @keydown=${() => ''} id="alias-lock" slot="prepend">
|
||||
<uui-icon name=${this._aliasLocked ? 'icon-lock' : 'icon-unlocked'}></uui-icon>
|
||||
</div>`}
|
||||
: html`
|
||||
<div @click=${this.#onToggleAliasLock} @keydown=${() => ''} id="alias-lock" slot="prepend">
|
||||
<uui-icon name=${this._aliasLocked ? 'icon-lock' : 'icon-unlocked'}></uui-icon>
|
||||
</div>
|
||||
`}
|
||||
</uui-input>
|
||||
</uui-input>
|
||||
`;
|
||||
@@ -99,6 +110,10 @@ export class UmbInputWithAliasElement extends UmbFormControlMixin<string>(UmbLit
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
#name > uui-input {
|
||||
max-width: 50%;
|
||||
}
|
||||
|
||||
:host(:invalid:not([pristine])) {
|
||||
color: var(--uui-color-danger);
|
||||
}
|
||||
|
||||
@@ -46,6 +46,8 @@ export class UmbContentTypeStructureManager<
|
||||
private readonly _contentTypeContainers = this.#contentTypes.asObservablePart((x) =>
|
||||
x.flatMap((x) => x.containers ?? []),
|
||||
);
|
||||
readonly contentTypeUniques = this.#contentTypes.asObservablePart((x) => x.map((y) => y.unique));
|
||||
readonly contentTypeAliases = this.#contentTypes.asObservablePart((x) => x.map((y) => y.alias));
|
||||
|
||||
#containers: UmbArrayState<UmbPropertyTypeContainerModel> = new UmbArrayState<UmbPropertyTypeContainerModel>(
|
||||
[],
|
||||
@@ -205,6 +207,12 @@ export class UmbContentTypeStructureManager<
|
||||
getContentTypes() {
|
||||
return this.#contentTypes.getValue();
|
||||
}
|
||||
getContentTypeUniques() {
|
||||
return this.#contentTypes.getValue().map((x) => x.unique);
|
||||
}
|
||||
getContentTypeAliases() {
|
||||
return this.#contentTypes.getValue().map((x) => x.alias);
|
||||
}
|
||||
|
||||
// TODO: We could move the actions to another class?
|
||||
|
||||
|
||||
@@ -1,15 +1,15 @@
|
||||
import type { UUIInputEvent } from '@umbraco-cms/backoffice/external/uui';
|
||||
import { css, customElement, html, nothing, property, repeat, state, when } from '@umbraco-cms/backoffice/external/lit';
|
||||
import { umbConfirmModal } from '@umbraco-cms/backoffice/modal';
|
||||
import { UmbLitElement, umbFocus } from '@umbraco-cms/backoffice/lit-element';
|
||||
import { css, html, customElement, property, state, nothing, repeat } from '@umbraco-cms/backoffice/external/lit';
|
||||
import { UmbTextStyles } from '@umbraco-cms/backoffice/style';
|
||||
import type {
|
||||
UmbContentTypeContainerStructureHelper,
|
||||
UmbContentTypeModel,
|
||||
UmbPropertyTypeContainerModel,
|
||||
} from '@umbraco-cms/backoffice/content-type';
|
||||
import type { UUIInputEvent } from '@umbraco-cms/backoffice/external/uui';
|
||||
|
||||
import './content-type-design-editor-properties.element.js';
|
||||
import { umbConfirmModal } from '@umbraco-cms/backoffice/modal';
|
||||
import { UmbTextStyles } from '@umbraco-cms/backoffice/style';
|
||||
|
||||
@customElement('umb-content-type-design-editor-group')
|
||||
export class UmbContentTypeWorkspaceViewEditGroupElement extends UmbLitElement {
|
||||
@@ -147,64 +147,78 @@ export class UmbContentTypeWorkspaceViewEditGroupElement extends UmbLitElement {
|
||||
}
|
||||
|
||||
render() {
|
||||
return this._inherited !== undefined && this._groupId
|
||||
? html`
|
||||
<uui-box>
|
||||
${this.#renderContainerHeader()}
|
||||
<umb-content-type-design-editor-properties
|
||||
.editContentTypePath=${this.editContentTypePath}
|
||||
container-id=${this._groupId}></umb-content-type-design-editor-properties>
|
||||
</uui-box>
|
||||
`
|
||||
: '';
|
||||
if (this._inherited === undefined || !this._groupId) return nothing;
|
||||
return html`
|
||||
<uui-box>
|
||||
${this.#renderContainerHeader()}
|
||||
<umb-content-type-design-editor-properties
|
||||
.editContentTypePath=${this.editContentTypePath}
|
||||
container-id=${this._groupId}></umb-content-type-design-editor-properties>
|
||||
</uui-box>
|
||||
`;
|
||||
}
|
||||
|
||||
// TODO: impl UMB_EDIT_DOCUMENT_TYPE_PATH_PATTERN, but we need either a generic type or a way to get the path pattern.... [NL]
|
||||
#renderContainerHeader() {
|
||||
return html`<div slot="header">
|
||||
return html`
|
||||
<div slot="header">
|
||||
<div>
|
||||
${this.sortModeActive && this._hasOwnerContainer ? html`<uui-icon name="icon-navigation"></uui-icon>` : null}
|
||||
${when(
|
||||
this.sortModeActive && this._hasOwnerContainer,
|
||||
() => html`<uui-icon name="icon-navigation"></uui-icon>`,
|
||||
)}
|
||||
<uui-input
|
||||
id="group-name"
|
||||
label=${this.localize.term('contentTypeEditor_group')}
|
||||
placeholder=${this.localize.term('placeholders_entername')}
|
||||
.value=${this._group!.name}
|
||||
?disabled=${!this._hasOwnerContainer}
|
||||
@change=${this.#renameGroup}
|
||||
@blur=${this.#blurGroup}
|
||||
@change=${this.#renameGroup}
|
||||
${this._group!.name === '' ? umbFocus() : nothing}></uui-input>
|
||||
</div>
|
||||
</div>
|
||||
<div slot="header-actions">
|
||||
${this._hasOwnerContainer === false && this._inheritedFrom
|
||||
? html`<uui-tag look="default" class="inherited">
|
||||
${when(
|
||||
this._hasOwnerContainer === false && this._inheritedFrom,
|
||||
() => html`
|
||||
<uui-tag look="default" class="inherited">
|
||||
<uui-icon name="icon-merge"></uui-icon>
|
||||
<span
|
||||
>${this.localize.term('contentTypeEditor_inheritedFrom')}
|
||||
${repeat(
|
||||
this._inheritedFrom,
|
||||
this._inheritedFrom!,
|
||||
(inherited) => inherited.unique,
|
||||
(inherited) => html`
|
||||
<a href=${this.editContentTypePath + 'edit/' + inherited.unique}>${inherited.name}</a>
|
||||
`,
|
||||
)}
|
||||
</span>
|
||||
</uui-tag>`
|
||||
: null}
|
||||
${!this._inherited && !this.sortModeActive
|
||||
? html`<uui-button compact label="${this.localize.term('actions_delete')}" @click="${this.#requestRemove}">
|
||||
</uui-tag>
|
||||
`,
|
||||
)}
|
||||
${when(
|
||||
!this._inherited && !this.sortModeActive,
|
||||
() => html`
|
||||
<uui-button compact label=${this.localize.term('actions_delete')} @click=${this.#requestRemove}>
|
||||
<uui-icon name="delete"></uui-icon>
|
||||
</uui-button>`
|
||||
: nothing}
|
||||
${this.sortModeActive
|
||||
? html` <uui-input
|
||||
</uui-button>
|
||||
`,
|
||||
)}
|
||||
${when(
|
||||
this.sortModeActive,
|
||||
() => html`
|
||||
<uui-input
|
||||
type="number"
|
||||
label=${this.localize.term('sort_sortOrder')}
|
||||
@change=${(e: UUIInputEvent) =>
|
||||
this._singleValueUpdate('sortOrder', parseInt(e.target.value as string) || 0)}
|
||||
.value=${this.group!.sortOrder ?? 0}
|
||||
?disabled=${!this._hasOwnerContainer}></uui-input>`
|
||||
: nothing}
|
||||
</div> `;
|
||||
?disabled=${!this._hasOwnerContainer}
|
||||
@change=${(e: UUIInputEvent) =>
|
||||
this._singleValueUpdate('sortOrder', parseInt(e.target.value as string) || 0)}></uui-input>
|
||||
`,
|
||||
)}
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
|
||||
static styles = [
|
||||
@@ -229,6 +243,12 @@ export class UmbContentTypeWorkspaceViewEditGroupElement extends UmbLitElement {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: var(--uui-size-3);
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
#group-name {
|
||||
--uui-input-border-color: transparent;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
uui-input[type='number'] {
|
||||
|
||||
@@ -1,18 +1,28 @@
|
||||
import './content-type-design-editor-property.element.js';
|
||||
import { UMB_CONTENT_TYPE_WORKSPACE_CONTEXT } from '../../content-type-workspace.context-token.js';
|
||||
import type { UmbContentTypeDesignEditorPropertyElement } from './content-type-design-editor-property.element.js';
|
||||
import { UMB_CONTENT_TYPE_DESIGN_EDITOR_CONTEXT } from './content-type-design-editor.context.js';
|
||||
import type { UmbContentTypeDesignEditorPropertyElement } from './content-type-design-editor-property.element.js';
|
||||
import {
|
||||
css,
|
||||
customElement,
|
||||
html,
|
||||
ifDefined,
|
||||
property,
|
||||
repeat,
|
||||
state,
|
||||
when,
|
||||
} from '@umbraco-cms/backoffice/external/lit';
|
||||
import { UmbLitElement } from '@umbraco-cms/backoffice/lit-element';
|
||||
import { css, html, customElement, property, state, repeat, ifDefined } from '@umbraco-cms/backoffice/external/lit';
|
||||
import { UmbTextStyles } from '@umbraco-cms/backoffice/style';
|
||||
import type { UmbContentTypeModel, UmbPropertyTypeModel } from '@umbraco-cms/backoffice/content-type';
|
||||
import {
|
||||
UmbContentTypePropertyStructureHelper,
|
||||
UMB_PROPERTY_TYPE_SETTINGS_MODAL,
|
||||
} from '@umbraco-cms/backoffice/content-type';
|
||||
import type { UmbContentTypeModel, UmbPropertyTypeModel } from '@umbraco-cms/backoffice/content-type';
|
||||
import { type UmbSorterConfig, UmbSorterController } from '@umbraco-cms/backoffice/sorter';
|
||||
import { type UmbModalRouteBuilder, UmbModalRouteRegistrationController } from '@umbraco-cms/backoffice/router';
|
||||
|
||||
import './content-type-design-editor-property.element.js';
|
||||
|
||||
const SORTER_CONFIG: UmbSorterConfig<UmbPropertyTypeModel, UmbContentTypeDesignEditorPropertyElement> = {
|
||||
getUniqueOfElement: (element) => {
|
||||
return element.getAttribute('data-umb-property-id');
|
||||
@@ -201,15 +211,16 @@ export class UmbContentTypeDesignEditorPropertiesElement extends UmbLitElement {
|
||||
)}
|
||||
</div>
|
||||
|
||||
${!this._sortModeActive
|
||||
? html`<uui-button
|
||||
${when(
|
||||
!this._sortModeActive,
|
||||
() => html`
|
||||
<uui-button
|
||||
id="btn-add"
|
||||
href=${ifDefined(this._modalRouteBuilderNewProperty?.({ sortOrder: -1 }))}
|
||||
label=${this.localize.term('contentTypeEditor_addProperty')}
|
||||
id="add"
|
||||
look="placeholder"
|
||||
href=${ifDefined(this._modalRouteBuilderNewProperty?.({ sortOrder: -1 }))}>
|
||||
<umb-localize key="contentTypeEditor_addProperty">Add property</umb-localize>
|
||||
</uui-button> `
|
||||
: ''}
|
||||
look="placeholder"></uui-button>
|
||||
`,
|
||||
)}
|
||||
`
|
||||
: '';
|
||||
}
|
||||
@@ -217,8 +228,9 @@ export class UmbContentTypeDesignEditorPropertiesElement extends UmbLitElement {
|
||||
static styles = [
|
||||
UmbTextStyles,
|
||||
css`
|
||||
#add {
|
||||
#btn-add {
|
||||
width: 100%;
|
||||
--uui-button-height: var(--uui-size-14);
|
||||
}
|
||||
|
||||
#property-list[sort-mode-active]:not(:has(umb-content-type-design-editor-property)) {
|
||||
|
||||
@@ -1,19 +1,17 @@
|
||||
import { UMB_CONTENT_TYPE_WORKSPACE_CONTEXT } from '../../content-type-workspace.context-token.js';
|
||||
import type { UmbContentTypeWorkspaceViewEditGroupElement } from './content-type-design-editor-group.element.js';
|
||||
import { UMB_CONTENT_TYPE_DESIGN_EDITOR_CONTEXT } from './content-type-design-editor.context.js';
|
||||
import type { UmbContentTypeWorkspaceViewEditGroupElement } from './content-type-design-editor-group.element.js';
|
||||
import { css, customElement, html, nothing, property, repeat, state } from '@umbraco-cms/backoffice/external/lit';
|
||||
import { UmbContentTypeContainerStructureHelper } from '@umbraco-cms/backoffice/content-type';
|
||||
import { UmbLitElement } from '@umbraco-cms/backoffice/lit-element';
|
||||
import { css, html, customElement, property, state, repeat, nothing } from '@umbraco-cms/backoffice/external/lit';
|
||||
import {
|
||||
UmbContentTypeContainerStructureHelper,
|
||||
type UmbContentTypeModel,
|
||||
type UmbPropertyTypeContainerModel,
|
||||
} from '@umbraco-cms/backoffice/content-type';
|
||||
import { UmbModalRouteRegistrationController } from '@umbraco-cms/backoffice/router';
|
||||
import { UmbSorterController } from '@umbraco-cms/backoffice/sorter';
|
||||
import { UMB_WORKSPACE_MODAL } from '@umbraco-cms/backoffice/modal';
|
||||
import type { UmbContentTypeModel, UmbPropertyTypeContainerModel } from '@umbraco-cms/backoffice/content-type';
|
||||
import type { UmbSorterConfig } from '@umbraco-cms/backoffice/sorter';
|
||||
|
||||
import './content-type-design-editor-properties.element.js';
|
||||
import './content-type-design-editor-group.element.js';
|
||||
import { type UmbSorterConfig, UmbSorterController } from '@umbraco-cms/backoffice/sorter';
|
||||
import { UMB_WORKSPACE_MODAL } from '@umbraco-cms/backoffice/modal';
|
||||
import { UmbModalRouteRegistrationController } from '@umbraco-cms/backoffice/router';
|
||||
|
||||
const SORTER_CONFIG: UmbSorterConfig<UmbPropertyTypeContainerModel, UmbContentTypeWorkspaceViewEditGroupElement> = {
|
||||
getUniqueOfElement: (element) => element.group?.id,
|
||||
@@ -120,6 +118,7 @@ export class UmbContentTypeDesignEditorTabElement extends UmbLitElement {
|
||||
this._editContentTypePath = routeBuilder({});
|
||||
});
|
||||
});
|
||||
|
||||
this.consumeContext(UMB_CONTENT_TYPE_DESIGN_EDITOR_CONTEXT, (context) => {
|
||||
this.observe(
|
||||
context.isSorting,
|
||||
@@ -134,6 +133,7 @@ export class UmbContentTypeDesignEditorTabElement extends UmbLitElement {
|
||||
'_observeIsSorting',
|
||||
);
|
||||
});
|
||||
|
||||
this.observe(
|
||||
this.#groupStructureHelper.mergedContainers,
|
||||
(groups) => {
|
||||
@@ -142,6 +142,7 @@ export class UmbContentTypeDesignEditorTabElement extends UmbLitElement {
|
||||
},
|
||||
null,
|
||||
);
|
||||
|
||||
this.observe(
|
||||
this.#groupStructureHelper.hasProperties,
|
||||
(hasProperties) => {
|
||||
@@ -194,13 +195,13 @@ export class UmbContentTypeDesignEditorTabElement extends UmbLitElement {
|
||||
|
||||
#renderAddGroupButton() {
|
||||
if (this._sortModeActive) return;
|
||||
return html`<uui-button
|
||||
label=${this.localize.term('contentTypeEditor_addGroup')}
|
||||
id="add"
|
||||
look="placeholder"
|
||||
@click=${this.#onAddGroup}>
|
||||
${this.localize.term('contentTypeEditor_addGroup')}
|
||||
</uui-button>`;
|
||||
return html`
|
||||
<uui-button
|
||||
id="btn-add"
|
||||
label=${this.localize.term('contentTypeEditor_addGroup')}
|
||||
look="placeholder"
|
||||
@click=${this.#onAddGroup}></uui-button>
|
||||
`;
|
||||
}
|
||||
|
||||
static styles = [
|
||||
@@ -213,12 +214,9 @@ export class UmbContentTypeDesignEditorTabElement extends UmbLitElement {
|
||||
visibility: hidden;
|
||||
}
|
||||
|
||||
#add {
|
||||
#btn-add {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
#add:first-child {
|
||||
margin-top: var(--uui-size-layout-1);
|
||||
--uui-button-height: var(--uui-size-24);
|
||||
}
|
||||
|
||||
uui-box,
|
||||
|
||||
@@ -369,7 +369,7 @@ export class UmbContentTypeDesignEditorElement extends UmbLitElement implements
|
||||
return html`
|
||||
<umb-body-layout header-fit-height>
|
||||
<div id="header" slot="header">
|
||||
<div id="container-list" class="flex">${this.renderTabsNavigation()} ${this.renderAddButton()}</div>
|
||||
<div id="container-list">${this.renderTabsNavigation()} ${this.renderAddButton()}</div>
|
||||
${this.renderActions()}
|
||||
</div>
|
||||
<umb-router-slot
|
||||
@@ -388,10 +388,12 @@ export class UmbContentTypeDesignEditorElement extends UmbLitElement implements
|
||||
renderAddButton() {
|
||||
// TODO: Localize this:
|
||||
if (this._sortModeActive) return;
|
||||
return html`<uui-button id="add-tab" @click="${this.#addTab}" label="Add tab">
|
||||
<uui-icon name="icon-add"></uui-icon>
|
||||
Add tab
|
||||
</uui-button>`;
|
||||
return html`
|
||||
<uui-button id="add-tab" @click="${this.#addTab}" label="Add tab">
|
||||
<uui-icon name="icon-add"></uui-icon>
|
||||
Add tab
|
||||
</uui-button>
|
||||
`;
|
||||
}
|
||||
|
||||
renderActions() {
|
||||
@@ -399,37 +401,41 @@ export class UmbContentTypeDesignEditorElement extends UmbLitElement implements
|
||||
? this.localize.term('general_reorderDone')
|
||||
: this.localize.term('general_reorder');
|
||||
|
||||
return html`<div>
|
||||
${this._compositionRepositoryAlias
|
||||
? html`<uui-button
|
||||
look="outline"
|
||||
label=${this.localize.term('contentTypeEditor_compositions')}
|
||||
compact
|
||||
@click=${this.#openCompositionModal}>
|
||||
<uui-icon name="icon-merge"></uui-icon>
|
||||
${this.localize.term('contentTypeEditor_compositions')}
|
||||
</uui-button>`
|
||||
: ''}
|
||||
<uui-button look="outline" label=${sortButtonText} compact @click=${this.#toggleSortMode}>
|
||||
<uui-icon name="icon-navigation"></uui-icon>
|
||||
${sortButtonText}
|
||||
</uui-button>
|
||||
</div>`;
|
||||
return html`
|
||||
<div id="actions">
|
||||
${this._compositionRepositoryAlias
|
||||
? html` <uui-button
|
||||
look="outline"
|
||||
label=${this.localize.term('contentTypeEditor_compositions')}
|
||||
compact
|
||||
@click=${this.#openCompositionModal}>
|
||||
<uui-icon name="icon-merge"></uui-icon>
|
||||
${this.localize.term('contentTypeEditor_compositions')}
|
||||
</uui-button>`
|
||||
: ''}
|
||||
<uui-button look="outline" label=${sortButtonText} compact @click=${this.#toggleSortMode}>
|
||||
<uui-icon name="icon-navigation"></uui-icon>
|
||||
${sortButtonText}
|
||||
</uui-button>
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
|
||||
renderTabsNavigation() {
|
||||
if (!this._tabs || this._tabs.length === 0) return;
|
||||
|
||||
return html`<div id="tabs-group" class="flex">
|
||||
<uui-tab-group>
|
||||
${this.renderRootTab()}
|
||||
${repeat(
|
||||
this._tabs,
|
||||
(tab) => tab.id,
|
||||
(tab) => this.renderTab(tab),
|
||||
)}
|
||||
</uui-tab-group>
|
||||
</div>`;
|
||||
return html`
|
||||
<div id="tabs-group">
|
||||
<uui-tab-group>
|
||||
${this.renderRootTab()}
|
||||
${repeat(
|
||||
this._tabs,
|
||||
(tab) => tab.id,
|
||||
(tab) => this.renderTab(tab),
|
||||
)}
|
||||
</uui-tab-group>
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
|
||||
renderRootTab() {
|
||||
@@ -439,14 +445,16 @@ export class UmbContentTypeDesignEditorElement extends UmbLitElement implements
|
||||
// If we don't have any root groups and we are not in sort mode, then we don't want to render the root tab.
|
||||
return nothing;
|
||||
}
|
||||
return html`<uui-tab
|
||||
id="root-tab"
|
||||
class=${this._hasRootGroups || rootTabActive ? '' : 'content-tab-is-empty'}
|
||||
label=${this.localize.term('general_generic')}
|
||||
.active=${rootTabActive}
|
||||
href=${rootTabPath}>
|
||||
${this.localize.term('general_generic')}
|
||||
</uui-tab>`;
|
||||
return html`
|
||||
<uui-tab
|
||||
id="root-tab"
|
||||
class=${this._hasRootGroups || rootTabActive ? '' : 'content-tab-is-empty'}
|
||||
label=${this.localize.term('general_generic')}
|
||||
.active=${rootTabActive}
|
||||
href=${rootTabPath}>
|
||||
${this.localize.term('general_generic')}
|
||||
</uui-tab>
|
||||
`;
|
||||
}
|
||||
|
||||
renderTab(tab: UmbPropertyTypeContainerModel) {
|
||||
@@ -556,17 +564,26 @@ export class UmbContentTypeDesignEditorElement extends UmbLitElement implements
|
||||
|
||||
#header {
|
||||
width: 100%;
|
||||
min-height: var(--uui-size-15);
|
||||
min-height: var(--uui-size-16);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
flex-wrap: nowrap;
|
||||
}
|
||||
|
||||
.flex {
|
||||
#container-list {
|
||||
display: flex;
|
||||
}
|
||||
|
||||
#tabs-group {
|
||||
display: flex;
|
||||
}
|
||||
|
||||
#actions {
|
||||
display: flex;
|
||||
gap: var(--uui-size-space-2);
|
||||
}
|
||||
|
||||
uui-tab-group {
|
||||
flex-wrap: nowrap;
|
||||
}
|
||||
|
||||
@@ -134,6 +134,7 @@ export interface MetaEntityActionMoveToKind extends MetaEntityActionDefaultKind
|
||||
moveRepositoryAlias: string;
|
||||
treeRepositoryAlias: string;
|
||||
treeAlias: string;
|
||||
foldersOnly?: boolean;
|
||||
}
|
||||
|
||||
// FOLDER
|
||||
|
||||
@@ -33,7 +33,7 @@ export class UmbPropertyLayoutElement extends UmbLitElement {
|
||||
/**
|
||||
* Orientation: Horizontal is the default where label goes left and editor right.
|
||||
* Vertical is where label goes above the editor.
|
||||
* @type {string}
|
||||
* @enum ['horizontal', 'vertical']
|
||||
* @attr
|
||||
* @default ''
|
||||
*/
|
||||
@@ -130,6 +130,14 @@ export class UmbPropertyLayoutElement extends UmbLitElement {
|
||||
color: var(--uui-color-text-alt);
|
||||
}
|
||||
|
||||
#description * {
|
||||
max-width: 100%;
|
||||
}
|
||||
|
||||
#description pre {
|
||||
overflow: auto;
|
||||
}
|
||||
|
||||
#editorColumn {
|
||||
margin-top: var(--uui-size-space-3);
|
||||
}
|
||||
|
||||
@@ -1,19 +1,118 @@
|
||||
import type { Meta, Story } from '@storybook/web-components';
|
||||
import type { Meta, StoryObj } from '@storybook/web-components';
|
||||
import type { UmbPropertyLayoutElement } from './property-layout.element.js';
|
||||
import { html } from '@umbraco-cms/backoffice/external/lit';
|
||||
|
||||
import './property-layout.element.js';
|
||||
|
||||
export default {
|
||||
const meta: Meta<UmbPropertyLayoutElement> = {
|
||||
title: 'Workspaces/Property Layout',
|
||||
component: 'umb-property-layout',
|
||||
id: 'umb-property-layout',
|
||||
} as Meta;
|
||||
component: 'umb-property-layout',
|
||||
args: {
|
||||
alias: 'Umb.PropertyLayout.Text',
|
||||
label: 'Text',
|
||||
description: 'Description',
|
||||
orientation: 'horizontal',
|
||||
},
|
||||
argTypes: {
|
||||
orientation: {
|
||||
options: ['horizontal', 'vertical'],
|
||||
},
|
||||
},
|
||||
render: (storyObj) => {
|
||||
return html`
|
||||
<umb-property-layout
|
||||
.invalid=${storyObj.invalid}
|
||||
.alias=${storyObj.alias}
|
||||
.label=${storyObj.label}
|
||||
.description=${storyObj.description}
|
||||
.orientation=${storyObj.orientation}>
|
||||
<div slot="action-menu"><uui-button color="" look="placeholder">Action Menu</uui-button></div>
|
||||
<div slot="editor"><uui-button color="" look="placeholder">Editor</uui-button></div>
|
||||
</umb-property-layout>
|
||||
`;
|
||||
},
|
||||
parameters: {
|
||||
docs: {
|
||||
source: {
|
||||
code: `
|
||||
<umb-property-layout alias="My.Alias" text="My label" description="My description">
|
||||
<div slot="action-menu"><uui-button color="" look="placeholder">Action Menu</uui-button></div>
|
||||
<div slot="editor"><uui-button color="" look="placeholder">Editor</uui-button></div>
|
||||
</umb-property-layout>
|
||||
`,
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
export const AAAOverview: Story<UmbPropertyLayoutElement> = () =>
|
||||
html` <umb-property-layout label="Label" description="Description">
|
||||
<div slot="action-menu"><uui-button color="" look="placeholder">Action Menu</uui-button></div>
|
||||
export default meta;
|
||||
type Story = StoryObj<UmbPropertyLayoutElement>;
|
||||
|
||||
<div slot="editor"><uui-button color="" look="placeholder">Editor</uui-button></div>
|
||||
</umb-property-layout>`;
|
||||
AAAOverview.storyName = 'Overview';
|
||||
export const Overview: Story = {};
|
||||
|
||||
export const Vertical: Story = {
|
||||
args: {
|
||||
orientation: 'vertical',
|
||||
},
|
||||
render: (storyObj) => {
|
||||
return html`
|
||||
<umb-property-layout
|
||||
style="width: 500px;"
|
||||
.alias=${storyObj.alias}
|
||||
.label=${storyObj.label}
|
||||
.description=${storyObj.description}
|
||||
.orientation=${storyObj.orientation}>
|
||||
<uui-textarea slot="editor"></uui-textarea>
|
||||
</umb-property-layout>
|
||||
`;
|
||||
},
|
||||
};
|
||||
|
||||
export const WithInvalid: Story = {
|
||||
args: {
|
||||
invalid: true,
|
||||
},
|
||||
};
|
||||
|
||||
export const WithMarkdown: Story = {
|
||||
decorators: [(story) => html` <div style="max-width: 500px; margin:1rem;">${story()}</div> `],
|
||||
args: {
|
||||
alias: 'Umb.PropertyLayout.Markdown',
|
||||
label: 'Markdown',
|
||||
description: `
|
||||
# Markdown
|
||||
This is a markdown description
|
||||
|
||||
## Subtitle
|
||||
- List item 1
|
||||
- List item 2
|
||||
|
||||
### Sub subtitle
|
||||
1. Numbered list item 1
|
||||
2. Numbered list item 2
|
||||
|
||||
\`\`\`html
|
||||
<umb-property-layout>
|
||||
<div slot="editor"></div>
|
||||
</umb-property-layout>
|
||||
|
||||
\`\`\`
|
||||
|
||||
> Blockquote
|
||||
|
||||
**Bold text**
|
||||
|
||||
*Italic text*
|
||||
|
||||
[Link](https://umbraco.com)
|
||||
|
||||

|
||||
|
||||
<details>
|
||||
<summary>Read more</summary>
|
||||
Details content
|
||||
</details>
|
||||
`,
|
||||
},
|
||||
};
|
||||
|
||||
@@ -72,16 +72,21 @@ export type resolvePlacementArgs<T, ElementType extends HTMLElement> = {
|
||||
pointerY: number;
|
||||
};
|
||||
|
||||
type UniqueType = string | symbol | number;
|
||||
|
||||
/**
|
||||
* Internal type, which is adjusted to become the public one.
|
||||
* @internal */
|
||||
type INTERNAL_UmbSorterConfig<T, ElementType extends HTMLElement> = {
|
||||
/**
|
||||
* Define how to retrive the unique identifier of an element. If this method returns undefined, the move will be cancelled.
|
||||
*/
|
||||
getUniqueOfElement: (element: ElementType) => string | null | symbol | number | undefined;
|
||||
getUniqueOfModel: (modeEntry: T) => string | null | symbol | number | undefined;
|
||||
getUniqueOfElement: (element: ElementType) => UniqueType | null | undefined;
|
||||
getUniqueOfModel: (modeEntry: T) => UniqueType | null | undefined;
|
||||
/**
|
||||
* Optionally define a unique identifier for each sorter experience, all Sorters that uses the same identifier to connect with other sorters.
|
||||
*/
|
||||
identifier: string | symbol;
|
||||
identifier: UniqueType;
|
||||
/**
|
||||
* A query selector for the item element.
|
||||
*/
|
||||
@@ -107,6 +112,7 @@ type INTERNAL_UmbSorterConfig<T, ElementType extends HTMLElement> = {
|
||||
* The selector to find the draggable element within the item.
|
||||
*/
|
||||
draggableSelector?: string;
|
||||
|
||||
//boundarySelector?: string;
|
||||
dataTransferResolver?: (dataTransfer: DataTransfer | null, currentItem: T) => void;
|
||||
onStart?: (argument: { item: T; element: ElementType }) => void;
|
||||
@@ -166,7 +172,7 @@ type INTERNAL_UmbSorterConfig<T, ElementType extends HTMLElement> = {
|
||||
performItemRemove?: (argument: { item: T }) => Promise<boolean> | boolean;
|
||||
};
|
||||
|
||||
// External type with some properties optional, as they have defaults:
|
||||
// External type with some properties optional, as they have fallback values:
|
||||
export type UmbSorterConfig<T, ElementType extends HTMLElement = HTMLElement> = Omit<
|
||||
INTERNAL_UmbSorterConfig<T, ElementType>,
|
||||
'ignorerSelector' | 'containerSelector' | 'identifier'
|
||||
@@ -178,6 +184,19 @@ export type UmbSorterConfig<T, ElementType extends HTMLElement = HTMLElement> =
|
||||
* @class UmbSorterController
|
||||
* @implements {UmbControllerInterface}
|
||||
* @description This controller can make user able to sort items.
|
||||
* @example
|
||||
*
|
||||
* This example shows how to setup a sorter controller with no special needs.
|
||||
* Assuming your declaring this on a Umbraco Element(UmbControllerHostElement):
|
||||
*
|
||||
* ```ts
|
||||
* const sorter = new UmbSorterController(this, {
|
||||
* itemSelector: '.item',
|
||||
* containerSelector: '.container',
|
||||
* getUniqueOfElement: (element) => element.dataset.id,
|
||||
* getUniqueOfModel: (model) => model.id
|
||||
* });
|
||||
* ```
|
||||
*/
|
||||
export class UmbSorterController<T, ElementType extends HTMLElement = HTMLElement> extends UmbControllerBase {
|
||||
//
|
||||
@@ -274,16 +293,13 @@ export class UmbSorterController<T, ElementType extends HTMLElement = HTMLElemen
|
||||
}
|
||||
}
|
||||
|
||||
hasItem(unique: string) {
|
||||
hasItem(unique: UniqueType) {
|
||||
return this.#model.find((x) => this.#config.getUniqueOfModel(x) === unique) !== undefined;
|
||||
}
|
||||
|
||||
/*
|
||||
getItem(unique: string) {
|
||||
if (!unique) return undefined;
|
||||
getItem(unique: UniqueType) {
|
||||
return this.#model.find((x) => this.#config.getUniqueOfModel(x) === unique);
|
||||
}
|
||||
*/
|
||||
|
||||
hostConnected() {
|
||||
this.#isConnected = true;
|
||||
@@ -453,7 +469,7 @@ export class UmbSorterController<T, ElementType extends HTMLElement = HTMLElemen
|
||||
|
||||
if (UmbSorterController.activeElement && UmbSorterController.activeElement !== element) {
|
||||
// TODO: Remove this console log at one point.
|
||||
console.log("drag start realized that something was already active, so we'll end it. -------!!!!#€#%#€");
|
||||
console.error('drag start ws cancelled due to another drag was still active');
|
||||
this.#handleDragEnd();
|
||||
}
|
||||
|
||||
@@ -470,6 +486,7 @@ export class UmbSorterController<T, ElementType extends HTMLElement = HTMLElemen
|
||||
UmbSorterController.activeItem = this.getItemOfElement(UmbSorterController.activeElement! as ElementType);
|
||||
|
||||
UmbSorterController.originalSorter = this as unknown as UmbSorterController<unknown>;
|
||||
// Notice, it is acceptable here to get index via object reference, but only cause there has been no change at this stage, otherwise we cannot trust the object instance is represented in the model — it could have mutated or been cloned [NL]
|
||||
UmbSorterController.originalIndex = this.#model.indexOf(UmbSorterController.activeItem);
|
||||
|
||||
if (!UmbSorterController.activeItem) {
|
||||
@@ -478,7 +495,7 @@ export class UmbSorterController<T, ElementType extends HTMLElement = HTMLElemen
|
||||
}
|
||||
|
||||
// Get the current index of the item:
|
||||
UmbSorterController.activeIndex = this.#model.indexOf(UmbSorterController.activeItem as T);
|
||||
UmbSorterController.activeIndex = UmbSorterController.originalIndex;
|
||||
|
||||
UmbSorterController.activeElement!.style.transform = 'translateZ(0)'; // Solves problem with FireFox and ShadowDom in the drag-image.
|
||||
|
||||
@@ -804,10 +821,21 @@ export class UmbSorterController<T, ElementType extends HTMLElement = HTMLElemen
|
||||
}
|
||||
|
||||
// TODO: Could get item via attr.
|
||||
public async moveItemInModel(newIndex: number, fromCtrl: UmbSorterController<unknown>) {
|
||||
const item = UmbSorterController.activeItem;
|
||||
public async moveItemInModel(newIndex: number, fromCtrl: UmbSorterController<T, ElementType>) {
|
||||
if (!UmbSorterController.activeItem) {
|
||||
console.error('There is no active item to move');
|
||||
return false;
|
||||
}
|
||||
const itemUnique = this.#config.getUniqueOfModel(UmbSorterController.activeItem);
|
||||
if (!itemUnique) {
|
||||
console.error('Failed to retrieve active item unique');
|
||||
return false;
|
||||
}
|
||||
// We use the getItem method to find the current item/object of this entry, as we cannot trust the object instance(activeItem) to be the same as in the model. [NL]
|
||||
// So notice, item in this method is the real modal entry reference, where in many other cases we use the activeItem which might not be up to date with the real entry of the model. [NL]
|
||||
const item = fromCtrl.getItem(itemUnique);
|
||||
if (!item) {
|
||||
console.error('Could not find item of sync item');
|
||||
console.error('Could not find item of model to move', itemUnique, this.#model);
|
||||
return false;
|
||||
}
|
||||
if (this.notifyRequestDrop({ item }) === false) {
|
||||
@@ -819,10 +847,9 @@ export class UmbSorterController<T, ElementType extends HTMLElement = HTMLElemen
|
||||
if (localMove) {
|
||||
// Local move:
|
||||
|
||||
// TODO: Maybe this should be replaceable/configurable:
|
||||
const oldIndex = this.#model.indexOf(item);
|
||||
if (oldIndex === -1) {
|
||||
console.error('Could not find item in model');
|
||||
console.error('Could not find item in model when performing internal move', this.getHostElement(), this.#model);
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -847,30 +874,33 @@ export class UmbSorterController<T, ElementType extends HTMLElement = HTMLElemen
|
||||
// Not a local move:
|
||||
|
||||
if ((await fromCtrl.removeItem(item)) !== true) {
|
||||
console.error('Sync could not remove item');
|
||||
console.error('Sync could not remove item when moving to a new container');
|
||||
return false;
|
||||
}
|
||||
|
||||
if (this.#config.performItemInsert) {
|
||||
const result = await this.#config.performItemInsert({ item, newIndex });
|
||||
if (result === false) {
|
||||
console.error('Sync could not insert after a move a new container');
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
const newModel = [...this.#model];
|
||||
newModel.splice(newIndex, 0, item);
|
||||
this.#model = newModel;
|
||||
|
||||
this.#config.onContainerChange?.({
|
||||
model: newModel,
|
||||
item,
|
||||
from: fromCtrl as unknown as UmbSorterController<T, ElementType>,
|
||||
});
|
||||
this.#config.onChange?.({ model: newModel, item });
|
||||
}
|
||||
|
||||
// If everything went well, we can set new activeSorter to this:
|
||||
UmbSorterController.activeSorter = this as unknown as UmbSorterController<unknown>;
|
||||
UmbSorterController.activeIndex = newIndex;
|
||||
// If everything went well, we can set the new activeSorter (and dropSorter) to this, as we are switching container. [NL]
|
||||
UmbSorterController.activeSorter = this as unknown as UmbSorterController<unknown>;
|
||||
UmbSorterController.dropSorter = this as unknown as UmbSorterController<unknown>;
|
||||
UmbSorterController.activeIndex = newIndex;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
|
||||
@@ -1,12 +1,14 @@
|
||||
import type { UmbEntityModel } from '@umbraco-cms/backoffice/entity';
|
||||
|
||||
export interface UmbTreeRootItemsRequestArgs {
|
||||
foldersOnly?: boolean;
|
||||
skip?: number;
|
||||
take?: number;
|
||||
}
|
||||
|
||||
export interface UmbTreeChildrenOfRequestArgs {
|
||||
parent: UmbEntityModel;
|
||||
foldersOnly?: boolean;
|
||||
skip?: number;
|
||||
take?: number;
|
||||
}
|
||||
|
||||
@@ -49,6 +49,9 @@ export class UmbDefaultTreeContext<
|
||||
#startNode = new UmbObjectState<UmbTreeStartNode | undefined>(undefined);
|
||||
startNode = this.#startNode.asObservable();
|
||||
|
||||
#foldersOnly = new UmbBooleanState(false);
|
||||
foldersOnly = this.#foldersOnly.asObservable();
|
||||
|
||||
#manifest?: ManifestTree;
|
||||
#repository?: UmbTreeRepository<TreeItemType, TreeRootType>;
|
||||
#actionEventContext?: UmbActionEventContext;
|
||||
@@ -178,6 +181,7 @@ export class UmbDefaultTreeContext<
|
||||
|
||||
// If we have a start node get children of that instead of the root
|
||||
const startNode = this.getStartNode();
|
||||
const foldersOnly = this.#foldersOnly.getValue();
|
||||
const additionalArgs = this.#additionalRequestArgs.getValue();
|
||||
|
||||
const { data } = startNode?.unique
|
||||
@@ -187,11 +191,13 @@ export class UmbDefaultTreeContext<
|
||||
unique: startNode.unique,
|
||||
entityType: startNode.entityType,
|
||||
},
|
||||
foldersOnly,
|
||||
skip,
|
||||
take,
|
||||
})
|
||||
: await this.#repository!.requestTreeRootItems({
|
||||
...additionalArgs,
|
||||
foldersOnly,
|
||||
skip,
|
||||
take,
|
||||
});
|
||||
@@ -241,6 +247,36 @@ export class UmbDefaultTreeContext<
|
||||
this.loadTree();
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the startNode config
|
||||
* @return {UmbTreeStartNode}
|
||||
* @memberof UmbDefaultTreeContext
|
||||
*/
|
||||
getStartNode() {
|
||||
return this.#startNode.getValue();
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the foldersOnly config
|
||||
* @param {boolean} foldersOnly
|
||||
* @memberof UmbDefaultTreeContext
|
||||
*/
|
||||
setFoldersOnly(foldersOnly: boolean) {
|
||||
this.#foldersOnly.setValue(foldersOnly);
|
||||
// we need to reset the tree if this config changes
|
||||
this.#resetTree();
|
||||
this.loadTree();
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the foldersOnly config
|
||||
* @return {boolean}
|
||||
* @memberof UmbDefaultTreeContext
|
||||
*/
|
||||
getFoldersOnly() {
|
||||
return this.#foldersOnly.getValue();
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the requestArgs config and reloads the tree.
|
||||
*/
|
||||
@@ -254,15 +290,6 @@ export class UmbDefaultTreeContext<
|
||||
return this.#additionalRequestArgs.getValue();
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the startNode config
|
||||
* @return {UmbTreeStartNode}
|
||||
* @memberof UmbDefaultTreeContext
|
||||
*/
|
||||
getStartNode() {
|
||||
return this.#startNode.getValue();
|
||||
}
|
||||
|
||||
#resetTree() {
|
||||
this.#treeRoot.setValue(undefined);
|
||||
this.#rootItems.setValue([]);
|
||||
|
||||
@@ -31,6 +31,9 @@ export class UmbDefaultTreeElement extends UmbLitElement {
|
||||
@property({ type: Object, attribute: false })
|
||||
startNode?: UmbTreeStartNode;
|
||||
|
||||
@property({ type: Boolean, attribute: false })
|
||||
foldersOnly?: boolean = false;
|
||||
|
||||
@property({ attribute: false })
|
||||
selectableFilter: (item: UmbTreeItemModelBase) => boolean = () => true;
|
||||
|
||||
@@ -87,6 +90,10 @@ export class UmbDefaultTreeElement extends UmbLitElement {
|
||||
this.#treeContext!.setHideTreeRoot(this.hideTreeRoot);
|
||||
}
|
||||
|
||||
if (_changedProperties.has('foldersOnly')) {
|
||||
this.#treeContext!.setFoldersOnly(this.foldersOnly ?? false);
|
||||
}
|
||||
|
||||
if (_changedProperties.has('selectableFilter')) {
|
||||
this.#treeContext!.selectableFilter = this.selectableFilter;
|
||||
}
|
||||
|
||||
@@ -13,8 +13,12 @@ export class UmbMoveToEntityAction extends UmbEntityActionBase<MetaEntityActionM
|
||||
|
||||
const modalManager = await this.getContext(UMB_MODAL_MANAGER_CONTEXT);
|
||||
const modalContext = modalManager.open(this, UMB_TREE_PICKER_MODAL, {
|
||||
data: { treeAlias: this.args.meta.treeAlias },
|
||||
}) as any; // TODO: make generic picker interface with selection
|
||||
data: {
|
||||
treeAlias: this.args.meta.treeAlias,
|
||||
foldersOnly: this.args.meta.foldersOnly,
|
||||
},
|
||||
});
|
||||
|
||||
const value = await modalContext.onSubmit();
|
||||
const destinationUnique = value.selection[0];
|
||||
if (destinationUnique === undefined) throw new Error('Destination Unique is not available');
|
||||
|
||||
@@ -65,6 +65,9 @@ export abstract class UmbTreeItemContextBase<
|
||||
#path = new UmbStringState('');
|
||||
readonly path = this.#path.asObservable();
|
||||
|
||||
#foldersOnly = new UmbBooleanState(false);
|
||||
readonly foldersOnly = this.#foldersOnly.asObservable();
|
||||
|
||||
treeContext?: UmbDefaultTreeContext<TreeItemType, TreeRootType>;
|
||||
#sectionContext?: UmbSectionContext;
|
||||
#sectionSidebarContext?: UmbSectionSidebarContext;
|
||||
@@ -175,6 +178,7 @@ export abstract class UmbTreeItemContextBase<
|
||||
|
||||
const skip = loadMore ? this.#paging.skip : 0;
|
||||
const take = loadMore ? this.#paging.take : this.pagination.getCurrentPageNumber() * this.#paging.take;
|
||||
const foldersOnly = this.#foldersOnly.getValue();
|
||||
const additionalArgs = this.treeContext?.getAdditionalRequestArgs();
|
||||
|
||||
const { data } = await repository.requestTreeItemsOf({
|
||||
@@ -182,6 +186,7 @@ export abstract class UmbTreeItemContextBase<
|
||||
unique: this.unique,
|
||||
entityType: this.entityType,
|
||||
},
|
||||
foldersOnly,
|
||||
skip,
|
||||
take,
|
||||
...additionalArgs,
|
||||
@@ -240,6 +245,7 @@ export abstract class UmbTreeItemContextBase<
|
||||
this.treeContext = treeContext;
|
||||
this.#observeIsSelectable();
|
||||
this.#observeIsSelected();
|
||||
this.#observeFoldersOnly();
|
||||
});
|
||||
|
||||
this.consumeContext(UMB_ACTION_EVENT_CONTEXT, (instance) => {
|
||||
@@ -311,6 +317,18 @@ export abstract class UmbTreeItemContextBase<
|
||||
);
|
||||
}
|
||||
|
||||
#observeFoldersOnly() {
|
||||
if (!this.treeContext || this.unique === undefined) return;
|
||||
|
||||
this.observe(
|
||||
this.treeContext.foldersOnly,
|
||||
(foldersOnly) => {
|
||||
this.#foldersOnly.setValue(foldersOnly);
|
||||
},
|
||||
'observeFoldersOnly',
|
||||
);
|
||||
}
|
||||
|
||||
#observeSectionPath() {
|
||||
if (!this.#sectionContext) return;
|
||||
|
||||
|
||||
@@ -101,6 +101,7 @@ export class UmbTreePickerModalElement<TreeItemType extends UmbTreeItemModelBase
|
||||
filter: this.data?.filter,
|
||||
selectableFilter: this.data?.pickableFilter,
|
||||
startNode: this.data?.startNode,
|
||||
foldersOnly: this.data?.foldersOnly,
|
||||
}}
|
||||
@selection-change=${this.#onSelectionChange}
|
||||
@selected=${this.#onSelected}
|
||||
|
||||
@@ -21,6 +21,7 @@ export interface UmbTreePickerModalData<
|
||||
// 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;
|
||||
foldersOnly?: boolean;
|
||||
}
|
||||
|
||||
export interface UmbTreePickerModalValue extends UmbPickerModalValue {}
|
||||
|
||||
@@ -15,6 +15,7 @@ const entityActions: Array<ManifestTypes> = [
|
||||
treeRepositoryAlias: UMB_DATA_TYPE_TREE_REPOSITORY_ALIAS,
|
||||
moveRepositoryAlias: UMB_MOVE_DATA_TYPE_REPOSITORY_ALIAS,
|
||||
treeAlias: UMB_DATA_TYPE_TREE_ALIAS,
|
||||
foldersOnly: true,
|
||||
},
|
||||
},
|
||||
];
|
||||
|
||||
@@ -50,7 +50,11 @@ export class UmbDataTypeTreeServerDataSource extends UmbTreeServerDataSourceBase
|
||||
|
||||
const getRootItems = async (args: UmbTreeRootItemsRequestArgs) => {
|
||||
// eslint-disable-next-line local-rules/no-direct-api-import
|
||||
return DataTypeService.getTreeDataTypeRoot({ skip: args.skip, take: args.take });
|
||||
return DataTypeService.getTreeDataTypeRoot({
|
||||
foldersOnly: args.foldersOnly,
|
||||
skip: args.skip,
|
||||
take: args.take,
|
||||
});
|
||||
};
|
||||
|
||||
const getChildrenOf = (args: UmbTreeChildrenOfRequestArgs) => {
|
||||
@@ -60,6 +64,7 @@ const getChildrenOf = (args: UmbTreeChildrenOfRequestArgs) => {
|
||||
// eslint-disable-next-line local-rules/no-direct-api-import
|
||||
return DataTypeService.getTreeDataTypeChildren({
|
||||
parentId: args.parent.unique,
|
||||
foldersOnly: args.foldersOnly,
|
||||
skip: args.skip,
|
||||
take: args.take,
|
||||
});
|
||||
|
||||
@@ -15,6 +15,7 @@ const entityActions: Array<ManifestTypes> = [
|
||||
treeRepositoryAlias: UMB_DOCUMENT_BLUEPRINT_TREE_REPOSITORY_ALIAS,
|
||||
moveRepositoryAlias: UMB_MOVE_DOCUMENT_BLUEPRINT_REPOSITORY_ALIAS,
|
||||
treeAlias: UMB_DOCUMENT_BLUEPRINT_TREE_ALIAS,
|
||||
foldersOnly: true,
|
||||
},
|
||||
},
|
||||
];
|
||||
|
||||
@@ -41,7 +41,11 @@ export class UmbDocumentBlueprintTreeServerDataSource extends UmbTreeServerDataS
|
||||
|
||||
const getRootItems = (args: UmbTreeRootItemsRequestArgs) =>
|
||||
// eslint-disable-next-line local-rules/no-direct-api-import
|
||||
DocumentBlueprintService.getTreeDocumentBlueprintRoot({ skip: args.skip, take: args.take });
|
||||
DocumentBlueprintService.getTreeDocumentBlueprintRoot({
|
||||
foldersOnly: args.foldersOnly,
|
||||
skip: args.skip,
|
||||
take: args.take,
|
||||
});
|
||||
|
||||
const getChildrenOf = (args: UmbTreeChildrenOfRequestArgs) => {
|
||||
if (args.parent.unique === null) {
|
||||
@@ -50,6 +54,7 @@ const getChildrenOf = (args: UmbTreeChildrenOfRequestArgs) => {
|
||||
// eslint-disable-next-line local-rules/no-direct-api-import
|
||||
return DocumentBlueprintService.getTreeDocumentBlueprintChildren({
|
||||
parentId: args.parent.unique,
|
||||
foldersOnly: args.foldersOnly,
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
@@ -78,14 +78,14 @@ export class UmbDocumentTypeWorkspaceEditorElement extends UmbLitElement {
|
||||
return html`
|
||||
<umb-workspace-editor alias="Umb.Workspace.DocumentType">
|
||||
<div id="header" slot="header">
|
||||
<uui-button id="icon" @click=${this._handleIconClick} label="icon" compact>
|
||||
<uui-button id="icon" compact label="icon" look="outline" @click=${this._handleIconClick}>
|
||||
<umb-icon name=${ifDefined(this._icon)}></umb-icon>
|
||||
</uui-button>
|
||||
|
||||
<div id="editors">
|
||||
<umb-input-with-alias
|
||||
id="name"
|
||||
label="name"
|
||||
label=${this.localize.term('placeholders_entername')}
|
||||
value=${this._name}
|
||||
alias=${this._alias}
|
||||
?auto-generate-alias=${this._isNew}
|
||||
@@ -116,6 +116,7 @@ export class UmbDocumentTypeWorkspaceEditorElement extends UmbLitElement {
|
||||
#header {
|
||||
display: flex;
|
||||
flex: 1 1 auto;
|
||||
gap: var(--uui-size-space-2);
|
||||
}
|
||||
|
||||
#editors {
|
||||
@@ -140,9 +141,9 @@ export class UmbDocumentTypeWorkspaceEditorElement extends UmbLitElement {
|
||||
}
|
||||
|
||||
#icon {
|
||||
font-size: calc(var(--uui-size-layout-3) / 2);
|
||||
margin-right: var(--uui-size-space-2);
|
||||
margin-left: calc(var(--uui-size-space-4) * -1);
|
||||
font-size: var(--uui-size-8);
|
||||
height: 60px;
|
||||
width: 60px;
|
||||
}
|
||||
`,
|
||||
];
|
||||
|
||||
@@ -127,7 +127,7 @@ export function getDocumentHistoryTagStyleAndText(type: UmbDocumentAuditLogType)
|
||||
case UmbDocumentAuditLog.CUSTOM:
|
||||
return {
|
||||
style: { look: 'placeholder', color: 'default' },
|
||||
text: { label: 'auditTrails_custom', desc: '' },
|
||||
text: { label: 'auditTrails_smallCustom', desc: 'auditTrails_custom' },
|
||||
};
|
||||
|
||||
default:
|
||||
|
||||
@@ -15,6 +15,7 @@ const entityActions: Array<ManifestTypes> = [
|
||||
treeRepositoryAlias: UMB_MEDIA_TYPE_TREE_REPOSITORY_ALIAS,
|
||||
moveRepositoryAlias: UMB_MOVE_MEDIA_TYPE_REPOSITORY_ALIAS,
|
||||
treeAlias: UMB_MEDIA_TYPE_TREE_ALIAS,
|
||||
foldersOnly: true,
|
||||
},
|
||||
},
|
||||
];
|
||||
|
||||
@@ -41,7 +41,11 @@ export class UmbMediaTypeTreeServerDataSource extends UmbTreeServerDataSourceBas
|
||||
|
||||
const getRootItems = (args: UmbTreeRootItemsRequestArgs) =>
|
||||
// eslint-disable-next-line local-rules/no-direct-api-import
|
||||
MediaTypeService.getTreeMediaTypeRoot({ skip: args.skip, take: args.take });
|
||||
MediaTypeService.getTreeMediaTypeRoot({
|
||||
foldersOnly: args.foldersOnly,
|
||||
skip: args.skip,
|
||||
take: args.take,
|
||||
});
|
||||
|
||||
const getChildrenOf = (args: UmbTreeChildrenOfRequestArgs) => {
|
||||
if (args.parent.unique === null) {
|
||||
@@ -50,6 +54,7 @@ const getChildrenOf = (args: UmbTreeChildrenOfRequestArgs) => {
|
||||
// eslint-disable-next-line local-rules/no-direct-api-import
|
||||
return MediaTypeService.getTreeMediaTypeChildren({
|
||||
parentId: args.parent.unique,
|
||||
foldersOnly: args.foldersOnly,
|
||||
skip: args.skip,
|
||||
take: args.take,
|
||||
});
|
||||
|
||||
@@ -80,32 +80,34 @@ export class UmbMediaTypeWorkspaceEditorElement extends UmbLitElement {
|
||||
}
|
||||
|
||||
render() {
|
||||
return html`<umb-workspace-editor alias="Umb.Workspace.MediaType">
|
||||
<div id="header" slot="header">
|
||||
<uui-button id="icon" @click=${this._handleIconClick} label="icon" compact>
|
||||
<umb-icon name=${ifDefined(this._icon)}></umb-icon>
|
||||
</uui-button>
|
||||
return html`
|
||||
<umb-workspace-editor alias="Umb.Workspace.MediaType">
|
||||
<div id="header" slot="header">
|
||||
<uui-button id="icon" compact label="icon" look="outline" @click=${this._handleIconClick}>
|
||||
<umb-icon name=${ifDefined(this._icon)}></umb-icon>
|
||||
</uui-button>
|
||||
|
||||
<div id="editors">
|
||||
<umb-input-with-alias
|
||||
id="name"
|
||||
label="name"
|
||||
value=${this._name}
|
||||
alias=${this._alias}
|
||||
?auto-generate-alias=${this._isNew}
|
||||
@change="${this.#onNameAndAliasChange}"
|
||||
${umbFocus()}>
|
||||
</umb-input-with-alias>
|
||||
<div id="editors">
|
||||
<umb-input-with-alias
|
||||
id="name"
|
||||
label=${this.localize.term('placeholders_entername')}
|
||||
value=${this._name}
|
||||
alias=${this._alias}
|
||||
?auto-generate-alias=${this._isNew}
|
||||
@change="${this.#onNameAndAliasChange}"
|
||||
${umbFocus()}>
|
||||
</umb-input-with-alias>
|
||||
|
||||
<uui-input
|
||||
id="description"
|
||||
.label=${this.localize.term('placeholders_enterDescription')}
|
||||
.value=${this._description}
|
||||
.placeholder=${this.localize.term('placeholders_enterDescription')}
|
||||
@input=${this.#onDescriptionChange}></uui-input>
|
||||
<uui-input
|
||||
id="description"
|
||||
.label=${this.localize.term('placeholders_enterDescription')}
|
||||
.value=${this._description}
|
||||
.placeholder=${this.localize.term('placeholders_enterDescription')}
|
||||
@input=${this.#onDescriptionChange}></uui-input>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</umb-workspace-editor>`;
|
||||
</umb-workspace-editor>
|
||||
`;
|
||||
}
|
||||
|
||||
static styles = [
|
||||
@@ -119,6 +121,7 @@ export class UmbMediaTypeWorkspaceEditorElement extends UmbLitElement {
|
||||
#header {
|
||||
display: flex;
|
||||
flex: 1 1 auto;
|
||||
gap: var(--uui-size-space-2);
|
||||
}
|
||||
|
||||
#editors {
|
||||
@@ -143,9 +146,9 @@ export class UmbMediaTypeWorkspaceEditorElement extends UmbLitElement {
|
||||
}
|
||||
|
||||
#icon {
|
||||
font-size: calc(var(--uui-size-layout-3) / 2);
|
||||
margin-right: var(--uui-size-space-2);
|
||||
margin-left: calc(var(--uui-size-space-4) * -1);
|
||||
font-size: var(--uui-size-8);
|
||||
height: 60px;
|
||||
width: 60px;
|
||||
}
|
||||
`,
|
||||
];
|
||||
|
||||
@@ -135,7 +135,7 @@ export class UmbMediaWorkspaceViewInfoReferenceElement extends UmbLitElement {
|
||||
}
|
||||
|
||||
#renderItems() {
|
||||
if (!this._items?.length) return html`<p>${this.localize.term('references_DataTypeNoReferences')}</p>`;
|
||||
if (!this._items?.length) return html`<p><umb-localize key="references_itemHasNoReferences">This item has no references.</umb-localize></p>`;
|
||||
return html`
|
||||
<uui-table>
|
||||
<uui-table-head>
|
||||
|
||||
@@ -22,10 +22,6 @@ export class UmbMemberTypeWorkspaceEditorElement extends UmbLitElement {
|
||||
@state()
|
||||
private _isNew?: boolean;
|
||||
|
||||
@state()
|
||||
private _iconColorAlias?: string;
|
||||
// TODO: Color should be using an alias, and look up in some dictionary/key/value) of project-colors.
|
||||
|
||||
#workspaceContext?: typeof UMB_MEMBER_TYPE_WORKSPACE_CONTEXT.TYPE;
|
||||
|
||||
constructor() {
|
||||
@@ -82,14 +78,14 @@ export class UmbMemberTypeWorkspaceEditorElement extends UmbLitElement {
|
||||
return html`
|
||||
<umb-workspace-editor alias="Umb.Workspace.MemberType">
|
||||
<div id="header" slot="header">
|
||||
<uui-button id="icon" @click=${this._handleIconClick} label="icon" compact>
|
||||
<uui-icon name="${ifDefined(this._icon)}" style="color: ${this._iconColorAlias}"></uui-icon>
|
||||
<uui-button id="icon" compact label="icon" look="outline" @click=${this._handleIconClick}>
|
||||
<umb-icon name=${ifDefined(this._icon)}></umb-icon>
|
||||
</uui-button>
|
||||
|
||||
<div id="editors">
|
||||
<umb-input-with-alias
|
||||
id="name"
|
||||
label="name"
|
||||
label=${this.localize.term('placeholders_entername')}
|
||||
value=${this._name}
|
||||
alias=${this._alias}
|
||||
?auto-generate-alias=${this._isNew}
|
||||
@@ -120,6 +116,7 @@ export class UmbMemberTypeWorkspaceEditorElement extends UmbLitElement {
|
||||
#header {
|
||||
display: flex;
|
||||
flex: 1 1 auto;
|
||||
gap: var(--uui-size-space-2);
|
||||
}
|
||||
|
||||
#editors {
|
||||
@@ -156,9 +153,9 @@ export class UmbMemberTypeWorkspaceEditorElement extends UmbLitElement {
|
||||
}
|
||||
|
||||
#icon {
|
||||
font-size: calc(var(--uui-size-layout-3) / 2);
|
||||
margin-right: var(--uui-size-space-2);
|
||||
margin-left: calc(var(--uui-size-space-4) * -1);
|
||||
font-size: var(--uui-size-8);
|
||||
height: 60px;
|
||||
width: 60px;
|
||||
}
|
||||
`,
|
||||
];
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import type { UmbPropertyEditorConfigCollection } from '../../core/property-editor/config/index.js';
|
||||
import type { UmbPropertyEditorConfigCollection } from '@umbraco-cms/backoffice/property-editor';
|
||||
import { html, customElement, property, state } from '@umbraco-cms/backoffice/external/lit';
|
||||
import { UmbLitElement } from '@umbraco-cms/backoffice/lit-element';
|
||||
import { UMB_DOCUMENT_COLLECTION_ALIAS } from '@umbraco-cms/backoffice/document';
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { UmbPropertyValueChangeEvent } from '../../core/property-editor/index.js';
|
||||
import type { UmbPropertyEditorConfigCollection } from '../../core/property-editor/index.js';
|
||||
import { UmbPropertyValueChangeEvent } from '@umbraco-cms/backoffice/property-editor';
|
||||
import type { UmbPropertyEditorConfigCollection } from '@umbraco-cms/backoffice/property-editor';
|
||||
import { html, customElement, property, state, ifDefined } from '@umbraco-cms/backoffice/external/lit';
|
||||
import { UmbLitElement } from '@umbraco-cms/backoffice/lit-element';
|
||||
import type { UmbInputDateElement } from '@umbraco-cms/backoffice/components';
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { UmbPropertyValueChangeEvent } from '../../core/property-editor/index.js';
|
||||
import { UmbPropertyValueChangeEvent } from '@umbraco-cms/backoffice/property-editor';
|
||||
import { customElement, html, property, state } from '@umbraco-cms/backoffice/external/lit';
|
||||
import { UmbLitElement } from '@umbraco-cms/backoffice/lit-element';
|
||||
import type { UmbChangeEvent } from '@umbraco-cms/backoffice/event';
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import type { UmbInputNumberRangeElement } from '../../core/components/input-number-range/input-number-range.element.js';
|
||||
import type { UmbInputNumberRangeElement } from '@umbraco-cms/backoffice/components';
|
||||
import { customElement, html, property, state } from '@umbraco-cms/backoffice/external/lit';
|
||||
import { UmbFormControlMixin } from '@umbraco-cms/backoffice/validation';
|
||||
import { UmbLitElement } from '@umbraco-cms/backoffice/lit-element';
|
||||
@@ -7,8 +7,6 @@ import type { UmbNumberRangeValueType } from '@umbraco-cms/backoffice/models';
|
||||
import type { UmbPropertyEditorConfigCollection } from '@umbraco-cms/backoffice/property-editor';
|
||||
import type { UmbPropertyEditorUiElement } from '@umbraco-cms/backoffice/extension-registry';
|
||||
|
||||
import '../../core/components/input-number-range/input-number-range.element.js';
|
||||
|
||||
/**
|
||||
* @element umb-property-editor-ui-number-range
|
||||
*/
|
||||
|
||||
@@ -1,12 +1,10 @@
|
||||
import type { UmbInputRadioButtonListElement } from '../../core/components/input-radio-button-list/input-radio-button-list.element.js';
|
||||
import type { UmbInputRadioButtonListElement } from '@umbraco-cms/backoffice/components';
|
||||
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 { UmbPropertyEditorConfigCollection } from '@umbraco-cms/backoffice/property-editor';
|
||||
import type { UmbPropertyEditorUiElement } from '@umbraco-cms/backoffice/extension-registry';
|
||||
|
||||
import '../../core/components/input-radio-button-list/input-radio-button-list.element.js';
|
||||
|
||||
/**
|
||||
* @element umb-property-editor-ui-radio-button-list
|
||||
*/
|
||||
|
||||
@@ -23,7 +23,7 @@ export const manifest: ManifestPropertyEditorSchema = {
|
||||
],
|
||||
defaultData: [
|
||||
{ alias: 'minVal', value: 0 },
|
||||
{ alias: 'maxVal', value: 0 },
|
||||
{ alias: 'maxVal', value: 100 },
|
||||
],
|
||||
},
|
||||
},
|
||||
|
||||
@@ -50,7 +50,7 @@ export const manifests: Array<ManifestTypes> = [
|
||||
},
|
||||
{
|
||||
alias: 'step',
|
||||
value: 0,
|
||||
value: 1,
|
||||
},
|
||||
],
|
||||
},
|
||||
|
||||
@@ -1,26 +1,28 @@
|
||||
import type { UmbInputSliderElement } from '../../core/components/input-slider/input-slider.element.js';
|
||||
import type { UmbInputSliderElement } from '@umbraco-cms/backoffice/components';
|
||||
import { customElement, html, 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 { UmbPropertyEditorConfigCollection } from '@umbraco-cms/backoffice/property-editor';
|
||||
import type { UmbPropertyEditorUiElement } from '@umbraco-cms/backoffice/extension-registry';
|
||||
|
||||
export type UmbSliderValue = { from: number; to: number } | undefined;
|
||||
|
||||
/**
|
||||
* @element umb-property-editor-ui-slider
|
||||
*/
|
||||
@customElement('umb-property-editor-ui-slider')
|
||||
export class UmbPropertyEditorUISliderElement extends UmbLitElement implements UmbPropertyEditorUiElement {
|
||||
@property({ type: Object })
|
||||
value: { to?: number; from?: number } | undefined = undefined;
|
||||
value: UmbSliderValue | undefined;
|
||||
|
||||
@state()
|
||||
_enableRange = false;
|
||||
|
||||
@state()
|
||||
_initVal1?: number;
|
||||
_initVal1: number = 0;
|
||||
|
||||
@state()
|
||||
_initVal2?: number;
|
||||
_initVal2: number = 1;
|
||||
|
||||
@state()
|
||||
_step = 1;
|
||||
@@ -35,11 +37,24 @@ export class UmbPropertyEditorUISliderElement extends UmbLitElement implements U
|
||||
if (!config) return;
|
||||
|
||||
this._enableRange = Boolean(config.getValueByAlias('enableRange')) ?? false;
|
||||
this._initVal1 = Number(config.getValueByAlias('initVal1'));
|
||||
this._initVal2 = Number(config.getValueByAlias('initVal2'));
|
||||
this._step = Number(config.getValueByAlias('step')) ?? 1;
|
||||
|
||||
// Make sure that step is higher than 0 (decimals ok).
|
||||
const step = (config.getValueByAlias('step') ?? 1) as number;
|
||||
this._step = step > 0 ? step : 1;
|
||||
|
||||
this._initVal1 = Number(config.getValueByAlias('initVal1')) ?? 0;
|
||||
this._initVal2 = Number(config.getValueByAlias('initVal2')) ?? this._initVal1 + this._step;
|
||||
|
||||
this._min = Number(config.getValueByAlias('minVal')) ?? 0;
|
||||
this._max = Number(config.getValueByAlias('maxVal')) ?? 100;
|
||||
|
||||
if (this._min === this._max) {
|
||||
this._max = this._min + 100;
|
||||
//TODO Maybe we want to show some kind of error element rather than trying to fix the mistake made by the user...?
|
||||
throw new Error(
|
||||
`Property Editor Slider: min and max are currently equal. Please change your data type configuration. To render the slider correctly, we changed this slider to: min = ${this._min}, max = ${this._max}`,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
#getValueObject(value: string) {
|
||||
@@ -55,8 +70,8 @@ export class UmbPropertyEditorUISliderElement extends UmbLitElement implements U
|
||||
render() {
|
||||
return html`
|
||||
<umb-input-slider
|
||||
.valueLow=${this.value?.from ?? this._initVal1 ?? 0}
|
||||
.valueHigh=${this.value?.to ?? this._initVal2 ?? 0}
|
||||
.valueLow=${this.value?.from ?? this._initVal1}
|
||||
.valueHigh=${this.value?.to ?? this._initVal2}
|
||||
.step=${this._step}
|
||||
.min=${this._min}
|
||||
.max=${this._max}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import type { UmbInputToggleElement } from '../../core/components/input-toggle/input-toggle.element.js';
|
||||
import type { UmbInputToggleElement } from '@umbraco-cms/backoffice/components';
|
||||
import { customElement, html, property, state } from '@umbraco-cms/backoffice/external/lit';
|
||||
import { UmbLitElement } from '@umbraco-cms/backoffice/lit-element';
|
||||
import { UmbPropertyValueChangeEvent } from '@umbraco-cms/backoffice/property-editor';
|
||||
|
||||
@@ -171,8 +171,8 @@ export class UmbPartialViewWorkspaceContext
|
||||
if (error) {
|
||||
throw new Error(error.message);
|
||||
}
|
||||
this.setIsNew(false);
|
||||
this.#data.setValue(data);
|
||||
this.setIsNew(false);
|
||||
|
||||
// TODO: this might not be the right place to alert the tree, but it works for now
|
||||
const eventContext = await this.getContext(UMB_ACTION_EVENT_CONTEXT);
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
import { loadManifestApi } from '../../../../libs/extension-api/functions/load-manifest-api.function.js';
|
||||
import { availableLanguages } from './input-tiny-mce.languages.js';
|
||||
import { defaultFallbackConfig } from './input-tiny-mce.defaults.js';
|
||||
import { pastePreProcessHandler } from './input-tiny-mce.handlers.js';
|
||||
import { uriAttributeSanitizer } from './input-tiny-mce.sanitizer.js';
|
||||
import type { TinyMcePluginArguments, UmbTinyMcePluginBase } from './tiny-mce-plugin.js';
|
||||
import { loadManifestApi } from '@umbraco-cms/backoffice/extension-api';
|
||||
import { css, customElement, html, property, query, state } from '@umbraco-cms/backoffice/external/lit';
|
||||
import { firstValueFrom } from '@umbraco-cms/backoffice/external/rxjs';
|
||||
import { getProcessedImageUrl } from '@umbraco-cms/backoffice/utils';
|
||||
|
||||
@@ -105,4 +105,6 @@ export class UmbCurrentUserStore extends UmbContextBase<UmbCurrentUserStore> {
|
||||
}
|
||||
}
|
||||
|
||||
export default UmbCurrentUserStore;
|
||||
|
||||
export const UMB_CURRENT_USER_STORE_CONTEXT = new UmbContextToken<UmbCurrentUserStore>('UmbCurrentUserStore');
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
import { UmbCurrentUserStore } from './current-user.store.js';
|
||||
import type { ManifestRepository, ManifestStore, ManifestTypes } from '@umbraco-cms/backoffice/extension-registry';
|
||||
|
||||
export const UMB_CURRENT_USER_REPOSITORY_ALIAS = 'Umb.Repository.CurrentUser';
|
||||
@@ -14,7 +13,7 @@ const store: ManifestStore = {
|
||||
type: 'store',
|
||||
alias: 'Umb.Store.CurrentUser',
|
||||
name: 'Current User Store',
|
||||
api: UmbCurrentUserStore,
|
||||
api: () => import('./current-user.store.js'),
|
||||
};
|
||||
|
||||
export const manifests: Array<ManifestTypes> = [repository, store];
|
||||
|
||||
@@ -210,13 +210,13 @@ export class UmbUserGroupWorkspaceEditorElement extends UmbLitElement {
|
||||
#renderHeader() {
|
||||
return html`
|
||||
<div id="header" slot="header">
|
||||
<uui-button id="icon" @click=${this.#onIconClick} label="icon" compact>
|
||||
<uui-button id="icon" compact label="icon" look="outline" @click=${this.#onIconClick}>
|
||||
<umb-icon name=${this._icon || ''}></umb-icon>
|
||||
</uui-button>
|
||||
|
||||
<umb-input-with-alias
|
||||
id="name"
|
||||
label=${this.localize.term('general_name')}
|
||||
label=${this.localize.term('placeholders_entername')}
|
||||
.value=${this._name}
|
||||
alias=${ifDefined(this._alias)}
|
||||
?auto-generate-alias=${this._isNew}
|
||||
@@ -339,9 +339,12 @@ export class UmbUserGroupWorkspaceEditorElement extends UmbLitElement {
|
||||
}
|
||||
|
||||
#renderRightColumn() {
|
||||
return html` <uui-box headline="Actions">
|
||||
<umb-entity-action-list .entityType=${UMB_USER_GROUP_ENTITY_TYPE} .unique=${this._unique}></umb-entity-action-list
|
||||
></uui-box>`;
|
||||
return html`
|
||||
<uui-box headline="Actions">
|
||||
<umb-entity-action-list .entityType=${UMB_USER_GROUP_ENTITY_TYPE} .unique=${this._unique}>
|
||||
</umb-entity-action-list>
|
||||
</uui-box>
|
||||
`;
|
||||
}
|
||||
|
||||
static styles = [
|
||||
@@ -355,11 +358,14 @@ export class UmbUserGroupWorkspaceEditorElement extends UmbLitElement {
|
||||
#header {
|
||||
display: flex;
|
||||
flex: 1 1 auto;
|
||||
gap: var(--uui-size-space-3);
|
||||
gap: var(--uui-size-space-2);
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
#icon {
|
||||
font-size: calc(var(--uui-size-layout-3) / 2);
|
||||
font-size: var(--uui-size-5);
|
||||
height: 30px;
|
||||
width: 30px;
|
||||
}
|
||||
|
||||
#name {
|
||||
|
||||
@@ -34,7 +34,7 @@ export default {
|
||||
},
|
||||
}),
|
||||
commonjs({
|
||||
include: ['node_modules/base64-js/**/*', 'node_modules/tinymce/**/*']
|
||||
include: ['node_modules/base64-js/**/*', 'node_modules/tinymce/**/*'],
|
||||
}),
|
||||
esbuildPlugin({ ts: true, tsconfig: './tsconfig.json', target: 'auto', json: true }),
|
||||
],
|
||||
@@ -53,6 +53,9 @@ export default {
|
||||
<link rel="stylesheet" href="src/css/user-defined.css">
|
||||
<link rel="stylesheet" href="node_modules/@umbraco-ui/uui-css/dist/uui-css.css">
|
||||
<link rel="stylesheet" href="src/css/umb-css.css">
|
||||
<script type="module">
|
||||
import '@umbraco-cms/backoffice/components';
|
||||
</script>
|
||||
</head>
|
||||
<body>
|
||||
<script type="module" src="${testFramework}"></script>
|
||||
|
||||
Reference in New Issue
Block a user