NOTE: Introduced feature in
v0.3.1
.
keybind
is a Svelte Action, which runs a on_bind(event: IKeybindEvent): void
callback whenever your assigned element has focus.
<script>
import {
Box,
TextInput,
keybind,
} from "@kahi-ui/framework";
let input_active = false;
let window_active = false;
</script>
<svelte:window
use:keybind={{
binds: "shift+m",
on_bind: (event) =>
(window_active = event.detail.active),
}}
/>
<Box
palette={window_active
? "affirmative"
: "negative"}
padding="small"
>
Press SHIFT+M to activate bind.
</Box>
<br />
<TextInput
placeholder="Click inside of me for focus, and press CTRL+ENTER to activate bind."
palette={input_active ? "affirmative" : "negative"}
actions={[
[
keybind,
{
binds: "control+enter",
on_bind: (event) =>
(input_active =
event.detail.active),
},
],
]}
/>
Imports
import {
keybind,
action_activate,
action_exit,
action_submit,
navigate_down,
navigate_left,
navigate_right,
navigate_up,
} from "@kahi-ui/framework";
Compatibility
Svelte Actions are always ran on the Browser only with Javascript is enabled. So should not be used for critical end-user functionality, only progressive enhancement.
Built-In Shortcuts
NOTE: Introduced feature in
v0.5.3
.
The Framework provides a series of built-in keybinds using pre-configured standard keybinds that you can use in your own code.
-
action_activate
— Used for activating the current context, e.g. a focused label.Enter
,
-
action_exit
— Used for exiting the current context, e.g. a prompt.Esc
-
action_submit
— Used for submitting the current context, e.g. a focused input.Enter
-
navigate_down
— Used for navigating to the next item down.ArrowDown
- Repeatable,
250ms
throttle
-
navigate_left
— Used for navigating to the next item left.ArrowLeft
- Repeatable,
250ms
throttle
-
navigate_right
— Used for navigating to the next item right.ArrowRight
- Repeatable,
250ms
throttle
-
navigate_up
— Used for navigating to the next item up.ArrowUp
- Repeatable,
250ms
throttle
<script>
import {
TextInput,
action_submit,
} from "@kahi-ui/framework";
let active = false;
let value = "";
</script>
<TextInput
palette={active ? "affirmative" : "negative"}
placeholder="Enter a value and then press enter."
actions={[
[
action_submit,
{
on_bind: (event) => {
active = event.detail.active;
if (active) {
alert(
`You submitted "${value}" as your input!`
);
}
},
},
],
]}
bind:value
/>
Binding
You can configure which set of keys you want to activate the binding by listing the Key Values in a key1+key2+keyN
format via the IKeybindOptions.binds: string | string[]
option. You can (theoretically) listen to as many keys as you want.
<script>
import {Box, keybind} from "@kahi-ui/framework";
let active = false;
</script>
<svelte:window
use:keybind={{
binds: "shift+m",
on_bind: (event) =>
(active = event.detail.active),
}}
/>
<Box
palette={active ? "affirmative" : "negative"}
padding="small"
>
Press SHIFT+M to activate bind.
</Box>
You can also have a single binding listen to multiple sets of keys by passing in an array.
<script>
import {
Box,
Text,
keybind,
} from "@kahi-ui/framework";
let active = false;
</script>
<svelte:window
use:keybind={{
binds: ["control+enter", "shift+m"],
on_bind: (event) =>
(active = event.detail.active),
}}
/>
<Box
palette={active ? "affirmative" : "negative"}
padding="small"
>
Press CTRL+ENTER <Text is="strong">OR</Text> SHIFT+M
to activate bind.
</Box>
Active Binding
You can detect if the keybind is currently being pressed via the IKeybindEvent.detail.active
member.
Event Management
Just like with regular events, you can use IKeybindEvent.preventDefault: () => void
/ IKeybindEvent.stopPropagation: () => void
functions to cancel propagation / the Browser using default behavior.
<script>
import {
Box,
Code,
keybind,
} from "@kahi-ui/framework";
let active = false;
</script>
<svelte:window
use:keybind={{
binds: "f5",
on_bind: (event) => {
event.preventDefault();
active = event.detail.active;
},
}}
/>
<Box
palette={active ? "affirmative" : "negative"}
padding="small"
>
Press F5 to activate bind.
</Box>
Repeat
You can enable listening to repeat binding activations (e.g. binding being held down) via the IKeybindOptions.repeat: boolean
option. And then detect if the current callback is a repeat via the IKeybindEvent.detail.repeat
member.
<script>
import {
Box,
Code,
keybind,
} from "@kahi-ui/framework";
let active = false;
let value = "";
</script>
<svelte:window
use:keybind={{
binds: "shift+m",
repeat: true,
on_bind: (event) => {
active = event.detail.active;
if (event.detail.repeat) {
value += "I am on repeat!\n";
}
},
}}
/>
<Box
palette={active ? "affirmative" : "negative"}
padding="small"
>
Press and hold SHIFT+M to activate bind.
</Box>
<Code is="pre">
{value}
</Code>
Throttling
When listening to repeat binding activations, you can throttle activation callbacks for every N amount of milliseconds via the IKeybindOptions.repeat_throttle: number
option.
<script>
import {
Box,
Code,
keybind,
} from "@kahi-ui/framework";
let active = false;
let value = "";
</script>
<svelte:window
use:keybind={{
binds: "shift+m",
repeat: true,
repeat_throttle: 100,
on_bind: (event) => {
active = event.detail.active;
if (event.detail.repeat) {
value += "I am on repeat!\n";
}
},
}}
/>
<Box
palette={active ? "affirmative" : "negative"}
padding="small"
>
Press and hold SHIFT+M to activate bind.
</Box>
<Code is="pre">
{value}
</Code>
NOTE: Introduced feature in
v0.4.13
.
NOTE: By using
throttle_cancel
in this pseudo search UI, the Browser scrolling via arrow keys is disabled.
You can also enable the keybind
Action to automatically call preventDefault
/ stopPropagation
on all throttled keydown
/ keyup
events via the IKeybindOptions.throttle_cancel: boolean
option.
<script>
import {
Box,
Code,
TextInput,
Scrollable,
Stack,
keybind,
} from "@kahi-ui/framework";
import {tick} from "svelte";
let scrollable_element;
let active = false;
let current = 0;
async function update_scrollable() {
// NOTE: Have to wait for the DOM to update after our render runs again
await tick();
const current_element =
scrollable_element.querySelector(
`[data-palette="accent"]`
);
if (current_element) {
current_element.scrollIntoView({
block: "nearest",
behavior: "smooth",
});
}
}
</script>
<TextInput
palette={active ? "affirmative" : "negative"}
placeholder="Focus me and press and hold ARROWDOWN / ARROWUP"
actions={[
[
keybind,
{
binds: "arrowdown",
repeat: true,
repeat_throttle: 100,
throttle_cancel: true,
on_bind: (event) => {
if (!event.detail.active) return;
event.preventDefault();
current = Math.min(current + 1, 9);
update_scrollable();
},
},
],
[
keybind,
{
binds: "arrowup",
repeat: true,
repeat_throttle: 100,
throttle_cancel: true,
on_bind: (event) => {
if (!event.detail.active) return;
event.preventDefault();
current = Math.max(current - 1, 0);
update_scrollable();
},
},
],
]}
on:focusin={() => (active = true)}
on:focusout={() => (active = false)}
/>
<Box>
<Scrollable
bind:element={scrollable_element}
style="height:5rem;"
>
{#each new Array(10).fill(null) as _, index}
<Box
palette={current === index
? "accent"
: "auto"}
>
Search Result #{index + 1}
</Box>
{/each}
</Scrollable>
</Box>