diff --git a/frontend/components/MultiplayerPanel.js b/frontend/components/MultiplayerPanel.js index 9062f42977..4c3447c618 100644 --- a/frontend/components/MultiplayerPanel.js +++ b/frontend/components/MultiplayerPanel.js @@ -6,6 +6,7 @@ import { PlutoActionsContext } from "../common/PlutoContext.js" // TODO: investigate why effect is bad with low throttle rate? // .... update notebook should be pretty fast. const CURSOR_THROTTLE_RATE = 100 +const DEFAULT_CURSOR_COLOR = "#eeeeee" const mouse_data_to_point = ({relative_to_cell, relative_x, relative_y}) => { const cell_elt = document.getElementById(relative_to_cell) @@ -21,6 +22,9 @@ const mouse_data_to_point = ({relative_to_cell, relative_x, relative_y}) => { ] } +// l² norm in pixel space +const dist2 = (x1, y1, x2, y2) => (x1 - x2) ^ 2 + (y1 - y2) ^ 2 + const update_mouse_data = (mouseX, mouseY) => { const cell_nodes = Array.from(document.querySelectorAll("pluto-notebook > pluto-cell")) @@ -31,13 +35,13 @@ const update_mouse_data = (mouseX, mouseY) => { const mousePt = [mouseX, mouseY] - for (let cell_elt of cell_nodes) { - const dist_with_cell = dist2(mousePt, [cell_elt.offsetLeft, cell_elt.offsetTop]) + for (let { id: cell_id, offsetLeft: cell_x, offsetTop: cell_y } of cell_nodes) { + const dist_with_cell = dist2(mouseX, mouseY, cell_x, cell_y) if (dist_with_cell < best_dist) { best_dist = dist_with_cell - best_index = cell_elt.id - relative_x = mouseX - cell_elt.offsetLeft - relative_y = mouseY - cell_elt.offsetTop + best_index = cell_id + relative_x = mouseX - cell_x + relative_y = mouseY - cell_y } } @@ -60,8 +64,8 @@ const usePerfectCursor = (cb, point) => { return onPointChange } +// hex needs to be a string of length 7 example: '#bedbad' const hexToRGBA = (hex, alpha) => `rgba(${parseInt(hex.slice(1,3),16)},${parseInt(hex.slice(3,5),16)},${parseInt(hex.slice(5,8),16)},${alpha})` -const dist2 = ([x1,y1], [x2,y2]) => (x1 - x2) ^ 2 + (y1 - y2) ^ 2 const Cursor = ({ mouse: point, color }) => { if (!point) return @@ -76,7 +80,7 @@ const Cursor = ({ mouse: point, color }) => { const onPointMove = usePerfectCursor(animate) useLayoutEffect(() => onPointMove(point), [point, onPointMove]) - color = color ?? "#eeeeee" + color = color ?? DEFAULT_CURSOR_COLOR return html`
{ width: 20, height: 20, borderRadius: 10, - border: "solid 8px " + hexToRGBA(color , 0.5 ), + border: "solid 4px " + hexToRGBA(color , 0.5), backgroundColor: color, + "-webkit-background-clip": "padding-box", /* for Safari */ + backgroundClip: "padding-box", /* for IE9+, Firefox 4+, Opera, Chrome */ }} >
` @@ -113,10 +119,13 @@ export const MultiplayerPanel = ({ users, client_id }) => { }) }, CURSOR_THROTTLE_RATE), [client_id]) - // usePassiveDocumentEventListener("blur", update_notebook(notebook => { - // if (!(client_id in notebook.users)) return - // notebook.users[client_id].mouse = null - // }), []) + + const hide_mouse_for_client = useCallback(() => update_notebook(notebook => { + if (!(client_id in notebook.users)) return + notebook.users[client_id].mouse = null + }), [client_id]) + + usePassiveDocumentEventListener("blur", hide_mouse_for_client, [hide_mouse_for_client]) return html`