refactor pickers

This commit is contained in:
Niels Lyngsø
2022-12-13 15:43:41 +01:00
parent 91a4d0a859
commit 78b6903aad
22 changed files with 67 additions and 61 deletions

View File

@@ -1,21 +1,18 @@
import { html, LitElement } from 'lit';
import { property } from 'lit/decorators.js';
import { UUIModalSidebarSize } from '@umbraco-ui/uui-modal-sidebar';
// import { UmbModalService, UmbModalType } from '../../../core/services/modal';
import { UmbPickerData } from '../../../core/services/modal/layouts/modal-layout-picker-base';
import { UmbContextConsumerMixin } from '@umbraco-cms/context-api';
//TODO: These should probably be imported dynamically.
import './picker-layout-section.element';
import './picker-layout-user-group.element';
import './picker-layout-user.element';
import '../input-section/picker-layout-section.element';
import '../input-user-group/picker-layout-user-group.element';
import '../input-user/picker-layout-user.element';
import { UmbModalService, UmbModalType } from '@umbraco-cms/services';
export interface UmbPickerData {
multiple: boolean;
selection: Array<string>;
}
/** TODO: Make use of UUI FORM Mixin, to make it easily take part of a form. */
export class UmbInputListBase extends UmbContextConsumerMixin(LitElement) {
export class UmbPicker extends UmbContextConsumerMixin(LitElement) {
@property({ type: Array })
public value: Array<string> = [];
@@ -23,10 +20,10 @@ export class UmbPicker extends UmbContextConsumerMixin(LitElement) {
public multiple = true;
@property({ type: String })
public type: UmbModalType = 'sidebar';
public modalType: UmbModalType = 'sidebar';
@property({ type: String })
public size: UUIModalSidebarSize = 'small';
public modalSize: UUIModalSidebarSize = 'small';
protected pickerLayout?: string;
private _modalService?: UmbModalService;
@@ -42,14 +39,14 @@ export class UmbPicker extends UmbContextConsumerMixin(LitElement) {
if (!this.pickerLayout) return;
const modalHandler = this._modalService?.open(this.pickerLayout, {
type: this.type,
size: this.size,
type: this.modalType,
size: this.modalSize,
data: {
multiple: this.multiple,
selection: this.value,
},
});
modalHandler?.onClose().then((data: UmbPickerData) => {
modalHandler?.onClose().then((data: UmbPickerData<string>) => {
if (data) {
this.value = data.selection;
this.selectionUpdated();

View File

@@ -1,12 +1,12 @@
import { UUITextStyles } from '@umbraco-ui/uui-css';
import { css, html, nothing } from 'lit';
import { customElement, state } from 'lit/decorators.js';
import { UmbPicker } from './picker';
import { UmbInputListBase } from '../input-list-base/input-list-base';
import type { ManifestSection } from '@umbraco-cms/models';
import { umbExtensionsRegistry } from '@umbraco-cms/extensions-registry';
@customElement('umb-picker-section')
export class UmbPickerSectionElement extends UmbPicker {
@customElement('umb-input-section')
export class UmbInputPickerSectionElement extends UmbInputListBase {
static styles = [
UUITextStyles,
css`
@@ -85,6 +85,6 @@ export class UmbPickerSectionElement extends UmbPicker {
declare global {
interface HTMLElementTagNameMap {
'umb-picker-section': UmbPickerSectionElement;
'umb-input-section': UmbInputPickerSectionElement;
}
}

View File

@@ -6,7 +6,7 @@ import { expect, fixture, html } from '@open-wc/testing';
// describe('UmbPickerSectionElement', () => {
// let element: UmbPickerSectionElement;
// beforeEach(async () => {
// element = await fixture(html`<umb-picker-section></umb-picker-section>`);
// element = await fixture(html`<umb-input-section></umb-input-section>`);
// });
// it('is defined with its own instance', () => {

View File

@@ -1,14 +1,14 @@
import { UUITextStyles } from '@umbraco-ui/uui-css';
import { css, html } from 'lit';
import { customElement, state } from 'lit/decorators.js';
import { UmbPickerLayout } from './picker-layout';
import { UmbPickerLayoutBase } from '../../../core/services/modal/layouts/modal-layout-picker-base';
import { UmbObserverMixin } from '@umbraco-cms/observable-api';
import { UmbContextConsumerMixin } from '@umbraco-cms/context-api';
import { umbExtensionsRegistry } from '@umbraco-cms/extensions-registry';
import type { ManifestSection } from '@umbraco-cms/models';
@customElement('umb-picker-layout-section')
export class UmbPickerLayoutSectionElement extends UmbContextConsumerMixin(UmbObserverMixin(UmbPickerLayout)) {
export class UmbPickerLayoutSectionElement extends UmbContextConsumerMixin(UmbObserverMixin(UmbPickerLayoutBase)) {
static styles = [
UUITextStyles,
css`

View File

@@ -1,13 +1,13 @@
import { UUITextStyles } from '@umbraco-ui/uui-css';
import { css, html, nothing } from 'lit';
import { customElement, state } from 'lit/decorators.js';
import { UmbPicker } from './picker';
import { UmbInputListBase } from '../input-list-base';
import type { UserGroupEntity } from '@umbraco-cms/models';
import { UmbObserverMixin } from '@umbraco-cms/observable-api';
import { UmbUserGroupStore } from '@umbraco-cms/stores/user/user-group.store';
@customElement('umb-picker-user-group')
export class UmbPickerUserGroupElement extends UmbObserverMixin(UmbPicker) {
@customElement('umb-input-user-group')
export class UmbInputPickerUserGroupElement extends UmbObserverMixin(UmbInputListBase) {
static styles = [
UUITextStyles,
css`
@@ -95,6 +95,6 @@ export class UmbPickerUserGroupElement extends UmbObserverMixin(UmbPicker) {
declare global {
interface HTMLElementTagNameMap {
'umb-picker-user-group': UmbPickerUserGroupElement;
'umb-input-user-group': UmbInputPickerUserGroupElement;
}
}

View File

@@ -6,7 +6,7 @@ import { expect, fixture, html } from '@open-wc/testing';
// describe('UmbPickerLayoutUserGroupElement', () => {
// let element: UmbPickerUserGroupElement;
// beforeEach(async () => {
// element = await fixture(html`<umb-picker-user-group></umb-picker-user-group>`);
// element = await fixture(html`<umb-input-user-group></umb-input-user-group>`);
// });
// it('is defined with its own instance', () => {

View File

@@ -1,14 +1,14 @@
import { UUITextStyles } from '@umbraco-ui/uui-css';
import { css, html } from 'lit';
import { customElement, state } from 'lit/decorators.js';
import { UmbPickerLayout } from './picker-layout';
import { UmbPickerLayoutBase } from '../../../core/services/modal/layouts/modal-layout-picker-base';
import { UmbContextConsumerMixin } from '@umbraco-cms/context-api';
import { UmbObserverMixin } from '@umbraco-cms/observable-api';
import type { UserGroupDetails } from '@umbraco-cms/models';
import { UmbUserGroupStore } from '@umbraco-cms/stores/user/user-group.store';
@customElement('umb-picker-layout-user-group')
export class UmbPickerLayoutUserGroupElement extends UmbContextConsumerMixin(UmbObserverMixin(UmbPickerLayout)) {
export class UmbPickerLayoutUserGroupElement extends UmbContextConsumerMixin(UmbObserverMixin(UmbPickerLayoutBase)) {
static styles = [
UUITextStyles,
css`

View File

@@ -1,13 +1,13 @@
import { UUITextStyles } from '@umbraco-ui/uui-css';
import { css, html, nothing, PropertyValueMap } from 'lit';
import { customElement, state } from 'lit/decorators.js';
import { UmbPicker } from './picker';
import { UmbInputListBase } from '../input-list-base';
import { UmbObserverMixin } from '@umbraco-cms/observable-api';
import type { UserEntity } from '@umbraco-cms/models';
import { UmbUserStore } from '@umbraco-cms/stores/user/user.store';
@customElement('umb-picker-user')
export class UmbPickerUserElement extends UmbObserverMixin(UmbPicker) {
@customElement('umb-input-user')
export class UmbPickerUserElement extends UmbObserverMixin(UmbInputListBase) {
static styles = [
UUITextStyles,
css`
@@ -89,6 +89,6 @@ export class UmbPickerUserElement extends UmbObserverMixin(UmbPicker) {
declare global {
interface HTMLElementTagNameMap {
'umb-picker-user': UmbPickerUserElement;
'umb-input-user': UmbPickerUserElement;
}
}

View File

@@ -6,7 +6,7 @@ import { expect, fixture, html } from '@open-wc/testing';
// describe('UmbPickerUserElement', () => {
// let element: UmbPickerUserElement;
// beforeEach(async () => {
// element = await fixture(html`<umb-picker-user></umb-picker-user>`);
// element = await fixture(html`<umb-input-user></umb-input-user>`);
// });
// it('is defined with its own instance', () => {

View File

@@ -1,14 +1,14 @@
import { UUITextStyles } from '@umbraco-ui/uui-css';
import { css, html } from 'lit';
import { customElement, state } from 'lit/decorators.js';
import { UmbPickerLayout } from './picker-layout';
import { UmbPickerLayoutBase } from '../../../core/services/modal/layouts/modal-layout-picker-base';
import type { UserDetails } from '@umbraco-cms/models';
import { UmbContextConsumerMixin } from '@umbraco-cms/context-api';
import { UmbObserverMixin } from '@umbraco-cms/observable-api';
import { UmbUserStore } from '@umbraco-cms/stores/user/user.store';
@customElement('umb-picker-layout-user')
export class UmbPickerLayoutUserElement extends UmbContextConsumerMixin(UmbObserverMixin(UmbPickerLayout)) {
export class UmbPickerLayoutUserElement extends UmbContextConsumerMixin(UmbObserverMixin(UmbPickerLayoutBase)) {
static styles = [
UUITextStyles,
css`

View File

@@ -5,8 +5,8 @@ import { customElement, property, state } from 'lit/decorators.js';
import { repeat } from 'lit/directives/repeat.js';
import { UmbUserGroupContext } from './user-group.context';
import { UmbObserverMixin } from '@umbraco-cms/observable-api';
import '@umbraco-cms/sections/users/picker-user.element';
import '@umbraco-cms/sections/users/picker-section.element';
import '@umbraco-cms/components/input-user/input-user.element';
import '@umbraco-cms/components/input-section/input-section.element';
import { UmbContextConsumerMixin, UmbContextProviderMixin } from '@umbraco-cms/context-api';
import type { ManifestEditorAction, ManifestWithLoader, UserDetails, UserGroupDetails } from '@umbraco-cms/models';
import { umbExtensionsRegistry } from '@umbraco-cms/extensions-registry';
@@ -299,10 +299,10 @@ export class UmbEditorUserGroupElement extends UmbContextProviderMixin(
return html` <uui-box>
<div slot="headline">Assign access</div>
<umb-editor-property-layout label="Sections" description="Add sections to give users access">
<umb-picker-section
<umb-input-section
slot="editor"
.value=${this._userGroup.sections}
@change=${(e: any) => this._updateProperty('sections', e.target.value)}></umb-picker-section>
@change=${(e: any) => this._updateProperty('sections', e.target.value)}></umb-input-section>
</umb-editor-property-layout>
<umb-editor-property-layout
label="Content start node"
@@ -360,9 +360,9 @@ export class UmbEditorUserGroupElement extends UmbContextProviderMixin(
private renderRightColumn() {
return html`<uui-box>
<div slot="headline">Users</div>
<umb-picker-user
<umb-input-user
@change=${(e: Event) => this._updateUserKeys((e.target as any).value)}
.value=${this._userKeys || []}></umb-picker-user>
.value=${this._userKeys || []}></umb-input-user>
</uui-box>`;
}

View File

@@ -5,17 +5,17 @@ import { customElement, property, state } from 'lit/decorators.js';
import { ifDefined } from 'lit/directives/if-defined.js';
import { repeat } from 'lit/directives/repeat.js';
import { UmbUserStore } from '@umbraco-cms/stores/user';
import { getTagLookAndColor } from '../../sections/users/user-extensions';
import { UmbUserContext } from './user.context';
import { UmbUserStore } from '@umbraco-cms/stores/user';
import { UmbContextProviderMixin, UmbContextConsumerMixin } from '@umbraco-cms/context-api';
import { umbExtensionsRegistry } from '@umbraco-cms/extensions-registry';
import type { ManifestEditorAction, ManifestWithLoader, UserDetails } from '@umbraco-cms/models';
import { UmbObserverMixin } from '@umbraco-cms/observable-api';
import '../../property-editor-uis/content-picker/property-editor-ui-content-picker.element';
import '@umbraco-cms/sections/users/picker-user-group.element';
import '@umbraco-cms/components/input-user-group/input-user-group.element';
@customElement('umb-editor-user')
@@ -226,10 +226,10 @@ export class UmbEditorUserElement extends UmbContextProviderMixin(
<div slot="headline">Assign access</div>
<div id="assign-access">
<umb-editor-property-layout label="Groups" description="Add groups to assign access and permissions">
<umb-picker-user-group
<umb-input-user-group
slot="editor"
.value=${this._user.userGroups}
@change=${(e: any) => this._updateProperty('userGroups', e.target.value)}></umb-picker-user-group>
@change=${(e: any) => this._updateProperty('userGroups', e.target.value)}></umb-input-user-group>
</umb-editor-property-layout>
<umb-editor-property-layout
label="Content start node"

View File

@@ -2,7 +2,7 @@ import { css, html, nothing } from 'lit';
import { UUITextStyles } from '@umbraco-ui/uui-css/lib';
import { customElement, query, state } from 'lit/decorators.js';
import { UUIInputPasswordElement } from '@umbraco-ui/uui';
import { UmbPickerUserGroupElement } from '../../picker-user-group.element';
import { UmbInputPickerUserGroupElement } from '@umbraco-cms/components/input-user-group/input-user-group.element';
import { UmbContextConsumerMixin } from '@umbraco-cms/context-api';
import type { UserDetails } from '@umbraco-cms/models';
import { UmbModalLayoutElement, UmbNotificationDefaultData, UmbNotificationService } from '@umbraco-cms/services';
@@ -84,7 +84,7 @@ export class UmbEditorViewUsersCreateElement extends UmbContextConsumerMixin(Umb
const name = formData.get('name') as string;
const email = formData.get('email') as string;
//TODO: How should we handle pickers forms?
const userGroupPicker = form.querySelector('#userGroups') as UmbPickerUserGroupElement;
const userGroupPicker = form.querySelector('#userGroups') as UmbInputPickerUserGroupElement;
const userGroups = userGroupPicker?.value || [];
this._userStore?.invite(name, email, '', userGroups).then((user) => {
@@ -141,7 +141,7 @@ export class UmbEditorViewUsersCreateElement extends UmbContextConsumerMixin(Umb
<uui-form-layout-item>
<uui-label id="userGroupsLabel" slot="label" for="userGroups" required>User group</uui-label>
<span slot="description">Add groups to assign access and permissions</span>
<umb-picker-user-group id="userGroups" name="userGroups"></umb-picker-user-group>
<umb-input-user-group id="userGroups" name="userGroups"></umb-input-user-group>
</uui-form-layout-item>
</form>
</uui-form>`;

View File

@@ -1,7 +1,7 @@
import { css, html, nothing } from 'lit';
import { UUITextStyles } from '@umbraco-ui/uui-css/lib';
import { customElement, query, state } from 'lit/decorators.js';
import { UmbPickerUserGroupElement } from '../../picker-user-group.element';
import { UmbInputPickerUserGroupElement } from '@umbraco-cms/components/input-user-group/input-user-group.element';
import { UmbContextConsumerMixin } from '@umbraco-cms/context-api';
import type { UserDetails } from '@umbraco-cms/models';
import { UmbModalLayoutElement } from '@umbraco-cms/services';
@@ -78,7 +78,7 @@ export class UmbEditorViewUsersInviteElement extends UmbContextConsumerMixin(Umb
const name = formData.get('name') as string;
const email = formData.get('email') as string;
//TODO: How should we handle pickers forms?
const userGroupPicker = form.querySelector('#userGroups') as UmbPickerUserGroupElement;
const userGroupPicker = form.querySelector('#userGroups') as UmbInputPickerUserGroupElement;
const userGroups = userGroupPicker?.value || [];
const message = formData.get('message') as string;
@@ -128,7 +128,7 @@ export class UmbEditorViewUsersInviteElement extends UmbContextConsumerMixin(Umb
<uui-form-layout-item>
<uui-label id="userGroupsLabel" slot="label" for="userGroups" required>User group</uui-label>
<span slot="description">Add groups to assign access and permissions</span>
<umb-picker-user-group id="userGroups" name="userGroups"></umb-picker-user-group>
<umb-input-user-group id="userGroups" name="userGroups"></umb-input-user-group>
</uui-form-layout-item>
<uui-form-layout-item>
<uui-label id="messageLabel" slot="label" for="message" required>Message</uui-label>

View File

@@ -11,6 +11,7 @@ export interface UmbModalContentPickerData {
import '../../../../../backoffice/trees/documents/tree-documents.element';
import { UmbTreeElement } from '../../../../../backoffice/trees/shared/tree.element';
// TODO: make use of UmbPickerLayoutBase
@customElement('umb-modal-layout-content-picker')
export class UmbModalLayoutContentPickerElement extends UmbModalLayoutElement<UmbModalContentPickerData> {
static styles = [

View File

@@ -8,6 +8,7 @@ export interface UmbModalIconPickerData {
selection: string[];
}
// TODO: Make use of UmbPickerLayoutBase
@customElement('umb-modal-layout-icon-picker')
export class UmbModalLayoutIconPickerElement extends UmbModalLayoutElement<UmbModalIconPickerData> {
static styles = [

View File

@@ -1,10 +1,15 @@
import { state } from 'lit/decorators.js';
import { UmbPickerData } from './picker';
import { UmbModalLayoutElement } from '@umbraco-cms/services';
export class UmbPickerLayout extends UmbModalLayoutElement<UmbPickerData> {
export interface UmbPickerData<selectType = string> {
multiple: boolean;
selection: Array<selectType>;
}
export class UmbPickerLayoutBase<selectType> extends UmbModalLayoutElement<UmbPickerData<selectType>> {
@state()
private _selection: Array<string> = [];
private _selection: Array<selectType> = [];
connectedCallback(): void {
super.connectedCallback();
@@ -19,27 +24,27 @@ export class UmbPickerLayout extends UmbModalLayoutElement<UmbPickerData> {
this.modalHandler?.close();
}
protected _handleKeydown(e: KeyboardEvent, key: string) {
protected _handleKeydown(e: KeyboardEvent, key: selectType) {
if (e.key === 'Enter') {
this._handleItemClick(key);
}
}
protected _handleItemClick(clickedKey: string) {
protected _handleItemClick(key: selectType) {
if (this.data?.multiple) {
if (this._isSelected(clickedKey)) {
this._selection = this._selection.filter((key) => key !== clickedKey);
if (this._isSelected(key)) {
this._selection = this._selection.filter((key) => key !== key);
} else {
this._selection.push(clickedKey);
this._selection.push(key);
}
} else {
this._selection = [clickedKey];
this._selection = [key];
}
this.requestUpdate('_selection');
}
protected _isSelected(key: string): boolean {
protected _isSelected(key: selectType): boolean {
return this._selection.includes(key);
}
}

View File

@@ -19,6 +19,7 @@ interface GroupedPropertyEditorUIs {
[key: string]: Array<ManifestPropertyEditorUI>;
}
// TODO: make use of UmbPickerLayoutBase
@customElement('umb-modal-layout-property-editor-ui-picker')
export class UmbModalLayoutPropertyEditorUIPickerElement extends UmbContextConsumerMixin(UmbObserverMixin(LitElement)) {
static styles = [

View File

@@ -22,6 +22,7 @@ export class UmbModalHandler {
this.size = options?.size || 'small';
this.element = this._createElement(element, options);
// TODO: Consider if its right to use Promises, or use another event based system? Would we need to be able to cancel an event, to then prevent the closing..?
this._closePromise = new Promise((resolve) => {
this._closeResolver = resolve;
});