@peachy/hooks 0.0.7 → 0.0.9
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/CHANGELOG.md +8 -0
- package/README.md +91 -0
- package/dist/base/index.d.mts +2 -0
- package/dist/base/index.mjs +3 -0
- package/dist/base/use-reference.d.mts +9 -0
- package/dist/base/use-reference.mjs +24 -0
- package/dist/gio/index.d.mts +3 -0
- package/dist/gio/index.mjs +4 -0
- package/dist/gio/use-network-monitor.d.mts +12 -0
- package/dist/gio/use-network-monitor.mjs +28 -0
- package/dist/gio/use-setting.d.mts +6 -0
- package/dist/gio/use-setting.mjs +22 -0
- package/dist/gobject/use-binding.d.mts +2 -2
- package/dist/gobject/use-binding.mjs +4 -9
- package/dist/index.d.mts +3 -1
- package/dist/index.mjs +3 -1
- package/dist/types.d.mts +1 -0
- package/dist/types.mjs +1 -0
- package/package.json +3 -2
- package/src/base/index.ts +1 -0
- package/src/base/use-reference.ts +27 -0
- package/src/gio/index.ts +2 -0
- package/src/gio/use-network-monitor.ts +35 -0
- package/src/gio/use-setting.ts +25 -0
- package/src/gobject/use-binding.ts +5 -14
- package/src/index.ts +1 -0
- package/src/types.ts +0 -0
package/CHANGELOG.md
CHANGED
package/README.md
CHANGED
|
@@ -1,3 +1,94 @@
|
|
|
1
1
|
# @peachy/hooks
|
|
2
2
|
|
|
3
3
|
Useful hooks for React applications.
|
|
4
|
+
|
|
5
|
+
## Installation
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
npm install @peachy/hooks
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
## Usage
|
|
12
|
+
|
|
13
|
+
There are different categories of hooks you can use:
|
|
14
|
+
|
|
15
|
+
### Gio
|
|
16
|
+
|
|
17
|
+
#### `useNetworkMonitor`
|
|
18
|
+
|
|
19
|
+
Returns information about the network connection.
|
|
20
|
+
|
|
21
|
+
```tsx
|
|
22
|
+
import { useNetworkMonitor } from "@peachy/hooks";
|
|
23
|
+
|
|
24
|
+
const App = () => {
|
|
25
|
+
const { connected } = useNetworkMonitor();
|
|
26
|
+
|
|
27
|
+
return (
|
|
28
|
+
<Gtk.Label>
|
|
29
|
+
{connected ? "Connected" : "Disconnected"}
|
|
30
|
+
</Gtk.Label>
|
|
31
|
+
);
|
|
32
|
+
};
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
Provided values:
|
|
36
|
+
|
|
37
|
+
- `connected`: A boolean indicating whether the device is connected to _internet_.
|
|
38
|
+
- `available`: A boolean indicating whether this internet is connected to any network like WiFI, mobile data, ethernet, etc. This does not mean that it can access the internet.
|
|
39
|
+
- `state`: The type of connectivity. One of `local`, `limited`, `portal`, and `full`. Check https://gjs-docs.gnome.org/gio20~2.0/gio.networkconnectivity to learn more.
|
|
40
|
+
- `metered`: A boolean indicating whether the device is metered. When `true`, the device is metered, meaning that the user is charged for data usage and bandwidth is limited.
|
|
41
|
+
|
|
42
|
+
#### `useSetting`
|
|
43
|
+
|
|
44
|
+
Get the value of a setting while listening for changes, and change gsettings.
|
|
45
|
+
|
|
46
|
+
```tsx
|
|
47
|
+
import { useSetting } from "@peachy/hooks";
|
|
48
|
+
|
|
49
|
+
const App = () => {
|
|
50
|
+
const [value, changeValue] = useSetting("org.gnome.desktop.interface", "gtk-theme");
|
|
51
|
+
|
|
52
|
+
return (
|
|
53
|
+
<Gtk.Box>
|
|
54
|
+
<Gtk.Label>
|
|
55
|
+
Theme: {value}
|
|
56
|
+
</Gtk.Label>
|
|
57
|
+
<Gtk.Button onClick={() => changeValue("Adwaita-dark")}>
|
|
58
|
+
Change Theme
|
|
59
|
+
</Gtk.Button>
|
|
60
|
+
</Gtk.Box>
|
|
61
|
+
);
|
|
62
|
+
};
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
### GObject
|
|
66
|
+
|
|
67
|
+
#### `useBinding`
|
|
68
|
+
|
|
69
|
+
Bind to a property, and listen for changes.
|
|
70
|
+
|
|
71
|
+
```tsx
|
|
72
|
+
import Adw from "gi://Adw?version=1";
|
|
73
|
+
|
|
74
|
+
import { useBinding } from "@peachy/hooks";
|
|
75
|
+
|
|
76
|
+
const App = () => {
|
|
77
|
+
const styleManager = Adw.StyleManager.get_default();
|
|
78
|
+
|
|
79
|
+
const [darkMode] = useBinding(styleManager, "dark");
|
|
80
|
+
const [accentColor] = useBinding(styleManager, "accentColor");
|
|
81
|
+
|
|
82
|
+
return (
|
|
83
|
+
<Gtk.Box>
|
|
84
|
+
<Gtk.Label>
|
|
85
|
+
Theme: {darkMode ? "Dark" : "Light"}
|
|
86
|
+
</Gtk.Label>
|
|
87
|
+
|
|
88
|
+
<Gtk.Label>
|
|
89
|
+
Accent Color: {accentColor}
|
|
90
|
+
</Gtk.Label>
|
|
91
|
+
</Gtk.Box>
|
|
92
|
+
);
|
|
93
|
+
};
|
|
94
|
+
```
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { RefObject } from "react";
|
|
2
|
+
|
|
3
|
+
//#region src/base/use-reference.d.ts
|
|
4
|
+
/**
|
|
5
|
+
* Returns the value inside a `ref`, correctly checking it after the initial render
|
|
6
|
+
*/
|
|
7
|
+
declare function useReference<T>(value: T | RefObject<T>, defaultValue?: T): T | null;
|
|
8
|
+
//#endregion
|
|
9
|
+
export { useReference };
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { useEffect, useState } from "react";
|
|
2
|
+
|
|
3
|
+
//#region src/base/use-reference.ts
|
|
4
|
+
/**
|
|
5
|
+
* Returns the value inside a `ref`, correctly checking it after the initial render
|
|
6
|
+
*/
|
|
7
|
+
function useReference(value, defaultValue) {
|
|
8
|
+
const [result, setResult] = useState(() => {
|
|
9
|
+
return getValue(value) ?? (defaultValue || null);
|
|
10
|
+
});
|
|
11
|
+
useEffect(() => {
|
|
12
|
+
setResult(getValue(value));
|
|
13
|
+
}, [value]);
|
|
14
|
+
return result;
|
|
15
|
+
}
|
|
16
|
+
function getValue(object) {
|
|
17
|
+
return isRef(object) ? object.current : object;
|
|
18
|
+
}
|
|
19
|
+
function isRef(object) {
|
|
20
|
+
return typeof object === "object" && object !== null && "current" in object;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
//#endregion
|
|
24
|
+
export { useReference };
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import Gio from "gi://Gio?version=2.0";
|
|
2
|
+
|
|
3
|
+
//#region src/gio/use-network-monitor.d.ts
|
|
4
|
+
interface NetworkMonitor {
|
|
5
|
+
connected: boolean;
|
|
6
|
+
state: Gio.NetworkConnectivity;
|
|
7
|
+
metered: boolean;
|
|
8
|
+
available: boolean;
|
|
9
|
+
}
|
|
10
|
+
declare function useNetworkMonitor(): NetworkMonitor;
|
|
11
|
+
//#endregion
|
|
12
|
+
export { useNetworkMonitor };
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { useCallback, useEffect, useState } from "react";
|
|
2
|
+
import Gio from "gi://Gio?version=2.0";
|
|
3
|
+
|
|
4
|
+
//#region src/gio/use-network-monitor.ts
|
|
5
|
+
function useNetworkMonitor() {
|
|
6
|
+
const monitor = Gio.NetworkMonitor.get_default();
|
|
7
|
+
const [state, setState] = useState(() => calculateState());
|
|
8
|
+
const calculateState = useCallback(() => {
|
|
9
|
+
return {
|
|
10
|
+
available: monitor.network_available,
|
|
11
|
+
connected: monitor.connectivity === Gio.NetworkConnectivity.FULL,
|
|
12
|
+
state: monitor.connectivity,
|
|
13
|
+
metered: monitor.network_metered
|
|
14
|
+
};
|
|
15
|
+
}, []);
|
|
16
|
+
useEffect(() => {
|
|
17
|
+
const id = monitor.connect("network-changed", () => {
|
|
18
|
+
setState(calculateState());
|
|
19
|
+
});
|
|
20
|
+
return () => {
|
|
21
|
+
monitor.disconnect(id);
|
|
22
|
+
};
|
|
23
|
+
}, []);
|
|
24
|
+
return state;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
//#endregion
|
|
28
|
+
export { useNetworkMonitor };
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { useEffect, useState } from "react";
|
|
2
|
+
import Gio from "gi://Gio?version=2.0";
|
|
3
|
+
|
|
4
|
+
//#region src/gio/use-setting.ts
|
|
5
|
+
function useSetting(schema, key) {
|
|
6
|
+
const settings = Gio.Settings.new(schema);
|
|
7
|
+
const getValue = () => settings.get_value(key).recursiveUnpack();
|
|
8
|
+
const [value, setValue] = useState(() => getValue());
|
|
9
|
+
useEffect(() => {
|
|
10
|
+
const handler = settings.connect("changed::" + key, () => {
|
|
11
|
+
setValue(getValue());
|
|
12
|
+
});
|
|
13
|
+
return () => settings.disconnect(handler);
|
|
14
|
+
}, [settings, key]);
|
|
15
|
+
const changeValue = (newValue) => {
|
|
16
|
+
settings.set_value(key, newValue);
|
|
17
|
+
};
|
|
18
|
+
return [value, changeValue];
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
//#endregion
|
|
22
|
+
export { useSetting };
|
|
@@ -3,7 +3,7 @@ import GObject from "gi://GObject?version=2.0";
|
|
|
3
3
|
|
|
4
4
|
//#region src/gobject/use-binding.d.ts
|
|
5
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
|
|
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
8
|
//#endregion
|
|
9
9
|
export { useBinding };
|
|
@@ -1,16 +1,17 @@
|
|
|
1
|
+
import { useReference } from "../base/use-reference.mjs";
|
|
2
|
+
import "../base/index.mjs";
|
|
1
3
|
import { useEffect, useState } from "react";
|
|
2
4
|
|
|
3
5
|
//#region src/gobject/use-binding.ts
|
|
4
6
|
function useBinding(object, _property, defaultValue, converter) {
|
|
5
7
|
const property = toKebabCase(_property);
|
|
8
|
+
const value = useReference(object);
|
|
6
9
|
const [result, setResult] = useState(() => {
|
|
7
|
-
const value = getValue(object);
|
|
8
10
|
if (!value) return defaultValue;
|
|
9
11
|
const initialResult = value[property];
|
|
10
12
|
return converter ? converter(initialResult) : initialResult;
|
|
11
13
|
});
|
|
12
14
|
useEffect(() => {
|
|
13
|
-
const value = getValue(object);
|
|
14
15
|
if (!value) {
|
|
15
16
|
console.error(`Object is null or undefined`);
|
|
16
17
|
return;
|
|
@@ -22,13 +23,7 @@ function useBinding(object, _property, defaultValue, converter) {
|
|
|
22
23
|
value.disconnect(id);
|
|
23
24
|
};
|
|
24
25
|
}, []);
|
|
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;
|
|
26
|
+
return [result];
|
|
32
27
|
}
|
|
33
28
|
function toKebabCase(str) {
|
|
34
29
|
return str.replace(/([A-Z])/g, "-$1").replace(/_/g, "-").toLowerCase();
|
package/dist/index.d.mts
CHANGED
package/dist/index.mjs
CHANGED
package/dist/types.d.mts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { };
|
package/dist/types.mjs
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@peachy/hooks",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.9",
|
|
4
4
|
"description": "",
|
|
5
5
|
"main": "./dist/index.mjs",
|
|
6
6
|
"exports": {
|
|
@@ -16,10 +16,11 @@
|
|
|
16
16
|
"author": "",
|
|
17
17
|
"license": "MIT",
|
|
18
18
|
"devDependencies": {
|
|
19
|
+
"@types/react": "^19.2.8",
|
|
19
20
|
"react": "^19.2.3",
|
|
20
21
|
"tsdown": "0.20.0-beta.4",
|
|
21
22
|
"typescript": "^5.9.3",
|
|
22
|
-
"@peachy/react": "0.0.
|
|
23
|
+
"@peachy/react": "0.0.9"
|
|
23
24
|
},
|
|
24
25
|
"peerDependencies": {
|
|
25
26
|
"react": "^19.2.0"
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from "./use-reference";
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { RefObject, useEffect, useState } from "react";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Returns the value inside a `ref`, correctly checking it after the initial render
|
|
5
|
+
*/
|
|
6
|
+
export function useReference<T>(
|
|
7
|
+
value: T | RefObject<T>,
|
|
8
|
+
defaultValue?: T,
|
|
9
|
+
): T | null {
|
|
10
|
+
const [result, setResult] = useState<T | null>(() => {
|
|
11
|
+
return getValue(value) ?? (defaultValue || null);
|
|
12
|
+
});
|
|
13
|
+
|
|
14
|
+
useEffect(() => {
|
|
15
|
+
setResult(getValue(value));
|
|
16
|
+
}, [value]);
|
|
17
|
+
|
|
18
|
+
return result;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
function getValue<T>(object: T | RefObject<T>) {
|
|
22
|
+
return isRef(object) ? object.current : object;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
function isRef<T>(object: unknown): object is RefObject<T> {
|
|
26
|
+
return typeof object === "object" && object !== null && "current" in object;
|
|
27
|
+
}
|
package/src/gio/index.ts
ADDED
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import Gio from "gi://Gio?version=2.0";
|
|
2
|
+
import { useCallback, useEffect, useState } from "react";
|
|
3
|
+
|
|
4
|
+
interface NetworkMonitor {
|
|
5
|
+
connected: boolean;
|
|
6
|
+
state: Gio.NetworkConnectivity;
|
|
7
|
+
metered: boolean;
|
|
8
|
+
available: boolean;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export function useNetworkMonitor(): NetworkMonitor {
|
|
12
|
+
const monitor = Gio.NetworkMonitor.get_default();
|
|
13
|
+
const [state, setState] = useState(() => calculateState());
|
|
14
|
+
|
|
15
|
+
const calculateState = useCallback(() => {
|
|
16
|
+
return {
|
|
17
|
+
available: monitor.network_available,
|
|
18
|
+
connected: monitor.connectivity === Gio.NetworkConnectivity.FULL,
|
|
19
|
+
state: monitor.connectivity,
|
|
20
|
+
metered: monitor.network_metered,
|
|
21
|
+
} as NetworkMonitor;
|
|
22
|
+
}, []);
|
|
23
|
+
|
|
24
|
+
useEffect(() => {
|
|
25
|
+
const id = monitor.connect("network-changed", () => {
|
|
26
|
+
setState(calculateState());
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
return () => {
|
|
30
|
+
monitor.disconnect(id);
|
|
31
|
+
};
|
|
32
|
+
}, []);
|
|
33
|
+
|
|
34
|
+
return state;
|
|
35
|
+
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import Gio from "gi://Gio?version=2.0";
|
|
2
|
+
import GLib from "gi://GLib?version=2.0";
|
|
3
|
+
import { useEffect, useState } from "react";
|
|
4
|
+
|
|
5
|
+
export function useSetting<T = unknown>(schema: string, key: string) {
|
|
6
|
+
const settings = Gio.Settings.new(schema);
|
|
7
|
+
|
|
8
|
+
const getValue = () => settings.get_value(key).recursiveUnpack() as T;
|
|
9
|
+
|
|
10
|
+
const [value, setValue] = useState<T>(() => getValue());
|
|
11
|
+
|
|
12
|
+
useEffect(() => {
|
|
13
|
+
const handler = settings.connect("changed::" + key, () => {
|
|
14
|
+
setValue(getValue());
|
|
15
|
+
});
|
|
16
|
+
|
|
17
|
+
return () => settings.disconnect(handler);
|
|
18
|
+
}, [settings, key]);
|
|
19
|
+
|
|
20
|
+
const changeValue = (newValue: GLib.Variant) => {
|
|
21
|
+
settings.set_value(key, newValue);
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
return [value, changeValue] as [T, (newValue: GLib.Variant) => void];
|
|
25
|
+
}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import GObject from "gi://GObject?version=2.0";
|
|
2
2
|
import { RefObject, useEffect, useState } from "react";
|
|
3
|
+
import { useReference } from "../base";
|
|
3
4
|
|
|
4
5
|
type NonFunctionKeys<T> = {
|
|
5
6
|
[K in keyof T]: T[K] extends Function
|
|
@@ -19,7 +20,7 @@ export function useBinding<
|
|
|
19
20
|
property: Prop,
|
|
20
21
|
defaultValue?: T[Prop],
|
|
21
22
|
converter?: undefined,
|
|
22
|
-
): T[Prop];
|
|
23
|
+
): [T[Prop]];
|
|
23
24
|
export function useBinding<
|
|
24
25
|
T extends GObject.Object,
|
|
25
26
|
Prop extends NonFunctionKeys<T>,
|
|
@@ -29,7 +30,7 @@ export function useBinding<
|
|
|
29
30
|
property: Prop,
|
|
30
31
|
defaultValue: T[Prop] | undefined,
|
|
31
32
|
converter: Converter,
|
|
32
|
-
): ReturnType<Converter
|
|
33
|
+
): [ReturnType<Converter>];
|
|
33
34
|
export function useBinding<
|
|
34
35
|
T extends GObject.Object,
|
|
35
36
|
Prop extends NonFunctionKeys<T>,
|
|
@@ -41,9 +42,9 @@ export function useBinding<
|
|
|
41
42
|
converter?: Converter,
|
|
42
43
|
): any {
|
|
43
44
|
const property = toKebabCase(_property);
|
|
45
|
+
const value = useReference(object);
|
|
44
46
|
|
|
45
47
|
const [result, setResult] = useState(() => {
|
|
46
|
-
const value = getValue(object);
|
|
47
48
|
if (!value) return defaultValue;
|
|
48
49
|
const initialResult = value[property];
|
|
49
50
|
return converter ? converter(initialResult) : initialResult;
|
|
@@ -51,8 +52,6 @@ export function useBinding<
|
|
|
51
52
|
|
|
52
53
|
// run this on initial render
|
|
53
54
|
useEffect(() => {
|
|
54
|
-
const value = getValue(object);
|
|
55
|
-
|
|
56
55
|
if (!value) {
|
|
57
56
|
console.error(`Object is null or undefined`);
|
|
58
57
|
return;
|
|
@@ -67,15 +66,7 @@ export function useBinding<
|
|
|
67
66
|
};
|
|
68
67
|
}, []);
|
|
69
68
|
|
|
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;
|
|
69
|
+
return [result] as const;
|
|
79
70
|
}
|
|
80
71
|
|
|
81
72
|
function toKebabCase(str: string): string {
|
package/src/index.ts
CHANGED
package/src/types.ts
ADDED
|
File without changes
|