|
|
@ -6,53 +6,59 @@ import {
|
|
|
|
createContext,
|
|
|
|
createContext,
|
|
|
|
forwardRef,
|
|
|
|
forwardRef,
|
|
|
|
useContext,
|
|
|
|
useContext,
|
|
|
|
} from 'react'
|
|
|
|
} from "react";
|
|
|
|
import { cx } from 'styled-system/css'
|
|
|
|
import { cx } from "styled-system/css";
|
|
|
|
import { type StyledComponent, isCssProperty, styled } from 'styled-system/jsx'
|
|
|
|
import { type StyledComponent, isCssProperty, styled } from "styled-system/jsx";
|
|
|
|
|
|
|
|
|
|
|
|
type Props = Record<string, unknown>
|
|
|
|
type Props = Record<string, unknown>;
|
|
|
|
type Recipe = {
|
|
|
|
type Recipe = {
|
|
|
|
(props?: Props): Props
|
|
|
|
(props?: Props): Props;
|
|
|
|
splitVariantProps: (props: Props) => [Props, Props]
|
|
|
|
splitVariantProps: (props: Props) => [Props, Props];
|
|
|
|
}
|
|
|
|
};
|
|
|
|
type Slot<R extends Recipe> = keyof ReturnType<R>
|
|
|
|
type Slot<R extends Recipe> = keyof ReturnType<R>;
|
|
|
|
type Options = { forwardProps?: string[] }
|
|
|
|
type Options = { forwardProps?: string[] };
|
|
|
|
|
|
|
|
|
|
|
|
const shouldForwardProp = (prop: string, variantKeys: string[], options: Options = {}) =>
|
|
|
|
const shouldForwardProp = (
|
|
|
|
options.forwardProps?.includes(prop) || (!variantKeys.includes(prop) && !isCssProperty(prop))
|
|
|
|
prop: string,
|
|
|
|
|
|
|
|
variantKeys: string[],
|
|
|
|
|
|
|
|
options: Options = {}
|
|
|
|
|
|
|
|
) =>
|
|
|
|
|
|
|
|
options.forwardProps?.includes(prop) ||
|
|
|
|
|
|
|
|
(!variantKeys.includes(prop) && !isCssProperty(prop));
|
|
|
|
|
|
|
|
|
|
|
|
export const createStyleContext = <R extends Recipe>(recipe: R) => {
|
|
|
|
export const createStyleContext = <R extends Recipe>(recipe: R) => {
|
|
|
|
const StyleContext = createContext<Record<Slot<R>, string> | null>(null)
|
|
|
|
const StyleContext = createContext<Record<Slot<R>, string> | null>(null);
|
|
|
|
|
|
|
|
|
|
|
|
const withRootProvider = <P extends {}>(Component: ElementType) => {
|
|
|
|
const withRootProvider = <P extends {}>(Component: ElementType) => {
|
|
|
|
const StyledComponent = (props: P) => {
|
|
|
|
const StyledComponent = (props: P) => {
|
|
|
|
const [variantProps, otherProps] = recipe.splitVariantProps(props)
|
|
|
|
const [variantProps, otherProps] = recipe.splitVariantProps(props);
|
|
|
|
const slotStyles = recipe(variantProps) as Record<Slot<R>, string>
|
|
|
|
const slotStyles = recipe(variantProps) as Record<Slot<R>, string>;
|
|
|
|
|
|
|
|
|
|
|
|
return (
|
|
|
|
return (
|
|
|
|
<StyleContext.Provider value={slotStyles}>
|
|
|
|
<StyleContext.Provider value={slotStyles}>
|
|
|
|
<Component {...otherProps} />
|
|
|
|
<Component {...otherProps} />
|
|
|
|
</StyleContext.Provider>
|
|
|
|
</StyleContext.Provider>
|
|
|
|
)
|
|
|
|
);
|
|
|
|
}
|
|
|
|
};
|
|
|
|
return StyledComponent
|
|
|
|
return StyledComponent;
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
const withProvider = <T, P extends { className?: string | undefined }>(
|
|
|
|
const withProvider = <T, P extends { className?: string | undefined }>(
|
|
|
|
Component: ElementType,
|
|
|
|
Component: ElementType,
|
|
|
|
slot: Slot<R>,
|
|
|
|
slot: Slot<R>,
|
|
|
|
options?: Options,
|
|
|
|
options?: Options
|
|
|
|
): ForwardRefExoticComponent<PropsWithoutRef<P> & RefAttributes<T>> => {
|
|
|
|
): ForwardRefExoticComponent<PropsWithoutRef<P> & RefAttributes<T>> => {
|
|
|
|
const StyledComponent = styled(
|
|
|
|
const StyledComponent = styled(
|
|
|
|
Component,
|
|
|
|
Component,
|
|
|
|
{},
|
|
|
|
{},
|
|
|
|
{
|
|
|
|
{
|
|
|
|
shouldForwardProp: (prop, variantKeys) => shouldForwardProp(prop, variantKeys, options),
|
|
|
|
shouldForwardProp: (prop, variantKeys) =>
|
|
|
|
},
|
|
|
|
shouldForwardProp(prop, variantKeys, options),
|
|
|
|
) as StyledComponent<ElementType>
|
|
|
|
}
|
|
|
|
|
|
|
|
) as StyledComponent<ElementType>;
|
|
|
|
const StyledSlotProvider = forwardRef<T, P>((props, ref) => {
|
|
|
|
const StyledSlotProvider = forwardRef<T, P>((props, ref) => {
|
|
|
|
const [variantProps, otherProps] = recipe.splitVariantProps(props)
|
|
|
|
const [variantProps, otherProps] = recipe.splitVariantProps(props);
|
|
|
|
const slotStyles = recipe(variantProps) as Record<Slot<R>, string>
|
|
|
|
const slotStyles = recipe(variantProps) as Record<Slot<R>, string>;
|
|
|
|
|
|
|
|
|
|
|
|
return (
|
|
|
|
return (
|
|
|
|
<StyleContext.Provider value={slotStyles}>
|
|
|
|
<StyleContext.Provider value={slotStyles}>
|
|
|
@ -62,34 +68,38 @@ export const createStyleContext = <R extends Recipe>(recipe: R) => {
|
|
|
|
className={cx(slotStyles?.[slot], props.className)}
|
|
|
|
className={cx(slotStyles?.[slot], props.className)}
|
|
|
|
/>
|
|
|
|
/>
|
|
|
|
</StyleContext.Provider>
|
|
|
|
</StyleContext.Provider>
|
|
|
|
)
|
|
|
|
);
|
|
|
|
})
|
|
|
|
});
|
|
|
|
// @ts-expect-error
|
|
|
|
// @ts-expect-error
|
|
|
|
StyledSlotProvider.displayName = Component.displayName || Component.name
|
|
|
|
StyledSlotProvider.displayName = Component.displayName || Component.name;
|
|
|
|
|
|
|
|
|
|
|
|
return StyledSlotProvider
|
|
|
|
return StyledSlotProvider;
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
const withContext = <T, P extends { className?: string | undefined }>(
|
|
|
|
const withContext = <T, P extends { className?: string | undefined }>(
|
|
|
|
Component: ElementType,
|
|
|
|
Component: ElementType,
|
|
|
|
slot: Slot<R>,
|
|
|
|
slot: Slot<R>
|
|
|
|
): ForwardRefExoticComponent<PropsWithoutRef<P> & RefAttributes<T>> => {
|
|
|
|
): ForwardRefExoticComponent<PropsWithoutRef<P> & RefAttributes<T>> => {
|
|
|
|
const StyledComponent = styled(Component)
|
|
|
|
const StyledComponent = styled(Component);
|
|
|
|
const StyledSlotComponent = forwardRef<T, P>((props, ref) => {
|
|
|
|
const StyledSlotComponent = forwardRef<T, P>((props, ref) => {
|
|
|
|
const slotStyles = useContext(StyleContext)
|
|
|
|
const slotStyles = useContext(StyleContext);
|
|
|
|
return (
|
|
|
|
return (
|
|
|
|
<StyledComponent {...props} ref={ref} className={cx(slotStyles?.[slot], props.className)} />
|
|
|
|
<StyledComponent
|
|
|
|
)
|
|
|
|
{...props}
|
|
|
|
})
|
|
|
|
ref={ref}
|
|
|
|
|
|
|
|
className={cx(slotStyles?.[slot], props.className)}
|
|
|
|
|
|
|
|
/>
|
|
|
|
|
|
|
|
);
|
|
|
|
|
|
|
|
});
|
|
|
|
// @ts-expect-error
|
|
|
|
// @ts-expect-error
|
|
|
|
StyledSlotComponent.displayName = Component.displayName || Component.name
|
|
|
|
StyledSlotComponent.displayName = Component.displayName || Component.name;
|
|
|
|
|
|
|
|
|
|
|
|
return StyledSlotComponent
|
|
|
|
return StyledSlotComponent;
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
return {
|
|
|
|
return {
|
|
|
|
withRootProvider,
|
|
|
|
withRootProvider,
|
|
|
|
withProvider,
|
|
|
|
withProvider,
|
|
|
|
withContext,
|
|
|
|
withContext,
|
|
|
|
}
|
|
|
|
};
|
|
|
|
}
|
|
|
|
};
|
|
|
|