Mission Control Dashboard - Initial implementation

This commit is contained in:
Daniel Arroyo
2026-03-27 18:36:05 +00:00
parent 257cea2c7d
commit a8fb4d4555
12516 changed files with 2307128 additions and 2 deletions

View File

@@ -0,0 +1,90 @@
import { isHTMLElement } from '../../utils/is-html-element.mjs';
import { isDragActive } from '../drag/state/is-active.mjs';
import { isNodeOrChild } from '../utils/is-node-or-child.mjs';
import { isPrimaryPointer } from '../utils/is-primary-pointer.mjs';
import { setupGesture } from '../utils/setup.mjs';
import { isElementKeyboardAccessible } from './utils/is-keyboard-accessible.mjs';
import { enableKeyboardPress } from './utils/keyboard.mjs';
import { isPressing } from './utils/state.mjs';
/**
* Filter out events that are not primary pointer events, or are triggering
* while a Motion gesture is active.
*/
function isValidPressEvent(event) {
return isPrimaryPointer(event) && !isDragActive();
}
const claimedPointerDownEvents = new WeakSet();
/**
* Create a press gesture.
*
* Press is different to `"pointerdown"`, `"pointerup"` in that it
* automatically filters out secondary pointer events like right
* click and multitouch.
*
* It also adds accessibility support for keyboards, where
* an element with a press gesture will receive focus and
* trigger on Enter `"keydown"` and `"keyup"` events.
*
* This is different to a browser's `"click"` event, which does
* respond to keyboards but only for the `"click"` itself, rather
* than the press start and end/cancel. The element also needs
* to be focusable for this to work, whereas a press gesture will
* make an element focusable by default.
*
* @public
*/
function press(targetOrSelector, onPressStart, options = {}) {
const [targets, eventOptions, cancelEvents] = setupGesture(targetOrSelector, options);
const startPress = (startEvent) => {
const target = startEvent.currentTarget;
if (!isValidPressEvent(startEvent))
return;
if (claimedPointerDownEvents.has(startEvent))
return;
isPressing.add(target);
if (options.stopPropagation) {
claimedPointerDownEvents.add(startEvent);
}
const onPressEnd = onPressStart(target, startEvent);
const onPointerEnd = (endEvent, success) => {
window.removeEventListener("pointerup", onPointerUp);
window.removeEventListener("pointercancel", onPointerCancel);
if (isPressing.has(target)) {
isPressing.delete(target);
}
if (!isValidPressEvent(endEvent)) {
return;
}
if (typeof onPressEnd === "function") {
onPressEnd(endEvent, { success });
}
};
const onPointerUp = (upEvent) => {
onPointerEnd(upEvent, target === window ||
target === document ||
options.useGlobalTarget ||
isNodeOrChild(target, upEvent.target));
};
const onPointerCancel = (cancelEvent) => {
onPointerEnd(cancelEvent, false);
};
window.addEventListener("pointerup", onPointerUp, eventOptions);
window.addEventListener("pointercancel", onPointerCancel, eventOptions);
};
targets.forEach((target) => {
const pointerDownTarget = options.useGlobalTarget ? window : target;
pointerDownTarget.addEventListener("pointerdown", startPress, eventOptions);
if (isHTMLElement(target)) {
target.addEventListener("focus", (event) => enableKeyboardPress(event, eventOptions));
if (!isElementKeyboardAccessible(target) &&
!target.hasAttribute("tabindex")) {
target.tabIndex = 0;
}
}
});
return cancelEvents;
}
export { press };
//# sourceMappingURL=index.mjs.map

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,34 @@
const keyboardAccessibleElements = new Set([
"BUTTON",
"INPUT",
"SELECT",
"TEXTAREA",
"A",
]);
/**
* Checks if an element is natively keyboard accessible (focusable).
* Used by the press gesture to determine if we need to add tabIndex.
*/
function isElementKeyboardAccessible(element) {
return (keyboardAccessibleElements.has(element.tagName) ||
element.isContentEditable === true);
}
const textInputElements = new Set(["INPUT", "SELECT", "TEXTAREA"]);
/**
* Checks if an element has text selection or direct interaction behavior
* that should block drag gestures from starting.
*
* This specifically targets form controls where the user might want to select
* text or interact with the control (e.g., sliders, dropdowns).
*
* Buttons and links are NOT included because they don't have click-and-move
* actions of their own - they only respond to click events, so dragging
* should still work when initiated from these elements.
*/
function isElementTextInput(element) {
return (textInputElements.has(element.tagName) ||
element.isContentEditable === true);
}
export { isElementKeyboardAccessible, isElementTextInput };
//# sourceMappingURL=is-keyboard-accessible.mjs.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"is-keyboard-accessible.mjs","sources":["../../../../../src/gestures/press/utils/is-keyboard-accessible.ts"],"sourcesContent":["const keyboardAccessibleElements = new Set([\n \"BUTTON\",\n \"INPUT\",\n \"SELECT\",\n \"TEXTAREA\",\n \"A\",\n])\n\n/**\n * Checks if an element is natively keyboard accessible (focusable).\n * Used by the press gesture to determine if we need to add tabIndex.\n */\nexport function isElementKeyboardAccessible(element: Element) {\n return (\n keyboardAccessibleElements.has(element.tagName) ||\n (element as HTMLElement).isContentEditable === true\n )\n}\n\nconst textInputElements = new Set([\"INPUT\", \"SELECT\", \"TEXTAREA\"])\n\n/**\n * Checks if an element has text selection or direct interaction behavior\n * that should block drag gestures from starting.\n *\n * This specifically targets form controls where the user might want to select\n * text or interact with the control (e.g., sliders, dropdowns).\n *\n * Buttons and links are NOT included because they don't have click-and-move\n * actions of their own - they only respond to click events, so dragging\n * should still work when initiated from these elements.\n */\nexport function isElementTextInput(element: Element) {\n return (\n textInputElements.has(element.tagName) ||\n (element as HTMLElement).isContentEditable === true\n )\n}\n"],"names":[],"mappings":"AAAA,MAAM,0BAA0B,GAAG,IAAI,GAAG,CAAC;IACvC,QAAQ;IACR,OAAO;IACP,QAAQ;IACR,UAAU;IACV,GAAG;AACN,CAAA,CAAC;AAEF;;;AAGG;AACG,SAAU,2BAA2B,CAAC,OAAgB,EAAA;IACxD,QACI,0BAA0B,CAAC,GAAG,CAAC,OAAO,CAAC,OAAO,CAAC;AAC9C,QAAA,OAAuB,CAAC,iBAAiB,KAAK,IAAI;AAE3D;AAEA,MAAM,iBAAiB,GAAG,IAAI,GAAG,CAAC,CAAC,OAAO,EAAE,QAAQ,EAAE,UAAU,CAAC,CAAC;AAElE;;;;;;;;;;AAUG;AACG,SAAU,kBAAkB,CAAC,OAAgB,EAAA;IAC/C,QACI,iBAAiB,CAAC,GAAG,CAAC,OAAO,CAAC,OAAO,CAAC;AACrC,QAAA,OAAuB,CAAC,iBAAiB,KAAK,IAAI;AAE3D;;;;"}

View File

@@ -0,0 +1,39 @@
import { isPressing } from './state.mjs';
/**
* Filter out events that are not "Enter" keys.
*/
function filterEvents(callback) {
return (event) => {
if (event.key !== "Enter")
return;
callback(event);
};
}
function firePointerEvent(target, type) {
target.dispatchEvent(new PointerEvent("pointer" + type, { isPrimary: true, bubbles: true }));
}
const enableKeyboardPress = (focusEvent, eventOptions) => {
const element = focusEvent.currentTarget;
if (!element)
return;
const handleKeydown = filterEvents(() => {
if (isPressing.has(element))
return;
firePointerEvent(element, "down");
const handleKeyup = filterEvents(() => {
firePointerEvent(element, "up");
});
const handleBlur = () => firePointerEvent(element, "cancel");
element.addEventListener("keyup", handleKeyup, eventOptions);
element.addEventListener("blur", handleBlur, eventOptions);
});
element.addEventListener("keydown", handleKeydown, eventOptions);
/**
* Add an event listener that fires on blur to remove the keydown events.
*/
element.addEventListener("blur", () => element.removeEventListener("keydown", handleKeydown), eventOptions);
};
export { enableKeyboardPress };
//# sourceMappingURL=keyboard.mjs.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"keyboard.mjs","sources":["../../../../../src/gestures/press/utils/keyboard.ts"],"sourcesContent":["import { isPressing } from \"./state\"\n\n/**\n * Filter out events that are not \"Enter\" keys.\n */\nfunction filterEvents(callback: (event: KeyboardEvent) => void) {\n return (event: KeyboardEvent) => {\n if (event.key !== \"Enter\") return\n callback(event)\n }\n}\n\nfunction firePointerEvent(target: EventTarget, type: \"down\" | \"up\" | \"cancel\") {\n target.dispatchEvent(\n new PointerEvent(\"pointer\" + type, { isPrimary: true, bubbles: true })\n )\n}\n\nexport const enableKeyboardPress = (\n focusEvent: FocusEvent,\n eventOptions: AddEventListenerOptions\n) => {\n const element = focusEvent.currentTarget as HTMLElement\n if (!element) return\n\n const handleKeydown = filterEvents(() => {\n if (isPressing.has(element)) return\n\n firePointerEvent(element, \"down\")\n\n const handleKeyup = filterEvents(() => {\n firePointerEvent(element, \"up\")\n })\n\n const handleBlur = () => firePointerEvent(element, \"cancel\")\n\n element.addEventListener(\"keyup\", handleKeyup, eventOptions)\n element.addEventListener(\"blur\", handleBlur, eventOptions)\n })\n\n element.addEventListener(\"keydown\", handleKeydown, eventOptions)\n\n /**\n * Add an event listener that fires on blur to remove the keydown events.\n */\n element.addEventListener(\n \"blur\",\n () => element.removeEventListener(\"keydown\", handleKeydown),\n eventOptions\n )\n}\n"],"names":[],"mappings":";;AAEA;;AAEG;AACH,SAAS,YAAY,CAAC,QAAwC,EAAA;IAC1D,OAAO,CAAC,KAAoB,KAAI;AAC5B,QAAA,IAAI,KAAK,CAAC,GAAG,KAAK,OAAO;YAAE;QAC3B,QAAQ,CAAC,KAAK,CAAC;AACnB,IAAA,CAAC;AACL;AAEA,SAAS,gBAAgB,CAAC,MAAmB,EAAE,IAA8B,EAAA;IACzE,MAAM,CAAC,aAAa,CAChB,IAAI,YAAY,CAAC,SAAS,GAAG,IAAI,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,OAAO,EAAE,IAAI,EAAE,CAAC,CACzE;AACL;MAEa,mBAAmB,GAAG,CAC/B,UAAsB,EACtB,YAAqC,KACrC;AACA,IAAA,MAAM,OAAO,GAAG,UAAU,CAAC,aAA4B;AACvD,IAAA,IAAI,CAAC,OAAO;QAAE;AAEd,IAAA,MAAM,aAAa,GAAG,YAAY,CAAC,MAAK;AACpC,QAAA,IAAI,UAAU,CAAC,GAAG,CAAC,OAAO,CAAC;YAAE;AAE7B,QAAA,gBAAgB,CAAC,OAAO,EAAE,MAAM,CAAC;AAEjC,QAAA,MAAM,WAAW,GAAG,YAAY,CAAC,MAAK;AAClC,YAAA,gBAAgB,CAAC,OAAO,EAAE,IAAI,CAAC;AACnC,QAAA,CAAC,CAAC;QAEF,MAAM,UAAU,GAAG,MAAM,gBAAgB,CAAC,OAAO,EAAE,QAAQ,CAAC;QAE5D,OAAO,CAAC,gBAAgB,CAAC,OAAO,EAAE,WAAW,EAAE,YAAY,CAAC;QAC5D,OAAO,CAAC,gBAAgB,CAAC,MAAM,EAAE,UAAU,EAAE,YAAY,CAAC;AAC9D,IAAA,CAAC,CAAC;IAEF,OAAO,CAAC,gBAAgB,CAAC,SAAS,EAAE,aAAa,EAAE,YAAY,CAAC;AAEhE;;AAEG;AACH,IAAA,OAAO,CAAC,gBAAgB,CACpB,MAAM,EACN,MAAM,OAAO,CAAC,mBAAmB,CAAC,SAAS,EAAE,aAAa,CAAC,EAC3D,YAAY,CACf;AACL;;;;"}

View File

@@ -0,0 +1,4 @@
const isPressing = new WeakSet();
export { isPressing };
//# sourceMappingURL=state.mjs.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"state.mjs","sources":["../../../../../src/gestures/press/utils/state.ts"],"sourcesContent":["export const isPressing = new WeakSet<EventTarget>()\n"],"names":[],"mappings":"AAAO,MAAM,UAAU,GAAG,IAAI,OAAO;;;;"}