@musecat/functionkit 1.1.0 → 1.2.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/.agents/references/cookie/cookie.md +10 -4
- package/README.md +4 -0
- package/package.json +18 -1
- package/packages/cookie/cookie.shared.ts +10 -2
- package/packages/utils/floatingMotion.ts +1 -3
- 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
|
@@ -8,16 +8,22 @@ Client-side cookie read/write/delete utilities. All functions except `parseClien
|
|
|
8
8
|
|
|
9
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
10
|
|
|
11
|
+
All mutation functions accept an optional `documentRef` to inject a mock document for testing. `setClientCookie` also accepts an optional `CookieStoreLike` via options.
|
|
12
|
+
|
|
11
13
|
## Type Signatures
|
|
12
14
|
|
|
13
15
|
```ts
|
|
14
|
-
function getClientCookie(name: string): string |
|
|
15
|
-
function setClientCookie(name: string, value: string, days?: number): void;
|
|
16
|
-
function clearClientCookie(name: string,
|
|
17
|
-
function clearAllClientCookies():
|
|
16
|
+
function getClientCookie(name: string): string | undefined;
|
|
17
|
+
function setClientCookie(name: string, value: string, days?: number, documentRef?: DocumentCookieRef): void;
|
|
18
|
+
function clearClientCookie(name: string, options?: ClearClientCookieOptions): void;
|
|
19
|
+
function clearAllClientCookies(options?: ClearAllClientCookiesOptions): string[];
|
|
18
20
|
function parseClientCookieNames(cookieString: string): string[]; // SSR-safe
|
|
19
21
|
```
|
|
20
22
|
|
|
23
|
+
- `DocumentCookieRef` = `{ cookie: string }`
|
|
24
|
+
- `ClearClientCookieOptions` = `{ hostname?: string; path?: string; documentRef?: DocumentCookieRef; cookieStore?: CookieStoreLike }`
|
|
25
|
+
- `ClearAllClientCookiesOptions` extends `ClearClientCookieOptions` with `{ includeRoot?: boolean; cookieString?: string }`
|
|
26
|
+
|
|
21
27
|
## Example Code
|
|
22
28
|
|
|
23
29
|
```tsx
|
package/README.md
CHANGED
|
@@ -29,6 +29,10 @@ export default function Example() {
|
|
|
29
29
|
}
|
|
30
30
|
```
|
|
31
31
|
|
|
32
|
+
## References
|
|
33
|
+
|
|
34
|
+
This package includes `.agents/references/` directory with detailed documentation for every hook, component, and utility. Use it alongside TypeScript definitions as a guide when building with `@musecat/functionkit`.
|
|
35
|
+
|
|
32
36
|
## Acknowledgements
|
|
33
37
|
|
|
34
38
|
- [toss/react-simplikit](https://github.com/toss/react-simplikit/)
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@musecat/functionkit",
|
|
3
|
-
"version": "1.1
|
|
3
|
+
"version": "1.2.1",
|
|
4
4
|
"description": "React 19 + TypeScript frontend utility library — hooks, components, datetime, cookie, and utils",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"react",
|
|
@@ -10,6 +10,19 @@
|
|
|
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
|
+
".agents/",
|
|
22
|
+
"README.md",
|
|
23
|
+
"LICENSE"
|
|
24
|
+
],
|
|
25
|
+
"sideEffects": false,
|
|
13
26
|
"repository": {
|
|
14
27
|
"type": "git",
|
|
15
28
|
"url": "https://github.com/TheTechclip/FunctionKit.git"
|
|
@@ -31,6 +44,10 @@
|
|
|
31
44
|
"test": "vitest run",
|
|
32
45
|
"test:coverage": "vitest run --coverage"
|
|
33
46
|
},
|
|
47
|
+
"publishConfig": {
|
|
48
|
+
"registry": "https://registry.npmjs.org/",
|
|
49
|
+
"access": "public"
|
|
50
|
+
},
|
|
34
51
|
"peerDependencies": {
|
|
35
52
|
"react": "^19.0.0",
|
|
36
53
|
"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);
|
package/.gitattributes
DELETED
|
@@ -1,31 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
name: Bug report
|
|
3
|
-
about: Report a bug to help us improve
|
|
4
|
-
title: "[BUG] "
|
|
5
|
-
labels: bug
|
|
6
|
-
---
|
|
7
|
-
|
|
8
|
-
## Describe the Bug
|
|
9
|
-
|
|
10
|
-
A clear and concise description of what the bug is.
|
|
11
|
-
|
|
12
|
-
## To Reproduce
|
|
13
|
-
|
|
14
|
-
Steps to reproduce the behavior:
|
|
15
|
-
1. Import '...'
|
|
16
|
-
2. Call '...'
|
|
17
|
-
3. See error
|
|
18
|
-
|
|
19
|
-
## Expected Behavior
|
|
20
|
-
|
|
21
|
-
A clear and concise description of what you expected to happen.
|
|
22
|
-
|
|
23
|
-
## Environment
|
|
24
|
-
|
|
25
|
-
- React version:
|
|
26
|
-
- @musecat/functionkit version:
|
|
27
|
-
- Browser / Node version:
|
|
28
|
-
|
|
29
|
-
## Additional Context
|
|
30
|
-
|
|
31
|
-
Add any other context about the problem here.
|
|
@@ -1,22 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
name: Feature request
|
|
3
|
-
about: Suggest an idea for this project
|
|
4
|
-
title: "[FEATURE] "
|
|
5
|
-
labels: enhancement
|
|
6
|
-
---
|
|
7
|
-
|
|
8
|
-
## Problem Statement
|
|
9
|
-
|
|
10
|
-
Is your feature request related to a problem? Please describe clearly.
|
|
11
|
-
|
|
12
|
-
## Proposed Solution
|
|
13
|
-
|
|
14
|
-
A clear and concise description of what you want to happen.
|
|
15
|
-
|
|
16
|
-
## Alternative Solutions
|
|
17
|
-
|
|
18
|
-
A description of any alternative solutions or features you've considered.
|
|
19
|
-
|
|
20
|
-
## Additional Context
|
|
21
|
-
|
|
22
|
-
Add any other context or examples about the feature request here.
|
|
@@ -1,22 +0,0 @@
|
|
|
1
|
-
## Description
|
|
2
|
-
|
|
3
|
-
Please include a summary of the change and which issue is fixed.
|
|
4
|
-
|
|
5
|
-
## Type of Change
|
|
6
|
-
|
|
7
|
-
- [ ] Bug fix
|
|
8
|
-
- [ ] New feature
|
|
9
|
-
- [ ] Breaking change
|
|
10
|
-
- [ ] Documentation update
|
|
11
|
-
|
|
12
|
-
## How Has This Been Tested?
|
|
13
|
-
|
|
14
|
-
Please describe the tests that you ran to verify your changes.
|
|
15
|
-
|
|
16
|
-
## Checklist
|
|
17
|
-
|
|
18
|
-
- [ ] My code follows the project's code style
|
|
19
|
-
- [ ] I have added tests that prove my fix/feature works
|
|
20
|
-
- [ ] `npm run lint` passes
|
|
21
|
-
- [ ] `npm test` passes
|
|
22
|
-
- [ ] Barrel exports (`index.ts`) are updated if needed
|
package/.github/workflows/ci.yml
DELETED
|
@@ -1,20 +0,0 @@
|
|
|
1
|
-
name: CI
|
|
2
|
-
|
|
3
|
-
on:
|
|
4
|
-
push:
|
|
5
|
-
branches: [master]
|
|
6
|
-
pull_request:
|
|
7
|
-
branches: [master]
|
|
8
|
-
|
|
9
|
-
jobs:
|
|
10
|
-
quality:
|
|
11
|
-
runs-on: ubuntu-latest
|
|
12
|
-
steps:
|
|
13
|
-
- uses: actions/checkout@v4
|
|
14
|
-
- uses: actions/setup-node@v4
|
|
15
|
-
with:
|
|
16
|
-
node-version: 22
|
|
17
|
-
cache: npm
|
|
18
|
-
- run: npm ci
|
|
19
|
-
- run: npm run lint
|
|
20
|
-
- run: npm test
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
f1f55548-1baa-4638-bd0f-f3da5e3262f0
|
package/.prettierrc
DELETED
package/AGENTS.md
DELETED
|
@@ -1,119 +0,0 @@
|
|
|
1
|
-
# FunctionKit (@musecat/functionkit)
|
|
2
|
-
|
|
3
|
-
React 19 frontend utility library. Single package, ESM.
|
|
4
|
-
|
|
5
|
-
## Import Convention Enforcement (Mandatory)
|
|
6
|
-
|
|
7
|
-
- **Barrel imports are the rule:** `import { useDebounce } from "@musecat/functionkit"`.
|
|
8
|
-
- **Subpath imports are only allowed when:** the export is not in the barrel (`useCheckInvisible`, `SwitchCase`, `ScrolltoTop`) or tree-shaking is absolutely required.
|
|
9
|
-
- **Type imports also go through the barrel:** `import type { AppLocale, DateInput } from "@musecat/functionkit"`.
|
|
10
|
-
- **No reimplementation:** If FunctionKit already provides a feature, reuse it. Never write a duplicate.
|
|
11
|
-
|
|
12
|
-
## SSR Safety Classification
|
|
13
|
-
|
|
14
|
-
| Category | Condition | Examples |
|
|
15
|
-
|---|---|---|
|
|
16
|
-
| **SSR-safe** | No `"use client"`, pure function | `formatLongDate`, `mergeRefs`, `SwitchCase`, `getDeviceInfo`, `floatingMotion`, `seen`, `parseClientCookieNames`, `debounce` |
|
|
17
|
-
| **Client-only** | `"use client"` or `window` access | All hooks, `ViewportPortal`, `ScrolltoTop`, `browserStorage`, `cookie` (except `parseClientCookieNames`), `clipboardShare`, `isEditableKeyboardTarget` |
|
|
18
|
-
|
|
19
|
-
SSR-safe functions can be used freely in Server Components, RSC, and Client Components. Client-only items must only be used in `"use client"` components.
|
|
20
|
-
|
|
21
|
-
## Barrel Export Mapping
|
|
22
|
-
|
|
23
|
-
The barrel (`index.ts`) exports 63 named exports. **Items NOT in the barrel (require subpath imports):**
|
|
24
|
-
- `SwitchCase` — component subpath
|
|
25
|
-
- `ScrolltoTop` — component subpath
|
|
26
|
-
- `useCheckInvisible` — `@musecat/functionkit/hooks/useCheckInvisible`
|
|
27
|
-
- `useCheckScroll` — `@musecat/functionkit/hooks/useCheckScroll`
|
|
28
|
-
|
|
29
|
-
When adding new features, register them in the barrel.
|
|
30
|
-
|
|
31
|
-
## Hook Selection Guidelines
|
|
32
|
-
|
|
33
|
-
| Scenario | Hook to Use |
|
|
34
|
-
|---|---|
|
|
35
|
-
| Debounce rapid state changes | `useDebounce` or `useDebouncedCallback` |
|
|
36
|
-
| Polling / repeated execution | `useInterval` |
|
|
37
|
-
| Delayed single execution | `useTimeout` |
|
|
38
|
-
| Keyboard list navigation | `useKeyboardListNavigation` |
|
|
39
|
-
| Long press gesture | `useLongPress` |
|
|
40
|
-
| Single/double click distinction | `useDoubleClick` |
|
|
41
|
-
| Geolocation tracking | `useGeolocation` |
|
|
42
|
-
| Client date formatting | `useClientDateTime` |
|
|
43
|
-
| Relative time ("3 minutes ago") | `useRelativeDateTime` |
|
|
44
|
-
| IntersectionObserver | `useIntersectionObserver` |
|
|
45
|
-
| Keyboard avoidance (mobile) | `useAvoidKeyboard` |
|
|
46
|
-
| Viewport height | `useViewportHeight` |
|
|
47
|
-
| Media query matching | `useViewportMatch` |
|
|
48
|
-
| Scroll position detection | `useCheckScroll` (subpath) |
|
|
49
|
-
| Element visibility detection | `useCheckInvisible` (subpath) |
|
|
50
|
-
| Toggle state | `useToggleState` |
|
|
51
|
-
| Hydration guard | `useHasMounted` |
|
|
52
|
-
| Stable callback reference | `usePreservedCallback` |
|
|
53
|
-
| Deep equality reference | `usePreservedReference` |
|
|
54
|
-
| Ref lifecycle | `useRefEffect` |
|
|
55
|
-
|
|
56
|
-
## Component Selection Guidelines
|
|
57
|
-
|
|
58
|
-
- **SwitchCase** — Prefer over inline &&/ternary for multi-branch rendering. SSR-safe.
|
|
59
|
-
- **ViewportPortal** — Fixed-position overlays, modals, toasts. Client-only.
|
|
60
|
-
- **ScrolltoTop** — Scroll reset on page navigation. Client-only.
|
|
61
|
-
|
|
62
|
-
Never write raw `createContext` + `Provider` + `useContext` boilerplate. Use `buildContext` instead.
|
|
63
|
-
|
|
64
|
-
## DateTime Formatting Guidelines
|
|
65
|
-
|
|
66
|
-
| Context | Use | SSR-safe |
|
|
67
|
-
|---|---|---|
|
|
68
|
-
| Server static date | `formatServerDate` / `formatServerDateTime` | Yes |
|
|
69
|
-
| Client date | `formatClientDate` / `formatClientDateTime` | No |
|
|
70
|
-
| Relative time ("3분 전") | `formatServerRelative` (server) / `formatClientRelative` (client) | Mixed |
|
|
71
|
-
| Remaining time ("2일 3시간 남음") | `formatRemainingText` | Yes |
|
|
72
|
-
| UTC key ("YYYY-MM-DD") | `formatUtcDateKey` / `parseUtcDateInput` | Yes |
|
|
73
|
-
| Raw formatters | `formatLongDate`, `formatDotDate`, `formatKoreanTime` etc. | Yes |
|
|
74
|
-
|
|
75
|
-
**Locale handling:** `DateInput` accepts `string | number | Date`. Use `normalizeAppLocale("ko")` → `"kr"`, `toIntlLocale("kr")` → `"ko-KR"`.
|
|
76
|
-
|
|
77
|
-
## Cookie & Storage API Guidelines
|
|
78
|
-
|
|
79
|
-
| Operation | API | SSR-safe |
|
|
80
|
-
|---|---|---|
|
|
81
|
-
| Read cookie | `getClientCookie(name)` | No |
|
|
82
|
-
| Write cookie | `setClientCookie(name, value, days?)` | No |
|
|
83
|
-
| Delete cookie | `clearClientCookie(name)` / `clearAllClientCookies()` | No |
|
|
84
|
-
| Parse cookie names | `parseClientCookieNames(cookieString)` | **Yes** |
|
|
85
|
-
| localStorage | `getLocalStorage` / `updateLocalStorage` / `removeLocalStorage` | No |
|
|
86
|
-
| sessionStorage | `getSessionStorage` / `updateSessionStorage` / `removeSessionStorage` | No |
|
|
87
|
-
|
|
88
|
-
`clearAllClientCookies` tries all domain/path combinations — use with caution.
|
|
89
|
-
|
|
90
|
-
## Context & Utility Patterns
|
|
91
|
-
|
|
92
|
-
- **`buildContext`:** Creates `createContext` + Provider + `useContext` in one call. `const [useMyCtx, MyProvider] = buildContext<MyType>()`.
|
|
93
|
-
- **`mergeRefs`:** Merges multiple refs into a single callback ref. Use when forwarding refs.
|
|
94
|
-
- **`getDeviceInfo`:** UA-based device detection. Callable at module scope. Returns `isMobile`, `isIOS`, `isAndroid`, etc.
|
|
95
|
-
- **`isEditableKeyboardTarget`:** Checks if a key event target is an editable element. Use before keyboard navigation.
|
|
96
|
-
- **`NavigatorClipboard` / `NavigatorShare`:** Clipboard copy/share with automatic fallback on failure.
|
|
97
|
-
- **`floatingMotion`:** `getFloatingMotionPreset` for tooltip/popover/modal animation configs. 4 modes (anchored, center-selected, modal-center, mobile-sheet).
|
|
98
|
-
- **`seen`:** `hasSeenKey` / `buildSeenValue` for "user has already seen" state management. SSR-safe.
|
|
99
|
-
- **`subscribeKeyboardHeight`:** Low-level keyboard height change detection. Used internally by `useKeyboardHeight`.
|
|
100
|
-
|
|
101
|
-
## Documentation Maintenance
|
|
102
|
-
|
|
103
|
-
- When hooks, components, or utils are added, modified, or removed, update the corresponding `.agents/references/` documentation.
|
|
104
|
-
- When the barrel (`index.ts`) export list changes, update the Barrel Export Mapping section above.
|
|
105
|
-
- Before using any feature, read the corresponding `.agents/references/` doc first.
|
|
106
|
-
|
|
107
|
-
### Reference File Mapping
|
|
108
|
-
|
|
109
|
-
| Category | File(s) |
|
|
110
|
-
|---|---|
|
|
111
|
-
| Components | `.agents/references/components/SwitchCase.md`, `ScrolltoTop.md`, `ViewportPortal.md` |
|
|
112
|
-
| Hooks | `.agents/references/hooks/usePreservedCallback.md`, `usePreservedReference.md`, `useRefEffect.md`, `useDebounce.md`, `useDebouncedCallback.md`, `useInterval.md`, `useTimeout.md`, `useKeyboardListNavigation.md`, `useLongPress.md`, `useDoubleClick.md`, `useIntersectionObserver.md`, `useCheckInvisible.md`, `useCheckScroll.md`, `useViewportHeight.md`, `useViewportMatch.md`, `useAvoidKeyboard.md`, `useToggleState.md`, `useHasMounted.md`, `useGeolocation.md`, `useKeyboardHeight.md`, `useClientDateTime.md`, `useRelativeDateTime.md` |
|
|
113
|
-
| Cookie | `.agents/references/cookie/cookie.md` |
|
|
114
|
-
| DateTime | `.agents/references/datetime/dateTime.shared.md`, `dateTime.client.md`, `dateTime.server.md` |
|
|
115
|
-
| Utils | `.agents/references/utils/buildContext.md`, `mergeRefs.md`, `floatingMotion.md`, `browserStorage.md`, `getDeviceInfo.md`, `clipboardShare.md`, `isEditableKeyboardTarget.md`, `seen.md`, `subscribeKeyboardHeight.md` |
|
|
116
|
-
|
|
117
|
-
### usePreservedCallback Trade-off
|
|
118
|
-
|
|
119
|
-
`usePreservedCallback` uses `useRef` + `useEffect` to keep a stable callback reference that always calls the latest version. Because the ref is synced via `useEffect` (after paint), the returned callback may hold a **stale value** during the first render or in the gap between a re-render and the effect flush. In practice this is never an issue — the callback is only invoked in event handlers that fire post-paint. This is the same pattern used by react-hook-form, Radix UI, and others. If synchronous invocation during render is required, use a regular `useCallback` with inline deps instead.
|
package/CODE_OF_CONDUCT.md
DELETED
|
@@ -1,55 +0,0 @@
|
|
|
1
|
-
# Contributor Covenant Code of Conduct
|
|
2
|
-
|
|
3
|
-
## Our Pledge
|
|
4
|
-
|
|
5
|
-
We as members, contributors, and leaders pledge to make participation in our
|
|
6
|
-
community a harassment-free experience for everyone, regardless of age, body
|
|
7
|
-
size, visible or invisible disability, ethnicity, sex characteristics, gender
|
|
8
|
-
identity and expression, level of experience, education, socio-economic status,
|
|
9
|
-
nationality, personal appearance, race, religion, or sexual identity
|
|
10
|
-
and orientation.
|
|
11
|
-
|
|
12
|
-
We pledge to act and interact in ways that contribute to an open, welcoming,
|
|
13
|
-
diverse, inclusive, and healthy community.
|
|
14
|
-
|
|
15
|
-
## Our Standards
|
|
16
|
-
|
|
17
|
-
Examples of behavior that contributes to a positive environment:
|
|
18
|
-
|
|
19
|
-
- Demonstrating empathy and kindness toward other people
|
|
20
|
-
- Being respectful of differing opinions, viewpoints, and experiences
|
|
21
|
-
- Giving and gracefully accepting constructive feedback
|
|
22
|
-
- Accepting responsibility and apologizing to those affected by our mistakes
|
|
23
|
-
- Focusing on what is best not just for us as individuals, but for the overall community
|
|
24
|
-
|
|
25
|
-
Examples of unacceptable behavior:
|
|
26
|
-
|
|
27
|
-
- The use of sexualized language or imagery, and sexual attention or advances
|
|
28
|
-
- Trolling, insulting or derogatory comments, and personal or political attacks
|
|
29
|
-
- Public or private harassment
|
|
30
|
-
- Publishing others' private information without explicit permission
|
|
31
|
-
- Other conduct which could reasonably be considered inappropriate in a professional setting
|
|
32
|
-
|
|
33
|
-
## Enforcement Responsibilities
|
|
34
|
-
|
|
35
|
-
Project maintainers are responsible for clarifying and enforcing our standards of
|
|
36
|
-
acceptable behavior and will take appropriate and fair corrective action in
|
|
37
|
-
response to any behavior that they deem inappropriate, threatening, offensive,
|
|
38
|
-
or harmful.
|
|
39
|
-
|
|
40
|
-
## Scope
|
|
41
|
-
|
|
42
|
-
This Code of Conduct applies within all community spaces, and also applies when
|
|
43
|
-
an individual is officially representing the community in public spaces.
|
|
44
|
-
|
|
45
|
-
## Enforcement
|
|
46
|
-
|
|
47
|
-
Instances of abusive, harassing, or otherwise unacceptable behavior may be
|
|
48
|
-
reported to the project maintainers. All complaints will be reviewed and
|
|
49
|
-
investigated promptly and fairly.
|
|
50
|
-
|
|
51
|
-
## Attribution
|
|
52
|
-
|
|
53
|
-
This Code of Conduct is adapted from the [Contributor Covenant](https://www.contributor-covenant.org),
|
|
54
|
-
version 2.1, available at
|
|
55
|
-
https://www.contributor-covenant.org/version/2/1/code_of_conduct.html.
|
package/CONTRIBUTING.md
DELETED
|
@@ -1,39 +0,0 @@
|
|
|
1
|
-
# Contributing to @musecat/functionkit
|
|
2
|
-
|
|
3
|
-
Thank you for your interest in contributing! We welcome bug reports, feature suggestions, and pull requests.
|
|
4
|
-
|
|
5
|
-
## Development Setup
|
|
6
|
-
|
|
7
|
-
```bash
|
|
8
|
-
git clone https://github.com/TheTechclip/FunctionKit.git
|
|
9
|
-
cd FunctionKit
|
|
10
|
-
npm install
|
|
11
|
-
```
|
|
12
|
-
|
|
13
|
-
## Available Scripts
|
|
14
|
-
|
|
15
|
-
| Command | Description |
|
|
16
|
-
|---|---|
|
|
17
|
-
| `npm test` | Run all tests with Vitest |
|
|
18
|
-
| `npm run test:coverage` | Run tests with coverage report |
|
|
19
|
-
| `npm run lint` | Lint all files with Biome |
|
|
20
|
-
| `npm run format` | Format all files with Prettier + Biome |
|
|
21
|
-
|
|
22
|
-
## Code Standards
|
|
23
|
-
|
|
24
|
-
- TypeScript strict mode is enabled — ensure your code compiles without errors.
|
|
25
|
-
- All new features must include corresponding tests.
|
|
26
|
-
- Hooks must start with `"use client"` directive. Pure utility functions must NOT.
|
|
27
|
-
- Barrel exports (`index.ts`) must be updated for new public APIs.
|
|
28
|
-
- Follow the existing code style (tabs, double quotes, semicolons).
|
|
29
|
-
|
|
30
|
-
## Pull Request Process
|
|
31
|
-
|
|
32
|
-
1. Create a feature branch from `master`.
|
|
33
|
-
2. Write tests for your changes.
|
|
34
|
-
3. Ensure `npm run lint && npm test` passes.
|
|
35
|
-
4. Open a pull request describing the change and its motivation.
|
|
36
|
-
|
|
37
|
-
## Questions?
|
|
38
|
-
|
|
39
|
-
Feel free to open a discussion or issue for any questions.
|
package/biome.json
DELETED
|
@@ -1,25 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"$schema": "https://biomejs.dev/schemas/2.5.4/schema.json",
|
|
3
|
-
"assist": { "actions": { "source": { "organizeImports": "on" } } },
|
|
4
|
-
"linter": {
|
|
5
|
-
"enabled": true,
|
|
6
|
-
"rules": {
|
|
7
|
-
"preset": "recommended"
|
|
8
|
-
}
|
|
9
|
-
},
|
|
10
|
-
"formatter": {
|
|
11
|
-
"enabled": true,
|
|
12
|
-
"indentStyle": "tab",
|
|
13
|
-
"indentWidth": 2,
|
|
14
|
-
"lineWidth": 100
|
|
15
|
-
},
|
|
16
|
-
"javascript": {
|
|
17
|
-
"formatter": {
|
|
18
|
-
"quoteStyle": "double",
|
|
19
|
-
"semicolons": "always"
|
|
20
|
-
}
|
|
21
|
-
},
|
|
22
|
-
"files": {
|
|
23
|
-
"includes": ["**", "!**/dist", "!**/coverage", "!**/node_modules"]
|
|
24
|
-
}
|
|
25
|
-
}
|
|
@@ -1,15 +0,0 @@
|
|
|
1
|
-
import { render } from "@testing-library/react";
|
|
2
|
-
import { describe, expect, test, vi } from "vitest";
|
|
3
|
-
import { ScrolltoTop } from "@/packages/components/ScrolltoTop";
|
|
4
|
-
|
|
5
|
-
describe("ScrolltoTop", () => {
|
|
6
|
-
test("calls window.scrollTo(0, 0) on mount", () => {
|
|
7
|
-
const originalScrollTo = window.scrollTo;
|
|
8
|
-
window.scrollTo = vi.fn();
|
|
9
|
-
|
|
10
|
-
render(<ScrolltoTop />);
|
|
11
|
-
expect(window.scrollTo).toHaveBeenCalledWith(0, 0);
|
|
12
|
-
|
|
13
|
-
window.scrollTo = originalScrollTo;
|
|
14
|
-
});
|
|
15
|
-
});
|
|
@@ -1,33 +0,0 @@
|
|
|
1
|
-
import { render } from "@testing-library/react";
|
|
2
|
-
import { describe, expect, test } from "vitest";
|
|
3
|
-
import { SwitchCase } from "@/packages/components/SwitchCase";
|
|
4
|
-
|
|
5
|
-
describe("SwitchCase", () => {
|
|
6
|
-
const cases = {
|
|
7
|
-
1: <div>Case 1</div>,
|
|
8
|
-
2: <div>Case 2</div>,
|
|
9
|
-
three: <div>Case Three</div>,
|
|
10
|
-
};
|
|
11
|
-
|
|
12
|
-
test("renders correctly matching case", () => {
|
|
13
|
-
const { container } = render(<SwitchCase value={1} cases={cases} />);
|
|
14
|
-
expect(container.textContent).toBe("Case 1");
|
|
15
|
-
});
|
|
16
|
-
|
|
17
|
-
test("renders another case", () => {
|
|
18
|
-
const { container } = render(<SwitchCase value="three" cases={cases} />);
|
|
19
|
-
expect(container.textContent).toBe("Case Three");
|
|
20
|
-
});
|
|
21
|
-
|
|
22
|
-
test("renders otherwise when case does not exist", () => {
|
|
23
|
-
const { container } = render(
|
|
24
|
-
<SwitchCase value={99} cases={cases} otherwise={<div>Fallback</div>} />,
|
|
25
|
-
);
|
|
26
|
-
expect(container.textContent).toBe("Fallback");
|
|
27
|
-
});
|
|
28
|
-
|
|
29
|
-
test("renders null when case does not exist and otherwise is not provided", () => {
|
|
30
|
-
const { container } = render(<SwitchCase value={99} cases={cases} />);
|
|
31
|
-
expect(container.innerHTML).toBe("");
|
|
32
|
-
});
|
|
33
|
-
});
|
|
@@ -1,46 +0,0 @@
|
|
|
1
|
-
import { render, screen } from "@testing-library/react";
|
|
2
|
-
import { beforeEach, describe, expect, test } from "vitest";
|
|
3
|
-
import { getViewportPortalRoot, ViewportPortal } from "@/packages/components/ViewportPortal";
|
|
4
|
-
|
|
5
|
-
describe("ViewportPortal", () => {
|
|
6
|
-
beforeEach(() => {
|
|
7
|
-
document.body.innerHTML = "";
|
|
8
|
-
});
|
|
9
|
-
|
|
10
|
-
test("getViewportPortalRoot creates a root element if it doesn't exist", () => {
|
|
11
|
-
const root = getViewportPortalRoot();
|
|
12
|
-
expect(root).not.toBeNull();
|
|
13
|
-
expect(root?.id).toBe("viewport-portal-root");
|
|
14
|
-
expect(document.getElementById("viewport-portal-root")).toBe(root);
|
|
15
|
-
});
|
|
16
|
-
|
|
17
|
-
test("getViewportPortalRoot returns existing root element if it exists", () => {
|
|
18
|
-
const root1 = getViewportPortalRoot();
|
|
19
|
-
const root2 = getViewportPortalRoot();
|
|
20
|
-
expect(root1).toBe(root2);
|
|
21
|
-
expect(document.querySelectorAll("#viewport-portal-root").length).toBe(1);
|
|
22
|
-
});
|
|
23
|
-
|
|
24
|
-
test("getViewportPortalRoot returns null when document is undefined (SSR)", () => {
|
|
25
|
-
const origDocument = global.document;
|
|
26
|
-
// @ts-expect-error
|
|
27
|
-
delete global.document;
|
|
28
|
-
expect(getViewportPortalRoot()).toBeNull();
|
|
29
|
-
global.document = origDocument;
|
|
30
|
-
});
|
|
31
|
-
|
|
32
|
-
test("ViewportPortal renders children into the portal root", () => {
|
|
33
|
-
render(
|
|
34
|
-
<ViewportPortal>
|
|
35
|
-
<div data-testid="portal-content">Portal Content</div>
|
|
36
|
-
</ViewportPortal>,
|
|
37
|
-
);
|
|
38
|
-
|
|
39
|
-
const root = document.getElementById("viewport-portal-root");
|
|
40
|
-
expect(root).not.toBeNull();
|
|
41
|
-
|
|
42
|
-
const content = screen.getByTestId("portal-content");
|
|
43
|
-
expect(content.textContent).toBe("Portal Content");
|
|
44
|
-
expect(root?.contains(content)).toBe(true);
|
|
45
|
-
});
|
|
46
|
-
});
|