@pushwoosh/kit-helpers 4.12.3 → 4.12.4
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/analytics/trackEvent.d.ts +4 -0
- package/analytics/trackEvent.js +4 -0
- package/analytics/utils.d.ts +2 -0
- package/expand.d.ts +5 -0
- package/expand.js +5 -0
- package/flex.d.ts +2 -0
- package/flex.js +2 -0
- package/formatNumber.d.ts +5 -0
- package/formatNumber.js +4 -0
- package/grid.d.ts +5 -0
- package/grid.js +5 -0
- package/hooks/types.d.ts +2 -0
- package/hooks/useAsync.d.ts +4 -0
- package/hooks/useAsync.js +4 -0
- package/hooks/useAsyncFn.d.ts +7 -0
- package/hooks/useAsyncFn.js +4 -0
- package/hooks/useAsyncFunction.d.ts +6 -0
- package/hooks/useAsyncFunction.js +4 -0
- package/hooks/useDelayedEffect.d.ts +4 -0
- package/hooks/useDelayedEffect.js +4 -0
- package/hooks/useDelayedValue.d.ts +1 -0
- package/hooks/useDelayedValue.js +1 -0
- package/hooks/useEffectOnce.d.ts +1 -0
- package/hooks/useEffectOnce.js +1 -0
- package/hooks/useMount.d.ts +1 -0
- package/hooks/useMount.js +1 -0
- package/hooks/useMountedState.d.ts +1 -0
- package/hooks/useMountedState.js +1 -0
- package/hooks/useUnmount.d.ts +1 -0
- package/hooks/useUnmount.js +1 -0
- package/package.json +2 -2
- package/styled-helpers.d.ts +26 -0
- package/styled-helpers.js +16 -0
- package/ai-documentation.md +0 -838
package/ai-documentation.md
DELETED
|
@@ -1,838 +0,0 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
---
|
|
4
|
-
title: "@pushwoosh/kit-helpers — API Reference"
|
|
5
|
-
version: "2.0.0"
|
|
6
|
-
description: "React component library for building Pushwoosh products with TypeScript, styled-components, and comprehensive hook utilities"
|
|
7
|
-
---
|
|
8
|
-
|
|
9
|
-
# @pushwoosh/kit-helpers
|
|
10
|
-
|
|
11
|
-
**Version:** 2.0.0
|
|
12
|
-
|
|
13
|
-
React component library for building Pushwoosh products with TypeScript, styled-components, and comprehensive hook utilities.
|
|
14
|
-
|
|
15
|
-
> This page groups the API by Components, Hooks, Utilities, and Types. All styled-component props are prefixed with `$` to avoid DOM prop warnings.
|
|
16
|
-
|
|
17
|
-
---
|
|
18
|
-
|
|
19
|
-
## Components
|
|
20
|
-
|
|
21
|
-
### GridContainer
|
|
22
|
-
- **Path:** `~/src/grid`
|
|
23
|
-
- **Description:** Base grid container component using CSS Grid for flexible layouts
|
|
24
|
-
- **Interface:**
|
|
25
|
-
```ts
|
|
26
|
-
GridContainer<{
|
|
27
|
-
$alignContent?: 'center' | 'start' | 'end' | 'space-between' | 'space-around' | 'space-evenly' | 'stretch' | 'baseline';
|
|
28
|
-
$alignItems?: 'center' | 'start' | 'end' | 'baseline' | 'stretch';
|
|
29
|
-
$basis?: StrNum;
|
|
30
|
-
$bgColor?: string;
|
|
31
|
-
$border?: string | CssBoxSides<string>;
|
|
32
|
-
$borderRadius?: StrNum;
|
|
33
|
-
$bottom?: StrNum;
|
|
34
|
-
$boxShadow?: string;
|
|
35
|
-
$flex?: StrNum;
|
|
36
|
-
$gap?: StrNum;
|
|
37
|
-
$gridAutoFlow?: 'row' | 'column' | 'dense' | 'row dense' | 'column dense';
|
|
38
|
-
$grow?: number;
|
|
39
|
-
$height?: StrNum;
|
|
40
|
-
$inset?: StrNum;
|
|
41
|
-
$justifyContent?: 'center' | 'start' | 'end' | 'space-between' | 'space-around' | 'space-evenly' | 'stretch';
|
|
42
|
-
$justifyItems?: 'center' | 'start' | 'end' | 'baseline' | 'stretch';
|
|
43
|
-
$left?: StrNum;
|
|
44
|
-
$margin?: StrNum | CssBoxSides<StrNum>;
|
|
45
|
-
$maxHeight?: StrNum;
|
|
46
|
-
$maxWidth?: StrNum;
|
|
47
|
-
$minHeight?: StrNum;
|
|
48
|
-
$minWidth?: StrNum;
|
|
49
|
-
$overflow?: 'hidden' | 'auto' | 'scroll' | 'visible';
|
|
50
|
-
$overflowX?: 'hidden' | 'auto' | 'scroll' | 'visible';
|
|
51
|
-
$overflowY?: 'hidden' | 'auto' | 'scroll' | 'visible';
|
|
52
|
-
$padding?: StrNum | CssBoxSides<StrNum>;
|
|
53
|
-
$placeItems?: 'center' | 'start' | 'end' | 'baseline' | 'stretch';
|
|
54
|
-
$position?: 'static' | 'relative' | 'absolute' | 'fixed' | 'sticky';
|
|
55
|
-
$right?: StrNum;
|
|
56
|
-
$shrink?: number;
|
|
57
|
-
$top?: StrNum;
|
|
58
|
-
$width?: StrNum;
|
|
59
|
-
$zIndex?: number;
|
|
60
|
-
}>
|
|
61
|
-
```
|
|
62
|
-
|
|
63
|
-
**Props**
|
|
64
|
-
|
|
65
|
-
| Name | Type | Required | Default | Description | Values |
|
|
66
|
-
|---|---|:---:|:---:|---|---|
|
|
67
|
-
| `$alignContent` | enum | | | Aligns the grid content along the cross axis | `center`, `start`, `end`, `space-between`, `space-around`, `space-evenly`, `stretch`, `baseline` |
|
|
68
|
-
| `$alignItems` | enum | | | Aligns grid items along the cross axis | `center`, `start`, `end`, `baseline`, `stretch` |
|
|
69
|
-
| `$basis` | string\|number | | | `flex-basis` (only effective when parent is a flex container; number → px) | |
|
|
70
|
-
| `$bgColor` | string | | | Background color of the grid container | |
|
|
71
|
-
| `$border` | object | | | Border styling, can be string or object with top/right/bottom/left properties | |
|
|
72
|
-
| `$borderRadius` | string\|number | | | Border radius, accepts number (converted to px) or string | |
|
|
73
|
-
| `$bottom` | string\|number | | | `bottom` offset (number → px) | |
|
|
74
|
-
| `$boxShadow` | string | | | CSS box shadow property | |
|
|
75
|
-
| `$flex` | string\|number | | | `flex` shorthand (only effective when parent is a flex container) | |
|
|
76
|
-
| `$gap` | string\|number | | | Gap between grid items, accepts number (converted to px) or string | |
|
|
77
|
-
| `$gridAutoFlow` | enum | | | Controls how auto-placed items are inserted in the grid | `row`, `column`, `dense`, `row dense`, `column dense` |
|
|
78
|
-
| `$grow` | number | | | `flex-grow` (only effective when parent is a flex container) | |
|
|
79
|
-
| `$height` | string\|number | | | `height` (number → px; strings like `'100%'` / `'auto'` / `'fit-content'` pass through) | |
|
|
80
|
-
| `$inset` | string\|number | | | `inset` CSS shorthand (number → px, string passes through, e.g. `'10px 20px'`) | |
|
|
81
|
-
| `$justifyContent` | enum | | | Aligns the grid content along the main axis | `center`, `start`, `end`, `space-between`, `space-around`, `space-evenly`, `stretch` |
|
|
82
|
-
| `$justifyItems` | enum | | | Aligns grid items along the main axis | `center`, `start`, `end`, `baseline`, `stretch` |
|
|
83
|
-
| `$left` | string\|number | | | `left` offset (number → px) | |
|
|
84
|
-
| `$margin` | object | | | Margin spacing, can be number/string or object with directional properties | |
|
|
85
|
-
| `$maxHeight` | string\|number | | | `max-height` (number → px) | |
|
|
86
|
-
| `$maxWidth` | string\|number | | | `max-width` (number → px) | |
|
|
87
|
-
| `$minHeight` | string\|number | | | `min-height` (number → px) | |
|
|
88
|
-
| `$minWidth` | string\|number | | | `min-width` (number → px) | |
|
|
89
|
-
| `$overflow` | enum | | | `overflow` | `hidden`, `auto`, `scroll`, `visible` |
|
|
90
|
-
| `$overflowX` | enum | | | `overflow-x` | `hidden`, `auto`, `scroll`, `visible` |
|
|
91
|
-
| `$overflowY` | enum | | | `overflow-y` | `hidden`, `auto`, `scroll`, `visible` |
|
|
92
|
-
| `$padding` | object | | | Padding spacing, can be number/string or object with directional properties | |
|
|
93
|
-
| `$placeItems` | enum | | | Shorthand for align-items and justify-items | `center`, `start`, `end`, `baseline`, `stretch` |
|
|
94
|
-
| `$position` | enum | | | `position` | `static`, `relative`, `absolute`, `fixed`, `sticky` |
|
|
95
|
-
| `$right` | string\|number | | | `right` offset (number → px) | |
|
|
96
|
-
| `$shrink` | number | | | `flex-shrink` (only effective when parent is a flex container) | |
|
|
97
|
-
| `$top` | string\|number | | | `top` offset (number → px) | |
|
|
98
|
-
| `$width` | string\|number | | | `width` (number → px; strings pass through) | |
|
|
99
|
-
| `$zIndex` | number | | | `z-index` | |
|
|
100
|
-
|
|
101
|
-
**Examples**
|
|
102
|
-
```tsx
|
|
103
|
-
<GridContainer $gap={16} $padding={20} $bgColor="#f0f0f0">
|
|
104
|
-
<div>Item 1</div>
|
|
105
|
-
<div>Item 2</div>
|
|
106
|
-
</GridContainer>
|
|
107
|
-
```
|
|
108
|
-
```tsx
|
|
109
|
-
// Absolute tooltip
|
|
110
|
-
<GridContainer $position="absolute" $top={40} $left={20} $width={350} $zIndex={10}>
|
|
111
|
-
<Tooltip />
|
|
112
|
-
</GridContainer>
|
|
113
|
-
```
|
|
114
|
-
```tsx
|
|
115
|
-
// Stretch horizontally
|
|
116
|
-
<GridContainer $position="absolute" $left={0} $right={0} $bottom={0}>
|
|
117
|
-
<Footer />
|
|
118
|
-
</GridContainer>
|
|
119
|
-
```
|
|
120
|
-
```tsx
|
|
121
|
-
// Scrollable region
|
|
122
|
-
<GridContainer $padding={16} $maxHeight={400} $overflow="auto">
|
|
123
|
-
...
|
|
124
|
-
</GridContainer>
|
|
125
|
-
```
|
|
126
|
-
|
|
127
|
-
**Notes**
|
|
128
|
-
- All props are prefixed with `$` to indicate they are styled-component props
|
|
129
|
-
- Numbers are automatically converted to `px` units; strings (e.g. `'100%'`, `'fit-content'`, `'2rem'`) pass through unchanged
|
|
130
|
-
- Use `CssBoxSides` for asymmetric margin/padding/border
|
|
131
|
-
- `$inset` maps directly to the CSS `inset` shorthand; for per-side control use individual `$top` / `$right` / `$bottom` / `$left`
|
|
132
|
-
- Prefer specific layout components (Columns, Rows, Horizontal, Vertical) for common patterns
|
|
133
|
-
|
|
134
|
-
---
|
|
135
|
-
|
|
136
|
-
### Columns
|
|
137
|
-
- **Path:** `~/src/grid`
|
|
138
|
-
- **Description:** Grid container that creates equal or custom-sized columns
|
|
139
|
-
- **Interface:** `Columns<{ $sizes: StrNum; ...GridContainerProps }>`
|
|
140
|
-
|
|
141
|
-
**Props**
|
|
142
|
-
|
|
143
|
-
| Name | Type | Required | Default | Description |
|
|
144
|
-
|---|---|:---:|:---:|---|
|
|
145
|
-
| `$sizes` | string\|number | ✓ | | Number for equal columns (e.g., `3`) or string for custom sizing (e.g., `"1fr 2fr"`) |
|
|
146
|
-
|
|
147
|
-
**Examples**
|
|
148
|
-
```tsx
|
|
149
|
-
<Columns $sizes={3} $gap={8}>
|
|
150
|
-
<div>Col 1</div>
|
|
151
|
-
<div>Col 2</div>
|
|
152
|
-
<div>Col 3</div>
|
|
153
|
-
</Columns>
|
|
154
|
-
```
|
|
155
|
-
```tsx
|
|
156
|
-
<Columns $sizes="1fr 2fr" $gap={8}>
|
|
157
|
-
<div>Narrow</div>
|
|
158
|
-
<div>Wide</div>
|
|
159
|
-
</Columns>
|
|
160
|
-
```
|
|
161
|
-
|
|
162
|
-
**Notes**
|
|
163
|
-
- Extends GridContainer with all its props
|
|
164
|
-
- Number value creates equal columns using `repeat(n, 1fr)`
|
|
165
|
-
- String value is passed directly to `grid-template-columns`
|
|
166
|
-
- Ideal for responsive layouts with fixed column counts
|
|
167
|
-
|
|
168
|
-
---
|
|
169
|
-
|
|
170
|
-
### Rows
|
|
171
|
-
- **Path:** `~/src/grid`
|
|
172
|
-
- **Description:** Grid container that creates equal or custom-sized rows
|
|
173
|
-
- **Interface:** `Rows<{ $sizes: StrNum; ...GridContainerProps }>`
|
|
174
|
-
|
|
175
|
-
**Props**
|
|
176
|
-
|
|
177
|
-
| Name | Type | Required | Default | Description |
|
|
178
|
-
|---|---|:---:|:---:|---|
|
|
179
|
-
| `$sizes` | string\|number | ✓ | | Number for equal rows or string for custom sizing |
|
|
180
|
-
|
|
181
|
-
**Example**
|
|
182
|
-
```tsx
|
|
183
|
-
<Rows $sizes={3} $gap={10}>
|
|
184
|
-
<div>Row 1</div>
|
|
185
|
-
<div>Row 2</div>
|
|
186
|
-
<div>Row 3</div>
|
|
187
|
-
</Rows>
|
|
188
|
-
```
|
|
189
|
-
|
|
190
|
-
**Notes**
|
|
191
|
-
- Similar to Columns but for vertical layouts
|
|
192
|
-
- Extends GridContainer with all its props
|
|
193
|
-
- Use for vertical stacking with specific row heights
|
|
194
|
-
|
|
195
|
-
---
|
|
196
|
-
|
|
197
|
-
### Horizontal
|
|
198
|
-
- **Path:** `~/src/grid`
|
|
199
|
-
- **Description:** Grid container with automatic column flow for horizontal layouts
|
|
200
|
-
- **Interface:** `Horizontal<GridContainerProps>`
|
|
201
|
-
|
|
202
|
-
**Example**
|
|
203
|
-
```tsx
|
|
204
|
-
<Horizontal $justifyContent="start" $gap={8}>
|
|
205
|
-
<Button>One</Button>
|
|
206
|
-
<Button>Two</Button>
|
|
207
|
-
<Button>Three</Button>
|
|
208
|
-
</Horizontal>
|
|
209
|
-
```
|
|
210
|
-
|
|
211
|
-
**Notes**
|
|
212
|
-
- Pre-configured with `gridAutoFlow: 'column'`
|
|
213
|
-
- Perfect for button groups, inline elements, or navigation items
|
|
214
|
-
- Inherits all GridContainer props
|
|
215
|
-
- Items flow horizontally automatically
|
|
216
|
-
|
|
217
|
-
---
|
|
218
|
-
|
|
219
|
-
### Vertical
|
|
220
|
-
- **Path:** `~/src/grid`
|
|
221
|
-
- **Description:** Grid container with automatic row flow for vertical layouts
|
|
222
|
-
- **Interface:** `Vertical<GridContainerProps>`
|
|
223
|
-
|
|
224
|
-
**Example**
|
|
225
|
-
```tsx
|
|
226
|
-
<Vertical $gap={10}>
|
|
227
|
-
<Header />
|
|
228
|
-
<Content />
|
|
229
|
-
<Footer />
|
|
230
|
-
</Vertical>
|
|
231
|
-
```
|
|
232
|
-
|
|
233
|
-
**Notes**
|
|
234
|
-
- Pre-configured with `gridAutoFlow: 'row'`
|
|
235
|
-
- Default behavior for vertical stacking
|
|
236
|
-
- Inherits all GridContainer props
|
|
237
|
-
- Use when you need explicit vertical flow control
|
|
238
|
-
|
|
239
|
-
---
|
|
240
|
-
|
|
241
|
-
### FlexContainer
|
|
242
|
-
- **Path:** `~/src/flex`
|
|
243
|
-
- **Description:** Base flex container component using CSS Flexbox for one-dimensional layouts
|
|
244
|
-
- **Interface:**
|
|
245
|
-
```ts
|
|
246
|
-
FlexContainer<{
|
|
247
|
-
// Layout (flex-specific)
|
|
248
|
-
$flexDirection?: 'row' | 'row-reverse' | 'column' | 'column-reverse';
|
|
249
|
-
$flexWrap?: 'nowrap' | 'wrap' | 'wrap-reverse';
|
|
250
|
-
// Container alignment (shared with GridContainer)
|
|
251
|
-
$alignContent?: 'center' | 'start' | 'end' | 'space-between' | 'space-around' | 'space-evenly' | 'stretch' | 'baseline';
|
|
252
|
-
$alignItems?: 'center' | 'start' | 'end' | 'baseline' | 'stretch';
|
|
253
|
-
$gap?: StrNum;
|
|
254
|
-
$justifyContent?: 'center' | 'start' | 'end' | 'space-between' | 'space-around' | 'space-evenly' | 'stretch';
|
|
255
|
-
// Plus all CommonBoxProps: sizing ($width/$height/$min*/$max*), spacing ($padding/$margin),
|
|
256
|
-
// appearance ($bgColor/$border/$borderRadius/$boxShadow), position ($position/$top/$right/$bottom/$left/$inset/$zIndex),
|
|
257
|
-
// overflow ($overflow/$overflowX/$overflowY), flex-item ($flex/$grow/$shrink/$basis)
|
|
258
|
-
}>
|
|
259
|
-
```
|
|
260
|
-
|
|
261
|
-
**Props**
|
|
262
|
-
|
|
263
|
-
| Name | Type | Required | Default | Description | Values |
|
|
264
|
-
|---|---|:---:|:---:|---|---|
|
|
265
|
-
| `$flexDirection` | enum | | `row` (browser default) | `flex-direction` — main axis direction | `row`, `row-reverse`, `column`, `column-reverse` |
|
|
266
|
-
| `$flexWrap` | enum | | `nowrap` (browser default) | `flex-wrap` — whether items wrap onto multiple lines | `nowrap`, `wrap`, `wrap-reverse` |
|
|
267
|
-
|
|
268
|
-
Plus everything from `CommonContainerProps` (`$alignContent`, `$alignItems`, `$justifyContent`, `$gap`) and `CommonBoxProps` — see `GridContainer` props table for the full shared list. Grid-specific props (`$gridAutoFlow`, `$justifyItems`, `$placeItems`) are NOT available on `FlexContainer`.
|
|
269
|
-
|
|
270
|
-
**Examples**
|
|
271
|
-
```tsx
|
|
272
|
-
<FlexContainer $gap={8} $alignItems="center">
|
|
273
|
-
<Icon />
|
|
274
|
-
<span>Label</span>
|
|
275
|
-
</FlexContainer>
|
|
276
|
-
```
|
|
277
|
-
```tsx
|
|
278
|
-
// Column with growing child filling remaining space
|
|
279
|
-
<FlexContainer $flexDirection="column" $height="100vh">
|
|
280
|
-
<Header />
|
|
281
|
-
<Main $flex={1} $overflow="auto" />
|
|
282
|
-
<Footer />
|
|
283
|
-
</FlexContainer>
|
|
284
|
-
```
|
|
285
|
-
|
|
286
|
-
**Notes**
|
|
287
|
-
- Use when items have content-driven sizes or need to wrap; prefer `GridContainer` for explicit 2D tracks
|
|
288
|
-
- Children can use `$flex` / `$grow` / `$shrink` / `$basis` from `CommonBoxProps` to control sizing — this works on any `GridContainer`/`FlexContainer` child too
|
|
289
|
-
- Prefer the `Cluster` preset for wrap-based layouts (tags/chips)
|
|
290
|
-
|
|
291
|
-
---
|
|
292
|
-
|
|
293
|
-
### Cluster
|
|
294
|
-
- **Path:** `~/src/flex`
|
|
295
|
-
- **Description:** Flex container with wrapping enabled — for tag/chip/badge groups
|
|
296
|
-
- **Interface:** `Cluster<FlexContainerProps>`
|
|
297
|
-
|
|
298
|
-
**Example**
|
|
299
|
-
```tsx
|
|
300
|
-
<Cluster $gap={8}>
|
|
301
|
-
{tags.map((tag) => <Tag key={tag.id}>{tag.label}</Tag>)}
|
|
302
|
-
</Cluster>
|
|
303
|
-
```
|
|
304
|
-
|
|
305
|
-
**Notes**
|
|
306
|
-
- Pre-configured with `$flexWrap: 'wrap'`
|
|
307
|
-
- Inherits all `FlexContainer` props (override `$flexWrap` if needed)
|
|
308
|
-
- Perfect for chip/tag rows that need to wrap on narrow widths
|
|
309
|
-
|
|
310
|
-
---
|
|
311
|
-
|
|
312
|
-
### Collapse
|
|
313
|
-
- **Path:** `~/src/expand`
|
|
314
|
-
- **Description:** Animated collapsible component with height transition
|
|
315
|
-
- **Interface:** `Collapse<{ expanded: boolean; timeout?: number; className?: string; internalClassName?: string; children: (() => ReactNode) | ReactNode; }>`
|
|
316
|
-
|
|
317
|
-
**Props**
|
|
318
|
-
|
|
319
|
-
| Name | Type | Required | Default | Description |
|
|
320
|
-
|---|---|:---:|:---:|---|
|
|
321
|
-
| `expanded` | boolean | ✓ | | Controls whether content is visible or collapsed |
|
|
322
|
-
| `timeout` | number | | `100` | Animation duration in milliseconds |
|
|
323
|
-
| `className` | string | | | CSS class for the outer wrapper |
|
|
324
|
-
| `internalClassName` | string | | | CSS class for the inner content wrapper |
|
|
325
|
-
| `children` | function\|ReactNode | ✓ | | Content to show/hide, can be render function for lazy rendering |
|
|
326
|
-
|
|
327
|
-
**Examples**
|
|
328
|
-
```tsx
|
|
329
|
-
<Collapse expanded={isOpen}>
|
|
330
|
-
<div>Collapsible content here</div>
|
|
331
|
-
</Collapse>
|
|
332
|
-
```
|
|
333
|
-
```tsx
|
|
334
|
-
<Collapse expanded={isOpen}>
|
|
335
|
-
{() => <ExpensiveComponent />}
|
|
336
|
-
</Collapse>
|
|
337
|
-
```
|
|
338
|
-
|
|
339
|
-
**Notes**
|
|
340
|
-
- Uses `react-transition-group` for animations
|
|
341
|
-
- Height is calculated dynamically from content
|
|
342
|
-
- Function children enable lazy rendering for performance
|
|
343
|
-
- Smooth height and opacity transitions
|
|
344
|
-
- Content is removed from DOM when collapsed
|
|
345
|
-
|
|
346
|
-
---
|
|
347
|
-
|
|
348
|
-
### ExpandTransition
|
|
349
|
-
- **Path:** `~/src/expand`
|
|
350
|
-
- **Description:** Advanced expand/collapse transition with zoom effect
|
|
351
|
-
- **Interface:** `ExpandTransition<{ timeout?: number; className?: string; externalClassName?: string; internalClassName?: string; }>`
|
|
352
|
-
|
|
353
|
-
**Props**
|
|
354
|
-
|
|
355
|
-
| Name | Type | Required | Default | Description |
|
|
356
|
-
|---|---|:---:|:---:|---|
|
|
357
|
-
| `timeout` | number | | `100` | Animation duration in milliseconds |
|
|
358
|
-
| `className` | string | | | Base CSS class for transition states |
|
|
359
|
-
| `externalClassName` | string | | | Additional class for outer wrapper |
|
|
360
|
-
| `internalClassName` | string | | | Class for inner content wrapper |
|
|
361
|
-
|
|
362
|
-
**Example**
|
|
363
|
-
```tsx
|
|
364
|
-
<TransitionGroup component={null}>
|
|
365
|
-
{visible && (
|
|
366
|
-
<ExpandTransition key={0}>
|
|
367
|
-
<Card>Content</Card>
|
|
368
|
-
</ExpandTransition>
|
|
369
|
-
)}
|
|
370
|
-
</TransitionGroup>
|
|
371
|
-
```
|
|
372
|
-
|
|
373
|
-
**Notes**
|
|
374
|
-
- Must be used with `TransitionGroup` from `react-transition-group`
|
|
375
|
-
- Creates zoom-in/zoom-out effect with position animation
|
|
376
|
-
- More complex than Collapse, use for special UI effects
|
|
377
|
-
- Requires `key` prop when used in TransitionGroup
|
|
378
|
-
|
|
379
|
-
---
|
|
380
|
-
|
|
381
|
-
## Hooks
|
|
382
|
-
|
|
383
|
-
### useClickAway
|
|
384
|
-
- **Path:** `~/src/hooks/useClickAway`
|
|
385
|
-
- **Description:** Detects clicks outside a referenced element and triggers a callback
|
|
386
|
-
- **Signature:**
|
|
387
|
-
```ts
|
|
388
|
-
useClickAway<T extends HTMLElement>(
|
|
389
|
-
use: boolean,
|
|
390
|
-
ref: RefObject<T | null>,
|
|
391
|
-
cb: (event: MouseEvent | TouchEvent) => void
|
|
392
|
-
): void
|
|
393
|
-
```
|
|
394
|
-
|
|
395
|
-
**Params:** `use` – enable/disable; `ref` – target element ref; `cb` – callback on outside click
|
|
396
|
-
|
|
397
|
-
**Returns:** `void`
|
|
398
|
-
|
|
399
|
-
**Example**
|
|
400
|
-
```tsx
|
|
401
|
-
const ref = useRef<HTMLDivElement>(null);
|
|
402
|
-
useClickAway(isOpen, ref, () => setIsOpen(false));
|
|
403
|
-
```
|
|
404
|
-
|
|
405
|
-
**Notes**
|
|
406
|
-
- Handles both mouse and touch events
|
|
407
|
-
- Callback is persisted to avoid re-subscriptions
|
|
408
|
-
- Can be toggled on/off with `use` parameter
|
|
409
|
-
- Perfect for dropdowns, modals, and popovers
|
|
410
|
-
- Automatically cleans up event listeners
|
|
411
|
-
|
|
412
|
-
---
|
|
413
|
-
|
|
414
|
-
### usePersistentFunction
|
|
415
|
-
- **Path:** `~/src/hooks/usePersistentFunction`
|
|
416
|
-
- **Description:** Creates a stable function reference that always calls the latest version of a callback
|
|
417
|
-
- **Signature:**
|
|
418
|
-
```ts
|
|
419
|
-
usePersistentFunction<Args extends any[], Return>(
|
|
420
|
-
callback: (...args: Args) => Return
|
|
421
|
-
): (...args: Args) => Return
|
|
422
|
-
```
|
|
423
|
-
|
|
424
|
-
**Example**
|
|
425
|
-
```tsx
|
|
426
|
-
const stableHandler = usePersistentFunction(() => {
|
|
427
|
-
console.log(currentState); // Always uses latest state
|
|
428
|
-
});
|
|
429
|
-
```
|
|
430
|
-
|
|
431
|
-
**Notes**
|
|
432
|
-
- Prevents unnecessary re-renders in child components
|
|
433
|
-
- Always executes the latest callback version
|
|
434
|
-
- Alternative to `useCallback` with empty deps
|
|
435
|
-
- Ideal for event handlers and effect cleanup functions
|
|
436
|
-
- Maintains referential equality across renders
|
|
437
|
-
|
|
438
|
-
---
|
|
439
|
-
|
|
440
|
-
### useAsync
|
|
441
|
-
- **Path:** `~/src/hooks/useAsync`
|
|
442
|
-
- **Description:** Automatically executes an async function and manages its state
|
|
443
|
-
- **Signature:**
|
|
444
|
-
```ts
|
|
445
|
-
useAsync<T extends FunctionReturningPromise>(fn: T, deps?: DependencyList): AsyncState<T>
|
|
446
|
-
```
|
|
447
|
-
|
|
448
|
-
**Example**
|
|
449
|
-
```tsx
|
|
450
|
-
const { loading, error, value } = useAsync(
|
|
451
|
-
() => fetchUserData(userId),
|
|
452
|
-
[userId]
|
|
453
|
-
);
|
|
454
|
-
```
|
|
455
|
-
|
|
456
|
-
**Notes**
|
|
457
|
-
- Executes immediately on mount and when deps change
|
|
458
|
-
- Handles loading states automatically
|
|
459
|
-
- Catches and exposes errors
|
|
460
|
-
- Starts with `loading: true`
|
|
461
|
-
- Re-runs when dependencies change
|
|
462
|
-
|
|
463
|
-
---
|
|
464
|
-
|
|
465
|
-
### useAsyncFn
|
|
466
|
-
- **Path:** `~/src/hooks/useAsyncFn`
|
|
467
|
-
- **Description:** Returns an async function wrapper with state management
|
|
468
|
-
- **Signature:**
|
|
469
|
-
```ts
|
|
470
|
-
useAsyncFn<T extends FunctionReturningPromise>(
|
|
471
|
-
fn: T,
|
|
472
|
-
deps?: DependencyList,
|
|
473
|
-
initialState?: AsyncState<T>
|
|
474
|
-
): [AsyncState<T>, (p?: CallbackParams) => ReturnType<T>]
|
|
475
|
-
```
|
|
476
|
-
|
|
477
|
-
**Example**
|
|
478
|
-
```tsx
|
|
479
|
-
const [state, doFetch] = useAsyncFn(
|
|
480
|
-
() => api.saveData(data),
|
|
481
|
-
[data]
|
|
482
|
-
);
|
|
483
|
-
// Later: doFetch({ reset: true });
|
|
484
|
-
```
|
|
485
|
-
|
|
486
|
-
**Notes**
|
|
487
|
-
- Manual trigger unlike `useAsync`
|
|
488
|
-
- Returns both state and executable function
|
|
489
|
-
- Supports `reset` option to clear previous state
|
|
490
|
-
- Prevents race conditions with call tracking
|
|
491
|
-
- Safe to use with unmounted components
|
|
492
|
-
|
|
493
|
-
---
|
|
494
|
-
|
|
495
|
-
### useAsyncFunction
|
|
496
|
-
- **Path:** `~/src/hooks/useAsyncFunction`
|
|
497
|
-
- **Description:** Creates a persistent async function with state tracking and call counting
|
|
498
|
-
- **Signature:**
|
|
499
|
-
```ts
|
|
500
|
-
useAsyncFunction<T extends FunctionReturningPromise>(fn: T): [AsyncFunctionState<T>, T]
|
|
501
|
-
```
|
|
502
|
-
|
|
503
|
-
**Example**
|
|
504
|
-
```tsx
|
|
505
|
-
const [state, saveUser] = useAsyncFunction(async (user) => {
|
|
506
|
-
return api.saveUser(user);
|
|
507
|
-
});
|
|
508
|
-
// state.count tracks number of calls
|
|
509
|
-
```
|
|
510
|
-
|
|
511
|
-
**Notes**
|
|
512
|
-
- Includes call counter in state
|
|
513
|
-
- Function reference is always stable
|
|
514
|
-
- Tracks loading state per call
|
|
515
|
-
- Useful for forms and repeated actions
|
|
516
|
-
- Prevents state updates on unmounted components
|
|
517
|
-
|
|
518
|
-
---
|
|
519
|
-
|
|
520
|
-
### useEffectOnce
|
|
521
|
-
- **Path:** `~/src/hooks/useEffectOnce`
|
|
522
|
-
- **Description:** Runs an effect only once on mount
|
|
523
|
-
- **Signature:** `useEffectOnce(effect: EffectCallback): void`
|
|
524
|
-
|
|
525
|
-
**Example**
|
|
526
|
-
```tsx
|
|
527
|
-
useEffectOnce(() => {
|
|
528
|
-
initializeApp();
|
|
529
|
-
return () => cleanup();
|
|
530
|
-
});
|
|
531
|
-
```
|
|
532
|
-
|
|
533
|
-
**Notes**
|
|
534
|
-
- Equivalent to `useEffect` with empty deps
|
|
535
|
-
- Cleaner API for one-time setup
|
|
536
|
-
- Supports cleanup function return
|
|
537
|
-
- Use for initialization logic
|
|
538
|
-
|
|
539
|
-
---
|
|
540
|
-
|
|
541
|
-
### useMount
|
|
542
|
-
- **Path:** `~/src/hooks/useMount`
|
|
543
|
-
- **Description:** Runs a function on component mount
|
|
544
|
-
- **Signature:** `useMount(fn: () => void): void`
|
|
545
|
-
|
|
546
|
-
**Example**
|
|
547
|
-
```tsx
|
|
548
|
-
useMount(() => {
|
|
549
|
-
console.log('Component mounted');
|
|
550
|
-
});
|
|
551
|
-
```
|
|
552
|
-
|
|
553
|
-
**Notes**
|
|
554
|
-
- Simpler than `useEffectOnce` when no cleanup needed
|
|
555
|
-
- Only for side effects on mount
|
|
556
|
-
- No return value expected
|
|
557
|
-
|
|
558
|
-
---
|
|
559
|
-
|
|
560
|
-
### useUnmount
|
|
561
|
-
- **Path:** `~/src/hooks/useUnmount`
|
|
562
|
-
- **Description:** Runs a function on component unmount
|
|
563
|
-
- **Signature:** `useUnmount(fn: () => any): void`
|
|
564
|
-
|
|
565
|
-
**Example**
|
|
566
|
-
```tsx
|
|
567
|
-
useUnmount(() => {
|
|
568
|
-
saveProgress();
|
|
569
|
-
disconnectSocket();
|
|
570
|
-
});
|
|
571
|
-
```
|
|
572
|
-
|
|
573
|
-
**Notes**
|
|
574
|
-
- Always uses latest version of cleanup function
|
|
575
|
-
- Perfect for cleanup operations
|
|
576
|
-
- Saves form data, closes connections, etc.
|
|
577
|
-
- Function ref is updated each render
|
|
578
|
-
|
|
579
|
-
---
|
|
580
|
-
|
|
581
|
-
### useMountedState
|
|
582
|
-
- **Path:** `~/src/hooks/useMountedState`
|
|
583
|
-
- **Description:** Returns a function that checks if component is still mounted
|
|
584
|
-
- **Signature:** `useMountedState(): () => boolean`
|
|
585
|
-
|
|
586
|
-
**Example**
|
|
587
|
-
```tsx
|
|
588
|
-
const isMounted = useMountedState();
|
|
589
|
-
|
|
590
|
-
setTimeout(() => {
|
|
591
|
-
if (isMounted()) {
|
|
592
|
-
setState(newValue);
|
|
593
|
-
}
|
|
594
|
-
}, 1000);
|
|
595
|
-
```
|
|
596
|
-
|
|
597
|
-
**Notes**
|
|
598
|
-
- Prevents state updates on unmounted components
|
|
599
|
-
- Essential for async operations
|
|
600
|
-
- Check before `setState` in callbacks
|
|
601
|
-
- Stable function reference
|
|
602
|
-
|
|
603
|
-
---
|
|
604
|
-
|
|
605
|
-
### useDelayedValue
|
|
606
|
-
- **Path:** `~/src/hooks/useDelayedValue`
|
|
607
|
-
- **Description:** Delays value updates by specified milliseconds
|
|
608
|
-
- **Signature:** `useDelayedValue<T>(value: T, delay: number): T`
|
|
609
|
-
|
|
610
|
-
**Example**
|
|
611
|
-
```tsx
|
|
612
|
-
const debouncedSearch = useDelayedValue(searchTerm, 300);
|
|
613
|
-
useEffect(() => {
|
|
614
|
-
performSearch(debouncedSearch);
|
|
615
|
-
}, [debouncedSearch]);
|
|
616
|
-
```
|
|
617
|
-
|
|
618
|
-
**Notes**
|
|
619
|
-
- Acts as a debounce for value changes
|
|
620
|
-
- Useful for search inputs and API calls
|
|
621
|
-
- Cancels pending updates on new values
|
|
622
|
-
- Returns initial value immediately
|
|
623
|
-
|
|
624
|
-
---
|
|
625
|
-
|
|
626
|
-
### useDelayedEffect
|
|
627
|
-
- **Path:** `~/src/hooks/useDelayedEffect`
|
|
628
|
-
- **Description:** Runs an effect after a delay with cancellation support
|
|
629
|
-
- **Signature:**
|
|
630
|
-
```ts
|
|
631
|
-
useDelayedEffect(
|
|
632
|
-
effect: (getIsRunning: () => boolean) => void,
|
|
633
|
-
deps: DependencyList,
|
|
634
|
-
delay: number
|
|
635
|
-
): void
|
|
636
|
-
```
|
|
637
|
-
|
|
638
|
-
**Example**
|
|
639
|
-
```tsx
|
|
640
|
-
useDelayedEffect(
|
|
641
|
-
(getIsRunning) => {
|
|
642
|
-
if (getIsRunning()) {
|
|
643
|
-
performExpensiveOperation();
|
|
644
|
-
}
|
|
645
|
-
},
|
|
646
|
-
[data],
|
|
647
|
-
500
|
|
648
|
-
);
|
|
649
|
-
```
|
|
650
|
-
|
|
651
|
-
**Notes**
|
|
652
|
-
- Provides cancellation check via `getIsRunning`
|
|
653
|
-
- Clears timeout on deps change or unmount
|
|
654
|
-
- Useful for debounced side effects
|
|
655
|
-
- Check `getIsRunning()` before state updates
|
|
656
|
-
|
|
657
|
-
---
|
|
658
|
-
|
|
659
|
-
## Utilities
|
|
660
|
-
|
|
661
|
-
### formatNumber
|
|
662
|
-
- **Path:** `~/src/formatNumber`
|
|
663
|
-
- **Description:** Formats numbers with spacing or short notation
|
|
664
|
-
- **Signature:** `formatNumber(number: number, format: 'spaced' | 'short'): string`
|
|
665
|
-
|
|
666
|
-
**Examples**
|
|
667
|
-
```ts
|
|
668
|
-
formatNumber(1234567, 'spaced') // '1 234 567'
|
|
669
|
-
formatNumber(1234567, 'short') // '1.2M'
|
|
670
|
-
formatNumber(999, 'short') // '999'
|
|
671
|
-
```
|
|
672
|
-
|
|
673
|
-
**Notes**
|
|
674
|
-
- `spaced`: Adds spaces every 3 digits
|
|
675
|
-
- `short`: Uses K, M, B notation above 10,000
|
|
676
|
-
- Short format keeps one decimal place
|
|
677
|
-
- Numbers below 10K in short format use spaced display
|
|
678
|
-
- Ideal for statistics and dashboards
|
|
679
|
-
|
|
680
|
-
---
|
|
681
|
-
|
|
682
|
-
### toCssValue
|
|
683
|
-
- **Path:** `~/src/styled-helpers` (also re-exported from `~/src/grid` for back-compat)
|
|
684
|
-
- **Description:** Converts number to px string or returns string as-is
|
|
685
|
-
- **Signature:** `toCssValue(value: string | number): string`
|
|
686
|
-
|
|
687
|
-
**Examples**
|
|
688
|
-
```ts
|
|
689
|
-
toCssValue(16) // '16px'
|
|
690
|
-
toCssValue('2rem') // '2rem'
|
|
691
|
-
```
|
|
692
|
-
|
|
693
|
-
**Notes**
|
|
694
|
-
- Used internally by grid and flex components
|
|
695
|
-
- Numbers become pixels
|
|
696
|
-
- Strings pass through unchanged
|
|
697
|
-
- Enables flexible prop values
|
|
698
|
-
|
|
699
|
-
---
|
|
700
|
-
|
|
701
|
-
### elementAddListeners
|
|
702
|
-
- **Path:** `~/src/hooks/useClickAway`
|
|
703
|
-
- **Description:** Adds event listeners to elements with cleanup function
|
|
704
|
-
- **Signature:**
|
|
705
|
-
```ts
|
|
706
|
-
elementAddListeners<
|
|
707
|
-
K extends keyof HTMLElementEventMap,
|
|
708
|
-
E extends HTMLElement
|
|
709
|
-
>(element: E | Document, eventNames: K | K[], listener: EventListener): () => void
|
|
710
|
-
```
|
|
711
|
-
|
|
712
|
-
**Example**
|
|
713
|
-
```ts
|
|
714
|
-
const cleanup = elementAddListeners(
|
|
715
|
-
document,
|
|
716
|
-
['mousedown', 'touchstart'],
|
|
717
|
-
handleClick
|
|
718
|
-
);
|
|
719
|
-
```
|
|
720
|
-
|
|
721
|
-
**Notes**
|
|
722
|
-
- Returns cleanup function
|
|
723
|
-
- Supports multiple event types
|
|
724
|
-
- Works with `Document` or `HTMLElement`
|
|
725
|
-
- Used internally by `useClickAway`
|
|
726
|
-
|
|
727
|
-
---
|
|
728
|
-
|
|
729
|
-
### subscribeClickAway
|
|
730
|
-
- **Path:** `~/src/hooks/useClickAway`
|
|
731
|
-
- **Description:** Subscribes to clicks outside an element
|
|
732
|
-
- **Signature:**
|
|
733
|
-
```typescript
|
|
734
|
-
subscribeClickAway<T extends HTMLElement>(
|
|
735
|
-
element: T,
|
|
736
|
-
callback: (event: MouseEvent | TouchEvent) => void
|
|
737
|
-
): () => void
|
|
738
|
-
```
|
|
739
|
-
|
|
740
|
-
**Example**
|
|
741
|
-
```ts
|
|
742
|
-
const unsubscribe = subscribeClickAway(dropdownEl, () => closeDropdown());
|
|
743
|
-
```
|
|
744
|
-
|
|
745
|
-
**Notes**
|
|
746
|
-
- Standalone version of `useClickAway` logic
|
|
747
|
-
- Returns unsubscribe function
|
|
748
|
-
- Handles mouse and touch events
|
|
749
|
-
- Can be used outside React components
|
|
750
|
-
|
|
751
|
-
---
|
|
752
|
-
|
|
753
|
-
## Types
|
|
754
|
-
|
|
755
|
-
### StrNum
|
|
756
|
-
- **Definition:** `type StrNum = string | number`
|
|
757
|
-
- **Description:** Union type for CSS values that can be string or number
|
|
758
|
-
|
|
759
|
-
### CssBoxSides
|
|
760
|
-
- **Definition:**
|
|
761
|
-
```ts
|
|
762
|
-
type CssBoxSides<T> =
|
|
763
|
-
| { top?: T; right?: T; bottom?: T; left?: T }
|
|
764
|
-
| { horizontal: T; top?: T; bottom?: T }
|
|
765
|
-
| { vertical: T; right?: T; left?: T }
|
|
766
|
-
```
|
|
767
|
-
- **Description:** Flexible type for directional CSS properties like margin/padding
|
|
768
|
-
|
|
769
|
-
### CommonBoxProps
|
|
770
|
-
- **Path:** `~/src/styled-helpers`
|
|
771
|
-
- **Description:** Shared box-model/positioning/flex-item props consumed by both `GridContainer` and `FlexContainer`. Covers sizing (`$width`, `$height`, `$minWidth`/`$minHeight`, `$maxWidth`/`$maxHeight`), spacing (`$padding`, `$margin`), appearance (`$bgColor`, `$border`, `$borderRadius`, `$boxShadow`), position (`$position`, `$top`/`$right`/`$bottom`/`$left`, `$inset`, `$zIndex`), overflow (`$overflow`/`$overflowX`/`$overflowY`), and flex-item sizing (`$flex`, `$grow`, `$shrink`, `$basis`)
|
|
772
|
-
|
|
773
|
-
### CommonContainerProps
|
|
774
|
-
- **Path:** `~/src/styled-helpers`
|
|
775
|
-
- **Definition:** `CommonBoxProps & { $alignContent?: AlignContentValue; $alignItems?: ItemAlignmentValue; $gap?: StrNum; $justifyContent?: JustifyContentValue }`
|
|
776
|
-
- **Description:** Extends `CommonBoxProps` with alignment props that work in both grid and flex contexts. Used as the base for `GridContainer` and `FlexContainer` props
|
|
777
|
-
|
|
778
|
-
### ItemAlignmentValue
|
|
779
|
-
- **Path:** `~/src/styled-helpers`
|
|
780
|
-
- **Definition:** `'center' | 'start' | 'end' | 'baseline' | 'stretch'`
|
|
781
|
-
- **Description:** Valid values for item-level alignment props: `$alignItems`, `$justifyItems`, `$placeItems`
|
|
782
|
-
|
|
783
|
-
### JustifyContentValue
|
|
784
|
-
- **Path:** `~/src/styled-helpers`
|
|
785
|
-
- **Definition:** `'center' | 'start' | 'end' | 'space-between' | 'space-around' | 'space-evenly' | 'stretch'`
|
|
786
|
-
- **Description:** Valid values for `$justifyContent`. Per CSS Box Alignment L3, `justify-content` does NOT accept `baseline` — use `AlignContentValue` for `$alignContent`
|
|
787
|
-
|
|
788
|
-
### AlignContentValue
|
|
789
|
-
- **Path:** `~/src/styled-helpers`
|
|
790
|
-
- **Definition:** `JustifyContentValue | 'baseline'`
|
|
791
|
-
- **Description:** Valid values for `$alignContent`. Same as `JustifyContentValue` plus `baseline` (which is spec-valid only for the cross-axis content distribution)
|
|
792
|
-
|
|
793
|
-
### AsyncState
|
|
794
|
-
- **Definition:** `type AsyncState<T> = { loading: boolean; error?: Error; value?: T }`
|
|
795
|
-
- **Description:** State shape for async operations with loading, error, and value
|
|
796
|
-
|
|
797
|
-
### FunctionReturningPromise
|
|
798
|
-
- **Definition:** `type FunctionReturningPromise = (...args: any[]) => Promise<any>`
|
|
799
|
-
- **Description:** Generic type for any async function
|
|
800
|
-
|
|
801
|
-
### PromiseType
|
|
802
|
-
- **Definition:** `type PromiseType<P extends Promise<any>> = P extends Promise<infer T> ? T : never`
|
|
803
|
-
- **Description:** Extracts the resolved type from a Promise
|
|
804
|
-
|
|
805
|
-
---
|
|
806
|
-
|
|
807
|
-
## Patterns
|
|
808
|
-
|
|
809
|
-
### styledComponentProps
|
|
810
|
-
- **Description:** All styled-component props are prefixed with `$` to avoid React prop warnings
|
|
811
|
-
- **Example:** `$gap, $padding, $margin` instead of `gap, padding, margin`
|
|
812
|
-
|
|
813
|
-
### lazyRendering
|
|
814
|
-
- **Description:** Components accept function children for deferred rendering
|
|
815
|
-
- **Example:** `<Collapse expanded={show}>{() => <ExpensiveComponent />}</Collapse>`
|
|
816
|
-
|
|
817
|
-
### persistentCallbacks
|
|
818
|
-
- **Description:** Use `usePersistentFunction` to maintain stable references while using latest closure values
|
|
819
|
-
- **Example:** `const stable = usePersistentFunction(callback)`
|
|
820
|
-
|
|
821
|
-
### asyncStateManagement
|
|
822
|
-
- **Description:** Consistent async state shape with loading, error, and value properties
|
|
823
|
-
- **Example:** All async hooks return `{ loading, error, value }` state
|
|
824
|
-
|
|
825
|
-
---
|
|
826
|
-
|
|
827
|
-
## Best Practices
|
|
828
|
-
- Use specific layout components (Columns, Horizontal, Vertical) over generic GridContainer
|
|
829
|
-
- Use the `Cluster` preset for wrap-based layouts; reach for `FlexContainer` for everything else
|
|
830
|
-
- Prefer `FlexContainer` over `GridContainer` when item sizes are content-driven or you need wrapping; prefer Grid when you have explicit row/column tracks
|
|
831
|
-
- Set `$flex` / `$grow` / `$shrink` / `$basis` on any `GridContainer`/`FlexContainer` child to control its size inside a flex parent — no extra wrapper needed
|
|
832
|
-
- Prefix all styled-component props with `$` to avoid DOM prop warnings
|
|
833
|
-
- Use `usePersistentFunction` for callbacks passed to memoized components
|
|
834
|
-
- Check `isMounted()` before state updates in async operations
|
|
835
|
-
- Use function children in `Collapse` for expensive components
|
|
836
|
-
- Leverage TypeScript's discriminated unions in `AsyncState` for type safety
|
|
837
|
-
- Use `useDelayedValue` for debouncing user input
|
|
838
|
-
- Combine `useClickAway` with portals for robust dropdown behavior
|