property type workspace
This commit is contained in:
@@ -25,8 +25,8 @@
|
||||
"./code-editor": "./dist-cms/packages/templating/code-editor/index.js",
|
||||
"./collection": "./dist-cms/packages/core/collection/index.js",
|
||||
"./components": "./dist-cms/packages/core/components/index.js",
|
||||
"./content": "./dist-cms/packages/core/content/index.js",
|
||||
"./content-type": "./dist-cms/packages/core/content-type/index.js",
|
||||
"./content": "./dist-cms/packages/core/content/index.js",
|
||||
"./culture": "./dist-cms/packages/core/culture/index.js",
|
||||
"./current-user": "./dist-cms/packages/user/current-user/index.js",
|
||||
"./data-type": "./dist-cms/packages/data-type/index.js",
|
||||
@@ -35,9 +35,9 @@
|
||||
"./document-blueprint": "./dist-cms/packages/documents/document-blueprints/index.js",
|
||||
"./document-type": "./dist-cms/packages/documents/document-types/index.js",
|
||||
"./document": "./dist-cms/packages/documents/documents/index.js",
|
||||
"./entity": "./dist-cms/packages/core/entity/index.js",
|
||||
"./entity-action": "./dist-cms/packages/core/entity-action/index.js",
|
||||
"./entity-bulk-action": "./dist-cms/packages/core/entity-bulk-action/index.js",
|
||||
"./entity": "./dist-cms/packages/core/entity/index.js",
|
||||
"./event": "./dist-cms/packages/core/event/index.js",
|
||||
"./extension-registry": "./dist-cms/packages/core/extension-registry/index.js",
|
||||
"./icon": "./dist-cms/packages/core/icon-registry/index.js",
|
||||
@@ -63,6 +63,7 @@
|
||||
"./picker-input": "./dist-cms/packages/core/picker-input/index.js",
|
||||
"./property-action": "./dist-cms/packages/core/property-action/index.js",
|
||||
"./property-editor": "./dist-cms/packages/core/property-editor/index.js",
|
||||
"./property-type": "./dist-cms/packages/core/property-type/index.js",
|
||||
"./property": "./dist-cms/packages/core/property/index.js",
|
||||
"./recycle-bin": "./dist-cms/packages/core/recycle-bin/index.js",
|
||||
"./relation-type": "./dist-cms/packages/relations/relation-types/index.js",
|
||||
@@ -73,8 +74,8 @@
|
||||
"./script": "./dist-cms/packages/templating/scripts/index.js",
|
||||
"./search": "./dist-cms/packages/search/index.js",
|
||||
"./section": "./dist-cms/packages/core/section/index.js",
|
||||
"./settings": "./dist-cms/packages/settings/index.js",
|
||||
"./server-file-system": "./dist-cms/packages/core/server-file-system/index.js",
|
||||
"./settings": "./dist-cms/packages/settings/index.js",
|
||||
"./sorter": "./dist-cms/packages/core/sorter/index.js",
|
||||
"./static-file": "./dist-cms/packages/static-file/index.js",
|
||||
"./store": "./dist-cms/packages/core/store/index.js",
|
||||
|
||||
@@ -10,6 +10,7 @@ import { manifests as iconRegistryManifests } from './icon-registry/manifests.js
|
||||
import { manifests as localizationManifests } from './localization/manifests.js';
|
||||
import { manifests as modalManifests } from './modal/common/manifests.js';
|
||||
import { manifests as propertyActionManifests } from './property-action/manifests.js';
|
||||
import { manifests as propertyTypeManifests } from './property-type/manifests.js';
|
||||
import { manifests as recycleBinManifests } from './recycle-bin/manifests.js';
|
||||
import { manifests as sectionManifests } from './section/manifests.js';
|
||||
import { manifests as serverFileSystemManifests } from './server-file-system/manifests.js';
|
||||
@@ -33,6 +34,7 @@ export const manifests: Array<ManifestTypes | UmbBackofficeManifestKind> = [
|
||||
...workspaceManifests,
|
||||
...contentManifests,
|
||||
...contentTypeManifests,
|
||||
...propertyTypeManifests,
|
||||
...settingsManifests,
|
||||
...modalManifests,
|
||||
...entityActionManifests,
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
export * from './workspace/index.js';
|
||||
@@ -0,0 +1,4 @@
|
||||
import { manifests as workspaceManifests } from './workspace/manifests.js';
|
||||
import type { ManifestTypes } from '@umbraco-cms/backoffice/extension-registry';
|
||||
|
||||
export const manifests: Array<ManifestTypes> = [...workspaceManifests];
|
||||
@@ -0,0 +1,3 @@
|
||||
import type { UmbPropertyTypeModel, UmbPropertyTypeScaffoldModel } from '@umbraco-cms/backoffice/content-type';
|
||||
|
||||
export type UmbPropertyTypeData = UmbPropertyTypeModel | UmbPropertyTypeScaffoldModel;
|
||||
@@ -0,0 +1 @@
|
||||
export const UMB_PROPERTY_TYPE_WORKSPACE_ALIAS = 'Umb.Workspace.PropertyType';
|
||||
@@ -0,0 +1,2 @@
|
||||
export * from './property-type-workspace.context-token.js';
|
||||
export * from './property-type-workspace.modal-token.js';
|
||||
@@ -0,0 +1,57 @@
|
||||
import { UMB_PROPERTY_TYPE_WORKSPACE_ALIAS } from './constants.js';
|
||||
import { UmbSubmitWorkspaceAction } from '@umbraco-cms/backoffice/workspace';
|
||||
import type { ManifestTypes } from '@umbraco-cms/backoffice/extension-registry';
|
||||
|
||||
export const manifests: Array<ManifestTypes> = [
|
||||
{
|
||||
type: 'workspace',
|
||||
kind: 'routable',
|
||||
name: 'Block Workspace',
|
||||
alias: UMB_PROPERTY_TYPE_WORKSPACE_ALIAS,
|
||||
api: () => import('./property-type-workspace.context.js'),
|
||||
meta: {
|
||||
entityType: 'property-type',
|
||||
},
|
||||
},
|
||||
{
|
||||
type: 'workspaceView',
|
||||
alias: 'Umb.WorkspaceView.PropertyType.Settings',
|
||||
name: 'Block Workspace Content View',
|
||||
js: () => import('./views/settings/property-workspace-view-settings.element.js'),
|
||||
weight: 1000,
|
||||
meta: {
|
||||
label: '#general_content',
|
||||
pathname: 'content',
|
||||
icon: 'icon-document',
|
||||
},
|
||||
conditions: [
|
||||
{
|
||||
alias: 'Umb.Condition.WorkspaceAlias',
|
||||
match: UMB_PROPERTY_TYPE_WORKSPACE_ALIAS,
|
||||
},
|
||||
],
|
||||
TODO_conditions: [
|
||||
{
|
||||
alias: 'Umb.Condition.BlockEntryShowContentEdit',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
type: 'workspaceAction',
|
||||
kind: 'default',
|
||||
alias: 'Umb.WorkspaceAction.PropertyType.Submit',
|
||||
name: 'Submit Property Type Workspace Action',
|
||||
api: UmbSubmitWorkspaceAction,
|
||||
meta: {
|
||||
label: '#general_submit',
|
||||
look: 'primary',
|
||||
color: 'positive',
|
||||
},
|
||||
conditions: [
|
||||
{
|
||||
alias: 'Umb.Condition.WorkspaceAlias',
|
||||
oneOf: [UMB_PROPERTY_TYPE_WORKSPACE_ALIAS],
|
||||
},
|
||||
],
|
||||
},
|
||||
];
|
||||
@@ -0,0 +1,64 @@
|
||||
import { UMB_PROPERTY_TYPE_WORKSPACE_CONTEXT } from './property-type-workspace.context-token.js';
|
||||
import { UmbTextStyles } from '@umbraco-cms/backoffice/style';
|
||||
import { customElement, css, html, state, property } from '@umbraco-cms/backoffice/external/lit';
|
||||
import { UmbLitElement } from '@umbraco-cms/backoffice/lit-element';
|
||||
import { UmbRepositoryItemsManager } from '@umbraco-cms/backoffice/repository';
|
||||
import type { UmbDocumentTypeItemModel } from '@umbraco-cms/backoffice/document-type';
|
||||
import { UMB_DOCUMENT_TYPE_ITEM_REPOSITORY_ALIAS } from '@umbraco-cms/backoffice/document-type';
|
||||
|
||||
@customElement('umb-property-type-workspace-editor')
|
||||
export class UmbPropertyTypeWorkspaceEditorElement extends UmbLitElement {
|
||||
//
|
||||
#itemManager = new UmbRepositoryItemsManager<UmbDocumentTypeItemModel>(
|
||||
this,
|
||||
UMB_DOCUMENT_TYPE_ITEM_REPOSITORY_ALIAS,
|
||||
(x) => x.unique,
|
||||
);
|
||||
|
||||
#workspaceContext?: typeof UMB_PROPERTY_TYPE_WORKSPACE_CONTEXT.TYPE;
|
||||
|
||||
@state()
|
||||
_name?: string;
|
||||
|
||||
@property({ type: String, attribute: false })
|
||||
workspaceAlias?: string;
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
|
||||
this.consumeContext(UMB_PROPERTY_TYPE_WORKSPACE_CONTEXT, (instance) => {
|
||||
this.#workspaceContext = instance;
|
||||
this.#workspaceContext?.createPropertyDatasetContext(this);
|
||||
});
|
||||
}
|
||||
|
||||
override render() {
|
||||
return this.workspaceAlias
|
||||
? html`
|
||||
<umb-workspace-editor
|
||||
alias=${this.workspaceAlias}
|
||||
headline=${this.localize.term('blockEditor_blockConfigurationOverlayTitle', [this._name])}>
|
||||
</umb-workspace-editor>
|
||||
`
|
||||
: '';
|
||||
}
|
||||
|
||||
static override styles = [
|
||||
UmbTextStyles,
|
||||
css`
|
||||
:host {
|
||||
display: block;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
`,
|
||||
];
|
||||
}
|
||||
|
||||
export default UmbPropertyTypeWorkspaceEditorElement;
|
||||
|
||||
declare global {
|
||||
interface HTMLElementTagNameMap {
|
||||
'umb-property-type-workspace-editor': UmbPropertyTypeWorkspaceEditorElement;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
import type { UmbPropertyTypeWorkspaceContext } from './property-type-workspace.context.js';
|
||||
import { UmbContextToken } from '@umbraco-cms/backoffice/context-api';
|
||||
import type { UmbWorkspaceContext } from '@umbraco-cms/backoffice/workspace';
|
||||
|
||||
export const UMB_PROPERTY_TYPE_WORKSPACE_CONTEXT = new UmbContextToken<
|
||||
UmbWorkspaceContext,
|
||||
UmbPropertyTypeWorkspaceContext
|
||||
>(
|
||||
'UmbWorkspaceContext',
|
||||
undefined,
|
||||
(context): context is UmbPropertyTypeWorkspaceContext => (context as any).IS_PROPERTY_TYPE_WORKSPACE_CONTEXT,
|
||||
);
|
||||
@@ -0,0 +1,189 @@
|
||||
import { UmbPropertyTypeWorkspaceEditorElement } from './property-type-workspace-editor.element.js';
|
||||
import type { UmbPropertyDatasetContext } from '@umbraco-cms/backoffice/property';
|
||||
import type {
|
||||
UmbInvariantDatasetWorkspaceContext,
|
||||
UmbRoutableWorkspaceContext,
|
||||
} from '@umbraco-cms/backoffice/workspace';
|
||||
import {
|
||||
UmbSubmittableWorkspaceContextBase,
|
||||
UmbInvariantWorkspacePropertyDatasetContext,
|
||||
UmbWorkspaceIsNewRedirectController,
|
||||
} from '@umbraco-cms/backoffice/workspace';
|
||||
import { UmbObjectState } from '@umbraco-cms/backoffice/observable-api';
|
||||
import type { UmbControllerHost } from '@umbraco-cms/backoffice/controller-api';
|
||||
import type { ManifestWorkspace } from '@umbraco-cms/backoffice/extension-registry';
|
||||
import {
|
||||
UMB_CONTENT_TYPE_WORKSPACE_CONTEXT,
|
||||
UmbPropertyTypeModel,
|
||||
UmbPropertyTypeSettingsModalData,
|
||||
} from '@umbraco-cms/backoffice/content-type';
|
||||
import { UmbId } from '@umbraco-cms/backoffice/id';
|
||||
import { UmbPropertyTypeData } from '../types.js';
|
||||
|
||||
export class UmbPropertyTypeWorkspaceContext<PropertyTypeData extends UmbPropertyTypeModel = UmbPropertyTypeModel>
|
||||
extends UmbSubmittableWorkspaceContextBase<PropertyTypeData>
|
||||
implements UmbInvariantDatasetWorkspaceContext, UmbRoutableWorkspaceContext
|
||||
{
|
||||
// Just for context token safety:
|
||||
public readonly IS_PROPERTY_TYPE_WORKSPACE_CONTEXT = true;
|
||||
|
||||
#entityType: string;
|
||||
#data = new UmbObjectState<PropertyTypeData | undefined>(undefined);
|
||||
readonly data = this.#data.asObservable();
|
||||
|
||||
readonly name = this.#data.asObservablePart((data) => data?.name);
|
||||
readonly unique = this.#data.asObservablePart((data) => data?.id);
|
||||
|
||||
constructor(host: UmbControllerHost, args: { manifest: ManifestWorkspace }) {
|
||||
super(host, args.manifest.alias);
|
||||
const manifest = args.manifest;
|
||||
this.#entityType = manifest.meta?.entityType;
|
||||
|
||||
this.routes.setRoutes([
|
||||
{
|
||||
// Would it make more sense to have groupKey before elementTypeKey?
|
||||
path: 'create/:containerUnique',
|
||||
component: UmbPropertyTypeWorkspaceEditorElement,
|
||||
setup: async (component, info) => {
|
||||
(component as UmbPropertyTypeWorkspaceEditorElement).workspaceAlias = manifest.alias;
|
||||
|
||||
const containerUnique =
|
||||
info.match.params.containerUnique === 'null' ? null : info.match.params.containerUnique;
|
||||
this.create(containerUnique);
|
||||
|
||||
new UmbWorkspaceIsNewRedirectController(
|
||||
this,
|
||||
this,
|
||||
this.getHostElement().shadowRoot!.querySelector('umb-router-slot')!,
|
||||
);
|
||||
},
|
||||
},
|
||||
{
|
||||
path: 'edit/:unique',
|
||||
component: UmbPropertyTypeWorkspaceEditorElement,
|
||||
setup: (component, info) => {
|
||||
(component as UmbPropertyTypeWorkspaceEditorElement).workspaceAlias = manifest.alias;
|
||||
|
||||
const unique = info.match.params.unique;
|
||||
this.load(unique);
|
||||
},
|
||||
},
|
||||
]);
|
||||
}
|
||||
|
||||
protected override resetState() {
|
||||
super.resetState();
|
||||
this.#data.setValue(undefined);
|
||||
}
|
||||
|
||||
createPropertyDatasetContext(host: UmbControllerHost): UmbPropertyDatasetContext {
|
||||
return new UmbInvariantWorkspacePropertyDatasetContext(host, this);
|
||||
}
|
||||
|
||||
async load(unique: string) {
|
||||
this.resetState();
|
||||
const context = await this.getContext(UMB_CONTENT_TYPE_WORKSPACE_CONTEXT);
|
||||
this.observe(await context.structure.propertyStructureById(unique), (property) => {
|
||||
if (property) {
|
||||
this.#data.setValue(property as PropertyTypeData);
|
||||
}
|
||||
// Fallback to undefined:
|
||||
this.#data.setValue(undefined);
|
||||
});
|
||||
}
|
||||
|
||||
async create(containerId?: string | null) {
|
||||
this.resetState();
|
||||
|
||||
let data: PropertyTypeData = {
|
||||
id: UmbId.new(),
|
||||
container: containerId ? { id: containerId } : null,
|
||||
alias: '',
|
||||
name: '',
|
||||
description: '',
|
||||
variesByCulture: false,
|
||||
variesBySegment: false,
|
||||
validation: {
|
||||
mandatory: false,
|
||||
mandatoryMessage: null,
|
||||
regEx: null,
|
||||
regExMessage: null,
|
||||
},
|
||||
appearance: {
|
||||
labelOnTop: false,
|
||||
},
|
||||
sortOrder: 0,
|
||||
} as PropertyTypeData;
|
||||
|
||||
// If we have a modal context, we blend in the modal preset data: [NL]
|
||||
if (this.modalContext) {
|
||||
data = { ...data, ...this.modalContext.data.preset };
|
||||
}
|
||||
|
||||
this.setIsNew(true);
|
||||
this.#data.setValue(data);
|
||||
return { data };
|
||||
}
|
||||
|
||||
getData() {
|
||||
return this.#data.getValue();
|
||||
}
|
||||
updateData(partialData: Partial<PropertyTypeData>) {
|
||||
this.#data?.update(partialData);
|
||||
}
|
||||
|
||||
getUnique() {
|
||||
return this.getData()!.id;
|
||||
}
|
||||
|
||||
getEntityType() {
|
||||
return this.#entityType;
|
||||
}
|
||||
|
||||
getName() {
|
||||
return this.#data.getValue()?.name;
|
||||
}
|
||||
setName(name: string | undefined) {
|
||||
this.#data.update({ name: name });
|
||||
}
|
||||
|
||||
async propertyValueByAlias<ReturnType = unknown>(propertyAlias: string) {
|
||||
return this.#data.asObservablePart((data) => data?.[propertyAlias as keyof PropertyTypeData] as ReturnType);
|
||||
}
|
||||
|
||||
getPropertyValue<ReturnType = unknown>(propertyAlias: string) {
|
||||
return this.#data.getValue()?.[propertyAlias as keyof PropertyTypeData] as ReturnType;
|
||||
}
|
||||
|
||||
async setPropertyValue(alias: string, value: unknown) {
|
||||
const currentData = this.#data.value;
|
||||
if (currentData) {
|
||||
this.#data.update({ ...currentData, [alias]: value });
|
||||
}
|
||||
}
|
||||
|
||||
async submit() {
|
||||
if (!this.modalContext) {
|
||||
throw new Error('Needs to be in a modal to submit.');
|
||||
}
|
||||
const contentTypeUnique = (this.modalContext.data as unknown as UmbPropertyTypeSettingsModalData).contentTypeUnique;
|
||||
|
||||
const data = this.#data.getValue();
|
||||
if (!data) {
|
||||
throw new Error('No data to submit.');
|
||||
}
|
||||
|
||||
const context = await this.getContext(UMB_CONTENT_TYPE_WORKSPACE_CONTEXT);
|
||||
|
||||
context.structure.insertProperty(contentTypeUnique, data);
|
||||
|
||||
this.setIsNew(false);
|
||||
}
|
||||
|
||||
public override destroy(): void {
|
||||
this.#data.destroy();
|
||||
super.destroy();
|
||||
}
|
||||
}
|
||||
|
||||
export { UmbPropertyTypeWorkspaceContext as api };
|
||||
@@ -0,0 +1,18 @@
|
||||
import type { UmbWorkspaceModalData, UmbWorkspaceModalValue } from '@umbraco-cms/backoffice/modal';
|
||||
import { UmbModalToken } from '@umbraco-cms/backoffice/modal';
|
||||
|
||||
export interface UmbBlockWorkspaceData<OriginDataType = unknown> extends UmbWorkspaceModalData {
|
||||
originData: OriginDataType;
|
||||
}
|
||||
|
||||
export const UMB_PROPERTY_TYPE_WORKSPACE_MODAL = new UmbModalToken<UmbBlockWorkspaceData, UmbWorkspaceModalValue>(
|
||||
'Umb.Modal.Workspace',
|
||||
{
|
||||
modal: {
|
||||
type: 'sidebar',
|
||||
size: 'small',
|
||||
},
|
||||
data: { entityType: 'property-type', preset: {}, originData: {} },
|
||||
},
|
||||
// Recast the type, so the entityType data prop is not required:
|
||||
) as UmbModalToken<Omit<UmbWorkspaceModalData, 'entityType'>, UmbWorkspaceModalValue>;
|
||||
@@ -0,0 +1,469 @@
|
||||
import { css, html, customElement, state, nothing } from '@umbraco-cms/backoffice/external/lit';
|
||||
import { UmbTextStyles } from '@umbraco-cms/backoffice/style';
|
||||
import { UmbLitElement, umbFocus } from '@umbraco-cms/backoffice/lit-element';
|
||||
import type { UmbWorkspaceViewElement } from '@umbraco-cms/backoffice/extension-registry';
|
||||
import { UMB_CONTENT_TYPE_WORKSPACE_CONTEXT, UmbPropertyTypeModel } from '@umbraco-cms/backoffice/content-type';
|
||||
import { UUIBooleanInputEvent, UUIInputEvent, UUISelectEvent } from '@umbraco-cms/backoffice/external/uui';
|
||||
import { generateAlias } from '@umbraco-cms/backoffice/utils';
|
||||
import { UMB_PROPERTY_TYPE_WORKSPACE_CONTEXT } from '../../../index.js';
|
||||
|
||||
@customElement('umb-property-type-workspace-view-settings')
|
||||
export class UmbPropertyTypeWorkspaceViewSettingsElement extends UmbLitElement implements UmbWorkspaceViewElement {
|
||||
#context?: typeof UMB_PROPERTY_TYPE_WORKSPACE_CONTEXT.TYPE;
|
||||
|
||||
@state() private _customValidationOptions: Array<Option> = [
|
||||
{
|
||||
name: this.localize.term('validation_validateNothing'),
|
||||
value: '!NOVALIDATION!',
|
||||
selected: true,
|
||||
},
|
||||
{
|
||||
name: this.localize.term('validation_validateAsEmail'),
|
||||
value: '[a-zA-Z0-9_.+-]+@[a-zA-Z0-9-]+\\.[a-zA-Z0-9-.]+',
|
||||
},
|
||||
{
|
||||
name: this.localize.term('validation_validateAsNumber'),
|
||||
value: '^[0-9]*$',
|
||||
},
|
||||
{
|
||||
name: this.localize.term('validation_validateAsUrl'),
|
||||
value: 'https?://[a-zA-Z0-9-.]+\\.[a-zA-Z]{2,}',
|
||||
},
|
||||
{
|
||||
name: this.localize.term('validation_enterCustomValidation'),
|
||||
value: '.+',
|
||||
},
|
||||
];
|
||||
|
||||
@state()
|
||||
private _data?: UmbPropertyTypeModel;
|
||||
|
||||
@state()
|
||||
private _aliasLocked = true;
|
||||
|
||||
@state()
|
||||
private _contentTypeVariesByCulture?: boolean;
|
||||
|
||||
@state()
|
||||
private _contentTypeVariesBySegment?: boolean;
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
|
||||
this.consumeContext(UMB_PROPERTY_TYPE_WORKSPACE_CONTEXT, (instance) => {
|
||||
this.#context = instance;
|
||||
this.observe(instance.data, (data) => (this._data = data));
|
||||
});
|
||||
|
||||
this.consumeContext(UMB_CONTENT_TYPE_WORKSPACE_CONTEXT, (instance) => {
|
||||
this.observe(instance.variesByCulture, (variesByCulture) => (this._contentTypeVariesByCulture = variesByCulture));
|
||||
this.observe(instance.variesBySegment, (variesBySegment) => (this._contentTypeVariesBySegment = variesBySegment));
|
||||
}).passContextAliasMatches();
|
||||
}
|
||||
|
||||
updateValue(partialValue: Partial<UmbPropertyTypeModel>) {
|
||||
this.#context?.updateData(partialValue);
|
||||
}
|
||||
|
||||
#onNameChange(event: UUIInputEvent) {
|
||||
const oldName = this._data?.name;
|
||||
const oldAlias = this._data?.alias;
|
||||
this.updateValue({ name: event.target.value.toString() });
|
||||
if (this._aliasLocked) {
|
||||
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.updateValue({ alias: generateAlias(this._data?.name ?? '') });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#onAliasChange(event: UUIInputEvent) {
|
||||
const alias = generateAlias(event.target.value.toString());
|
||||
if (this._aliasLocked) {
|
||||
this.updateValue({ alias });
|
||||
}
|
||||
}
|
||||
|
||||
#onDescriptionChange(event: UUIInputEvent) {
|
||||
this.updateValue({ description: event.target.value.toString() });
|
||||
}
|
||||
|
||||
#onDataTypeIdChange(event: UUIInputEvent) {
|
||||
const dataTypeUnique = event.target.value.toString();
|
||||
this.updateValue({ dataType: { unique: dataTypeUnique } });
|
||||
}
|
||||
|
||||
#onMandatoryChange(event: UUIBooleanInputEvent) {
|
||||
const mandatory = event.target.checked;
|
||||
this.updateValue({
|
||||
validation: { ...this._data?.validation, mandatory },
|
||||
});
|
||||
}
|
||||
|
||||
#onMandatoryMessageChange(event: UUIInputEvent) {
|
||||
const mandatoryMessage = event.target.value.toString();
|
||||
this.updateValue({
|
||||
validation: { ...this._data?.validation, mandatory: this._data?.validation.mandatory ?? false, mandatoryMessage },
|
||||
});
|
||||
}
|
||||
|
||||
#setAppearanceNormal() {
|
||||
const currentValue = this._data?.appearance?.labelOnTop;
|
||||
if (currentValue !== true) return;
|
||||
|
||||
this.updateValue({
|
||||
appearance: { ...this._data?.appearance, labelOnTop: false },
|
||||
});
|
||||
}
|
||||
#setAppearanceTop() {
|
||||
const currentValue = this._data?.appearance?.labelOnTop;
|
||||
if (currentValue === true) return;
|
||||
|
||||
this.updateValue({
|
||||
appearance: { ...this._data?.appearance, labelOnTop: true },
|
||||
});
|
||||
}
|
||||
|
||||
#onToggleAliasLock() {
|
||||
this._aliasLocked = !this._aliasLocked;
|
||||
}
|
||||
|
||||
#onCustomValidationChange(event: UUISelectEvent) {
|
||||
const value = event.target.value.toString();
|
||||
const regEx = value !== '!NOVALIDATION!' ? value : null;
|
||||
|
||||
this.updateValue({
|
||||
validation: { ...this._data?.validation, mandatory: this._data?.validation.mandatory ?? false, regEx },
|
||||
});
|
||||
}
|
||||
|
||||
#onValidationRegExChange(event: UUIInputEvent) {
|
||||
const value = event.target.value.toString();
|
||||
const regEx = value !== '!NOVALIDATION!' ? value : null;
|
||||
const betterChoice = this._customValidationOptions.find((option) => {
|
||||
option.selected = option.value === value;
|
||||
return option.selected;
|
||||
});
|
||||
if (betterChoice === undefined) {
|
||||
this._customValidationOptions[4].selected = true;
|
||||
this.requestUpdate('_customValidationOptions');
|
||||
}
|
||||
this.updateValue({
|
||||
validation: { ...this._data?.validation, mandatory: this._data?.validation.mandatory ?? false, regEx },
|
||||
});
|
||||
}
|
||||
#onValidationMessageChange(event: UUIInputEvent) {
|
||||
const regExMessage = event.target.value.toString();
|
||||
this.updateValue({
|
||||
validation: { ...this._data?.validation, mandatory: this._data?.validation.mandatory ?? false, regExMessage },
|
||||
});
|
||||
}
|
||||
|
||||
#onVaryByCultureChange(event: UUIBooleanInputEvent) {
|
||||
const variesByCulture = event.target.checked;
|
||||
this.updateValue({
|
||||
variesByCulture,
|
||||
});
|
||||
}
|
||||
|
||||
override render() {
|
||||
return this._data
|
||||
? html`
|
||||
<uui-box>
|
||||
<div class="container">
|
||||
<!-- TODO: Align styling across this and the property of document type workspace editor, or consider if this can go away for a different UX flow -->
|
||||
<uui-input
|
||||
id="name-input"
|
||||
name="name"
|
||||
label=${this.localize.term('placeholders_entername')}
|
||||
@input=${this.#onNameChange}
|
||||
.value=${this._data?.name}
|
||||
placeholder=${this.localize.term('placeholders_entername')}
|
||||
${umbFocus()}>
|
||||
<!-- TODO: validation for bad characters -->
|
||||
</uui-input>
|
||||
<uui-input
|
||||
id="alias-input"
|
||||
name="alias"
|
||||
@input=${this.#onAliasChange}
|
||||
.value=${this._data?.alias}
|
||||
label=${this.localize.term('placeholders_enterAlias')}
|
||||
placeholder=${this.localize.term('placeholders_enterAlias')}
|
||||
?disabled=${this._aliasLocked}>
|
||||
<!-- TODO: validation for bad characters -->
|
||||
<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-textarea
|
||||
id="description-input"
|
||||
name="description"
|
||||
@input=${this.#onDescriptionChange}
|
||||
label=${this.localize.term('placeholders_enterDescription')}
|
||||
placeholder=${this.localize.term('placeholders_enterDescription')}
|
||||
.value=${this._data?.description}></uui-textarea>
|
||||
</div>
|
||||
<umb-data-type-flow-input
|
||||
.value=${this._data?.dataType?.unique ?? ''}
|
||||
@change=${this.#onDataTypeIdChange}></umb-data-type-flow-input>
|
||||
<hr />
|
||||
<div class="container">
|
||||
<b><umb-localize key="validation_validation">Validation</umb-localize></b>
|
||||
${this.#renderMandatory()}
|
||||
<p style="margin-bottom: 0">
|
||||
<umb-localize key="validation_customValidation">Custom validation</umb-localize>
|
||||
</p>
|
||||
${this.#renderCustomValidation()}
|
||||
</div>
|
||||
<hr />
|
||||
${this.#renderVariationControls()}
|
||||
<div class="container">
|
||||
<b style="margin-bottom: var(--uui-size-space-3)">
|
||||
<umb-localize key="contentTypeEditor_displaySettingsHeadline">Appearance</umb-localize>
|
||||
</b>
|
||||
<div id="appearances">${this.#renderAlignLeftIcon()} ${this.#renderAlignTopIcon()}</div>
|
||||
</div>
|
||||
</uui-box>
|
||||
`
|
||||
: '';
|
||||
}
|
||||
|
||||
#renderAlignLeftIcon() {
|
||||
return html`<button
|
||||
type="button"
|
||||
@click=${this.#setAppearanceNormal}
|
||||
class="appearance left ${this._data?.appearance?.labelOnTop ? '' : 'selected'}">
|
||||
<svg width="200" height="48" viewBox="0 0 200 60" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<rect width="94" height="14" rx="6" fill="currentColor" />
|
||||
<rect y="22" width="64" height="9" rx="4" fill="currentColor" fill-opacity="0.4" />
|
||||
<rect x="106" width="94" height="60" rx="5" fill="currentColor" fill-opacity="0.4" />
|
||||
</svg>
|
||||
<label class="appearance-label">
|
||||
<umb-localize key="contentTypeEditor_displaySettingsLabelOnLeft">Label to the left</umb-localize>
|
||||
</label>
|
||||
</button>`;
|
||||
}
|
||||
|
||||
#renderAlignTopIcon() {
|
||||
return html`
|
||||
<button
|
||||
type="button"
|
||||
@click=${this.#setAppearanceTop}
|
||||
class="appearance top ${this._data?.appearance?.labelOnTop ? 'selected' : ''}">
|
||||
<svg width="140" height="48" viewBox="0 0 140 60" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<rect width="90" height="14" rx="6" fill="currentColor" />
|
||||
<rect y="22" width="64" height="9" rx="4" fill="currentColor" fill-opacity="0.4" />
|
||||
<rect y="42" width="140" height="36" rx="5" fill="currentColor" fill-opacity="0.4" />
|
||||
</svg>
|
||||
<label class="appearance-label">
|
||||
<umb-localize key="contentTypeEditor_displaySettingsLabelOnTop">Label above (full-width)</umb-localize>
|
||||
</label>
|
||||
</button>
|
||||
`;
|
||||
}
|
||||
|
||||
#renderMandatory() {
|
||||
return html`<div style="display: flex; justify-content: space-between">
|
||||
<label for="mandatory">
|
||||
<umb-localize key="validation_fieldIsMandatory">Field is mandatory</umb-localize>
|
||||
</label>
|
||||
<uui-toggle
|
||||
@change=${this.#onMandatoryChange}
|
||||
id="mandatory"
|
||||
.checked=${this._data?.validation?.mandatory ?? false}
|
||||
slot="editor"></uui-toggle>
|
||||
</div>
|
||||
${this._data?.validation?.mandatory
|
||||
? html`<uui-input
|
||||
name="mandatory-message"
|
||||
value=${this._data.validation?.mandatoryMessage ?? ''}
|
||||
@change=${this.#onMandatoryMessageChange}
|
||||
style="margin-top: var(--uui-size-space-1)"
|
||||
id="mandatory-message"
|
||||
placeholder=${this.localize.term('validation_mandatoryMessage')}
|
||||
label=${this.localize.term('validation_mandatoryMessage')}></uui-input>`
|
||||
: ''}`;
|
||||
}
|
||||
|
||||
#renderCustomValidation() {
|
||||
return html`<uui-select
|
||||
style="margin-top: var(--uui-size-space-1)"
|
||||
@change=${this.#onCustomValidationChange}
|
||||
.options=${this._customValidationOptions}></uui-select>
|
||||
|
||||
${this._data?.validation?.regEx !== null
|
||||
? html`
|
||||
<uui-input
|
||||
name="pattern"
|
||||
style="margin-bottom: var(--uui-size-space-1); margin-top: var(--uui-size-space-5);"
|
||||
@change=${this.#onValidationRegExChange}
|
||||
placeholder=${this.localize.term('validation_validationRegExp')}
|
||||
label=${this.localize.term('validation_validationRegExp')}
|
||||
.value=${this._data.validation?.regEx ?? ''}></uui-input>
|
||||
<uui-textarea
|
||||
name="pattern-message"
|
||||
@change=${this.#onValidationMessageChange}
|
||||
placeholder=${this.localize.term('validation_validationRegExpMessage')}
|
||||
label=${this.localize.term('validation_validationRegExpMessage')}
|
||||
.value=${this._data.validation?.regExMessage ?? ''}></uui-textarea>
|
||||
`
|
||||
: nothing} `;
|
||||
}
|
||||
|
||||
#renderVariationControls() {
|
||||
return this._contentTypeVariesByCulture || this._contentTypeVariesBySegment
|
||||
? html` <div class="container">
|
||||
<b><umb-localize key="contentTypeEditor_variantsHeading">Allow variations</umb-localize></b>
|
||||
${this._contentTypeVariesByCulture ? this.#renderVaryByCulture() : ''}
|
||||
</div>
|
||||
<hr />`
|
||||
: '';
|
||||
}
|
||||
#renderVaryByCulture() {
|
||||
return html`<uui-toggle
|
||||
@change=${this.#onVaryByCultureChange}
|
||||
.checked=${this._data?.variesByCulture ?? false}
|
||||
label=${this.localize.term('contentTypeEditor_cultureVariantLabel')}></uui-toggle> `;
|
||||
}
|
||||
|
||||
static override styles = [
|
||||
UmbTextStyles,
|
||||
css`
|
||||
:host {
|
||||
color: var(--uui-color-text);
|
||||
}
|
||||
#content {
|
||||
padding: var(--uui-size-layout-1);
|
||||
}
|
||||
#alias-input,
|
||||
#label-input,
|
||||
#description-input {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
#alias-input {
|
||||
border-color: transparent;
|
||||
background: var(--uui-color-surface);
|
||||
}
|
||||
|
||||
#label-input {
|
||||
font-weight: bold; /* TODO: UUI Input does not support bold text yet */
|
||||
--uui-input-border-color: transparent;
|
||||
}
|
||||
#label-input input {
|
||||
font-weight: bold;
|
||||
--uui-input-border-color: transparent;
|
||||
}
|
||||
|
||||
#alias-lock {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
cursor: pointer;
|
||||
}
|
||||
#alias-lock uui-icon {
|
||||
margin-bottom: 2px;
|
||||
/* margin: 0; */
|
||||
}
|
||||
#description-input {
|
||||
--uui-textarea-border-color: transparent;
|
||||
font-weight: 0.5rem; /* TODO: Cant change font size of UUI textarea yet */
|
||||
}
|
||||
|
||||
#appearances {
|
||||
display: flex;
|
||||
gap: var(--uui-size-layout-1);
|
||||
max-width: 350px;
|
||||
margin: 0 auto;
|
||||
}
|
||||
.appearance {
|
||||
position: relative;
|
||||
display: flex;
|
||||
border: 1px solid var(--uui-color-border-standalone);
|
||||
background-color: transparent;
|
||||
padding: var(--uui-size-space-4) var(--uui-size-space-5);
|
||||
align-items: center;
|
||||
border-radius: var(--uui-border-radius);
|
||||
opacity: 0.8;
|
||||
flex-direction: column;
|
||||
justify-content: space-between;
|
||||
gap: var(--uui-size-space-3);
|
||||
}
|
||||
.appearance-label {
|
||||
font-size: 0.8rem;
|
||||
line-height: 1;
|
||||
font-weight: bold;
|
||||
pointer-events: none;
|
||||
}
|
||||
.appearance.left {
|
||||
flex-grow: 1;
|
||||
}
|
||||
.appearance.top {
|
||||
flex-shrink: 1;
|
||||
}
|
||||
.appearance svg {
|
||||
display: flex;
|
||||
width: 100%;
|
||||
color: var(--uui-color-text);
|
||||
}
|
||||
.appearance:not(.selected):hover {
|
||||
border-color: var(--uui-color-border-emphasis);
|
||||
cursor: pointer;
|
||||
opacity: 1;
|
||||
}
|
||||
.appearance.selected {
|
||||
background-color: var(--uui-color-surface);
|
||||
border-color: var(--uui-color-selected);
|
||||
color: var(--uui-color-selected);
|
||||
opacity: 1;
|
||||
}
|
||||
.appearance.selected svg {
|
||||
color: var(--uui-color-selected);
|
||||
}
|
||||
.appearance.selected::after {
|
||||
content: '';
|
||||
position: absolute;
|
||||
inset: 0;
|
||||
border-radius: 6px;
|
||||
opacity: 0.1;
|
||||
background-color: var(--uui-color-selected);
|
||||
}
|
||||
hr {
|
||||
border: none;
|
||||
border-top: 1px solid var(--uui-color-divider);
|
||||
margin-top: var(--uui-size-space-6);
|
||||
margin-bottom: var(--uui-size-space-5);
|
||||
}
|
||||
uui-input {
|
||||
width: 100%;
|
||||
}
|
||||
#alias-lock {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
cursor: pointer;
|
||||
}
|
||||
#alias-lock uui-icon {
|
||||
margin-bottom: 2px;
|
||||
}
|
||||
.container {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
uui-form,
|
||||
form {
|
||||
display: block;
|
||||
height: 100%;
|
||||
}
|
||||
`,
|
||||
];
|
||||
}
|
||||
|
||||
export default UmbPropertyTypeWorkspaceViewSettingsElement;
|
||||
|
||||
declare global {
|
||||
interface HTMLElementTagNameMap {
|
||||
'umb-property-type-workspace-view-settings': UmbPropertyTypeWorkspaceViewSettingsElement;
|
||||
}
|
||||
}
|
||||
@@ -51,8 +51,8 @@ DON'T EDIT THIS FILE DIRECTLY. It is generated by /devops/tsconfig/index.js
|
||||
"@umbraco-cms/backoffice/code-editor": ["./src/packages/templating/code-editor/index.ts"],
|
||||
"@umbraco-cms/backoffice/collection": ["./src/packages/core/collection/index.ts"],
|
||||
"@umbraco-cms/backoffice/components": ["./src/packages/core/components/index.ts"],
|
||||
"@umbraco-cms/backoffice/content": ["./src/packages/core/content/index.ts"],
|
||||
"@umbraco-cms/backoffice/content-type": ["./src/packages/core/content-type/index.ts"],
|
||||
"@umbraco-cms/backoffice/content": ["./src/packages/core/content/index.ts"],
|
||||
"@umbraco-cms/backoffice/culture": ["./src/packages/core/culture/index.ts"],
|
||||
"@umbraco-cms/backoffice/current-user": ["./src/packages/user/current-user/index.ts"],
|
||||
"@umbraco-cms/backoffice/data-type": ["./src/packages/data-type/index.ts"],
|
||||
@@ -61,9 +61,9 @@ DON'T EDIT THIS FILE DIRECTLY. It is generated by /devops/tsconfig/index.js
|
||||
"@umbraco-cms/backoffice/document-blueprint": ["./src/packages/documents/document-blueprints/index.ts"],
|
||||
"@umbraco-cms/backoffice/document-type": ["./src/packages/documents/document-types/index.ts"],
|
||||
"@umbraco-cms/backoffice/document": ["./src/packages/documents/documents/index.ts"],
|
||||
"@umbraco-cms/backoffice/entity": ["./src/packages/core/entity/index.ts"],
|
||||
"@umbraco-cms/backoffice/entity-action": ["./src/packages/core/entity-action/index.ts"],
|
||||
"@umbraco-cms/backoffice/entity-bulk-action": ["./src/packages/core/entity-bulk-action/index.ts"],
|
||||
"@umbraco-cms/backoffice/entity": ["./src/packages/core/entity/index.ts"],
|
||||
"@umbraco-cms/backoffice/event": ["./src/packages/core/event/index.ts"],
|
||||
"@umbraco-cms/backoffice/extension-registry": ["./src/packages/core/extension-registry/index.ts"],
|
||||
"@umbraco-cms/backoffice/icon": ["./src/packages/core/icon-registry/index.ts"],
|
||||
@@ -89,6 +89,7 @@ DON'T EDIT THIS FILE DIRECTLY. It is generated by /devops/tsconfig/index.js
|
||||
"@umbraco-cms/backoffice/picker-input": ["./src/packages/core/picker-input/index.ts"],
|
||||
"@umbraco-cms/backoffice/property-action": ["./src/packages/core/property-action/index.ts"],
|
||||
"@umbraco-cms/backoffice/property-editor": ["./src/packages/core/property-editor/index.ts"],
|
||||
"@umbraco-cms/backoffice/property-type": ["./src/packages/core/property-type/index.ts"],
|
||||
"@umbraco-cms/backoffice/property": ["./src/packages/core/property/index.ts"],
|
||||
"@umbraco-cms/backoffice/recycle-bin": ["./src/packages/core/recycle-bin/index.ts"],
|
||||
"@umbraco-cms/backoffice/relation-type": ["./src/packages/relations/relation-types/index.ts"],
|
||||
@@ -99,8 +100,8 @@ DON'T EDIT THIS FILE DIRECTLY. It is generated by /devops/tsconfig/index.js
|
||||
"@umbraco-cms/backoffice/script": ["./src/packages/templating/scripts/index.ts"],
|
||||
"@umbraco-cms/backoffice/search": ["./src/packages/search/index.ts"],
|
||||
"@umbraco-cms/backoffice/section": ["./src/packages/core/section/index.ts"],
|
||||
"@umbraco-cms/backoffice/settings": ["./src/packages/settings/index.ts"],
|
||||
"@umbraco-cms/backoffice/server-file-system": ["./src/packages/core/server-file-system/index.ts"],
|
||||
"@umbraco-cms/backoffice/settings": ["./src/packages/settings/index.ts"],
|
||||
"@umbraco-cms/backoffice/sorter": ["./src/packages/core/sorter/index.ts"],
|
||||
"@umbraco-cms/backoffice/static-file": ["./src/packages/static-file/index.ts"],
|
||||
"@umbraco-cms/backoffice/store": ["./src/packages/core/store/index.ts"],
|
||||
|
||||
Reference in New Issue
Block a user