This commit is contained in:
Jesper Møller Jensen
2023-05-15 17:37:14 +12:00
parent 7bd7192ed1
commit eb75ba5ea8
6 changed files with 168 additions and 129 deletions

View File

@@ -1 +1 @@
import './input-user-group/input-user-group.element';
import './input-user-group/user-group-input.element';

View File

@@ -1,99 +0,0 @@
import { UUITextStyles } from '@umbraco-ui/uui-css';
import { css, html, nothing } from 'lit';
import { customElement, state } from 'lit/decorators.js';
import { UmbInputListBaseElement } from '../../../../core/components/input-list-base/input-list-base';
import { UmbUserGroupStore, UMB_USER_GROUP_STORE_CONTEXT_TOKEN } from '../../repository/user-group.store';
import type { UserGroupEntity } from '../../types';
import { UMB_USER_GROUP_PICKER_MODAL } from '@umbraco-cms/backoffice/modal';
@customElement('umb-input-user-group')
export class UmbInputPickerUserGroupElement extends UmbInputListBaseElement {
@state()
private _userGroups: Array<UserGroupEntity> = [];
private _userGroupStore?: UmbUserGroupStore;
connectedCallback(): void {
super.connectedCallback();
this.pickerToken = UMB_USER_GROUP_PICKER_MODAL;
this.consumeContext(UMB_USER_GROUP_STORE_CONTEXT_TOKEN, (usersContext) => {
this._userGroupStore = usersContext;
//this._observeUserGroups();
});
}
/*
private _observeUserGroups() {
if (this.value.length > 0 && this._userGroupStore) {
this.observe(this._userGroupStore.getByKeys(this.value), (userGroups) => (this._userGroups = userGroups));
} else {
this._userGroups = [];
}
}
*/
selectionUpdated() {
//this._observeUserGroups();
this.dispatchEvent(new CustomEvent('change', { bubbles: true, composed: true }));
}
private _renderUserGroupList() {
if (this._userGroups.length === 0) return nothing;
return html`<div id="user-list">
${this._userGroups.map(
(userGroup) => html`
<div class="user-group">
<div>
<uui-icon .name=${userGroup.icon}></uui-icon>
<span>${userGroup.name}</span>
</div>
<uui-button
@click=${() => this.removeFromSelection(userGroup.id)}
label="remove"
color="danger"></uui-button>
</div>
`
)}
</div> `;
}
renderContent() {
return html`${this._renderUserGroupList()}`;
}
static styles = [
UUITextStyles,
css`
:host {
display: flex;
flex-direction: column;
gap: var(--uui-size-space-4);
}
#user-group-list {
display: flex;
flex-direction: column;
gap: var(--uui-size-space-4);
}
.user-group {
display: flex;
align-items: center;
gap: var(--uui-size-space-2);
}
.user-group div {
display: flex;
align-items: center;
gap: var(--uui-size-4);
}
.user-group uui-button {
margin-left: auto;
}
`,
];
}
declare global {
interface HTMLElementTagNameMap {
'umb-input-user-group': UmbInputPickerUserGroupElement;
}
}

View File

@@ -1,29 +0,0 @@
import { Meta, StoryObj } from '@storybook/web-components';
import './input-user-group.element';
import type { UmbInputPickerUserGroupElement } from './input-user-group.element';
const meta: Meta<UmbInputPickerUserGroupElement> = {
title: 'Components/Inputs/User Group',
component: 'umb-user-group-input',
argTypes: {
modalType: {
control: 'inline-radio',
options: ['dialog', 'sidebar'],
defaultValue: 'sidebar',
description: 'The type of modal to use when selecting user groups',
},
modalSize: {
control: 'select',
options: ['small', 'medium', 'large', 'full'],
defaultValue: 'small',
description: 'The size of the modal to use when selecting user groups, only applicable to sidebar not dialog',
},
},
};
export default meta;
type Story = StoryObj<UmbInputPickerUserGroupElement>;
export const Overview: Story = {
args: {},
};

View File

@@ -0,0 +1,138 @@
import { css, html } from 'lit';
import { UUITextStyles } from '@umbraco-ui/uui-css/lib';
import { customElement, property, state } from 'lit/decorators.js';
import { FormControlMixin } from '@umbraco-ui/uui-base/lib/mixins';
import { UmbUserGroupPickerContext } from './user-group-input.context';
import { UmbLitElement } from '@umbraco-cms/internal/lit-element';
import type { UserItemResponseModel } from '@umbraco-cms/backoffice/backend-api';
@customElement('umb-user-group-input')
export class UmbUserGroupInputElement extends FormControlMixin(UmbLitElement) {
/**
* This is a minimum amount of selected items in this input.
* @type {number}
* @attr
* @default 0
*/
@property({ type: Number })
public get min(): number {
return this.#pickerContext.min;
}
public set min(value: number) {
this.#pickerContext.min = value;
}
/**
* Min validation message.
* @type {boolean}
* @attr
* @default
*/
@property({ type: String, attribute: 'min-message' })
minMessage = 'This field need more items';
/**
* This is a maximum amount of selected items in this input.
* @type {number}
* @attr
* @default Infinity
*/
@property({ type: Number })
public get max(): number {
return this.#pickerContext.max;
}
public set max(value: number) {
this.#pickerContext.max = value;
}
/**
* Max validation message.
* @type {boolean}
* @attr
* @default
*/
@property({ type: String, attribute: 'min-message' })
maxMessage = 'This field exceeds the allowed amount of items';
public get selectedIds(): Array<string> {
return this.#pickerContext.getSelection();
}
public set selectedIds(ids: Array<string>) {
this.#pickerContext.setSelection(ids);
}
@property()
public set value(idsString: string) {
console.log('set value', idsString);
// Its with full purpose we don't call super.value, as thats being handled by the observation of the context selection.
this.selectedIds = idsString.split(/[ ,]+/);
}
@state()
private _items?: Array<UserItemResponseModel>;
#pickerContext = new UmbUserGroupPickerContext(this);
constructor() {
super();
this.addValidator(
'rangeUnderflow',
() => this.minMessage,
() => !!this.min && this.#pickerContext.getSelection().length < this.min
);
this.addValidator(
'rangeOverflow',
() => this.maxMessage,
() => !!this.max && this.#pickerContext.getSelection().length > this.max
);
this.observe(this.#pickerContext.selection, (selection) => (super.value = selection.join(',')));
this.observe(this.#pickerContext.selectedItems, (selectedItems) => (this._items = selectedItems));
}
protected getFormElement() {
return undefined;
}
render() {
return html`
<uui-ref-list>${this._items?.map((item) => this._renderItem(item))}</uui-ref-list>
<uui-button id="add-button" look="placeholder" @click=${() => this.#pickerContext.openPicker()} label="open"
>Add</uui-button
>
`;
}
private _renderItem(item: UserItemResponseModel) {
if (!item.id) return;
return html`
<uui-ref-node-user name=${item.name}>
<uui-action-bar slot="actions">
<uui-button @click=${() => this.#pickerContext.requestRemoveItem(item.id!)} label="Remove ${item.name}"
>Remove</uui-button
>
</uui-action-bar>
</uui-ref-node-user>
`;
}
static styles = [
UUITextStyles,
css`
#add-button {
width: 100%;
}
`,
];
}
export default UmbUserGroupInputElement;
declare global {
interface HTMLElementTagNameMap {
'umb-user-group-input': UmbUserGroupInputElement;
}
}

View File

@@ -0,0 +1,29 @@
import { Meta, StoryObj } from '@storybook/web-components';
import './input-user-group.element';
import type { UmbUserGroupInputElement } from './user-group-input.element';
const meta: Meta<UmbUserGroupInputElement> = {
title: 'Components/Inputs/User Group',
component: 'umb-input-user-group',
argTypes: {
// modalType: {
// control: 'inline-radio',
// options: ['dialog', 'sidebar'],
// defaultValue: 'sidebar',
// description: 'The type of modal to use when selecting user groups',
// },
// modalSize: {
// control: 'select',
// options: ['small', 'medium', 'large', 'full'],
// defaultValue: 'small',
// description: 'The size of the modal to use when selecting user groups, only applicable to sidebar not dialog',
// },
},
};
export default meta;
type Story = StoryObj<UmbUserGroupInputElement>;
export const Overview: Story = {
args: {},
};