@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.
Files changed (91) hide show
  1. package/package.json +17 -1
  2. package/packages/cookie/cookie.shared.ts +10 -2
  3. package/packages/utils/floatingMotion.ts +1 -3
  4. package/.agents/references/components/ScrolltoTop.md +0 -36
  5. package/.agents/references/components/SwitchCase.md +0 -47
  6. package/.agents/references/components/ViewportPortal.md +0 -34
  7. package/.agents/references/cookie/cookie.md +0 -47
  8. package/.agents/references/datetime/dateTime.client.md +0 -42
  9. package/.agents/references/datetime/dateTime.server.md +0 -42
  10. package/.agents/references/datetime/dateTime.shared.md +0 -86
  11. package/.agents/references/hooks/useAvoidKeyboard.md +0 -29
  12. package/.agents/references/hooks/useCheckInvisible.md +0 -36
  13. package/.agents/references/hooks/useCheckScroll.md +0 -36
  14. package/.agents/references/hooks/useClientDateTime.md +0 -39
  15. package/.agents/references/hooks/useDebounce.md +0 -46
  16. package/.agents/references/hooks/useDebouncedCallback.md +0 -34
  17. package/.agents/references/hooks/useDoubleClick.md +0 -40
  18. package/.agents/references/hooks/useGeolocation.md +0 -37
  19. package/.agents/references/hooks/useHasMounted.md +0 -27
  20. package/.agents/references/hooks/useIntersectionObserver.md +0 -41
  21. package/.agents/references/hooks/useInterval.md +0 -44
  22. package/.agents/references/hooks/useKeyboardHeight.md +0 -33
  23. package/.agents/references/hooks/useKeyboardListNavigation.md +0 -58
  24. package/.agents/references/hooks/useLongPress.md +0 -48
  25. package/.agents/references/hooks/usePreservedCallback.md +0 -37
  26. package/.agents/references/hooks/usePreservedReference.md +0 -34
  27. package/.agents/references/hooks/useRefEffect.md +0 -35
  28. package/.agents/references/hooks/useRelativeDateTime.md +0 -35
  29. package/.agents/references/hooks/useTimeout.md +0 -42
  30. package/.agents/references/hooks/useToggleState.md +0 -39
  31. package/.agents/references/hooks/useViewportHeight.md +0 -26
  32. package/.agents/references/hooks/useViewportMatch.md +0 -32
  33. package/.agents/references/utils/browserStorage.md +0 -41
  34. package/.agents/references/utils/buildContext.md +0 -47
  35. package/.agents/references/utils/clipboardShare.md +0 -59
  36. package/.agents/references/utils/floatingMotion.md +0 -67
  37. package/.agents/references/utils/getDeviceInfo.md +0 -43
  38. package/.agents/references/utils/isEditableKeyboardTarget.md +0 -35
  39. package/.agents/references/utils/mergeRefs.md +0 -29
  40. package/.agents/references/utils/seen.md +0 -37
  41. package/.agents/references/utils/subscribeKeyboardHeight.md +0 -35
  42. package/.gitattributes +0 -2
  43. package/.github/ISSUE_TEMPLATE/bug_report.md +0 -31
  44. package/.github/ISSUE_TEMPLATE/feature_request.md +0 -22
  45. package/.github/PULL_REQUEST_TEMPLATE.md +0 -22
  46. package/.github/workflows/ci.yml +0 -20
  47. package/.local/state/gh/device-id +0 -1
  48. package/.prettierrc +0 -8
  49. package/AGENTS.md +0 -119
  50. package/CODE_OF_CONDUCT.md +0 -55
  51. package/CONTRIBUTING.md +0 -39
  52. package/biome.json +0 -25
  53. package/tests/components/ScrolltoTop.test.tsx +0 -15
  54. package/tests/components/SwitchCase.test.tsx +0 -33
  55. package/tests/components/ViewportPortal.test.tsx +0 -46
  56. package/tests/cookie/cookie.test.ts +0 -192
  57. package/tests/datetime/datetime.test.ts +0 -461
  58. package/tests/hooks/useAvoidKeyboard.test.ts +0 -43
  59. package/tests/hooks/useCheckInvisible.test.ts +0 -59
  60. package/tests/hooks/useCheckScroll.test.ts +0 -23
  61. package/tests/hooks/useClientDateTime.test.ts +0 -12
  62. package/tests/hooks/useDebounce.test.ts +0 -96
  63. package/tests/hooks/useDebouncedCallback.test.ts +0 -63
  64. package/tests/hooks/useDoubleClick.test.ts +0 -82
  65. package/tests/hooks/useGeolocation.test.ts +0 -201
  66. package/tests/hooks/useHasMounted.test.ts +0 -10
  67. package/tests/hooks/useIntersectionObserver.test.ts +0 -78
  68. package/tests/hooks/useInterval.test.ts +0 -56
  69. package/tests/hooks/useKeyboardHeight.test.ts +0 -10
  70. package/tests/hooks/useKeyboardListNavigation.test.ts +0 -404
  71. package/tests/hooks/useLongPress.test.ts +0 -214
  72. package/tests/hooks/usePreservedCallback.test.ts +0 -33
  73. package/tests/hooks/usePreservedReference.test.ts +0 -42
  74. package/tests/hooks/useRefEffect.test.ts +0 -67
  75. package/tests/hooks/useRelativeDateTime.test.ts +0 -21
  76. package/tests/hooks/useTimeout.test.ts +0 -72
  77. package/tests/hooks/useToggleState.test.ts +0 -43
  78. package/tests/hooks/useViewportHeight.test.ts +0 -32
  79. package/tests/hooks/useViewportMatch.test.ts +0 -35
  80. package/tests/setup.ts +0 -9
  81. package/tests/utils/browserStorage.test.ts +0 -114
  82. package/tests/utils/buildContext.test.tsx +0 -26
  83. package/tests/utils/checkDevice.test.ts +0 -140
  84. package/tests/utils/clipboardShare.test.ts +0 -152
  85. package/tests/utils/floatingMotion.test.ts +0 -135
  86. package/tests/utils/keyboardTarget.test.ts +0 -85
  87. package/tests/utils/mergeRefs.test.ts +0 -50
  88. package/tests/utils/seen.test.ts +0 -40
  89. package/tests/utils/subscribeKeyboardHeight.test.ts +0 -172
  90. package/tsconfig.json +0 -18
  91. package/vitest.config.ts +0 -22
@@ -1,47 +0,0 @@
1
- # buildContext
2
-
3
- ## Purpose
4
-
5
- Eliminates `createContext` + `Provider` + `useContext` boilerplate. Creates a type-safe context pair in a single call: a typed accessor hook and a Provider component with automatic value memoization.
6
-
7
- ## Usage Logic
8
-
9
- Creates a React context with the generic type `T`. Returns a tuple `[useCtx, Provider]`. The Provider wraps its `value` in `useMemo` to prevent unnecessary re-renders. If `displayName` is provided, it's assigned to the context for DevTools.
10
-
11
- ## Type Signature
12
-
13
- ```ts
14
- function buildContext<T>(
15
- displayName?: string
16
- ): [
17
- useCtx: () => T,
18
- Provider: React.FC<{ value: T; children: ReactNode }>
19
- ];
20
- ```
21
-
22
- ## Example Code
23
-
24
- ```tsx
25
- import { buildContext } from "@musecat/functionkit";
26
-
27
- interface Theme {
28
- color: string;
29
- bg: string;
30
- }
31
-
32
- const [useTheme, ThemeProvider] = buildContext<Theme>("ThemeContext");
33
-
34
- // Usage
35
- function App() {
36
- return (
37
- <ThemeProvider value={{ color: "#000", bg: "#fff" }}>
38
- <ThemedButton />
39
- </ThemeProvider>
40
- );
41
- }
42
-
43
- function ThemedButton() {
44
- const theme = useTheme();
45
- return <button style={{ color: theme.color, background: theme.bg }}>Click</button>;
46
- }
47
- ```
@@ -1,59 +0,0 @@
1
- # NavigatorClipboard / NavigatorShare (clipboardShare)
2
-
3
- ## Purpose
4
-
5
- Provides easy-to-use React components for clipboard copy and native share. Handles error cases with automatic fallback (e.g., if `navigator.share` fails, falls back to clipboard).
6
-
7
- ## Usage Logic
8
-
9
- **NavigatorClipboard**: On click, calls `navigator.clipboard.writeText(text)`. Calls `onComplete` with the result.
10
-
11
- **NavigatorShare**: On click, calls `navigator.share({ title, text, url })`. If share fails (or is unavailable), falls back to clipboard copy and calls `onComplete` with fallback info.
12
-
13
- ## Type Signatures
14
-
15
- ```ts
16
- interface NavigatorClipboardProps {
17
- text: string;
18
- onComplete?: (result: NavigatorClipboardResult) => void;
19
- children?: ReactNode;
20
- }
21
-
22
- interface NavigatorShareProps {
23
- title?: string;
24
- text?: string;
25
- url?: string;
26
- onComplete?: (result: NavigatorShareResult) => void;
27
- children?: ReactNode;
28
- }
29
-
30
- interface NavigatorClipboardResult {
31
- copied: boolean;
32
- error?: Error;
33
- }
34
-
35
- interface NavigatorShareResult {
36
- shared: boolean;
37
- copied: boolean;
38
- error?: Error;
39
- }
40
- ```
41
-
42
- ## Example Code
43
-
44
- ```tsx
45
- import { NavigatorClipboard, NavigatorShare } from "@musecat/functionkit";
46
-
47
- function ShareButton({ url }: { url: string }) {
48
- return (
49
- <div>
50
- <NavigatorClipboard text={url}>
51
- <button>Copy Link</button>
52
- </NavigatorClipboard>
53
- <NavigatorShare title="Check this out" url={url}>
54
- <button>Share</button>
55
- </NavigatorShare>
56
- </div>
57
- );
58
- }
59
- ```
@@ -1,67 +0,0 @@
1
- # floatingMotion
2
-
3
- ## Purpose
4
-
5
- Provides animation presets and transform utilities for floating UI elements: tooltips, popovers, modals, and mobile sheets. Calculates `initial`, `animate`, and `exit` states for Framer Motion.
6
-
7
- ## Usage Logic
8
-
9
- **`getFloatingMotionPreset(mode, placement?)`**: Returns a complete Framer Motion preset object based on the mode and optional placement.
10
-
11
- **`getFloatingTransformOrigin(placement)`**: Returns the correct `transformOrigin` string for a given placement.
12
- **`getFloatingHiddenTransform(placement)`**: Returns the hidden-state transform offset for a given placement.
13
-
14
- ### Modes
15
- | Mode | Description | Animation |
16
- |------|-------------|-----------|
17
- | `anchored` | Tooltips, popovers | Scale + fade from placement origin |
18
- | `center-selected` | Center-positioned selection | Scale in place |
19
- | `modal-center` | Modal windows | Scale + fade in center |
20
- | `mobile-sheet` | Bottom sheets | Slide up from bottom |
21
-
22
- ## Type Signatures
23
-
24
- ```ts
25
- type FloatingMotionMode = "anchored" | "center-selected" | "modal-center" | "mobile-sheet";
26
- type FloatingPlacement = "top" | "bottom" | "left" | "right" | "top-start" | "top-end" | ...;
27
- type FloatingMotionPreset = {
28
- initial: Variants;
29
- animate: Variants;
30
- exit: Variants;
31
- transition: Transition;
32
- };
33
-
34
- function getFloatingMotionPreset(
35
- mode: FloatingMotionMode,
36
- placement?: FloatingPlacement
37
- ): FloatingMotionPreset;
38
-
39
- function getFloatingTransformOrigin(placement: FloatingPlacement): string;
40
- function getFloatingHiddenTransform(placement: FloatingPlacement): { x?: number; y?: number };
41
- ```
42
-
43
- ## Example Code
44
-
45
- ```tsx
46
- import { getFloatingMotionPreset } from "@musecat/functionkit";
47
- import { motion, AnimatePresence } from "framer-motion";
48
-
49
- function Tooltip({ open, children }: { open: boolean; children: ReactNode }) {
50
- const preset = getFloatingMotionPreset("anchored", "top");
51
-
52
- return (
53
- <AnimatePresence>
54
- {open && (
55
- <motion.div
56
- variants={preset}
57
- initial="initial"
58
- animate="animate"
59
- exit="exit"
60
- >
61
- {children}
62
- </motion.div>
63
- )}
64
- </AnimatePresence>
65
- );
66
- }
67
- ```
@@ -1,43 +0,0 @@
1
- # getDeviceInfo (checkDevice)
2
-
3
- ## Purpose
4
-
5
- Parses the User-Agent string to detect the user's device type, operating system, and browser. Callable at module scope — no `window` required.
6
-
7
- ## Usage Logic
8
-
9
- Analyzes `navigator.userAgent` with a series of regex checks. Returns a `DeviceInfo` object with boolean flags. SSR-safe — can be called in Server Components, RSC, or module initialization.
10
-
11
- ## Type Signatures
12
-
13
- ```ts
14
- interface DeviceInfo {
15
- isMobile: boolean;
16
- isIOS: boolean;
17
- isAndroid: boolean;
18
- isSafari: boolean;
19
- isIOSSafari: boolean;
20
- isMacSafari: boolean;
21
- isSamsungBrowser: boolean;
22
- isTouchDevice: boolean;
23
- browser: string | null;
24
- }
25
-
26
- function getDeviceInfo(): DeviceInfo;
27
- ```
28
-
29
- ## Example Code
30
-
31
- ```tsx
32
- import { getDeviceInfo } from "@musecat/functionkit";
33
-
34
- const { isMobile, isIOS, isIOSSafari } = getDeviceInfo();
35
-
36
- // Call at module scope
37
- const platform = isIOS ? "ios" : isAndroid ? "android" : "web";
38
-
39
- export function detectBrowser() {
40
- const { isSafari, browser } = getDeviceInfo();
41
- return { isSafari, browser };
42
- }
43
- ```
@@ -1,35 +0,0 @@
1
- # isEditableKeyboardTarget (keyboardTarget)
2
-
3
- ## Purpose
4
-
5
- Checks if a keyboard event target is an editable element (`input`, `textarea`, `select`, or `contentEditable`). Used to prevent keyboard navigation hooks from interfering with native input editing.
6
-
7
- ## Usage Logic
8
-
9
- Examines the target element's tag name and `isContentEditable` property. Excludes buttons, checkboxes, and radio inputs from the editable set.
10
-
11
- ## Type Signature
12
-
13
- ```ts
14
- function isEditableKeyboardTarget(target: EventTarget | null): boolean;
15
- ```
16
-
17
- ## Example Code
18
-
19
- ```tsx
20
- import { isEditableKeyboardTarget, useKeyboardListNavigation } from "@musecat/functionkit";
21
-
22
- function SearchableList({ items }: { items: string[] }) {
23
- const { handleListKeyDown } = useKeyboardListNavigation({
24
- itemCount: items.length,
25
- onSelect: (i) => console.log(items[i]),
26
- });
27
-
28
- const onKeyDown = (e: React.KeyboardEvent) => {
29
- if (isEditableKeyboardTarget(e.target)) return; // Don't steal from inputs
30
- handleListKeyDown(e);
31
- };
32
-
33
- return <ul onKeyDown={onKeyDown}>...</ul>;
34
- }
35
- ```
@@ -1,29 +0,0 @@
1
- # mergeRefs
2
-
3
- ## Purpose
4
-
5
- Combines multiple React refs (callback refs and/or object refs) into a single callback ref. Useful when forwarding refs from parent components while keeping internal refs.
6
-
7
- ## Usage Logic
8
-
9
- Returns a callback ref that, when called with a DOM node, sets `ref.current` on all object refs and invokes all callback refs with the node. Filters out `undefined` refs.
10
-
11
- ## Type Signature
12
-
13
- ```ts
14
- function mergeRefs<T>(
15
- ...refs: (React.Ref<T> | undefined)[]
16
- ): (node: T) => void;
17
- ```
18
-
19
- ## Example Code
20
-
21
- ```tsx
22
- import { mergeRefs } from "@musecat/functionkit";
23
- import { forwardRef, useRef } from "react";
24
-
25
- const Input = forwardRef<HTMLInputElement, Props>((props, ref) => {
26
- const internalRef = useRef<HTMLInputElement>(null);
27
- return <input ref={mergeRefs(ref, internalRef)} {...props} />;
28
- });
29
- ```
@@ -1,37 +0,0 @@
1
- # seen
2
-
3
- ## Purpose
4
-
5
- Manages a "user has already seen this" state as a JSON string. Useful for marking items as read, dismissing onboarding prompts, or tracking feature discovery without server state.
6
-
7
- ## Usage Logic
8
-
9
- Stores an array of string keys serialized as JSON. `parseSeen` deserializes, `hasSeenKey` checks a specific key, and `buildSeenValue` appends a new key and returns the updated JSON string. SSR-safe — pure functions with no side effects.
10
-
11
- ## Type Signatures
12
-
13
- ```ts
14
- const SEEN_STORAGE_KEY = "seen";
15
-
16
- function parseSeen(seen: string | null): string[];
17
- function hasSeenKey(seen: string | null, key: string): boolean;
18
- function buildSeenValue(prev: string | null, key: string): string;
19
- ```
20
-
21
- ## Example Code
22
-
23
- ```tsx
24
- import { hasSeenKey, buildSeenValue, SEEN_STORAGE_KEY } from "@musecat/functionkit";
25
- import { getLocalStorage, updateLocalStorage } from "@musecat/functionkit";
26
-
27
- function useOnboarding() {
28
- const stored = getLocalStorage(SEEN_STORAGE_KEY);
29
- const hasSeenWalkthrough = hasSeenKey(stored, "walkthrough-v1");
30
-
31
- const dismissWalkthrough = () => {
32
- updateLocalStorage(SEEN_STORAGE_KEY, buildSeenValue(stored, "walkthrough-v1"));
33
- };
34
-
35
- return { hasSeenWalkthrough, dismissWalkthrough };
36
- }
37
- ```
@@ -1,35 +0,0 @@
1
- # subscribeKeyboardHeight
2
-
3
- ## Purpose
4
-
5
- A low-level utility that subscribes to `visualViewport` resize/scroll events and reports the estimated keyboard height. Used internally by `useKeyboardHeight`.
6
-
7
- ## Usage Logic
8
-
9
- Attaches `resize` and `scroll` event listeners to `window.visualViewport`. Computes keyboard height as `window.innerHeight - visualViewport.height`. Throttles callbacks at the specified interval (default 16ms). Returns an unsubscribe function.
10
-
11
- ## Type Signature
12
-
13
- ```ts
14
- function subscribeKeyboardHeight(
15
- handler: (height: number) => void,
16
- throttleMs?: number
17
- ): { unsubscribe: () => void };
18
- ```
19
-
20
- ## Example Code
21
-
22
- ```tsx
23
- import { subscribeKeyboardHeight } from "@musecat/functionkit";
24
- import { useEffect } from "react";
25
-
26
- function useCustomKeyboardHandler() {
27
- useEffect(() => {
28
- const { unsubscribe } = subscribeKeyboardHeight(
29
- (height) => console.log("Keyboard height:", height),
30
- 100
31
- );
32
- return unsubscribe;
33
- }, []);
34
- }
35
- ```
package/.gitattributes DELETED
@@ -1,2 +0,0 @@
1
- # Auto detect text files and perform LF normalization
2
- * text=auto
@@ -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
@@ -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
@@ -1,8 +0,0 @@
1
- {
2
- "useTabs": true,
3
- "tabWidth": 2,
4
- "printWidth": 100,
5
- "singleQuote": false,
6
- "semi": true,
7
- "trailingComma": "all"
8
- }
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.
@@ -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.