Skip to content

Commit

Permalink
add support for phx-window-keyup, phx-window-keydown, phx-key and upd…
Browse files Browse the repository at this point in the history
…ate light example to use key bindings
  • Loading branch information
floodfx committed Feb 1, 2022
1 parent 6f0b726 commit 57bd058
Show file tree
Hide file tree
Showing 5 changed files with 44 additions and 26 deletions.
6 changes: 3 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -36,9 +36,9 @@ This is still in ⍺lpha territory. You probably shouldn't put this into produc
| Focus Events | `phx-window-focus` | [ ] |
| Key Events | `phx-keydown` | [ ] |
| Key Events | `phx-keyup` | [ ] |
| Key Events | `phx-window-keydown` | [ ] |
| Key Events | `phx-window-keyup` | [ ] |
| Key Events | `phx-key` | [ ] |
| Key Events | `phx-window-keydown` | [x] |
| Key Events | `phx-window-keyup` | [x] |
| Key Events | `phx-key` | [x] |
| DOM Patching | `phx-update` | [ ] |
| DOM Patching | `phx-remove` | [ ] |
| JS Interop | `phx-hook` | [ ] |
Expand Down
41 changes: 24 additions & 17 deletions src/examples/light_liveview.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,60 +5,67 @@ export interface LightContext {
brightness: number;
}

export type LightEvent = "on" | "off" | "up" | "down";
export type LightEvent = "on" | "off" | "up" | "down" | "key_update";

export class LightLiveViewComponent extends BaseLiveViewComponent<LightContext, never> implements
LiveViewComponent<LightContext, never>,
LiveViewExternalEventListener<LightContext, "on", any>,
LiveViewExternalEventListener<LightContext, "off", any> {
LiveViewExternalEventListener<LightContext, "on", { key: string }>,
LiveViewExternalEventListener<LightContext, "off", { key: string }> {


mount(params: any, session: any, socket: LiveViewSocket<LightContext>) {
return { brightness: 10 };
};

render(context: LightContext) {
const { brightness } = context;
return html`
<div id="light">
<h1>Front Porch Light</h1>
<div class="meter">
<span style="width: ${context.brightness} %>%">
${context.brightness}%
</span>
<div>
<div>${brightness}%</div>
<progress id="light_meter" style="width: 300px; height: 2em; opacity: ${brightness / 100}" value="${brightness}" max="100"></progress>
</div>
<button phx-click="off">
Off
<button phx-click="off" phx-window-keydown="key_update" phx-key="ArrowLeft">
⬅️ Off
</button>
<button phx-click="down">
Down
<button phx-click="down" phx-window-keydown="key_update" phx-key="ArrowDown">
⬇️ Down
</button>
<button phx-click="up">
Up
<button phx-click="up" phx-window-keydown="key_update" phx-key="ArrowUp">
⬆️ Up
</button>
<button phx-click="on">
On
<button phx-click="on" phx-window-keydown="key_update" phx-key="ArrowRight">
➡️ On
</button>
</div>
`
};

handleEvent(event: LightEvent, params: any, socket: LiveViewSocket<LightContext>) {
handleEvent(event: LightEvent, params: { key: string }, socket: LiveViewSocket<LightContext>) {
const ctx: LightContext = { brightness: socket.context.brightness };
switch (event) {
// map key_update to arrow keys
const lightEvent = event === "key_update" ? params.key : event;
console.log("lightEvent:", lightEvent);
switch (lightEvent) {
case 'off':
case 'ArrowLeft':
ctx.brightness = 0;
break;
case 'on':
case 'ArrowRight':
ctx.brightness = 100;
break;
case 'up':
case 'ArrowUp':
ctx.brightness = Math.min(ctx.brightness + 10, 100);
break;
case 'down':
case 'ArrowDown':
ctx.brightness = Math.max(ctx.brightness - 10, 0);
break;
}
Expand Down
6 changes: 4 additions & 2 deletions src/server/socket/component_manager.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { WebSocket } from "ws";
import { BaseLiveViewComponent, LiveViewComponent, LiveViewSocket, StringPropertyValues } from "..";
import { newHeartbeatReply, newPhxReply, PhxClickPayload, PhxDiffReply, PhxFormPayload, PhxHeartbeatIncoming, PhxIncomingMessage, PhxJoinIncoming, PhxJoinPayload, PhxLivePatchIncoming, PhxLivePatchPushPayload, PhxOutgoingLivePatchPush, PhxOutgoingMessage, PhxSocketProtocolNames } from "./types";
import { newHeartbeatReply, newPhxReply, PhxClickPayload, PhxDiffReply, PhxFormPayload, PhxHeartbeatIncoming, PhxIncomingMessage, PhxJoinIncoming, PhxLivePatchIncoming, PhxOutgoingLivePatchPush, PhxOutgoingMessage, PhxKeyDownPayload, PhxKeyUpPayload } from "./types";
import jwt from 'jsonwebtoken';
import { SessionData } from "express-session";

Expand Down Expand Up @@ -68,7 +68,7 @@ export class LiveViewComponentManager {
this.sendPhxReply(ws, newHeartbeatReply(message));
}

onEvent(ws: WebSocket, message: PhxIncomingMessage<PhxClickPayload | PhxFormPayload>) {
onEvent(ws: WebSocket, message: PhxIncomingMessage<PhxClickPayload | PhxFormPayload | PhxKeyUpPayload | PhxKeyDownPayload>) {
const [joinRef, messageRef, topic, _, payload] = message;
const { type, event } = payload;

Expand All @@ -80,6 +80,8 @@ export class LiveViewComponentManager {
} else if (type === "form") {
// @ts-ignore - URLSearchParams has an entries method but not typed
value = Object.fromEntries(new URLSearchParams(payload.value))
} else if (type === "keyup" || type === "keydown") {
value = payload.value;
}

if (isEventHandler(this.component)) {
Expand Down
5 changes: 3 additions & 2 deletions src/server/socket/message_router.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { RenderedNode, PhxOutgoingMessage, PhxJoinIncoming, PhxHeartbeatIncoming, PhxIncomingMessage, PhxClickPayload, PhxFormPayload, PhxDiffReply, PhxLivePatchIncoming } from './types';
import { PhxJoinIncoming, PhxHeartbeatIncoming, PhxIncomingMessage, PhxClickPayload, PhxFormPayload, PhxLivePatchIncoming, PhxKeyDownPayload, PhxKeyUpPayload } from './types';
import WebSocket from 'ws';
import { LiveViewComponent } from '../types';
import { LiveViewRouter } from '../types';
Expand Down Expand Up @@ -32,7 +32,8 @@ export class MessageRouter {
// lookup component manager for this topic
componentManager = this.topicComponentManager[topic];
if (componentManager) {
componentManager.onEvent(ws, rawPhxMessage as PhxIncomingMessage<PhxClickPayload | PhxFormPayload>);
const message = rawPhxMessage as PhxIncomingMessage<PhxClickPayload | PhxFormPayload | PhxKeyDownPayload | PhxKeyUpPayload>;
componentManager.onEvent(ws, message);
} else {
console.log("expected component manager for topic", topic);
}
Expand Down
12 changes: 10 additions & 2 deletions src/server/socket/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -75,8 +75,16 @@ export type PhxClickPayload = PhxEventPayload<"click", { value: { value: string
export type PhxFormPayload = PhxEventPayload<"form", { value: string }> & PhxEventUploads;


export type PhxClickEvent = PhxIncomingMessage<PhxClickPayload>
export type PhxFormEvent = PhxIncomingMessage<PhxFormPayload>

// See https://developer.mozilla.org/en-US/docs/Web/API/KeyboardEvent/key/Key_Values
// for all the string values for the key that kicked off the event
//{type: "keyup", event: "key_update", value: {key: "ArrowUp"}}
export type PhxKeyUpPayload = PhxEventPayload<"keyup", { value: { key: string } }>;
export type PhxKeyDownPayload = PhxEventPayload<"keydown", { value: { key: string } }>;


// export type PhxClickEvent = PhxIncomingMessage<PhxClickPayload>
// export type PhxFormEvent = PhxIncomingMessage<PhxFormPayload>


export const newPhxReply = (from: PhxIncomingMessage<unknown>, payload: any): PhxReply => {
Expand Down

0 comments on commit 57bd058

Please sign in to comment.