diff --git a/src/Umbraco.Web.UI.Client/src/backoffice/property-editors/property-editor-icon-picker.element.ts b/src/Umbraco.Web.UI.Client/src/backoffice/property-editors/property-editor-icon-picker.element.ts new file mode 100644 index 0000000000..9b3cbc3b92 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/backoffice/property-editors/property-editor-icon-picker.element.ts @@ -0,0 +1,41 @@ +import { html, LitElement } from 'lit'; +import { customElement } from 'lit/decorators.js'; + +import { UmbContextConsumerMixin } from '../../core/context'; +import { UmbModalService } from '../../core/services/modal'; + +// TODO: remove these imports when they are part of UUI +import '@umbraco-ui/uui-modal'; +import '@umbraco-ui/uui-modal-sidebar'; +import '@umbraco-ui/uui-modal-container'; +import '@umbraco-ui/uui-modal-dialog'; + +@customElement('umb-property-editor-icon-picker') +class UmbPropertyEditorIconPicker extends UmbContextConsumerMixin(LitElement) { + private _modalService?: UmbModalService; + + constructor() { + super(); + this.consumeContext('umbModalService', (modalService: UmbModalService) => { + this._modalService = modalService; + }); + } + + private _openModal() { + this._modalService?.open('umb-modal-layout-icon-picker', { type: 'sidebar', size: 'small' }); + } + + render() { + return html` + + Pick an icon + + `; + } +} + +declare global { + interface HTMLElementTagNameMap { + 'umb-property-editor-icon-picker': UmbPropertyEditorIconPicker; + } +} diff --git a/src/Umbraco.Web.UI.Client/src/core/services/modal/layouts/icon-picker/modal-layout-icon-picker.element.ts b/src/Umbraco.Web.UI.Client/src/core/services/modal/layouts/icon-picker/modal-layout-icon-picker.element.ts new file mode 100644 index 0000000000..dd94ddf65d --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/core/services/modal/layouts/icon-picker/modal-layout-icon-picker.element.ts @@ -0,0 +1,304 @@ +import { css, html } from 'lit'; +import { UUITextStyles } from '@umbraco-ui/uui-css/lib'; +import { customElement, property, state } from 'lit/decorators.js'; +import { UmbModalLayoutElement } from '../modal-layout.element'; + +import '../../../../../backoffice/editors/shared/editor-entity/editor-entity.element'; + +@customElement('umb-modal-layout-icon-picker') +class UmbModalLayoutIconPickerElement extends UmbModalLayoutElement { + static styles = [ + UUITextStyles, + css` + :host { + position: relative; + } + + #container { + display: flex; + flex-direction: column; + height: 100%; + background-color: white; + box-shadow: var(--uui-shadow-depth-1, 0 1px 3px rgba(0, 0, 0, 0.12), 0 1px 2px rgba(0, 0, 0, 0.24)); + border-radius: var(--uui-border-radius); + padding: var(--uui-size-space-5); + box-sizing: border-box; + } + #container hr { + height: 1px; + border: none; + background-color: var(--uui-color-divider); + margin: 20px 0; + } + + #searchbar { + width: 100%; + } + #searchbar_icon { + padding-left: 6px; + } + + input[type='radio'] { + display: none; + position: absolute; + } + + #palette { + display: flex; + flex-wrap: wrap; + } + + #palette .colorspot { + box-sizing: border-box; + line-height: 0; + padding: 4px; + margin: 5px 5px 5px 0; + height: 25px; + width: 25px; + display: inline-block; + border-radius: 5px; + } + + #palette .checkmark { + height: 100%; + width: 100%; + display: none; + color: white; + background-color: rgba(0, 0, 0, 0.2); + border-radius: 100%; + } + #palette input[type='radio']:checked ~ .checkmark { + display: block; + } + + #icon-selection { + line-height: 0; + display: grid; + grid-template-columns: repeat(auto-fit, minmax(40px, auto)); + overflow-y: scroll; + max-height: 100%; + min-height: 0; + } + + #icon-selection .icon { + display: inline-block; + border-radius: 2px; + width: 100%; + height: 100%; + padding: 8px; + box-sizing: border-box; + } + + #icon-selection .icon-container { + display: inline-block; + } + + /*#icon-selection input[type='radio']:checked ~ .icon { + background-color: rgba(0, 0, 0, 0.1); + border: 1px solid #ffeeee; + }*/ + + #icon-selection .icon:focus, + #icon-selection .icon:hover, + #icon-selection .icon.selected { + background-color: rgba(0, 0, 0, 0.1); + } + `, + ]; + + @property({ type: Array }) + iconlist = [ + 'add', + 'alert', + 'attachment', + 'calendar', + 'check', + 'clipboard', + 'code', + 'colorpicker', + 'copy', + 'delete', + 'document', + 'download', + 'edit', + 'favorite', + 'folder', + 'forbidden', + 'info', + 'link', + 'lock', + 'pause', + 'picture', + 'play', + 'remove', + 'search', + 'see', + 'settings', + 'subtract', + 'sync', + 'unlock', + 'unsee', + 'wand', + 'wrong', + ]; + + @property({ type: Array }) + iconlistFiltered: Array; + + @property({ type: Array }) + colorlist = [ + '#000000', + '#373737', + '#9e9e9e', + '#607d8b', + '#2196f3', + '#03a9f4', + '#3f51b5', + '#9c27b0', + '#673ab7', + '#00bcd4', + '#4caf50', + '#8bc34a', + '#cddc39', + '#ffeb3b', + '#ffc107', + '#ff9800', + '#ff5722', + '#f44336', + '#e91e63', + '#795548', + ]; + + @state() + private _currentColor: string; + + @state() + private _currentIcon: string; + + private _changeIconColor(e: { target: HTMLInputElement; type: any; key: unknown }) { + if (e.type == 'click') { + this._currentColor = e.target.id; + } else if (e.type == 'keyup' && e.key == 'Enter') { + e.target.children[0].setAttribute('checked', 'true'); + this._currentColor = e.target.children[0].id; + } + } + + private _changeIcon(e: { target: HTMLInputElement; type: any; key: unknown }) { + if (e.type == 'click' || (e.type == 'keyup' && e.key == 'Enter')) { + this._currentIcon = e.target.id; + } + } + + private _filterIcons(e: { target: HTMLInputElement }) { + if (e.target.value) { + this.iconlistFiltered = this.iconlist.filter((icon) => icon.includes(e.target.value)); + } else { + this.iconlistFiltered = this.iconlist; + } + } + + private _setBackground(color: string) { + return 'background-color: ' + color; + } + private _setColor(color: string) { + return 'color: ' + color; + } + + private _close() { + this.modalHandler?.close(); + } + + private _save() { + this.modalHandler?.close({ color: this._currentColor, icon: this._currentIcon }); + } + + constructor() { + super(); + this._currentColor = ''; + this._currentIcon = ''; + this.iconlistFiltered = []; + } + + connectedCallback(): void { + super.connectedCallback(); + this._currentColor = this.colorlist[0]; + this._currentIcon = this.iconlist[0]; + this.iconlistFiltered = this.iconlist; + } + + render() { + return html` + +

Select icon

+
+ ${this.renderSearchbar()} +
+ +
${this.renderPalette()}
+ +
+ ${this.renderIconSelection()} +
+ Close + + Save + +
+ `; + } + + renderSearchbar() { + return html` + + `; + } + + renderPalette() { + return html`${this.colorlist.map((color) => { + return html``; + })}`; + } + + renderIconSelection() { + return html`${this.iconlistFiltered.map((icon) => { + return html` + + `; + })}`; + } +} + +declare global { + interface HTMLElementTagNameMap { + 'umb-modal-layout-icon-picker': UmbModalLayoutIconPickerElement; + } +} diff --git a/src/Umbraco.Web.UI.Client/src/stories/icon-picker.stories.ts b/src/Umbraco.Web.UI.Client/src/stories/icon-picker.stories.ts new file mode 100644 index 0000000000..5aa329fb1a --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/stories/icon-picker.stories.ts @@ -0,0 +1,40 @@ +import '../backoffice/components/backoffice-modal-container.element'; +import '../core/services/modal/layouts/content-picker/modal-layout-content-picker.element'; +import '../core/context/context-provider.element'; +import '../backoffice/editors/shared/editor-layout/editor-layout.element'; + +import '../backoffice/property-editors/property-editor-icon-picker.element'; +import '../core/services/modal/layouts/icon-picker/modal-layout-icon-picker.element'; + +import '@umbraco-ui/uui-modal'; +import '@umbraco-ui/uui-modal-container'; +import '@umbraco-ui/uui-modal-sidebar'; +import '@umbraco-ui/uui-modal-dialog'; + +import { Meta } from '@storybook/web-components'; +import { html } from 'lit-html'; +import { UmbModalService } from '../core/services/modal'; + +export default { + title: 'Editors/Icon Picker', + component: 'umb-property-editor-icon-picker', + id: 'icon-picker', + decorators: [ + (story) => + html` + + + ${story()} + + + `, + ], +} as Meta; + +export const IconPickerEditor = () => html` + `; + +export const IconPickerModalLayout = () => html``;