@teo-garcia/react-shared 0.1.9 → 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 +8 -11
  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 +60 -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
@@ -81,10 +81,7 @@ export function HealthStatus() {
81
81
  ### Utilities
82
82
 
83
83
  ```tsx
84
- import {
85
- isClient,
86
- isDevelopment,
87
- } from '@teo-garcia/react-shared/utils'
84
+ import { isClient, isDevelopment } from '@teo-garcia/react-shared/utils'
88
85
 
89
86
  if (isDevelopment()) {
90
87
  console.log('Running in dev mode')
@@ -108,13 +105,13 @@ export function App() {
108
105
 
109
106
  ## Exports
110
107
 
111
- | Export | Description |
112
- | ------ | ----------- |
113
- | `@teo-garcia/react-shared/components` | Shared React UI components |
114
- | `@teo-garcia/react-shared/hooks` | Shared React hooks |
115
- | `@teo-garcia/react-shared/utils` | Framework-agnostic utilities |
116
- | `@teo-garcia/react-shared/adapters/theme` | Theme adapters |
117
- | `@teo-garcia/react-shared/adapters/environment` | Environment adapters |
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 |
118
115
 
119
116
  ## Included APIs
120
117
 
@@ -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
+ }
package/dist/types.d.ts CHANGED
@@ -1,40 +1,6 @@
1
- /**
2
- * Theme mode type - represents the three possible theme states
3
- */
4
- export type ThemeMode = 'light' | 'dark' | 'system';
5
- /**
6
- * ThemeAdapter interface - allows components to work with different theme providers
7
- * Implementations should map their specific theme library to this interface
8
- */
9
- export interface ThemeAdapter {
10
- /** Current theme mode */
11
- theme: ThemeMode;
12
- /** Function to update the theme */
13
- setTheme: (theme: ThemeMode) => void;
14
- }
15
- /**
16
- * EnvironmentAdapter interface - abstracts environment detection across frameworks
17
- * Different frameworks (Next.js vs Vite) use different environment variable systems
18
- */
19
- export interface EnvironmentAdapter {
20
- /** Check if running in development mode */
21
- isDevelopment: () => boolean;
22
- /** Check if running in production mode */
23
- isProduction?: () => boolean;
24
- /** Check if running on server */
25
- isServer?: () => boolean;
26
- /** Check if running on client */
27
- isClient?: () => boolean;
28
- }
29
- /**
30
- * ErrorBoundary fallback render function type
31
- */
32
1
  export interface ErrorBoundaryProps {
33
- /** The React children to wrap */
34
2
  children: React.ReactNode;
35
- /** Optional fallback UI to render on error */
36
3
  fallback?: React.ReactNode | ((error: Error) => React.ReactNode);
37
- /** Optional callback when an error is caught */
38
4
  onError?: (error: Error, errorInfo: React.ErrorInfo) => void;
39
5
  }
40
6
  //# sourceMappingURL=types.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,MAAM,MAAM,SAAS,GAAG,OAAO,GAAG,MAAM,GAAG,QAAQ,CAAA;AAEnD;;;GAGG;AACH,MAAM,WAAW,YAAY;IAC3B,yBAAyB;IACzB,KAAK,EAAE,SAAS,CAAA;IAChB,mCAAmC;IACnC,QAAQ,EAAE,CAAC,KAAK,EAAE,SAAS,KAAK,IAAI,CAAA;CACrC;AAED;;;GAGG;AACH,MAAM,WAAW,kBAAkB;IACjC,2CAA2C;IAC3C,aAAa,EAAE,MAAM,OAAO,CAAA;IAC5B,0CAA0C;IAC1C,YAAY,CAAC,EAAE,MAAM,OAAO,CAAA;IAC5B,iCAAiC;IACjC,QAAQ,CAAC,EAAE,MAAM,OAAO,CAAA;IACxB,iCAAiC;IACjC,QAAQ,CAAC,EAAE,MAAM,OAAO,CAAA;CACzB;AAED;;GAEG;AACH,MAAM,WAAW,kBAAkB;IACjC,iCAAiC;IACjC,QAAQ,EAAE,KAAK,CAAC,SAAS,CAAA;IACzB,8CAA8C;IAC9C,QAAQ,CAAC,EAAE,KAAK,CAAC,SAAS,GAAG,CAAC,CAAC,KAAK,EAAE,KAAK,KAAK,KAAK,CAAC,SAAS,CAAC,CAAA;IAChE,gDAAgD;IAChD,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE,KAAK,EAAE,SAAS,EAAE,KAAK,CAAC,SAAS,KAAK,IAAI,CAAA;CAC7D"}
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,kBAAkB;IACjC,QAAQ,EAAE,KAAK,CAAC,SAAS,CAAA;IACzB,QAAQ,CAAC,EAAE,KAAK,CAAC,SAAS,GAAG,CAAC,CAAC,KAAK,EAAE,KAAK,KAAK,KAAK,CAAC,SAAS,CAAC,CAAA;IAChE,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE,KAAK,EAAE,SAAS,EAAE,KAAK,CAAC,SAAS,KAAK,IAAI,CAAA;CAC7D"}
@@ -0,0 +1,3 @@
1
+ import { type ClassValue } from 'clsx';
2
+ export declare function cn(...inputs: ClassValue[]): string;
3
+ //# sourceMappingURL=cn.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cn.d.ts","sourceRoot":"","sources":["../../src/utils/cn.ts"],"names":[],"mappings":"AAAA,OAAO,EAAQ,KAAK,UAAU,EAAE,MAAM,MAAM,CAAA;AAG5C,wBAAgB,EAAE,CAAC,GAAG,MAAM,EAAE,UAAU,EAAE,GAAG,MAAM,CAElD"}
@@ -0,0 +1,5 @@
1
+ import { clsx } from 'clsx';
2
+ import { twMerge } from 'tailwind-merge';
3
+ export function cn(...inputs) {
4
+ return twMerge(clsx(inputs));
5
+ }
@@ -1,6 +1,2 @@
1
- /**
2
- * Utilities - Pure functions and helpers
3
- */
4
- export * from './environment.js';
5
- export * from './msw.js';
1
+ export { cn } from './cn.js';
6
2
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/utils/index.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,cAAc,kBAAkB,CAAA;AAChC,cAAc,UAAU,CAAA"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/utils/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,EAAE,EAAE,MAAM,SAAS,CAAA"}
@@ -1,5 +1 @@
1
- /**
2
- * Utilities - Pure functions and helpers
3
- */
4
- export * from './environment.js';
5
- export * from './msw.js';
1
+ export { cn } from './cn.js';
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@teo-garcia/react-shared",
3
- "version": "0.1.9",
4
- "description": "Shared React components, hooks, utilities, and adapters for fullstack web templates",
3
+ "version": "1.0.0",
4
+ "description": "Shared React hooks, utilities, and test helpers for the teo-garcia template portfolio",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
7
7
  "types": "./dist/index.d.ts",
@@ -14,14 +14,6 @@
14
14
  "import": "./dist/components/index.js",
15
15
  "types": "./dist/components/index.d.ts"
16
16
  },
17
- "./components/theme-switch": {
18
- "import": "./dist/components/theme-switch/index.js",
19
- "types": "./dist/components/theme-switch/index.d.ts"
20
- },
21
- "./components/viewport-info": {
22
- "import": "./dist/components/viewport-info/index.js",
23
- "types": "./dist/components/viewport-info/index.d.ts"
24
- },
25
17
  "./components/error-boundary": {
26
18
  "import": "./dist/components/error-boundary/index.js",
27
19
  "types": "./dist/components/error-boundary/index.d.ts"
@@ -30,33 +22,41 @@
30
22
  "import": "./dist/hooks/index.js",
31
23
  "types": "./dist/hooks/index.d.ts"
32
24
  },
33
- "./hooks/use-healthcheck": {
34
- "import": "./dist/hooks/use-healthcheck.js",
35
- "types": "./dist/hooks/use-healthcheck.d.ts"
25
+ "./hooks/use-debounce": {
26
+ "import": "./dist/hooks/use-debounce.js",
27
+ "types": "./dist/hooks/use-debounce.d.ts"
36
28
  },
37
- "./utils": {
38
- "import": "./dist/utils/index.js",
39
- "types": "./dist/utils/index.d.ts"
29
+ "./hooks/use-isomorphic-layout-effect": {
30
+ "import": "./dist/hooks/use-isomorphic-layout-effect.js",
31
+ "types": "./dist/hooks/use-isomorphic-layout-effect.d.ts"
32
+ },
33
+ "./hooks/use-local-storage": {
34
+ "import": "./dist/hooks/use-local-storage.js",
35
+ "types": "./dist/hooks/use-local-storage.d.ts"
36
+ },
37
+ "./hooks/use-media-query": {
38
+ "import": "./dist/hooks/use-media-query.js",
39
+ "types": "./dist/hooks/use-media-query.d.ts"
40
40
  },
41
- "./utils/environment": {
42
- "import": "./dist/utils/environment.js",
43
- "types": "./dist/utils/environment.d.ts"
41
+ "./hooks/use-on-click-outside": {
42
+ "import": "./dist/hooks/use-on-click-outside.js",
43
+ "types": "./dist/hooks/use-on-click-outside.d.ts"
44
44
  },
45
- "./utils/msw": {
46
- "import": "./dist/utils/msw.js",
47
- "types": "./dist/utils/msw.d.ts"
45
+ "./hooks/use-previous": {
46
+ "import": "./dist/hooks/use-previous.js",
47
+ "types": "./dist/hooks/use-previous.d.ts"
48
48
  },
49
- "./adapters": {
50
- "import": "./dist/adapters/index.js",
51
- "types": "./dist/adapters/index.d.ts"
49
+ "./utils": {
50
+ "import": "./dist/utils/index.js",
51
+ "types": "./dist/utils/index.d.ts"
52
52
  },
53
- "./adapters/theme": {
54
- "import": "./dist/adapters/theme/index.js",
55
- "types": "./dist/adapters/theme/index.d.ts"
53
+ "./utils/cn": {
54
+ "import": "./dist/utils/cn.js",
55
+ "types": "./dist/utils/cn.d.ts"
56
56
  },
57
- "./adapters/environment": {
58
- "import": "./dist/adapters/environment/index.js",
59
- "types": "./dist/adapters/environment/index.d.ts"
57
+ "./test-utils": {
58
+ "import": "./dist/test-utils/index.js",
59
+ "types": "./dist/test-utils/index.d.ts"
60
60
  },
61
61
  "./types": {
62
62
  "import": "./dist/types.js",
@@ -70,13 +70,10 @@
70
70
  ],
71
71
  "keywords": [
72
72
  "react",
73
- "components",
74
73
  "hooks",
75
74
  "utilities",
76
- "shared",
77
- "typescript",
78
- "nextjs",
79
- "react-router"
75
+ "testing",
76
+ "typescript"
80
77
  ],
81
78
  "author": "teo-garcia",
82
79
  "license": "MIT",
@@ -84,35 +81,53 @@
84
81
  "type": "git",
85
82
  "url": "git+https://github.com/teo-garcia/react-shared.git"
86
83
  },
84
+ "prettier": "@teo-garcia/prettier-config-shared",
87
85
  "packageManager": "pnpm@10.2.0",
88
86
  "engines": {
89
87
  "node": ">=24"
90
88
  },
89
+ "dependencies": {
90
+ "clsx": "^2.1.1",
91
+ "tailwind-merge": "^3.0.0"
92
+ },
91
93
  "peerDependencies": {
92
- "lucide-react": "^0.400.0",
93
94
  "react": "^19.0.0",
94
- "react-dom": "^19.0.0"
95
+ "react-dom": "^19.0.0",
96
+ "@tanstack/react-query": "^5.0.0",
97
+ "@testing-library/react": "^16.0.0"
95
98
  },
96
99
  "peerDependenciesMeta": {
97
- "lucide-react": {
98
- "optional": false
100
+ "@tanstack/react-query": {
101
+ "optional": true
102
+ },
103
+ "@testing-library/react": {
104
+ "optional": true
99
105
  }
100
106
  },
101
107
  "devDependencies": {
108
+ "@teo-garcia/prettier-config-shared": "^0.2.9",
102
109
  "@tanstack/react-query": "^5.0.0",
110
+ "@testing-library/jest-dom": "^6.0.0",
111
+ "@testing-library/react": "^16.0.0",
103
112
  "@types/node": "^24.0.0",
104
113
  "@types/react": "^19.0.0",
105
114
  "@types/react-dom": "^19.0.0",
106
- "msw": "^2.0.0",
107
- "next-themes": "^0.4.0",
115
+ "@vitejs/plugin-react": "^5.0.4",
116
+ "happy-dom": "^16.0.0",
108
117
  "prettier": "^3.0.0",
109
- "typescript": "^5.0.0"
118
+ "react": "^19.0.0",
119
+ "react-dom": "^19.0.0",
120
+ "typescript": "^5.0.0",
121
+ "vite-tsconfig-paths": "^6.0.0",
122
+ "vitest": "^4.0.0"
110
123
  },
111
124
  "scripts": {
112
- "build": "tsc",
113
- "check": "pnpm format:check && pnpm build",
125
+ "build": "rm -rf dist && tsc",
126
+ "check": "pnpm format:check && pnpm build && pnpm test",
127
+ "coverage": "vitest run --coverage",
114
128
  "format": "prettier --write .",
115
129
  "format:check": "prettier --check .",
130
+ "test": "vitest run",
116
131
  "prepublishOnly": "pnpm build"
117
132
  }
118
133
  }
@@ -1,6 +0,0 @@
1
- /**
2
- * Environment adapters - abstract environment detection across different frameworks
3
- */
4
- export { nextEnvironmentAdapter } from './next.js';
5
- export { viteEnvironmentAdapter } from './vite.js';
6
- //# sourceMappingURL=index.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/adapters/environment/index.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EAAE,sBAAsB,EAAE,MAAM,WAAW,CAAA;AAClD,OAAO,EAAE,sBAAsB,EAAE,MAAM,WAAW,CAAA"}
@@ -1,5 +0,0 @@
1
- /**
2
- * Environment adapters - abstract environment detection across different frameworks
3
- */
4
- export { nextEnvironmentAdapter } from './next.js';
5
- export { viteEnvironmentAdapter } from './vite.js';