@cplieger/web-terminal-ui 3.0.0 → 3.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +1 -1
- package/src/features/context-menu.ts +28 -7
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@cplieger/web-terminal-ui",
|
|
3
|
-
"version": "3.0.
|
|
3
|
+
"version": "3.0.1",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "Reference touch-first browser UI for @cplieger/web-terminal-engine: a composable kernel + feature modules (tabs, activity monitor, mobile toolbar, context menu, IME, predictive echo, viewport handling) and CSS. One createTerminal() call.",
|
|
6
6
|
"license": "GPL-3.0-or-later",
|
|
@@ -18,6 +18,10 @@ import type { ClipboardApi } from "./clipboard.js";
|
|
|
18
18
|
const LONG_PRESS_MS = 500;
|
|
19
19
|
const LONG_PRESS_MOVE_PX = 10;
|
|
20
20
|
const EDGE_MARGIN = 8;
|
|
21
|
+
// When the menu can't fit below the anchor (a long-press near the bottom edge or
|
|
22
|
+
// above the keyboard), it opens above the point. This gap lifts it clear of the
|
|
23
|
+
// fingertip so the touch does not cover the menu items.
|
|
24
|
+
const MENU_GAP = 16;
|
|
21
25
|
|
|
22
26
|
export interface ContextMenuOptions {
|
|
23
27
|
/** The clipboard feature value, so the menu can offer Copy/Paste through its
|
|
@@ -91,16 +95,33 @@ export function contextMenu(opts: ContextMenuOptions = {}): TerminalFeature {
|
|
|
91
95
|
if (menu.childElementCount === 0) {
|
|
92
96
|
return; // nothing to offer
|
|
93
97
|
}
|
|
94
|
-
// Make visible (so it has measurable size) then
|
|
95
|
-
// viewport. position:fixed => x/y are viewport coordinates
|
|
98
|
+
// Make visible (so it has measurable size), then place it within the
|
|
99
|
+
// VISIBLE viewport. position:fixed => x/y are viewport coordinates; the
|
|
100
|
+
// visual viewport (when present) is the area above the on-screen
|
|
101
|
+
// keyboard, so clamping to it keeps the menu off the keyboard too.
|
|
96
102
|
menu.classList.add("visible");
|
|
103
|
+
const vv = window.visualViewport;
|
|
104
|
+
const viewLeft = vv ? vv.offsetLeft : 0;
|
|
105
|
+
const viewTop = vv ? vv.offsetTop : 0;
|
|
106
|
+
const viewWidth = vv ? vv.width : window.innerWidth;
|
|
107
|
+
const viewHeight = vv ? vv.height : window.innerHeight;
|
|
108
|
+
const menuW = menu.offsetWidth;
|
|
109
|
+
const menuH = menu.offsetHeight;
|
|
97
110
|
const left = Math.max(
|
|
98
|
-
EDGE_MARGIN,
|
|
99
|
-
Math.min(x,
|
|
111
|
+
viewLeft + EDGE_MARGIN,
|
|
112
|
+
Math.min(x, viewLeft + viewWidth - menuW - EDGE_MARGIN),
|
|
100
113
|
);
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
114
|
+
// Open just below the point; but if that would overflow the visible
|
|
115
|
+
// bottom (a long-press near the bottom edge, where the user naturally
|
|
116
|
+
// taps), flip above the finger so the menu is neither clipped off-screen
|
|
117
|
+
// nor hidden under the fingertip.
|
|
118
|
+
let top = y;
|
|
119
|
+
if (y + menuH + EDGE_MARGIN > viewTop + viewHeight) {
|
|
120
|
+
top = y - menuH - MENU_GAP;
|
|
121
|
+
}
|
|
122
|
+
top = Math.max(
|
|
123
|
+
viewTop + EDGE_MARGIN,
|
|
124
|
+
Math.min(top, viewTop + viewHeight - menuH - EDGE_MARGIN),
|
|
104
125
|
);
|
|
105
126
|
menu.style.left = `${String(left)}px`;
|
|
106
127
|
menu.style.top = `${String(top)}px`;
|