diff --git a/api/js/etemplate/Et2Select/Et2SelectThumbnail.ts b/api/js/etemplate/Et2Select/Et2SelectThumbnail.ts new file mode 100644 index 0000000000..190a5bc83a --- /dev/null +++ b/api/js/etemplate/Et2Select/Et2SelectThumbnail.ts @@ -0,0 +1,122 @@ +/** + * EGroupware eTemplate2 - Image selection WebComponent + * + * @license http://opensource.org/licenses/gpl-license.php GPL - GNU General Public License + * @package api + * @link https://www.egroupware.org + * @author Nathan Gray + */ + +import {Et2Select} from "./Et2Select"; +import {css} from "@lion/core"; +import {SelectOption} from "./FindSelectOptions"; + +export class Et2SelectThumbnail extends Et2Select +{ + static get styles() + { + return [ + ...super.styles, + css` + + /* Hide selected options from the dropdown */ + ::slotted([checked]) + { + display: none; + } + /* Hide dropdown icon */ + ::part(icon), .select__icon { + display: none; + } + ` + ]; + } + + constructor(...args : any[]) + { + super(...args); + this.search = false; + this.allowFreeEntries = true; + this.editModeEnabled = true; + this.multiple = true; + this.pill = false; + } + + /** + * Create an entry that is not in the options and add it to the value + * Overridden here to set the icon as the text, since this is a thumbnail + * + * @param {string} text Used as both value and label + */ + public createFreeEntry(text : string) : boolean + { + if(!this.validateFreeEntry(text)) + { + return false; + } + // Make sure not to double-add + if(!this.select_options.find(o => o.value == text)) + { + this.select_options.push({ + value: text, + label: "", + icon: text + }); + this.requestUpdate('select_options'); + } + + // Make sure not to double-add + if(this.multiple && this.value.indexOf(text) == -1) + { + this.value.push(text); + } + else if(!this.multiple) + { + this.value = text; + return; + } + + // Once added to options, add to value / tags + this.updateComplete.then(() => + { + this.menuItems.forEach(o => + { + if(o.value == text) + { + o.dispatchEvent(new Event("click")); + } + }); + this.syncItemsFromValue(); + }); + return true; + } + + get tagTag() : string + { + return "et2-thumbnail-tag"; + } + + /** + * Customise how tags are rendered. This overrides what SlSelect + * does in syncItemsFromValue(). + * This is a copy+paste from SlSelect.syncItemsFromValue(). + * + * @param item + * @protected + */ + protected _createTagNode(item) + { + let tag = super._createTagNode(item); + + // Different image - slot in just an image so we can have complete control over styling + tag.querySelector("[slot=prefix]")?.remove(); + let img = document.createElement("img"); + img.slot = "prefix"; + img.src = item.value; + tag.append(img); + + return tag; + } +} + +customElements.define("et2-select-thumbnail", Et2SelectThumbnail); \ No newline at end of file diff --git a/api/js/etemplate/Et2Select/SearchMixin.ts b/api/js/etemplate/Et2Select/SearchMixin.ts index 5b90584ccc..8d44f5f578 100644 --- a/api/js/etemplate/Et2Select/SearchMixin.ts +++ b/api/js/etemplate/Et2Select/SearchMixin.ts @@ -261,7 +261,7 @@ export const Et2WithSearchMixin = >(superclass this.classList.toggle("search", this.searchEnabled); // Missing any of the required attributes? Don't change anything. - if(!this.searchEnabled) + if(!this.searchEnabled && !this.editModeEnabled) { return; } @@ -797,7 +797,7 @@ export const Et2WithSearchMixin = >(superclass */ public startEdit(tag : Et2Tag) { - const tag_value = tag.textContent.trim(); + const tag_value = tag.value; // hide the menu //this.dropdown.hide() diff --git a/api/js/etemplate/Et2Select/Tag/Et2ThumbnailTag.ts b/api/js/etemplate/Et2Select/Tag/Et2ThumbnailTag.ts new file mode 100644 index 0000000000..ff4c8d2e32 --- /dev/null +++ b/api/js/etemplate/Et2Select/Tag/Et2ThumbnailTag.ts @@ -0,0 +1,48 @@ +/** + * EGroupware eTemplate2 - Thumbnail Tag WebComponent + * + * @license http://opensource.org/licenses/gpl-license.php GPL - GNU General Public License + * @package api + * @link https://www.egroupware.org + * @author Nathan Gray + */ +import {css} from "@lion/core"; +import shoelace from "../../Styles/shoelace"; +import {Et2Tag} from "./Et2Tag"; + +/** + * Used in a Et2ThumbnailSelect with multiple=true + * + * It's just easier to deal with the styling here due to scoping + */ +export class Et2ThumbnailTag extends Et2Tag +{ + + static get styles() + { + return [ + super.styles, + shoelace, css` + .tag { + --icon-width: 100%; + max-width: 15em; + height: unset; + } + + ::slotted(img) { + width: 100%; + height: 50px; + } + + `]; + } + + constructor(...args : []) + { + super(...args); + this.pill = false + } + +} + +customElements.define("et2-thumbnail-tag", Et2ThumbnailTag); \ No newline at end of file diff --git a/api/js/etemplate/etemplate2.ts b/api/js/etemplate/etemplate2.ts index eaa5726491..0f6e50019c 100644 --- a/api/js/etemplate/etemplate2.ts +++ b/api/js/etemplate/etemplate2.ts @@ -53,8 +53,10 @@ import './Et2Select/Et2Select'; import './Et2Select/Et2SelectAccount'; import './Et2Select/Et2SelectEmail'; import './Et2Select/Et2SelectReadonly'; +import './Et2Select/Et2SelectThumbnail' import './Et2Select/Tag/Et2Tag'; -import './Et2Select/Tag/Et2CategoryTag' +import './Et2Select/Tag/Et2CategoryTag'; +import './Et2Select/Tag/Et2ThumbnailTag'; import './Et2Textarea/Et2Textarea'; import './Et2Textbox/Et2Textbox'; import './Et2Textbox/Et2TextboxReadonly'; diff --git a/api/src/Etemplate/Widget/Taglist.php b/api/src/Etemplate/Widget/Taglist.php index 3eefb99565..b06ea214ad 100644 --- a/api/src/Etemplate/Widget/Taglist.php +++ b/api/src/Etemplate/Widget/Taglist.php @@ -209,4 +209,5 @@ class Taglist extends Etemplate\Widget } } -Etemplate\Widget::registerWidget(__NAMESPACE__ . '\\Taglist', array('taglist', 'et2-select-email')); \ No newline at end of file +Etemplate\Widget::registerWidget(__NAMESPACE__ . '\\Taglist', array( + 'taglist', 'et2-select-email', 'et2-select-thumbnail')); \ No newline at end of file