property type workspace

This commit is contained in:
Niels Lyngsø
2024-07-04 11:12:22 +02:00
parent 8b128eb0b9
commit b2232cef6e
14 changed files with 830 additions and 6 deletions

View File

@@ -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",

View File

@@ -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,

View File

@@ -0,0 +1 @@
export * from './workspace/index.js';

View File

@@ -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];

View File

@@ -0,0 +1,3 @@
import type { UmbPropertyTypeModel, UmbPropertyTypeScaffoldModel } from '@umbraco-cms/backoffice/content-type';
export type UmbPropertyTypeData = UmbPropertyTypeModel | UmbPropertyTypeScaffoldModel;

View File

@@ -0,0 +1 @@
export const UMB_PROPERTY_TYPE_WORKSPACE_ALIAS = 'Umb.Workspace.PropertyType';

View File

@@ -0,0 +1,2 @@
export * from './property-type-workspace.context-token.js';
export * from './property-type-workspace.modal-token.js';

View File

@@ -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],
},
],
},
];

View File

@@ -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;
}
}

View File

@@ -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,
);

View File

@@ -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 };

View File

@@ -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>;

View File

@@ -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;
}
}

View File

@@ -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"],