Skip to content

Commit b0b1ef7

Browse files
authored
Merge pull request #1752 from umbraco/feature/input-with-alias
Feat: input with alias
2 parents b04739e + 0bbd07f commit b0b1ef7

File tree

9 files changed

+214
-242
lines changed

9 files changed

+214
-242
lines changed

src/packages/core/components/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ export * from './input-radio-button-list/index.js';
2525
export * from './input-slider/index.js';
2626
export * from './input-toggle/index.js';
2727
export * from './input-upload-field/index.js';
28+
export * from './input-with-alias/input-with-alias.element.js';
2829
export * from './multiple-color-picker-input/index.js';
2930
export * from './multiple-text-string-input/index.js';
3031
export * from './popover-layout/index.js';

src/packages/core/components/input-slider/input-slider.element.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import { html, customElement, property } from '@umbraco-cms/backoffice/external/
22
import { UUIFormControlMixin } from '@umbraco-cms/backoffice/external/uui';
33
import { UmbLitElement } from '@umbraco-cms/backoffice/lit-element';
44
import type { UUISliderEvent } from '@umbraco-cms/backoffice/external/uui';
5+
import { UmbChangeEvent } from '@umbraco-cms/backoffice/event';
56

67
@customElement('umb-input-slider')
78
export class UmbInputSliderElement extends UUIFormControlMixin(UmbLitElement, '') {
@@ -30,7 +31,7 @@ export class UmbInputSliderElement extends UUIFormControlMixin(UmbLitElement, ''
3031
#onChange(e: UUISliderEvent) {
3132
e.stopPropagation();
3233
this.value = e.target.value as string;
33-
this.dispatchEvent(new CustomEvent('change', { bubbles: true, composed: true }));
34+
this.dispatchEvent(new UmbChangeEvent());
3435
}
3536

3637
render() {
Lines changed: 127 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,127 @@
1+
import { css, customElement, html, nothing, property, state } from '@umbraco-cms/backoffice/external/lit';
2+
import { UmbChangeEvent } from '@umbraco-cms/backoffice/event';
3+
import { UmbFormControlMixin } from '@umbraco-cms/backoffice/validation';
4+
import { UmbLitElement } from '@umbraco-cms/backoffice/lit-element';
5+
import { type UUIInputElement, UUIInputEvent } from '@umbraco-cms/backoffice/external/uui';
6+
import { generateAlias } from '@umbraco-cms/backoffice/utils';
7+
8+
@customElement('umb-input-with-alias')
9+
export class UmbInputWithAliasElement extends UmbFormControlMixin<string>(UmbLitElement) {
10+
@property({ type: String })
11+
label: string = '';
12+
13+
@property({ type: String, reflect: false })
14+
alias?: string;
15+
16+
@property({ type: Boolean, reflect: true, attribute: 'alias-readonly' })
17+
aliasReadonly = false;
18+
19+
@property({ type: Boolean, attribute: 'auto-generate-alias' })
20+
autoGenerateAlias?: boolean;
21+
22+
@state()
23+
private _aliasLocked = true;
24+
25+
firstUpdated() {
26+
this.shadowRoot?.querySelectorAll<UUIInputElement>('uui-input').forEach((x) => this.addFormControlElement(x));
27+
}
28+
29+
focus() {
30+
return this.shadowRoot?.querySelector<UUIInputElement>('uui-input')?.focus();
31+
}
32+
33+
#onNameChange(event: UUIInputEvent) {
34+
if (event instanceof UUIInputEvent) {
35+
const target = event.composedPath()[0] as UUIInputElement;
36+
37+
if (typeof target?.value === 'string') {
38+
const oldName = this.value;
39+
const oldAlias = this.alias ?? '';
40+
this.value = event.target.value.toString();
41+
if (this.autoGenerateAlias && this._aliasLocked) {
42+
// If locked we will update the alias, but only if it matches the generated alias of the old name [NL]
43+
const expectedOldAlias = generateAlias(oldName ?? '');
44+
// 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]
45+
if (expectedOldAlias === oldAlias) {
46+
this.alias = generateAlias(this.value);
47+
}
48+
}
49+
this.dispatchEvent(new UmbChangeEvent());
50+
}
51+
}
52+
}
53+
54+
#onAliasChange(e: UUIInputEvent) {
55+
if (event instanceof UUIInputEvent) {
56+
const target = event.composedPath()[0] as UUIInputElement;
57+
if (typeof target?.value === 'string') {
58+
this.alias = target.value;
59+
this.dispatchEvent(new UmbChangeEvent());
60+
}
61+
}
62+
e.stopPropagation();
63+
}
64+
65+
#onToggleAliasLock() {
66+
this._aliasLocked = !this._aliasLocked;
67+
}
68+
69+
render() {
70+
// Localizations: [NL]
71+
return html`
72+
<uui-input id="name" label=${this.label} .value=${this.value} @input="${this.#onNameChange}">
73+
<!-- TODO: should use UUI-LOCK-INPUT, but that does not fire an event when its locked/unlocked -->
74+
<uui-input
75+
name="alias"
76+
slot="append"
77+
label="alias"
78+
@input=${this.#onAliasChange}
79+
.value=${this.alias}
80+
placeholder="Enter alias..."
81+
?disabled=${this._aliasLocked && !this.aliasReadonly}
82+
?readonly=${this.aliasReadonly}>
83+
<!-- TODO: validation for bad characters -->
84+
${this.aliasReadonly
85+
? nothing
86+
: html`<div @click=${this.#onToggleAliasLock} @keydown=${() => ''} id="alias-lock" slot="prepend">
87+
<uui-icon name=${this._aliasLocked ? 'icon-lock' : 'icon-unlocked'}></uui-icon>
88+
</div>`}
89+
</uui-input>
90+
</uui-input>
91+
`;
92+
}
93+
94+
static styles = css`
95+
#name {
96+
width: 100%;
97+
flex: 1 1 auto;
98+
align-items: center;
99+
}
100+
101+
:host(:invalid:not([pristine])) {
102+
color: var(--uui-color-danger);
103+
}
104+
:host(:invalid:not([pristine])) > uui-input {
105+
border-color: var(--uui-color-danger);
106+
}
107+
108+
#alias-lock {
109+
display: flex;
110+
align-items: center;
111+
justify-content: center;
112+
cursor: pointer;
113+
}
114+
115+
#alias-lock uui-icon {
116+
margin-bottom: 2px;
117+
}
118+
`;
119+
}
120+
121+
export default UmbInputWithAliasElement;
122+
123+
declare global {
124+
interface HTMLElementTagNameMap {
125+
'umb-input-with-alias': UmbInputWithAliasElement;
126+
}
127+
}

src/packages/documents/document-types/workspace/document-type-workspace-editor.element.ts

Lines changed: 18 additions & 69 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,9 @@
11
import { UMB_DOCUMENT_TYPE_WORKSPACE_CONTEXT } from './document-type-workspace.context-token.js';
2+
import type { UmbInputWithAliasElement } from '@umbraco-cms/backoffice/components';
23
import { umbFocus, UmbLitElement } from '@umbraco-cms/backoffice/lit-element';
3-
import type { UUIInputElement } from '@umbraco-cms/backoffice/external/uui';
4-
import { UUIInputEvent } from '@umbraco-cms/backoffice/external/uui';
54
import { css, html, customElement, state, ifDefined } from '@umbraco-cms/backoffice/external/lit';
65
import { UMB_MODAL_MANAGER_CONTEXT, UMB_ICON_PICKER_MODAL } from '@umbraco-cms/backoffice/modal';
7-
import { generateAlias } from '@umbraco-cms/backoffice/utils';
6+
87
@customElement('umb-document-type-workspace-editor')
98
export class UmbDocumentTypeWorkspaceEditorElement extends UmbLitElement {
109
@state()
@@ -14,10 +13,10 @@ export class UmbDocumentTypeWorkspaceEditorElement extends UmbLitElement {
1413
private _alias?: string;
1514

1615
@state()
17-
private _aliasLocked = true;
16+
private _icon?: string;
1817

1918
@state()
20-
private _icon?: string;
19+
private _isNew?: boolean;
2120

2221
#workspaceContext?: typeof UMB_DOCUMENT_TYPE_WORKSPACE_CONTEXT.TYPE;
2322

@@ -35,42 +34,7 @@ export class UmbDocumentTypeWorkspaceEditorElement extends UmbLitElement {
3534
this.observe(this.#workspaceContext.name, (name) => (this._name = name), '_observeName');
3635
this.observe(this.#workspaceContext.alias, (alias) => (this._alias = alias), '_observeAlias');
3736
this.observe(this.#workspaceContext.icon, (icon) => (this._icon = icon), '_observeIcon');
38-
}
39-
40-
// TODO. find a way where we don't have to do this for all workspaces.
41-
#onNameChange(event: UUIInputEvent) {
42-
if (event instanceof UUIInputEvent) {
43-
const target = event.composedPath()[0] as UUIInputElement;
44-
45-
if (typeof target?.value === 'string') {
46-
const oldName = this._name;
47-
const oldAlias = this._alias;
48-
const newName = event.target.value.toString();
49-
if (this._aliasLocked) {
50-
const expectedOldAlias = generateAlias(oldName ?? '');
51-
// 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.)
52-
if (expectedOldAlias === oldAlias) {
53-
this.#workspaceContext?.setAlias(generateAlias(newName));
54-
}
55-
}
56-
this.#workspaceContext?.setName(target.value);
57-
}
58-
}
59-
}
60-
61-
// TODO. find a way where we don't have to do this for all workspaces.
62-
#onAliasChange(event: UUIInputEvent) {
63-
if (event instanceof UUIInputEvent) {
64-
const target = event.composedPath()[0] as UUIInputElement;
65-
if (typeof target?.value === 'string') {
66-
this.#workspaceContext?.setAlias(target.value);
67-
}
68-
}
69-
event.stopPropagation();
70-
}
71-
72-
#onToggleAliasLock() {
73-
this._aliasLocked = !this._aliasLocked;
37+
this.observe(this.#workspaceContext.isNew, (isNew) => (this._isNew = isNew), '_observeIsNew');
7438
}
7539

7640
private async _handleIconClick() {
@@ -92,6 +56,11 @@ export class UmbDocumentTypeWorkspaceEditorElement extends UmbLitElement {
9256
});
9357
}
9458

59+
#onNameAndAliasChange(event: InputEvent & { target: UmbInputWithAliasElement }) {
60+
this.#workspaceContext?.setName(event.target.value ?? '');
61+
this.#workspaceContext?.setAlias(event.target.alias ?? '');
62+
}
63+
9564
render() {
9665
return html`
9766
<umb-workspace-editor alias="Umb.Workspace.DocumentType">
@@ -100,22 +69,14 @@ export class UmbDocumentTypeWorkspaceEditorElement extends UmbLitElement {
10069
<umb-icon name=${ifDefined(this._icon)}></umb-icon>
10170
</uui-button>
10271
103-
<uui-input id="name" label="name" .value=${this._name} @input="${this.#onNameChange}" ${umbFocus()}>
104-
<!-- TODO: should use UUI-LOCK-INPUT, but that does not fire an event when its locked/unlocked -->
105-
<uui-input
106-
name="alias"
107-
slot="append"
108-
label="alias"
109-
@input=${this.#onAliasChange}
110-
.value=${this._alias}
111-
placeholder="Enter alias..."
112-
?disabled=${this._aliasLocked}>
113-
<!-- TODO: validation for bad characters -->
114-
<div @click=${this.#onToggleAliasLock} @keydown=${() => ''} id="alias-lock" slot="prepend">
115-
<uui-icon name=${this._aliasLocked ? 'icon-lock' : 'icon-unlocked'}></uui-icon>
116-
</div>
117-
</uui-input>
118-
</uui-input>
72+
<umb-input-with-alias
73+
id="name"
74+
label="name"
75+
value=${this._name}
76+
alias=${this._alias}
77+
?auto-generate-alias=${this._isNew}
78+
@change="${this.#onNameAndAliasChange}"
79+
${umbFocus()}></umb-input-with-alias>
11980
</div>
12081
</umb-workspace-editor>
12182
`;
@@ -136,18 +97,6 @@ export class UmbDocumentTypeWorkspaceEditorElement extends UmbLitElement {
13697
13798
#name {
13899
width: 100%;
139-
flex: 1 1 auto;
140-
align-items: center;
141-
}
142-
143-
#alias-lock {
144-
display: flex;
145-
align-items: center;
146-
justify-content: center;
147-
cursor: pointer;
148-
}
149-
#alias-lock uui-icon {
150-
margin-bottom: 2px;
151100
}
152101
153102
#icon {

src/packages/documents/document-types/workspace/document-type-workspace.context.ts

Lines changed: 12 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -287,18 +287,18 @@ export class UmbDocumentTypeWorkspaceContext
287287
this.setDefaultTemplate(templateEntity);
288288
}
289289

290-
if ((await this.structure.create(parent.unique)) === true) {
291-
// TODO: this might not be the right place to alert the tree, but it works for now
292-
const eventContext = await this.getContext(UMB_ACTION_EVENT_CONTEXT);
293-
const event = new UmbRequestReloadTreeItemChildrenEvent({
294-
entityType: parent.entityType,
295-
unique: parent.unique,
296-
});
297-
eventContext.dispatchEvent(event);
298-
299-
this.setIsNew(false);
300-
this.createTemplateMode = false;
301-
}
290+
await this.structure.create(parent.unique);
291+
292+
// TODO: this might not be the right place to alert the tree, but it works for now
293+
const eventContext = await this.getContext(UMB_ACTION_EVENT_CONTEXT);
294+
const event = new UmbRequestReloadTreeItemChildrenEvent({
295+
entityType: parent.entityType,
296+
unique: parent.unique,
297+
});
298+
eventContext.dispatchEvent(event);
299+
300+
this.setIsNew(false);
301+
this.createTemplateMode = false;
302302
} else {
303303
await this.structure.save();
304304

0 commit comments

Comments
 (0)