Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[pigment-css] Add Box component #41451

Merged
merged 1 commit into from
Mar 15, 2024
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
6 changes: 6 additions & 0 deletions packages/pigment-css-react/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -132,6 +132,12 @@
},
"./exports/createUseThemeProps": {
"default": "./exports/createUseThemeProps.js"
},
"./Box": {
"types": "./build/Box.d.ts",
"import": "./build/Box.mjs",
"require": "./build/Box.js",
"default": "./build/Box.js"
}
},
"nx": {
Expand Down
26 changes: 26 additions & 0 deletions packages/pigment-css-react/src/Box.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import type { BaseDefaultProps, Substitute, NoInfer } from './base';
import type { SxProp } from './sx';

export type PolymorphicComponentProps<
BaseProps extends object,
AsTarget extends React.ElementType | undefined,
AsTargetProps extends object = AsTarget extends React.ElementType
? React.ComponentPropsWithRef<AsTarget>
: BaseDefaultProps,
> = NoInfer<Omit<Substitute<BaseProps, AsTargetProps>, 'as' | 'component'>> & {
as?: AsTarget;
component?: AsTarget;
sx?: SxProp;
children?: React.ReactNode;
};

export interface PolymorphicComponent<BaseProps extends BaseDefaultProps>
extends React.ForwardRefExoticComponent<BaseProps> {
<AsTarget extends React.ElementType | undefined = undefined>(
props: PolymorphicComponentProps<BaseProps, AsTarget>,
): JSX.Element;
}

declare const Box: PolymorphicComponent<{}>;

export { Box };
51 changes: 51 additions & 0 deletions packages/pigment-css-react/src/Box.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
import * as React from 'react';

// eslint-disable-next-line react/prop-types
export const Box = React.forwardRef(
(
{
as = 'div',
// Added to support compatibility with @mui/system
component,
/**
* The type of the transformed sx prop is either a
* "string" if the css passed was fully static or an
* object with the following shape:
* {
* className: string,
* vars: Record<string, [string | number, boolean]>
* }
*/
sx,
className,
style,
...rest
},
ref,
) => {
const Component = component ?? as;
// eslint-disable-next-line react/prop-types
const sxClass = typeof sx === 'string' ? sx : sx?.className;
const classes = [className, sxClass].filter(Boolean).join(' ');
// eslint-disable-next-line react/prop-types
const sxVars = sx && typeof sx !== 'string' ? sx?.vars : {};
const varStyles = {};

if (sxVars) {
Object.entries(sxVars).forEach(([cssVariable, [value, isUnitLess]]) => {
if (typeof value === 'string' || isUnitLess) {
varStyles[`--${cssVariable}`] = value;
} else {
varStyles[`--${cssVariable}`] = `${value}px`;
}
});
}

const styles = {
...style,
...varStyles,
};

return <Component ref={ref} className={classes} style={styles} {...rest} />;
},
);
29 changes: 29 additions & 0 deletions packages/pigment-css-react/src/base.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,3 +35,32 @@ export type CSSObjectNoCallback =
| CSSPropertiesMultiValue
| CSSPseudosNoCallback
| CSSOthersObjectNoCallback;

export type BaseDefaultProps = object;

export type NoInfer<T> = [T][T extends any ? 0 : never];
type FastOmit<T extends object, U extends string | number | symbol> = {
[K in keyof T as K extends U ? never : K]: T[K];
};

export type Substitute<A extends object, B extends object> = FastOmit<A, keyof B> & B;
Comment on lines +41 to +46
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's find time to comment on the purpose of these types later 😅. I can imagine how hard to make the PolymorphicComponent work.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah. Definitely. I hope this doesn't get more complex than this 🤣.


export type PolymorphicComponentProps<
SxProp,
BaseProps extends object,
AsTarget extends React.ElementType | undefined,
AsTargetProps extends object = AsTarget extends React.ElementType
? React.ComponentPropsWithRef<AsTarget>
: BaseDefaultProps,
> = NoInfer<Omit<Substitute<BaseProps, AsTargetProps>, 'as'>> & {
as?: AsTarget;
sx?: SxProp;
children?: React.ReactNode;
};

export interface PolymorphicComponent<SxProp, BaseProps extends BaseDefaultProps>
extends React.ForwardRefExoticComponent<BaseProps> {
<AsTarget extends React.ElementType | undefined = undefined>(
props: PolymorphicComponentProps<SxProp, BaseProps, AsTarget>,
): JSX.Element;
}
2 changes: 1 addition & 1 deletion packages/pigment-css-react/src/processors/sx.ts
Original file line number Diff line number Diff line change
Expand Up @@ -94,7 +94,7 @@ export class SxProcessor extends BaseProcessor {

doRuntimeReplacement() {
const t = this.astService;
// do not replace if sx prop is not on zero-runtime styled component
// do not replace if sx prop is not a Pigment styled component
if (!this.elementClassName) {
return;
}
Expand Down
30 changes: 2 additions & 28 deletions packages/pigment-css-react/src/styled.d.ts
Original file line number Diff line number Diff line change
@@ -1,19 +1,11 @@
import type * as React from 'react';
import type { CSSObject } from './base';
import type { BaseDefaultProps, CSSObject, PolymorphicComponent, Substitute } from './base';
import type { ThemeArgs } from './theme';
import type { SxProp } from './sx';
import { Primitve } from './keyframes';

type Falsy = false | 0 | '' | null | undefined;

type BaseDefaultProps = object;

export type NoInfer<T> = [T][T extends any ? 0 : never];
type FastOmit<T extends object, U extends string | number | symbol> = {
[K in keyof T as K extends U ? never : K]: T[K];
};
export type Substitute<A extends object, B extends object> = FastOmit<A, keyof B> & B;

export interface StyledVariants<Props extends BaseDefaultProps> {
props: Partial<Props> | ((props: Props) => boolean);
style: CSSObject<Props>;
Expand All @@ -31,26 +23,8 @@ export type StyledArgument<Props extends BaseDefaultProps> =
| StyledCssArgument<Props>
| StyledCallback<Props>;

export type PolymorphicComponentProps<
BaseProps extends object,
AsTarget extends React.ElementType | undefined,
AsTargetProps extends object = AsTarget extends React.ElementType
? React.ComponentPropsWithRef<AsTarget>
: BaseDefaultProps,
> = NoInfer<Omit<Substitute<BaseProps, AsTargetProps>, 'as'>> & {
as?: AsTarget;
sx?: SxProp;
};

export interface PolymorphicComponent<BaseProps extends BaseDefaultProps>
extends React.ForwardRefExoticComponent<BaseProps> {
<AsTarget extends React.ElementType | undefined = undefined>(
props: PolymorphicComponentProps<BaseProps, AsTarget>,
): JSX.Element;
}

export interface StyledComponent<Props extends BaseDefaultProps = BaseDefaultProps>
extends PolymorphicComponent<Props> {
extends PolymorphicComponent<SxProp, Props> {
defaultProps?: Partial<Props> | undefined;
toString: () => string;
}
Expand Down
22 changes: 22 additions & 0 deletions packages/pigment-css-react/tests/Box.spec.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import * as React from 'react';
import { Box } from '@pigment-css/react/Box';

export function App() {
return (
<Box as="div" sx={{ display: 'flex' }}>
brijeshb42 marked this conversation as resolved.
Show resolved Hide resolved
<Box as="p" sx={() => ({ color: 'primary' })}>
Hello{' '}
<Box as="a" href="https://mui.com" download>
Link
</Box>
<Box component="dialog" open>
Dialog
</Box>
{/* @ts-expect-error */}
<Box component="dialog" as="button" open>
Dialog 2
</Box>
</Box>
</Box>
);
}
2 changes: 1 addition & 1 deletion packages/pigment-css-react/tsup.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ const baseConfig: Options = {
export default defineConfig([
{
...baseConfig,
entry: ['./src/index.ts', './src/theme.ts'],
entry: ['./src/index.ts', './src/theme.ts', './src/Box.jsx'],
},
{
...baseConfig,
Expand Down
Loading