@teo-garcia/react-shared 0.1.8 → 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (84) hide show
  1. package/README.md +76 -168
  2. package/dist/components/error-boundary/error-boundary.d.ts.map +1 -1
  3. package/dist/components/error-boundary/error-boundary.js +1 -1
  4. package/dist/components/index.d.ts +0 -5
  5. package/dist/components/index.d.ts.map +1 -1
  6. package/dist/components/index.js +0 -5
  7. package/dist/hooks/index.d.ts +6 -5
  8. package/dist/hooks/index.d.ts.map +1 -1
  9. package/dist/hooks/index.js +6 -4
  10. package/dist/hooks/use-debounce.d.ts +2 -0
  11. package/dist/hooks/use-debounce.d.ts.map +1 -0
  12. package/dist/hooks/use-debounce.js +9 -0
  13. package/dist/hooks/use-isomorphic-layout-effect.d.ts +3 -0
  14. package/dist/hooks/use-isomorphic-layout-effect.d.ts.map +1 -0
  15. package/dist/hooks/use-isomorphic-layout-effect.js +4 -0
  16. package/dist/hooks/use-local-storage.d.ts +2 -0
  17. package/dist/hooks/use-local-storage.d.ts.map +1 -0
  18. package/dist/hooks/use-local-storage.js +37 -0
  19. package/dist/hooks/use-media-query.d.ts +2 -0
  20. package/dist/hooks/use-media-query.d.ts.map +1 -0
  21. package/dist/hooks/use-media-query.js +18 -0
  22. package/dist/hooks/use-on-click-outside.d.ts +3 -0
  23. package/dist/hooks/use-on-click-outside.d.ts.map +1 -0
  24. package/dist/hooks/use-on-click-outside.js +17 -0
  25. package/dist/hooks/use-previous.d.ts +2 -0
  26. package/dist/hooks/use-previous.d.ts.map +1 -0
  27. package/dist/hooks/use-previous.js +8 -0
  28. package/dist/index.d.ts +6 -4
  29. package/dist/index.d.ts.map +1 -1
  30. package/dist/index.js +6 -9
  31. package/dist/test-utils/index.d.ts +14 -0
  32. package/dist/test-utils/index.d.ts.map +1 -0
  33. package/dist/test-utils/index.js +22 -0
  34. package/dist/types.d.ts +0 -34
  35. package/dist/types.d.ts.map +1 -1
  36. package/dist/utils/cn.d.ts +3 -0
  37. package/dist/utils/cn.d.ts.map +1 -0
  38. package/dist/utils/cn.js +5 -0
  39. package/dist/utils/index.d.ts +1 -5
  40. package/dist/utils/index.d.ts.map +1 -1
  41. package/dist/utils/index.js +1 -5
  42. package/package.json +62 -45
  43. package/dist/adapters/environment/index.d.ts +0 -6
  44. package/dist/adapters/environment/index.d.ts.map +0 -1
  45. package/dist/adapters/environment/index.js +0 -5
  46. package/dist/adapters/environment/next.d.ts +0 -17
  47. package/dist/adapters/environment/next.d.ts.map +0 -1
  48. package/dist/adapters/environment/next.js +0 -20
  49. package/dist/adapters/environment/vite.d.ts +0 -17
  50. package/dist/adapters/environment/vite.d.ts.map +0 -1
  51. package/dist/adapters/environment/vite.js +0 -20
  52. package/dist/adapters/index.d.ts +0 -9
  53. package/dist/adapters/index.d.ts.map +0 -1
  54. package/dist/adapters/index.js +0 -8
  55. package/dist/adapters/theme/custom.d.ts +0 -32
  56. package/dist/adapters/theme/custom.d.ts.map +0 -1
  57. package/dist/adapters/theme/custom.js +0 -26
  58. package/dist/adapters/theme/index.d.ts +0 -6
  59. package/dist/adapters/theme/index.d.ts.map +0 -1
  60. package/dist/adapters/theme/index.js +0 -5
  61. package/dist/adapters/theme/next-themes.d.ts +0 -18
  62. package/dist/adapters/theme/next-themes.d.ts.map +0 -1
  63. package/dist/adapters/theme/next-themes.js +0 -23
  64. package/dist/components/theme-switch/index.d.ts +0 -3
  65. package/dist/components/theme-switch/index.d.ts.map +0 -1
  66. package/dist/components/theme-switch/index.js +0 -1
  67. package/dist/components/theme-switch/theme-switch.d.ts +0 -36
  68. package/dist/components/theme-switch/theme-switch.d.ts.map +0 -1
  69. package/dist/components/theme-switch/theme-switch.js +0 -74
  70. package/dist/components/viewport-info/index.d.ts +0 -3
  71. package/dist/components/viewport-info/index.d.ts.map +0 -1
  72. package/dist/components/viewport-info/index.js +0 -1
  73. package/dist/components/viewport-info/viewport-info.d.ts +0 -40
  74. package/dist/components/viewport-info/viewport-info.d.ts.map +0 -1
  75. package/dist/components/viewport-info/viewport-info.js +0 -69
  76. package/dist/hooks/use-healthcheck.d.ts +0 -42
  77. package/dist/hooks/use-healthcheck.d.ts.map +0 -1
  78. package/dist/hooks/use-healthcheck.js +0 -53
  79. package/dist/utils/environment.d.ts +0 -71
  80. package/dist/utils/environment.d.ts.map +0 -1
  81. package/dist/utils/environment.js +0 -86
  82. package/dist/utils/msw.d.ts +0 -54
  83. package/dist/utils/msw.d.ts.map +0 -1
  84. package/dist/utils/msw.js +0 -62
package/README.md CHANGED
@@ -1,55 +1,57 @@
1
+ <div align="center">
2
+
1
3
  # @teo-garcia/react-shared
2
4
 
3
- Shared React components, hooks, utilities, and adapters for fullstack web templates.
5
+ **Shared React components, hooks, utilities, and adapters for fullstack web
6
+ templates**
4
7
 
5
- This package provides framework-agnostic React utilities that work across Next.js, React Router, and other React-based frameworks using the adapter pattern.
8
+ [![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](LICENSE)
9
+ [![npm](https://img.shields.io/npm/v/@teo-garcia/react-shared?color=blue)](https://www.npmjs.com/package/@teo-garcia/react-shared)
10
+ [![React](https://img.shields.io/badge/React-18+-61DAFB?logo=react&logoColor=black)](https://react.dev)
6
11
 
7
- ## Installation
12
+ Part of the [@teo-garcia/templates](https://github.com/teo-garcia/templates)
13
+ ecosystem
8
14
 
9
- ```bash
10
- pnpm add @teo-garcia/react-shared
11
- ```
15
+ </div>
12
16
 
13
- ### Peer Dependencies
17
+ ---
14
18
 
15
- This package requires the following peer dependencies:
19
+ ## Features
16
20
 
17
- ```bash
18
- pnpm add react react-dom lucide-react
19
- ```
21
+ | Area | Includes |
22
+ | ----------------- | ------------------------------------------------------------------ |
23
+ | **Components** | `ThemeSwitch`, `ViewportInfo`, `ErrorBoundary` |
24
+ | **Hooks** | `useHealthcheck` with React Query |
25
+ | **Utilities** | Environment helpers and MSW setup helpers |
26
+ | **Adapters** | Theme and environment adapters for Next.js and Vite-based apps |
27
+ | **Compatibility** | Framework-agnostic usage across Next.js and React Router templates |
28
+ | **Type Safety** | TypeScript definitions for all exports |
29
+ | **Packaging** | Tree-shakeable module exports |
20
30
 
21
- For hooks functionality, you'll also need:
31
+ ## Requirements
22
32
 
23
- ```bash
24
- pnpm add @tanstack/react-query
25
- ```
33
+ - React 18+
34
+ - TypeScript
35
+ - Tailwind CSS for included component styles
26
36
 
27
- For MSW utilities:
37
+ ## Quick Start
28
38
 
29
39
  ```bash
30
- pnpm add -D msw
31
- ```
40
+ # Install the package
41
+ pnpm add @teo-garcia/react-shared
32
42
 
33
- ## Features
43
+ # Required peer dependencies
44
+ pnpm add react react-dom lucide-react
34
45
 
35
- - **Components**: Ready-to-use UI components (ThemeSwitch, ViewportInfo, ErrorBoundary)
36
- - **Hooks**: Reusable React hooks (useHealthcheck)
37
- - **Utilities**: Pure helper functions (environment detection, MSW setup)
38
- - **Adapters**: Framework bridges for theme providers and environment detection
39
- - **Framework Agnostic**: Works with Next.js, React Router, and other React frameworks
40
- - **TypeScript**: Full type safety with TypeScript definitions
41
- - **Tree-shakeable**: Import only what you need
46
+ # Optional for hooks
47
+ pnpm add @tanstack/react-query
42
48
 
43
- ## Usage
49
+ # Optional for MSW helpers
50
+ pnpm add -D msw
51
+ ```
44
52
 
45
53
  ### Components
46
54
 
47
- #### ThemeSwitch
48
-
49
- A button that cycles through light, dark, and system themes.
50
-
51
- **Next.js (with next-themes):**
52
-
53
55
  ```tsx
54
56
  import { ThemeSwitch } from '@teo-garcia/react-shared/components'
55
57
  import { useNextThemesAdapter } from '@teo-garcia/react-shared/adapters/theme'
@@ -60,75 +62,8 @@ export function App() {
60
62
  }
61
63
  ```
62
64
 
63
- **React Router (with custom provider):**
64
-
65
- ```tsx
66
- import { ThemeSwitch } from '@teo-garcia/react-shared/components'
67
- import { createCustomThemeAdapter } from '@teo-garcia/react-shared/adapters/theme'
68
- import { useTheme } from '~/components/theme-provider'
69
-
70
- export function App() {
71
- const themeAdapter = createCustomThemeAdapter(useTheme())
72
- return <ThemeSwitch themeAdapter={themeAdapter} />
73
- }
74
- ```
75
-
76
- #### ViewportInfo
77
-
78
- Displays current viewport dimensions and Tailwind breakpoint (dev mode only).
79
-
80
- ```tsx
81
- import { ViewportInfo } from '@teo-garcia/react-shared/components'
82
- import { nextEnvironmentAdapter } from '@teo-garcia/react-shared/adapters/environment'
83
-
84
- export function App() {
85
- return <ViewportInfo environmentAdapter={nextEnvironmentAdapter} />
86
- }
87
- ```
88
-
89
- **For Vite/React Router:**
90
-
91
- ```tsx
92
- import { ViewportInfo } from '@teo-garcia/react-shared/components'
93
- import { viteEnvironmentAdapter } from '@teo-garcia/react-shared/adapters/environment'
94
-
95
- export function App() {
96
- return <ViewportInfo environmentAdapter={viteEnvironmentAdapter} />
97
- }
98
- ```
99
-
100
- #### ErrorBoundary
101
-
102
- Catches JavaScript errors in child components.
103
-
104
- ```tsx
105
- import { ErrorBoundary } from '@teo-garcia/react-shared/components'
106
-
107
- export function App() {
108
- return (
109
- <ErrorBoundary
110
- fallback={(error) => (
111
- <div>
112
- <h1>Something went wrong</h1>
113
- <p>{error.message}</p>
114
- </div>
115
- )}
116
- onError={(error, errorInfo) => {
117
- console.error('Error caught:', error, errorInfo)
118
- }}
119
- >
120
- <YourApp />
121
- </ErrorBoundary>
122
- )
123
- }
124
- ```
125
-
126
65
  ### Hooks
127
66
 
128
- #### useHealthcheck
129
-
130
- Fetches health status from an API endpoint using React Query.
131
-
132
67
  ```tsx
133
68
  import { useHealthcheck } from '@teo-garcia/react-shared/hooks'
134
69
 
@@ -145,78 +80,45 @@ export function HealthStatus() {
145
80
 
146
81
  ### Utilities
147
82
 
148
- #### Environment Detection
149
-
150
83
  ```tsx
151
- import { isDevelopment, isProduction, isServer, isClient } from '@teo-garcia/react-shared/utils'
84
+ import { isClient, isDevelopment } from '@teo-garcia/react-shared/utils'
152
85
 
153
86
  if (isDevelopment()) {
154
87
  console.log('Running in dev mode')
155
88
  }
156
89
 
157
90
  if (isClient()) {
158
- // Access browser APIs
159
91
  localStorage.setItem('key', 'value')
160
92
  }
161
93
  ```
162
94
 
163
- #### MSW Setup
164
-
165
- ```tsx
166
- import { setupMSWBrowser } from '@teo-garcia/react-shared/utils'
167
- import { handlers } from './mocks/handlers'
168
-
169
- // In your app initialization
170
- if (isDevelopment()) {
171
- await setupMSWBrowser(handlers)
172
- }
173
- ```
174
-
175
95
  ### Adapters
176
96
 
177
- Adapters bridge the gap between different framework-specific implementations and our components.
178
-
179
- #### Theme Adapters
180
-
181
- **Next.js (next-themes):**
182
-
183
- ```tsx
184
- import { useNextThemesAdapter } from '@teo-garcia/react-shared/adapters/theme'
185
-
186
- const themeAdapter = useNextThemesAdapter()
187
- ```
188
-
189
- **Custom Theme Provider:**
190
-
191
97
  ```tsx
192
- import { createCustomThemeAdapter } from '@teo-garcia/react-shared/adapters/theme'
193
- import { useTheme } from '~/your-theme-provider'
194
-
195
- const themeAdapter = createCustomThemeAdapter(useTheme())
196
- ```
197
-
198
- #### Environment Adapters
199
-
200
- **Next.js:**
98
+ import { ViewportInfo } from '@teo-garcia/react-shared/components'
99
+ import { viteEnvironmentAdapter } from '@teo-garcia/react-shared/adapters/environment'
201
100
 
202
- ```tsx
203
- import { nextEnvironmentAdapter } from '@teo-garcia/react-shared/adapters/environment'
204
- ;<ViewportInfo environmentAdapter={nextEnvironmentAdapter} />
101
+ export function App() {
102
+ return <ViewportInfo environmentAdapter={viteEnvironmentAdapter} />
103
+ }
205
104
  ```
206
105
 
207
- **Vite/React Router:**
106
+ ## Exports
208
107
 
209
- ```tsx
210
- import { viteEnvironmentAdapter } from '@teo-garcia/react-shared/adapters/environment'
211
- ;<ViewportInfo environmentAdapter={viteEnvironmentAdapter} />
212
- ```
108
+ | Export | Description |
109
+ | ----------------------------------------------- | ---------------------------- |
110
+ | `@teo-garcia/react-shared/components` | Shared React UI components |
111
+ | `@teo-garcia/react-shared/hooks` | Shared React hooks |
112
+ | `@teo-garcia/react-shared/utils` | Framework-agnostic utilities |
113
+ | `@teo-garcia/react-shared/adapters/theme` | Theme adapters |
114
+ | `@teo-garcia/react-shared/adapters/environment` | Environment adapters |
213
115
 
214
- ## API Reference
116
+ ## Included APIs
215
117
 
216
118
  ### Components
217
119
 
218
120
  - `ThemeSwitch` - Theme switcher button
219
- - `ViewportInfo` - Viewport dimensions display (dev only)
121
+ - `ViewportInfo` - Viewport dimensions display for development
220
122
  - `ErrorBoundary` - Error boundary component
221
123
 
222
124
  ### Hooks
@@ -225,35 +127,41 @@ import { viteEnvironmentAdapter } from '@teo-garcia/react-shared/adapters/enviro
225
127
 
226
128
  ### Utilities
227
129
 
228
- - `isDevelopment()` - Check if in development mode
229
- - `isProduction()` - Check if in production mode
230
- - `isServer()` - Check if running on server
231
- - `isClient()` - Check if running on client
232
- - `setupMSWBrowser()` - Setup MSW for browser
233
- - `setupMSWServer()` - Setup MSW for Node.js/tests
130
+ - `isDevelopment()` - Check if running in development mode
131
+ - `isProduction()` - Check if running in production mode
132
+ - `isServer()` - Check if running on the server
133
+ - `isClient()` - Check if running in the browser
134
+ - `setupMSWBrowser()` - Setup MSW for browser usage
135
+ - `setupMSWServer()` - Setup MSW for Node.js and test usage
234
136
 
235
137
  ### Adapters
236
138
 
237
- **Theme:**
139
+ - `useNextThemesAdapter()` - Adapter for `next-themes`
140
+ - `createCustomThemeAdapter()` - Adapter for custom theme providers
141
+ - `nextEnvironmentAdapter` - Environment adapter for Next.js
142
+ - `viteEnvironmentAdapter` - Environment adapter for Vite-based apps
238
143
 
239
- - `useNextThemesAdapter()` - Adapter for next-themes
240
- - `createCustomThemeAdapter()` - Create adapter for custom theme provider
144
+ ## Notes
241
145
 
242
- **Environment:**
146
+ - Components assume Tailwind CSS is available in the consuming app.
147
+ - Hooks and adapters are designed to be framework-agnostic where possible.
148
+ - Consumers should import only the module paths they need.
243
149
 
244
- - `nextEnvironmentAdapter` - Environment detection for Next.js
245
- - `viteEnvironmentAdapter` - Environment detection for Vite
246
-
247
- ## Requirements
150
+ ## Related Packages
248
151
 
249
- - **Tailwind CSS**: Components use Tailwind CSS classes
250
- - **React 18+**: Uses modern React features
251
- - **TypeScript**: Full type definitions included
152
+ | Package | Description |
153
+ | ------------------------------------------------------------------------------------------ | ------------------- |
154
+ | [@teo-garcia/eslint-config-shared](https://github.com/teo-garcia/eslint-config-shared) | ESLint rules |
155
+ | [@teo-garcia/prettier-config-shared](https://github.com/teo-garcia/prettier-config-shared) | Prettier formatting |
156
+ | [@teo-garcia/tsconfig-shared](https://github.com/teo-garcia/tsconfig-shared) | TypeScript settings |
157
+ | [@teo-garcia/vitest-config-shared](https://github.com/teo-garcia/vitest-config-shared) | Test configuration |
252
158
 
253
159
  ## License
254
160
 
255
161
  MIT
256
162
 
257
- ## Author
163
+ ---
258
164
 
259
- teo-garcia
165
+ <div align="center">
166
+ <sub>Built by <a href="https://github.com/teo-garcia">teo-garcia</a></sub>
167
+ </div>
@@ -1 +1 @@
1
- {"version":3,"file":"error-boundary.d.ts","sourceRoot":"","sources":["../../../src/components/error-boundary/error-boundary.tsx"],"names":[],"mappings":"AAEA,OAAO,EAAE,SAAS,EAAE,KAAK,SAAS,EAAE,MAAM,OAAO,CAAA;AAEjD,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,aAAa,CAAA;AAErD,UAAU,kBAAkB;IAC1B,QAAQ,EAAE,OAAO,CAAA;IACjB,KAAK,EAAE,KAAK,GAAG,IAAI,CAAA;CACpB;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAgDG;AACH,qBAAa,aAAc,SAAQ,SAAS,CAAC,kBAAkB,EAAE,kBAAkB,CAAC;gBACtE,KAAK,EAAE,kBAAkB;IAKrC;;;OAGG;IACH,MAAM,CAAC,wBAAwB,CAAC,KAAK,EAAE,KAAK,GAAG,kBAAkB;IAIjE;;;OAGG;IACH,iBAAiB,CAAC,KAAK,EAAE,KAAK,EAAE,SAAS,EAAE,KAAK,CAAC,SAAS,GAAG,IAAI;IAOjE,MAAM,IAAI,SAAS;CAwCpB"}
1
+ {"version":3,"file":"error-boundary.d.ts","sourceRoot":"","sources":["../../../src/components/error-boundary/error-boundary.tsx"],"names":[],"mappings":"AAEA,OAAO,EAAE,SAAS,EAAE,KAAK,SAAS,EAAE,MAAM,OAAO,CAAA;AAEjD,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,aAAa,CAAA;AAErD,UAAU,kBAAkB;IAC1B,QAAQ,EAAE,OAAO,CAAA;IACjB,KAAK,EAAE,KAAK,GAAG,IAAI,CAAA;CACpB;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAgDG;AACH,qBAAa,aAAc,SAAQ,SAAS,CAC1C,kBAAkB,EAClB,kBAAkB,CACnB;gBACa,KAAK,EAAE,kBAAkB;IAKrC;;;OAGG;IACH,MAAM,CAAC,wBAAwB,CAAC,KAAK,EAAE,KAAK,GAAG,kBAAkB;IAIjE;;;OAGG;IACH,iBAAiB,CAAC,KAAK,EAAE,KAAK,EAAE,SAAS,EAAE,KAAK,CAAC,SAAS,GAAG,IAAI;IAOjE,MAAM,IAAI,SAAS;CA0CpB"}
@@ -85,7 +85,7 @@ export class ErrorBoundary extends Component {
85
85
  return fallback;
86
86
  }
87
87
  // Default fallback UI if none provided
88
- return (_jsxs("div", { role: "alert", style: {
88
+ return (_jsxs("div", { role: 'alert', style: {
89
89
  padding: '20px',
90
90
  margin: '20px',
91
91
  border: '1px solid #f5c6cb',
@@ -1,7 +1,2 @@
1
- /**
2
- * Components - Reusable React UI components
3
- */
4
1
  export * from './error-boundary/index.js';
5
- export * from './theme-switch/index.js';
6
- export * from './viewport-info/index.js';
7
2
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/components/index.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,cAAc,2BAA2B,CAAA;AACzC,cAAc,yBAAyB,CAAA;AACvC,cAAc,0BAA0B,CAAA"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/components/index.ts"],"names":[],"mappings":"AAAA,cAAc,2BAA2B,CAAA"}
@@ -1,6 +1 @@
1
- /**
2
- * Components - Reusable React UI components
3
- */
4
1
  export * from './error-boundary/index.js';
5
- export * from './theme-switch/index.js';
6
- export * from './viewport-info/index.js';
@@ -1,6 +1,7 @@
1
- /**
2
- * Hooks - Reusable React hooks for common functionality
3
- */
4
- export { useHealthcheck } from './use-healthcheck.js';
5
- export type { UseHealthcheckOptions } from './use-healthcheck.js';
1
+ export { useDebounce } from './use-debounce.js';
2
+ export { useIsomorphicLayoutEffect } from './use-isomorphic-layout-effect.js';
3
+ export { useLocalStorage } from './use-local-storage.js';
4
+ export { useMediaQuery } from './use-media-query.js';
5
+ export { useOnClickOutside } from './use-on-click-outside.js';
6
+ export { usePrevious } from './use-previous.js';
6
7
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/hooks/index.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAA;AACrD,YAAY,EAAE,qBAAqB,EAAE,MAAM,sBAAsB,CAAA"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/hooks/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAA;AAC/C,OAAO,EAAE,yBAAyB,EAAE,MAAM,mCAAmC,CAAA;AAC7E,OAAO,EAAE,eAAe,EAAE,MAAM,wBAAwB,CAAA;AACxD,OAAO,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAA;AACpD,OAAO,EAAE,iBAAiB,EAAE,MAAM,2BAA2B,CAAA;AAC7D,OAAO,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAA"}
@@ -1,4 +1,6 @@
1
- /**
2
- * Hooks - Reusable React hooks for common functionality
3
- */
4
- export { useHealthcheck } from './use-healthcheck.js';
1
+ export { useDebounce } from './use-debounce.js';
2
+ export { useIsomorphicLayoutEffect } from './use-isomorphic-layout-effect.js';
3
+ export { useLocalStorage } from './use-local-storage.js';
4
+ export { useMediaQuery } from './use-media-query.js';
5
+ export { useOnClickOutside } from './use-on-click-outside.js';
6
+ export { usePrevious } from './use-previous.js';
@@ -0,0 +1,2 @@
1
+ export declare function useDebounce<T>(value: T, delay: number): T;
2
+ //# sourceMappingURL=use-debounce.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"use-debounce.d.ts","sourceRoot":"","sources":["../../src/hooks/use-debounce.ts"],"names":[],"mappings":"AAEA,wBAAgB,WAAW,CAAC,CAAC,EAAE,KAAK,EAAE,CAAC,EAAE,KAAK,EAAE,MAAM,GAAG,CAAC,CASzD"}
@@ -0,0 +1,9 @@
1
+ import { useEffect, useState } from 'react';
2
+ export function useDebounce(value, delay) {
3
+ const [debouncedValue, setDebouncedValue] = useState(value);
4
+ useEffect(() => {
5
+ const timer = setTimeout(() => setDebouncedValue(value), delay);
6
+ return () => clearTimeout(timer);
7
+ }, [value, delay]);
8
+ return debouncedValue;
9
+ }
@@ -0,0 +1,3 @@
1
+ import { useEffect } from 'react';
2
+ export declare const useIsomorphicLayoutEffect: typeof useEffect;
3
+ //# sourceMappingURL=use-isomorphic-layout-effect.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"use-isomorphic-layout-effect.d.ts","sourceRoot":"","sources":["../../src/hooks/use-isomorphic-layout-effect.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAmB,MAAM,OAAO,CAAA;AAIlD,eAAO,MAAM,yBAAyB,kBACuB,CAAA"}
@@ -0,0 +1,4 @@
1
+ import { useEffect, useLayoutEffect } from 'react';
2
+ // useLayoutEffect warns in SSR. This hook silences that by falling back to
3
+ // useEffect on the server where window is not available.
4
+ export const useIsomorphicLayoutEffect = typeof window !== 'undefined' ? useLayoutEffect : useEffect;
@@ -0,0 +1,2 @@
1
+ export declare function useLocalStorage<T>(key: string, initialValue: T): [T, (value: T | ((prev: T) => T)) => void, () => void];
2
+ //# sourceMappingURL=use-local-storage.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"use-local-storage.d.ts","sourceRoot":"","sources":["../../src/hooks/use-local-storage.ts"],"names":[],"mappings":"AAYA,wBAAgB,eAAe,CAAC,CAAC,EAC/B,GAAG,EAAE,MAAM,EACX,YAAY,EAAE,CAAC,GACd,CAAC,CAAC,EAAE,CAAC,KAAK,EAAE,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,CAAC,KAAK,CAAC,CAAC,KAAK,IAAI,EAAE,MAAM,IAAI,CAAC,CAmCxD"}
@@ -0,0 +1,37 @@
1
+ import { useCallback, useRef, useState } from 'react';
2
+ function readFromStorage(key, fallback) {
3
+ if (typeof window === 'undefined')
4
+ return fallback;
5
+ try {
6
+ const item = window.localStorage.getItem(key);
7
+ return item !== null ? JSON.parse(item) : fallback;
8
+ }
9
+ catch {
10
+ return fallback;
11
+ }
12
+ }
13
+ export function useLocalStorage(key, initialValue) {
14
+ const initialValueRef = useRef(initialValue);
15
+ const [storedValue, setStoredValue] = useState(() => readFromStorage(key, initialValueRef.current));
16
+ const setValue = useCallback((value) => {
17
+ setStoredValue((prev) => {
18
+ const next = value instanceof Function ? value(prev) : value;
19
+ if (typeof window !== 'undefined') {
20
+ try {
21
+ window.localStorage.setItem(key, JSON.stringify(next));
22
+ }
23
+ catch (error) {
24
+ console.error(`useLocalStorage: failed to write key "${key}"`, error);
25
+ }
26
+ }
27
+ return next;
28
+ });
29
+ }, [key]);
30
+ const removeValue = useCallback(() => {
31
+ if (typeof window !== 'undefined') {
32
+ window.localStorage.removeItem(key);
33
+ }
34
+ setStoredValue(initialValueRef.current);
35
+ }, [key]);
36
+ return [storedValue, setValue, removeValue];
37
+ }
@@ -0,0 +1,2 @@
1
+ export declare function useMediaQuery(query: string): boolean;
2
+ //# sourceMappingURL=use-media-query.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"use-media-query.d.ts","sourceRoot":"","sources":["../../src/hooks/use-media-query.ts"],"names":[],"mappings":"AAEA,wBAAgB,aAAa,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAkBpD"}
@@ -0,0 +1,18 @@
1
+ import { useEffect, useState } from 'react';
2
+ export function useMediaQuery(query) {
3
+ const [matches, setMatches] = useState(() => {
4
+ if (typeof window === 'undefined')
5
+ return false;
6
+ return window.matchMedia(query).matches;
7
+ });
8
+ useEffect(() => {
9
+ if (typeof window === 'undefined')
10
+ return;
11
+ const mql = window.matchMedia(query);
12
+ setMatches(mql.matches);
13
+ const handler = (event) => setMatches(event.matches);
14
+ mql.addEventListener('change', handler);
15
+ return () => mql.removeEventListener('change', handler);
16
+ }, [query]);
17
+ return matches;
18
+ }
@@ -0,0 +1,3 @@
1
+ import { type RefObject } from 'react';
2
+ export declare function useOnClickOutside<T extends HTMLElement>(ref: RefObject<T | null>, handler: (event: MouseEvent | TouchEvent) => void): void;
3
+ //# sourceMappingURL=use-on-click-outside.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"use-on-click-outside.d.ts","sourceRoot":"","sources":["../../src/hooks/use-on-click-outside.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,SAAS,EAAa,MAAM,OAAO,CAAA;AAEjD,wBAAgB,iBAAiB,CAAC,CAAC,SAAS,WAAW,EACrD,GAAG,EAAE,SAAS,CAAC,CAAC,GAAG,IAAI,CAAC,EACxB,OAAO,EAAE,CAAC,KAAK,EAAE,UAAU,GAAG,UAAU,KAAK,IAAI,GAChD,IAAI,CAeN"}
@@ -0,0 +1,17 @@
1
+ import { useEffect } from 'react';
2
+ export function useOnClickOutside(ref, handler) {
3
+ useEffect(() => {
4
+ const listener = (event) => {
5
+ const el = ref.current;
6
+ if (!el || el.contains(event.target))
7
+ return;
8
+ handler(event);
9
+ };
10
+ document.addEventListener('mousedown', listener);
11
+ document.addEventListener('touchstart', listener);
12
+ return () => {
13
+ document.removeEventListener('mousedown', listener);
14
+ document.removeEventListener('touchstart', listener);
15
+ };
16
+ }, [ref, handler]);
17
+ }
@@ -0,0 +1,2 @@
1
+ export declare function usePrevious<T>(value: T): T | undefined;
2
+ //# sourceMappingURL=use-previous.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"use-previous.d.ts","sourceRoot":"","sources":["../../src/hooks/use-previous.ts"],"names":[],"mappings":"AAEA,wBAAgB,WAAW,CAAC,CAAC,EAAE,KAAK,EAAE,CAAC,GAAG,CAAC,GAAG,SAAS,CAQtD"}
@@ -0,0 +1,8 @@
1
+ import { useEffect, useRef } from 'react';
2
+ export function usePrevious(value) {
3
+ const ref = useRef(undefined);
4
+ useEffect(() => {
5
+ ref.current = value;
6
+ }, [value]);
7
+ return ref.current;
8
+ }
package/dist/index.d.ts CHANGED
@@ -1,16 +1,18 @@
1
1
  /**
2
2
  * @teo-garcia/react-shared
3
3
  *
4
- * Shared React components, hooks, utilities, and adapters for fullstack web templates.
4
+ * Shared React hooks, utilities, and test helpers for the teo-garcia template portfolio.
5
5
  *
6
- * This package provides framework-agnostic React utilities that work across
7
- * Next.js, React Router, and other React-based frameworks.
6
+ * Exports:
7
+ * - Hooks: useDebounce, useIsomorphicLayoutEffect, useLocalStorage, useMediaQuery, useOnClickOutside, usePrevious
8
+ * - Components: ErrorBoundary
9
+ * - Utils: cn (clsx + tailwind-merge)
10
+ * - Test utilities: createWrapper, renderWithProviders (import from react-shared/test-utils)
8
11
  *
9
12
  * @packageDocumentation
10
13
  */
11
14
  export * from './components/index.js';
12
15
  export * from './hooks/index.js';
13
16
  export * from './utils/index.js';
14
- export * from './adapters/index.js';
15
17
  export * from './types.js';
16
18
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAGH,cAAc,uBAAuB,CAAA;AAGrC,cAAc,kBAAkB,CAAA;AAGhC,cAAc,kBAAkB,CAAA;AAGhC,cAAc,qBAAqB,CAAA;AAGnC,cAAc,YAAY,CAAA"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAEH,cAAc,uBAAuB,CAAA;AACrC,cAAc,kBAAkB,CAAA;AAChC,cAAc,kBAAkB,CAAA;AAChC,cAAc,YAAY,CAAA"}
package/dist/index.js CHANGED
@@ -1,20 +1,17 @@
1
1
  /**
2
2
  * @teo-garcia/react-shared
3
3
  *
4
- * Shared React components, hooks, utilities, and adapters for fullstack web templates.
4
+ * Shared React hooks, utilities, and test helpers for the teo-garcia template portfolio.
5
5
  *
6
- * This package provides framework-agnostic React utilities that work across
7
- * Next.js, React Router, and other React-based frameworks.
6
+ * Exports:
7
+ * - Hooks: useDebounce, useIsomorphicLayoutEffect, useLocalStorage, useMediaQuery, useOnClickOutside, usePrevious
8
+ * - Components: ErrorBoundary
9
+ * - Utils: cn (clsx + tailwind-merge)
10
+ * - Test utilities: createWrapper, renderWithProviders (import from react-shared/test-utils)
8
11
  *
9
12
  * @packageDocumentation
10
13
  */
11
- // Export all components
12
14
  export * from './components/index.js';
13
- // Export all hooks
14
15
  export * from './hooks/index.js';
15
- // Export all utilities
16
16
  export * from './utils/index.js';
17
- // Export all adapters
18
- export * from './adapters/index.js';
19
- // Export all types
20
17
  export * from './types.js';
@@ -0,0 +1,14 @@
1
+ import { QueryClient } from '@tanstack/react-query';
2
+ import { type RenderOptions, type RenderResult } from '@testing-library/react';
3
+ import { type ReactNode } from 'react';
4
+ export interface WrapperOptions {
5
+ queryClient?: QueryClient;
6
+ }
7
+ export declare function createWrapper(options?: WrapperOptions): ({ children }: {
8
+ children: ReactNode;
9
+ }) => import("react").FunctionComponentElement<import("@tanstack/react-query").QueryClientProviderProps>;
10
+ export interface RenderWithProvidersOptions extends Omit<RenderOptions, 'wrapper'> {
11
+ queryClient?: QueryClient;
12
+ }
13
+ export declare function renderWithProviders(ui: React.ReactElement, options?: RenderWithProvidersOptions): RenderResult;
14
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/test-utils/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAuB,MAAM,uBAAuB,CAAA;AACxE,OAAO,EAEL,KAAK,aAAa,EAClB,KAAK,YAAY,EAClB,MAAM,wBAAwB,CAAA;AAC/B,OAAO,EAAiB,KAAK,SAAS,EAAE,MAAM,OAAO,CAAA;AAErD,MAAM,WAAW,cAAc;IAC7B,WAAW,CAAC,EAAE,WAAW,CAAA;CAC1B;AAED,wBAAgB,aAAa,CAAC,OAAO,GAAE,cAAmB,IAUhC,cAAc;IAAE,QAAQ,EAAE,SAAS,CAAA;CAAE,wGAG9D;AAED,MAAM,WAAW,0BACf,SAAQ,IAAI,CAAC,aAAa,EAAE,SAAS,CAAC;IACtC,WAAW,CAAC,EAAE,WAAW,CAAA;CAC1B;AAED,wBAAgB,mBAAmB,CACjC,EAAE,EAAE,KAAK,CAAC,YAAY,EACtB,OAAO,GAAE,0BAA+B,GACvC,YAAY,CAMd"}
@@ -0,0 +1,22 @@
1
+ import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
2
+ import { render, } from '@testing-library/react';
3
+ import { createElement } from 'react';
4
+ export function createWrapper(options = {}) {
5
+ const client = options.queryClient ??
6
+ new QueryClient({
7
+ defaultOptions: {
8
+ queries: { retry: false, gcTime: 0 },
9
+ mutations: { retry: false },
10
+ },
11
+ });
12
+ return function Wrapper({ children }) {
13
+ return createElement(QueryClientProvider, { client }, children);
14
+ };
15
+ }
16
+ export function renderWithProviders(ui, options = {}) {
17
+ const { queryClient, ...renderOptions } = options;
18
+ return render(ui, {
19
+ wrapper: createWrapper({ queryClient }),
20
+ ...renderOptions,
21
+ });
22
+ }