@pushwoosh/kit-helpers 4.8.3 → 4.8.5
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/ai-documentation.mdx +681 -0
- package/hooks/useClickAway.d.ts +27 -3
- package/hooks/useClickAway.js +41 -10
- package/package.json +2 -2
|
@@ -0,0 +1,681 @@
|
|
|
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 is generated from `ai-documentation.json` and 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';
|
|
28
|
+
$alignItems?: 'center' | 'start' | 'end' | 'baseline' | 'stretch';
|
|
29
|
+
$bgColor?: string;
|
|
30
|
+
$border?: string | CssBoxSides<string>;
|
|
31
|
+
$borderRadius?: StrNum;
|
|
32
|
+
$boxShadow?: string;
|
|
33
|
+
$gap?: StrNum;
|
|
34
|
+
$gridAutoFlow?: 'row' | 'column' | 'dense' | 'row dense' | 'column dense';
|
|
35
|
+
$justifyContent?: 'center' | 'start' | 'end' | 'space-between' | 'space-around' | 'space-evenly' | 'stretch';
|
|
36
|
+
$justifyItems?: 'center' | 'start' | 'end' | 'baseline' | 'stretch';
|
|
37
|
+
$margin?: StrNum | CssBoxSides<StrNum>;
|
|
38
|
+
$padding?: StrNum | CssBoxSides<StrNum>;
|
|
39
|
+
$placeItems?: 'center' | 'start' | 'end' | 'stretch';
|
|
40
|
+
}>
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
**Props**
|
|
44
|
+
|
|
45
|
+
| Name | Type | Required | Default | Description | Values |
|
|
46
|
+
|---|---|:---:|:---:|---|---|
|
|
47
|
+
| `$alignContent` | enum | | | Aligns the grid content along the cross axis | `center`, `start`, `end`, `space-between`, `space-around`, `space-evenly`, `stretch` |
|
|
48
|
+
| `$alignItems` | enum | | | Aligns grid items along the cross axis | `center`, `start`, `end`, `baseline`, `stretch` |
|
|
49
|
+
| `$bgColor` | string | | | Background color of the grid container | |
|
|
50
|
+
| `$border` | object | | | Border styling, can be string or object with top/right/bottom/left properties | |
|
|
51
|
+
| `$borderRadius` | string\|number | | | Border radius, accepts number (converted to px) or string | |
|
|
52
|
+
| `$boxShadow` | string | | | CSS box shadow property | |
|
|
53
|
+
| `$gap` | string\|number | | | Gap between grid items, accepts number (converted to px) or string | |
|
|
54
|
+
| `$gridAutoFlow` | enum | | | Controls how auto-placed items are inserted in the grid | `row`, `column`, `dense`, `row dense`, `column dense` |
|
|
55
|
+
| `$justifyContent` | enum | | | Aligns the grid content along the main axis | `center`, `start`, `end`, `space-between`, `space-around`, `space-evenly`, `stretch` |
|
|
56
|
+
| `$justifyItems` | enum | | | Aligns grid items along the main axis | `center`, `start`, `end`, `baseline`, `stretch` |
|
|
57
|
+
| `$margin` | object | | | Margin spacing, can be number/string or object with directional properties | |
|
|
58
|
+
| `$padding` | object | | | Padding spacing, can be number/string or object with directional properties | |
|
|
59
|
+
| `$placeItems` | enum | | | Shorthand for align-items and justify-items | `center`, `start`, `end`, `stretch` |
|
|
60
|
+
|
|
61
|
+
**Example**
|
|
62
|
+
```tsx
|
|
63
|
+
<GridContainer $gap={16} $padding={20} $bgColor="#f0f0f0">
|
|
64
|
+
<div>Item 1</div>
|
|
65
|
+
<div>Item 2</div>
|
|
66
|
+
</GridContainer>
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
**Notes**
|
|
70
|
+
- All props are prefixed with `$` to indicate they are styled-component props
|
|
71
|
+
- Numbers are automatically converted to `px` units
|
|
72
|
+
- Use `CssBoxSides` for asymmetric margin/padding/border
|
|
73
|
+
- Prefer specific layout components (Columns, Rows, Horizontal, Vertical) for common patterns
|
|
74
|
+
|
|
75
|
+
---
|
|
76
|
+
|
|
77
|
+
### Columns
|
|
78
|
+
- **Path:** `~/src/grid`
|
|
79
|
+
- **Description:** Grid container that creates equal or custom-sized columns
|
|
80
|
+
- **Interface:** `Columns<{ $sizes: StrNum; ...GridContainerProps }>`
|
|
81
|
+
|
|
82
|
+
**Props**
|
|
83
|
+
|
|
84
|
+
| Name | Type | Required | Default | Description |
|
|
85
|
+
|---|---|:---:|:---:|---|
|
|
86
|
+
| `$sizes` | string\|number | ✓ | | Number for equal columns (e.g., `3`) or string for custom sizing (e.g., `"1fr 2fr"`) |
|
|
87
|
+
|
|
88
|
+
**Examples**
|
|
89
|
+
```tsx
|
|
90
|
+
<Columns $sizes={3} $gap={8}>
|
|
91
|
+
<div>Col 1</div>
|
|
92
|
+
<div>Col 2</div>
|
|
93
|
+
<div>Col 3</div>
|
|
94
|
+
</Columns>
|
|
95
|
+
```
|
|
96
|
+
```tsx
|
|
97
|
+
<Columns $sizes="1fr 2fr" $gap={8}>
|
|
98
|
+
<div>Narrow</div>
|
|
99
|
+
<div>Wide</div>
|
|
100
|
+
</Columns>
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
**Notes**
|
|
104
|
+
- Extends GridContainer with all its props
|
|
105
|
+
- Number value creates equal columns using `repeat(n, 1fr)`
|
|
106
|
+
- String value is passed directly to `grid-template-columns`
|
|
107
|
+
- Ideal for responsive layouts with fixed column counts
|
|
108
|
+
|
|
109
|
+
---
|
|
110
|
+
|
|
111
|
+
### Rows
|
|
112
|
+
- **Path:** `~/src/grid`
|
|
113
|
+
- **Description:** Grid container that creates equal or custom-sized rows
|
|
114
|
+
- **Interface:** `Rows<{ $sizes: StrNum; ...GridContainerProps }>`
|
|
115
|
+
|
|
116
|
+
**Props**
|
|
117
|
+
|
|
118
|
+
| Name | Type | Required | Default | Description |
|
|
119
|
+
|---|---|:---:|:---:|---|
|
|
120
|
+
| `$sizes` | string\|number | ✓ | | Number for equal rows or string for custom sizing |
|
|
121
|
+
|
|
122
|
+
**Example**
|
|
123
|
+
```tsx
|
|
124
|
+
<Rows $sizes={3} $gap={10}>
|
|
125
|
+
<div>Row 1</div>
|
|
126
|
+
<div>Row 2</div>
|
|
127
|
+
<div>Row 3</div>
|
|
128
|
+
</Rows>
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
**Notes**
|
|
132
|
+
- Similar to Columns but for vertical layouts
|
|
133
|
+
- Extends GridContainer with all its props
|
|
134
|
+
- Use for vertical stacking with specific row heights
|
|
135
|
+
|
|
136
|
+
---
|
|
137
|
+
|
|
138
|
+
### Horizontal
|
|
139
|
+
- **Path:** `~/src/grid`
|
|
140
|
+
- **Description:** Grid container with automatic column flow for horizontal layouts
|
|
141
|
+
- **Interface:** `Horizontal<GridContainerProps>`
|
|
142
|
+
|
|
143
|
+
**Example**
|
|
144
|
+
```tsx
|
|
145
|
+
<Horizontal $justifyContent="start" $gap={8}>
|
|
146
|
+
<Button>One</Button>
|
|
147
|
+
<Button>Two</Button>
|
|
148
|
+
<Button>Three</Button>
|
|
149
|
+
</Horizontal>
|
|
150
|
+
```
|
|
151
|
+
|
|
152
|
+
**Notes**
|
|
153
|
+
- Pre-configured with `gridAutoFlow: 'column'`
|
|
154
|
+
- Perfect for button groups, inline elements, or navigation items
|
|
155
|
+
- Inherits all GridContainer props
|
|
156
|
+
- Items flow horizontally automatically
|
|
157
|
+
|
|
158
|
+
---
|
|
159
|
+
|
|
160
|
+
### Vertical
|
|
161
|
+
- **Path:** `~/src/grid`
|
|
162
|
+
- **Description:** Grid container with automatic row flow for vertical layouts
|
|
163
|
+
- **Interface:** `Vertical<GridContainerProps>`
|
|
164
|
+
|
|
165
|
+
**Example**
|
|
166
|
+
```tsx
|
|
167
|
+
<Vertical $gap={10}>
|
|
168
|
+
<Header />
|
|
169
|
+
<Content />
|
|
170
|
+
<Footer />
|
|
171
|
+
</Vertical>
|
|
172
|
+
```
|
|
173
|
+
|
|
174
|
+
**Notes**
|
|
175
|
+
- Pre-configured with `gridAutoFlow: 'row'`
|
|
176
|
+
- Default behavior for vertical stacking
|
|
177
|
+
- Inherits all GridContainer props
|
|
178
|
+
- Use when you need explicit vertical flow control
|
|
179
|
+
|
|
180
|
+
---
|
|
181
|
+
|
|
182
|
+
### Collapse
|
|
183
|
+
- **Path:** `~/src/expand`
|
|
184
|
+
- **Description:** Animated collapsible component with height transition
|
|
185
|
+
- **Interface:** `Collapse<{ expanded: boolean; timeout?: number; className?: string; internalClassName?: string; children: (() => ReactNode) | ReactNode; }>`
|
|
186
|
+
|
|
187
|
+
**Props**
|
|
188
|
+
|
|
189
|
+
| Name | Type | Required | Default | Description |
|
|
190
|
+
|---|---|:---:|:---:|---|
|
|
191
|
+
| `expanded` | boolean | ✓ | | Controls whether content is visible or collapsed |
|
|
192
|
+
| `timeout` | number | | `100` | Animation duration in milliseconds |
|
|
193
|
+
| `className` | string | | | CSS class for the outer wrapper |
|
|
194
|
+
| `internalClassName` | string | | | CSS class for the inner content wrapper |
|
|
195
|
+
| `children` | function\|ReactNode | ✓ | | Content to show/hide, can be render function for lazy rendering |
|
|
196
|
+
|
|
197
|
+
**Examples**
|
|
198
|
+
```tsx
|
|
199
|
+
<Collapse expanded={isOpen}>
|
|
200
|
+
<div>Collapsible content here</div>
|
|
201
|
+
</Collapse>
|
|
202
|
+
```
|
|
203
|
+
```tsx
|
|
204
|
+
<Collapse expanded={isOpen}>
|
|
205
|
+
{() => <ExpensiveComponent />}
|
|
206
|
+
</Collapse>
|
|
207
|
+
```
|
|
208
|
+
|
|
209
|
+
**Notes**
|
|
210
|
+
- Uses `react-transition-group` for animations
|
|
211
|
+
- Height is calculated dynamically from content
|
|
212
|
+
- Function children enable lazy rendering for performance
|
|
213
|
+
- Smooth height and opacity transitions
|
|
214
|
+
- Content is removed from DOM when collapsed
|
|
215
|
+
|
|
216
|
+
---
|
|
217
|
+
|
|
218
|
+
### ExpandTransition
|
|
219
|
+
- **Path:** `~/src/expand`
|
|
220
|
+
- **Description:** Advanced expand/collapse transition with zoom effect
|
|
221
|
+
- **Interface:** `ExpandTransition<{ timeout?: number; className?: string; externalClassName?: string; internalClassName?: string; }>`
|
|
222
|
+
|
|
223
|
+
**Props**
|
|
224
|
+
|
|
225
|
+
| Name | Type | Required | Default | Description |
|
|
226
|
+
|---|---|:---:|:---:|---|
|
|
227
|
+
| `timeout` | number | | `100` | Animation duration in milliseconds |
|
|
228
|
+
| `className` | string | | | Base CSS class for transition states |
|
|
229
|
+
| `externalClassName` | string | | | Additional class for outer wrapper |
|
|
230
|
+
| `internalClassName` | string | | | Class for inner content wrapper |
|
|
231
|
+
|
|
232
|
+
**Example**
|
|
233
|
+
```tsx
|
|
234
|
+
<TransitionGroup component={null}>
|
|
235
|
+
{visible && (
|
|
236
|
+
<ExpandTransition key={0}>
|
|
237
|
+
<Card>Content</Card>
|
|
238
|
+
</ExpandTransition>
|
|
239
|
+
)}
|
|
240
|
+
</TransitionGroup>
|
|
241
|
+
```
|
|
242
|
+
|
|
243
|
+
**Notes**
|
|
244
|
+
- Must be used with `TransitionGroup` from `react-transition-group`
|
|
245
|
+
- Creates zoom-in/zoom-out effect with position animation
|
|
246
|
+
- More complex than Collapse, use for special UI effects
|
|
247
|
+
- Requires `key` prop when used in TransitionGroup
|
|
248
|
+
|
|
249
|
+
---
|
|
250
|
+
|
|
251
|
+
## Hooks
|
|
252
|
+
|
|
253
|
+
### useClickAway
|
|
254
|
+
- **Path:** `~/src/hooks/useClickAway`
|
|
255
|
+
- **Description:** Detects clicks outside a referenced element and triggers a callback
|
|
256
|
+
- **Signature:**
|
|
257
|
+
```ts
|
|
258
|
+
useClickAway<T extends HTMLElement>(
|
|
259
|
+
use: boolean,
|
|
260
|
+
ref: RefObject<T | null>,
|
|
261
|
+
cb: (event: MouseEvent | TouchEvent) => void
|
|
262
|
+
): void
|
|
263
|
+
```
|
|
264
|
+
|
|
265
|
+
**Params:** `use` – enable/disable; `ref` – target element ref; `cb` – callback on outside click
|
|
266
|
+
|
|
267
|
+
**Returns:** `void`
|
|
268
|
+
|
|
269
|
+
**Example**
|
|
270
|
+
```tsx
|
|
271
|
+
const ref = useRef<HTMLDivElement>(null);
|
|
272
|
+
useClickAway(isOpen, ref, () => setIsOpen(false));
|
|
273
|
+
```
|
|
274
|
+
|
|
275
|
+
**Notes**
|
|
276
|
+
- Handles both mouse and touch events
|
|
277
|
+
- Callback is persisted to avoid re-subscriptions
|
|
278
|
+
- Can be toggled on/off with `use` parameter
|
|
279
|
+
- Perfect for dropdowns, modals, and popovers
|
|
280
|
+
- Automatically cleans up event listeners
|
|
281
|
+
|
|
282
|
+
---
|
|
283
|
+
|
|
284
|
+
### usePersistentFunction
|
|
285
|
+
- **Path:** `~/src/hooks/usePersistentFunction`
|
|
286
|
+
- **Description:** Creates a stable function reference that always calls the latest version of a callback
|
|
287
|
+
- **Signature:**
|
|
288
|
+
```ts
|
|
289
|
+
usePersistentFunction<Args extends any[], Return>(
|
|
290
|
+
callback: (...args: Args) => Return
|
|
291
|
+
): (...args: Args) => Return
|
|
292
|
+
```
|
|
293
|
+
|
|
294
|
+
**Example**
|
|
295
|
+
```tsx
|
|
296
|
+
const stableHandler = usePersistentFunction(() => {
|
|
297
|
+
console.log(currentState); // Always uses latest state
|
|
298
|
+
});
|
|
299
|
+
```
|
|
300
|
+
|
|
301
|
+
**Notes**
|
|
302
|
+
- Prevents unnecessary re-renders in child components
|
|
303
|
+
- Always executes the latest callback version
|
|
304
|
+
- Alternative to `useCallback` with empty deps
|
|
305
|
+
- Ideal for event handlers and effect cleanup functions
|
|
306
|
+
- Maintains referential equality across renders
|
|
307
|
+
|
|
308
|
+
---
|
|
309
|
+
|
|
310
|
+
### useAsync
|
|
311
|
+
- **Path:** `~/src/hooks/useAsync`
|
|
312
|
+
- **Description:** Automatically executes an async function and manages its state
|
|
313
|
+
- **Signature:**
|
|
314
|
+
```ts
|
|
315
|
+
useAsync<T extends FunctionReturningPromise>(fn: T, deps?: DependencyList): AsyncState<T>
|
|
316
|
+
```
|
|
317
|
+
|
|
318
|
+
**Example**
|
|
319
|
+
```tsx
|
|
320
|
+
const { loading, error, value } = useAsync(
|
|
321
|
+
() => fetchUserData(userId),
|
|
322
|
+
[userId]
|
|
323
|
+
);
|
|
324
|
+
```
|
|
325
|
+
|
|
326
|
+
**Notes**
|
|
327
|
+
- Executes immediately on mount and when deps change
|
|
328
|
+
- Handles loading states automatically
|
|
329
|
+
- Catches and exposes errors
|
|
330
|
+
- Starts with `loading: true`
|
|
331
|
+
- Re-runs when dependencies change
|
|
332
|
+
|
|
333
|
+
---
|
|
334
|
+
|
|
335
|
+
### useAsyncFn
|
|
336
|
+
- **Path:** `~/src/hooks/useAsyncFn`
|
|
337
|
+
- **Description:** Returns an async function wrapper with state management
|
|
338
|
+
- **Signature:**
|
|
339
|
+
```ts
|
|
340
|
+
useAsyncFn<T extends FunctionReturningPromise>(
|
|
341
|
+
fn: T,
|
|
342
|
+
deps?: DependencyList,
|
|
343
|
+
initialState?: AsyncState<T>
|
|
344
|
+
): [AsyncState<T>, (p?: CallbackParams) => ReturnType<T>]
|
|
345
|
+
```
|
|
346
|
+
|
|
347
|
+
**Example**
|
|
348
|
+
```tsx
|
|
349
|
+
const [state, doFetch] = useAsyncFn(
|
|
350
|
+
() => api.saveData(data),
|
|
351
|
+
[data]
|
|
352
|
+
);
|
|
353
|
+
// Later: doFetch({ reset: true });
|
|
354
|
+
```
|
|
355
|
+
|
|
356
|
+
**Notes**
|
|
357
|
+
- Manual trigger unlike `useAsync`
|
|
358
|
+
- Returns both state and executable function
|
|
359
|
+
- Supports `reset` option to clear previous state
|
|
360
|
+
- Prevents race conditions with call tracking
|
|
361
|
+
- Safe to use with unmounted components
|
|
362
|
+
|
|
363
|
+
---
|
|
364
|
+
|
|
365
|
+
### useAsyncFunction
|
|
366
|
+
- **Path:** `~/src/hooks/useAsyncFunction`
|
|
367
|
+
- **Description:** Creates a persistent async function with state tracking and call counting
|
|
368
|
+
- **Signature:**
|
|
369
|
+
```ts
|
|
370
|
+
useAsyncFunction<T extends FunctionReturningPromise>(fn: T): [AsyncFunctionState<T>, T]
|
|
371
|
+
```
|
|
372
|
+
|
|
373
|
+
**Example**
|
|
374
|
+
```tsx
|
|
375
|
+
const [state, saveUser] = useAsyncFunction(async (user) => {
|
|
376
|
+
return api.saveUser(user);
|
|
377
|
+
});
|
|
378
|
+
// state.count tracks number of calls
|
|
379
|
+
```
|
|
380
|
+
|
|
381
|
+
**Notes**
|
|
382
|
+
- Includes call counter in state
|
|
383
|
+
- Function reference is always stable
|
|
384
|
+
- Tracks loading state per call
|
|
385
|
+
- Useful for forms and repeated actions
|
|
386
|
+
- Prevents state updates on unmounted components
|
|
387
|
+
|
|
388
|
+
---
|
|
389
|
+
|
|
390
|
+
### useEffectOnce
|
|
391
|
+
- **Path:** `~/src/hooks/useEffectOnce`
|
|
392
|
+
- **Description:** Runs an effect only once on mount
|
|
393
|
+
- **Signature:** `useEffectOnce(effect: EffectCallback): void`
|
|
394
|
+
|
|
395
|
+
**Example**
|
|
396
|
+
```tsx
|
|
397
|
+
useEffectOnce(() => {
|
|
398
|
+
initializeApp();
|
|
399
|
+
return () => cleanup();
|
|
400
|
+
});
|
|
401
|
+
```
|
|
402
|
+
|
|
403
|
+
**Notes**
|
|
404
|
+
- Equivalent to `useEffect` with empty deps
|
|
405
|
+
- Cleaner API for one-time setup
|
|
406
|
+
- Supports cleanup function return
|
|
407
|
+
- Use for initialization logic
|
|
408
|
+
|
|
409
|
+
---
|
|
410
|
+
|
|
411
|
+
### useMount
|
|
412
|
+
- **Path:** `~/src/hooks/useMount`
|
|
413
|
+
- **Description:** Runs a function on component mount
|
|
414
|
+
- **Signature:** `useMount(fn: () => void): void`
|
|
415
|
+
|
|
416
|
+
**Example**
|
|
417
|
+
```tsx
|
|
418
|
+
useMount(() => {
|
|
419
|
+
console.log('Component mounted');
|
|
420
|
+
});
|
|
421
|
+
```
|
|
422
|
+
|
|
423
|
+
**Notes**
|
|
424
|
+
- Simpler than `useEffectOnce` when no cleanup needed
|
|
425
|
+
- Only for side effects on mount
|
|
426
|
+
- No return value expected
|
|
427
|
+
|
|
428
|
+
---
|
|
429
|
+
|
|
430
|
+
### useUnmount
|
|
431
|
+
- **Path:** `~/src/hooks/useUnmount`
|
|
432
|
+
- **Description:** Runs a function on component unmount
|
|
433
|
+
- **Signature:** `useUnmount(fn: () => any): void`
|
|
434
|
+
|
|
435
|
+
**Example**
|
|
436
|
+
```tsx
|
|
437
|
+
useUnmount(() => {
|
|
438
|
+
saveProgress();
|
|
439
|
+
disconnectSocket();
|
|
440
|
+
});
|
|
441
|
+
```
|
|
442
|
+
|
|
443
|
+
**Notes**
|
|
444
|
+
- Always uses latest version of cleanup function
|
|
445
|
+
- Perfect for cleanup operations
|
|
446
|
+
- Saves form data, closes connections, etc.
|
|
447
|
+
- Function ref is updated each render
|
|
448
|
+
|
|
449
|
+
---
|
|
450
|
+
|
|
451
|
+
### useMountedState
|
|
452
|
+
- **Path:** `~/src/hooks/useMountedState`
|
|
453
|
+
- **Description:** Returns a function that checks if component is still mounted
|
|
454
|
+
- **Signature:** `useMountedState(): () => boolean`
|
|
455
|
+
|
|
456
|
+
**Example**
|
|
457
|
+
```tsx
|
|
458
|
+
const isMounted = useMountedState();
|
|
459
|
+
|
|
460
|
+
setTimeout(() => {
|
|
461
|
+
if (isMounted()) {
|
|
462
|
+
setState(newValue);
|
|
463
|
+
}
|
|
464
|
+
}, 1000);
|
|
465
|
+
```
|
|
466
|
+
|
|
467
|
+
**Notes**
|
|
468
|
+
- Prevents state updates on unmounted components
|
|
469
|
+
- Essential for async operations
|
|
470
|
+
- Check before `setState` in callbacks
|
|
471
|
+
- Stable function reference
|
|
472
|
+
|
|
473
|
+
---
|
|
474
|
+
|
|
475
|
+
### useDelayedValue
|
|
476
|
+
- **Path:** `~/src/hooks/useDelayedValue`
|
|
477
|
+
- **Description:** Delays value updates by specified milliseconds
|
|
478
|
+
- **Signature:** `useDelayedValue<T>(value: T, delay: number): T`
|
|
479
|
+
|
|
480
|
+
**Example**
|
|
481
|
+
```tsx
|
|
482
|
+
const debouncedSearch = useDelayedValue(searchTerm, 300);
|
|
483
|
+
useEffect(() => {
|
|
484
|
+
performSearch(debouncedSearch);
|
|
485
|
+
}, [debouncedSearch]);
|
|
486
|
+
```
|
|
487
|
+
|
|
488
|
+
**Notes**
|
|
489
|
+
- Acts as a debounce for value changes
|
|
490
|
+
- Useful for search inputs and API calls
|
|
491
|
+
- Cancels pending updates on new values
|
|
492
|
+
- Returns initial value immediately
|
|
493
|
+
|
|
494
|
+
---
|
|
495
|
+
|
|
496
|
+
### useDelayedEffect
|
|
497
|
+
- **Path:** `~/src/hooks/useDelayedEffect`
|
|
498
|
+
- **Description:** Runs an effect after a delay with cancellation support
|
|
499
|
+
- **Signature:**
|
|
500
|
+
```ts
|
|
501
|
+
useDelayedEffect(
|
|
502
|
+
effect: (getIsRunning: () => boolean) => void,
|
|
503
|
+
deps: DependencyList,
|
|
504
|
+
delay: number
|
|
505
|
+
): void
|
|
506
|
+
```
|
|
507
|
+
|
|
508
|
+
**Example**
|
|
509
|
+
```tsx
|
|
510
|
+
useDelayedEffect(
|
|
511
|
+
(getIsRunning) => {
|
|
512
|
+
if (getIsRunning()) {
|
|
513
|
+
performExpensiveOperation();
|
|
514
|
+
}
|
|
515
|
+
},
|
|
516
|
+
[data],
|
|
517
|
+
500
|
|
518
|
+
);
|
|
519
|
+
```
|
|
520
|
+
|
|
521
|
+
**Notes**
|
|
522
|
+
- Provides cancellation check via `getIsRunning`
|
|
523
|
+
- Clears timeout on deps change or unmount
|
|
524
|
+
- Useful for debounced side effects
|
|
525
|
+
- Check `getIsRunning()` before state updates
|
|
526
|
+
|
|
527
|
+
---
|
|
528
|
+
|
|
529
|
+
## Utilities
|
|
530
|
+
|
|
531
|
+
### formatNumber
|
|
532
|
+
- **Path:** `~/src/formatNumber`
|
|
533
|
+
- **Description:** Formats numbers with spacing or short notation
|
|
534
|
+
- **Signature:** `formatNumber(number: number, format: 'spaced' | 'short'): string`
|
|
535
|
+
|
|
536
|
+
**Examples**
|
|
537
|
+
```ts
|
|
538
|
+
formatNumber(1234567, 'spaced') // '1 234 567'
|
|
539
|
+
formatNumber(1234567, 'short') // '1.2M'
|
|
540
|
+
formatNumber(999, 'short') // '999'
|
|
541
|
+
```
|
|
542
|
+
|
|
543
|
+
**Notes**
|
|
544
|
+
- `spaced`: Adds spaces every 3 digits
|
|
545
|
+
- `short`: Uses K, M, B notation above 10,000
|
|
546
|
+
- Short format keeps one decimal place
|
|
547
|
+
- Numbers below 10K in short format use spaced display
|
|
548
|
+
- Ideal for statistics and dashboards
|
|
549
|
+
|
|
550
|
+
---
|
|
551
|
+
|
|
552
|
+
### toCssValue
|
|
553
|
+
- **Path:** `~/src/grid`
|
|
554
|
+
- **Description:** Converts number to px string or returns string as-is
|
|
555
|
+
- **Signature:** `toCssValue(value: string | number): string`
|
|
556
|
+
|
|
557
|
+
**Examples**
|
|
558
|
+
```ts
|
|
559
|
+
toCssValue(16) // '16px'
|
|
560
|
+
toCssValue('2rem') // '2rem'
|
|
561
|
+
```
|
|
562
|
+
|
|
563
|
+
**Notes**
|
|
564
|
+
- Used internally by grid components
|
|
565
|
+
- Numbers become pixels
|
|
566
|
+
- Strings pass through unchanged
|
|
567
|
+
- Enables flexible prop values
|
|
568
|
+
|
|
569
|
+
---
|
|
570
|
+
|
|
571
|
+
### elementAddListeners
|
|
572
|
+
- **Path:** `~/src/hooks/useClickAway`
|
|
573
|
+
- **Description:** Adds event listeners to elements with cleanup function
|
|
574
|
+
- **Signature:**
|
|
575
|
+
```ts
|
|
576
|
+
elementAddListeners<
|
|
577
|
+
K extends keyof HTMLElementEventMap,
|
|
578
|
+
E extends HTMLElement
|
|
579
|
+
>(element: E | Document, eventNames: K | K[], listener: EventListener): () => void
|
|
580
|
+
```
|
|
581
|
+
|
|
582
|
+
**Example**
|
|
583
|
+
```ts
|
|
584
|
+
const cleanup = elementAddListeners(
|
|
585
|
+
document,
|
|
586
|
+
['mousedown', 'touchstart'],
|
|
587
|
+
handleClick
|
|
588
|
+
);
|
|
589
|
+
```
|
|
590
|
+
|
|
591
|
+
**Notes**
|
|
592
|
+
- Returns cleanup function
|
|
593
|
+
- Supports multiple event types
|
|
594
|
+
- Works with `Document` or `HTMLElement`
|
|
595
|
+
- Used internally by `useClickAway`
|
|
596
|
+
|
|
597
|
+
---
|
|
598
|
+
|
|
599
|
+
### subscribeClickAway
|
|
600
|
+
- **Path:** `~/src/hooks/useClickAway`
|
|
601
|
+
- **Description:** Subscribes to clicks outside an element
|
|
602
|
+
- **Signature:**
|
|
603
|
+
```ts
|
|
604
|
+
subscribeClickAway<T extends HTMLElement>(
|
|
605
|
+
element: T,
|
|
606
|
+
callback: (event: MouseEvent | TouchEvent) => void
|
|
607
|
+
): () => void
|
|
608
|
+
```
|
|
609
|
+
|
|
610
|
+
**Example**
|
|
611
|
+
```ts
|
|
612
|
+
const unsubscribe = subscribeClickAway(dropdownEl, () => closeDropdown());
|
|
613
|
+
```
|
|
614
|
+
|
|
615
|
+
**Notes**
|
|
616
|
+
- Standalone version of `useClickAway` logic
|
|
617
|
+
- Returns unsubscribe function
|
|
618
|
+
- Handles mouse and touch events
|
|
619
|
+
- Can be used outside React components
|
|
620
|
+
|
|
621
|
+
---
|
|
622
|
+
|
|
623
|
+
## Types
|
|
624
|
+
|
|
625
|
+
### StrNum
|
|
626
|
+
- **Definition:** `type StrNum = string | number`
|
|
627
|
+
- **Description:** Union type for CSS values that can be string or number
|
|
628
|
+
|
|
629
|
+
### CssBoxSides
|
|
630
|
+
- **Definition:**
|
|
631
|
+
```ts
|
|
632
|
+
type CssBoxSides<T> =
|
|
633
|
+
| { top?: T; right?: T; bottom?: T; left?: T }
|
|
634
|
+
| { horizontal: T; top?: T; bottom?: T }
|
|
635
|
+
| { vertical: T; right?: T; left?: T }
|
|
636
|
+
```
|
|
637
|
+
- **Description:** Flexible type for directional CSS properties like margin/padding
|
|
638
|
+
|
|
639
|
+
### AsyncState
|
|
640
|
+
- **Definition:** `type AsyncState<T> = { loading: boolean; error?: Error; value?: T }`
|
|
641
|
+
- **Description:** State shape for async operations with loading, error, and value
|
|
642
|
+
|
|
643
|
+
### FunctionReturningPromise
|
|
644
|
+
- **Definition:** `type FunctionReturningPromise = (...args: any[]) => Promise<any>`
|
|
645
|
+
- **Description:** Generic type for any async function
|
|
646
|
+
|
|
647
|
+
### PromiseType
|
|
648
|
+
- **Definition:** `type PromiseType<P extends Promise<any>> = P extends Promise<infer T> ? T : never`
|
|
649
|
+
- **Description:** Extracts the resolved type from a Promise
|
|
650
|
+
|
|
651
|
+
---
|
|
652
|
+
|
|
653
|
+
## Patterns
|
|
654
|
+
|
|
655
|
+
### styledComponentProps
|
|
656
|
+
- **Description:** All styled-component props are prefixed with `$` to avoid React prop warnings
|
|
657
|
+
- **Example:** `$gap, $padding, $margin` instead of `gap, padding, margin`
|
|
658
|
+
|
|
659
|
+
### lazyRendering
|
|
660
|
+
- **Description:** Components accept function children for deferred rendering
|
|
661
|
+
- **Example:** `<Collapse expanded={show}>{() => <ExpensiveComponent />}</Collapse>`
|
|
662
|
+
|
|
663
|
+
### persistentCallbacks
|
|
664
|
+
- **Description:** Use `usePersistentFunction` to maintain stable references while using latest closure values
|
|
665
|
+
- **Example:** `const stable = usePersistentFunction(callback)`
|
|
666
|
+
|
|
667
|
+
### asyncStateManagement
|
|
668
|
+
- **Description:** Consistent async state shape with loading, error, and value properties
|
|
669
|
+
- **Example:** All async hooks return `{ loading, error, value }` state
|
|
670
|
+
|
|
671
|
+
---
|
|
672
|
+
|
|
673
|
+
## Best Practices
|
|
674
|
+
- Use specific layout components (Columns, Horizontal, Vertical) over generic GridContainer
|
|
675
|
+
- Prefix all styled-component props with `$` to avoid DOM prop warnings
|
|
676
|
+
- Use `usePersistentFunction` for callbacks passed to memoized components
|
|
677
|
+
- Check `isMounted()` before state updates in async operations
|
|
678
|
+
- Use function children in `Collapse` for expensive components
|
|
679
|
+
- Leverage TypeScript's discriminated unions in `AsyncState` for type safety
|
|
680
|
+
- Use `useDelayedValue` for debouncing user input
|
|
681
|
+
- Combine `useClickAway` with portals for robust dropdown behavior
|
package/hooks/useClickAway.d.ts
CHANGED
|
@@ -1,3 +1,27 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
1
|
+
import { type RefObject } from 'react';
|
|
2
|
+
type ClickAwayCallback = (event: MouseEvent | TouchEvent) => void;
|
|
3
|
+
/**
|
|
4
|
+
* Adds event listeners to an element and returns a cleanup function.
|
|
5
|
+
* @param element The target HTML element.
|
|
6
|
+
* @param eventNames The event name(s) to listen for.
|
|
7
|
+
* @param listener The callback function to execute when the event occurs.
|
|
8
|
+
* @returns A function to remove the added event listeners.
|
|
9
|
+
*/
|
|
10
|
+
export declare function elementAddListener<K extends keyof HTMLElementEventMap, E extends HTMLElement>(element: E | Document, // Allow Document as well
|
|
11
|
+
eventNames: K | K[], listener: (this: E | Document, ev: HTMLElementEventMap[K]) => void): () => void;
|
|
12
|
+
/**
|
|
13
|
+
* Subscribes to mousedown/touchstart events outside a given element.
|
|
14
|
+
* @param element The element to detect clicks away from.
|
|
15
|
+
* @param callback The function to call when a click away is detected.
|
|
16
|
+
* @returns A function to unsubscribe the listeners.
|
|
17
|
+
*/
|
|
18
|
+
export declare function subscribeClickAway<T extends HTMLElement>(element: T, callback: ClickAwayCallback): () => void;
|
|
19
|
+
/**
|
|
20
|
+
* Custom hook to trigger a callback when a click occurs outside the referenced element.
|
|
21
|
+
* @param use Whether the hook is active.
|
|
22
|
+
* @param ref A React ref object pointing to the target HTMLElement.
|
|
23
|
+
* @param cb The callback function to execute on a click away.
|
|
24
|
+
*/
|
|
25
|
+
export declare function useClickAway<T extends HTMLElement>(use: boolean, ref: RefObject<T | null>, // Use RefObject<T | null> for better type safety
|
|
26
|
+
cb: ClickAwayCallback): void;
|
|
27
|
+
export {};
|
package/hooks/useClickAway.js
CHANGED
|
@@ -1,28 +1,59 @@
|
|
|
1
1
|
import { useEffect } from 'react';
|
|
2
2
|
import usePersistentFunction from './usePersistentFunction';
|
|
3
|
-
|
|
3
|
+
/**
|
|
4
|
+
* Adds event listeners to an element and returns a cleanup function.
|
|
5
|
+
* @param element The target HTML element.
|
|
6
|
+
* @param eventNames The event name(s) to listen for.
|
|
7
|
+
* @param listener The callback function to execute when the event occurs.
|
|
8
|
+
* @returns A function to remove the added event listeners.
|
|
9
|
+
*/
|
|
10
|
+
export function elementAddListener(element,
|
|
11
|
+
// Allow Document as well
|
|
12
|
+
eventNames, listener) {
|
|
4
13
|
const eventNamesArray = Array.isArray(eventNames) ? eventNames : [eventNames];
|
|
5
14
|
eventNamesArray.forEach(eventName => {
|
|
6
|
-
element.addEventListener(eventName, listener);
|
|
15
|
+
element.addEventListener(eventName, listener); // Cast needed as addEventListener expects EventListener
|
|
7
16
|
});
|
|
8
17
|
return () => {
|
|
9
18
|
eventNamesArray.forEach(eventName => {
|
|
10
|
-
element.removeEventListener(eventName, listener);
|
|
19
|
+
element.removeEventListener(eventName, listener); // Cast needed as removeEventListener expects EventListener
|
|
11
20
|
});
|
|
12
21
|
};
|
|
13
22
|
}
|
|
23
|
+
/**
|
|
24
|
+
* Subscribes to mousedown/touchstart events outside a given element.
|
|
25
|
+
* @param element The element to detect clicks away from.
|
|
26
|
+
* @param callback The function to call when a click away is detected.
|
|
27
|
+
* @returns A function to unsubscribe the listeners.
|
|
28
|
+
*/
|
|
14
29
|
export function subscribeClickAway(element, callback) {
|
|
30
|
+
// Use document directly, no need for 'any' cast
|
|
15
31
|
return elementAddListener(document, ['mousedown', 'touchstart'], event => {
|
|
16
|
-
|
|
17
|
-
|
|
32
|
+
// Type assertion for event target
|
|
33
|
+
const target = event.target;
|
|
34
|
+
// Check if the element exists and the click target is outside the element
|
|
35
|
+
if (element && !element.contains(target)) {
|
|
36
|
+
callback(event); // Assert event type for the callback
|
|
18
37
|
}
|
|
19
38
|
});
|
|
20
39
|
}
|
|
21
|
-
|
|
22
|
-
|
|
40
|
+
/**
|
|
41
|
+
* Custom hook to trigger a callback when a click occurs outside the referenced element.
|
|
42
|
+
* @param use Whether the hook is active.
|
|
43
|
+
* @param ref A React ref object pointing to the target HTMLElement.
|
|
44
|
+
* @param cb The callback function to execute on a click away.
|
|
45
|
+
*/
|
|
46
|
+
export function useClickAway(use, ref,
|
|
47
|
+
// Use RefObject<T | null> for better type safety
|
|
48
|
+
cb) {
|
|
49
|
+
// Persist the callback function to avoid re-subscribing on every render
|
|
50
|
+
const persistentCb = usePersistentFunction(cb);
|
|
51
|
+
// eslint-disable-next-line consistent-return
|
|
23
52
|
useEffect(() => {
|
|
24
|
-
if
|
|
25
|
-
|
|
53
|
+
// Only subscribe if the hook is active and the ref points to an element
|
|
54
|
+
if (use && ref.current) {
|
|
55
|
+
// Return the cleanup function directly
|
|
56
|
+
return subscribeClickAway(ref.current, persistentCb);
|
|
26
57
|
}
|
|
27
|
-
}, [use, ref,
|
|
58
|
+
}, [use, ref, persistentCb]);
|
|
28
59
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@pushwoosh/kit-helpers",
|
|
3
|
-
"version": "4.8.
|
|
3
|
+
"version": "4.8.5",
|
|
4
4
|
"description": "React component for build Pushwoosh products",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"module": "index.js",
|
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
"sideEffects": false,
|
|
9
9
|
"scripts": {
|
|
10
10
|
"start": "npm run playground",
|
|
11
|
-
"build": "pushwoosh-engine lib:build",
|
|
11
|
+
"build": "pushwoosh-engine lib:build && cp ai-documentation.mdx dist/ai-documentation.mdx",
|
|
12
12
|
"playground": "pushwoosh-engine lib:playground",
|
|
13
13
|
"check": "npm run check:lint && npm run check:types",
|
|
14
14
|
"check:lint": "echo \"Run check:lint\" && eslint .",
|