Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion packages/components/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,8 @@
"dependencies": {
"@chakra-ui/utils": "2.2.2",
"@ficus-ui/style-system": "workspace:*",
"@ficus-ui/theme": "workspace:*"
"@ficus-ui/theme": "workspace:*",
"react-fast-compare": "3.2.2"
},
"keywords": [],
"author": "",
Expand Down
6 changes: 0 additions & 6 deletions packages/components/src/system/base-elements.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import {
Image as RNImage,
Pressable as RNPressable,
ScrollView as RNScrollView,
Text as RNText,
View as RNView,
Expand All @@ -14,11 +13,6 @@ export const baseRNElements = {
Image: RNImage,
Text: RNText,
ScrollView: RNScrollView,
Pressable: RNPressable,
} as const;

export type BaseRNElements = keyof typeof baseRNElements;

export type BaseRNElementProps<T extends BaseRNElements> = React.ComponentProps<
(typeof baseRNElements)[T]
>;
2 changes: 1 addition & 1 deletion packages/components/src/system/forward-ref.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,6 @@ export function forwardRef<
// @ts-ignore
return forwardReactRef(component) as unknown as ComponentWithAs<
Component,
Props
Omit<Props, 'as'>
>;
}
1 change: 1 addition & 0 deletions packages/components/src/system/index.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
export * from './system';
export * from './forward-ref';
export * from './factory';
export * from './use-style-config';
8 changes: 2 additions & 6 deletions packages/components/src/system/system.types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,27 +15,24 @@ export type FicusProps<T extends RNElementType> = SystemProps<T> & {
};

export type SystemProps<T extends RNElementType> = T extends 'Text'
? BaseSystemProps<TextStyleProps> // Ensure BaseSystemProps always gets a type argument
: BaseSystemProps<Record<string, unknown>>; // Provide a default type argument
? BaseSystemProps<TextStyleProps>
: BaseSystemProps;

export interface AsProps<T extends RNElementType = RNElementType> {
as?: T;
}

// Props for a component with `as` prop to dynamically change the component type
export type PropsOf<T extends RNElementType> = Omit<
NativeElementProps<T>,
'ref'
> &
AsProps<T>;

// Omit common props like `as` or any additional props you define
export type OmitCommonProps<
Target,
OmitAdditionalProps extends keyof any = never,
> = Omit<Target, 'as' | OmitAdditionalProps>;

// Utility type to merge props of the base component and the `as` component
export type RightJoinProps<
SourceProps extends object = {},
OverrideProps extends object = {},
Expand All @@ -52,7 +49,6 @@ export type MergeWithAs<
| RightJoinProps<AsCompProps, AdditionalProps>
) & { as?: AsComponent };

// Component type with `as` prop for dynamic component rendering
export type ComponentWithAs<
Component extends RNElementType,
Props extends object = {},
Expand Down
22 changes: 6 additions & 16 deletions packages/components/src/system/system.utils.ts
Original file line number Diff line number Diff line change
@@ -1,23 +1,13 @@
import {
type BaseRNElementProps,
type BaseRNElements,
baseRNElements,
} from './base-elements';

/**
* To allow user to extend custom component with ficus properties
*/
type CustomNativeElementProps<T extends React.ComponentType<any>> =
React.ComponentProps<T>;
import { type BaseRNElements, baseRNElements } from './base-elements';

export type RNElementType = BaseRNElements | React.ComponentType<any>;

export type NativeElementProps<T extends RNElementType> =
T extends BaseRNElements
? BaseRNElementProps<T>
: T extends React.ComponentType<any>
? CustomNativeElementProps<T>
: never;
React.ComponentPropsWithRef<
T extends BaseRNElements
? (typeof baseRNElements)[T]
: React.ComponentType<any>
>;

export function getComponent<T extends RNElementType>(component: T) {
return typeof component === 'string'
Expand Down
73 changes: 73 additions & 0 deletions packages/components/src/system/use-style-config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
import { useRef } from 'react';

import { compact, get, mergeWith, omit } from '@chakra-ui/utils';
import {
Dict,
type SystemStyleObject,
type ThemingProps,
resolveStyleConfig,
} from '@ficus-ui/style-system';
import { useTheme } from '@ficus-ui/theme';
import isEqual from 'react-fast-compare';

type StylesRef = SystemStyleObject | Record<string, SystemStyleObject>;

function useStyleConfigFn(
themeKey: string | null,
props: ThemingProps & Record<string, any> = {}
) {
const { styleConfig: styleConfigProp, ...rest } = props;

const { theme } = useTheme();

const themeStyleConfig = themeKey
? get(theme, `components.${themeKey}`)
: undefined;

const styleConfig = styleConfigProp || themeStyleConfig;

const mergedProps = mergeWith(
{ theme },
styleConfig?.defaultProps ?? {},
compact(omit(rest, ['children'])),
(obj, src) => (!obj ? src : undefined)
);

/**
* Store the computed styles in a `ref` to avoid unneeded re-computation
*/
const stylesRef = useRef<StylesRef>({});

if (styleConfig) {
const getStyles = resolveStyleConfig(styleConfig);
const styles = getStyles(mergedProps);

const isStyleEqual = isEqual(stylesRef.current, styles);

if (!isStyleEqual) {
stylesRef.current = styles;
}
}

return stylesRef.current;
}

/**
* Allow to resolve style config from the theme and merge it with props passed by the user.
*/
export function useStyleConfig(
themeKey: string,
props: ThemingProps & Record<string, any> = {}
) {
return useStyleConfigFn(themeKey, props) as SystemStyleObject;
}

/**
* Allow to resolve style config for multi parts component
*/
export function useMultiStyleConfig(
themeKey: string,
props: ThemingProps & Dict = {}
) {
return useStyleConfigFn(themeKey, props) as Record<string, SystemStyleObject>;
}
2 changes: 0 additions & 2 deletions packages/style-system/src/config/colors.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,12 @@ import { Config } from '../utils/prop-config';

export const color: Config<ColorProps> = {
color: t.colors('color'),
textColor: t.colors('color'),
overlayColor: t.colors('overlayColor'),
shadowColor: t.colors('shadowColor'),
};

export interface ColorProps {
color?: string;
textColor?: string;
overlayColor?: string;
shadowColor?: string;
}
2 changes: 1 addition & 1 deletion packages/style-system/src/config/flexbox.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ export const flexbox: Config<FlexboxProps> = {
shrink: t.flexbox('flexShrink'),
justifyContent: true,
justify: t.flexbox('justifyContent'),
alignSelf: t.flexbox('alignItems'),
alignSelf: t.flexbox('alignSelf'),
alignItems: true,
align: t.flexbox('alignItems'),
};
Expand Down
2 changes: 2 additions & 0 deletions packages/style-system/src/config/text.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import { transforms } from '../utils/transform-functions';
import { ResponsiveValue } from '../utils/types';

export const text: Config<TextStyleProps> = {
textColor: t.colors('color'),
fontSize: t.prop('fontSize', 'fontSizes', transforms.getThemeProp),
textDecorLine: t.prop('textDecorationLine'),
textDecorStyle: t.prop('textDecorationStyle'),
Expand Down Expand Up @@ -34,6 +35,7 @@ export const text: Config<TextStyleProps> = {
* Only for React native Text Component
*/
export interface TextStyleProps {
textColor?: string;
fontSize?: ResponsiveValue<string | number>;
textDecorLine?: ResponsiveValue<TextStyle['textDecorationLine']>;
textDecorStyle?: ResponsiveValue<TextStyle['textDecorationStyle']>;
Expand Down
64 changes: 60 additions & 4 deletions packages/style-system/src/define-style.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,14 +26,14 @@ export function defineStyle<T extends SystemStyleInterpolation>(styles: T) {
type DefaultProps = {
size?: string;
variant?: string;
colorScheme: string;
colorScheme?: string;
};

export type StyleConfig = {
baseStyle?: SystemStyleInterpolation;
sizes?: { [size: string]: SystemStyleInterpolation };
variants?: { [variant: string]: SystemStyleInterpolation };
defaultProps: DefaultProps;
defaultProps?: DefaultProps;
};

export function defineStyleConfig<
Expand All @@ -42,8 +42,8 @@ export function defineStyleConfig<
Variants extends Dict<SystemStyleInterpolation>,
>(config: {
baseStyle?: BaseStyle;
sizes: Sizes;
variants: Variants;
sizes?: Sizes;
variants?: Variants;
defaultProps?: {
size?: keyof Sizes;
variant?: keyof Variants;
Expand All @@ -54,3 +54,59 @@ export function defineStyleConfig<
}

// ------------------------------------------------------------------ //

type Anatomy = string[];

export type PartsStyleObject<Parts extends Anatomy = Anatomy> = Partial<
Record<Parts[number], SystemStyleObject>
>;

export type PartsStyleFunction<Parts extends Anatomy = Anatomy> = (
props: StyleFunctionProps
) => PartsStyleObject<Parts>;

export type PartsStyleInterpolation<Parts extends Anatomy = Anatomy> =
| PartsStyleObject<Parts>
| PartsStyleFunction<Parts>;

export interface MultiStyleConfig<Parts extends Anatomy = Anatomy> {
parts: Parts['keys'];
baseStyle?: PartsStyleInterpolation<Parts>;
sizes?: { [size: string]: PartsStyleInterpolation<Parts> };
variants?: { [variant: string]: PartsStyleInterpolation<Parts> };
defaultProps?: DefaultProps;
}

// ------------------------------------------------------------------ //

/**
* Returns an object of helpers that can be used to define
* the style configuration for a multi-part component.
*/
export function createMultiStyleConfigHelpers<Part extends string>(
parts: Part[] | Readonly<Part[]>
) {
return {
definePartsStyle<PartStyles extends PartsStyleInterpolation<Part[]>>(
config: PartStyles
) {
return config;
},
defineMultiStyleConfig<
BaseStyle extends PartsStyleInterpolation<Part[]>,
Sizes extends Dict<PartsStyleInterpolation<Part[]>>,
Variants extends Dict<PartsStyleInterpolation<Part[]>>,
>(config: {
baseStyle?: BaseStyle;
sizes?: Sizes;
variants?: Variants;
defaultProps?: {
size?: keyof Sizes;
variant?: keyof Variants;
colorScheme?: string;
};
}) {
return { parts: parts as Part[], ...config };
},
};
}
1 change: 1 addition & 0 deletions packages/style-system/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,3 +6,4 @@ export * from './define-style';
export * from './style-sheet';
export * from './utils';
export * from './config';
export * from './style-config';
Loading
Loading