This commit is contained in:
Simon Martens
2025-06-04 13:31:30 +02:00
parent 4cf1c2ffd3
commit dccc3ed8ca
5 changed files with 421 additions and 257 deletions

View File

@@ -1,6 +1,9 @@
// Define CSS class names as constants, above the component class
const USER_PROVIDED_MENU_BUTTON_CLASS = "div-menu-button";
const TS_MENU_COMPONENT = "ts-menu";
const TS_POPUP = "ts-menu-popup";
const TS_LIST = "ts-menu-list";
const TS_MENU_LABEL = "menu-label";
const TS_PLACEHOLDER_MESSAGE = "ts-menu-placeholder-message";
const TS_LIST_ITEM = "ts-menu-list-item";
const TS_ITEM_ACTION = "ts-menu-item-action";
@@ -8,9 +11,9 @@ const TS_ITEM_IS_SELECTED = "ts-item-is-selected";
const TS_CONTENT_CLOSE_BUTTON = "ts-content-close-button";
const TAILWIND_HIDDEN_CLASS = "hidden";
const TS_MENU_BUTTON_STATE_DISABLED = "ts-menu-button--disabled";
// New constants for menu label states
const TS_MENU_LABEL_IN_MENU_STATE = "ts-menu-label--in-menu";
const TS_MENU_LABEL_DISPLAYED_STATE = "ts-menu-label--displayed";
const TS_TARGET_PLACEHOLDER_CLASS = "ts-target-placeholder"; // For the target area placeholder
//
// Prereq: child divs must have prop data-value, and a label element
// the label element must have class "menu-label"
@@ -76,7 +79,7 @@ export class DivMenu extends HTMLElement {
this._observer = new MutationObserver(this._handleMutations);
this._originalChildDivs.forEach((sourceDiv) => {
const menuLabelElement = sourceDiv.querySelector("label.menu-label");
const menuLabelElement = sourceDiv.querySelector("label." + TS_MENU_LABEL);
const itemValue = sourceDiv.dataset.value;
if (!menuLabelElement || !itemValue) {
@@ -84,14 +87,6 @@ export class DivMenu extends HTMLElement {
return;
}
const closeButton = document.createElement("button");
closeButton.type = "button";
closeButton.className = TS_CONTENT_CLOSE_BUTTON;
closeButton.innerHTML = "×";
closeButton.dataset.value = itemValue;
closeButton.addEventListener("click", this._handleContentClose);
sourceDiv.prepend(closeButton);
const isInitiallySelected = !sourceDiv.classList.contains(TAILWIND_HIDDEN_CLASS);
this.appendChild(sourceDiv);
@@ -135,12 +130,6 @@ export class DivMenu extends HTMLElement {
if (this._observer) {
this._observer.disconnect();
}
this._menuItemsMap.forEach((itemData) => {
const closeButton = itemData.sourceDiv.querySelector(`.${TS_CONTENT_CLOSE_BUTTON}`);
if (closeButton) {
closeButton.removeEventListener("click", this._handleContentClose);
}
});
if (this._menuButton) {
this._menuButton.removeEventListener("click", this._toggleMenu);
}
@@ -202,7 +191,6 @@ export class DivMenu extends HTMLElement {
if (!hasSelectedItems) {
const placeholder = document.createElement("p");
placeholder.className = TS_TARGET_PLACEHOLDER_CLASS;
placeholder.innerHTML = "<em>Selected content will appear here.</em>";
this._targetElement.appendChild(placeholder);
}
}
@@ -255,10 +243,11 @@ export class DivMenu extends HTMLElement {
}
_toggleMenu(event) {
event.preventDefault();
event.stopPropagation();
if (this._menuButton && this._menuButton.classList.contains(TS_MENU_BUTTON_STATE_DISABLED)) {
return;
}
event.stopPropagation();
const isHidden = this._menuPopupElement.style.display === "none" || this._menuPopupElement.style.display === "";
if (isHidden) {
this._checkMenuPlaceholderAndButton();

View File

@@ -1,8 +1,8 @@
@layer components {
.dbform .inputwrapper {
@apply rounded-xs border-2 border-transparent px-3
@apply rounded-xs border-2 border-transparent pl-3 pr-1.5
py-1 pb-1.5 border-l-2 focus-within:border-l-slate-600
bg-slate-200 focus-within:bg-slate-100 transition-all duration-100;
bg-stone-100 focus-within:bg-slate-100 transition-all duration-100;
}
.dbform .inputwrapper .inputlabel {
@@ -22,6 +22,10 @@
@apply mt-1 block w-full focus:border-none focus:outline-none;
}
.dbform .inputwrapper .inputtextarea {
@apply mt-1 block w-full focus:border-none focus:outline-none min-h-[20rem] resize-y;
}
.dbform .submitbutton {
@apply w-full inline-flex justify-center py-2 px-4 border border-transparent rounded-md shadow-sm text-sm font-medium text-white bg-slate-700 hover:bg-slate-800 cursor-pointer focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-slate-500 active:bg-slate-900 transition-all duration-75;
}
@@ -30,6 +34,10 @@
@apply w-full inline-flex justify-center py-2 px-4 border border-transparent rounded-md text-sm font-medium text-gray-800 bg-stone-200 hover:bg-stone-300 cursor-pointer focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-slate-500 no-underline;
}
.dbform div-menu {
@apply relative inline-block;
}
/* Multi-Select-Role example styles */
.msr-selected-items-container {
@apply rounded-md;
@@ -270,104 +278,28 @@
@apply disabled:hidden;
}
.ts-menu {
position: relative;
display: inline-block;
}
.ts-menu-popup {
display: none;
position: absolute;
top: 100%;
left: 0;
background-color: white;
border: 1px solid #ccc;
border-radius: 6px;
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
z-index: 1000;
min-width: 220px; /* Slightly wider for longer labels */
margin-top: 4px;
}
.ts-menu-toggle-button {
padding: 10px 15px;
font-size: 1rem;
color: #ffffff;
background-color: #3498db;
border: none;
border-radius: 6px;
cursor: pointer;
transition:
background-color 0.2s ease,
opacity 0.2s ease;
}
.ts-menu-toggle-button:hover {
background-color: #2980b9;
}
.ts-menu-toggle-button:focus {
outline: 2px solid #2980b9;
outline-offset: 2px;
}
.ts-menu-toggle-button:disabled {
background-color: #bdc3c7;
cursor: not-allowed;
opacity: 0.7;
@apply hidden absolute top-full left-0 bg-white border border-gray-300 rounded-xs shadow z-50 min-w-[200px] mt-1;
}
.ts-menu-list {
list-style: none;
padding: 8px 0;
padding: 0px 0;
margin: 0;
}
.ts-menu-list-item {
.ts-menu-list li {
@apply cursor-pointer ml-0 list-none px-2.5 py-1 hover:bg-gray-100 border-b;
}
.ts-menu-placeholder-message {
padding: 10px 15px;
font-style: italic;
color: #7f8c8d;
text-align: center;
display: none;
.ts-menu-list li:last-child {
@apply border-b-0;
}
.ts-menu-item-action {
display: block;
width: 100%;
text-align: left;
padding: 10px 15px;
background: none;
border: none;
cursor: pointer;
font-size: 0.95rem;
color: #333;
transition:
background-color 0.2s ease,
color 0.2s ease;
}
.ts-menu-item-action:hover {
background-color: #ecf0f1;
}
.ts-item-is-selected {
font-weight: bold;
color: #2980b9;
background-color: #e8f4f8;
}
.ts-content-close-button {
position: absolute;
top: 8px; /* Adjusted for better alignment with padding */
right: 8px; /* Adjusted */
background-color: #e74c3c;
color: white;
border: none;
border-radius: 50%;
width: 22px; /* Slightly larger */
height: 22px; /* Slightly larger */
font-size: 13px;
line-height: 20px;
text-align: center;
cursor: pointer;
font-weight: bold;
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.2);
transition: background-color 0.2s ease;
}
.ts-content-close-button:hover {
background-color: #c0392b;
.ts-menu-list li button {
@apply w-full cursor-pointer text-left text-gray-700;
}
.form-submit-button {
margin-top: 20px;
padding: 12px 20px;