mirror of
https://github.com/felipegcoutinho/openmonetis.git
synced 2026-05-09 11:01:45 +00:00
refactor(core): centraliza hooks, providers e base compartilhada
This commit is contained in:
3
lib/hooks/index.ts
Normal file
3
lib/hooks/index.ts
Normal file
@@ -0,0 +1,3 @@
|
||||
export { useControlledState } from "./use-controlled-state";
|
||||
export { useFormState } from "./use-form-state";
|
||||
export { useIsMobile, useMobile } from "./use-mobile";
|
||||
51
lib/hooks/use-controlled-state.ts
Normal file
51
lib/hooks/use-controlled-state.ts
Normal file
@@ -0,0 +1,51 @@
|
||||
import { useCallback, useEffect, useState } from "react";
|
||||
|
||||
/**
|
||||
* Hook for managing controlled/uncontrolled state pattern
|
||||
* Allows a component to work both in controlled and uncontrolled mode
|
||||
*
|
||||
* @param controlledValue - The controlled value (undefined for uncontrolled mode)
|
||||
* @param defaultValue - Default value for uncontrolled mode
|
||||
* @param onChange - Callback when value changes
|
||||
* @returns Tuple of [currentValue, setValue]
|
||||
*
|
||||
* @example
|
||||
* ```tsx
|
||||
* function MyComponent({ value, onChange }) {
|
||||
* const [internalValue, setValue] = useControlledState(value, false, onChange);
|
||||
* // Works both as controlled and uncontrolled
|
||||
* }
|
||||
* ```
|
||||
*/
|
||||
export function useControlledState<T>(
|
||||
controlledValue: T | undefined,
|
||||
defaultValue: T,
|
||||
onChange?: (value: T) => void,
|
||||
): [T, (value: T) => void] {
|
||||
const [internalValue, setInternalValue] = useState<T>(defaultValue);
|
||||
|
||||
// Sync internal value when controlled value changes
|
||||
useEffect(() => {
|
||||
if (controlledValue !== undefined) {
|
||||
setInternalValue(controlledValue);
|
||||
}
|
||||
}, [controlledValue]);
|
||||
|
||||
// Use controlled value if provided, otherwise use internal value
|
||||
const value = controlledValue !== undefined ? controlledValue : internalValue;
|
||||
|
||||
const setValue = useCallback(
|
||||
(newValue: T) => {
|
||||
// Update internal state if uncontrolled
|
||||
if (controlledValue === undefined) {
|
||||
setInternalValue(newValue);
|
||||
}
|
||||
|
||||
// Always call onChange if provided
|
||||
onChange?.(newValue);
|
||||
},
|
||||
[controlledValue, onChange],
|
||||
);
|
||||
|
||||
return [value, setValue];
|
||||
}
|
||||
60
lib/hooks/use-form-state.ts
Normal file
60
lib/hooks/use-form-state.ts
Normal file
@@ -0,0 +1,60 @@
|
||||
import { useCallback, useRef, useState } from "react";
|
||||
|
||||
/**
|
||||
* Hook for managing form state with type-safe field updates
|
||||
*
|
||||
* @param initialValues - Initial form values
|
||||
* @returns Object with formState, updateField, updateFields, replaceForm, resetForm
|
||||
*
|
||||
* @example
|
||||
* ```tsx
|
||||
* const { formState, updateField, resetForm } = useFormState({
|
||||
* name: '',
|
||||
* email: ''
|
||||
* });
|
||||
*
|
||||
* updateField('name', 'John');
|
||||
* ```
|
||||
*/
|
||||
export function useFormState<T extends object>(initialValues: T) {
|
||||
const latestInitialValuesRef = useRef(initialValues);
|
||||
latestInitialValuesRef.current = initialValues;
|
||||
|
||||
const [formState, setFormState] = useState<T>(initialValues);
|
||||
|
||||
/**
|
||||
* Updates a single field in the form state
|
||||
*/
|
||||
const updateField = useCallback(
|
||||
<K extends keyof T>(field: K, value: T[K]) => {
|
||||
setFormState((prev) => ({ ...prev, [field]: value }));
|
||||
},
|
||||
[],
|
||||
);
|
||||
|
||||
/**
|
||||
* Resets form to initial values
|
||||
*/
|
||||
const resetForm = useCallback((nextValues?: T) => {
|
||||
setFormState(nextValues ?? latestInitialValuesRef.current);
|
||||
}, []);
|
||||
|
||||
/**
|
||||
* Updates multiple fields at once
|
||||
*/
|
||||
const updateFields = useCallback((updates: Partial<T>) => {
|
||||
setFormState((prev) => ({ ...prev, ...updates }));
|
||||
}, []);
|
||||
|
||||
const replaceForm = useCallback((nextValues: T) => {
|
||||
setFormState(nextValues);
|
||||
}, []);
|
||||
|
||||
return {
|
||||
formState,
|
||||
updateField,
|
||||
updateFields,
|
||||
replaceForm,
|
||||
resetForm,
|
||||
};
|
||||
}
|
||||
28
lib/hooks/use-mobile.ts
Normal file
28
lib/hooks/use-mobile.ts
Normal file
@@ -0,0 +1,28 @@
|
||||
import * as React from "react";
|
||||
|
||||
const MOBILE_BREAKPOINT = 768;
|
||||
const MOBILE_MEDIA_QUERY = `(max-width: ${MOBILE_BREAKPOINT - 1}px)`;
|
||||
|
||||
export function useIsMobile() {
|
||||
const subscribe = React.useCallback((onStoreChange: () => void) => {
|
||||
if (typeof window === "undefined") {
|
||||
return () => {};
|
||||
}
|
||||
|
||||
const mediaQueryList = window.matchMedia(MOBILE_MEDIA_QUERY);
|
||||
mediaQueryList.addEventListener("change", onStoreChange);
|
||||
return () => mediaQueryList.removeEventListener("change", onStoreChange);
|
||||
}, []);
|
||||
|
||||
const getSnapshot = React.useCallback(() => {
|
||||
if (typeof window === "undefined") {
|
||||
return false;
|
||||
}
|
||||
|
||||
return window.matchMedia(MOBILE_MEDIA_QUERY).matches;
|
||||
}, []);
|
||||
|
||||
return React.useSyncExternalStore(subscribe, getSnapshot, () => false);
|
||||
}
|
||||
|
||||
export const useMobile = useIsMobile;
|
||||
Reference in New Issue
Block a user