@celestia-island/hikari 0.55.43 → 0.55.44

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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@celestia-island/hikari",
3
- "version": "0.55.43",
3
+ "version": "0.55.44",
4
4
  "private": false,
5
5
  "type": "module",
6
6
  "description": "Hikari Vue 3 component library — production-grade UI components based on shittim-chest design system",
@@ -385,6 +385,22 @@ describe("useContextMenu + HkContextMenuProvider", () => {
385
385
  unbind();
386
386
  });
387
387
 
388
+ it("keeps real right-clicks working inside the suppression window (only long-press synthesis is swallowed)", () => {
389
+ const target = document.createElement("div");
390
+ target.setAttribute("data-bind-target", "");
391
+ document.body.appendChild(target);
392
+ const { opens, api } = mockApi();
393
+ const unbind = bindContextMenu(target, api, (point) => {
394
+ return { x: point.x, y: point.y, items: [{ key: "a", label: "A" }] };
395
+ });
396
+ // Two genuine pointer right-clicks, ~0ms apart: BOTH open (a
397
+ // desktop user re-aiming fast must not lose the second menu).
398
+ target.dispatchEvent(new MouseEvent("contextmenu", { bubbles: true, cancelable: true, clientX: 10, clientY: 10 }));
399
+ target.dispatchEvent(new MouseEvent("contextmenu", { bubbles: true, cancelable: true, clientX: 30, clientY: 30 }));
400
+ expect(opens).toHaveLength(2);
401
+ unbind();
402
+ });
403
+
388
404
  it("lets a null build defer to outer bindings without preventing", () => {
389
405
  const target = document.createElement("div");
390
406
  target.setAttribute("data-bind-target", "");
@@ -110,6 +110,12 @@ export function bindContextMenu(
110
110
  let holdTimer: ReturnType<typeof setTimeout> | null = null;
111
111
  let holdOrigin: { x: number; y: number } | null = null;
112
112
  let lastOpenedAt = 0;
113
+ /** The kind that last opened a menu — the suppression window only
114
+ * guards the LONG-PRESS path (platforms synthesize a native
115
+ * contextmenu right after ours). A desktop user right-clicking twice
116
+ * within the window is two REAL gestures: the second one must open
117
+ * its own menu, never be swallowed. */
118
+ let lastOpenKind: ContextTriggerPoint["kind"] | null = null;
113
119
 
114
120
  const cancelHold = () => {
115
121
  if (holdTimer !== null) {
@@ -123,6 +129,7 @@ export function bindContextMenu(
123
129
  const request = build(point);
124
130
  if (!request) return false;
125
131
  lastOpenedAt = performance.now();
132
+ lastOpenKind = point.kind;
126
133
  api.open(request);
127
134
  return true;
128
135
  };
@@ -130,7 +137,10 @@ export function bindContextMenu(
130
137
  const onContextMenu = (event: MouseEvent) => {
131
138
  // Our own long-press just opened the menu; a synthesized native
132
139
  // event for the same gesture must not re-open (or replace) it.
133
- if (performance.now() - lastOpenedAt < CONTEXT_SUPPRESS_MS) {
140
+ if (
141
+ lastOpenKind === "longpress"
142
+ && performance.now() - lastOpenedAt < CONTEXT_SUPPRESS_MS
143
+ ) {
134
144
  event.preventDefault();
135
145
  return;
136
146
  }