mirror of
https://github.com/mountain-loop/yaak.git
synced 2026-05-15 20:27:09 +02:00
Split codebase (#455)
This commit is contained in:
30
packages/ui/src/hooks/useContainerSize.ts
Normal file
30
packages/ui/src/hooks/useContainerSize.ts
Normal file
@@ -0,0 +1,30 @@
|
||||
import type { RefObject } from "react";
|
||||
import { useLayoutEffect, useState } from "react";
|
||||
|
||||
export function useContainerSize(ref: RefObject<HTMLElement | null>) {
|
||||
const [size, setSize] = useState<{ width: number; height: number }>({ width: 0, height: 0 });
|
||||
|
||||
useLayoutEffect(() => {
|
||||
const el = ref.current;
|
||||
if (el) {
|
||||
const observer = new ResizeObserver((entries) => {
|
||||
for (const entry of entries) {
|
||||
if (entry.target === el) {
|
||||
setSize({ width: entry.contentRect.width, height: entry.contentRect.height });
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
observer.observe(el);
|
||||
|
||||
return () => {
|
||||
observer.unobserve(el);
|
||||
observer.disconnect();
|
||||
};
|
||||
}
|
||||
|
||||
return undefined;
|
||||
}, [ref]);
|
||||
|
||||
return size;
|
||||
}
|
||||
12
packages/ui/src/hooks/useDebouncedState.ts
Normal file
12
packages/ui/src/hooks/useDebouncedState.ts
Normal file
@@ -0,0 +1,12 @@
|
||||
import { debounce } from "@yaakapp-internal/lib";
|
||||
import type { Dispatch, SetStateAction } from "react";
|
||||
import { useMemo, useState } from "react";
|
||||
|
||||
export function useDebouncedState<T>(
|
||||
defaultValue: T,
|
||||
delay = 500,
|
||||
): [T, Dispatch<SetStateAction<T>>, Dispatch<SetStateAction<T>>] {
|
||||
const [state, setState] = useState<T>(defaultValue);
|
||||
const debouncedSetState = useMemo(() => debounce(setState, delay), [delay]);
|
||||
return [state, debouncedSetState, setState];
|
||||
}
|
||||
8
packages/ui/src/hooks/useDebouncedValue.ts
Normal file
8
packages/ui/src/hooks/useDebouncedValue.ts
Normal file
@@ -0,0 +1,8 @@
|
||||
import { useEffect } from "react";
|
||||
import { useDebouncedState } from "./useDebouncedState";
|
||||
|
||||
export function useDebouncedValue<T>(value: T, delay = 500) {
|
||||
const [state, setState] = useDebouncedState<T>(value, delay);
|
||||
useEffect(() => setState(value), [setState, value]);
|
||||
return state;
|
||||
}
|
||||
22
packages/ui/src/hooks/useIsFullscreen.ts
Normal file
22
packages/ui/src/hooks/useIsFullscreen.ts
Normal file
@@ -0,0 +1,22 @@
|
||||
import { useQuery } from "@tanstack/react-query";
|
||||
import { getCurrentWebviewWindow } from "@tauri-apps/api/webviewWindow";
|
||||
import { useWindowSize } from "react-use";
|
||||
import { useDebouncedValue } from "./useDebouncedValue";
|
||||
|
||||
export function useIsFullscreen() {
|
||||
const windowSize = useWindowSize();
|
||||
const debouncedWindowWidth = useDebouncedValue(windowSize.width);
|
||||
|
||||
// NOTE: Fullscreen state isn't updated right after resize event on Mac (needs to wait for animation) so
|
||||
// we'll wait for a bit using the debounced window size. Hopefully Tauri eventually adds a way to listen
|
||||
// for fullscreen change events.
|
||||
|
||||
return (
|
||||
useQuery({
|
||||
queryKey: ["is_fullscreen", debouncedWindowWidth],
|
||||
queryFn: async () => {
|
||||
return getCurrentWebviewWindow().isFullscreen();
|
||||
},
|
||||
}).data ?? false
|
||||
);
|
||||
}
|
||||
26
packages/ui/src/hooks/usePortal.ts
Normal file
26
packages/ui/src/hooks/usePortal.ts
Normal file
@@ -0,0 +1,26 @@
|
||||
import { useRef } from "react";
|
||||
|
||||
const PORTAL_CONTAINER_ID = "react-portal";
|
||||
|
||||
export function usePortal(name: string) {
|
||||
const ref = useRef(getOrCreatePortal(name));
|
||||
return ref.current;
|
||||
}
|
||||
|
||||
function getOrCreatePortal(name: string) {
|
||||
let portalContainer = document.getElementById(PORTAL_CONTAINER_ID);
|
||||
if (!portalContainer) {
|
||||
portalContainer = document.createElement("div");
|
||||
portalContainer.id = PORTAL_CONTAINER_ID;
|
||||
document.body.appendChild(portalContainer);
|
||||
}
|
||||
|
||||
let existing = portalContainer.querySelector(`:scope > [data-portal-name="${name}"]`);
|
||||
if (!existing) {
|
||||
const el = document.createElement("div");
|
||||
el.setAttribute("data-portal-name", name);
|
||||
portalContainer.appendChild(el);
|
||||
existing = el;
|
||||
}
|
||||
return existing;
|
||||
}
|
||||
19
packages/ui/src/hooks/useTimedBoolean.ts
Normal file
19
packages/ui/src/hooks/useTimedBoolean.ts
Normal file
@@ -0,0 +1,19 @@
|
||||
import { useRef, useState } from "react";
|
||||
import { useUnmount } from "react-use";
|
||||
|
||||
/** Returns a boolean that is true for a given number of milliseconds. */
|
||||
export function useTimedBoolean(millis = 1500): [boolean, () => void] {
|
||||
const [value, setValue] = useState(false);
|
||||
const timeout = useRef<ReturnType<typeof setTimeout> | null>(null);
|
||||
const reset = () => timeout.current && clearTimeout(timeout.current);
|
||||
|
||||
useUnmount(reset);
|
||||
|
||||
const setToTrue = () => {
|
||||
setValue(true);
|
||||
reset();
|
||||
timeout.current = setTimeout(() => setValue(false), millis);
|
||||
};
|
||||
|
||||
return [value, setToTrue];
|
||||
}
|
||||
Reference in New Issue
Block a user