@trimble-oss/moduswebcomponents
Version:
Modus Web Components is a modern, accessible UI library built with Stencil JS that provides reusable web components following Trimble's Modus design system. This updated version focuses on improved flexibility, enhanced theming options, comprehensive cust
471 lines (465 loc) • 31 kB
JavaScript
'use strict';
var index = require('./index-CtEGR9Z7.js');
var logoConstants = require('./logo-constants-C5muH0UE.js');
var utils = require('./utils-B2oo-h7a.js');
/**
* Reorders a list item by swapping it with the adjacent item in the given direction.
*/
function reorderListItem(items, currentIndex, direction) {
const targetIdx = currentIndex + direction;
if (targetIdx < 0 || targetIdx >= items.length)
return null;
const newItems = [...items];
[newItems[currentIndex], newItems[targetIdx]] = [
newItems[targetIdx],
newItems[currentIndex],
];
return { items: newItems, targetIndex: targetIdx };
}
/**
* Reorders a grid item by removing it from the current position
* and inserting it at the target position.
*/
function reorderGridItem(items, currentIndex, offset) {
const targetIdx = currentIndex + offset;
if (targetIdx < 0 || targetIdx >= items.length)
return null;
const newItems = [...items];
const [movedItem] = newItems.splice(currentIndex, 1);
newItems.splice(targetIdx, 0, movedItem);
return { items: newItems, targetIndex: targetIdx };
}
/**
* Calculates the number of columns in the grid layout
* by comparing offsetTop values of consecutive items.
*/
function getGridColumnCount(el) {
const items = el.querySelectorAll('.modus-wc-app-menu-grid-item');
if (items.length <= 1)
return 1;
const firstTop = items[0].offsetTop;
let count = 1;
for (let i = 1; i < items.length; i++) {
if (items[i].offsetTop === firstTop) {
count++;
}
else {
break;
}
}
return count;
}
/**
* Returns the navigation offset for an arrow key based on the layout.
* Returns null if the key is not applicable for the current layout.
*/
function getNavigationOffset(key, layout, gridColumnCount) {
switch (key) {
case 'ArrowUp':
return layout === 'grid' ? -gridColumnCount : -1;
case 'ArrowDown':
return layout === 'grid' ? gridColumnCount : 1;
case 'ArrowLeft':
return layout === 'grid' ? -1 : null;
case 'ArrowRight':
return layout === 'grid' ? 1 : null;
default:
return null;
}
}
/**
* Returns the valid target index for focus navigation,
* or null if the target is out of bounds.
*/
function getTargetFocusIndex(currentIndex, offset, totalItems) {
const targetIdx = currentIndex + offset;
return targetIdx >= 0 && targetIdx < totalItems ? targetIdx : null;
}
/**
* Focuses the app menu item at the given index based on the layout.
*
* In list layout the focus target depends on edit mode: the wrapper row
* (tabIndex 0 in edit mode) or the inner `<li>` rendered by
* modus-wc-menu-item (native tab stop in normal mode).
*/
function focusAppMenuItem(el, layout, appIndex) {
var _a;
if (layout === 'grid') {
const items = el.querySelectorAll('.modus-wc-app-menu-grid-item');
(_a = items[appIndex]) === null || _a === void 0 ? void 0 : _a.focus();
}
else {
const rows = el.querySelectorAll('.modus-wc-app-menu-item-row');
const row = rows[appIndex];
if (!row)
return;
if (row.tabIndex === 0) {
row.focus();
}
else {
const li = row.querySelector('modus-wc-menu-item li');
li === null || li === void 0 ? void 0 : li.focus();
}
}
}
const modusWcAppMenuCss = "modus-wc-app-menu.modus-wc-app-menu{box-sizing:border-box;display:block;min-width:0;text-align:start;width:fit-content}modus-wc-app-menu.modus-wc-app-menu .modus-wc-panel{box-shadow:0 10px 15px -3px rgba(0, 0, 0, 0.1), 0 4px 6px -4px rgba(0, 0, 0, 0.1);gap:var(--modus-wc-spacing-lg)}modus-wc-app-menu.modus-wc-app-menu .modus-wc-panel [slot=body]{display:flex;flex-direction:column;gap:var(--modus-wc-spacing-lg);overflow-y:auto;padding:var(--modus-wc-spacing-lg);padding-inline-end:0;scrollbar-gutter:stable}modus-wc-app-menu.modus-wc-app-menu .modus-wc-panel .modus-wc-app-menu-header{align-content:center;align-items:center;align-self:stretch;display:flex;flex-wrap:wrap;gap:var(--modus-wc-spacing-sm);min-height:var(--modus-wc-size-sm);padding:0 var(--modus-wc-spacing-lg)}modus-wc-app-menu.modus-wc-app-menu .modus-wc-panel .modus-wc-app-menu-header .modus-wc-app-menu-header-title{align-content:flex-start;align-items:flex-start;display:flex;flex:1 0 0;flex-wrap:wrap}modus-wc-app-menu.modus-wc-app-menu .modus-wc-panel .modus-wc-app-menu-header .modus-wc-app-menu-header-end-content{align-items:center;display:flex;gap:var(--modus-wc-spacing-sm)}modus-wc-app-menu.modus-wc-app-menu .modus-wc-panel .modus-wc-menu{padding:var(--modus-wc-spacing-sm) var(--modus-wc-spacing-lg)}modus-wc-app-menu.modus-wc-app-menu .modus-wc-panel .modus-wc-app-menu-items{padding:0 var(--modus-wc-spacing-xs)}modus-wc-app-menu.modus-wc-app-menu .modus-wc-panel .modus-wc-app-menu-items li.modus-wc-menu-item-active,modus-wc-app-menu.modus-wc-app-menu .modus-wc-panel .modus-wc-app-menu-items .modus-wc-menu-item button:active{background-color:transparent;color:inherit}modus-wc-app-menu.modus-wc-app-menu .modus-wc-panel .modus-wc-app-menu-items{}modus-wc-app-menu.modus-wc-app-menu .modus-wc-panel .modus-wc-app-menu-items .modus-wc-app-menu-item-row:focus-visible{outline:none}modus-wc-app-menu.modus-wc-app-menu .modus-wc-panel .modus-wc-app-menu-items .modus-wc-app-menu-item-row:focus-visible modus-wc-menu-item .modus-wc-menu-item,modus-wc-app-menu.modus-wc-app-menu .modus-wc-panel .modus-wc-app-menu-items .modus-wc-app-menu-item-row:focus-visible modus-wc-menu-item .modus-wc-menu-item:hover{outline:2px solid var(--modus-wc-color-primary);outline-offset:-2px}modus-wc-app-menu.modus-wc-app-menu .modus-wc-panel .modus-wc-app-menu-items .modus-wc-app-menu-item-row{align-items:center;display:flex;margin-inline:calc(-1 * var(--modus-wc-spacing-xs));max-width:256px;position:relative}modus-wc-app-menu.modus-wc-app-menu .modus-wc-panel .modus-wc-app-menu-items .modus-wc-app-menu-item-row .modus-wc-app-menu-drag-icon{color:var(--modus-wc-color-base-content);position:absolute;transform:translateX(-10%);z-index:1}modus-wc-app-menu.modus-wc-app-menu .modus-wc-panel .modus-wc-app-menu-items .modus-wc-app-menu-item-row>modus-wc-menu-item{flex:1;min-width:0}modus-wc-app-menu.modus-wc-app-menu .modus-wc-panel .modus-wc-app-menu-items .modus-wc-app-menu-item-row.modus-wc-app-menu-draggable-item{cursor:grab}modus-wc-app-menu.modus-wc-app-menu .modus-wc-panel .modus-wc-app-menu-items .modus-wc-app-menu-item-row.modus-wc-app-menu-draggable-item .modus-wc-app-menu-drag-icon{cursor:grab}modus-wc-app-menu.modus-wc-app-menu .modus-wc-panel .modus-wc-app-menu-items .modus-wc-app-menu-item-row.modus-wc-app-menu-draggable-item{}modus-wc-app-menu.modus-wc-app-menu .modus-wc-panel .modus-wc-app-menu-items .modus-wc-app-menu-item-row.modus-wc-app-menu-draggable-item modus-wc-menu-item .modus-wc-menu-item button,modus-wc-app-menu.modus-wc-app-menu .modus-wc-panel .modus-wc-app-menu-items .modus-wc-app-menu-item-row.modus-wc-app-menu-draggable-item modus-wc-menu-item .modus-wc-menu-item button .modus-wc-menu-item-content,modus-wc-app-menu.modus-wc-app-menu .modus-wc-panel .modus-wc-app-menu-items .modus-wc-app-menu-item-row.modus-wc-app-menu-draggable-item modus-wc-menu-item .modus-wc-menu-item button .modus-wc-menu-item-content *{cursor:grab}modus-wc-app-menu.modus-wc-app-menu .modus-wc-panel .modus-wc-app-menu-items .modus-wc-app-menu-item-row.modus-wc-app-menu-draggable-item modus-wc-menu-item .modus-wc-menu-item button:active,modus-wc-app-menu.modus-wc-app-menu .modus-wc-panel .modus-wc-app-menu-items .modus-wc-app-menu-item-row.modus-wc-app-menu-draggable-item modus-wc-menu-item .modus-wc-menu-item button:active .modus-wc-menu-item-content,modus-wc-app-menu.modus-wc-app-menu .modus-wc-panel .modus-wc-app-menu-items .modus-wc-app-menu-item-row.modus-wc-app-menu-draggable-item modus-wc-menu-item .modus-wc-menu-item button:active .modus-wc-menu-item-content *{cursor:grabbing}modus-wc-app-menu.modus-wc-app-menu .modus-wc-panel .modus-wc-app-menu-items{}modus-wc-app-menu.modus-wc-app-menu .modus-wc-panel .modus-wc-app-menu-items .modus-wc-menu-item button{min-width:0}modus-wc-app-menu.modus-wc-app-menu .modus-wc-panel .modus-wc-app-menu-items .modus-wc-menu-item button .modus-wc-menu-item-content{gap:var(--modus-wc-spacing-lg);min-width:0;width:100%}modus-wc-app-menu.modus-wc-app-menu .modus-wc-panel .modus-wc-app-menu-items .modus-wc-menu-item button .modus-wc-menu-item-labels{flex:1;min-width:0;overflow:hidden;padding-inline-start:0}modus-wc-app-menu.modus-wc-app-menu .modus-wc-panel .modus-wc-app-menu-items .modus-wc-menu-item button .modus-wc-menu-item-labels>div:not(.modus-wc-menu-item-sublabel){max-width:100%;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}modus-wc-app-menu.modus-wc-app-menu .modus-wc-panel .modus-wc-app-menu-items .modus-wc-menu-item button .modus-wc-menu-item-labels>modus-wc-tooltip{display:block;flex:1;max-width:100%;min-width:0}modus-wc-app-menu.modus-wc-app-menu .modus-wc-panel .modus-wc-app-menu-items .modus-wc-menu-item button .modus-wc-menu-item-labels>modus-wc-tooltip>div{max-width:100%;min-width:0}modus-wc-app-menu.modus-wc-app-menu .modus-wc-panel .modus-wc-app-menu-items .modus-wc-menu-item button .modus-wc-menu-item-labels>modus-wc-tooltip>div>div:first-child{max-width:100%;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}modus-wc-app-menu.modus-wc-app-menu .modus-wc-panel .modus-wc-app-menu-items .modus-wc-menu-item button .modus-wc-menu-item-content [slot=start-icon]{align-items:center;display:inline-flex;flex-shrink:0;padding-inline-end:0}modus-wc-app-menu.modus-wc-app-menu .modus-wc-panel .modus-wc-app-menu-items .modus-wc-menu-item button .modus-wc-menu-item-content [slot=start-icon] .modus-wc-app-menu-app-logo{height:var(--modus-wc-input-height-sm);width:var(--modus-wc-input-height-sm)}modus-wc-app-menu.modus-wc-app-menu .modus-wc-panel .modus-wc-app-menu-grid{cursor:pointer;display:flex;flex-direction:column;gap:var(--modus-wc-spacing-sm);margin-inline:auto;width:fit-content}modus-wc-app-menu.modus-wc-app-menu .modus-wc-panel .modus-wc-app-menu-grid-row{align-self:stretch;display:grid;gap:0 var(--modus-wc-spacing-sm);grid-template-columns:repeat(3, minmax(0, 1fr));justify-items:stretch}modus-wc-app-menu.modus-wc-app-menu .modus-wc-panel .modus-wc-app-menu-grid-item{align-items:center;border-radius:var(--modus-wc-border-radius-sm);display:flex;flex-direction:column;padding:var(--modus-wc-spacing-lg) var(--modus-wc-spacing-xs);position:relative;width:var(--modus-wc-spacing-20)}modus-wc-app-menu.modus-wc-app-menu .modus-wc-panel .modus-wc-app-menu-grid-item>modus-wc-icon{align-items:center;display:inline-flex;inset-inline-end:0;justify-content:center;position:absolute;top:0;width:var(--modus-wc-size-xs)}modus-wc-app-menu.modus-wc-app-menu .modus-wc-panel .modus-wc-app-menu-grid-item:hover{background-color:var(--modus-wc-color-base-100)}modus-wc-app-menu.modus-wc-app-menu .modus-wc-panel .modus-wc-app-menu-grid-item{}modus-wc-app-menu.modus-wc-app-menu .modus-wc-panel .modus-wc-app-menu-grid-item:focus-visible{outline:2px solid var(--modus-wc-color-primary);outline-offset:-2px}modus-wc-app-menu.modus-wc-app-menu .modus-wc-panel .modus-wc-app-menu-grid-emblem{aspect-ratio:1/1;flex:1 0 0;min-height:var(--modus-wc-size-xs);min-width:var(--modus-wc-size-xs);width:var(--modus-wc-line-height-h0)}modus-wc-app-menu.modus-wc-app-menu .modus-wc-panel .modus-wc-app-menu-grid-item-label{align-self:stretch;-webkit-box-orient:vertical;color:var(--modus-wc-color-base-content);display:-webkit-box;-webkit-line-clamp:1;line-height:var(--modus-wc-font-size-2xl);overflow:hidden;text-align:center;text-overflow:ellipsis}modus-wc-app-menu.modus-wc-app-menu .modus-wc-panel .modus-wc-app-menu-draggable-item{cursor:grab;transition:box-shadow 0.2s ease}modus-wc-app-menu.modus-wc-app-menu .modus-wc-panel .modus-wc-app-menu-draggable-item:active{box-shadow:0 4px 6px -1px rgba(0, 0, 0, 0.1), 0 2px 4px -2px rgba(0, 0, 0, 0.1);cursor:grabbing}modus-wc-app-menu.modus-wc-app-menu .modus-wc-panel .modus-wc-app-menu-grabbed-item{background-color:var(--modus-wc-color-base-200);box-shadow:0 4px 6px -1px rgba(0, 0, 0, 0.1), 0 2px 4px -2px rgba(0, 0, 0, 0.1);outline:2px solid var(--modus-wc-color-primary);outline-offset:-2px}modus-wc-app-menu.modus-wc-app-menu .modus-wc-panel .modus-wc-app-menu-drag-source{opacity:0.3}modus-wc-app-menu.modus-wc-app-menu .modus-wc-panel .modus-wc-app-menu-drop-target{border-radius:var(--modus-wc-border-radius-sm);outline:2px solid var(--modus-wc-color-primary);outline-offset:-2px}";
const ModusWcAppMenu = class {
constructor(hostRef) {
index.registerInstance(this, hostRef);
this.layoutChange = index.createEvent(this, "layoutChange", 7);
this.itemsOrderChange = index.createEvent(this, "itemsOrderChange", 7);
this.itemClick = index.createEvent(this, "itemClick", 7);
this.inheritedAttributes = {};
/** custom class to apply to the menu */
this.customClass = '';
/** The layout of the menu. */
this.layout = 'list';
/** The apps to display in the menu. */
this.apps = [];
this.isEditMode = false;
this.draggedItemPos = null;
this.dropTargetIndex = null;
this.grabbedItemPos = null;
this.truncatedApps = new Set();
this.appsSnapshot = null;
}
componentWillLoad() {
this.inheritedAttributes = utils.inheritAriaAttributes(this.el);
}
componentDidLoad() {
this.scheduleTooltipUpdate();
}
onAppsChange() {
if (!this.isEditMode) {
this.scheduleTooltipUpdate();
}
}
scheduleTooltipUpdate() {
requestAnimationFrame(() => {
if (this.layout === 'grid') {
this.updateGridTooltips();
}
else {
this.updateListTooltips();
}
});
}
setsAreEqual(a, b) {
if (a.size !== b.size)
return false;
for (const name of a) {
if (!b.has(name))
return false;
}
return true;
}
setTruncatedApps(next) {
if (this.setsAreEqual(next, this.truncatedApps))
return;
this.truncatedApps = next;
}
updateGridTooltips() {
const updated = new Set();
const gridItems = this.el.querySelectorAll('.modus-wc-app-menu-grid-item');
gridItems.forEach((gridItem, appIndex) => {
var _a, _b;
const label = gridItem.querySelector('.modus-wc-app-menu-grid-item-label');
const appName = (_b = (_a = this.apps) === null || _a === void 0 ? void 0 : _a[appIndex]) === null || _b === void 0 ? void 0 : _b.appName;
if (label && appName) {
const isTruncated = label.scrollWidth > label.clientWidth ||
label.scrollHeight > label.clientHeight;
if (isTruncated) {
updated.add(appName);
}
}
});
this.setTruncatedApps(updated);
}
/** Label text node that visually truncates (inner div), not `.modus-wc-menu-item-labels`. */
getListMenuItemLabelTextElement(row) {
var _a, _b;
const labels = row.querySelector('.modus-wc-menu-item-labels');
if (!labels)
return null;
// Walk direct children only; `:scope >` is unavailable in mock-doc (used in tests).
for (const child of Array.from(labels.children)) {
if (child.tagName === 'MODUS-WC-TOOLTIP') {
const textDiv = (_b = (_a = child.firstElementChild) === null || _a === void 0 ? void 0 : _a.firstElementChild) !== null && _b !== void 0 ? _b : null;
return textDiv;
}
if (child.tagName === 'DIV' &&
!child.classList.contains('modus-wc-menu-item-sublabel')) {
return child;
}
}
return null;
}
updateListTooltips() {
const updated = new Set();
const rows = this.el.querySelectorAll('.modus-wc-app-menu-item-row');
rows.forEach((row, appIndex) => {
var _a, _b;
const appName = (_b = (_a = this.apps) === null || _a === void 0 ? void 0 : _a[appIndex]) === null || _b === void 0 ? void 0 : _b.appName;
if (!appName)
return;
const textEl = this.getListMenuItemLabelTextElement(row);
if (!textEl)
return;
const isTruncated = textEl.scrollWidth > textEl.clientWidth ||
textEl.scrollHeight > textEl.clientHeight;
if (isTruncated) {
updated.add(appName);
}
});
this.setTruncatedApps(updated);
}
getDisplayName(appName) {
var _a, _b;
return (_b = (_a = logoConstants.LOGO_VARIANTS[appName]) === null || _a === void 0 ? void 0 : _a.displayName) !== null && _b !== void 0 ? _b : appName;
}
onLayoutChange(newLayout) {
this.layoutChange.emit({ layout: newLayout });
this.scheduleTooltipUpdate();
}
handleEdit() {
var _a, _b;
this.appsSnapshot = [...((_a = this.apps) !== null && _a !== void 0 ? _a : [])];
this.isEditMode = true;
const layout = (_b = this.layout) !== null && _b !== void 0 ? _b : 'list';
requestAnimationFrame(() => {
focusAppMenuItem(this.el, layout, 0);
});
}
hasOrderChangedSinceEdit() {
var _a;
const snapshot = this.appsSnapshot;
if (!snapshot)
return false;
const current = (_a = this.apps) !== null && _a !== void 0 ? _a : [];
if (current.length !== snapshot.length)
return true;
// Length equality guarantees snapshot[i] is defined here.
return current.some((item, i) => item.appName !== snapshot[i].appName);
}
handleDone() {
var _a;
const shouldEmit = this.hasOrderChangedSinceEdit();
this.isEditMode = false;
this.grabbedItemPos = null;
this.appsSnapshot = null;
if (shouldEmit) {
this.itemsOrderChange.emit([...((_a = this.apps) !== null && _a !== void 0 ? _a : [])]);
}
}
handleCancel() {
if (this.appsSnapshot) {
this.apps = this.appsSnapshot;
}
this.appsSnapshot = null;
this.isEditMode = false;
this.grabbedItemPos = null;
}
handleKeyDown(e, appIndex) {
var _a, _b;
switch (e.key) {
case ' ':
case 'Enter': {
if (this.isEditMode) {
e.preventDefault();
this.grabbedItemPos = this.grabbedItemPos ? null : { appIndex };
}
else {
const app = (_a = this.apps) === null || _a === void 0 ? void 0 : _a[appIndex];
if (app) {
e.preventDefault();
this.itemClick.emit({ appName: app.appName });
}
}
break;
}
case 'ArrowUp':
case 'ArrowDown':
case 'ArrowLeft':
case 'ArrowRight': {
const layout = (_b = this.layout) !== null && _b !== void 0 ? _b : 'list';
const gridCols = layout === 'grid' ? getGridColumnCount(this.el) : 1;
const offset = getNavigationOffset(e.key, layout, gridCols);
if (offset === null)
return;
e.preventDefault();
e.stopPropagation();
if (this.isEditMode && this.grabbedItemPos) {
this.reorderByKeyboard(appIndex, offset);
}
else {
this.navigateFocusByKeyboard(appIndex, offset);
}
break;
}
case 'Escape':
if (this.isEditMode && this.grabbedItemPos) {
e.preventDefault();
this.grabbedItemPos = null;
}
break;
}
}
reorderByKeyboard(appIndex, offset) {
var _a, _b;
const apps = (_a = this.apps) !== null && _a !== void 0 ? _a : [];
const layout = (_b = this.layout) !== null && _b !== void 0 ? _b : 'list';
const result = layout === 'grid'
? reorderGridItem(apps, appIndex, offset)
: reorderListItem(apps, appIndex, offset);
if (result) {
this.apps = result.items;
this.grabbedItemPos = { appIndex: result.targetIndex };
requestAnimationFrame(() => {
focusAppMenuItem(this.el, layout, result.targetIndex);
});
}
}
navigateFocusByKeyboard(appIndex, offset) {
var _a, _b, _c;
const layout = (_a = this.layout) !== null && _a !== void 0 ? _a : 'list';
const targetIdx = getTargetFocusIndex(appIndex, offset, (_c = (_b = this.apps) === null || _b === void 0 ? void 0 : _b.length) !== null && _c !== void 0 ? _c : 0);
if (targetIdx !== null) {
requestAnimationFrame(() => {
focusAppMenuItem(this.el, layout, targetIdx);
});
}
}
isGrabbed(appIndex) {
var _a;
return ((_a = this.grabbedItemPos) === null || _a === void 0 ? void 0 : _a.appIndex) === appIndex;
}
// Release the grab when keyboard focus moves out of the grabbed row,
// otherwise the previously grabbed row keeps its outline alongside the
// newly focused one (e.g., after pressing Tab).
handleRowFocusOut(e, appIndex) {
if (!this.isGrabbed(appIndex))
return;
const row = e.currentTarget;
const next = e.relatedTarget;
if (row && next && row.contains(next))
return;
this.grabbedItemPos = null;
}
isDragSource(appIndex) {
var _a;
return ((_a = this.draggedItemPos) === null || _a === void 0 ? void 0 : _a.appIndex) === appIndex;
}
handleDragStart(e, appIndex) {
if (!this.isEditMode)
return;
this.draggedItemPos = { appIndex };
if (e.dataTransfer) {
e.dataTransfer.effectAllowed = 'move';
}
}
handleDragOver(e) {
if (!this.isEditMode)
return;
e.preventDefault();
}
handleDragEnter(e, appIndex) {
if (!this.isEditMode || !this.draggedItemPos)
return;
e.preventDefault();
this.dropTargetIndex =
this.draggedItemPos.appIndex !== appIndex ? appIndex : null;
}
handleDragEnd() {
this.draggedItemPos = null;
this.dropTargetIndex = null;
}
handleDragLeave(e) {
const container = e.currentTarget;
const related = e.relatedTarget;
if (!related || !container.contains(related)) {
this.dropTargetIndex = null;
}
}
handleDrop(e, targetAppIndex) {
var _a;
if (!this.isEditMode || !this.draggedItemPos)
return;
e.preventDefault();
e.stopPropagation();
const apps = [...((_a = this.apps) !== null && _a !== void 0 ? _a : [])];
const { appIndex } = this.draggedItemPos;
const [movedItem] = apps.splice(appIndex, 1);
if (!movedItem)
return;
apps.splice(targetAppIndex, 0, movedItem);
this.apps = apps;
this.draggedItemPos = null;
this.dropTargetIndex = null;
}
handleContainerDrop(e) {
var _a;
if (!this.isEditMode || !this.draggedItemPos)
return;
e.preventDefault();
const apps = [...((_a = this.apps) !== null && _a !== void 0 ? _a : [])];
const { appIndex } = this.draggedItemPos;
const [movedItem] = apps.splice(appIndex, 1);
if (!movedItem)
return;
apps.push(movedItem);
this.apps = apps;
this.draggedItemPos = null;
this.dropTargetIndex = null;
}
renderListLayout() {
var _a;
const apps = (_a = this.apps) !== null && _a !== void 0 ? _a : [];
return (index.h("div", { class: "modus-wc-app-menu-items", onDragLeave: (e) => this.handleDragLeave(e), onDragOver: (e) => this.handleDragOver(e), onDrop: (e) => this.handleContainerDrop(e) }, index.h("modus-wc-menu", null, apps.map((app, appIndex) => (index.h("div", { "aria-label": this.getDisplayName(app.appName), "aria-roledescription": this.isEditMode ? 'reorderable item' : undefined, class: `modus-wc-app-menu-item-row ${this.isEditMode ? 'modus-wc-app-menu-draggable-item' : ''} ${this.isGrabbed(appIndex) ? 'modus-wc-app-menu-grabbed-item' : ''} ${this.isDragSource(appIndex) ? 'modus-wc-app-menu-drag-source' : ''} ${this.dropTargetIndex === appIndex ? 'modus-wc-app-menu-drop-target' : ''}`, draggable: this.isEditMode, onClick: () => {
if (!this.isEditMode) {
this.itemClick.emit({ appName: app.appName });
}
}, onDragEnd: () => this.handleDragEnd(), onDragEnter: (e) => this.handleDragEnter(e, appIndex), onDragOver: (e) => this.handleDragOver(e), onDragStart: (e) => this.handleDragStart(e, appIndex), onDrop: (e) => this.handleDrop(e, appIndex), onFocusout: (e) => this.handleRowFocusOut(e, appIndex), onKeyDown: (e) => this.handleKeyDown(e, appIndex), role: this.isEditMode ? 'option' : 'listitem', tabindex: this.isEditMode ? 0 : -1 }, this.isEditMode && (index.h("modus-wc-icon", { name: "drag_indicator", "custom-class": "modus-wc-app-menu-drag-icon", size: "xs" })), index.h("modus-wc-menu-item", { label: this.getDisplayName(app.appName), tooltipContent: this.truncatedApps.has(app.appName)
? this.getDisplayName(app.appName)
: undefined, tooltipPosition: "auto", onItemSelect: (e) => e.stopPropagation() }, index.h("modus-wc-logo", { name: app.appName, "custom-class": "modus-wc-app-menu-app-logo", emblem: true, slot: "start-icon" }))))))));
}
renderGridLayout() {
var _a;
const apps = (_a = this.apps) !== null && _a !== void 0 ? _a : [];
return (index.h("div", { class: "modus-wc-app-menu-grid", onDragLeave: (e) => this.handleDragLeave(e), onDragOver: (e) => this.handleDragOver(e), onDrop: (e) => this.handleContainerDrop(e) }, index.h("div", { class: "modus-wc-app-menu-grid-row", role: this.isEditMode ? 'listbox' : 'list' }, apps.map((app, appIndex) => (
// Intentionally unkeyed: Stencil's keyed reconciliation triggers
// insertBefore on modus-wc-app-menu-grid-item DOM nodes during a reorder, which fires
// a transient disconnectedCallback on descendant custom elements.
// modus-wc-tooltip only sets up its popper/popover in
// componentDidLoad (runs once), so after the disconnect/reconnect
// its popover is detached from document.body and hover stops
// showing the tooltip for any moved item. Reusing nodes by
// position keeps the tooltip subtree connected.
index.h("div", { "aria-label": this.getDisplayName(app.appName), "aria-roledescription": this.isEditMode ? 'reorderable item' : undefined, class: `modus-wc-app-menu-grid-item ${this.isEditMode ? 'modus-wc-app-menu-draggable-item' : ''} ${this.isGrabbed(appIndex) ? 'modus-wc-app-menu-grabbed-item' : ''} ${this.isDragSource(appIndex) ? 'modus-wc-app-menu-drag-source' : ''} ${this.dropTargetIndex === appIndex ? 'modus-wc-app-menu-drop-target' : ''}`, draggable: this.isEditMode, onClick: () => {
if (!this.isEditMode) {
this.itemClick.emit({ appName: app.appName });
}
}, onDragEnd: () => this.handleDragEnd(), onDragEnter: (e) => this.handleDragEnter(e, appIndex), onDragOver: (e) => this.handleDragOver(e), onDragStart: (e) => this.handleDragStart(e, appIndex), onDrop: (e) => this.handleDrop(e, appIndex), onFocusout: (e) => this.handleRowFocusOut(e, appIndex), onKeyDown: (e) => this.handleKeyDown(e, appIndex), role: this.isEditMode ? 'option' : 'listitem', tabindex: 0 }, this.isEditMode && (index.h("modus-wc-icon", { name: "drag_indicator", "custom-class": "modus-wc-app-menu-drag-icon", size: "xs" })), index.h("modus-wc-logo", { name: app.appName, "custom-class": "modus-wc-app-menu-grid-emblem", emblem: true }), index.h("modus-wc-tooltip", { content: this.getDisplayName(app.appName), disabled: !this.truncatedApps.has(app.appName), position: "auto" }, index.h("modus-wc-typography", { "custom-class": "modus-wc-app-menu-grid-item-label", size: "sm", label: this.getDisplayName(app.appName) }))))))));
}
getClasses() {
const classList = ['modus-wc-app-menu'];
// Append any consumer-provided class alongside the base component class.
if (this.customClass)
classList.push(this.customClass);
return classList.join(' ');
}
render() {
return (index.h(index.Host, Object.assign({ key: 'a51b600d32deb9b1c554382d2011c39a0392bddb' }, this.inheritedAttributes, { class: this.getClasses() }), index.h("modus-wc-panel", { key: 'b1374169662b8c55102b28c29bf814a2f9624ddb', height: "568px", width: "320px" }, index.h("div", { key: '093037f4f705b9151e5914862e89eb888e5a98bd', slot: "body" }, index.h("div", { key: 'fb47edc9a60a1f23c5426c83d558bc739cdf1e39', class: "modus-wc-app-menu-header" }, index.h("div", { key: '9a341355eadd42521e25cd64afa18ac7d88c4da3', class: "modus-wc-app-menu-header-title" }, index.h("modus-wc-typography", { key: 'c610e3af5b8374a22a1c2730387def677498928d', size: "2xl", weight: "semibold", hierarchy: "h3", label: this.isEditMode ? 'Edit' : 'Trimble Apps' })), index.h("div", { key: 'f66e9c0044054497628e90bca7aae50fdebe483c', class: "modus-wc-app-menu-header-end-content" }, !this.isEditMode ? (index.h("modus-wc-button", { "aria-label": "Edit app order", shape: "square", size: "sm", variant: "filled", color: "tertiary", onButtonClick: () => this.handleEdit() }, index.h("modus-wc-icon", { name: "pencil", variant: "solid" }))) : ([
index.h("modus-wc-button", { size: "sm", color: "tertiary", onButtonClick: () => this.handleCancel() }, "Cancel"),
index.h("modus-wc-button", { size: "sm", color: "primary", onButtonClick: () => this.handleDone() }, "Done"),
]))), index.h("div", { key: '290ad2a3985cc6ab149b90b6bbd2876a29dbaa5a', class: "modus-wc-app-menu-body" }, this.layout === 'list'
? this.renderListLayout()
: this.renderGridLayout())))));
}
get el() { return index.getElement(this); }
static get watchers() { return {
"apps": ["onAppsChange"],
"layout": ["onLayoutChange"]
}; }
};
ModusWcAppMenu.style = modusWcAppMenuCss;
exports.modus_wc_app_menu = ModusWcAppMenu;