Files
Housekeeping-web-admin/node_modules/@base44/vite-plugin/dist/injections/layer-dropdown/dropdown-ui.js
T
2026-06-24 09:48:54 +02:00

186 lines
7.1 KiB
JavaScript

/** Dropdown UI component for layer navigation */
import { DROPDOWN_CONTAINER_STYLES, DROPDOWN_ITEM_BASE_STYLES, DROPDOWN_ITEM_ACTIVE_COLOR, DROPDOWN_ITEM_ACTIVE_BG, DROPDOWN_ITEM_ACTIVE_FONT_WEIGHT, DROPDOWN_ITEM_HOVER_BG, DEPTH_INDENT_PX, BASE_PADDING_PX, CHEVRON_COLLAPSED, CHEVRON_EXPANDED, CHEVRON_ATTR, LAYER_DROPDOWN_ATTR, } from "./consts.js";
import { applyStyles, getLayerDisplayName } from "./utils.js";
import { PLUGIN_ELEMENT_ATTR } from "../utils.js";
let activeDropdown = null;
let activeLabel = null;
let outsideMousedownHandler = null;
let activeOnHoverEnd = null;
let activeKeydownHandler = null;
function createDropdownItem(layer, isActive, { onSelect, onHover, onHoverEnd }) {
const item = document.createElement("div");
item.textContent = getLayerDisplayName(layer);
applyStyles(item, DROPDOWN_ITEM_BASE_STYLES);
const depth = layer.depth ?? 0;
if (depth > 0) {
item.style.paddingLeft = `${BASE_PADDING_PX + depth * DEPTH_INDENT_PX}px`;
}
if (isActive) {
item.style.color = DROPDOWN_ITEM_ACTIVE_COLOR;
item.style.backgroundColor = DROPDOWN_ITEM_ACTIVE_BG;
item.style.fontWeight = DROPDOWN_ITEM_ACTIVE_FONT_WEIGHT;
}
item.addEventListener("mouseenter", () => {
if (!isActive)
item.style.backgroundColor = DROPDOWN_ITEM_HOVER_BG;
if (onHover)
onHover(layer);
});
item.addEventListener("mouseleave", () => {
if (!isActive)
item.style.backgroundColor = "transparent";
if (onHoverEnd)
onHoverEnd();
});
item.addEventListener("click", (e) => {
e.stopPropagation();
e.preventDefault();
onSelect(layer);
});
return item;
}
/** Create the dropdown DOM element with layer items */
export function createDropdownElement(layers, currentElement, callbacks) {
const container = document.createElement("div");
container.setAttribute(LAYER_DROPDOWN_ATTR, "true");
container.setAttribute(PLUGIN_ELEMENT_ATTR, "true");
applyStyles(container, DROPDOWN_CONTAINER_STYLES);
layers.forEach((layer) => {
const isActive = layer.element === currentElement;
container.appendChild(createDropdownItem(layer, isActive, callbacks));
});
return container;
}
/** Add chevron indicator and pointer-events to the label */
export function enhanceLabelWithChevron(label) {
if (label.querySelector(`[${CHEVRON_ATTR}]`))
return;
const chevron = document.createElement("span");
chevron.setAttribute(CHEVRON_ATTR, "true");
chevron.style.display = "inline-flex";
chevron.innerHTML = CHEVRON_COLLAPSED;
label.appendChild(chevron);
label.style.display = "inline-flex";
label.style.alignItems = "center";
label.style.cursor = "pointer";
label.style.userSelect = "none";
label.style.whiteSpace = "nowrap";
label.style.pointerEvents = "auto";
label.setAttribute(LAYER_DROPDOWN_ATTR, "true");
label.setAttribute(PLUGIN_ELEMENT_ATTR, "true");
}
function setupKeyboardNavigation(dropdown, layers, currentElement, { onSelect, onHover, onHoverEnd }) {
const items = Array.from(dropdown.children);
let focusedIndex = layers.findIndex((l) => l.element === currentElement);
const setFocusedItem = (index) => {
if (focusedIndex >= 0 && focusedIndex < items.length) {
const prev = items[focusedIndex];
if (prev.style.color !== DROPDOWN_ITEM_ACTIVE_COLOR) {
prev.style.backgroundColor = "transparent";
}
}
focusedIndex = index;
if (focusedIndex >= 0 && focusedIndex < items.length) {
const cur = items[focusedIndex];
if (cur.style.color !== DROPDOWN_ITEM_ACTIVE_COLOR) {
cur.style.backgroundColor = DROPDOWN_ITEM_HOVER_BG;
}
cur.scrollIntoView({ block: "nearest" });
if (onHover && focusedIndex >= 0 && focusedIndex < layers.length) {
onHover(layers[focusedIndex]);
}
}
};
activeKeydownHandler = (e) => {
if (e.key === "ArrowDown") {
e.preventDefault();
e.stopPropagation();
setFocusedItem(focusedIndex < items.length - 1 ? focusedIndex + 1 : 0);
}
else if (e.key === "ArrowUp") {
e.preventDefault();
e.stopPropagation();
setFocusedItem(focusedIndex > 0 ? focusedIndex - 1 : items.length - 1);
}
else if (e.key === "Enter" && focusedIndex >= 0 && focusedIndex < layers.length) {
e.preventDefault();
e.stopPropagation();
if (onHoverEnd)
onHoverEnd();
onSelect(layers[focusedIndex]);
closeDropdown();
}
};
document.addEventListener("keydown", activeKeydownHandler, true);
}
function setupOutsideClickHandler(dropdown, label) {
let skipFirst = true;
outsideMousedownHandler = (e) => {
if (skipFirst) {
skipFirst = false;
return;
}
const target = e.target;
if (!dropdown.contains(target) && target !== label) {
closeDropdown();
}
};
document.addEventListener("mousedown", outsideMousedownHandler, true);
}
/** Show the dropdown below the label element */
export function showDropdown(label, layers, currentElement, callbacks) {
closeDropdown();
const dropdown = createDropdownElement(layers, currentElement, {
...callbacks,
onSelect: (layer) => {
if (callbacks.onHoverEnd)
callbacks.onHoverEnd();
callbacks.onSelect(layer);
closeDropdown();
},
});
const overlay = label.parentElement;
if (!overlay)
return;
dropdown.style.top = `${label.offsetTop + label.offsetHeight + 2}px`;
dropdown.style.left = `${label.offsetLeft}px`;
overlay.appendChild(dropdown);
activeDropdown = dropdown;
activeLabel = label;
const chevronEl = label.querySelector(`[${CHEVRON_ATTR}]`);
if (chevronEl) {
chevronEl.innerHTML = CHEVRON_EXPANDED;
}
activeOnHoverEnd = callbacks.onHoverEnd ?? null;
setupKeyboardNavigation(dropdown, layers, currentElement, callbacks);
setupOutsideClickHandler(dropdown, label);
}
/** Close the active dropdown and clean up listeners */
export function closeDropdown() {
const chevronEl = activeLabel?.querySelector(`[${CHEVRON_ATTR}]`);
if (chevronEl) {
chevronEl.innerHTML = CHEVRON_COLLAPSED;
}
activeLabel = null;
if (activeOnHoverEnd) {
activeOnHoverEnd();
activeOnHoverEnd = null;
}
if (activeDropdown && activeDropdown.parentNode) {
activeDropdown.remove();
}
activeDropdown = null;
if (outsideMousedownHandler) {
document.removeEventListener("mousedown", outsideMousedownHandler, true);
outsideMousedownHandler = null;
}
if (activeKeydownHandler) {
document.removeEventListener("keydown", activeKeydownHandler, true);
activeKeydownHandler = null;
}
}
/** Check if a dropdown is currently visible */
export function isDropdownOpen() {
return activeDropdown !== null;
}
//# sourceMappingURL=dropdown-ui.js.map