dockview-core
Version:
Zero dependency layout manager supporting tabs, groups, grids and splitviews for vanilla TypeScript
314 lines (313 loc) • 12.3 kB
JavaScript
;
var __values = (this && this.__values) || function(o) {
var s = typeof Symbol === "function" && Symbol.iterator, m = s && o[s], i = 0;
if (m) return m.call(o);
if (o && typeof o.length === "number") return {
next: function () {
if (o && i >= o.length) o = void 0;
return { value: o && o[i++], done: !o };
}
};
throw new TypeError(s ? "Object is not iterable." : "Symbol.iterator is not defined.");
};
var __read = (this && this.__read) || function (o, n) {
var m = typeof Symbol === "function" && o[Symbol.iterator];
if (!m) return o;
var i = m.call(o), r, ar = [], e;
try {
while ((n === void 0 || n-- > 0) && !(r = i.next()).done) ar.push(r.value);
}
catch (error) { e = { error: error }; }
finally {
try {
if (r && !r.done && (m = i["return"])) m.call(i);
}
finally { if (e) throw e.error; }
}
return ar;
};
var __spreadArray = (this && this.__spreadArray) || function (to, from, pack) {
if (pack || arguments.length === 2) for (var i = 0, l = from.length, ar; i < l; i++) {
if (ar || !(i in from)) {
if (!ar) ar = Array.prototype.slice.call(from, 0, i);
ar[i] = from[i];
}
}
return to.concat(ar || Array.prototype.slice.call(from));
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.ContextMenuController = void 0;
var dom_1 = require("../dom");
function popoverZIndexFor(target) {
if (!(target instanceof HTMLElement)) {
return undefined;
}
// Floating overlays live in the shell as siblings of the popover anchor
// and the AriaLevelTracker sets their inline z-index. Without this, a
// popover opened from inside a floating group would render behind it
// because they share the shell stacking context.
var relativeParent = (0, dom_1.findRelativeZIndexParent)(target);
return (relativeParent === null || relativeParent === void 0 ? void 0 : relativeParent.style.zIndex)
? "calc(".concat(relativeParent.style.zIndex, " * 2)")
: undefined;
}
var _nextId = 0;
var nextContextMenuItemId = function () { return "dv-ctx-menu-item-".concat(_nextId++); };
function isItemConfig(item) {
return typeof item === 'object';
}
function buildItem(label, close, action, disabled) {
var el = document.createElement('div');
el.className = 'dv-context-menu-item';
el.setAttribute('role', 'menuitem');
if (disabled) {
el.classList.add('dv-context-menu-item--disabled');
el.setAttribute('aria-disabled', 'true');
}
el.textContent = label;
if (!disabled) {
el.addEventListener('click', function () {
action();
close();
});
}
return el;
}
function buildSeparator() {
var el = document.createElement('div');
el.className = 'dv-context-menu-separator';
el.setAttribute('role', 'separator');
return el;
}
function isCoarsePrimaryInput() {
if (typeof window === 'undefined' || !window.matchMedia) {
return false;
}
var coarse = window.matchMedia('(pointer: coarse)').matches;
var fine = window.matchMedia('(pointer: fine)').matches;
return coarse && !fine;
}
function buildRenameInput(tabGroup) {
var wrapper = document.createElement('div');
wrapper.className = 'dv-context-menu-rename';
var input = document.createElement('input');
input.className = 'dv-context-menu-rename-input';
input.type = 'text';
input.placeholder = 'Name This Group';
input.value = tabGroup.label;
input.addEventListener('input', function () {
tabGroup.setLabel(input.value);
});
input.addEventListener('keydown', function (e) {
if (e.key !== 'Escape' && e.key !== 'Enter') {
e.stopPropagation();
}
});
input.addEventListener('click', function (e) {
e.stopPropagation();
});
wrapper.appendChild(input);
// Skip auto-focus on touch-primary devices: focusing the input pops the
// on-screen keyboard, which fires `window resize`, which `PopupService`
// listens to and uses to dismiss the popover — so the menu opens, the
// keyboard appears, and the menu immediately closes before the user can
// type. The user can still tap the input to focus it intentionally.
if (!isCoarsePrimaryInput()) {
requestAnimationFrame(function () {
input.focus();
input.select();
});
}
return wrapper;
}
function buildColorPicker(tabGroup, palette) {
var e_1, _a;
var wrapper = document.createElement('div');
wrapper.className = 'dv-context-menu-color-picker';
if (!palette.enabled) {
// Opt-out: render no swatches. Returning a wrapper rather than null
// keeps the call site simple; the wrapper is empty and visually inert.
return wrapper;
}
var _loop_1 = function (entry) {
var swatch = document.createElement('div');
swatch.className = 'dv-context-menu-color-swatch';
// Use a CSS custom property rather than setting `backgroundColor`
// directly: the IDL setter validates the value against a color
// grammar and rejects `var(...)` references in some environments
// (notably jsdom; some browsers have historically had similar
// quirks). The matching SCSS rule reads the var at use time.
swatch.style.setProperty('--dv-tab-group-color', entry.value);
if (entry.label) {
swatch.title = entry.label;
}
if (tabGroup.color === entry.id) {
swatch.classList.add('dv-context-menu-color-swatch--selected');
}
swatch.addEventListener('click', function () {
tabGroup.setColor(entry.id);
});
wrapper.appendChild(swatch);
};
try {
for (var _b = __values(palette.entries()), _c = _b.next(); !_c.done; _c = _b.next()) {
var entry = _c.value;
_loop_1(entry);
}
}
catch (e_1_1) { e_1 = { error: e_1_1 }; }
finally {
try {
if (_c && !_c.done && (_a = _b.return)) _a.call(_b);
}
finally { if (e_1) throw e_1.error; }
}
return wrapper;
}
var ContextMenuController = /** @class */ (function () {
function ContextMenuController(accessor) {
this.accessor = accessor;
}
ContextMenuController.prototype.show = function (panel, group, event) {
var e_2, _a;
var _b, _c;
if (!this.accessor.options.getTabContextMenuItems) {
return;
}
var items = this.accessor.options.getTabContextMenuItems({
panel: panel,
group: group,
api: this.accessor.api,
event: event,
});
if (items.length === 0) {
return;
}
event.preventDefault();
var popupService = this.accessor.getPopupServiceForGroup(group);
var close = function () { return popupService.close(); };
var menuEl = document.createElement('div');
menuEl.className = 'dv-context-menu';
menuEl.setAttribute('role', 'menu');
var _loop_2 = function (item) {
if (item === 'separator') {
menuEl.appendChild(buildSeparator());
}
else if (item === 'close') {
menuEl.appendChild(buildItem('Close', close, function () { return panel.api.close(); }));
}
else if (item === 'closeOthers') {
menuEl.appendChild(buildItem('Close Others', close, function () {
group.panels
.filter(function (p) { return p !== panel; })
.forEach(function (p) { return p.api.close(); });
}));
}
else if (item === 'closeAll') {
menuEl.appendChild(buildItem('Close All', close, function () {
__spreadArray([], __read(group.panels), false).forEach(function (p) { return p.api.close(); });
}));
}
else if (isItemConfig(item) && item.element) {
menuEl.appendChild(item.element);
}
else if (isItemConfig(item) && item.component) {
var renderer = (_c = (_b = this_1.accessor.options).createContextMenuItemComponent) === null || _c === void 0 ? void 0 : _c.call(_b, {
id: nextContextMenuItemId(),
component: item.component,
});
if (renderer) {
renderer.init({
panel: panel,
group: group,
api: this_1.accessor.api,
close: close,
componentProps: item.componentProps,
});
menuEl.appendChild(renderer.element);
}
}
else if (isItemConfig(item) && item.label) {
menuEl.appendChild(buildItem(item.label, close, function () { var _a; return (_a = item.action) === null || _a === void 0 ? void 0 : _a.call(item); }, item.disabled));
}
};
var this_1 = this;
try {
for (var items_1 = __values(items), items_1_1 = items_1.next(); !items_1_1.done; items_1_1 = items_1.next()) {
var item = items_1_1.value;
_loop_2(item);
}
}
catch (e_2_1) { e_2 = { error: e_2_1 }; }
finally {
try {
if (items_1_1 && !items_1_1.done && (_a = items_1.return)) _a.call(items_1);
}
finally { if (e_2) throw e_2.error; }
}
popupService.openPopover(menuEl, {
x: event.clientX,
y: event.clientY,
zIndex: popoverZIndexFor(event.target),
});
};
ContextMenuController.prototype.showForChip = function (tabGroup, group, event) {
var e_3, _a;
if (!this.accessor.options.getTabGroupChipContextMenuItems) {
return;
}
var items = this.accessor.options.getTabGroupChipContextMenuItems({
tabGroup: tabGroup,
group: group,
api: this.accessor.api,
event: event,
});
if (items.length === 0) {
return;
}
event.preventDefault();
var popupService = this.accessor.getPopupServiceForGroup(group);
var close = function () { return popupService.close(); };
var menuEl = document.createElement('div');
menuEl.className = 'dv-context-menu';
menuEl.setAttribute('role', 'menu');
var _loop_3 = function (item) {
if (item === 'separator') {
menuEl.appendChild(buildSeparator());
}
else if (item === 'rename') {
menuEl.appendChild(buildRenameInput(tabGroup));
}
else if (item === 'colorPicker') {
menuEl.appendChild(buildColorPicker(tabGroup, this_2.accessor.tabGroupColorPalette));
}
else if (isItemConfig(item) && item.element) {
menuEl.appendChild(item.element);
}
else if (isItemConfig(item) && item.label) {
menuEl.appendChild(buildItem(item.label, close, function () { var _a; return (_a = item.action) === null || _a === void 0 ? void 0 : _a.call(item); }, item.disabled));
}
};
var this_2 = this;
try {
for (var items_2 = __values(items), items_2_1 = items_2.next(); !items_2_1.done; items_2_1 = items_2.next()) {
var item = items_2_1.value;
_loop_3(item);
}
}
catch (e_3_1) { e_3 = { error: e_3_1 }; }
finally {
try {
if (items_2_1 && !items_2_1.done && (_a = items_2.return)) _a.call(items_2);
}
finally { if (e_3) throw e_3.error; }
}
popupService.openPopover(menuEl, {
x: event.clientX,
y: event.clientY,
zIndex: popoverZIndexFor(event.target),
});
};
return ContextMenuController;
}());
exports.ContextMenuController = ContextMenuController;