@1money/component-ui 0.0.23 → 0.0.25

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 (107) hide show
  1. package/es/components/Table/interface.d.ts +2 -1
  2. package/es/components/Table/renderers/EmptyState.d.ts +2 -1
  3. package/es/components/Table/renderers/EmptyState.js +15 -8
  4. package/es/components/Table/style/Table.css +1 -1
  5. package/es/index.css +1 -1
  6. package/es/stories/docs/ComponentDocsPage.js +234 -0
  7. package/es/stories/docs/componentDocMeta.js +97 -0
  8. package/es/stories/docs/storybook-docs.css +79 -0
  9. package/lib/components/Table/interface.d.ts +2 -1
  10. package/lib/components/Table/renderers/EmptyState.d.ts +2 -1
  11. package/lib/components/Table/renderers/EmptyState.js +15 -7
  12. package/lib/components/Table/style/Table.css +1 -1
  13. package/lib/index.css +1 -1
  14. package/lib/stories/docs/ComponentDocsPage.js +244 -0
  15. package/lib/stories/docs/componentDocMeta.js +104 -0
  16. package/lib/stories/docs/storybook-docs.css +79 -0
  17. package/package.json +23 -8
  18. package/scripts/mcp-server/README.md +267 -0
  19. package/scripts/mcp-server/bin.mjs +2 -0
  20. package/scripts/mcp-server/drift.json +5 -0
  21. package/scripts/mcp-server/examples.generated.json +2651 -0
  22. package/scripts/mcp-server/index.generated.json +18098 -0
  23. package/scripts/mcp-server/index.mjs +308 -26
  24. package/scripts/mcp-server/tools/get-examples.mjs +125 -0
  25. package/scripts/mcp-server/tools/get-library-info.mjs +25 -0
  26. package/scripts/mcp-server/tools/get-symbol.mjs +232 -0
  27. package/scripts/mcp-server/tools/get-token.mjs +60 -0
  28. package/scripts/mcp-server/tools/list-icons.mjs +38 -0
  29. package/scripts/mcp-server/tools/list-symbols.mjs +46 -0
  30. package/scripts/mcp-server/tools/resolve-import.mjs +125 -0
  31. package/scripts/mcp-server/tools/search-symbols.mjs +79 -0
  32. package/.agents/skills/1money-component-dev/SKILL.md +0 -224
  33. package/.agents/skills/1money-component-dev/checklist.md +0 -159
  34. package/.agents/skills/1money-component-dev/references/ComponentPatterns.md +0 -478
  35. package/.agents/skills/1money-component-dev/references/FigmaExtractionChecklist.md +0 -144
  36. package/.agents/skills/1money-component-dev/references/HooksGuide.md +0 -360
  37. package/.agents/skills/1money-component-dev/references/SemanticColors.md +0 -215
  38. package/.agents/skills/1money-component-dev/references/StyleSystemAPI.md +0 -389
  39. package/.claude/settings.local.json +0 -120
  40. package/.claude/skills/1money-component-dev/SKILL.md +0 -229
  41. package/.claude/skills/1money-component-dev/checklist.md +0 -159
  42. package/.claude/skills/1money-component-dev/references/ComponentPatterns.md +0 -478
  43. package/.claude/skills/1money-component-dev/references/FigmaExtractionChecklist.md +0 -144
  44. package/.claude/skills/1money-component-dev/references/HooksGuide.md +0 -360
  45. package/.claude/skills/1money-component-dev/references/SemanticColors.md +0 -215
  46. package/.claude/skills/1money-component-dev/references/StyleSystemAPI.md +0 -389
  47. package/.claude/skills/1money-component-review/SKILL.md +0 -316
  48. package/.claude/skills/component-pipeline/SKILL.md +0 -116
  49. package/.claude/skills/component-pipeline/checklist.md +0 -125
  50. package/.hintrc +0 -13
  51. package/@types/global.d.ts +0 -28
  52. package/AGENTS.md +0 -546
  53. package/CLAUDE.md +0 -1
  54. package/jest.setup.d.ts +0 -1
  55. package/jest.setup.ts +0 -1
  56. package/patches/primereact.patch +0 -323
  57. package/patches/react-pro-sidebar.patch +0 -6421
  58. package/public/favicon.ico +0 -0
  59. package/public/fonts/Aeonik/Aeonik-Air.ttf +0 -0
  60. package/public/fonts/Aeonik/Aeonik-AirItalic.ttf +0 -0
  61. package/public/fonts/Aeonik/Aeonik-Black.ttf +0 -0
  62. package/public/fonts/Aeonik/Aeonik-BlackItalic.ttf +0 -0
  63. package/public/fonts/Aeonik/Aeonik-Bold.ttf +0 -0
  64. package/public/fonts/Aeonik/Aeonik-BoldItalic.ttf +0 -0
  65. package/public/fonts/Aeonik/Aeonik-Light.ttf +0 -0
  66. package/public/fonts/Aeonik/Aeonik-LightItalic.ttf +0 -0
  67. package/public/fonts/Aeonik/Aeonik-Medium.ttf +0 -0
  68. package/public/fonts/Aeonik/Aeonik-MediumItalic.ttf +0 -0
  69. package/public/fonts/Aeonik/Aeonik-Regular.ttf +0 -0
  70. package/public/fonts/Aeonik/Aeonik-RegularItalic.ttf +0 -0
  71. package/public/fonts/Aeonik/Aeonik-Thin.ttf +0 -0
  72. package/public/fonts/Aeonik/Aeonik-ThinItalic.ttf +0 -0
  73. package/public/fonts/Inter/Inter-Black.ttf +0 -0
  74. package/public/fonts/Inter/Inter-BlackItalic.ttf +0 -0
  75. package/public/fonts/Inter/Inter-Bold.ttf +0 -0
  76. package/public/fonts/Inter/Inter-BoldItalic.ttf +0 -0
  77. package/public/fonts/Inter/Inter-ExtraBold.ttf +0 -0
  78. package/public/fonts/Inter/Inter-ExtraBoldItalic.ttf +0 -0
  79. package/public/fonts/Inter/Inter-ExtraLight.ttf +0 -0
  80. package/public/fonts/Inter/Inter-ExtraLightItalic.ttf +0 -0
  81. package/public/fonts/Inter/Inter-Italic.ttf +0 -0
  82. package/public/fonts/Inter/Inter-Light.ttf +0 -0
  83. package/public/fonts/Inter/Inter-LightItalic.ttf +0 -0
  84. package/public/fonts/Inter/Inter-Medium.ttf +0 -0
  85. package/public/fonts/Inter/Inter-MediumItalic.ttf +0 -0
  86. package/public/fonts/Inter/Inter-Regular.ttf +0 -0
  87. package/public/fonts/Inter/Inter-SemiBold.ttf +0 -0
  88. package/public/fonts/Inter/Inter-SemiBoldItalic.ttf +0 -0
  89. package/public/fonts/Inter/Inter-Thin.ttf +0 -0
  90. package/public/fonts/Inter/Inter-ThinItalic.ttf +0 -0
  91. package/public/fonts/Outfit/Outfit-Black.ttf +0 -0
  92. package/public/fonts/Outfit/Outfit-Bold.ttf +0 -0
  93. package/public/fonts/Outfit/Outfit-ExtraBold.ttf +0 -0
  94. package/public/fonts/Outfit/Outfit-ExtraLight.ttf +0 -0
  95. package/public/fonts/Outfit/Outfit-Light.ttf +0 -0
  96. package/public/fonts/Outfit/Outfit-Medium.ttf +0 -0
  97. package/public/fonts/Outfit/Outfit-Regular.ttf +0 -0
  98. package/public/fonts/Outfit/Outfit-SemiBold.ttf +0 -0
  99. package/public/fonts/Outfit/Outfit-Thin.ttf +0 -0
  100. package/public/github-mark.svg +0 -3
  101. package/public/tokens/GYEN.svg +0 -9
  102. package/public/tokens/PYUSD.svg +0 -9
  103. package/public/tokens/USDT.svg +0 -6
  104. package/scripts/mcp-server/resources.d.mts +0 -1
  105. package/scripts/mcp-server/resources.mjs +0 -102
  106. package/test/jsdom-global-register.d.ts +0 -1
  107. package/test/jsdom-global-register.js +0 -1
package/AGENTS.md DELETED
@@ -1,546 +0,0 @@
1
- # AGENTS.md
2
-
3
- This file provides guidance to Code Agent when working with code in this repository.
4
-
5
- ## Development Commands
6
-
7
- ### Development Server
8
- - `pnpm dev` - Start Storybook development server on port 6205
9
- - `pnpm start` - Alternative command to start development server
10
-
11
- ### Building
12
- - `pnpm build` - Build the component library (both CommonJS and ES modules)
13
-
14
- > Note: `omni build -n` (bypass pre-checks) and `omni build --demo` (build Storybook) are omni-door CLI options, not separate pnpm scripts.
15
-
16
- ### Testing and Linting
17
- - `pnpm test` - Run Jest tests (passes with no tests by default)
18
- - `pnpm lint` - Run all linting (ESLint, Prettier, Stylelint)
19
- - `pnpm lint:fix` - Auto-fix all linting issues
20
- - `pnpm lint:es` / `pnpm lint:es_fix` - ESLint only
21
- - `pnpm lint:prettier` / `pnpm lint:prettier_fix` - Prettier only
22
- - `pnpm lint:style` / `pnpm lint:style_fix` - Stylelint only
23
-
24
- ### Component Generation (omni-door CLI)
25
- - `omni new` - Interactive component scaffolding
26
- - `omni new ComponentName -f` - Generate functional component with given name
27
-
28
- > Note: These are omni-door CLI commands, not pnpm scripts. Ensure `@omni-door/cli` is installed globally or use `npx omni-door`.
29
-
30
- ### Package Management
31
- - `pnpm release` - Release new version (auto-builds first)
32
-
33
- > Note: Release options (`-i` ignore version iteration, `-m 0.3.25` specific version, `-n` bypass pre-checks) are omni-door CLI flags passed through `omni release`.
34
-
35
- ## Project Architecture
36
-
37
- ### Component Library Structure
38
- This is a standalone React component library built with PrimeReact as the base, using the omni-door CLI framework for scaffolding and build processes. Hooks are imported from `@1money/hooks`.
39
-
40
- **Core Dependencies:**
41
- - **@1money/hooks**: Shared React hooks library (linked locally via `file:../1money-hooks`)
42
- - **PrimeReact**: Base UI component library (>=10.9.0)
43
- - **react-tooltip**: Tooltip rendering (^5.28.1)
44
- - **React**: >=16.8.0 with React 19+ for development
45
- - **TypeScript**: Full TypeScript support with strict mode
46
-
47
- **Styling System:**
48
- - Custom SCSS-based design system (`src/styles/`) with token scales, theme engine, `om-sx` mixin, and atomic utility classes
49
- - NOT using TailwindCSS — all styling is handled by the internal SCSS system
50
- - See `src/styles/README.md` for full documentation
51
-
52
- **Build System:**
53
- - Dual output: CommonJS (`lib/`) and ES modules (`es/`)
54
- - Gulp-based build pipeline via omni-door
55
- - SCSS preprocessing with modern API
56
- - Path aliasing: `@/` maps to `src/`
57
-
58
- ### Directory Structure
59
- ```
60
- src/
61
- ├── components/ # All UI components
62
- │ └── ComponentName/
63
- │ ├── ComponentName.tsx # Main component file
64
- │ ├── interface.ts # TypeScript interfaces
65
- │ ├── index.ts # Export barrel
66
- │ ├── style/ # SCSS styles
67
- │ │ ├── ComponentName.scss
68
- │ │ └── index.ts # (some components)
69
- │ ├── ComponentName.stories.tsx # Storybook stories (optional)
70
- │ ├── __test__/ # Jest tests (optional)
71
- │ └── README.md # Component documentation (optional)
72
- ├── utils/ # Utility functions (classnames)
73
- ├── styles/ # SCSS design system (tokens, theme, system, utilities)
74
- │ ├── tokens/ # Design tokens (color, spacing, radius, shadow, etc.)
75
- │ ├── theme/ # Token → CSS variable transformation
76
- │ ├── system/ # om-sx mixin, responsive, variant DSL
77
- │ ├── utilities/ # Atomic CSS class generator
78
- │ ├── _api.scss # Public API entrypoint for components
79
- │ └── _settings.scss # Global settings
80
- └── index.ts # Main library export
81
- ```
82
-
83
- > Note: Not all components have stories, tests, or README. The full scaffold (stories + tests + README) is generated by `omni new` but may not exist for manually created components.
84
-
85
- ### Component Development Patterns
86
- - **Base Components**: Most components wrap PrimeReact components with custom styling
87
- - **Props Interface**: Each component has a dedicated `interface.ts` file
88
- - **Styling**: SCSS with the internal design system (`@use '@/styles/api' as *`), using `classnames` utility for class composition
89
- - **Testing**: Jest with snapshot testing (currently only Spinner and Icons have tests)
90
- - **Stories**: Storybook stories for documentation (currently only Spinner and Icons have stories)
91
-
92
- ### Key Files
93
- - `omni.config.js`: Main configuration for build, dev server, and scaffolding
94
- - `gulpfile.js`: Custom build pipeline configuration
95
- - `tsconfig.json`: TypeScript configuration with path mapping
96
- - `.storybook/`: Storybook configuration with custom theming
97
-
98
- ### Package Exports
99
- The library provides both named exports and individual component imports:
100
- ```js
101
- // Named imports
102
- import { Button, Checkbox, Tag, Tooltip, Notification, Icons, Spinner } from '@1money/components-ui';
103
-
104
- // Type imports
105
- import type { ButtonProps, IconName, SpinnerProps } from '@1money/components-ui';
106
-
107
- // Individual component imports (tree-shakeable)
108
- import { Button } from '@1money/components-ui/Button';
109
- import { Icons, IconWrapper, IconHover } from '@1money/components-ui/Icons';
110
-
111
- // CSS (required for styling)
112
- import '@1money/components-ui/index.css';
113
- ```
114
-
115
- **Available components:** Accordion, Alert, Button, Calendar, Carousel, Cell, Checkbox (+ CheckboxGroup), CoachMark, Copy, Dialog, Divider, Drawer, Dropdown, Empty, Flex, Grid (+ Row, Col), Icons (+ IconWrapper, IconHover), Input, Link, Navigation (+ Nav), Notification, Pagination, Popconfirm, Portal, ProForm (unified form system, see below), Progress, Radio (+ RadioGroup), ResizeObserver, Segment, Select, Skeleton, Slider, Space, Spinner, Step, Switch, Table (+ VirtualTable), Tabs, Tag, Tooltip, Tour, Trigger, Typography, Upload (+ UploadFileBar), VirtualList
116
-
117
- **ProForm system:** The form layer is consolidated into a single `ProForm` module — there is no standalone `Form` component. `ProForm` wraps a `<form>` element, provides the unified `ProFormContext` (combines form-core state and ProForm semantics), and exposes:
118
- - `ProForm` (root) with static sub-components: `ProForm.Item`, `ProForm.Dependency`, `ProForm.List`, `ProForm.Group`, `ProForm.FieldSet`, `ProForm.Submitter`, `ProForm.Text / Password / TextArea / Checkbox / CheckboxGroup / Switch / Select / RadioGroup / Slider / DatePicker / Upload`, plus hooks `ProForm.useForm`, `ProForm.useInstance`, `ProForm.useContext`.
119
- - `ProFormItem` — the public item primitive (supports `transform`, `convertValue`, `valueType`, `readonlyRender`, `colProps`, `mode`, `emptyText`, `valueEnum`).
120
- - Named exports for all field components, `ProFormDependency`, `ProFormList`, `ProFormGroup`, `DialogForm`, `DrawerForm`, `QueryFilter`, and `createProFormField` (the extension factory).
121
- - Internal-only: `FormItem` (the low-level item used by `ProFormItem`), `useFormCore`, `FormContext` — none of these are publicly exported.
122
-
123
- ### Development Workflow
124
- 1. Use `omni new ComponentName -f` to scaffold new components (generates interface, styles, stories, tests, and README)
125
- 2. All components must pass linting (`pnpm lint`) and tests (`pnpm test`) before building
126
- 3. Build process generates both CommonJS and ES module outputs with TypeScript declarations
127
- 4. Storybook (`pnpm dev`) serves as both development environment and documentation
128
- 5. After adding a new component, update `src/index.ts` and `package.json` exports
129
-
130
- ### Important Notes
131
- - Font assets (Aeonik, Inter, Outfit) are included in `public/fonts/`
132
- - Pre-commit hooks enforce code quality via husky and lint-staged
133
- - Hooks are imported from `@1money/hooks`, not defined locally
134
- - Styling uses the internal SCSS design system (`src/styles/`), not TailwindCSS
135
- - Component SCSS files should import `@use '@/styles/api' as *` for access to all design tokens and mixins
136
-
137
- ## Commit Convention
138
-
139
- This project follows the [Conventional Commits](https://www.conventionalcommits.org/) specification.
140
-
141
- ### Commit Message Format
142
-
143
- ```
144
- <type>(<scope>): <subject>
145
-
146
- [optional body]
147
-
148
- [optional footer]
149
- ```
150
-
151
- ### Types
152
-
153
- Allowed types are enforced by `commitlint.config.js`. Use the standard types below:
154
-
155
- | Type | Description |
156
- |------|-------------|
157
- | `feat` | New feature |
158
- | `feature` | New feature (alias for `feat`) |
159
- | `fix` | Bug fix |
160
- | `hotfix` | Urgent bug fix |
161
- | `docs` | Documentation changes only |
162
- | `style` | Code style changes (formatting, missing semicolons, etc.) — no logic change |
163
- | `refactor` | Code refactoring — no feature addition or bug fix |
164
- | `test` | Adding or updating tests |
165
- | `chore` | Build process, tooling, or dependency changes |
166
- | `revert` | Revert a previous commit |
167
- | `update` | General updates |
168
- | `upgrade` | Dependency upgrades |
169
- | `modify` | Minor modifications |
170
- | `merge` | Merge commits |
171
- | `optimize` | Performance or code optimization |
172
- | `revamp` | Major rework |
173
- | `refine` | Small refinements |
174
-
175
- > Note: `[OMNI-DOOR]` and `[@1MONEY/COMPONENTS-UI]` are also allowed for auto-generated commits by tooling.
176
-
177
- ### Scope
178
-
179
- The scope should be the name of the component or module affected:
180
-
181
- ```
182
- feat(Button): add loading state support
183
- fix(Input): correct placeholder color in dark mode
184
- docs(ProForm): update usage examples
185
- refactor(utils): simplify classnames helper
186
- ```
187
-
188
- Omit scope when the change is global or cross-cutting:
189
-
190
- ```
191
- chore: upgrade PrimeReact to v11
192
- ci: add lint check to PR workflow
193
- ```
194
-
195
- ### Subject Rules
196
-
197
- - ✅ Use imperative mood: "add feature" not "added feature" or "adds feature"
198
- - ✅ Start with a lowercase letter
199
- - ✅ No period at the end
200
- - ✅ Keep under 72 characters
201
- - ❌ Do not use vague messages like "fix bug", "update code", "WIP"
202
-
203
- ### Examples
204
-
205
- ```bash
206
- # ✅ Good
207
- feat(ProForm): add StepsForm component with validation support
208
- fix(Select): prevent dropdown from closing on internal scroll
209
- refactor(Button): extract size constants to avoid magic strings
210
- test(Input): add snapshot tests for controlled mode
211
- docs(README): add installation and usage guide
212
- chore: update pnpm lockfile after dependency upgrade
213
-
214
- # ❌ Bad
215
- fix bug
216
- update
217
- WIP
218
- feat: add stuff
219
- ```
220
-
221
- ### Breaking Changes
222
-
223
- Breaking changes must be noted in the footer with `BREAKING CHANGE:`:
224
-
225
- ```
226
- feat(ProForm): change onSubmit callback signature
227
-
228
- BREAKING CHANGE: onSubmit now receives (values, form) instead of (values).
229
- Consumers must update their onSubmit handlers accordingly.
230
- ```
231
-
232
- ### Multi-line Body
233
-
234
- Use the body to explain *why* a change was made, not *what* was changed (the diff shows that):
235
-
236
- ```
237
- fix(StepsForm): clear downstream step values on backward navigation
238
-
239
- When a user navigates backward in a multi-step form, previously entered
240
- values in later steps should be cleared to prevent stale data from
241
- being submitted. This matches the expected UX behavior.
242
- ```
243
-
244
- ## TypeScript Standards
245
-
246
- ### Core Principles
247
-
248
- - ✅ All components and functions must provide accurate type definitions
249
- - ✅ Avoid using `any`; define types as precisely as possible
250
- - ✅ Use interfaces (`interface`) instead of type aliases (`type`) for object structures
251
- - ✅ Export all public interface types for easier consumer usage
252
- - ✅ Follow TypeScript type design principles strictly to ensure type safety
253
- - ✅ Ensure compilation has no type errors or warnings
254
-
255
- ### Component Type Definitions
256
-
257
- ```tsx
258
- // ✅ Correct: use interface to define Props
259
- interface ButtonProps {
260
- type?: 'primary' | 'secondary' | 'warning';
261
- onClick?: (e: React.MouseEvent) => void;
262
- }
263
-
264
- // ❌ Incorrect: avoid using type to define object structures
265
- type ButtonProps = {
266
- type?: 'primary' | 'secondary';
267
- };
268
-
269
- // ✅ Correct: component Props interface naming
270
- interface ComponentNameProps {
271
- // ...
272
- }
273
-
274
- // ✅ Correct: component state interface naming
275
- interface ComponentNameState {
276
- // ...
277
- }
278
-
279
- // ✅ Correct: use ForwardRefRenderFunction to define ref
280
- const Component = React.forwardRef<ComponentRef, ComponentProps>((props, ref) => {
281
- // ...
282
- });
283
- ```
284
-
285
- ### Type Usage Best Practices
286
-
287
- - ✅ Use generics where appropriate to improve type flexibility
288
- - ✅ Use intersection types (`&`) to combine multiple types
289
- - ✅ Use literal union types to define a limited set of options
290
- - ✅ Avoid `enum`; prefer union types and `as const`
291
- - ✅ Rely on TypeScript type inference as much as possible
292
- - ✅ Use type assertions (`as`) only when necessary
293
-
294
- ```tsx
295
- // ✅ Recommended: use union types and as const
296
- const ButtonTypes = ['primary', 'secondary', 'warning'] as const;
297
- type ButtonType = (typeof ButtonTypes)[number];
298
-
299
- // ❌ Not recommended: use enum
300
- enum ButtonType {
301
- Primary = 'primary',
302
- Default = 'secondary',
303
- }
304
- ```
305
-
306
- ### Avoid Magic Strings
307
-
308
- - ✅ Define string constants or use `as const` arrays for repeated string values
309
- - ✅ Use constants for event names, action types, storage keys, and API endpoints
310
- - ✅ Extract hardcoded strings into named constants with descriptive names
311
- - ✅ Keep constants close to their usage or in a dedicated constants file
312
-
313
- ```tsx
314
- // ✅ Correct: define constants
315
- const STORAGE_KEY = 'user_preferences';
316
- const EVENT_SUBMIT = 'submit';
317
-
318
- localStorage.setItem(STORAGE_KEY, value);
319
- element.addEventListener(EVENT_SUBMIT, handler);
320
-
321
- // ✅ Correct: use as const for related values
322
- const ToastTypes = ['success', 'error', 'warning', 'info'] as const;
323
- type ToastType = (typeof ToastTypes)[number];
324
-
325
- // ❌ Incorrect: magic strings scattered in code
326
- localStorage.setItem('user_preferences', value);
327
- element.addEventListener('submit', handler);
328
- if (status === 'success') { ... }
329
- ```
330
-
331
- ## Custom Hooks Usage Guide
332
-
333
- Hooks are provided by `@1money/hooks`. Import them as:
334
- ```tsx
335
- import { useControlledState, useEventCallback } from '@1money/hooks';
336
- ```
337
-
338
- **Currently used in this library:** `useControlledState`, `useEventCallback` (in Checkbox and Tooltip).
339
-
340
- The following hooks are available and should be used when applicable:
341
-
342
- ### useControlledState
343
-
344
- **When to use**: Building components that support both controlled and uncontrolled modes.
345
-
346
- ```tsx
347
- // ✅ Use for form components that can be controlled or uncontrolled
348
- function Input({ value, defaultValue, onChange }: InputProps) {
349
- const [innerValue, setInnerValue] = useControlledState(defaultValue ?? '', value);
350
-
351
- const handleChange = (e: ChangeEvent<HTMLInputElement>) => {
352
- setInnerValue(e.target.value);
353
- onChange?.(e);
354
- };
355
-
356
- return <input value={innerValue} onChange={handleChange} />;
357
- }
358
- ```
359
-
360
- ### useEventCallback
361
-
362
- **When to use**: When you need a stable callback reference that always invokes the latest function version. Similar to React's `useEvent` RFC.
363
-
364
- ```tsx
365
- // ✅ Use when passing callbacks to optimized child components
366
- function Parent() {
367
- const [count, setCount] = useState(0);
368
-
369
- // Stable reference, always calls latest function
370
- const handleClick = useEventCallback(() => {
371
- console.log('Current count:', count); // Always gets latest count
372
- });
373
-
374
- return <MemoizedChild onClick={handleClick} />;
375
- }
376
- ```
377
-
378
- ### useLatest
379
-
380
- **When to use**: When you need to access the latest value in callbacks without causing re-renders or stale closures.
381
-
382
- ```tsx
383
- // ✅ Use to avoid stale closures in timers or event handlers
384
- function Timer({ delay, callback }: TimerProps) {
385
- const callbackRef = useLatest(callback);
386
-
387
- useEffect(() => {
388
- const id = setInterval(() => {
389
- callbackRef.current(); // Always calls the latest callback
390
- }, delay);
391
- return () => clearInterval(id);
392
- }, [delay]); // No need to include callback in deps
393
- }
394
- ```
395
-
396
- ### useLayoutEffect
397
-
398
- **When to use**: When you need to know if the effect is running on initial mount or subsequent updates.
399
-
400
- ```tsx
401
- // ✅ Use when behavior differs between mount and update
402
- function AnimatedComponent({ value }: Props) {
403
- useLayoutEffect((mount) => {
404
- if (mount) {
405
- // Initial mount - setup without animation
406
- element.style.opacity = '1';
407
- } else {
408
- // Update - animate the change
409
- animateOpacity(element);
410
- }
411
- }, [value]);
412
- }
413
- ```
414
-
415
- ### useMemoizedFn
416
-
417
- **When to use**: When you need a stable function reference without managing a dependency array, while always calling the latest version.
418
-
419
- ```tsx
420
- // ✅ Use for event handlers passed to child components
421
- function Form({ onSubmit }: FormProps) {
422
- const [data, setData] = useState({});
423
-
424
- // Stable reference, no deps needed, always uses latest data
425
- const handleSubmit = useMemoizedFn(() => {
426
- onSubmit(data);
427
- });
428
-
429
- return <ExpensiveChild onSubmit={handleSubmit} />;
430
- }
431
- ```
432
-
433
- ### usePrevious
434
-
435
- **When to use**: When you need to compare current value with the previous render's value.
436
-
437
- ```tsx
438
- // ✅ Use for detecting value changes
439
- function Counter({ count }: Props) {
440
- const prevCount = usePrevious(count);
441
-
442
- useEffect(() => {
443
- if (prevCount !== undefined && count > prevCount) {
444
- console.log('Count increased!');
445
- }
446
- }, [count, prevCount]);
447
- }
448
- ```
449
-
450
- ### useSafeState
451
-
452
- **When to use**: When setting state in async callbacks where the component might unmount before completion.
453
-
454
- ```tsx
455
- // ✅ Use for async operations to prevent memory leaks
456
- function AsyncComponent() {
457
- const [data, setData] = useSafeState<Data | null>(null);
458
-
459
- useEffect(() => {
460
- fetchData().then((result) => {
461
- setData(result); // Safe: won't update if unmounted
462
- });
463
- }, []);
464
- }
465
- ```
466
-
467
- ### useSyncState
468
-
469
- **When to use**: When React batches multiple state updates but you need to read the latest value synchronously.
470
-
471
- ```tsx
472
- // ✅ Use when multiple events fire simultaneously (e.g., onTransitionEnd)
473
- function AnimatedList() {
474
- const [getCompletedCount, setCompletedCount] = useSyncState(0);
475
-
476
- const handleTransitionEnd = () => {
477
- // Multiple transitions may end at once, React batches them
478
- // getCompletedCount() always returns the latest value
479
- setCompletedCount(getCompletedCount() + 1);
480
- };
481
- }
482
- ```
483
-
484
- ### useUpdateEffect
485
-
486
- **When to use**: When you want an effect to run only on updates, skipping the initial mount.
487
-
488
- ```tsx
489
- // ✅ Use when initial render shouldn't trigger the effect
490
- function SearchInput({ query, onSearch }: Props) {
491
- useUpdateEffect(() => {
492
- // Only runs when query changes, not on initial mount
493
- onSearch(query);
494
- }, [query]);
495
- }
496
- ```
497
-
498
- ### useLayoutState
499
-
500
- **When to use**: When you need to batch multiple synchronous state updates into a single microtask to reduce unnecessary re-renders. Updates are collected and applied together via `Promise.resolve`, triggering only one render.
501
-
502
- ```tsx
503
- // ✅ Use when multiple state updates happen synchronously and should be batched
504
- function VirtualTable({ columns }: Props) {
505
- const [colWidths, setColWidths] = useLayoutState<Map<string, number>>(new Map());
506
-
507
- const handleResize = (key: string, width: number) => {
508
- // Multiple columns may resize at once; updates are batched into one render
509
- setColWidths(prev => new Map(prev).set(key, width));
510
- };
511
-
512
- return <ResizableColumns widths={colWidths} onResize={handleResize} />;
513
- }
514
- ```
515
-
516
- ### useTimeoutLock
517
-
518
- **When to use**: When you need a short-lived lock that auto-resets after a timeout (100ms). Useful for preventing duplicate actions or debouncing rapid state changes.
519
-
520
- ```tsx
521
- // ✅ Use to prevent rapid duplicate submissions
522
- function SubmitButton() {
523
- const [setLock, getLock] = useTimeoutLock<string>();
524
-
525
- const handleClick = () => {
526
- if (getLock()) return; // Already locked
527
- setLock('submitting');
528
- submitForm();
529
- };
530
- }
531
- ```
532
-
533
- ### Hook Selection Guide
534
-
535
- | Scenario | Recommended Hook |
536
- |----------|------------------|
537
- | Controlled/uncontrolled component | `useControlledState` |
538
- | Stable callback for memoized children | `useEventCallback` or `useMemoizedFn` |
539
- | Access latest value without re-render | `useLatest` |
540
- | Different logic for mount vs update | `useLayoutEffect` |
541
- | Compare with previous value | `usePrevious` |
542
- | Async state updates after unmount | `useSafeState` |
543
- | Read latest state synchronously | `useSyncState` |
544
- | Skip effect on initial mount | `useUpdateEffect` |
545
- | Batch synchronous state updates | `useLayoutState` |
546
- | Short-lived lock with auto-reset | `useTimeoutLock` |
package/CLAUDE.md DELETED
@@ -1 +0,0 @@
1
- @AGENTS.md
package/jest.setup.d.ts DELETED
@@ -1 +0,0 @@
1
- import '@testing-library/jest-dom';
package/jest.setup.ts DELETED
@@ -1 +0,0 @@
1
- import '@testing-library/jest-dom';