@musecat/functionkit 1.1.0 → 1.2.0
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/package.json +17 -1
- package/packages/cookie/cookie.shared.ts +10 -2
- package/packages/utils/floatingMotion.ts +1 -3
- package/.agents/references/components/ScrolltoTop.md +0 -36
- package/.agents/references/components/SwitchCase.md +0 -47
- package/.agents/references/components/ViewportPortal.md +0 -34
- package/.agents/references/cookie/cookie.md +0 -47
- package/.agents/references/datetime/dateTime.client.md +0 -42
- package/.agents/references/datetime/dateTime.server.md +0 -42
- package/.agents/references/datetime/dateTime.shared.md +0 -86
- package/.agents/references/hooks/useAvoidKeyboard.md +0 -29
- package/.agents/references/hooks/useCheckInvisible.md +0 -36
- package/.agents/references/hooks/useCheckScroll.md +0 -36
- package/.agents/references/hooks/useClientDateTime.md +0 -39
- package/.agents/references/hooks/useDebounce.md +0 -46
- package/.agents/references/hooks/useDebouncedCallback.md +0 -34
- package/.agents/references/hooks/useDoubleClick.md +0 -40
- package/.agents/references/hooks/useGeolocation.md +0 -37
- package/.agents/references/hooks/useHasMounted.md +0 -27
- package/.agents/references/hooks/useIntersectionObserver.md +0 -41
- package/.agents/references/hooks/useInterval.md +0 -44
- package/.agents/references/hooks/useKeyboardHeight.md +0 -33
- package/.agents/references/hooks/useKeyboardListNavigation.md +0 -58
- package/.agents/references/hooks/useLongPress.md +0 -48
- package/.agents/references/hooks/usePreservedCallback.md +0 -37
- package/.agents/references/hooks/usePreservedReference.md +0 -34
- package/.agents/references/hooks/useRefEffect.md +0 -35
- package/.agents/references/hooks/useRelativeDateTime.md +0 -35
- package/.agents/references/hooks/useTimeout.md +0 -42
- package/.agents/references/hooks/useToggleState.md +0 -39
- package/.agents/references/hooks/useViewportHeight.md +0 -26
- package/.agents/references/hooks/useViewportMatch.md +0 -32
- package/.agents/references/utils/browserStorage.md +0 -41
- package/.agents/references/utils/buildContext.md +0 -47
- package/.agents/references/utils/clipboardShare.md +0 -59
- package/.agents/references/utils/floatingMotion.md +0 -67
- package/.agents/references/utils/getDeviceInfo.md +0 -43
- package/.agents/references/utils/isEditableKeyboardTarget.md +0 -35
- package/.agents/references/utils/mergeRefs.md +0 -29
- package/.agents/references/utils/seen.md +0 -37
- package/.agents/references/utils/subscribeKeyboardHeight.md +0 -35
- package/.gitattributes +0 -2
- package/.github/ISSUE_TEMPLATE/bug_report.md +0 -31
- package/.github/ISSUE_TEMPLATE/feature_request.md +0 -22
- package/.github/PULL_REQUEST_TEMPLATE.md +0 -22
- package/.github/workflows/ci.yml +0 -20
- package/.local/state/gh/device-id +0 -1
- package/.prettierrc +0 -8
- package/AGENTS.md +0 -119
- package/CODE_OF_CONDUCT.md +0 -55
- package/CONTRIBUTING.md +0 -39
- package/biome.json +0 -25
- package/tests/components/ScrolltoTop.test.tsx +0 -15
- package/tests/components/SwitchCase.test.tsx +0 -33
- package/tests/components/ViewportPortal.test.tsx +0 -46
- package/tests/cookie/cookie.test.ts +0 -192
- package/tests/datetime/datetime.test.ts +0 -461
- package/tests/hooks/useAvoidKeyboard.test.ts +0 -43
- package/tests/hooks/useCheckInvisible.test.ts +0 -59
- package/tests/hooks/useCheckScroll.test.ts +0 -23
- package/tests/hooks/useClientDateTime.test.ts +0 -12
- package/tests/hooks/useDebounce.test.ts +0 -96
- package/tests/hooks/useDebouncedCallback.test.ts +0 -63
- package/tests/hooks/useDoubleClick.test.ts +0 -82
- package/tests/hooks/useGeolocation.test.ts +0 -201
- package/tests/hooks/useHasMounted.test.ts +0 -10
- package/tests/hooks/useIntersectionObserver.test.ts +0 -78
- package/tests/hooks/useInterval.test.ts +0 -56
- package/tests/hooks/useKeyboardHeight.test.ts +0 -10
- package/tests/hooks/useKeyboardListNavigation.test.ts +0 -404
- package/tests/hooks/useLongPress.test.ts +0 -214
- package/tests/hooks/usePreservedCallback.test.ts +0 -33
- package/tests/hooks/usePreservedReference.test.ts +0 -42
- package/tests/hooks/useRefEffect.test.ts +0 -67
- package/tests/hooks/useRelativeDateTime.test.ts +0 -21
- package/tests/hooks/useTimeout.test.ts +0 -72
- package/tests/hooks/useToggleState.test.ts +0 -43
- package/tests/hooks/useViewportHeight.test.ts +0 -32
- package/tests/hooks/useViewportMatch.test.ts +0 -35
- package/tests/setup.ts +0 -9
- package/tests/utils/browserStorage.test.ts +0 -114
- package/tests/utils/buildContext.test.tsx +0 -26
- package/tests/utils/checkDevice.test.ts +0 -140
- package/tests/utils/clipboardShare.test.ts +0 -152
- package/tests/utils/floatingMotion.test.ts +0 -135
- package/tests/utils/keyboardTarget.test.ts +0 -85
- package/tests/utils/mergeRefs.test.ts +0 -50
- package/tests/utils/seen.test.ts +0 -40
- package/tests/utils/subscribeKeyboardHeight.test.ts +0 -172
- package/tsconfig.json +0 -18
- package/vitest.config.ts +0 -22
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@musecat/functionkit",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.2.0",
|
|
4
4
|
"description": "React 19 + TypeScript frontend utility library — hooks, components, datetime, cookie, and utils",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"react",
|
|
@@ -10,6 +10,18 @@
|
|
|
10
10
|
"frontend"
|
|
11
11
|
],
|
|
12
12
|
"license": "MIT",
|
|
13
|
+
"private": false,
|
|
14
|
+
"author": "Musecat Team",
|
|
15
|
+
"engines": {
|
|
16
|
+
"node": ">=20.0.0"
|
|
17
|
+
},
|
|
18
|
+
"files": [
|
|
19
|
+
"index.ts",
|
|
20
|
+
"packages/",
|
|
21
|
+
"README.md",
|
|
22
|
+
"LICENSE"
|
|
23
|
+
],
|
|
24
|
+
"sideEffects": false,
|
|
13
25
|
"repository": {
|
|
14
26
|
"type": "git",
|
|
15
27
|
"url": "https://github.com/TheTechclip/FunctionKit.git"
|
|
@@ -31,6 +43,10 @@
|
|
|
31
43
|
"test": "vitest run",
|
|
32
44
|
"test:coverage": "vitest run --coverage"
|
|
33
45
|
},
|
|
46
|
+
"publishConfig": {
|
|
47
|
+
"registry": "https://registry.npmjs.org/",
|
|
48
|
+
"access": "public"
|
|
49
|
+
},
|
|
34
50
|
"peerDependencies": {
|
|
35
51
|
"react": "^19.0.0",
|
|
36
52
|
"react-dom": "^19.0.0"
|
|
@@ -40,6 +40,7 @@ function parseClientCookie(cookieString: string): Record<string, string> {
|
|
|
40
40
|
}
|
|
41
41
|
|
|
42
42
|
import { parseClientCookieNames } from "./cookieNames.shared";
|
|
43
|
+
|
|
43
44
|
export { parseClientCookieNames };
|
|
44
45
|
|
|
45
46
|
export function getClientCookie(name: string): string | undefined {
|
|
@@ -50,11 +51,18 @@ export function getClientCookie(name: string): string | undefined {
|
|
|
50
51
|
return findClientCookie(name, document);
|
|
51
52
|
}
|
|
52
53
|
|
|
53
|
-
export function setClientCookie(
|
|
54
|
+
export function setClientCookie(
|
|
55
|
+
name: string,
|
|
56
|
+
value: string,
|
|
57
|
+
days?: number,
|
|
58
|
+
documentRef?: DocumentCookieRef,
|
|
59
|
+
) {
|
|
54
60
|
if (typeof document === "undefined") {
|
|
55
61
|
return;
|
|
56
62
|
}
|
|
57
63
|
|
|
64
|
+
const ref = documentRef ?? document;
|
|
65
|
+
|
|
58
66
|
let expires = "";
|
|
59
67
|
if (days) {
|
|
60
68
|
const date = new Date();
|
|
@@ -62,7 +70,7 @@ export function setClientCookie(name: string, value: string, days?: number) {
|
|
|
62
70
|
expires = `; expires=${date.toUTCString()}`;
|
|
63
71
|
}
|
|
64
72
|
|
|
65
|
-
|
|
73
|
+
ref.cookie = `${name}=${encodeURIComponent(value)}${expires}; path=/`;
|
|
66
74
|
}
|
|
67
75
|
|
|
68
76
|
export function clearClientCookie(name: string, options: ClearClientCookieOptions = {}): void {
|
|
@@ -57,9 +57,7 @@ function parsePlacement(placement: FloatingPlacement): [FloatingRow, FloatingCol
|
|
|
57
57
|
return [row, col];
|
|
58
58
|
}
|
|
59
59
|
|
|
60
|
-
export const getFloatingTransformOrigin = (
|
|
61
|
-
placement?: FloatingPlacement,
|
|
62
|
-
): string => {
|
|
60
|
+
export const getFloatingTransformOrigin = (placement?: FloatingPlacement): string => {
|
|
63
61
|
if (!placement) return "top center";
|
|
64
62
|
|
|
65
63
|
const [row, col] = parsePlacement(placement);
|
|
@@ -1,36 +0,0 @@
|
|
|
1
|
-
# ScrolltoTop
|
|
2
|
-
|
|
3
|
-
## Purpose
|
|
4
|
-
|
|
5
|
-
Scrolls the window to the top (`0, 0`) when the component mounts. Typically placed at the top of a page layout to reset scroll position on navigation.
|
|
6
|
-
|
|
7
|
-
## Usage Logic
|
|
8
|
-
|
|
9
|
-
Calls `window.scrollTo(0, 0)` inside a `useEffect` with empty dependencies. No state, no props.
|
|
10
|
-
|
|
11
|
-
## Type Signature
|
|
12
|
-
|
|
13
|
-
```ts
|
|
14
|
-
function ScrolltoTop(): null;
|
|
15
|
-
```
|
|
16
|
-
|
|
17
|
-
Renders nothing — returns `null`.
|
|
18
|
-
|
|
19
|
-
## Example Code
|
|
20
|
-
|
|
21
|
-
```tsx
|
|
22
|
-
import { ScrolltoTop } from "@musecat/functionkit";
|
|
23
|
-
|
|
24
|
-
export default function Page() {
|
|
25
|
-
return (
|
|
26
|
-
<>
|
|
27
|
-
<ScrolltoTop />
|
|
28
|
-
<PageContent />
|
|
29
|
-
</>
|
|
30
|
-
);
|
|
31
|
-
}
|
|
32
|
-
```
|
|
33
|
-
|
|
34
|
-
## Note
|
|
35
|
-
|
|
36
|
-
Not exported from the barrel. Import via component subpath. Client-only (`"use client"`).
|
|
@@ -1,47 +0,0 @@
|
|
|
1
|
-
# SwitchCase
|
|
2
|
-
|
|
3
|
-
## Purpose
|
|
4
|
-
|
|
5
|
-
A declarative `switch`/`case` component for JSX. Renders the matching `ReactNode` based on a value, with an optional fallback (`otherwise`). Eliminates messy ternary chains and `&&` conditions.
|
|
6
|
-
|
|
7
|
-
## Usage Logic
|
|
8
|
-
|
|
9
|
-
Takes a `value` and a `cases` record. Iterates over the keys of `cases` and returns the first match using `Object.hasOwn`. If no key matches, renders `otherwise` (or `null`).
|
|
10
|
-
|
|
11
|
-
SSR-safe — pure function with no `"use client"`.
|
|
12
|
-
|
|
13
|
-
## Type Signature
|
|
14
|
-
|
|
15
|
-
```ts
|
|
16
|
-
interface SwitchCaseProps<T extends string | number> {
|
|
17
|
-
value: T;
|
|
18
|
-
cases: Partial<Record<T, ReactNode>>;
|
|
19
|
-
otherwise?: ReactNode;
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
function SwitchCase<T extends string | number>(props: SwitchCaseProps<T>): ReactNode;
|
|
23
|
-
```
|
|
24
|
-
|
|
25
|
-
## Example Code
|
|
26
|
-
|
|
27
|
-
```tsx
|
|
28
|
-
import { SwitchCase } from "@musecat/functionkit";
|
|
29
|
-
|
|
30
|
-
function StatusBadge({ status }: { status: "loading" | "success" | "error" }) {
|
|
31
|
-
return (
|
|
32
|
-
<SwitchCase
|
|
33
|
-
value={status}
|
|
34
|
-
cases={{
|
|
35
|
-
loading: <Spinner />,
|
|
36
|
-
success: <CheckIcon />,
|
|
37
|
-
error: <ErrorIcon />,
|
|
38
|
-
}}
|
|
39
|
-
otherwise={<Fallback />}
|
|
40
|
-
/>
|
|
41
|
-
);
|
|
42
|
-
}
|
|
43
|
-
```
|
|
44
|
-
|
|
45
|
-
## Note
|
|
46
|
-
|
|
47
|
-
Not exported from the barrel. Import via component subpath.
|
|
@@ -1,34 +0,0 @@
|
|
|
1
|
-
# ViewportPortal
|
|
2
|
-
|
|
3
|
-
## Purpose
|
|
4
|
-
|
|
5
|
-
Renders children into a fixed-position portal root (`#viewport-portal-root`) using `createPortal`. Designed for overlays, modals, toasts, and tooltips that need to escape parent overflow/stacking contexts.
|
|
6
|
-
|
|
7
|
-
## Usage Logic
|
|
8
|
-
|
|
9
|
-
On first render, ensures the portal root `<div>` exists in `document.body`. Then portals children into it. The portal root has `position: fixed`, `inset: 0`, `z-index: 9999`, and `pointer-events: none`. Children must set `pointer-events: auto` to be interactive.
|
|
10
|
-
|
|
11
|
-
## Type Signature
|
|
12
|
-
|
|
13
|
-
```ts
|
|
14
|
-
function ViewportPortal({ children }: { children: ReactNode }): ReactPortal;
|
|
15
|
-
|
|
16
|
-
// Utility to get the portal root element
|
|
17
|
-
function getViewportPortalRoot(): HTMLDivElement;
|
|
18
|
-
```
|
|
19
|
-
|
|
20
|
-
## Example Code
|
|
21
|
-
|
|
22
|
-
```tsx
|
|
23
|
-
import { ViewportPortal } from "@musecat/functionkit";
|
|
24
|
-
|
|
25
|
-
function ToastContainer() {
|
|
26
|
-
return (
|
|
27
|
-
<ViewportPortal>
|
|
28
|
-
<div style={{ pointerEvents: "auto" }}>
|
|
29
|
-
<Toast message="Saved!" />
|
|
30
|
-
</div>
|
|
31
|
-
</ViewportPortal>
|
|
32
|
-
);
|
|
33
|
-
}
|
|
34
|
-
```
|
|
@@ -1,47 +0,0 @@
|
|
|
1
|
-
# Cookie API
|
|
2
|
-
|
|
3
|
-
## Purpose
|
|
4
|
-
|
|
5
|
-
Client-side cookie read/write/delete utilities. All functions except `parseClientCookieNames` access `document.cookie` and are client-only.
|
|
6
|
-
|
|
7
|
-
## Usage Logic
|
|
8
|
-
|
|
9
|
-
Each function wraps native `document.cookie` operations. `setClientCookie` handles expiry via `Max-Age`. `clearClientCookie` and `clearAllClientCookies` set `Max-Age=0` to delete. `clearAllClientCookies` iterates over all domain/path combinations to ensure thorough cleanup.
|
|
10
|
-
|
|
11
|
-
## Type Signatures
|
|
12
|
-
|
|
13
|
-
```ts
|
|
14
|
-
function getClientCookie(name: string): string | null;
|
|
15
|
-
function setClientCookie(name: string, value: string, days?: number): void;
|
|
16
|
-
function clearClientCookie(name: string, domain?: string, path?: string): void;
|
|
17
|
-
function clearAllClientCookies(): void;
|
|
18
|
-
function parseClientCookieNames(cookieString: string): string[]; // SSR-safe
|
|
19
|
-
```
|
|
20
|
-
|
|
21
|
-
## Example Code
|
|
22
|
-
|
|
23
|
-
```tsx
|
|
24
|
-
import {
|
|
25
|
-
getClientCookie,
|
|
26
|
-
setClientCookie,
|
|
27
|
-
clearClientCookie,
|
|
28
|
-
clearAllClientCookies,
|
|
29
|
-
parseClientCookieNames,
|
|
30
|
-
} from "@musecat/functionkit";
|
|
31
|
-
|
|
32
|
-
function useSession() {
|
|
33
|
-
const token = getClientCookie("session_token");
|
|
34
|
-
|
|
35
|
-
const login = (token: string) => setClientCookie("session_token", token, 7);
|
|
36
|
-
|
|
37
|
-
const logout = () => clearClientCookie("session_token");
|
|
38
|
-
|
|
39
|
-
return { token, login, logout };
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
// SSR-safe usage (Server Component)
|
|
43
|
-
function CookieNames({ cookieString }: { cookieString: string }) {
|
|
44
|
-
const names = parseClientCookieNames(cookieString);
|
|
45
|
-
return <div>Cookies: {names.join(", ")}</div>;
|
|
46
|
-
}
|
|
47
|
-
```
|
|
@@ -1,42 +0,0 @@
|
|
|
1
|
-
# dateTime.client
|
|
2
|
-
|
|
3
|
-
## Purpose
|
|
4
|
-
|
|
5
|
-
Client-side date/time formatting wrappers. Same signatures as `dateTime.server` but bundled exclusively for client usage. Use in `"use client"` components only.
|
|
6
|
-
|
|
7
|
-
## Usage Logic
|
|
8
|
-
|
|
9
|
-
Same API surface as `dateTime.server` — identical function signatures. Separated to keep the server bundle clean of client-only code.
|
|
10
|
-
|
|
11
|
-
## Type Signatures
|
|
12
|
-
|
|
13
|
-
```ts
|
|
14
|
-
function formatClientDate(date: DateInput, locale: AppLocale, preset?: DatePreset): string;
|
|
15
|
-
function formatClientTime(date: DateInput, locale: AppLocale, preset?: TimePreset): string;
|
|
16
|
-
function formatClientDateTime(
|
|
17
|
-
date: DateInput,
|
|
18
|
-
locale: AppLocale,
|
|
19
|
-
options?: { datePreset?: DatePreset; timePreset?: TimePreset }
|
|
20
|
-
): string;
|
|
21
|
-
function formatClientRelative(
|
|
22
|
-
date: DateInput,
|
|
23
|
-
locale: AppLocale,
|
|
24
|
-
options?: { maxRelativeDays?: number }
|
|
25
|
-
): string;
|
|
26
|
-
```
|
|
27
|
-
|
|
28
|
-
## Example Code
|
|
29
|
-
|
|
30
|
-
```tsx
|
|
31
|
-
"use client";
|
|
32
|
-
import { formatClientDate, formatClientRelative } from "@musecat/functionkit";
|
|
33
|
-
|
|
34
|
-
function PostMeta({ createdAt }: { createdAt: string }) {
|
|
35
|
-
return (
|
|
36
|
-
<div>
|
|
37
|
-
<time>{formatClientDate(createdAt, "kr", "long")}</time>
|
|
38
|
-
<span>{formatClientRelative(createdAt, "kr")}</span>
|
|
39
|
-
</div>
|
|
40
|
-
);
|
|
41
|
-
}
|
|
42
|
-
```
|
|
@@ -1,42 +0,0 @@
|
|
|
1
|
-
# dateTime.server
|
|
2
|
-
|
|
3
|
-
## Purpose
|
|
4
|
-
|
|
5
|
-
Server-side date/time formatting wrappers. Use in Server Components, RSC, and any server-side rendering context. Pure functions, SSR-safe.
|
|
6
|
-
|
|
7
|
-
## Usage Logic
|
|
8
|
-
|
|
9
|
-
Delegates to `dateTime.shared` utilities internally. Provides simplified `DatePreset`/`TimePreset`-based APIs that compose multiple shared functions.
|
|
10
|
-
|
|
11
|
-
## Type Signatures
|
|
12
|
-
|
|
13
|
-
```ts
|
|
14
|
-
function formatServerDate(date: DateInput, locale: AppLocale, preset?: DatePreset): string;
|
|
15
|
-
function formatServerTime(date: DateInput, locale: AppLocale, preset?: TimePreset): string;
|
|
16
|
-
function formatServerDateTime(
|
|
17
|
-
date: DateInput,
|
|
18
|
-
locale: AppLocale,
|
|
19
|
-
options?: { datePreset?: DatePreset; timePreset?: TimePreset }
|
|
20
|
-
): string;
|
|
21
|
-
function formatServerRelative(
|
|
22
|
-
date: DateInput,
|
|
23
|
-
locale: AppLocale,
|
|
24
|
-
options?: { maxRelativeDays?: number }
|
|
25
|
-
): string;
|
|
26
|
-
```
|
|
27
|
-
|
|
28
|
-
## Example Code
|
|
29
|
-
|
|
30
|
-
```tsx
|
|
31
|
-
import { formatServerDate, formatServerRelative } from "@musecat/functionkit";
|
|
32
|
-
|
|
33
|
-
// Server Component
|
|
34
|
-
export default function ArticlePage({ post }: { post: { createdAt: string } }) {
|
|
35
|
-
return (
|
|
36
|
-
<article>
|
|
37
|
-
<time>{formatServerDate(post.createdAt, "kr", "long")}</time>
|
|
38
|
-
<span>{formatServerRelative(post.createdAt, "kr", { maxRelativeDays: 7 })}</span>
|
|
39
|
-
</article>
|
|
40
|
-
);
|
|
41
|
-
}
|
|
42
|
-
```
|
|
@@ -1,86 +0,0 @@
|
|
|
1
|
-
# dateTime.shared
|
|
2
|
-
|
|
3
|
-
## Purpose
|
|
4
|
-
|
|
5
|
-
Core date/time formatting utilities and types. All functions are pure and SSR-safe — no `"use client"`, no `window` access. Forms the foundation for both server and client date wrappers.
|
|
6
|
-
|
|
7
|
-
## Types
|
|
8
|
-
|
|
9
|
-
```ts
|
|
10
|
-
type AppLocale = "kr" | "en" | "jp";
|
|
11
|
-
type DateInput = string | number | Date;
|
|
12
|
-
type DatePreset = "long" | "dot";
|
|
13
|
-
type TimePreset = "ko" | "12h" | "24h-minute" | "24h-second";
|
|
14
|
-
```
|
|
15
|
-
|
|
16
|
-
## Locale Conversion
|
|
17
|
-
|
|
18
|
-
```ts
|
|
19
|
-
// "ko" → "kr", "ja" → "jp", anything else → "en"
|
|
20
|
-
function normalizeAppLocale(locale: string): AppLocale;
|
|
21
|
-
|
|
22
|
-
// "kr" → "ko-KR", "jp" → "ja-JP", "en" → "en-US"
|
|
23
|
-
function toIntlLocale(locale: AppLocale): string;
|
|
24
|
-
```
|
|
25
|
-
|
|
26
|
-
## UTC Date Utilities
|
|
27
|
-
|
|
28
|
-
```ts
|
|
29
|
-
function toDate(input: DateInput): Date | null; // NaN-safe Date conversion
|
|
30
|
-
function toUtcMidnight(date: Date): Date; // UTC midnight of the given date
|
|
31
|
-
function parseUtcDateInput(input: string): Date | null; // "YYYY-MM-DD" → UTC Date
|
|
32
|
-
function formatUtcDateKey(date: Date): string; // UTC Date → "YYYY-MM-DD"
|
|
33
|
-
function getUtcWeekdayIndex(date: Date): number; // 0=Sunday
|
|
34
|
-
function addUtcDays(date: Date, days: number): Date; // Add days in UTC
|
|
35
|
-
```
|
|
36
|
-
|
|
37
|
-
## Formatted Output Functions
|
|
38
|
-
|
|
39
|
-
```ts
|
|
40
|
-
// Long date: "2026년 7월 16일" / "July 16, 2026" / "2026年7月16日"
|
|
41
|
-
function formatLongDate(date: DateInput, locale: AppLocale): string;
|
|
42
|
-
|
|
43
|
-
// Dot date: "2026. 7. 16."
|
|
44
|
-
function formatDotDate(date: DateInput, locale: AppLocale): string;
|
|
45
|
-
|
|
46
|
-
// 24-hour time: "14:30" or "14:30:00"
|
|
47
|
-
function format24HourTime(date: DateInput, options?: { withSeconds?: boolean }): string;
|
|
48
|
-
|
|
49
|
-
// Korean time: "오후 2시 30분"
|
|
50
|
-
function formatKoreanTime(date: DateInput): string;
|
|
51
|
-
|
|
52
|
-
// 12-hour with locale: "오후 2:30" / "2:30 PM" / "午後 2:30"
|
|
53
|
-
function formatTwelveHourTime(date: DateInput, locale: AppLocale): string;
|
|
54
|
-
```
|
|
55
|
-
|
|
56
|
-
## Relative / Remaining Time
|
|
57
|
-
|
|
58
|
-
```ts
|
|
59
|
-
// Relative: { text: "3분 전", isRelative: true }
|
|
60
|
-
function formatRelativeText(diffMs: number, locale: AppLocale): { text: string; isRelative: boolean };
|
|
61
|
-
|
|
62
|
-
// Remaining: "2일 3시간 남음" / "2 days 3 hours left" / "あと2日3時間"
|
|
63
|
-
function formatRemainingText(diffMs: number, locale: AppLocale): string;
|
|
64
|
-
```
|
|
65
|
-
|
|
66
|
-
## Example Code
|
|
67
|
-
|
|
68
|
-
```tsx
|
|
69
|
-
import {
|
|
70
|
-
formatLongDate,
|
|
71
|
-
formatRelativeText,
|
|
72
|
-
formatUtcDateKey,
|
|
73
|
-
normalizeAppLocale,
|
|
74
|
-
toIntlLocale,
|
|
75
|
-
} from "@musecat/functionkit";
|
|
76
|
-
|
|
77
|
-
function EventDate({ date, locale }: { date: Date; locale: string }) {
|
|
78
|
-
const appLocale = normalizeAppLocale(locale);
|
|
79
|
-
return <time>{formatLongDate(date, appLocale)}</time>;
|
|
80
|
-
}
|
|
81
|
-
|
|
82
|
-
function RelativeTime({ diffMs }: { diffMs: number }) {
|
|
83
|
-
const { text, isRelative } = formatRelativeText(diffMs, "kr");
|
|
84
|
-
return <span>{text}</span>;
|
|
85
|
-
}
|
|
86
|
-
```
|
|
@@ -1,29 +0,0 @@
|
|
|
1
|
-
# useAvoidKeyboard
|
|
2
|
-
|
|
3
|
-
## Purpose
|
|
4
|
-
|
|
5
|
-
Returns a style object that pushes the bottom of the viewport upward via `translateY` when the virtual keyboard appears. Useful for fixing mobile layout issues where the keyboard overlaps input fields.
|
|
6
|
-
|
|
7
|
-
## Usage Logic
|
|
8
|
-
|
|
9
|
-
Internally subscribes to `useKeyboardHeight`. When the keyboard opens, the returned style applies a negative `translateY` equal to the keyboard height, keeping the focused element visible. The transition is animated via CSS `transition`.
|
|
10
|
-
|
|
11
|
-
## Type Signature
|
|
12
|
-
|
|
13
|
-
```ts
|
|
14
|
-
function useAvoidKeyboard(): React.CSSProperties
|
|
15
|
-
```
|
|
16
|
-
|
|
17
|
-
Returns a style object containing `transform`, `transition`, and `willChange` properties.
|
|
18
|
-
|
|
19
|
-
## Example Code
|
|
20
|
-
|
|
21
|
-
```tsx
|
|
22
|
-
import { useAvoidKeyboard } from "@musecat/functionkit";
|
|
23
|
-
import { View } from "./YourView";
|
|
24
|
-
|
|
25
|
-
function ChatInput() {
|
|
26
|
-
const keyboardStyle = useAvoidKeyboard();
|
|
27
|
-
return <View style={keyboardStyle}>...</View>;
|
|
28
|
-
}
|
|
29
|
-
```
|
|
@@ -1,36 +0,0 @@
|
|
|
1
|
-
# useCheckInvisible
|
|
2
|
-
|
|
3
|
-
## Purpose
|
|
4
|
-
|
|
5
|
-
Detects whether an element with a given CSS class name has scrolled above the viewport (`top < 0`). Useful for triggering "sticky" or "collapsed" states when a section scrolls out of view.
|
|
6
|
-
|
|
7
|
-
## Usage Logic
|
|
8
|
-
|
|
9
|
-
Uses `IntersectionObserver` or scroll event tracking to monitor the first element matching the provided class name. Returns `true` when the element's top edge is above the viewport.
|
|
10
|
-
|
|
11
|
-
## Type Signature
|
|
12
|
-
|
|
13
|
-
```ts
|
|
14
|
-
function useCheckInvisible(className: string): boolean
|
|
15
|
-
```
|
|
16
|
-
|
|
17
|
-
| Parameter | Type | Description |
|
|
18
|
-
|-----------|------|-------------|
|
|
19
|
-
| `className` | `string` | CSS class name of the element to monitor |
|
|
20
|
-
|
|
21
|
-
Returns `boolean` — `true` when the element is scrolled out of view (top < 0).
|
|
22
|
-
|
|
23
|
-
## Example Code
|
|
24
|
-
|
|
25
|
-
```tsx
|
|
26
|
-
import { useCheckInvisible } from "@musecat/functionkit/hooks/useCheckInvisible";
|
|
27
|
-
|
|
28
|
-
function Header() {
|
|
29
|
-
const isHidden = useCheckInvisible("hero-section");
|
|
30
|
-
return <header className={isHidden ? "sticky" : ""}>...</header>;
|
|
31
|
-
}
|
|
32
|
-
```
|
|
33
|
-
|
|
34
|
-
## Note
|
|
35
|
-
|
|
36
|
-
Not exported from the barrel. Import via subpath only: `@musecat/functionkit/hooks/useCheckInvisible`.
|
|
@@ -1,36 +0,0 @@
|
|
|
1
|
-
# useCheckScroll
|
|
2
|
-
|
|
3
|
-
## Purpose
|
|
4
|
-
|
|
5
|
-
Detects whether the page has been scrolled vertically (`window.scrollY > 0`). Returns an `isScrolled` flag for toggling UI states such as sticky header shadows or floating action buttons.
|
|
6
|
-
|
|
7
|
-
## Usage Logic
|
|
8
|
-
|
|
9
|
-
Listens to the `scroll` event on `window`. Memoizes the check to avoid unnecessary re-renders.
|
|
10
|
-
|
|
11
|
-
## Type Signature
|
|
12
|
-
|
|
13
|
-
```ts
|
|
14
|
-
function useCheckScroll(): { isScrolled: boolean }
|
|
15
|
-
```
|
|
16
|
-
|
|
17
|
-
Returns `{ isScrolled: boolean }` — `isScrolled` is `true` when `window.scrollY > 0`.
|
|
18
|
-
|
|
19
|
-
## Example Code
|
|
20
|
-
|
|
21
|
-
```tsx
|
|
22
|
-
import { useCheckScroll } from "@musecat/functionkit/hooks/useCheckScroll";
|
|
23
|
-
|
|
24
|
-
function StickyHeader() {
|
|
25
|
-
const { isScrolled } = useCheckScroll();
|
|
26
|
-
return (
|
|
27
|
-
<header className={isScrolled ? "header--scrolled" : "header--top"}>
|
|
28
|
-
...
|
|
29
|
-
</header>
|
|
30
|
-
);
|
|
31
|
-
}
|
|
32
|
-
```
|
|
33
|
-
|
|
34
|
-
## Note
|
|
35
|
-
|
|
36
|
-
Not exported from the barrel. Import via subpath only: `@musecat/functionkit/hooks/useCheckScroll`.
|
|
@@ -1,39 +0,0 @@
|
|
|
1
|
-
# useClientDateTime
|
|
2
|
-
|
|
3
|
-
## Purpose
|
|
4
|
-
|
|
5
|
-
Formats a date on the client side using the user's locale and timezone. Returns the formatted text inside a `useEffect`, ensuring hydration safety via a `ready` flag.
|
|
6
|
-
|
|
7
|
-
## Usage Logic
|
|
8
|
-
|
|
9
|
-
Takes a `DateInput` and locale, then computes the formatted date/time string on mount and whenever inputs change. Returns a `ready` boolean (initially `false`) so the component can render a fallback (e.g., server-rendered date) before hydration completes.
|
|
10
|
-
|
|
11
|
-
## Type Signature
|
|
12
|
-
|
|
13
|
-
```ts
|
|
14
|
-
function useClientDateTime(
|
|
15
|
-
date: DateInput,
|
|
16
|
-
locale: AppLocale,
|
|
17
|
-
options?: {
|
|
18
|
-
datePreset?: DatePreset;
|
|
19
|
-
timePreset?: TimePreset;
|
|
20
|
-
}
|
|
21
|
-
): {
|
|
22
|
-
ready: boolean;
|
|
23
|
-
text: string;
|
|
24
|
-
date: Date | null;
|
|
25
|
-
}
|
|
26
|
-
```
|
|
27
|
-
|
|
28
|
-
## Example Code
|
|
29
|
-
|
|
30
|
-
```tsx
|
|
31
|
-
import { useClientDateTime } from "@musecat/functionkit";
|
|
32
|
-
|
|
33
|
-
function PostDate({ timestamp }: { timestamp: string }) {
|
|
34
|
-
const { ready, text } = useClientDateTime(timestamp, "kr", {
|
|
35
|
-
datePreset: "long",
|
|
36
|
-
});
|
|
37
|
-
return <time>{ready ? text : timestamp}</time>;
|
|
38
|
-
}
|
|
39
|
-
```
|
|
@@ -1,46 +0,0 @@
|
|
|
1
|
-
# useDebounce / debounce
|
|
2
|
-
|
|
3
|
-
## Purpose
|
|
4
|
-
|
|
5
|
-
Delays the propagation of a rapidly changing value. Includes both a raw utility function (`debounce`) for non-React use and a React hook (`useDebounce`) that auto-cleans up on unmount.
|
|
6
|
-
|
|
7
|
-
## Usage Logic
|
|
8
|
-
|
|
9
|
-
**`debounce`**: A pure function that wraps any callback with configurable leading/trailing edge execution. Returns the debounced function with a `.cancel()` method.
|
|
10
|
-
|
|
11
|
-
**`useDebounce`**: React hook that returns the debounced value. Internally uses `useState` + `useEffect` with the raw `debounce` utility. Automatically cancels pending timers on unmount.
|
|
12
|
-
|
|
13
|
-
## Type Signatures
|
|
14
|
-
|
|
15
|
-
```ts
|
|
16
|
-
// Pure function — SSR-safe
|
|
17
|
-
function debounce<T extends (...args: any[]) => any>(
|
|
18
|
-
fn: T,
|
|
19
|
-
delay: number,
|
|
20
|
-
options?: { leading?: boolean; trailing?: boolean }
|
|
21
|
-
): T & { cancel: () => void };
|
|
22
|
-
|
|
23
|
-
// React hook — client-only
|
|
24
|
-
function useDebounce<T>(
|
|
25
|
-
value: T,
|
|
26
|
-
delay: number,
|
|
27
|
-
options?: { leading?: boolean; trailing?: boolean }
|
|
28
|
-
): T;
|
|
29
|
-
```
|
|
30
|
-
|
|
31
|
-
## Example Code
|
|
32
|
-
|
|
33
|
-
```tsx
|
|
34
|
-
import { useDebounce } from "@musecat/functionkit";
|
|
35
|
-
|
|
36
|
-
function SearchInput() {
|
|
37
|
-
const [query, setQuery] = useState("");
|
|
38
|
-
const debouncedQuery = useDebounce(query, 300);
|
|
39
|
-
|
|
40
|
-
useEffect(() => {
|
|
41
|
-
if (debouncedQuery) fetchResults(debouncedQuery);
|
|
42
|
-
}, [debouncedQuery]);
|
|
43
|
-
|
|
44
|
-
return <input onChange={(e) => setQuery(e.target.value)} />;
|
|
45
|
-
}
|
|
46
|
-
```
|
|
@@ -1,34 +0,0 @@
|
|
|
1
|
-
# useDebouncedCallback
|
|
2
|
-
|
|
3
|
-
## Purpose
|
|
4
|
-
|
|
5
|
-
Provides a debounced callback for boolean value changes. Unlike `useDebounce` which debounces a value, this hook debounces the *invocation* of a callback, automatically canceling the previous pending call.
|
|
6
|
-
|
|
7
|
-
## Usage Logic
|
|
8
|
-
|
|
9
|
-
Wraps the `onChange` handler so it only fires after `timeThreshold` ms of inactivity. Each call replaces the previous pending timer, acting as a "settled" change detector.
|
|
10
|
-
|
|
11
|
-
## Type Signature
|
|
12
|
-
|
|
13
|
-
```ts
|
|
14
|
-
function useDebouncedCallback(
|
|
15
|
-
onChange: (value: boolean) => void,
|
|
16
|
-
timeThreshold: number
|
|
17
|
-
): (value: boolean) => void;
|
|
18
|
-
```
|
|
19
|
-
|
|
20
|
-
Returns a memoized setter that debounces calls to `onChange`.
|
|
21
|
-
|
|
22
|
-
## Example Code
|
|
23
|
-
|
|
24
|
-
```tsx
|
|
25
|
-
import { useDebouncedCallback } from "@musecat/functionkit";
|
|
26
|
-
|
|
27
|
-
function ToggleTracker() {
|
|
28
|
-
const handleChange = useDebouncedCallback((value) => {
|
|
29
|
-
console.log("Toggle settled:", value);
|
|
30
|
-
}, 500);
|
|
31
|
-
|
|
32
|
-
return <Toggle onChange={handleChange} />;
|
|
33
|
-
}
|
|
34
|
-
```
|