feat: Add generic components for UI
This commit is contained in:
38 files changed
+1562
-241
No files matched your search
@@ -31,12 +31,18 @@
|
||||
"@ermnvldmr/eslint-config": "workspace:*",
|
||||
"@ermnvldmr/jest-config": "workspace:*",
|
||||
"@ermnvldmr/vite-config": "workspace:*",
|
||||
"@types/react": "^19.1.2",
|
||||
"@types/react-dom": "^19.1.2",
|
||||
"jest": "^30.2.0",
|
||||
"react": "^19.1.0",
|
||||
"react-dom": "^19.1.0",
|
||||
"ts-jest": "^29.4.6",
|
||||
"typescript": "^5.8.3",
|
||||
"vite": "^7.2.2"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"react": "^19.1.0",
|
||||
"react-dom": "^19.1.0",
|
||||
"tailwindcss": "^4.1.4"
|
||||
}
|
||||
}
|
||||
@@ -2,4 +2,4 @@
|
||||
* Shared helper utilities for common functionality.
|
||||
*/
|
||||
|
||||
export { cn } from './cn/cn';
|
||||
export { cn } from './cn/cn';
|
||||
@@ -1,2 +1,3 @@
|
||||
export * from './types';
|
||||
export * from './functions';
|
||||
export * from './react';
|
||||
@@ -0,0 +1,11 @@
|
||||
import { castRef } from './castRef';
|
||||
|
||||
import type React from 'react';
|
||||
|
||||
describe('react/castRef', () => {
|
||||
it('should cast a ref to a different type', () => {
|
||||
const ref = { current: null } as unknown as React.ForwardedRef<HTMLDivElement>;
|
||||
const casted = castRef<HTMLSpanElement>(ref);
|
||||
expect(casted).toBe(ref);
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,19 @@
|
||||
import type React from 'react';
|
||||
|
||||
/**
|
||||
* Safely casts a React ref to a specific type.
|
||||
* This is a controlled escape hatch for situations where TypeScript's
|
||||
* built-in Ref types are too restrictive (e.g., in generic forwardRef components).
|
||||
*
|
||||
* @param ref - The ref to cast
|
||||
* @returns The casted ref
|
||||
* @example
|
||||
* ```tsx
|
||||
* <Component ref={castRef<HTMLDivElement>(ref)} />
|
||||
* ```
|
||||
*/
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
export function castRef<T>(ref: React.ForwardedRef<any>): React.ForwardedRef<T> {
|
||||
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions
|
||||
return ref as React.ForwardedRef<T>;
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
import React from 'react';
|
||||
|
||||
import { genericMemo } from './genericMemo';
|
||||
|
||||
describe('react/genericMemo', () => {
|
||||
it('should memoize a component', () => {
|
||||
const Component = ({ name }: { name: string }) => <div>{name}</div>;
|
||||
const Memoized = genericMemo(Component);
|
||||
|
||||
expect(Memoized).toBeDefined();
|
||||
// In React, memo returns a special object with type: component
|
||||
expect((Memoized as any).type).toBe(Component);
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,16 @@
|
||||
import React from 'react';
|
||||
|
||||
/**
|
||||
* A type-safe version of React.memo that preserves generic types.
|
||||
*
|
||||
* @param component - The component to memoize
|
||||
* @returns The memoized component
|
||||
* @example
|
||||
* ```tsx
|
||||
* export const MyComponent = genericMemo(MyGenericComponent);
|
||||
* ```
|
||||
*/
|
||||
export function genericMemo<T>(component: T): T {
|
||||
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions, @typescript-eslint/no-explicit-any
|
||||
return React.memo(component as any) as any as T;
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
export { castRef } from './castRef/castRef';
|
||||
export { genericMemo } from './genericMemo/genericMemo';
|
||||
|
||||
import type React from 'react';
|
||||
|
||||
/**
|
||||
* Common props for any React element.
|
||||
* Useful for forwarding props to dynamic components.
|
||||
*/
|
||||
export type ReactElementProps = React.HTMLAttributes<HTMLElement> & React.AllHTMLAttributes<HTMLElement>;
|
||||
Reference in new issue
Block a user