-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat(core): added onStartTyping utility + docs
- Loading branch information
Showing
6 changed files
with
127 additions
and
2 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,9 +1,12 @@ | ||
import { BROWSER } from 'esm-env'; | ||
|
||
export interface ConfigurableWindow { | ||
/** */ | ||
window?: Window; | ||
} | ||
|
||
export interface ConfigurableDocument { | ||
document?: Document; | ||
} | ||
|
||
export const defaultWindow = BROWSER ? window : undefined; | ||
export const defaultDocument = BROWSER ? document : undefined; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,74 @@ | ||
import { handleEventListener } from '../../browser/index.js'; | ||
import { noop } from '../../__internal__/utils.js'; | ||
import { defaultDocument, type ConfigurableDocument } from '../../__internal__/configurable.js'; | ||
import type { CleanupFunction } from '../../__internal__/types.js'; | ||
|
||
interface OnStartTypingOptions extends ConfigurableDocument { | ||
/** | ||
* Whether to auto-cleanup the event listener or not. | ||
* | ||
* If set to `true`, it must run in the component initialization lifecycle. | ||
* @default true | ||
*/ | ||
autoCleanup?: boolean; | ||
} | ||
|
||
/** | ||
* Fires when users start typing on non-editable elements. | ||
* @param callback The callback for when users start typing on non-editable elements. | ||
* @param options Additional options to customize the behavior. | ||
* @see https://svelte-librarian.github.io/sv-use/docs/core/sensors/on-start-typing | ||
*/ | ||
export function onStartTyping( | ||
callback: (event: KeyboardEvent) => void, | ||
options: OnStartTypingOptions = {} | ||
): CleanupFunction { | ||
const { autoCleanup = true, document = defaultDocument } = options; | ||
|
||
let cleanup: CleanupFunction = noop; | ||
|
||
if (document) { | ||
cleanup = handleEventListener(document, 'keydown', onKeydown, { autoCleanup, passive: true }); | ||
} | ||
|
||
function onKeydown(event: KeyboardEvent) { | ||
if (!isFocusedElementEditable() && isTypedCharValid(event)) { | ||
callback(event); | ||
} | ||
} | ||
|
||
function isFocusedElementEditable() { | ||
if (!document) return; | ||
|
||
if (!document.activeElement) return false; | ||
if (document.activeElement === document.body) return false; | ||
|
||
// Assume <input> and <textarea> elements are editable. | ||
switch (document.activeElement.tagName) { | ||
case 'INPUT': | ||
case 'TEXTAREA': | ||
return true; | ||
} | ||
|
||
// Check if any other focused element id editable. | ||
return document.activeElement.hasAttribute('contenteditable'); | ||
} | ||
|
||
function isTypedCharValid({ keyCode, metaKey, ctrlKey, altKey }: KeyboardEvent) { | ||
if (metaKey || ctrlKey || altKey) return false; | ||
|
||
// 0...9 | ||
if (keyCode >= 48 && keyCode <= 57) return true; | ||
|
||
// A...Z | ||
if (keyCode >= 65 && keyCode <= 90) return true; | ||
|
||
// a...z | ||
if (keyCode >= 97 && keyCode <= 122) return true; | ||
|
||
// All other keys. | ||
return false; | ||
} | ||
|
||
return cleanup; | ||
} |
26 changes: 26 additions & 0 deletions
26
packages/website/src/lib/docs/core/sensors/on-start-typing/Demo.svelte
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,26 @@ | ||
<script lang="ts"> | ||
import { onStartTyping } from '$sv-use/core'; | ||
let input = $state<HTMLInputElement>(); | ||
onStartTyping(() => { | ||
if (input !== document.activeElement) { | ||
input?.focus(); | ||
} | ||
}); | ||
</script> | ||
|
||
<div class="relative flex w-full flex-col gap-2"> | ||
<note class="text-sm">Type anything</note> | ||
<input | ||
bind:this={input} | ||
type="text" | ||
placeholder="Start typing to focus" | ||
class="rounded-md border border-zinc-300 px-3 py-2 text-sm" | ||
/> | ||
<input | ||
type="text" | ||
placeholder="Start typing has no effect here" | ||
class="rounded-md border border-zinc-300 px-3 py-2 text-sm" | ||
/> | ||
</div> |
21 changes: 21 additions & 0 deletions
21
packages/website/src/lib/docs/core/sensors/on-start-typing/index.md
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,21 @@ | ||
# onStartTyping | ||
|
||
Fires when users start typing on non-editable elements. | ||
|
||
## Usage | ||
|
||
```svelte | ||
<script> | ||
import { onStartTyping } from '@sv-use/core'; | ||
let input = $state(); | ||
onStartTyping(() => { | ||
if (input !== document.activeElement) { | ||
input?.focus(); | ||
} | ||
}); | ||
</script> | ||
<input bind:this={input} type="text" /> | ||
``` |