Skip to content

Commit

Permalink
✨ validate the nickname when the user stops typing (#230) (#235)
Browse files Browse the repository at this point in the history
* ✨ feat: validate nickname when the user stops typing after some delay
* ✨ feat(utils): added stops typing svelte action
* ✨ feat(utils): added debounce helper
  • Loading branch information
rezk2ll authored Jun 3, 2024
1 parent 7edae0b commit da5d6e6
Show file tree
Hide file tree
Showing 4 changed files with 83 additions and 3 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,10 @@
lastName,
nickName,
accepted
}).success || nickNameTaken || formLoading || !nickNamechecked;
}).success ||
nickNameTaken ||
formLoading ||
!nickNamechecked;
const handler = async () => {
if (disabled) return;
Expand Down Expand Up @@ -117,6 +120,7 @@
info={true}
infoTitle={$t('Matrix ID/Email')}
infoDescription={$t('username_info_tooltip')}
onStopTyping={checkNickName}
/>
{#if nickNameTaken === true}
<AvailableNicknames
Expand Down
14 changes: 13 additions & 1 deletion registration/src/lib/components/input/TextField.svelte
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
<script lang="ts">
import { clickOutside } from '$utils/html';
import { clickOutside, stopTyping } from '$utils/html';
import ErrorIcon from '$components/icons/ErrorIcon.svelte';
import InfoIcon from '$components/icons/InfoIcon.svelte';
import Spin from '$components/icons/SpinnerIcon.svelte';
import Valid from '$components/icons/ValidIcon.svelte';
import InfoTooltip from '$components/display/InfoTooltip.svelte';
import { onMount } from 'svelte';
export let label: string;
export let placeholder: string;
Expand All @@ -19,8 +20,17 @@
export let info: boolean = false;
export let infoTitle: string = '';
export let infoDescription: string = '';
export let onStopTyping: (() => void) | undefined = undefined;
let inputRef: HTMLInputElement;
let showInfo = false;
const action = onStopTyping ? stopTyping : () => {};
onMount(() => {
if (onStopTyping) {
inputRef.addEventListener('stopTyping', onStopTyping);
}
});
$: notValid = value.length > 0 && isInValid;
</script>
Expand All @@ -30,6 +40,8 @@
<InfoTooltip title={infoTitle} description={infoDescription} bind:show={showInfo} />
{/if}
<input
bind:this={inputRef}
use:action
autocomplete="off"
required
id={name}
Expand Down
45 changes: 44 additions & 1 deletion registration/src/lib/utils/html.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,21 @@
export const clickOutside = (node: HTMLElement, onEventFunction: () => void) => {
import { debounce } from '$utils/timeout';

type ActionType = {
destroy: () => void;
};

/**
* Click outside svelte action.
*
* calls a handler when the user clicks outside
*
* @param {HTMLElement} node - The node to listen to.
* @param {Function} onEventFunction - The function to call when the event is triggered.
* @example
* <div use:clickOutside={closeModal} />
* @returns {ActionType}
*/
export const clickOutside = (node: HTMLElement, onEventFunction: () => void): ActionType => {
const handleClick = (event: Event) => {
const path = event.composedPath();

Expand All @@ -15,3 +32,29 @@ export const clickOutside = (node: HTMLElement, onEventFunction: () => void) =>
}
};
};

/**
* Stop typing svelte action.
*
* fires a custom event when the user stops typing after some delay.
*
* @param {HTMLElement} node - The node to listen to.
* @example
* <input use:stopTyping on:stopTyping={typing} />
* @returns {ActionType}
*/
export const stopTyping = (node: HTMLElement): ActionType => {
const handler = debounce((event: Event) => {
if (node.contains(event.target as Node)) {
node.dispatchEvent(new CustomEvent('stopTyping'));
}
});

node.addEventListener('input', handler);

return {
destroy() {
node.removeEventListener('input', handler);
}
};
};
21 changes: 21 additions & 0 deletions registration/src/lib/utils/timeout.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
/**
* Debounce function
*
* @param fn Function to debounce
* @param waitFor Time to wait before executing the function
*
* @example
* const fn = () => console.log('Debounced!');
* const debounced = debounce(fn, 1000);
*/
export const debounce = <Params extends unknown[]>(
fn: (...args: Params) => void,
waitFor = 800
) => {
let timer: NodeJS.Timeout;

return (...args: Params) => {
clearTimeout(timer);
timer = setTimeout(() => fn(...args), waitFor);
};
};

0 comments on commit da5d6e6

Please sign in to comment.