Skip to content

Commit

Permalink
Add hooks and react dependencies (#1)
Browse files Browse the repository at this point in the history
  • Loading branch information
chrisllontop authored Dec 5, 2024
1 parent eaedf4c commit 2c1093c
Show file tree
Hide file tree
Showing 46 changed files with 1,826 additions and 15 deletions.
43 changes: 33 additions & 10 deletions biome.json
Original file line number Diff line number Diff line change
@@ -1,12 +1,35 @@
{
"$schema": "https://biomejs.dev/schemas/1.9.4/schema.json",
"organizeImports": {
"enabled": true
},
"linter": {
"enabled": true,
"rules": {
"recommended": true
}
}
"$schema": "https://biomejs.dev/schemas/1.9.4/schema.json",
"vcs": {
"enabled": false,
"clientKind": "git",
"useIgnoreFile": false
},
"files": {
"ignoreUnknown": false,
"ignore": []
},
"formatter": {
"enabled": true,
"indentStyle": "tab"
},
"organizeImports": {
"enabled": true
},
"linter": {
"enabled": true,
"rules": {
"recommended": true,
"suspicious": {
"noAssignInExpressions": "off",
"noExplicitAny": "off"
},
"correctness": {
"useExhaustiveDependencies": "off"
},
"complexity": {
"noBannedTypes": "off"
}
}
}
}
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
"version": "1.0.0",
"description": "",
"main": "index.js",
"type": "module",
"scripts": {
"lint": "pnpm recursive run lint",
"lint:fix": "pnpm recursive run lint:fix"
Expand Down
1 change: 1 addition & 0 deletions packages/core/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
"version": "1.0.0",
"description": "Component library collection for @rhino-ui",
"main": "index.js",
"type": "module",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1",
"lint": "biome format && biome lint",
Expand Down
16 changes: 11 additions & 5 deletions packages/hooks/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,10 @@
"name": "@rhino-ui/hooks",
"version": "1.0.0",
"description": "React hooks collection for @rhino-ui",
"main": "index.js",
"main": "./dist/index.js",
"type": "module",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1",
"build": "tsc",
"lint": "biome format && biome lint",
"lint:fix": "biome format --write && biome lint --fix"
},
Expand All @@ -15,9 +16,14 @@
"url": "https://rhinolabs.agency"
},
"license": "MIT",
"dependencies": {},
"dependencies": {
"react": "catalog:"
},
"devDependencies": {
"typescript": "catalog:",
"@biomejs/biome": "catalog:"
"@biomejs/biome": "catalog:",
"@types/react": "catalog:",
"@types/react-dom": "^18.3.1",
"react-dom": "^18.3.1",
"typescript": "catalog:"
}
}
5 changes: 5 additions & 0 deletions packages/hooks/src/App.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
const App = () => {
return <p>Test your hook here</p>;
};

export default App;
42 changes: 42 additions & 0 deletions packages/hooks/src/hooks/useArray.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
import { useState } from "react";

export default function useArray<T>(initialArray: T[]) {
const [array, setArray] = useState<T[]>(initialArray);

const push = (element: T) => {
setArray((prev) => [...prev, element]);
};

const filter = (callback: (element: T) => boolean) => {
setArray((prev) => prev.filter(callback));
};

const update = (index: number, newElement: T) => {
setArray((prev) => [
...prev.slice(0, index),
newElement,
...prev.slice(index + 1, prev.length),
]);
};

const remove = (index: number) => {
setArray((prev) => [
...prev.slice(0, index),
...prev.slice(index + 1, prev.length),
]);
};

const clear = () => {
setArray([]);
};

return {
array,
set: setArray,
push,
filter,
update,
remove,
clear,
};
}
42 changes: 42 additions & 0 deletions packages/hooks/src/hooks/useAsync.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
import { useState, useCallback } from "react";

interface UseAsyncState<T> {
data: T | null;
isLoading: boolean;
error: Error | null;
isSuccess: boolean;
}

export const useAsync = <T>() => {
const [state, setState] = useState<UseAsyncState<T>>({
data: null,
isLoading: false,
error: null,
isSuccess: false,
});

const execute = useCallback(async (asyncFunction: () => Promise<T>) => {
setState((prev) => ({ ...prev, isLoading: true, error: null }));

try {
const result = await asyncFunction();
setState({
data: result,
isLoading: false,
error: null,
isSuccess: true,
});
return result;
} catch (error) {
setState({
data: null,
isLoading: false,
error: error as Error,
isSuccess: false,
});
throw error;
}
}, []);

return { execute, ...state };
};
155 changes: 155 additions & 0 deletions packages/hooks/src/hooks/useAudio.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,155 @@
import { useEffect, useState, type RefObject } from "react";

export const useAudio = (ref: RefObject<HTMLAudioElement>) => {
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: Event) => {
setAudioState((prev) => {
return {
...prev,
isPaused: (e.target as HTMLAudioElement).paused,
};
});
};

const togglePause = () => (audio?.paused ? play() : pause());

const handleVolume = (delta: number) => {
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: Event) => {
if (e.target && audio) {
const newVolume = (e.target as HTMLAudioElement).volume * 100;

handleMute(audio.muted);
setAudioState((prev) => ({
...prev,
currentVolume: newVolume,
}));
}
};

const handleMute = (mute: boolean) => {
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: Event) => {
setAudioState((prev) => {
return {
...prev,
currentTime: (e.target as HTMLAudioElement).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),
};
};
Loading

0 comments on commit 2c1093c

Please sign in to comment.