@manapotion/core
Version:
1,131 lines (1,108 loc) • 37.9 kB
JavaScript
var __defProp = Object.defineProperty;
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
var __getOwnPropNames = Object.getOwnPropertyNames;
var __hasOwnProp = Object.prototype.hasOwnProperty;
var __export = (target, all) => {
for (var name in all)
__defProp(target, name, { get: all[name], enumerable: true });
};
var __copyProps = (to, from, except, desc) => {
if (from && typeof from === "object" || typeof from === "function") {
for (let key of __getOwnPropNames(from))
if (!__hasOwnProp.call(to, key) && key !== except)
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
}
return to;
};
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
// src/index.ts
var src_exports = {};
__export(src_exports, {
addMainLoopEffect: () => addMainLoopEffect,
browserStore: () => browserStore,
createJoystick: () => createJoystick,
enterFullscreen: () => enterFullscreen,
exitFullscreen: () => exitFullscreen,
getBrowser: () => getBrowser,
getJoysticks: () => getJoysticks,
getKeyboard: () => getKeyboard,
getMouse: () => getMouse,
joysticksStore: () => joysticksStore,
keyboardStore: () => keyboardStore,
lockKeys: () => lockKeys,
lockOrientation: () => lockOrientation,
lockPointer: () => lockPointer,
mountDeviceTypeListener: () => mountDeviceTypeListener,
mountFullscreenListener: () => mountFullscreenListener,
mountJoystickArea: () => mountJoystickArea,
mountKeyboardListener: () => mountKeyboardListener,
mountMouseButtonsListener: () => mountMouseButtonsListener,
mountMouseMoveListener: () => mountMouseMoveListener,
mountMouseScrollListener: () => mountMouseScrollListener,
mountPageFocusListener: () => mountPageFocusListener,
mountPageVisibilityListener: () => mountPageVisibilityListener,
mountPointerLockListener: () => mountPointerLockListener,
mountResizeListener: () => mountResizeListener,
mountScreenOrientationListener: () => mountScreenOrientationListener,
mouseStore: () => mouseStore,
pauseMainLoop: () => pauseMainLoop,
resetJoysticks: () => resetJoysticks,
resetKeyboard: () => resetKeyboard,
resetMouse: () => resetMouse,
resumeMainLoop: () => resumeMainLoop,
tailwindTheme: () => tailwindTheme,
unlockKeys: () => unlockKeys,
unlockOrientation: () => unlockOrientation,
unlockPointer: () => unlockPointer
});
module.exports = __toCommonJS(src_exports);
// ../../node_modules/zustand/esm/middleware.mjs
var import_meta = {};
var trackedConnections = /* @__PURE__ */ new Map();
var getTrackedConnectionState = (name) => {
const api = trackedConnections.get(name);
if (!api)
return {};
return Object.fromEntries(
Object.entries(api.stores).map(([key, api2]) => [key, api2.getState()])
);
};
var extractConnectionInformation = (store, extensionConnector, options) => {
if (store === void 0) {
return {
type: "untracked",
connection: extensionConnector.connect(options)
};
}
const existingConnection = trackedConnections.get(options.name);
if (existingConnection) {
return { type: "tracked", store, ...existingConnection };
}
const newConnection = {
connection: extensionConnector.connect(options),
stores: {}
};
trackedConnections.set(options.name, newConnection);
return { type: "tracked", store, ...newConnection };
};
var devtoolsImpl = (fn, devtoolsOptions = {}) => (set, get, api) => {
const { enabled, anonymousActionType, store, ...options } = devtoolsOptions;
let extensionConnector;
try {
extensionConnector = (enabled != null ? enabled : (import_meta.env ? import_meta.env.MODE : void 0) !== "production") && window.__REDUX_DEVTOOLS_EXTENSION__;
} catch (e) {
}
if (!extensionConnector) {
return fn(set, get, api);
}
const { connection, ...connectionInformation } = extractConnectionInformation(store, extensionConnector, options);
let isRecording = true;
api.setState = (state2, replace, nameOrAction) => {
const r = set(state2, replace);
if (!isRecording)
return r;
const action = nameOrAction === void 0 ? { type: anonymousActionType || "anonymous" } : typeof nameOrAction === "string" ? { type: nameOrAction } : nameOrAction;
if (store === void 0) {
connection == null ? void 0 : connection.send(action, get());
return r;
}
connection == null ? void 0 : connection.send(
{
...action,
type: `${store}/${action.type}`
},
{
...getTrackedConnectionState(options.name),
[store]: api.getState()
}
);
return r;
};
const setStateFromDevtools = (...a) => {
const originalIsRecording = isRecording;
isRecording = false;
set(...a);
isRecording = originalIsRecording;
};
const initialState = fn(api.setState, get, api);
if (connectionInformation.type === "untracked") {
connection == null ? void 0 : connection.init(initialState);
} else {
connectionInformation.stores[connectionInformation.store] = api;
connection == null ? void 0 : connection.init(
Object.fromEntries(
Object.entries(connectionInformation.stores).map(([key, store2]) => [
key,
key === connectionInformation.store ? initialState : store2.getState()
])
)
);
}
if (api.dispatchFromDevtools && typeof api.dispatch === "function") {
let didWarnAboutReservedActionType = false;
const originalDispatch = api.dispatch;
api.dispatch = (...a) => {
if ((import_meta.env ? import_meta.env.MODE : void 0) !== "production" && a[0].type === "__setState" && !didWarnAboutReservedActionType) {
console.warn(
'[zustand devtools middleware] "__setState" action type is reserved to set state from the devtools. Avoid using it.'
);
didWarnAboutReservedActionType = true;
}
originalDispatch(...a);
};
}
connection.subscribe((message) => {
var _a;
switch (message.type) {
case "ACTION":
if (typeof message.payload !== "string") {
console.error(
"[zustand devtools middleware] Unsupported action format"
);
return;
}
return parseJsonThen(
message.payload,
(action) => {
if (action.type === "__setState") {
if (store === void 0) {
setStateFromDevtools(action.state);
return;
}
if (Object.keys(action.state).length !== 1) {
console.error(
`
[zustand devtools middleware] Unsupported __setState action format.
When using 'store' option in devtools(), the 'state' should have only one key, which is a value of 'store' that was passed in devtools(),
and value of this only key should be a state object. Example: { "type": "__setState", "state": { "abc123Store": { "foo": "bar" } } }
`
);
}
const stateFromDevtools = action.state[store];
if (stateFromDevtools === void 0 || stateFromDevtools === null) {
return;
}
if (JSON.stringify(api.getState()) !== JSON.stringify(stateFromDevtools)) {
setStateFromDevtools(stateFromDevtools);
}
return;
}
if (!api.dispatchFromDevtools)
return;
if (typeof api.dispatch !== "function")
return;
api.dispatch(action);
}
);
case "DISPATCH":
switch (message.payload.type) {
case "RESET":
setStateFromDevtools(initialState);
if (store === void 0) {
return connection == null ? void 0 : connection.init(api.getState());
}
return connection == null ? void 0 : connection.init(getTrackedConnectionState(options.name));
case "COMMIT":
if (store === void 0) {
connection == null ? void 0 : connection.init(api.getState());
return;
}
return connection == null ? void 0 : connection.init(getTrackedConnectionState(options.name));
case "ROLLBACK":
return parseJsonThen(message.state, (state2) => {
if (store === void 0) {
setStateFromDevtools(state2);
connection == null ? void 0 : connection.init(api.getState());
return;
}
setStateFromDevtools(state2[store]);
connection == null ? void 0 : connection.init(getTrackedConnectionState(options.name));
});
case "JUMP_TO_STATE":
case "JUMP_TO_ACTION":
return parseJsonThen(message.state, (state2) => {
if (store === void 0) {
setStateFromDevtools(state2);
return;
}
if (JSON.stringify(api.getState()) !== JSON.stringify(state2[store])) {
setStateFromDevtools(state2[store]);
}
});
case "IMPORT_STATE": {
const { nextLiftedState } = message.payload;
const lastComputedState = (_a = nextLiftedState.computedStates.slice(-1)[0]) == null ? void 0 : _a.state;
if (!lastComputedState)
return;
if (store === void 0) {
setStateFromDevtools(lastComputedState);
} else {
setStateFromDevtools(lastComputedState[store]);
}
connection == null ? void 0 : connection.send(
null,
// FIXME no-any
nextLiftedState
);
return;
}
case "PAUSE_RECORDING":
return isRecording = !isRecording;
}
return;
}
});
return initialState;
};
var devtools = devtoolsImpl;
var parseJsonThen = (stringified, f) => {
let parsed;
try {
parsed = JSON.parse(stringified);
} catch (e) {
console.error(
"[zustand devtools middleware] Could not parse the received json",
e
);
}
if (parsed !== void 0)
f(parsed);
};
// ../../node_modules/zustand/esm/vanilla.mjs
var createStoreImpl = (createState) => {
let state2;
const listeners = /* @__PURE__ */ new Set();
const setState = (partial, replace) => {
const nextState = typeof partial === "function" ? partial(state2) : partial;
if (!Object.is(nextState, state2)) {
const previousState = state2;
state2 = (replace != null ? replace : typeof nextState !== "object" || nextState === null) ? nextState : Object.assign({}, state2, nextState);
listeners.forEach((listener) => listener(state2, previousState));
}
};
const getState = () => state2;
const getInitialState = () => initialState;
const subscribe = (listener) => {
listeners.add(listener);
return () => listeners.delete(listener);
};
const api = { setState, getState, getInitialState, subscribe };
const initialState = state2 = createState(setState, getState, api);
return api;
};
var createStore = (createState) => createState ? createStoreImpl(createState) : createStoreImpl;
// src/stores/browserStore.ts
var defaultBrowser = {
pointerLockSupported: false,
isFullscreen: false,
isPageVisible: true,
isPageFocused: true,
width: 0,
height: 0,
isDesktop: false,
isMobile: false,
isPortrait: false,
isLandscape: false
};
var browserStore = createStore()(
devtools(() => structuredClone(defaultBrowser), { name: "browser" })
);
var getBrowser = browserStore.getState;
// src/stores/keyboardStore.ts
var defaultKeyboard = {
codes: {},
keys: {},
ctrl: false,
shift: false,
alt: false,
meta: false
};
var keyboardStore = createStore()(
devtools(() => structuredClone(defaultKeyboard), { name: "keyboard" })
);
var getKeyboard = keyboardStore.getState;
var resetKeyboard = () => keyboardStore.setState(() => structuredClone(defaultKeyboard));
// src/stores/mouseStore.ts
var defaultMouse = {
locked: false,
position: { x: 0, y: 0 },
movement: { x: 0, y: 0 },
wheel: { y: 0 },
buttons: { left: false, middle: false, right: false }
};
var mouseStore = createStore()(
devtools(() => structuredClone(defaultMouse), { name: "mouse" })
);
var getMouse = mouseStore.getState;
var resetMouse = () => mouseStore.setState(() => structuredClone(defaultMouse));
// src/stores/joysticksStore.ts
var createJoystick = () => ({
isActive: false,
identifier: null,
origin: { x: null, y: null, angle: null, distance: null, distanceRatio: null },
follow: { x: null, y: null, angle: null, distance: null, distanceRatio: null },
current: { x: null, y: null },
movement: { x: 0, y: 0 }
});
var defaultJoysticks = {
movement: createJoystick(),
rotation: createJoystick()
};
var joysticksStore = createStore()(
devtools(() => structuredClone(defaultJoysticks), { name: "joysticks" })
);
var getJoysticks = joysticksStore.getState;
var resetJoysticks = () => joysticksStore.setState(() => structuredClone(defaultJoysticks));
// src/main-loop.ts
var callbacks = /* @__PURE__ */ new Map();
var callbackLastExecutions = /* @__PURE__ */ new Map();
var state = {
time: 0,
delta: 0,
timeRunning: 0,
callbackCount: 0
};
var running = false;
var previousTime = 0;
var animationFrameId = null;
var mainLoop = (time) => {
if (!running)
return;
state.time = time;
state.timeRunning += state.time - previousTime;
let callbackCount = 0;
for (const callbacksSet of callbacks.values()) {
callbackCount += callbacksSet.size;
}
state.callbackCount = callbackCount;
Array.from(callbacks.keys()).sort((a, b) => a - b).forEach((stage) => {
callbacks.get(stage)?.forEach((callback) => {
try {
callback(state);
} catch (error) {
console.error("Error in animation frame callback:", error);
}
});
});
previousTime = state.time;
animationFrameId = requestAnimationFrame(mainLoop);
};
var addMainLoopEffect = (callback, options) => {
const throttledCallback = (state2) => {
const lastExecution = callbackLastExecutions.get(callback) || 0;
const throttleInterval = options?.throttle || 0;
if (throttleInterval === 0) {
callbackLastExecutions.set(callback, state2.time);
state2.delta = (state2.time - previousTime) / 1e3;
callback(state2);
} else {
const intervalsPassed = Math.floor((state2.time - lastExecution) / throttleInterval);
if (intervalsPassed >= 1) {
const newLastExecution = lastExecution + intervalsPassed * throttleInterval;
callbackLastExecutions.set(callback, newLastExecution);
state2.delta = (newLastExecution - lastExecution) / 1e3;
callback(state2);
}
}
};
const stage = options?.stage || 0;
if (!callbacks.has(stage)) {
callbacks.set(stage, /* @__PURE__ */ new Set());
}
callbacks.get(stage)?.add(throttledCallback);
if (!running) {
running = true;
animationFrameId = requestAnimationFrame((time) => {
previousTime = Math.round(time);
callbackLastExecutions.forEach((_, callback2) => callbackLastExecutions.set(callback2, time));
mainLoop(time);
});
}
return () => {
callbacks.get(stage)?.delete(throttledCallback);
if (callbacks.get(stage)?.size === 0) {
callbacks.delete(stage);
}
};
};
var pauseMainLoop = () => {
running = false;
if (animationFrameId !== null) {
cancelAnimationFrame(animationFrameId);
animationFrameId = null;
}
};
var resumeMainLoop = () => {
if (!running && callbacks.size > 0) {
running = true;
animationFrameId = requestAnimationFrame((time) => {
previousTime = time;
callbackLastExecutions.forEach((_, callback) => callbackLastExecutions.set(callback, time));
mainLoop(time);
});
}
};
// src/browser.ts
var enterFullscreen = () => {
if (document.documentElement.requestFullscreen) {
return document.documentElement.requestFullscreen();
}
if (document.documentElement.mozRequestFullScreen) {
return document.documentElement.mozRequestFullScreen();
}
if (document.documentElement.webkitRequestFullscreen) {
return document.documentElement.webkitRequestFullscreen();
}
if (document.documentElement.msRequestFullscreen) {
return document.documentElement.msRequestFullscreen();
}
};
var exitFullscreen = () => {
if (document.exitFullscreen) {
return document.exitFullscreen();
}
if (document.mozCancelFullScreen) {
return document.mozCancelFullScreen();
}
if (document.webkitExitFullscreen) {
return document.webkitExitFullscreen();
}
if (document.msExitFullscreen) {
return document.msExitFullscreen();
}
};
var lockOrientation = (type) => {
if (screen.orientation.type.startsWith(type)) {
return;
}
if ("lock" in screen.orientation) {
return screen.orientation.lock(type);
}
};
var unlockOrientation = () => {
if ("unlock" in screen.orientation) {
return screen.orientation.unlock();
}
};
var lockKeys = (keys) => {
if ("keyboard" in navigator && navigator.keyboard && "lock" in navigator.keyboard && navigator.keyboard.lock) {
return navigator.keyboard.lock(keys);
}
};
var unlockKeys = () => {
if ("keyboard" in navigator && navigator.keyboard && "unlock" in navigator.keyboard && navigator.keyboard.unlock) {
return navigator.keyboard.unlock();
}
};
var lockPointer = (options) => "requestPointerLock" in document.body && document.body.requestPointerLock?.(options);
var unlockPointer = () => "exitPointerLock" in document && document.exitPointerLock?.();
// src/tailwind.ts
var xxxxxs = "192px";
var xxxxs = "256px";
var xxxs = "320px";
var xxs = "384px";
var xs = "512px";
var sm = "640px";
var md = "768px";
var lg = "1024px";
var xl = "1280px";
var xxl = "1536px";
var xxxl = "1792px";
var xxxxl = "2048px";
var xxxxxl = "2560px";
var screens = {
"5xs": xxxxxs,
"4xs": xxxxs,
"3xs": xxxs,
"2xs": xxs,
xs,
sm,
md,
lg,
xl,
"2xl": xxl,
"3xl": xxxl,
"4xl": xxxxl,
"5xl": xxxxxl
};
var extendScreens = {
// https://github.com/tailwindlabs/tailwindcss/issues/13022
"max-5xs": { raw: `not all and (min-width: ${xxxxxs})` },
"max-4xs": { raw: `not all and (min-width: ${xxxxs})` },
"max-3xs": { raw: `not all and (min-width: ${xxxs})` },
"max-2xs": { raw: `not all and (min-width: ${xxs})` },
"max-xs": { raw: `not all and (min-width: ${xs})` },
"max-sm": { raw: `not all and (min-width: ${sm})` },
"max-md": { raw: `not all and (min-width: ${md})` },
"max-lg": { raw: `not all and (min-width: ${lg})` },
"max-xl": { raw: `not all and (min-width: ${xl})` },
"max-2xl": { raw: `not all and (min-width: ${xxl})` },
"max-3xl": { raw: `not all and (min-width: ${xxxl})` },
"max-4xl": { raw: `not all and (min-width: ${xxxxl})` },
"max-5xl": { raw: `not all and (min-width: ${xxxxxl})` },
"5xs-h": { raw: `(min-height: ${xxxxxs})` },
"4xs-h": { raw: `(min-height: ${xxxxs})` },
"3xs-h": { raw: `(min-height: ${xxxs})` },
"2xs-h": { raw: `(min-height: ${xxs})` },
"xs-h": { raw: `(min-height: ${xs})` },
"sm-h": { raw: `(min-height: ${sm})` },
"md-h": { raw: `(min-height: ${md})` },
"lg-h": { raw: `(min-height: ${lg})` },
"xl-h": { raw: `(min-height: ${xl})` },
"2xl-h": { raw: `(min-height: ${xxl})` },
"3xl-h": { raw: `(min-height: ${xxxl})` },
"4xl-h": { raw: `(min-height: ${xxxxl})` },
"5xl-h": { raw: `(min-height: ${xxxxxl})` },
"max-5xs-h": { raw: `not all and (min-height: ${xxxxxs})` },
"max-4xs-h": { raw: `not all and (min-height: ${xxxxs})` },
"max-3xs-h": { raw: `not all and (min-height: ${xxxs})` },
"max-2xs-h": { raw: `not all and (min-height: ${xxs})` },
"max-xs-h": { raw: `not all and (min-height: ${xs})` },
"max-sm-h": { raw: `not all and (min-height: ${sm})` },
"max-md-h": { raw: `not all and (min-height: ${md})` },
"max-lg-h": { raw: `not all and (min-height: ${lg})` },
"max-xl-h": { raw: `not all and (min-height: ${xl})` },
"max-2xl-h": { raw: `not all and (min-height: ${xxl})` },
"max-3xl-h": { raw: `not all and (min-height: ${xxxl})` },
"max-4xl-h": { raw: `not all and (min-height: ${xxxxl})` },
"max-5xl-h": { raw: `not all and (min-height: ${xxxxxl})` },
mobile: { raw: "(hover: none) and (pointer: coarse)" },
desktop: { raw: "(hover: hover) and (pointer: fine)" }
};
var tailwindTheme = { screens, extend: { screens: extendScreens } };
// src/joystickarea.ts
var { sin, cos, sqrt, pow, atan2 } = Math;
var pi = Math.PI;
var mountJoystickArea = ({
joystick,
mode = "follow",
maxFollowDistance = 50,
maxOriginDistance = 50,
element,
onStart,
onEnd,
onMove
}) => {
if (!joystick) {
console.error("JoystickArea: an joystick object is required");
}
let resetMovementTimeout = null;
let endResetTimeout = null;
const resetJoystick = () => {
if (!joystick) {
return;
}
joystick.identifier = null;
joystick.isActive = false;
joystick.origin.x = null;
joystick.origin.y = null;
joystick.origin.angle = null;
joystick.origin.distance = null;
joystick.origin.distanceRatio = null;
joystick.follow.x = null;
joystick.follow.y = null;
joystick.follow.angle = null;
joystick.follow.distance = null;
joystick.follow.distanceRatio = null;
joystick.current.x = null;
joystick.current.y = null;
joystick.movement.x = 0;
joystick.movement.y = 0;
};
const handleTouchStart = (e) => {
e.preventDefault();
const touch = e.changedTouches.item(0);
if (!touch || !joystick || joystick.identifier != void 0) {
return;
}
const target = e.target;
const rect = target.getBoundingClientRect();
const currentX = touch.clientX - rect.left;
const currentY = rect.height - (touch.clientY - rect.top);
joystick.identifier = touch.identifier;
joystick.isActive = true;
joystick.origin.x = currentX;
joystick.origin.y = currentY;
joystick.origin.angle = 0;
joystick.origin.distance = 0;
joystick.origin.distanceRatio = 0;
if (mode === "follow") {
joystick.follow.x = currentX;
joystick.follow.y = currentY;
joystick.follow.angle = 0;
joystick.follow.distance = 0;
joystick.follow.distanceRatio = 0;
}
joystick.current.x = currentX;
joystick.current.y = currentY;
joystick.movement.x = 0;
joystick.movement.y = 0;
onStart?.(joystick);
};
const handleTouchMove = (e) => {
e.preventDefault();
if (!joystick) {
return;
}
for (let i = 0; i < e.changedTouches.length; i++) {
const touch = e.changedTouches.item(i);
if (!touch || joystick.identifier !== touch.identifier) {
continue;
}
if (joystick.origin.x === null || joystick.origin.y === null || joystick.current.x === null || joystick.current.y === null) {
continue;
}
const target = e.target;
const rect = target.getBoundingClientRect();
const fingerPositionX = touch.clientX - rect.left;
const fingerPositionY = rect.height - (touch.clientY - rect.top);
const fingerOriginDistance = sqrt(
pow(fingerPositionX - joystick.origin.x, 2) + pow(fingerPositionY - joystick.origin.y, 2)
);
joystick.origin.angle = (atan2(fingerPositionY - joystick.origin.y, fingerPositionX - joystick.origin.x) + 2 * pi) % (2 * pi);
const currentX = mode === "origin" ? joystick.origin.x + Math.min(fingerOriginDistance, maxOriginDistance) * cos(joystick.origin.angle) : touch.clientX - rect.left;
const currentY = mode === "origin" ? joystick.origin.y + Math.min(fingerOriginDistance, maxOriginDistance) * sin(joystick.origin.angle) : rect.height - (touch.clientY - rect.top);
joystick.movement.x = currentX - joystick.current.x;
joystick.movement.y = currentY - joystick.current.y;
joystick.current.x = currentX;
joystick.current.y = currentY;
joystick.origin.distance = sqrt(
pow(currentX - joystick.origin.x, 2) + pow(currentY - joystick.origin.y, 2)
);
if (mode === "origin") {
if (joystick.origin.distance > maxOriginDistance - 0.01) {
joystick.origin.distance = maxOriginDistance;
}
}
joystick.origin.distanceRatio = maxOriginDistance ? joystick.origin.distance / maxOriginDistance : 1;
if (joystick.origin.distanceRatio > 0.99) {
joystick.origin.distanceRatio = 1;
}
if (mode === "follow") {
if (joystick.follow.x !== null && joystick.follow.y !== null) {
joystick.follow.angle = (atan2(fingerPositionY - joystick.follow.y, fingerPositionX - joystick.follow.x) + 2 * pi) % (2 * pi);
joystick.follow.distance = sqrt(
pow(currentX - joystick.follow.x, 2) + pow(currentY - joystick.follow.y, 2)
);
if (joystick.follow.distance > maxFollowDistance - 0.01) {
joystick.follow.distance = maxFollowDistance;
}
joystick.follow.distanceRatio = maxFollowDistance ? joystick.follow.distance / maxFollowDistance : 1;
joystick.follow.angle = (atan2(currentY - joystick.follow.y, currentX - joystick.follow.x) + 2 * pi) % (2 * pi);
if (joystick.follow.distance >= maxFollowDistance) {
const oppositeFollowAngle = Math.atan2(
joystick.follow.y - currentY,
joystick.follow.x - currentX
);
joystick.follow.x = currentX + maxFollowDistance * cos(oppositeFollowAngle);
joystick.follow.y = currentY + maxFollowDistance * sin(oppositeFollowAngle);
}
}
}
joystick.origin.angle = (atan2(currentY - joystick.origin.y, currentX - joystick.origin.x) + 2 * pi) % (2 * pi);
onMove?.(joystick);
resetMovementTimeout && clearTimeout(resetMovementTimeout);
resetMovementTimeout = setTimeout(() => {
if (joystick && joystick.identifier !== void 0) {
joystick.movement.x = 0;
joystick.movement.y = 0;
onMove?.(joystick);
}
}, 70);
}
};
const handleTouchEnd = (e) => {
e.preventDefault();
if (!joystick) {
return;
}
for (let i = 0; i < e.changedTouches.length; i++) {
const touch = e.changedTouches.item(i);
if (!touch || joystick.identifier !== touch.identifier) {
continue;
}
joystick.movement.x = 0;
joystick.movement.y = 0;
onEnd?.(joystick);
endResetTimeout = setTimeout(() => {
resetJoystick();
onEnd?.(joystick);
}, 50);
}
};
element.addEventListener(
"touchstart",
handleTouchStart
/*, { passive: false } */
);
element.addEventListener(
"touchmove",
handleTouchMove
/*, { passive: false } */
);
element.addEventListener(
"touchend",
handleTouchEnd
/*, { passive: false } */
);
return () => {
resetMovementTimeout && clearTimeout(resetMovementTimeout);
endResetTimeout && clearTimeout(endResetTimeout);
element.removeEventListener("touchstart", handleTouchStart);
element.removeEventListener("touchmove", handleTouchMove);
element.removeEventListener("touchend", handleTouchEnd);
};
};
// src/listeners/devicetype.ts
var mountDeviceTypeListener = ({ onDeviceTypeChange }) => {
const desktopQuery = window.matchMedia("(hover: hover) and (pointer: fine)");
const mobileQuery = window.matchMedia("(hover: none) and (pointer: coarse)");
const update = () => {
const isDesktop = desktopQuery.matches;
const isMobile = mobileQuery.matches;
const payload = { isDesktop, isMobile };
browserStore.setState((s) => ({ ...s, ...payload }));
return payload;
};
const handleChange = () => {
const payload = update();
onDeviceTypeChange?.(payload);
};
update();
desktopQuery.addEventListener("change", handleChange);
return () => desktopQuery.removeEventListener("change", handleChange);
};
// src/listeners/fullscreen.ts
var mountFullscreenListener = ({ onFullscreenChange }) => {
const update = () => {
const isFullscreen = Boolean(document.fullscreenElement);
const payload = { isFullscreen };
browserStore.setState((s) => ({ ...s, ...payload }));
return payload;
};
const handleChange = () => {
const payload = update();
onFullscreenChange?.(payload);
};
update();
document.addEventListener("fullscreenchange", handleChange);
return () => document.removeEventListener("fullscreenchange", handleChange);
};
// src/listeners/keyboard.ts
var mountKeyboardListener = ({ onKeyUp, onKeyDown }) => {
const downHandler = (e) => {
const { key, code } = e;
const keyboard = getKeyboard();
if (keyboard.codes[code] || keyboard.keys[key]) {
return;
}
keyboardStore.setState((s) => ({
...s,
codes: { ...s.codes, [code]: true },
keys: { ...s.keys, [key]: true },
ctrl: e.ctrlKey,
shift: e.shiftKey,
alt: e.altKey,
meta: e.metaKey
}));
onKeyDown?.({
code,
key,
ctrl: e.ctrlKey,
shift: e.shiftKey,
alt: e.altKey,
meta: e.metaKey
});
};
const upHandler = (e) => {
keyboardStore.setState((s) => {
const newKeyboard = { ...s, codes: { ...s.codes }, keys: { ...s.keys } };
if (e.code) {
delete newKeyboard.codes[e.code];
}
if (e.key) {
delete newKeyboard.keys[e.key];
delete newKeyboard.keys[e.key.toUpperCase()];
delete newKeyboard.keys[e.key.toLowerCase()];
}
delete newKeyboard.keys.Dead;
newKeyboard.ctrl = e.ctrlKey;
newKeyboard.shift = e.shiftKey;
newKeyboard.alt = e.altKey;
newKeyboard.meta = e.metaKey;
return newKeyboard;
});
onKeyUp?.({
code: e.code,
key: e.key,
ctrl: e.ctrlKey,
shift: e.shiftKey,
alt: e.altKey,
meta: e.metaKey
});
};
window.addEventListener("keydown", downHandler);
window.addEventListener("keyup", upHandler);
return () => {
window.removeEventListener("keydown", downHandler);
window.removeEventListener("keyup", upHandler);
};
};
// src/listeners/mousebuttons.ts
var mountMouseButtonsListener = ({
onLeftMouseButtonDown,
onMiddleMouseButtonDown,
onRightMouseButtonDown,
onLeftMouseButtonUp,
onMiddleMouseButtonUp,
onRightMouseButtonUp
}) => {
const emptyObject = {};
const downHandler = (e) => {
const left = (e.buttons & 1) !== 0;
const middle = (e.buttons & 4) !== 0;
const right = (e.buttons & 2) !== 0;
mouseStore.setState((s) => {
const newMouse = structuredClone(s);
const buttons = newMouse.buttons;
buttons.left = left;
buttons.middle = middle;
buttons.right = right;
return newMouse;
});
if (e.button === 0) {
onLeftMouseButtonDown?.(emptyObject);
} else if (e.button === 1) {
onMiddleMouseButtonDown?.(emptyObject);
} else if (e.button === 2) {
onRightMouseButtonDown?.(emptyObject);
}
};
const upHandler = (e) => {
const left = (e.buttons & 1) !== 0;
const middle = (e.buttons & 4) !== 0;
const right = (e.buttons & 2) !== 0;
mouseStore.setState((s) => {
const newMouse = structuredClone(s);
const buttons = newMouse.buttons;
buttons.left = left;
buttons.middle = middle;
buttons.right = right;
return newMouse;
});
if (e.button === 0) {
onLeftMouseButtonUp?.(emptyObject);
} else if (e.button === 1) {
onMiddleMouseButtonUp?.(emptyObject);
} else if (e.button === 2) {
onRightMouseButtonUp?.(emptyObject);
}
};
window.addEventListener("mousedown", downHandler);
window.addEventListener("mouseup", upHandler);
return () => {
window.removeEventListener("mousedown", downHandler);
window.removeEventListener("mouseup", upHandler);
};
};
// src/listeners/mousemove.ts
var movementResetTimeout = null;
var mountMouseMoveListener = ({
onMouseMove,
mouseMovementResetDelay = 30
}) => {
const payload = { position: { x: 0, y: 0 }, movement: { x: 0, y: 0 } };
const handler = (e) => {
const mouse = getMouse();
const position = mouse.position;
const movement = mouse.movement;
position.x = e.clientX;
position.y = window.innerHeight - e.clientY;
movement.x = e.movementX;
movement.y = -e.movementY;
payload.position.x = position.x;
payload.position.y = position.y;
payload.movement.x = movement.x;
payload.movement.y = movement.y;
onMouseMove?.(payload);
movementResetTimeout && clearTimeout(movementResetTimeout);
if (mouseMovementResetDelay) {
movementResetTimeout = setTimeout(() => {
movement.x = 0;
movement.y = 0;
payload.movement.x = movement.x;
payload.movement.y = movement.y;
onMouseMove?.(payload);
}, mouseMovementResetDelay);
}
};
window.addEventListener("mousemove", handler);
return () => {
movementResetTimeout && clearTimeout(movementResetTimeout);
window.removeEventListener("mousemove", handler);
};
};
// src/listeners/mousescroll.ts
var resetTimeout = null;
var mountMouseScrollListener = ({
onScroll,
mouseScrollResetDelay = 100
}) => {
const payload = { y: 0 };
const handler = (e) => {
const mouse = getMouse();
const wheel = mouse.wheel;
wheel.y = e.deltaY;
payload.y = wheel.y;
onScroll?.(payload);
resetTimeout && clearTimeout(resetTimeout);
if (mouseScrollResetDelay) {
resetTimeout = setTimeout(() => {
wheel.y = 0;
payload.y = wheel.y;
onScroll?.(payload);
}, mouseScrollResetDelay);
}
};
window.addEventListener("wheel", handler);
return () => {
resetTimeout && clearTimeout(resetTimeout);
window.removeEventListener("wheel", handler);
};
};
// src/listeners/pagefocus.ts
var mountPageFocusListener = ({ onPageFocusChange }) => {
const update = () => {
const isPageFocused = document.hasFocus();
const payload = { isPageFocused };
browserStore.setState((s) => ({ ...s, ...payload }));
return payload;
};
const handleChange = () => {
const payload = update();
onPageFocusChange?.(payload);
};
update();
window.addEventListener("focus", handleChange);
window.addEventListener("blur", handleChange);
return () => {
window.removeEventListener("focus", handleChange);
window.removeEventListener("blur", handleChange);
};
};
// src/listeners/pagevisibility.ts
var mountPageVisibilityListener = ({
onPageVisibilityChange
}) => {
const update = () => {
const isPageVisible = !document.hidden;
const payload = { isPageVisible };
browserStore.setState((s) => ({ ...s, ...payload }));
return payload;
};
const handleChange = () => {
const payload = update();
onPageVisibilityChange?.(payload);
};
update();
document.addEventListener("visibilitychange", handleChange);
return () => document.removeEventListener("visibilitychange", handleChange);
};
// src/listeners/pointerlock.ts
var mountPointerLockListener = ({ onPointerLockChange }) => {
const update = () => {
const locked = Boolean(document.pointerLockElement);
mouseStore.setState((s) => {
const newMouse = structuredClone(s);
newMouse.locked = locked;
return newMouse;
});
const payload = { isPointerLocked: locked };
return payload;
};
const handleChange = () => {
const payload = update();
onPointerLockChange?.(payload);
};
update();
document.addEventListener("pointerlockchange", handleChange);
return () => document.removeEventListener("pointerlockchange", handleChange);
};
// src/listeners/resize.ts
var mountResizeListener = ({ onResize }) => {
const payload = { width: 0, height: 0 };
const handler = () => {
const browser = getBrowser();
const width = window.innerWidth;
const height = window.innerHeight;
browser.width = width;
browser.height = height;
payload.width = browser.width;
payload.height = browser.height;
onResize?.(payload);
};
handler();
window.addEventListener("resize", handler);
return () => window.removeEventListener("resize", handler);
};
// src/listeners/screenorientation.ts
var mountScreenOrientationListener = ({
onScreenOrientationChange
}) => {
const landscapeQuery = window.matchMedia("(orientation: landscape)");
const portraitQuery = window.matchMedia("(orientation: portrait)");
const update = () => {
const isLandscape = landscapeQuery.matches;
const isPortrait = portraitQuery.matches;
const payload = { isLandscape, isPortrait };
browserStore.setState((s) => ({ ...s, ...payload }));
return payload;
};
const handleChange = () => {
const payload = update();
onScreenOrientationChange?.(payload);
};
update();
landscapeQuery.addEventListener("change", handleChange);
return () => landscapeQuery.removeEventListener("change", handleChange);
};
// Annotate the CommonJS export names for ESM import in node:
0 && (module.exports = {
addMainLoopEffect,
browserStore,
createJoystick,
enterFullscreen,
exitFullscreen,
getBrowser,
getJoysticks,
getKeyboard,
getMouse,
joysticksStore,
keyboardStore,
lockKeys,
lockOrientation,
lockPointer,
mountDeviceTypeListener,
mountFullscreenListener,
mountJoystickArea,
mountKeyboardListener,
mountMouseButtonsListener,
mountMouseMoveListener,
mountMouseScrollListener,
mountPageFocusListener,
mountPageVisibilityListener,
mountPointerLockListener,
mountResizeListener,
mountScreenOrientationListener,
mouseStore,
pauseMainLoop,
resetJoysticks,
resetKeyboard,
resetMouse,
resumeMainLoop,
tailwindTheme,
unlockKeys,
unlockOrientation,
unlockPointer
});
;