consensys-ui
Version:
Consensys UI component library and design system
161 lines (146 loc) • 4.45 kB
text/typescript
import type {
WebButtonProps,
NativeButtonProps,
SharedButtonProps,
} from './types';
import type { WebClickEvent } from '../../types';
import type { AccessibilityRole, GestureResponderEvent } from 'react-native';
/**
* Identifies web-specific props (onClick, type, form)
* @returns true if props contain web-specific properties
*/
export function isWebButtonProps(props: unknown): props is WebButtonProps {
if (!props || typeof props !== 'object') return false;
const p = props as Partial<WebButtonProps>;
return typeof p.onClick === 'function' || 'type' in p || 'form' in p || 'formAction' in p;
}
/**
* Identifies native-specific props (onPress, onPressIn, onPressOut)
* @returns true if props contain native-specific properties
*/
export function isNativeButtonProps(props: unknown): props is NativeButtonProps {
if (!props || typeof props !== 'object') return false;
const p = props as Partial<NativeButtonProps>;
return typeof p.onPress === 'function' || typeof p.onPressIn === 'function' || typeof p.onPressOut === 'function';
}
/**
* Native prop validator - warns if web props detected
* - No props are filtered out (React Native ignores unrecognized props)
* - Web props like onClick, type will be ignored by native components
*/
export function toNativeProps(props: unknown): NativeButtonProps {
if (!props || typeof props !== 'object') {
return {} as NativeButtonProps;
}
if (isWebButtonProps(props)) {
console.warn('[CUI Button] - Web props detected in native environment. This is not supported and props will be ignored.');
}
return props as NativeButtonProps;
}
/**
* Converts native props to web props for hybrid support
* Maps:
* - onPress → onClick
* - onPressIn → onMouseDown
* - onPressOut → onMouseUp
*/
export function toWebProps(props: unknown): WebButtonProps {
if (!props || typeof props !== 'object') {
console.warn('[CUI Button] - Props must be an object');
}
if (isWebButtonProps(props)) {
return props as WebButtonProps;
}
if (!isNativeButtonProps(props)) {
console.warn('[CUI Button] - Mixed props detected. Props must be either web or native, not both.');
}
const {
onPress,
onPressIn,
onPressOut,
...restNative
} = props as NativeButtonProps;
const {
onClick,
onMouseDown,
onMouseUp,
...rest
} = restNative as WebButtonProps;
return {
...rest,
type: 'button',
onClick: onClick || onPress,
onMouseDown: onMouseDown || onPressIn,
onMouseUp: onMouseUp || onPressOut,
} as WebButtonProps;
}
/**
* Web accessibility props
* Maps:
* - disabled → aria-disabled
* - loading → aria-busy
*/
export const getWebButtonAccessibilityProps = (props: Partial<SharedButtonProps>) => {
const { disabled, loading } = props;
return {
'aria-disabled': disabled,
'aria-busy': loading,
role: 'button',
};
};
/**
* Native accessibility props
* Maps:
* - disabled → accessibilityState.disabled
* - loading → accessibilityState.busy
*/
export const getNativeButtonAccessibilityProps = (props: Partial<SharedButtonProps>) => {
const { disabled, loading } = props;
return {
accessibilityRole: 'button' as AccessibilityRole,
accessibilityState: {
disabled,
busy: loading,
},
};
};
/**
* Creates press event handlers for both web and native platforms
* Handles:
* - Press events (press in/out)
* - State management (pressed)
*/
export const createPressHandlers = <T extends GestureResponderEvent | WebClickEvent>(
setIsPressed: (isPressed: boolean) => void,
onPressIn?: ((e: T) => void) | null,
onPressOut?: ((e: T) => void) | null,
) => ({
handlePressIn: (event: T) => {
setIsPressed(true);
onPressIn?.(event);
},
handlePressOut: (event: T) => {
setIsPressed(false);
onPressOut?.(event);
},
});
/**
* Creates hover event handlers for web platform only
* Handles:
* - Hover events (hover in/out)
* - State management (hovered)
*/
export const createHoverHandlers = (
setIsHovered: (isHovered: boolean) => void,
onMouseEnter?: ((e: React.MouseEvent<Element>) => void) | null,
onMouseLeave?: ((e: React.MouseEvent<Element>) => void) | null,
) => ({
handleHoverIn: (event: React.MouseEvent<Element>) => {
setIsHovered(true);
onMouseEnter?.(event);
},
handleHoverOut: (event: React.MouseEvent<Element>) => {
setIsHovered(false);
onMouseLeave?.(event);
},
});