split data-type-picker from property-editor-ui-p..

This commit is contained in:
Niels Lyngsø
2023-05-18 22:03:08 +02:00
parent 0e8966bc57
commit 449ffbdf0a
16 changed files with 415 additions and 38 deletions

View File

@@ -0,0 +1,18 @@
import { UmbModalToken } from '@umbraco-cms/backoffice/modal';
export interface UmbDataTypePickerFlowModalData {
selection?: Array<string>;
submitLabel?: string;
}
export type UmbDataTypePickerFlowModalResult = {
selection: Array<string>;
};
export const UMB_DATA_TYPE_PICKER_FLOW_MODAL = new UmbModalToken<
UmbDataTypePickerFlowModalData,
UmbDataTypePickerFlowModalResult
>('Umb.Modal.DataTypePickerFlow', {
type: 'sidebar',
size: 'small',
});

View File

@@ -29,3 +29,4 @@ export * from './folder-modal.token';
export * from './partial-view-picker-modal.token';
export * from './dictionary-item-picker-modal.token';
export * from './data-type-picker-modal.token';
export * from './data-type-picker-flow-modal.token';

View File

@@ -1,5 +1,3 @@
// Temp file for data type types
import { DataTypeResponseModel } from '@umbraco-cms/backoffice/backend-api';
export interface UmbDataTypeModel extends Omit<DataTypeResponseModel, '$type'> {

View File

@@ -1,3 +1,5 @@
export type * from './data-type-model';
/** Tried to find a common base of our entities — used by Entity Workspace Context */
export type UmbEntityBase = {
id?: string;

View File

@@ -35,7 +35,6 @@ import './date-input/date-input.element';
import './input-color-picker/input-color-picker.element';
import './input-eye-dropper/input-eye-dropper.element';
import './input-multi-url/input-multi-url.element';
import './input-data-type/input-data-type.element';
import './input-slider/input-slider.element';
import './input-toggle/input-toggle.element';
import './input-upload-field/input-upload-field.element';

View File

@@ -0,0 +1,73 @@
import { UUIRefNodeElement } from '@umbraco-ui/uui';
import { html } from 'lit';
import { customElement, property } from 'lit/decorators.js';
/**
* @element umb-ref-data-type
* @description - Component for displaying a reference to a Data Type
* @extends UUIRefNodeElement
*/
@customElement('umb-ref-data-type')
export class UmbRefDataTypeElement extends UUIRefNodeElement {
protected fallbackIcon =
'<svg xmlns="https://www.w3.org/2000/svg" viewBox="0 0 512 512"><path d="M142.212 397.267l106.052-48.024L398.479 199.03l-26.405-26.442-90.519 90.517-15.843-15.891 90.484-90.486-16.204-16.217-150.246 150.243-47.534 106.513zm74.904-100.739l23.285-23.283 3.353 22.221 22.008 3.124-23.283 23.313-46.176 20.991 20.813-46.366zm257.6-173.71L416.188 64.3l-49.755 49.785 58.504 58.503 49.779-49.77zM357.357 300.227h82.826v116.445H68.929V300.227h88.719v-30.648H38.288v177.733h432.537V269.578H357.357v30.649z"></path></svg>';
/**
* Name
* @type {string}
* @attr
* @default ''
*/
@property({ type: String })
name = '';
/**
* Property Editor UI Alias
* @type {string}
* @attr
* @default ''
*/
@property({ type: String, attribute: 'property-editor-ui-alias' })
propertyEditorUiAlias = '';
/**
* Property Editor Model Alias
* @type {string}
* @attr
* @default ''
*/
@property({ type: String, attribute: 'property-editor-model-alias' })
propertyEditorModelAlias = '';
protected renderDetail() {
const details: string[] = [];
if (this.name !== '') {
details.push(this.name);
}
if (this.propertyEditorUiAlias !== '') {
details.push(this.propertyEditorUiAlias);
} else {
details.push('Data Type Property Editor UI Missing');
}
if (this.propertyEditorModelAlias !== '') {
details.push(this.propertyEditorModelAlias);
} else {
details.push('Data Type Property Editor Model Missing');
}
if (this.detail !== '') {
details.push(this.detail);
}
return html`<small id="detail">${details.join(' | ')}<slot name="detail"></slot></small>`;
}
static styles = [...UUIRefNodeElement.styles];
}
declare global {
interface HTMLElementTagNameMap {
'umb-ref-data-type': UmbRefDataTypeElement;
}
}

View File

@@ -0,0 +1,54 @@
import { Meta, StoryObj } from '@storybook/web-components';
import { html } from 'lit';
import './ref-data-type.element';
import type { UmbRefDataTypeElement } from './ref-data-type.element';
const meta: Meta<UmbRefDataTypeElement> = {
title: 'Components/Ref Data Type',
component: 'umb-ref-property-editor-ui',
};
export default meta;
type Story = StoryObj<UmbRefDataTypeElement>;
export const Overview: Story = {
args: {
name: 'Custom Data Type',
name: 'Umb.DataType.CustomUI',
dataTypeKey: 'Umbraco.JSON',
},
};
export const WithDetail: Story = {
args: {
name: 'Custom Data Type',
name: 'Umb.DataType.CustomUI',
dataTypeKey: 'Umbraco.JSON',
detail: 'With some custom details',
},
};
export const WithSlots: Story = {
args: {
name: 'Custom Data Type',
name: 'Umb.DataType.CustomUI',
dataTypeKey: 'Umbraco.JSON',
detail: 'With some custom details',
},
render: (args) => html`
<umb-ref-data-type
.name=${args.name}
.propertyEditorUiAlias=${args.name}
.propertyEditorModelAlias=${args.dataTypeKey}
.detail=${args.detail}>
<div slot="tag"><uui-tag color="positive">10</uui-tag></div>
<div slot="actions">
<uui-action-bar>
<uui-button label="delete" look="primary" color="danger" compact>
<uui-icon name="umb:delete"></uui-icon>
</uui-button>
</uui-action-bar>
</div>
</umb-ref-data-type>
`,
};

View File

@@ -9,8 +9,6 @@ import { customElement, property } from 'lit/decorators.js';
*/
@customElement('umb-ref-property-editor-ui')
export class UmbRefPropertyEditorUIElement extends UUIRefNodeElement {
protected fallbackIcon =
'<svg xmlns="https://www.w3.org/2000/svg" viewBox="0 0 512 512"><path d="M142.212 397.267l106.052-48.024L398.479 199.03l-26.405-26.442-90.519 90.517-15.843-15.891 90.484-90.486-16.204-16.217-150.246 150.243-47.534 106.513zm74.904-100.739l23.285-23.283 3.353 22.221 22.008 3.124-23.283 23.313-46.176 20.991 20.813-46.366zm257.6-173.71L416.188 64.3l-49.755 49.785 58.504 58.503 49.779-49.77zM357.357 300.227h82.826v116.445H68.929V300.227h88.719v-30.648H38.288v177.733h432.537V269.578H357.357v30.649z"></path></svg>';
@@ -30,7 +28,7 @@ export class UmbRefPropertyEditorUIElement extends UUIRefNodeElement {
* @default ''
*/
@property({ type: String, attribute: 'property-editor-model-alias' })
propertyEditorAlias = '';
propertyEditorModelAlias = '';
protected renderDetail() {
const details: string[] = [];
@@ -39,8 +37,8 @@ export class UmbRefPropertyEditorUIElement extends UUIRefNodeElement {
details.push(this.alias);
}
if (this.propertyEditorAlias !== '') {
details.push(this.propertyEditorAlias);
if (this.propertyEditorModelAlias !== '') {
details.push(this.propertyEditorModelAlias);
} else {
details.push('Property Editor Missing');
}
@@ -50,7 +48,7 @@ export class UmbRefPropertyEditorUIElement extends UUIRefNodeElement {
}
return html`<small id="detail">${details.join(' | ')}<slot name="detail"></slot></small>`;
}
static styles = [...UUIRefNodeElement.styles];
}

View File

@@ -15,7 +15,7 @@ export const Overview: Story = {
args: {
name: 'Custom Property Editor UI',
alias: 'Umb.PropertyEditorUI.CustomUI',
propertyEditorAlias: 'Umbraco.JSON',
propertyEditorModelAlias: 'Umbraco.JSON',
},
};
@@ -23,7 +23,7 @@ export const WithDetail: Story = {
args: {
name: 'Custom Property Editor UI',
alias: 'Umb.PropertyEditorUI.CustomUI',
propertyEditorAlias: 'Umbraco.JSON',
propertyEditorModelAlias: 'Umbraco.JSON',
detail: 'With some custom details',
},
};
@@ -32,14 +32,14 @@ export const WithSlots: Story = {
args: {
name: 'Custom Property Editor UI',
alias: 'Umb.PropertyEditorUI.CustomUI',
propertyEditorAlias: 'Umbraco.JSON',
propertyEditorModelAlias: 'Umbraco.JSON',
detail: 'With some custom details',
},
render: (args) => html`
<umb-ref-property-editor-ui
.name=${args.name}
.alias=${args.alias}
.propertyEditorAlias=${args.propertyEditorAlias}
.propertyEditorAlias=${args.propertyEditorModelAlias}
.detail=${args.detail}>
<div slot="tag"><uui-tag color="positive">10</uui-tag></div>
<div slot="actions">

View File

@@ -209,9 +209,9 @@ export class UmbPropertySettingsModalElement extends UmbModalBaseElement<
placeholder="Enter description..."
.value=${this._returnData.description}></uui-textarea>
</div>
<umb-input-data-type
<umb-data-type-flow-input
.value=${this._returnData.dataTypeId}
@change=${this.#onDataTypeIdChange}></umb-input-data-type>
@change=${this.#onDataTypeIdChange}></umb-data-type-flow-input>
<hr />
<div class="container">
<b>Validation</b>

View File

@@ -0,0 +1,209 @@
import { css, html } from 'lit';
import { UUITextStyles } from '@umbraco-ui/uui-css/lib';
import { customElement, property, state } from 'lit/decorators.js';
import { repeat } from 'lit/directives/repeat.js';
import { groupBy } from 'lodash-es';
import type { UUIInputEvent } from '@umbraco-ui/uui';
import {
UmbPropertyEditorUIPickerModalData,
UmbPropertyEditorUIPickerModalResult,
UmbModalHandler,
} from '@umbraco-cms/backoffice/modal';
import { ManifestPropertyEditorUI, umbExtensionsRegistry } from '@umbraco-cms/backoffice/extension-registry';
import { UmbLitElement } from '@umbraco-cms/internal/lit-element';
interface GroupedPropertyEditorUIs {
[key: string]: Array<ManifestPropertyEditorUI>;
}
@customElement('umb-data-type-picker-flow-modal')
export class UmbDataTypePickerFlowModalElement extends UmbLitElement {
@property({ attribute: false })
modalHandler?: UmbModalHandler<UmbPropertyEditorUIPickerModalData, UmbPropertyEditorUIPickerModalResult>;
@property({ type: Object })
data?: UmbPropertyEditorUIPickerModalData;
@state()
private _groupedPropertyEditorUIs: GroupedPropertyEditorUIs = {};
@state()
private _selection: Array<string> = [];
@state()
private _submitLabel = 'Select';
#propertyEditorUIs: Array<ManifestPropertyEditorUI> = [];
#currentFilterQuery = '';
connectedCallback(): void {
super.connectedCallback();
this._selection = this.data?.selection ?? [];
this._submitLabel = this.data?.submitLabel ?? this._submitLabel;
this._usePropertyEditorUIs();
}
private _usePropertyEditorUIs() {
if (!this.data) return;
this.observe(umbExtensionsRegistry.extensionsOfType('propertyEditorUI'), (propertyEditorUIs) => {
// TODO: this should use same code as querying.
this.#propertyEditorUIs = propertyEditorUIs;
this._performFiltering();
});
}
private _handleClick(propertyEditorUI: ManifestPropertyEditorUI) {
this._select(propertyEditorUI.alias);
}
private _select(alias: string) {
this._selection = [alias];
}
private _handleFilterInput(event: UUIInputEvent) {
const query = (event.target.value as string) || '';
this.#currentFilterQuery = query.toLowerCase();
this._performFiltering();
}
private _performFiltering() {
const result = !this.#currentFilterQuery
? this.#propertyEditorUIs
: this.#propertyEditorUIs.filter((propertyEditorUI) => {
return (
propertyEditorUI.name.toLowerCase().includes(this.#currentFilterQuery) ||
propertyEditorUI.alias.toLowerCase().includes(this.#currentFilterQuery)
);
});
// TODO: When filtering, then we should also display the available data types, in a separate list as the UIs.
this._groupedPropertyEditorUIs = groupBy(result, 'meta.group');
}
private _close() {
this.modalHandler?.reject();
}
private _submit() {
this.modalHandler?.submit({ selection: this._selection });
}
render() {
return html`
<umb-body-layout headline="Select Property Editor">
<uui-box> ${this._renderFilter()} ${this._renderGrid()} </uui-box>
<div slot="actions">
<uui-button label="Close" @click=${this._close}></uui-button>
<uui-button label="${this._submitLabel}" look="primary" color="positive" @click=${this._submit}></uui-button>
</div>
</umb-body-layout>
`;
}
private _renderFilter() {
return html` <uui-input
id="filter"
@input="${this._handleFilterInput}"
placeholder="Type to filter..."
label="Type to filter icons">
<uui-icon name="search" slot="prepend" id="filter-icon"></uui-icon>
</uui-input>`;
}
private _renderGrid() {
return html` ${Object.entries(this._groupedPropertyEditorUIs).map(
([key, value]) =>
html` <h4>${key}</h4>
${this._renderGroupItems(value)}`
)}`;
}
private _renderGroupItems(groupItems: Array<ManifestPropertyEditorUI>) {
return html` <ul id="item-grid">
${repeat(
groupItems,
(propertyEditorUI) => propertyEditorUI.alias,
(propertyEditorUI) => html` <li class="item" ?selected=${this._selection.includes(propertyEditorUI.alias)}>
<button type="button" @click="${() => this._handleClick(propertyEditorUI)}">
<uui-icon name="${propertyEditorUI.meta.icon}" class="icon"></uui-icon>
${propertyEditorUI.meta.label || propertyEditorUI.name}
</button>
</li>`
)}
</ul>`;
}
static styles = [
UUITextStyles,
css`
#filter {
width: 100%;
margin-bottom: var(--uui-size-space-4);
}
#filter-icon {
padding-left: var(--uui-size-space-2);
}
#item-grid {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(70px, 1fr));
margin: 0;
padding: 0;
grid-gap: var(--uui-size-space-4);
}
#item-grid .item {
display: flex;
align-items: flex-start;
justify-content: center;
list-style: none;
height: 100%;
border: 1px solid transparent;
border-radius: var(--uui-border-radius);
}
#item-grid .item:hover {
background: var(--uui-color-surface-emphasis);
color: var(--uui-color-interactive-emphasis);
cursor: pointer;
}
#item-grid .item[selected] button {
background: var(--uui-color-selected);
color: var(--uui-color-selected-contrast);
}
#item-grid .item button {
background: none;
border: none;
cursor: pointer;
padding: var(--uui-size-space-3);
display: flex;
align-items: center;
flex-direction: column;
justify-content: center;
font-size: 0.8rem;
height: 100%;
width: 100%;
color: var(--uui-color-interactive);
border-radius: var(--uui-border-radius);
}
#item-grid .item .icon {
font-size: 2em;
margin-bottom: var(--uui-size-space-2);
}
`,
];
}
export default UmbDataTypePickerFlowModalElement;
declare global {
interface HTMLElementTagNameMap {
'umb-data-type-picker-flow-modal': UmbDataTypePickerFlowModalElement;
}
}

View File

@@ -0,0 +1,19 @@
import { Meta, Story } from '@storybook/web-components';
import { html } from 'lit';
import type { UmbDataTypePickerFlowModalElement } from './data-type-picker-flow-modal.element';
import type { UmbPropertyEditorUIPickerModalData } from '@umbraco-cms/backoffice/modal';
import './data-type-picker-flow-modal.element';
import '../../../components/body-layout/body-layout.element';
export default {
title: 'API/Modals/Layouts/Data Type Picker Flow',
component: 'umb-data-type-picker-flow-modal',
id: 'umb-data-type-picker-flow-modal',
} as Meta;
const data: UmbPropertyEditorUIPickerModalData = { selection: [] };
export const Overview: Story<UmbDataTypePickerFlowModalElement> = () => html`
<umb-data-type-picker-flow-modal .data=${data as any}></umb-data-type-picker-flow-modal>
`;

View File

@@ -7,6 +7,12 @@ const modals: Array<ManifestModal> = [
name: 'Property Editor UI Picker Modal',
loader: () => import('./property-editor-ui-picker/property-editor-ui-picker-modal.element'),
},
{
type: 'modal',
alias: 'Umb.Modal.DataTypePickerFlow',
name: 'Data Type Picker Flow Modal',
loader: () => import('./data-type-picker-flow/data-type-picker-flow-modal.element'),
},
];
export const manifests = [...modals];

View File

@@ -86,13 +86,13 @@ export class UmbPropertyEditorUIPickerModalElement extends UmbLitElement {
render() {
return html`
<umb-workspace-editor headline="Select Property Editor UI">
<umb-body-layout headline="Select Property Editor UI">
<uui-box> ${this._renderFilter()} ${this._renderGrid()} </uui-box>
<div slot="actions">
<uui-button label="Close" @click=${this._close}></uui-button>
<uui-button label="${this._submitLabel}" look="primary" color="positive" @click=${this._submit}></uui-button>
</div>
</umb-workspace-editor>
</umb-body-layout>
`;
}

View File

@@ -3,27 +3,25 @@ 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 { UmbLitElement } from '@umbraco-cms/internal/lit-element';
import { ManifestPropertyEditorUI, umbExtensionsRegistry } from '@umbraco-cms/backoffice/extension-registry';
import {
UmbModalRouteRegistrationController,
UMB_PROPERTY_EDITOR_UI_PICKER_MODAL,
} from '@umbraco-cms/backoffice/modal';
import { umbExtensionsRegistry } from '@umbraco-cms/backoffice/extension-registry';
import { UmbModalRouteRegistrationController, UMB_DATA_TYPE_PICKER_FLOW_MODAL } from '@umbraco-cms/backoffice/modal';
import type { UmbDataTypeModel } from '@umbraco-cms/backoffice/models';
// Note: Does only support picking a single data type. But this could be developed later into this same component. To follow other picker input components.
/**
* Form control for picking a data type.
* @element umb-input-data-type
* Form control for picking or creating a data type.
* @element umb-data-type-flow-input
* @fires change - when the value of the input changes
* @fires blur - when the input loses focus
* @fires focus - when the input gains focus
*/
@customElement('umb-input-data-type')
@customElement('umb-data-type-flow-input')
export class UmbInputDataTypeElement extends FormControlMixin(UmbLitElement) {
protected getFormElement() {
return undefined;
}
@state() private _selectedPropertyEditorUI?: ManifestPropertyEditorUI;
@state() private _selectedDataType?: UmbDataTypeModel;
/**
* @param {string} dataTypeId
@@ -41,7 +39,7 @@ export class UmbInputDataTypeElement extends FormControlMixin(UmbLitElement) {
constructor() {
super();
new UmbModalRouteRegistrationController(this, UMB_PROPERTY_EDITOR_UI_PICKER_MODAL)
new UmbModalRouteRegistrationController(this, UMB_DATA_TYPE_PICKER_FLOW_MODAL)
.onSetup(() => {
return {
selection: [this._value.toString()],
@@ -62,33 +60,34 @@ export class UmbInputDataTypeElement extends FormControlMixin(UmbLitElement) {
#observeDataTypeId() {
if (!this._value) return;
this.observe(
/*this.observe(
umbExtensionsRegistry.getByTypeAndAlias('propertyEditorUI', this._value.toString()),
(propertyEditorUI) => {
if (!propertyEditorUI) return;
this._selectedPropertyEditorUI = propertyEditorUI;
this._selectedDataType = propertyEditorUI;
},
'observePropertyEditorUI'
);
);*/
}
render() {
return this._selectedPropertyEditorUI
return this._selectedDataType
? html`
<umb-ref-property-editor-ui
name=${this._selectedPropertyEditorUI.meta.label}
alias=${this._selectedPropertyEditorUI.alias}
property-editor-model-alias=${this._selectedPropertyEditorUI.meta.propertyEditorModel}
<umb-ref-data-type
name=${this._selectedDataType.name}
property-editor-ui-alias=${this._selectedDataType.propertyEditorAlias}
property-editor-model-alias=${this._selectedDataType.propertyEditorUiAlias}
@open=${() => {
console.warn('TO BE DONE..');
}}
border>
<uui-icon name="${this._selectedPropertyEditorUI.meta.icon}" slot="icon"></uui-icon>
<!-- TODO: Get the icon from property editor UI -->
<uui-icon name="${'document'}" slot="icon"></uui-icon>
<uui-action-bar slot="actions">
<uui-button label="Change" .href=${this._modalRoute}></uui-button>
</uui-action-bar>
</umb-ref-property-editor-ui>
</umb-ref-data-type>
`
: html`
<uui-button
@@ -114,6 +113,6 @@ export class UmbInputDataTypeElement extends FormControlMixin(UmbLitElement) {
declare global {
interface HTMLElementTagNameMap {
'umb-input-data-type': UmbInputDataTypeElement;
'umb-data-type-flow-input': UmbInputDataTypeElement;
}
}

View File

@@ -1 +1,2 @@
import './data-type-input/data-type-input.element';
import './data-type-flow-input/data-type-flow-input.element';