@peachy/hooks 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md ADDED
@@ -0,0 +1,3 @@
1
+ # @peachy/hooks
2
+
3
+ Useful hooks for React applications.
@@ -0,0 +1,2 @@
1
+ import { useBinding } from "./use-binding.mjs";
2
+ export { useBinding };
@@ -0,0 +1,3 @@
1
+ import { useBinding } from "./use-binding.mjs";
2
+
3
+ export { useBinding };
@@ -0,0 +1,9 @@
1
+ import { RefObject } from "react";
2
+ import GObject from "gi://GObject?version=2.0";
3
+
4
+ //#region src/gobject/use-binding.d.ts
5
+ type NonFunctionKeys<T> = { [K in keyof T]: T[K] extends Function ? never : K extends Symbol ? never : K extends number ? never : K }[keyof T];
6
+ declare function useBinding<T extends GObject.Object, Prop extends NonFunctionKeys<T>>(object: T | RefObject<T>, property: Prop, defaultValue?: T[Prop], converter?: undefined): T[Prop];
7
+ declare function useBinding<T extends GObject.Object, Prop extends NonFunctionKeys<T>, Converter extends (value: T[Prop]) => any>(object: T | RefObject<T>, property: Prop, defaultValue: T[Prop] | undefined, converter: Converter): ReturnType<Converter>;
8
+ //#endregion
9
+ export { useBinding };
@@ -0,0 +1,38 @@
1
+ import { useEffect, useState } from "react";
2
+
3
+ //#region src/gobject/use-binding.ts
4
+ function useBinding(object, _property, defaultValue, converter) {
5
+ const property = toKebabCase(_property);
6
+ const [result, setResult] = useState(() => {
7
+ const value = getValue(object);
8
+ if (!value) return defaultValue;
9
+ const initialResult = value[property];
10
+ return converter ? converter(initialResult) : initialResult;
11
+ });
12
+ useEffect(() => {
13
+ const value = getValue(object);
14
+ if (!value) {
15
+ console.error(`Object is null or undefined`);
16
+ return;
17
+ }
18
+ const id = value.connect(`notify::${property}`, () => {
19
+ setResult(value[property]);
20
+ });
21
+ return () => {
22
+ value.disconnect(id);
23
+ };
24
+ }, []);
25
+ return result;
26
+ }
27
+ function getValue(object) {
28
+ return isRef(object) ? object.current : object;
29
+ }
30
+ function isRef(object) {
31
+ return typeof object === "object" && object !== null && "current" in object;
32
+ }
33
+ function toKebabCase(str) {
34
+ return str.replace(/([A-Z])/g, "-$1").replace(/_/g, "-").toLowerCase();
35
+ }
36
+
37
+ //#endregion
38
+ export { useBinding };
@@ -0,0 +1,3 @@
1
+ import { useBinding } from "./gobject/use-binding.mjs";
2
+ import "./gobject/index.mjs";
3
+ export { useBinding };
package/dist/index.mjs ADDED
@@ -0,0 +1,4 @@
1
+ import { useBinding } from "./gobject/use-binding.mjs";
2
+ import "./gobject/index.mjs";
3
+
4
+ export { useBinding };
package/package.json ADDED
@@ -0,0 +1,31 @@
1
+ {
2
+ "name": "@peachy/hooks",
3
+ "version": "0.0.1",
4
+ "description": "",
5
+ "main": "./dist/index.mjs",
6
+ "exports": {
7
+ ".": {
8
+ "import": "./dist/index.mjs",
9
+ "types": "./dist/index.d.mts"
10
+ },
11
+ "./*": {
12
+ "import": "./dist/*/index.mjs",
13
+ "types": "./dist/*/index.d.mts"
14
+ }
15
+ },
16
+ "author": "",
17
+ "license": "MIT",
18
+ "devDependencies": {
19
+ "react": "^19.2.0",
20
+ "tsdown": "0.20.0-beta.3",
21
+ "typescript": "^5.9.3",
22
+ "@peachy/react": "0.0.1"
23
+ },
24
+ "peerDependencies": {
25
+ "react": "^19.2.0"
26
+ },
27
+ "scripts": {
28
+ "build": "tsdown"
29
+ },
30
+ "types": "./dist/index.d.mts"
31
+ }
@@ -0,0 +1 @@
1
+ export * from "./use-binding";
@@ -0,0 +1,86 @@
1
+ import GObject from "gi://GObject?version=2.0";
2
+ import { RefObject, useEffect, useState } from "react";
3
+
4
+ type NonFunctionKeys<T> = {
5
+ [K in keyof T]: T[K] extends Function
6
+ ? never
7
+ : K extends Symbol
8
+ ? never
9
+ : K extends number
10
+ ? never
11
+ : K;
12
+ }[keyof T];
13
+
14
+ export function useBinding<
15
+ T extends GObject.Object,
16
+ Prop extends NonFunctionKeys<T>,
17
+ >(
18
+ object: T | RefObject<T>,
19
+ property: Prop,
20
+ defaultValue?: T[Prop],
21
+ converter?: undefined,
22
+ ): T[Prop];
23
+ export function useBinding<
24
+ T extends GObject.Object,
25
+ Prop extends NonFunctionKeys<T>,
26
+ Converter extends (value: T[Prop]) => any,
27
+ >(
28
+ object: T | RefObject<T>,
29
+ property: Prop,
30
+ defaultValue: T[Prop] | undefined,
31
+ converter: Converter,
32
+ ): ReturnType<Converter>;
33
+ export function useBinding<
34
+ T extends GObject.Object,
35
+ Prop extends NonFunctionKeys<T>,
36
+ Converter extends (value: T[Prop]) => any,
37
+ >(
38
+ object: T | RefObject<T>,
39
+ _property: Prop,
40
+ defaultValue?: any,
41
+ converter?: Converter,
42
+ ): any {
43
+ const property = toKebabCase(_property);
44
+
45
+ const [result, setResult] = useState(() => {
46
+ const value = getValue(object);
47
+ if (!value) return defaultValue;
48
+ const initialResult = value[property];
49
+ return converter ? converter(initialResult) : initialResult;
50
+ });
51
+
52
+ // run this on initial render
53
+ useEffect(() => {
54
+ const value = getValue(object);
55
+
56
+ if (!value) {
57
+ console.error(`Object is null or undefined`);
58
+ return;
59
+ }
60
+
61
+ const id = value.connect(`notify::${property}`, () => {
62
+ setResult(value[property]);
63
+ });
64
+
65
+ return () => {
66
+ value.disconnect(id);
67
+ };
68
+ }, []);
69
+
70
+ return result;
71
+ }
72
+
73
+ function getValue<T extends GObject.Object>(object: T | RefObject<T>) {
74
+ return isRef(object) ? object.current : object;
75
+ }
76
+
77
+ function isRef<T>(object: unknown): object is RefObject<T> {
78
+ return typeof object === "object" && object !== null && "current" in object;
79
+ }
80
+
81
+ function toKebabCase(str: string): string {
82
+ return str
83
+ .replace(/([A-Z])/g, "-$1")
84
+ .replace(/_/g, "-")
85
+ .toLowerCase();
86
+ }
package/src/index.ts ADDED
@@ -0,0 +1 @@
1
+ export * from "./gobject";
package/tsconfig.json ADDED
@@ -0,0 +1,3 @@
1
+ {
2
+ "extends": "@peachy/react/tsconfig",
3
+ }
@@ -0,0 +1,10 @@
1
+ import { defineConfig } from "tsdown";
2
+
3
+ export default defineConfig({
4
+ entry: ["./src/**/*.ts"],
5
+ dts: true,
6
+ external: [/^gi:\/\/*/],
7
+ outputOptions: {
8
+ preserveModules: true,
9
+ },
10
+ });