Skip to content

Commit

Permalink
Pressed keys (#58)
Browse files Browse the repository at this point in the history
* poc

* pressed keys demo

* demo adjustments

* jsdoc

* docs

* changeset

* lint

* add even more flare to demo

* rename pressed to has

* format
  • Loading branch information
TGlide authored May 23, 2024
1 parent 27e03ad commit 68143e7
Show file tree
Hide file tree
Showing 15 changed files with 236 additions and 10 deletions.
5 changes: 5 additions & 0 deletions .changeset/nice-peas-begin.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"runed": minor
---

add PressedKeys utility
47 changes: 47 additions & 0 deletions logo.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ export type ElementSizeOptions = {
*
* @returns an object with `width` and `height` properties.
*
* @see {@link https://runed.dev/docs/utilities/use-element-size}
* @see {@link https://runed.dev/docs/utilities/element-size}
*/
export class ElementSize {
#size = $state({
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import { untrack } from "svelte";
* Returns an object with the mounted state of the component
* that invokes this function.
*
* @see {@link https://runed.dev/docs/utilities/use-mounted}
* @see {@link https://runed.dev/docs/utilities/is-mounted}
*/
export class IsMounted {
#isMounted = $state(false);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
*
* Useful for checking if a browser API is supported before attempting to use it.
*
* @see {@link https://runed.dev/docs/utilities/use-supported}
* @see {@link https://runed.dev/docs/utilities/supported}
*/
export class IsSupported {
#current = $state(false);
Expand Down
43 changes: 43 additions & 0 deletions packages/runed/src/lib/utilities/PressedKeys/PressedKeys.svelte.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
import { addEventListener } from "$lib/internal/utils/event.js";

/**
* Tracks which keys are currently pressed.
*
* @see {@link https://runed.dev/docs/utilities/pressed-keys}
*/
export class PressedKeys {
#pressedKeys = $state<Array<string>>([]);

constructor() {
$effect(() => {
const callbacks: VoidFunction[] = [];

callbacks.push(
addEventListener(window, "keydown", (e) => {
const key = e.key.toLowerCase();
if (!this.#pressedKeys.includes(key)) {
this.#pressedKeys.push(key);
}
})
);

callbacks.push(
addEventListener(window, "keyup", (e) => {
const key = e.key.toLowerCase();
this.#pressedKeys = this.#pressedKeys.filter((k) => k !== key);
})
);

return () => callbacks.forEach((c) => c());
});
}

has(...keys: string[]) {
const normalizedKeys = keys.map((key) => key.toLowerCase());
return normalizedKeys.every((key) => this.#pressedKeys.includes(key));
}

get all() {
return this.#pressedKeys;
}
}
1 change: 1 addition & 0 deletions packages/runed/src/lib/utilities/PressedKeys/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export { PressedKeys } from "./PressedKeys.svelte.js";
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import type { Getter } from "$lib/internal/types.js";
/**
* Holds the previous value of a getter.
*
* @see {@link https://runed.dev/docs/utilities/use-previous}
* @see {@link https://runed.dev/docs/utilities/previous}
*/
export class Previous<T> {
#previous = $state<T | undefined>(undefined);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ type StateHistoryOptions = {
/**
* Tracks the change history of a value, providing undo and redo capabilities.
*
* @see {@link https://runed.dev/docs/utilities/use-state-history}
* @see {@link https://runed.dev/docs/utilities/state-history}
*/
export class StateHistory<T> {
#redoStack = $state<LogEvent<T>[]>([]);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import { Readable } from "../Readable/readable.svelte.js";
* An object holding a reactive value that is equal to `document.activeElement`.
* It automatically listens for changes, keeping the reference up to date.
*
* @see {@link https://runed.dev/docs/utilities/use-active-element}
* @see {@link https://runed.dev/docs/utilities/active-element}
*/
export const activeElement = new Readable<Element | null>(null, (set, insideEffect) => {
function update() {
Expand Down
1 change: 1 addition & 0 deletions packages/runed/src/lib/utilities/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,3 +9,4 @@ export * from "./Previous/index.js";
export * from "./watch/index.js";
export * from "./Readable/index.js";
export * from "./Debounced/index.js";
export * from "./PressedKeys/index.js";
31 changes: 31 additions & 0 deletions sites/docs/content/utilities/pressed-keys.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
---
title: PressedKeys
description: Tracks which keys are currently pressed
category: Browser
---

<script>
import Demo from '$lib/components/demos/pressed-keys.svelte';
</script>

## Demo

<Demo />

## Usage

With an instance of `PressedKeys`, you can use the `has` method.

```ts
const keys = new PressedKeys();

const isArrowDownPressed = $derived(keys.has("ArrowDown"));
const isCtrlAPressed = $derived(keys.has("Control", "a"));
```

Or get all of the currently pressed keys:

```ts
const keys = new PressedKeys();
console.log(keys.all());
```
68 changes: 68 additions & 0 deletions sites/docs/src/lib/components/demos/pressed-keys.svelte
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
<script lang="ts">
import { PressedKeys, watch } from "runed";
import { fade, scale } from "svelte/transition";
import RunedIcon from "$lib/components/logos/runed-icon.svelte";
const keys = new PressedKeys();
const toPress = "Runed".split("");
const allPressed = $derived(keys.has(...toPress));
let guessedCorrectly = $state(false);
$effect(() => {
if (allPressed) {
guessedCorrectly = true;
}
});
let triedInputting = $state(false);
watch(
() => keys.all,
() => {
triedInputting = false;
}
);
</script>

<div class="relative rounded-md bg-card p-8">
<div
class="relative mx-auto flex w-min items-center justify-center gap-2 transition-all duration-300
{allPressed ? 'translate-x-[1.625rem]' : ''}"
>
{#if allPressed}
<div
transition:scale={{ start: 0.75, duration: 300 }}
class="absolute left-0 top-1/2 -translate-y-1/2 translate-x-[calc(-100%-0.5rem)]"
>
<RunedIcon class="size-11 " />
</div>
{/if}
{#each toPress as key}
<!-- svelte-ignore a11y_no_static_element_interactions a11y_click_events_have_key_events -->
<div
class="grid size-12 place-items-center rounded-lg border transition-all duration-200
{allPressed ? 'border-brand' : 'border-border'} bg-background"
onclick={() => (triedInputting = true)}
>
{#if keys.has(key)}
<span
class="duration-250 text-xl font-bold text-foreground transition-all"
transition:fade={{ duration: 100 }}
>
{key}
</span>
{/if}
</div>
{/each}
</div>
<p class="text-center">{guessedCorrectly ? "You did it! 🎉" : "Try and guess the password 👀"}</p>

{#if !guessedCorrectly && triedInputting}
<p
transition:fade={{ duration: 300 }}
class="absolute bottom-2 right-2 mb-0 text-center text-sm text-foreground/50"
>
Press any key to start, no need to select anything
</p>
{/if}
</div>
4 changes: 0 additions & 4 deletions sites/docs/src/lib/components/logos/index.ts

This file was deleted.

34 changes: 34 additions & 0 deletions sites/docs/src/lib/components/logos/runed-icon.svelte
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
<script lang="ts">
import type { SVGAttributes } from "svelte/elements";
let { ...restProps }: SVGAttributes<SVGElement> = $props();
</script>

<svg
width="122"
height="122"
viewBox="0 0 122 122"
fill="none"
xmlns="http://www.w3.org/2000/svg"
{...restProps}
>
<mask
id="mask0_1_13"
style="mask-type:luminance"
maskUnits="userSpaceOnUse"
x="0"
y="0"
width="122"
height="122"
>
<path d="M121.506 0H0V121.506H121.506V0Z" fill="white" />
</mask>
<g mask="url(#mask0_1_13)">
<path
fill-rule="evenodd"
clip-rule="evenodd"
d="M22.7164 0C10.1705 0 0 10.1705 0 22.7164V98.79C0 111.335 10.1705 121.506 22.7164 121.506H98.79C111.336 121.506 121.506 111.335 121.506 98.79V22.7164C121.506 10.1705 111.336 0 98.79 0H22.7164ZM48.6384 19.836L77.9001 36.4316C79.4403 37.3047 80.4152 38.9127 80.4836 40.6741C80.5516 42.4418 79.7121 44.1115 78.2399 45.1044L53.5554 61.7486L79.384 94.0559L79.388 94.0609C81.1205 96.252 80.7921 99.452 78.5787 101.22C77.6326 101.977 76.4988 102.335 75.3938 102.335C73.8991 102.335 72.4162 101.684 71.4057 100.417L51.2228 75.1658V97.0981C51.2228 99.9185 48.943 102.198 46.1222 102.198C43.3013 102.198 41.0215 99.9185 41.0215 97.0981V24.2708C41.0215 22.4643 41.9759 20.7814 43.5563 19.867C45.1369 18.9433 47.0699 18.9494 48.6353 19.8343L48.6384 19.836ZM51.2228 51.0277V33.0234L65.7205 41.2511L51.2228 51.0277Z"
fill="#EC4F27"
/>
</g>
</svg>

0 comments on commit 68143e7

Please sign in to comment.