UNPKG

dockview-core

Version:

Zero dependency layout manager supporting tabs, groups, grids and splitviews for vanilla TypeScript

254 lines (253 loc) 12.4 kB
"use strict"; var __extends = (this && this.__extends) || (function () { var extendStatics = function (d, b) { extendStatics = Object.setPrototypeOf || ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || function (d, b) { for (var p in b) if (Object.prototype.hasOwnProperty.call(b, p)) d[p] = b[p]; }; return extendStatics(d, b); }; return function (d, b) { if (typeof b !== "function" && b !== null) throw new TypeError("Class extends value " + String(b) + " is not a constructor or null"); extendStatics(d, b); function __() { this.constructor = d; } d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); }; })(); var __assign = (this && this.__assign) || function () { __assign = Object.assign || function(t) { for (var s, i = 1, n = arguments.length; i < n; i++) { s = arguments[i]; for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p)) t[p] = s[p]; } return t; }; return __assign.apply(this, arguments); }; Object.defineProperty(exports, "__esModule", { value: true }); exports.Tab = void 0; var events_1 = require("../../../events"); var lifecycle_1 = require("../../../lifecycle"); var dataTransfer_1 = require("../../../dnd/dataTransfer"); var dom_1 = require("../../../dom"); var backend_1 = require("../../../dnd/backend"); var longPress_1 = require("../../../dnd/pointer/longPress"); var dndCapabilities_1 = require("../../dndCapabilities"); var Tab = /** @class */ (function (_super) { __extends(Tab, _super); function Tab(panel, accessor, group) { var _this = _super.call(this) || this; _this.panel = panel; _this.accessor = accessor; _this.group = group; _this.content = undefined; _this.panelTransfer = dataTransfer_1.LocalSelectionTransfer.getInstance(); _this._direction = 'horizontal'; _this._onPointDown = new events_1.Emitter(); _this.onPointerDown = _this._onPointDown.event; _this._onTabClick = new events_1.Emitter(); _this.onTabClick = _this._onTabClick.event; _this._onDropped = new events_1.Emitter(); _this.onDrop = _this._onDropped.event; _this._onDragStart = new events_1.Emitter(); _this.onDragStart = _this._onDragStart.event; _this._onDragEnd = new events_1.Emitter(); _this.onDragEnd = _this._onDragEnd.event; var caps = (0, dndCapabilities_1.resolveDndCapabilities)(_this.accessor.options); _this._element = document.createElement('div'); _this._element.className = 'dv-tab'; _this._element.tabIndex = 0; _this._element.draggable = caps.html5; (0, dom_1.toggleClass)(_this.element, 'dv-inactive-tab', true); var canDisplayOverlay = function (event, position) { var _a; if (_this.group.locked) { return false; } var data = (0, dataTransfer_1.getPanelData)(); if (data && _this.accessor.id === data.viewId) { // Smooth-reorder takes over the in-flight visual when active, // so individual tab overlays are suppressed for internal drags. if (((_a = _this.accessor.options.theme) === null || _a === void 0 ? void 0 : _a.tabAnimation) === 'smooth') { return false; } return true; } return _this.group.model.canDisplayOverlay(event, position, 'tab'); }; _this.dropTarget = backend_1.html5Backend.createDropTarget(_this._element, { acceptedTargetZones: ['left', 'right'], overlayModel: _this._buildOverlayModel(), canDisplayOverlay: canDisplayOverlay, getOverrideTarget: function () { var _a; return (_a = group.model.dropTargetContainer) === null || _a === void 0 ? void 0 : _a.model; }, }); _this.pointerDropTarget = backend_1.pointerBackend.createDropTarget(_this._element, { acceptedTargetZones: ['left', 'right'], overlayModel: _this._buildOverlayModel(), canDisplayOverlay: canDisplayOverlay, getOverrideTarget: function () { var _a; return (_a = group.model.dropTargetContainer) === null || _a === void 0 ? void 0 : _a.model; }, }); var sharedDragOptions = { getData: function () { _this.panelTransfer.setData([ new dataTransfer_1.PanelTransfer(_this.accessor.id, _this.group.id, _this.panel.id), ], dataTransfer_1.PanelTransfer.prototype); return { dispose: function () { _this.panelTransfer.clearData(dataTransfer_1.PanelTransfer.prototype); }, }; }, // 30/-10 matches the HTML5 setDragImage offset that has been // shipped for years; pointer backend wraps in PointerGhost, // HTML5 backend feeds into setDragImage. createGhost: function () { return ({ element: _this._buildGhostElement(), offsetX: 30, offsetY: -10, }); }, onDragStart: function (event) { var _a; _this._onDragStart.fire(event); if (!(event instanceof PointerEvent) && ((_a = _this.accessor.options.theme) === null || _a === void 0 ? void 0 : _a.tabAnimation) === 'smooth') { // Delay collapse to next frame so the browser // captures the full drag image first. requestAnimationFrame(function () { (0, dom_1.toggleClass)(_this.element, 'dv-tab--dragging', true); }); } }, onDragEnd: function (event) { _this._onDragEnd.fire(event); }, }; _this.html5DragSource = backend_1.html5Backend.createDragSource(_this._element, __assign(__assign({}, sharedDragOptions), { disabled: !caps.html5 })); _this.pointerDragSource = backend_1.pointerBackend.createDragSource(_this._element, __assign(__assign({}, sharedDragOptions), { disabled: !caps.pointer, touchOnly: !caps.pointerHandlesMouse, isCancelled: function () { return !(0, dndCapabilities_1.resolveDndCapabilities)(_this.accessor.options).pointer; } })); // Both droptargets feed the same downstream stream; consumers don't // need to know which path produced the overlay. _this.onWillShowOverlay = events_1.Event.any(_this.dropTarget.onWillShowOverlay, _this.pointerDropTarget.onWillShowOverlay); _this.addDisposables(_this._onPointDown, _this._onTabClick, _this._onDropped, _this._onDragStart, _this._onDragEnd, _this.accessor.onDidOptionsChange(function () { var model = _this._buildOverlayModel(); _this.dropTarget.setOverlayModel(model); _this.pointerDropTarget.setOverlayModel(model); }), (0, events_1.addDisposableListener)(_this._element, 'dragend', function () { // The shared onDragEnd handler already fires _onDragEnd via // the HTML5 backend; just strip the dragging class here. (0, dom_1.toggleClass)(_this.element, 'dv-tab--dragging', false); }), _this.html5DragSource, (0, events_1.addDisposableListener)(_this._element, 'pointerdown', function (event) { _this._onPointDown.fire(event); }), (0, events_1.addDisposableListener)(_this._element, 'click', function (event) { _this._onTabClick.fire(event); }), (0, events_1.addDisposableListener)(_this._element, 'contextmenu', function (event) { _this.accessor.contextMenuController.show(_this.panel, _this.group, event); }), new longPress_1.LongPressDetector(_this._element, { onLongPress: function (event) { // Don't let a subsequent finger move arm a drag on top // of the just-opened menu. _this.pointerDragSource.cancelPending(); _this.accessor.contextMenuController.show(_this.panel, _this.group, event); }, }), _this.dropTarget.onDrop(function (event) { _this._onDropped.fire(event); }), _this.pointerDropTarget.onDrop(function (event) { _this._onDropped.fire(event); }), _this.dropTarget, _this.pointerDropTarget, _this.pointerDragSource); return _this; } Object.defineProperty(Tab.prototype, "element", { get: function () { return this._element; }, enumerable: false, configurable: true }); Tab.prototype.setActive = function (isActive) { (0, dom_1.toggleClass)(this.element, 'dv-active-tab', isActive); (0, dom_1.toggleClass)(this.element, 'dv-inactive-tab', !isActive); }; Tab.prototype.setContent = function (part) { if (this.content) { this._element.removeChild(this.content.element); } this.content = part; this._element.appendChild(this.content.element); }; Tab.prototype._buildOverlayModel = function () { var _a; // 'line' themes render a 4px insertion strip at the tab edge via the // anchor container's small-boundary path. 'fill' themes render a // half-width highlighted area, so we disable the small-boundary path // entirely (boundary = 0 ⟹ isSmall always false). var smallBoundary = ((_a = this.accessor.options.theme) === null || _a === void 0 ? void 0 : _a.dndTabIndicator) === 'line' ? Number.POSITIVE_INFINITY : 0; return { activationSize: { value: 50, type: 'percentage' }, smallWidthBoundary: smallBoundary, smallHeightBoundary: smallBoundary, }; }; Tab.prototype.setDirection = function (direction) { this._direction = direction; var zones = direction === 'vertical' ? ['top', 'bottom'] : ['left', 'right']; this.dropTarget.setTargetZones(zones); this.pointerDropTarget.setTargetZones(zones); }; Tab.prototype.updateDragAndDropState = function () { var caps = (0, dndCapabilities_1.resolveDndCapabilities)(this.accessor.options); this._element.draggable = caps.html5; this.html5DragSource.setDisabled(!caps.html5); this.pointerDragSource.setDisabled(!caps.pointer); this.pointerDragSource.setTouchOnly(!caps.pointerHandlesMouse); }; /** * Vertical tabs are flipped to horizontal so the ghost stays readable * during the drag rather than appearing sideways-rotated. */ Tab.prototype._buildGhostElement = function () { var style = getComputedStyle(this.element); var newNode = this.element.cloneNode(true); var isVertical = this._direction === 'vertical'; var verticalSkip = new Set([ 'writing-mode', 'inline-size', 'block-size', 'min-inline-size', 'min-block-size', 'max-inline-size', 'max-block-size', 'margin-inline', 'margin-inline-start', 'margin-inline-end', 'margin-block', 'margin-block-start', 'margin-block-end', 'padding-inline', 'padding-inline-start', 'padding-inline-end', 'padding-block', 'padding-block-start', 'padding-block-end', ]); Array.from(style).forEach(function (key) { if (isVertical && verticalSkip.has(key)) { return; } newNode.style.setProperty(key, style.getPropertyValue(key), style.getPropertyPriority(key)); }); if (isVertical) { newNode.style.setProperty('writing-mode', 'horizontal-tb'); newNode.style.setProperty('width', style.height); newNode.style.setProperty('height', style.width); } newNode.style.position = 'absolute'; newNode.classList.add('dv-tab-ghost-drag'); return newNode; }; return Tab; }(lifecycle_1.CompositeDisposable)); exports.Tab = Tab;