@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.
- package/es/components/Table/interface.d.ts +2 -1
- package/es/components/Table/renderers/EmptyState.d.ts +2 -1
- package/es/components/Table/renderers/EmptyState.js +15 -8
- package/es/components/Table/style/Table.css +1 -1
- package/es/index.css +1 -1
- package/es/stories/docs/ComponentDocsPage.js +234 -0
- package/es/stories/docs/componentDocMeta.js +97 -0
- package/es/stories/docs/storybook-docs.css +79 -0
- package/lib/components/Table/interface.d.ts +2 -1
- package/lib/components/Table/renderers/EmptyState.d.ts +2 -1
- package/lib/components/Table/renderers/EmptyState.js +15 -7
- package/lib/components/Table/style/Table.css +1 -1
- package/lib/index.css +1 -1
- package/lib/stories/docs/ComponentDocsPage.js +244 -0
- package/lib/stories/docs/componentDocMeta.js +104 -0
- package/lib/stories/docs/storybook-docs.css +79 -0
- package/package.json +23 -8
- package/scripts/mcp-server/README.md +267 -0
- package/scripts/mcp-server/bin.mjs +2 -0
- package/scripts/mcp-server/drift.json +5 -0
- package/scripts/mcp-server/examples.generated.json +2651 -0
- package/scripts/mcp-server/index.generated.json +18098 -0
- package/scripts/mcp-server/index.mjs +308 -26
- package/scripts/mcp-server/tools/get-examples.mjs +125 -0
- package/scripts/mcp-server/tools/get-library-info.mjs +25 -0
- package/scripts/mcp-server/tools/get-symbol.mjs +232 -0
- package/scripts/mcp-server/tools/get-token.mjs +60 -0
- package/scripts/mcp-server/tools/list-icons.mjs +38 -0
- package/scripts/mcp-server/tools/list-symbols.mjs +46 -0
- package/scripts/mcp-server/tools/resolve-import.mjs +125 -0
- package/scripts/mcp-server/tools/search-symbols.mjs +79 -0
- package/.agents/skills/1money-component-dev/SKILL.md +0 -224
- package/.agents/skills/1money-component-dev/checklist.md +0 -159
- package/.agents/skills/1money-component-dev/references/ComponentPatterns.md +0 -478
- package/.agents/skills/1money-component-dev/references/FigmaExtractionChecklist.md +0 -144
- package/.agents/skills/1money-component-dev/references/HooksGuide.md +0 -360
- package/.agents/skills/1money-component-dev/references/SemanticColors.md +0 -215
- package/.agents/skills/1money-component-dev/references/StyleSystemAPI.md +0 -389
- package/.claude/settings.local.json +0 -120
- package/.claude/skills/1money-component-dev/SKILL.md +0 -229
- package/.claude/skills/1money-component-dev/checklist.md +0 -159
- package/.claude/skills/1money-component-dev/references/ComponentPatterns.md +0 -478
- package/.claude/skills/1money-component-dev/references/FigmaExtractionChecklist.md +0 -144
- package/.claude/skills/1money-component-dev/references/HooksGuide.md +0 -360
- package/.claude/skills/1money-component-dev/references/SemanticColors.md +0 -215
- package/.claude/skills/1money-component-dev/references/StyleSystemAPI.md +0 -389
- package/.claude/skills/1money-component-review/SKILL.md +0 -316
- package/.claude/skills/component-pipeline/SKILL.md +0 -116
- package/.claude/skills/component-pipeline/checklist.md +0 -125
- package/.hintrc +0 -13
- package/@types/global.d.ts +0 -28
- package/AGENTS.md +0 -546
- package/CLAUDE.md +0 -1
- package/jest.setup.d.ts +0 -1
- package/jest.setup.ts +0 -1
- package/patches/primereact.patch +0 -323
- package/patches/react-pro-sidebar.patch +0 -6421
- package/public/favicon.ico +0 -0
- package/public/fonts/Aeonik/Aeonik-Air.ttf +0 -0
- package/public/fonts/Aeonik/Aeonik-AirItalic.ttf +0 -0
- package/public/fonts/Aeonik/Aeonik-Black.ttf +0 -0
- package/public/fonts/Aeonik/Aeonik-BlackItalic.ttf +0 -0
- package/public/fonts/Aeonik/Aeonik-Bold.ttf +0 -0
- package/public/fonts/Aeonik/Aeonik-BoldItalic.ttf +0 -0
- package/public/fonts/Aeonik/Aeonik-Light.ttf +0 -0
- package/public/fonts/Aeonik/Aeonik-LightItalic.ttf +0 -0
- package/public/fonts/Aeonik/Aeonik-Medium.ttf +0 -0
- package/public/fonts/Aeonik/Aeonik-MediumItalic.ttf +0 -0
- package/public/fonts/Aeonik/Aeonik-Regular.ttf +0 -0
- package/public/fonts/Aeonik/Aeonik-RegularItalic.ttf +0 -0
- package/public/fonts/Aeonik/Aeonik-Thin.ttf +0 -0
- package/public/fonts/Aeonik/Aeonik-ThinItalic.ttf +0 -0
- package/public/fonts/Inter/Inter-Black.ttf +0 -0
- package/public/fonts/Inter/Inter-BlackItalic.ttf +0 -0
- package/public/fonts/Inter/Inter-Bold.ttf +0 -0
- package/public/fonts/Inter/Inter-BoldItalic.ttf +0 -0
- package/public/fonts/Inter/Inter-ExtraBold.ttf +0 -0
- package/public/fonts/Inter/Inter-ExtraBoldItalic.ttf +0 -0
- package/public/fonts/Inter/Inter-ExtraLight.ttf +0 -0
- package/public/fonts/Inter/Inter-ExtraLightItalic.ttf +0 -0
- package/public/fonts/Inter/Inter-Italic.ttf +0 -0
- package/public/fonts/Inter/Inter-Light.ttf +0 -0
- package/public/fonts/Inter/Inter-LightItalic.ttf +0 -0
- package/public/fonts/Inter/Inter-Medium.ttf +0 -0
- package/public/fonts/Inter/Inter-MediumItalic.ttf +0 -0
- package/public/fonts/Inter/Inter-Regular.ttf +0 -0
- package/public/fonts/Inter/Inter-SemiBold.ttf +0 -0
- package/public/fonts/Inter/Inter-SemiBoldItalic.ttf +0 -0
- package/public/fonts/Inter/Inter-Thin.ttf +0 -0
- package/public/fonts/Inter/Inter-ThinItalic.ttf +0 -0
- package/public/fonts/Outfit/Outfit-Black.ttf +0 -0
- package/public/fonts/Outfit/Outfit-Bold.ttf +0 -0
- package/public/fonts/Outfit/Outfit-ExtraBold.ttf +0 -0
- package/public/fonts/Outfit/Outfit-ExtraLight.ttf +0 -0
- package/public/fonts/Outfit/Outfit-Light.ttf +0 -0
- package/public/fonts/Outfit/Outfit-Medium.ttf +0 -0
- package/public/fonts/Outfit/Outfit-Regular.ttf +0 -0
- package/public/fonts/Outfit/Outfit-SemiBold.ttf +0 -0
- package/public/fonts/Outfit/Outfit-Thin.ttf +0 -0
- package/public/github-mark.svg +0 -3
- package/public/tokens/GYEN.svg +0 -9
- package/public/tokens/PYUSD.svg +0 -9
- package/public/tokens/USDT.svg +0 -6
- package/scripts/mcp-server/resources.d.mts +0 -1
- package/scripts/mcp-server/resources.mjs +0 -102
- package/test/jsdom-global-register.d.ts +0 -1
- 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';
|