From 0b2ac50bfe0ecdfb8799bf5223871c4d2d845dcd Mon Sep 17 00:00:00 2001 From: Daniel Castillo Date: Wed, 25 Sep 2024 10:11:34 -0400 Subject: [PATCH] feat: add hooks for js and jsx #1 --- package.json | 8 +- snippets/js.code-snippets | 1664 +++++++++++++++++++++++++++++++ snippets/snippets.code-snippets | 1 - 3 files changed, 1670 insertions(+), 3 deletions(-) create mode 100644 snippets/js.code-snippets delete mode 100644 snippets/snippets.code-snippets diff --git a/package.json b/package.json index bd2535c..d96b5a6 100644 --- a/package.json +++ b/package.json @@ -12,8 +12,12 @@ "contributes": { "snippets": [ { - "language": "typescript", - "path": "./snippets/snippets.code-snippets" + "language": "javascript", + "path": "./snippets/js.code-snippets" + }, + { + "language": "javascriptreact", + "path": "./snippets/js.code-snippets" } ] } diff --git a/snippets/js.code-snippets b/snippets/js.code-snippets new file mode 100644 index 0000000..9aaa953 --- /dev/null +++ b/snippets/js.code-snippets @@ -0,0 +1,1664 @@ +{ + "useArray": { + "prefix": "useArray", + "body": [ + "import { useState } from 'react';", + "", + "export default function useArray(initialArray) {", + " const [array, setArray] = useState(initialArray);", + "", + " const push = (element) => {", + " setArray([...array, element]);", + " };", + "", + " const filter = (callback) => {", + " setArray(array.filter(callback));", + " };", + "", + " const update = (index, newElement) => {", + " setArray([", + " ...array.slice(0, index),", + " newElement,", + " ...array.slice(index + 1, array.length),", + " ]);", + " };", + "", + " const remove = (index) => {", + " setArray([", + " ...array.slice(0, index),", + " ...array.slice(index + 1, array.length),", + " ]);", + " };", + "", + " const clear = () => {", + " setArray([]);", + " };", + "", + " return {", + " array,", + " set: setArray,", + " push,", + " filter,", + " update,", + " remove,", + " clear,", + " };", + "}" + ], + "description": "React hook to manage and manipulate arrays" + }, + "useAudio": { + "prefix": "useAudio", + "body": [ + "import { useEffect, useState } from 'react';", + "", + "export const useAudio = (ref) => {", + " const audio = ref.current;", + "", + " const [audioState, setAudioState] = useState({", + " isPaused: audio ? audio?.paused : true,", + " isMuted: audio ? audio?.muted : false,", + " currentVolume: audio ? audio?.volume : 100,", + " currentTime: audio ? audio?.currentTime : 0,", + " });", + "", + " const play = () => {", + " audio?.play();", + " setAudioState((prev) => {", + " return {", + " ...prev,", + " isPaused: false,", + " isMuted: audio ? audio.muted : prev.isMuted,", + " };", + " });", + " };", + "", + " const pause = () => {", + " audio?.pause();", + " setAudioState((prev) => {", + " return {", + " ...prev,", + " isPaused: true,", + " };", + " });", + " };", + "", + " const handlePlayPauseControl = (e) => {", + " setAudioState((prev) => {", + " return {", + " ...prev,", + " isPaused: e.target.paused,", + " };", + " });", + " };", + "", + " const togglePause = () => (audio?.paused ? play() : pause());", + "", + " const handleVolume = (delta) => {", + " const deltaDecimal = delta / 100;", + "", + " if (audio) {", + " let newVolume = audio?.volume + deltaDecimal;", + "", + " if (newVolume >= 1) {", + " newVolume = 1;", + " } else if (newVolume <= 0) {", + " newVolume = 0;", + " }", + "", + " audio.volume = newVolume;", + " setAudioState((prev) => {", + " return {", + " ...prev,", + " currentVolume: newVolume * 100,", + " };", + " });", + " }", + " };", + "", + " const handleVolumeControl = (e) => {", + " if (e.target && audio) {", + " const newVolume = e.target.volume * 100;", + "", + " handleMute(audio.muted);", + " setAudioState((prev) => ({", + " ...prev,", + " currentVolume: newVolume,", + " }));", + " }", + " };", + "", + " const handleMute = (mute) => {", + " if (audio) {", + " audio.muted = mute;", + " setAudioState((prev) => {", + " return {", + " ...prev,", + " isMuted: mute,", + " };", + " });", + " }", + " };", + "", + " const handleTime = (delta = 5) => {", + " if (audio) {", + " let newTime = audio.currentTime + delta;", + "", + " if (newTime >= audio.duration) {", + " newTime = audio.duration;", + " } else if (newTime <= 0) {", + " newTime = 0;", + " }", + "", + " audio.currentTime = newTime;", + " setAudioState((prev) => {", + " return {", + " ...prev,", + " currentTime: newTime,", + " };", + " });", + " }", + " };", + "", + " const handleTimeControl = (e) => {", + " setAudioState((prev) => {", + " return {", + " ...prev,", + " currentTime: e.target.currentTime,", + " };", + " });", + " };", + "", + " useEffect(() => {", + " return () => {", + " pause();", + " };", + " }, []);", + "", + " useEffect(() => {", + " if (audio) {", + " audio.addEventListener('volumechange', handleVolumeControl);", + " audio.addEventListener('play', handlePlayPauseControl);", + " audio.addEventListener('pause', handlePlayPauseControl);", + " audio.addEventListener('timeupdate', handleTimeControl);", + "", + " return () => {", + " audio.removeEventListener('volumechange', handleVolumeControl);", + " audio.removeEventListener('play', handlePlayPauseControl);", + " audio.removeEventListener('pause', handlePlayPauseControl);", + " audio.removeEventListener('timeupdate', handleTimeControl);", + " };", + " }", + " }, [audio]);", + "", + " return {", + " ...audioState,", + " play,", + " pause,", + " togglePause,", + " increaseVolume: (increase = 5) => handleVolume(increase),", + " decreaseVolume: (decrease = 5) => handleVolume(decrease * -1),", + " mute: () => handleMute(true),", + " unmute: () => handleMute(false),", + " toggleMute: () => handleMute(!audio?.muted),", + " forward: (increase = 5) => handleTime(increase),", + " back: (decrease = 5) => handleTime(decrease * -1),", + " };", + "};" + ], + "description": "React hook to manage an audio" + }, + "useBattery": { + "prefix": "useBattery", + "body": [ + "import { useState, useEffect } from 'react';", + "", + "export const useBattery = () => {", + " const [batteryState, setBatteryState] = useState({", + " supported: true,", + " loading: true,", + " level: null,", + " charging: null,", + " chargingTime: null,", + " dischargingTime: null,", + " });", + "", + " useEffect(() => {", + " let battery;", + "", + " const handleBatteryChange = () => {", + " setBatteryState({", + " supported: true,", + " loading: false,", + " level: battery.level,", + " charging: battery.charging,", + " chargingTime: battery.chargingTime,", + " dischargingTime: battery.dischargingTime,", + " });", + " };", + "", + " if (!navigator.getBattery) {", + " setBatteryState((batteryState) => ({", + " ...batteryState,", + " supported: false,", + " loading: false,", + " }));", + " return;", + " }", + "", + " navigator.getBattery().then((_battery) => {", + " battery = _battery;", + " handleBatteryChange();", + "", + " _battery.addEventListener('levelchange', handleBatteryChange);", + " _battery.addEventListener('chargingchange', handleBatteryChange);", + " _battery.addEventListener('chargingtimechange', handleBatteryChange);", + " _battery.addEventListener('dischargingtimechange', handleBatteryChange);", + " });", + "", + " return () => {", + " if (battery) {", + " battery.removeEventListener('levelchange', handleBatteryChange);", + " battery.removeEventListener('chargingchange', handleBatteryChange);", + " battery.removeEventListener('chargingtimechange', handleBatteryChange);", + " battery.removeEventListener(", + " 'dischargingtimechange',", + " handleBatteryChange", + " );", + " }", + " };", + " }, []);", + "", + " return batteryState;", + "};" + ], + "description": "React hook to track the battery status of a user's device" + }, + "useClipboard": { + "prefix": "useClipboard", + "body": [ + "import { useState } from 'react';", + "", + "export const useClipboard = () => {", + " const [copiedText, setCopiedText] = useState('');", + "", + " const copyToClipboard = (value) => {", + " return new Promise((resolve, reject) => {", + " try {", + " if (navigator?.clipboard?.writeText) {", + " navigator.clipboard", + " .writeText(value)", + " .then(() => {", + " setCopiedText(value);", + " resolve(value);", + " })", + " .catch((e) => {", + " setCopiedText(null);", + " reject(e);", + " });", + " } else {", + " setCopiedText(null);", + " throw new Error('Clipboard not supported');", + " }", + " } catch (e) {", + " reject(e);", + " }", + " });", + " };", + "", + " return { copiedText, copyToClipboard };", + "};" + ], + "description": "React hook to copy to the clipboard" + }, + "useCountdown": { + "prefix": "useCountdown", + "body": [ + "import { useState, useEffect } from 'react';", + "", + "export const useCountdown = (min, max) => {", + " const [count, setCount] = useState(max);", + " const [paused, setPaused] = useState(false);", + " const [isOver, setIsOver] = useState(false);", + "", + " useEffect(() => {", + " if (paused) {", + " return;", + " }", + "", + " const interval = setInterval(() => {", + " setCount((prev) => prev - 1);", + " }, 1000);", + "", + " if (count <= min) {", + " setIsOver(true);", + " clearInterval(interval);", + " return;", + " }", + "", + " return () => clearInterval(interval);", + " }, [count, min, max, paused]);", + "", + " return {", + " current: count.toString(),", + " isPaused: paused,", + " isOver,", + " pause: () => setPaused(true),", + " play: () => setPaused(false),", + " reset: () => {", + " setIsOver(false);", + " setCount(max);", + " },", + " togglePause: () => {", + " setPaused(!paused);", + " },", + " };", + "};" + ], + "description": "React hook to create a countdown functionality" + }, + "useCountup": { + "prefix": "useCountup", + "body": [ + "import { useState, useEffect } from 'react';", + "", + "export const useCountup = (min, max) => {", + " const [count, setCount] = useState(min);", + " const [paused, setPaused] = useState(false);", + " const [isOver, setIsOver] = useState(false);", + "", + " useEffect(() => {", + " if (paused) {", + " return;", + " }", + "", + " const interval = setInterval(() => {", + " setCount((prev) => prev + 1);", + " }, 1000);", + "", + " if (count >= max) {", + " setIsOver(true);", + " clearInterval(interval);", + " return;", + " }", + "", + " return () => clearInterval(interval);", + " }, [count, min, max, paused]);", + "", + " return {", + " current: count.toString(),", + " isPaused: paused,", + " isOver,", + " pause: () => setPaused(true),", + " play: () => setPaused(false),", + " reset: () => {", + " setIsOver(false);", + " setCount(min);", + " },", + " togglePause: () => {", + " setPaused(!paused);", + " },", + " };", + "};" + ], + "description": "React hook to create a countup functionality" + }, + "useDebounce": { + "prefix": "useDebounce", + "body": [ + "import { useEffect, useState } from 'react';", + "", + "export const useDebounce = (value, delay) => {", + " const [debouncedValue, setDebouncedValue] = useState(value);", + "", + " useEffect(() => {", + " const handleTimeout = setTimeout(() => {", + " setDebouncedValue(value);", + " }, delay);", + "", + " return () => {", + " clearTimeout(handleTimeout);", + " };", + " }, [value, delay]);", + "", + " return debouncedValue;", + "};" + ], + "description": "React hook to delay the execution of function or state update" + }, + "useDownload": { + "prefix": "useDownload", + "body": [ + "import { useState } from 'react';", + "", + "export const useDownload = () => {", + " const [error, setError] = useState(null);", + " const [isDownloading, setIsDownloading] = useState(false);", + " const [progress, setProgress] = useState(null);", + "", + " const handleResponse = async (response) => {", + " if (!response.ok) {", + " throw new Error('Could not download file');", + " }", + "", + " const contentLength = response.headers.get('content-length');", + " const reader = response.clone().body?.getReader();", + "", + " if (!contentLength || !reader) {", + " const blob = await response.blob();", + "", + " return createBlobURL(blob);", + " }", + "", + " const stream = await getStream(contentLength, reader);", + " const newResponse = new Response(stream);", + " const blob = await newResponse.blob();", + "", + " return createBlobURL(blob);", + " };", + "", + " const getStream = async (contentLength, reader) => {", + " let loaded = 0;", + " const total = parseInt(contentLength, 10);", + "", + " return new ReadableStream({", + " async start(controller) {", + " try {", + " for (;;) {", + " const { done, value } = await reader.read();", + "", + " if (done) break;", + "", + " loaded += value.byteLength;", + " const percentage = Math.trunc((loaded / total) * 100);", + " setProgress(percentage);", + " controller.enqueue(value);", + " }", + " } catch (error) {", + " controller.error(error);", + " throw error;", + " } finally {", + " controller.close();", + " }", + " },", + " });", + " };", + "", + " const createBlobURL = (blob) => {", + " return window.URL.createObjectURL(blob);", + " };", + "", + " const handleDownload = (fileName, url) => {", + " const link = document.createElement('a');", + "", + " link.href = url;", + " link.setAttribute('download', fileName);", + " document.body.appendChild(link);", + " link.click();", + " document.body.removeChild(link);", + " window.URL.revokeObjectURL(url);", + " };", + "", + " const downloadFile = async (fileName, fileUrl) => {", + " setIsDownloading(true);", + " setError(null);", + " setProgress(null);", + "", + " try {", + " const response = await fetch(fileUrl);", + " const url = await handleResponse(response);", + "", + " handleDownload(fileName, url);", + " } catch (error) {", + " setError(error);", + " } finally {", + " setIsDownloading(false);", + " }", + " };", + "", + " return {", + " error,", + " isDownloading,", + " progress,", + " downloadFile,", + " };", + "};" + ], + "description": "React hook to download a file" + }, + "useEventListener": { + "prefix": "useEventListener", + "body": [ + "import { useEffect, useRef } from 'react';", + "", + "export default function useEventListener(", + " eventName,", + " callback,", + " element = window", + ") {", + " const callbackRef = useRef(callback);", + "", + " useEffect(() => {", + " callbackRef.current = callback;", + " }, [callback]);", + "", + " useEffect(() => {", + " if (!(element && element.addEventListener)) {", + " return;", + " }", + "", + " const handler = (e) => callbackRef.current(e);", + "", + " element.addEventListener(eventName, handler);", + "", + " return () => {", + " element.removeEventListener(eventName, handler);", + " };", + " }, [eventName, element]);", + "}" + ], + "description": "React hook to listen for events on a target element" + }, + "useFavicon": { + "prefix": "useFavicon", + "body": [ + "import { useEffect, useState } from 'react';", + "", + "export const useFavicon = () => {", + " const [faviconUrl, setFaviconUrl] = useState('');", + "", + " useEffect(() => {", + " let link = document.querySelector(`link[rel~='icon']`);", + "", + " if (!link) {", + " link = document.createElement('link');", + " link.type = 'image/x-icon';", + " link.rel = 'icon';", + " document.head.appendChild(link);", + " }", + "", + " link.href = faviconUrl;", + " }, [faviconUrl]);", + "", + " const changeFavicon = (newFavicon) => setFaviconUrl(newFavicon);", + "", + " return changeFavicon;", + "};" + ], + "description": "React hook to change the page favicon" + }, + "useFetch": { + "prefix": "useFetch", + "body": [ + "import { useState, useEffect } from 'react';", + "", + "export const useFetch = (url, reqOpt) => {", + " const [data, setData] = useState();", + " const [error, setError] = useState();", + " const [isLoading, setIsLoading] = useState(false);", + " const [isSuccess, setIsSuccess] = useState(false);", + "", + " const fetchData = async () => {", + " setIsLoading(true);", + "", + " try {", + " const res = await fetch(url, reqOpt && reqOpt);", + " const data = await res.json();", + "", + " if (res.status === 200) {", + " setIsSuccess(true);", + " setData(data);", + " setError(undefined);", + " } else {", + " setIsSuccess(false);", + " setError(data);", + " setData(undefined);", + " }", + " } catch (e) {", + " setIsSuccess(false);", + " setData(undefined);", + " if (e instanceof Error) {", + " setError(e);", + " }", + " }", + "", + " setIsLoading(false);", + " };", + "", + " useEffect(() => {", + " fetchData();", + " }, []);", + "", + " const refetch = () => fetchData();", + "", + " return { data, error, isLoading, isError: !isSuccess, isSuccess, refetch };", + "};" + ], + "description": "React hook to fetch data from an API" + }, + "useFirstRender": { + "prefix": "useFirstRender", + "body": [ + "import { useRef, useEffect } from 'react';", + "", + "export const useFirstRender = () => {", + " const firstRender = useRef(true);", + "", + " useEffect(() => {", + " firstRender.current = false;", + " }, []);", + "", + " return firstRender.current;", + "};" + ], + "description": "React hook to detect if it is the first render of a component" + }, + "useFirstVisit": { + "prefix": "useFirstVisit", + "body": [ + "import { useState, useEffect } from 'react';", + "", + "export const useFirstVisit = () => {", + " const [isFirstVisit, setIsFirstVisit] = useState(false);", + "", + " useEffect(() => {", + " const firstVisit = localStorage.getItem('firstVisit');", + "", + " if (firstVisit === null) {", + " localStorage.setItem('firstVisit', 'false');", + " setIsFirstVisit(true);", + " }", + " }, []);", + "", + " return isFirstVisit;", + "};" + ], + "description": "React hook to detect if it is the user's first visit" + }, + "useGeolocation": { + "prefix": "useGeolocation", + "body": [ + "import { useState } from 'react';", + "", + "export function useGeolocation(defaultPosition = null) {", + " const [isLoading, setIsLoading] = useState(false);", + " const [position, setPosition] = useState(defaultPosition);", + " const [error, setError] = useState(null);", + "", + " function getPosition() {", + " if (!navigator.geolocation)", + " return setError('Your browser does not support geolocation');", + "", + " setIsLoading(true);", + " navigator.geolocation.getCurrentPosition(", + " (pos) => {", + " setPosition({", + " lat: pos.coords.latitude,", + " lng: pos.coords.longitude,", + " });", + " setIsLoading(false);", + " },", + " (error) => {", + " setError(error.message);", + " setIsLoading(false);", + " }", + " );", + " }", + "", + " return { isLoading, position, error, getPosition };", + "}" + ], + "description": "React hook to get user's latitude and longitude" + }, + "useHover": { + "prefix": "useHover", + "body": [ + "import { useState, useEffect } from 'react';", + "", + "export const useHover = (ref) => {", + " const [isHovered, setIsHovered] = useState(false);", + "", + " const handleMouseEnter = () => setIsHovered(true);", + " const handleMouseLeave = () => setIsHovered(false);", + "", + " useEffect(() => {", + " const node = ref.current;", + "", + " if (node) {", + " node.addEventListener('mouseenter', handleMouseEnter);", + " node.addEventListener('mouseleave', handleMouseLeave);", + "", + " return () => {", + " node.removeEventListener('mouseenter', handleMouseEnter);", + " node.removeEventListener('mouseleave', handleMouseLeave);", + " };", + " }", + " }, [ref]);", + "", + " return isHovered;", + "};" + ], + "description": "React hook to track when an element is being hovered over" + }, + "useInput": { + "prefix": "useInput", + "body": [ + "import { useState } from 'react';", + "", + "export const useInput = (initialValue) => {", + " const [inputValue, setInputValue] = useState(initialValue);", + "", + " const onInputChange = (event) => {", + " setInputValue(event.target.value);", + " };", + "", + " return { inputValue, onInputChange };", + "};" + ], + "description": "React hook to handle an input element" + }, + "useIsTouchDevice": { + "prefix": "useIsTouchDevice", + "body": [ + "import { useEffect, useState } from 'react';", + "", + "export function useIsTouchDevice() {", + " const [isTouchDevice, setIsTouchDevice] = useState(false);", + "", + " useEffect(() => {", + " function onResize() {", + " setIsTouchDevice(", + " 'ontouchstart' in window ||", + " navigator.maxTouchPoints > 0 ||", + " navigator.maxTouchPoints > 0", + " );", + " }", + "", + " window.addEventListener('resize', onResize);", + " onResize();", + "", + " return () => {", + " window.removeEventListener('resize', onResize);", + " };", + " }, []);", + "", + " return isTouchDevice;", + "}" + ], + "description": "React hook to detect if the user's device is a touch device" + }, + "useKeyPress": { + "prefix": "useKeyPress", + "body": [ + "import { useState, useEffect } from 'react';", + "", + "export const useKeyPress = (config) => {", + " const [keyPressed, setKeyPressed] = useState(false);", + " const { key: targetKey, ctrl, alt, shift } = config;", + "", + " const handleKeyDown = (e) => {", + " const { key, ctrlKey, altKey, shiftKey } = e;", + "", + " if (", + " (!ctrl && !alt && !shift && key === targetKey) ||", + " (ctrl && key === targetKey && ctrlKey === ctrl) ||", + " (alt && key === targetKey && altKey === alt) ||", + " (shift && key === targetKey && shiftKey === shift)", + " ) {", + " setKeyPressed(true);", + " }", + " };", + "", + " const handleKeyUp = (e) => {", + " const { key, ctrlKey, altKey, shiftKey } = e;", + "", + " if (", + " (!ctrl && !alt && !shift && key === targetKey) ||", + " (ctrl && key === targetKey && ctrlKey === ctrl) ||", + " (alt && key === targetKey && altKey === alt) ||", + " (shift && key === targetKey && shiftKey === shift)", + " ) {", + " setKeyPressed(false);", + " }", + " };", + "", + " useEffect(() => {", + " window.addEventListener('keydown', handleKeyDown);", + " window.addEventListener('keyup', handleKeyUp);", + "", + " return () => {", + " window.removeEventListener('keydown', handleKeyDown);", + " window.removeEventListener('keyup', handleKeyUp);", + " };", + " }, []);", + "", + " return keyPressed;", + "};" + ], + "description": "React hook to detect when a specific key or a combination of keys is pressed or released" + }, + "useLang": { + "prefix": "useLang", + "body": [ + "import { useSyncExternalStore } from 'react';", + "", + "const langSubscribe = (cb) => {", + " window.addEventListener('languagechange', cb);", + " return () => window.removeEventListener('languagechange', cb);", + "};", + "", + "const getLang = () => navigator.language;", + "", + "export const useLang = () => useSyncExternalStore(langSubscribe, getLang);" + ], + "description": "React hook to detect the language selected in the browser" + }, + "useLocalStorage": { + "prefix": "useLocalStorage", + "body": [ + "import { useEffect, useSyncExternalStore, useCallback } from 'react';", + "", + "const isFunction = (value) => typeof value === 'function';", + "", + "const dispatchStorageEvent = (key, newValue) =>", + " window.dispatchEvent(new StorageEvent('storage', { key, newValue }));", + "", + "const getLocalStorageItem = (key) => window.localStorage.getItem(key);", + "", + "const setLocalStorageItem = (key, value) => {", + " const stringifiedValue = JSON.stringify(value);", + " window.localStorage.setItem(key, stringifiedValue);", + " dispatchStorageEvent(key, stringifiedValue);", + "};", + "", + "const removeLocalStorageItem = (key) => {", + " window.localStorage.removeItem(key);", + " dispatchStorageEvent(key, null);", + "};", + "", + "const localStorageSubscribe = (cb) => {", + " window.addEventListener('storage', cb);", + " return () => window.removeEventListener('storage', cb);", + "};", + "", + "export const useLocalStorage = (key, initialValue) => {", + " const getSnapshot = () => getLocalStorageItem(key);", + " const store = useSyncExternalStore(localStorageSubscribe, getSnapshot);", + "", + " const setState = useCallback(", + " (v) => {", + " try {", + " let nextState;", + " if (isFunction(v)) {", + " const parsedStore = store ? JSON.parse(store) : null;", + " nextState = v(parsedStore ?? initialValue);", + " } else {", + " nextState = v;", + " }", + "", + " if (nextState === undefined || nextState === null) {", + " removeLocalStorageItem(key);", + " } else {", + " setLocalStorageItem(key, nextState);", + " }", + " } catch (e) {", + " console.log(e);", + " }", + " },", + " [key, store, initialValue]", + " );", + "", + " useEffect(() => {", + " if (", + " getLocalStorageItem(key) === null &&", + " typeof initialValue !== 'undefined'", + " ) {", + " setLocalStorageItem(key, initialValue);", + " }", + " }, [key, initialValue]);", + "", + " return {", + " current: store ? JSON.parse(store) : initialValue,", + " setItemValue: setState,", + " removeItem: () => removeLocalStorageItem(key),", + " };", + "};" + ], + "description": "React hook to store, retrieve and delete data from the browser's localStorage API" + }, + "useNavigatorShare": { + "prefix": "useNavigatorShare", + "body": [ + "const errorMessages = {", + " NotAllowedError: 'Permission to share denied.',", + " AbortError: 'The sharing action was aborted.',", + " NotSupportedError: 'Your browser does not support the sharing feature.',", + " TypeError: 'Error while sharing: incorrect data type.',", + "};", + "", + "function checkPermission(files) {", + " if (!navigator.canShare) {", + " throw new Error('Your browser does not support the sharing feature.');", + " }", + "", + " if (!navigator.canShare({ files } || { files: [new File([], '')] })) {", + " throw new Error(", + " `Your browser does not allow sharing ${", + " files ? 'this type of ' : ''", + " } files.`", + " );", + " }", + "}", + "", + "function surroundTryCatch(fn) {", + " return async (data) => {", + " try {", + " await fn(data);", + " } catch (error) {", + " if (error.name in errorMessages) {", + " const message = `Error while sharing: ${errorMessages[error.name]}`;", + " console.error(message);", + " } else {", + " throw error;", + " }", + " }", + " };", + "}", + "", + "export const useNavigatorShare = () => {", + " async function shareInNavigator(data) {", + " if (data.files) checkPermission(data.files);", + "", + " await navigator.share({", + " title: data.title,", + " text: data.text ?? '',", + " url: data.url ?? '',", + " files: data.files ?? [],", + " });", + " }", + "", + " return {", + " shareInNavigator: surroundTryCatch(shareInNavigator),", + " };", + "};" + ], + "description": "React hook to share content through the browser" + }, + "useOffline": { + "prefix": "useOffline", + "body": [ + "import { useEffect, useState } from 'react';", + "", + "export const useOffline = () => {", + " const [offline, setOffline] = useState(null);", + "", + " useEffect(() => {", + " const handleNetworkState = () => {", + " setOffline(!offline);", + " };", + " addEventListener('offline', handleNetworkState);", + " addEventListener('online', handleNetworkState);", + "", + " return () => {", + " removeEventListener('online', handleNetworkState);", + " removeEventListener('offline', handleNetworkState);", + " };", + " }, [offline]);", + "", + " return !!offline;", + "};" + ], + "description": "Reaction hook to track if user is offline or not" + }, + "useOnScreen": { + "prefix": "useOnScreen", + "body": [ + "import { useEffect, useState } from 'react';", + "", + "export default function useOnScreen(ref, rootMargin = '0px') {", + " const [isIntersecting, setIntersecting] = useState(false);", + "", + " useEffect(() => {", + " const observer = new IntersectionObserver(", + " ([entry]) => {", + " setIntersecting(entry.isIntersecting);", + " },", + " {", + " rootMargin,", + " }", + " );", + "", + " if (ref.current) {", + " observer.observe(ref.current);", + " }", + "", + " return () => {", + " observer.unobserve(ref.current);", + " };", + " }, [ref, rootMargin]);", + "", + " return isIntersecting;", + "}" + ], + "description": "React hook to monitor whether a referenced element is visible on the screen" + }, + "useOutsideClick": { + "prefix": "useOutsideClick", + "body": [ + "import { useEffect } from 'react';", + "", + "export const useOutsideClick = (ref, fn) => {", + " useEffect(() => {", + " const handleClickOutside = (event) => {", + " if (ref.current && !ref.current.contains(event.target)) {", + " fn();", + " }", + " };", + "", + " document.addEventListener('click', handleClickOutside);", + " return () => {", + " document.removeEventListener('click', handleClickOutside);", + " };", + " }, [ref, fn]);", + "};" + ], + "description": "React hook to detect clicks outside of a specified component" + }, + "usePrevious": { + "prefix": "usePrevious", + "body": [ + "import { useRef } from 'react';", + "", + "export default function usePrevious(value) {", + " const currentRef = useRef(value);", + " const previousRef = useRef();", + "", + " if (currentRef.current !== value) {", + " previousRef.current = currentRef.current;", + " currentRef.current = value;", + " }", + "", + " return previousRef.current;", + "}" + ], + "description": "React hook to track the previous value of a variable" + }, + "useRandomColor": { + "prefix": "useRandomColor", + "body": [ + "import { useState } from 'react';", + "", + "export const useRandomColor = () => {", + " const [color, setColor] = useState('#000000');", + "", + " const generateColor = () => {", + " const newColor =", + " '#' +", + " Math.floor(Math.random() * 16777215)", + " .toString(16)", + " .padStart(6, '0');", + "", + " setColor(newColor);", + " return newColor;", + " };", + "", + " return { color, generateColor };", + "};" + ], + "description": "React hook to generate random colors" + }, + "useScript": { + "prefix": "useScript", + "body": [ + "import { useEffect, useState } from 'react';", + "", + "export const useScript = (url) => {", + " const [loading, setLoading] = useState(true);", + " const [error, setError] = useState(null);", + "", + " useEffect(() => {", + " const script = document.createElement('script');", + " script.src = url;", + " script.async = true;", + "", + " script.onload = () => {", + " setLoading(false);", + " };", + "", + " script.onerror = () => {", + " setError(`Failed to load script ${url}`);", + " setLoading(false);", + " };", + "", + " document.body.appendChild(script);", + "", + " return () => {", + " document.body.removeChild(script);", + " };", + " }, [url]);", + "", + " return { loading, error };", + "};" + ], + "description": "React hook to load a script" + }, + "useScroll": { + "prefix": "useScroll", + "body": [ + "import { useState, useLayoutEffect } from 'react';", + "", + "export const useScroll = () => {", + " const [position, setPosition] = useState({", + " x: 0,", + " y: 0,", + " });", + "", + " const handleScroll = () => {", + " setPosition({", + " x: window.scrollX,", + " y: window.scrollY,", + " });", + " };", + "", + " useLayoutEffect(() => {", + " window.addEventListener('scroll', handleScroll);", + "", + " return () => {", + " window.removeEventListener('scroll', handleScroll);", + " };", + " }, []);", + "", + " return { position, scrollTo: window.scrollTo };", + "};" + ], + "description": "React hook to track and manipule the scroll position of a web page" + }, + "useSearchParams": { + "prefix": "useSearchParams", + "body": [ + "import { useEffect, useState } from 'react';", + "", + "export const useSearchParams = (", + " url = location.href,", + " opt = { unique: true }", + ") => {", + " const _urlSearch = new URL(url);", + " const [params, setParams] = useState(() =>", + " Object.fromEntries(_urlSearch.searchParams.entries())", + " );", + "", + " useEffect(() => {", + " const len = Object.values(params).length;", + "", + " if (!opt || opt.unique || len === _urlSearch.searchParams?.size) return;", + "", + " for (const [key, value] of _urlSearch.searchParams) {", + " if (value === params?.[key]) continue;", + " if (", + " Array.isArray(params?.[key]) &&", + " Array.from(params?.[key]).includes(value)", + " )", + " continue;", + " setParams(() => ({", + " ...params,", + " [key]: [...(params?.[key] ?? []), value],", + " }));", + " }", + " }, []);", + "", + " return Object.fromEntries(", + " Object.entries(params).map(([key, value]) => [", + " key,", + " !Array.isArray(value)", + " ? JSON.parse(value)", + " : value.map((items) => JSON.parse(items)),", + " ])", + " );", + "};" + ], + "description": "React hook to extract search parameters from URL" + }, + "useStopwatch": { + "prefix": "useStopwatch", + "body": [ + "import { useState, useEffect } from 'react';", + "", + "const addLeadingZero = (digit) => {", + " let timeStr = '';", + "", + " digit % 10 === digit ? (timeStr += `0${digit}`) : (timeStr += `${digit}`);", + "", + " return timeStr;", + "};", + "", + "export const useStopwatch = () => {", + " const [time, setTime] = useState({", + " days: 0,", + " hours: 0,", + " minutes: 0,", + " seconds: 0,", + " });", + " const [paused, setPaused] = useState(false);", + " const divider = ':';", + " const [isOver, setIsOver] = useState(false);", + "", + " useEffect(() => {", + " if (paused) {", + " return;", + " }", + "", + " const interval = setInterval(() => {", + " setTime((prev) => {", + " let d = prev.days;", + " let h = prev.hours;", + " let m = prev.minutes;", + " let s = prev.seconds;", + "", + " if (s + 1 >= 60) {", + " s = 0;", + " if (m + 1 >= 60) {", + " m = 0;", + " if (h + 1 >= 24) {", + " h = 0;", + " d++;", + " } else {", + " h++;", + " }", + " } else {", + " m++;", + " }", + " } else {", + " s++;", + " }", + "", + " return { days: d, hours: h, minutes: m, seconds: s };", + " });", + " }, 1000);", + "", + " return () => clearInterval(interval);", + " }, [time, paused]);", + "", + " return {", + " current: `${addLeadingZero(time.days)}${divider}${addLeadingZero(", + " time.hours", + " )}${divider}${addLeadingZero(time.minutes)}${divider}${addLeadingZero(", + " time.seconds", + " )}`,", + " isPaused: paused,", + " isOver,", + " currentDays: time.days,", + " currentHours: time.hours,", + " currentMinutes: time.minutes,", + " currentSeconds: time.seconds,", + " elapsedSeconds:", + " time.days * 86400 + time.hours * 3600 + time.minutes * 60 + time.seconds,", + " pause: () => setPaused(true),", + " play: () => setPaused(false),", + " reset: () => {", + " setIsOver(false);", + " setTime({ days: 0, hours: 0, minutes: 0, seconds: 0 });", + " },", + " togglePause: () => {", + " setPaused(!paused);", + " },", + " };", + "};" + ], + "description": "React hook to create a stopwatch functionality" + }, + "useTimer": { + "prefix": "useTimer", + "body": [ + "import { useState, useEffect } from 'react';", + "", + "const parseTime = (time) => {", + " const splitTime = time.split(':');", + "", + " const [days, hours, minutes, seconds] = splitTime.map((value) =>", + " Number(value)", + " );", + "", + " return { days, hours, minutes, seconds };", + "};", + "", + "const addLeadingZero = (digit) => {", + " let timeStr = '';", + "", + " digit % 10 === digit ? (timeStr += `0${digit}`) : (timeStr += `${digit}`);", + "", + " return timeStr;", + "};", + "", + "export const useTimer = (startTime) => {", + " const { days, hours, minutes, seconds } = parseTime(startTime);", + " const [time, setTime] = useState({ days, hours, minutes, seconds });", + " const [paused, setPaused] = useState(false);", + " const divider = ':';", + " const [isOver, setIsOver] = useState(false);", + "", + " useEffect(() => {", + " if (paused) {", + " return;", + " }", + "", + " const interval = setInterval(() => {", + " setTime((prev) => {", + " let d = prev.days;", + " let h = prev.hours;", + " let m = prev.minutes;", + " let s = prev.seconds;", + "", + " if (s - 1 < 0) {", + " s = 59;", + " if (m - 1 < 0) {", + " m = 59;", + " if (h - 1 < 0) {", + " h = 23;", + " if (d - 1 >= 0) {", + " d--;", + " }", + " } else {", + " h--;", + " }", + " } else {", + " m--;", + " }", + " } else {", + " s--;", + " }", + "", + " return { days: d, hours: h, minutes: m, seconds: s };", + " });", + " }, 1000);", + "", + " if (", + " time.seconds === 0 &&", + " time.minutes === 0 &&", + " time.hours === 0 &&", + " time.days === 0", + " ) {", + " setIsOver(true);", + " clearInterval(interval);", + " return;", + " }", + "", + " return () => clearInterval(interval);", + " }, [days, hours, minutes, seconds, time, paused]);", + "", + " return {", + " current: `${addLeadingZero(time.days)}${divider}${addLeadingZero(", + " time.hours", + " )}${divider}${addLeadingZero(time.minutes)}${divider}${addLeadingZero(", + " time.seconds", + " )}`,", + " isPaused: paused,", + " isOver,", + " currentDays: time.days,", + " currentHours: time.hours,", + " currentMinutes: time.minutes,", + " currentSeconds: time.seconds,", + " elapsedSeconds:", + " days * 86400 +", + " hours * 3600 +", + " minutes * 60 +", + " seconds -", + " (time.days * 86400 +", + " time.hours * 3600 +", + " time.minutes * 60 +", + " time.seconds),", + " remainingSeconds:", + " time.days * 86400 + time.hours * 3600 + time.minutes * 60 + time.seconds,", + " pause: () => setPaused(true),", + " play: () => setPaused(false),", + " reset: () => {", + " setIsOver(false);", + " setTime({ days, hours, minutes, seconds });", + " },", + " togglePause: () => {", + " setPaused(!paused);", + " },", + " };", + "};" + ], + "description": "React hook to create a timer functionality" + }, + "useTitle": { + "prefix": "useTitle", + "body": [ + "import { useState, useEffect } from 'react';", + "", + "export const useTitle = () => {", + " const [title, setTitle] = useState(document.title);", + "", + " useEffect(() => {", + " document.title = title;", + " }, [title]);", + "", + " const changeTitle = (newTitle) => setTitle(newTitle);", + "", + " return { title, changeTitle };", + "};" + ], + "description": "React hook to change the page title" + }, + "useToggle": { + "prefix": "useToggle", + "body": [ + "import { useState } from 'react';", + "", + "export const useToggle = (initialValue) => {", + " const [current, setCurrent] = useState(initialValue);", + "", + " const handleToggle = () => setCurrent((prev) => !prev);", + "", + " return { current, handleToggle };", + "};" + ], + "description": "React hook to toggle a boolean value" + }, + "useVideo": { + "prefix": "useVideo", + "body": [ + "import { useEffect, useState } from 'react';", + "", + "export const useVideo = (ref) => {", + " const video = ref.current;", + "", + " const [videoState, setVideoState] = useState({", + " isPaused: video ? video?.paused : true,", + " isMuted: video ? video?.muted : false,", + " currentVolume: video ? video?.volume : 100,", + " currentTime: video ? video?.currentTime : 0,", + " });", + "", + " const play = () => {", + " video?.play();", + " setVideoState((prev) => {", + " return {", + " ...prev,", + " isPaused: false,", + " isMuted: video ? video.muted : prev.isMuted,", + " };", + " });", + " };", + "", + " const pause = () => {", + " video?.pause();", + " setVideoState((prev) => {", + " return {", + " ...prev,", + " isPaused: true,", + " };", + " });", + " };", + "", + " const handlePlayPauseControl = (e) => {", + " setVideoState((prev) => {", + " return {", + " ...prev,", + " isPaused: e.target.paused,", + " };", + " });", + " };", + "", + " const togglePause = () => (video?.paused ? play() : pause());", + "", + " const handleVolume = (delta) => {", + " const deltaDecimal = delta / 100;", + "", + " if (video) {", + " let newVolume = video?.volume + deltaDecimal;", + "", + " if (newVolume >= 1) {", + " newVolume = 1;", + " } else if (newVolume <= 0) {", + " newVolume = 0;", + " }", + "", + " video.volume = newVolume;", + " setVideoState((prev) => {", + " return {", + " ...prev,", + " currentVolume: newVolume * 100,", + " };", + " });", + " }", + " };", + "", + " const handleVolumeControl = (e) => {", + " if (e.target && video) {", + " const newVolume = e.target.volume * 100;", + "", + " if (newVolume === videoState.currentVolume) {", + " handleMute(video.muted);", + " return;", + " }", + "", + " setVideoState((prev) => ({", + " ...prev,", + " currentVolume: e.target.volume * 100,", + " }));", + " }", + " };", + "", + " const handleMute = (mute) => {", + " if (video) {", + " video.muted = mute;", + " setVideoState((prev) => {", + " return {", + " ...prev,", + " isMuted: mute,", + " };", + " });", + " }", + " };", + "", + " const handleTime = (delta = 5) => {", + " if (video) {", + " let newTime = video.currentTime + delta;", + "", + " if (newTime >= video.duration) {", + " newTime = video.duration;", + " } else if (newTime <= 0) {", + " newTime = 0;", + " }", + "", + " video.currentTime = newTime;", + " setVideoState((prev) => {", + " return {", + " ...prev,", + " currentTime: newTime,", + " };", + " });", + " }", + " };", + "", + " const handleTimeControl = (e) => {", + " setVideoState((prev) => {", + " return {", + " ...prev,", + " currentTime: e.target.currentTime,", + " };", + " });", + " };", + "", + " const toggleFullscreen = () => {", + " if (!document.fullscreenElement) {", + " video?.requestFullscreen().catch((err) => {", + " console.log(err);", + " });", + " } else {", + " document.exitFullscreen();", + " }", + " };", + "", + " useEffect(() => {", + " return () => {", + " pause();", + " };", + " }, []);", + "", + " useEffect(() => {", + " if (video) {", + " video.addEventListener('volumechange', handleVolumeControl);", + " video.addEventListener('play', handlePlayPauseControl);", + " video.addEventListener('pause', handlePlayPauseControl);", + " video.addEventListener('timeupdate', handleTimeControl);", + "", + " return () => {", + " video.removeEventListener('volumechange', handleVolumeControl);", + " video.removeEventListener('play', handlePlayPauseControl);", + " video.removeEventListener('pause', handlePlayPauseControl);", + " video.removeEventListener('timeupdate', handleTimeControl);", + " };", + " }", + " }, [video]);", + "", + " return {", + " ...videoState,", + " play,", + " pause,", + " togglePause,", + " increaseVolume: (increase = 5) => handleVolume(increase),", + " decreaseVolume: (decrease = 5) => handleVolume(decrease * -1),", + " mute: () => handleMute(true),", + " unmute: () => handleMute(false),", + " toggleMute: () => handleMute(!video?.muted),", + " forward: (increase = 5) => handleTime(increase),", + " back: (decrease = 5) => handleTime(decrease * -1),", + " toggleFullscreen,", + " };", + "};" + ], + "description": "React hook to manage a video" + }, + "useWindowSize": { + "prefix": "useWindowSize", + "body": [ + "import { useState, useEffect } from 'react';", + "", + "export const useWindowSize = () => {", + " const [windowSize, setWindowSize] = useState({", + " width: window.innerWidth,", + " height: window.innerHeight,", + " });", + "", + " const handleResize = () => {", + " setWindowSize({", + " width: window.innerWidth,", + " height: window.innerHeight,", + " });", + " };", + "", + " useEffect(() => {", + " window.addEventListener('resize', handleResize);", + "", + " return () => window.removeEventListener('resize', handleResize);", + " }, []);", + "", + " return windowSize;", + "};" + ], + "description": "React hook to track the dimensions of the browser window" + } +} diff --git a/snippets/snippets.code-snippets b/snippets/snippets.code-snippets deleted file mode 100644 index 9e26dfe..0000000 --- a/snippets/snippets.code-snippets +++ /dev/null @@ -1 +0,0 @@ -{} \ No newline at end of file