Skip to content

Commit

Permalink
[core] Implement useTheme processor
Browse files Browse the repository at this point in the history
to replace the useTheme function calls with an import from Pigment
package
  • Loading branch information
brijeshb42 committed May 20, 2024
1 parent b4e2d77 commit 2e9c97b
Show file tree
Hide file tree
Showing 12 changed files with 755 additions and 115 deletions.
5 changes: 5 additions & 0 deletions packages/pigment-css-react/exports/useTheme.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
Object.defineProperty(exports, '__esModule', {
value: true,
});

exports.default = require('../processors/useTheme').UseThemeProcessor;
7 changes: 4 additions & 3 deletions packages/pigment-css-react/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -41,8 +41,8 @@
"@emotion/react": "^11.11.4",
"@emotion/serialize": "^1.1.4",
"@emotion/styled": "^11.11.5",
"@mui/system": "^6.0.0-alpha.1",
"@mui/utils": "^6.0.0-alpha.1",
"@mui/system": "^6.0.0-alpha.6",
"@mui/utils": "^6.0.0-alpha.6",
"@wyw-in-js/processor-utils": "^0.5.3",
"@wyw-in-js/shared": "^0.5.3",
"@wyw-in-js/transform": "^0.5.3",
Expand Down Expand Up @@ -85,7 +85,8 @@
"keyframes": "./exports/keyframes.js",
"generateAtomics": "./exports/generateAtomics.js",
"css": "./exports/css.js",
"createUseThemeProps": "./exports/createUseThemeProps.js"
"createUseThemeProps": "./exports/createUseThemeProps.js",
"useTheme": "./exports/useTheme.js"
}
},
"files": [
Expand Down
36 changes: 1 addition & 35 deletions packages/pigment-css-react/src/RtlProvider.tsx
Original file line number Diff line number Diff line change
@@ -1,37 +1,3 @@
'use client';
/**
* This package has it's own version of RtlProvider to avoid including
* @mui/system in the bundle if someone is not using it.
*/
import * as React from 'react';
import PropTypes from 'prop-types';

type RtlContextType = boolean | undefined;

type RtlProviderProps = {
value?: RtlContextType;
};

const RtlContext = React.createContext<RtlContextType>(false);

function RtlProvider({ value, ...props }: RtlProviderProps) {
return <RtlContext.Provider value={value ?? true} {...props} />;
}

RtlProvider.propTypes /* remove-proptypes */ = {
// ┌────────────────────────────── Warning ──────────────────────────────┐
// │ These PropTypes are generated from the TypeScript type definitions. │
// │ To update them, edit the TypeScript types and run `pnpm proptypes`. │
// └─────────────────────────────────────────────────────────────────────┘
/**
* @ignore
*/
value: PropTypes.bool,
} as any;

export const useRtl = () => {
const value = React.useContext(RtlContext);
return value ?? false;
};

export default RtlProvider;
export * from '@mui/system/RtlProvider';
64 changes: 64 additions & 0 deletions packages/pigment-css-react/src/processors/useTheme.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
import type { Expression } from '@babel/types';
import { validateParams, type Params, type TailProcessorParams } from '@wyw-in-js/processor-utils';
import { type Replacements, type Rules } from '@wyw-in-js/shared';
import BaseProcessor from './base-processor';

export type Primitive = string | number | boolean | null | undefined;

export type TemplateCallback = (params: Record<string, unknown> | undefined) => string | number;

export class UseThemeProcessor extends BaseProcessor {
constructor(params: Params, ...args: TailProcessorParams) {
super([params[0]], ...args);
validateParams(params, ['callee', ['call']], `Invalid use of ${this.tagSource.imported} tag.`);
}

build() {
const cssText = '/* */';
const rules: Rules = {
[this.asSelector]: {
className: this.className,
cssText,
displayName: this.displayName,
start: this.location?.start ?? null,
},
};
const sourceMapReplacements: Replacements = [
{
length: cssText.length,
original: {
start: {
column: this.location?.start.column ?? 0,
line: this.location?.start.line ?? 0,
},
end: {
column: this.location?.end.column ?? 0,
line: this.location?.end.line ?? 0,
},
},
},
];
this.artifacts.push(['css', [rules, sourceMapReplacements]]);
}

doEvaltimeReplacement() {
this.replacer(this.value, false);
}

doRuntimeReplacement() {
const t = this.astService;
const useThemeIdentifier = t.addDefaultImport(
this.tagSource.imported,
`${process.env.PACKAGE_NAME as string}/theme`,
);
this.replacer(t.callExpression(useThemeIdentifier, [t.booleanLiteral(true)]), false);
}

get asSelector() {
return this.className;
}

get value(): Expression {
return this.astService.stringLiteral(this.className);
}
}
1 change: 0 additions & 1 deletion packages/pigment-css-react/src/styled.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
/* eslint-disable no-underscore-dangle */
import * as React from 'react';
import clsx from 'clsx';
import isPropValid from '@emotion/is-prop-valid';
Expand Down
2 changes: 1 addition & 1 deletion packages/pigment-css-react/tests/testUtils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ export async function runTransformation(
plugins: ['@babel/plugin-syntax-jsx'],
},
tagResolver(source: string, tag: string) {
if (source !== '@pigment-css/react') {
if (source !== '@pigment-css/react' && !source.endsWith('/zero-styled')) {
return null;
}
return require.resolve(`../exports/${tag}`);
Expand Down
125 changes: 125 additions & 0 deletions packages/pigment-css-react/tests/useTheme/fixtures/useTheme.input.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,125 @@
import { useTheme } from '../zero-styled';

export const Fade = React.forwardRef(function Fade(props, ref) {
const theme = useTheme();
const defaultTimeout = {
enter: theme.transitions.duration.enteringScreen,
exit: theme.transitions.duration.leavingScreen,
};

const {
addEndListener,
appear = true,
children,
easing,
in: inProp,
onEnter,
onEntered,
onEntering,
onExit,
onExited,
onExiting,
style,
timeout = defaultTimeout,
// eslint-disable-next-line react/prop-types
TransitionComponent = Transition,
...other
} = props;

const enableStrictModeCompat = true;
const nodeRef = React.useRef(null);
const handleRef = useForkRef(nodeRef, children.ref, ref);

const normalizedTransitionCallback = (callback) => (maybeIsAppearing) => {
if (callback) {
const node = nodeRef.current;

// onEnterXxx and onExitXxx callbacks have a different arguments.length value.
if (maybeIsAppearing === undefined) {
callback(node);
} else {
callback(node, maybeIsAppearing);
}
}
};

const handleEntering = normalizedTransitionCallback(onEntering);

const handleEnter = normalizedTransitionCallback((node, isAppearing) => {
reflow(node); // So the animation always start from the start.

const transitionProps = getTransitionProps(
{ style, timeout, easing },
{
mode: 'enter',
},
);

node.style.webkitTransition = theme.transitions.create('opacity', transitionProps);
node.style.transition = theme.transitions.create('opacity', transitionProps);

if (onEnter) {
onEnter(node, isAppearing);
}
});

const handleEntered = normalizedTransitionCallback(onEntered);

const handleExiting = normalizedTransitionCallback(onExiting);

const handleExit = normalizedTransitionCallback((node) => {
const transitionProps = getTransitionProps(
{ style, timeout, easing },
{
mode: 'exit',
},
);

node.style.webkitTransition = theme.transitions.create('opacity', transitionProps);
node.style.transition = theme.transitions.create('opacity', transitionProps);

if (onExit) {
onExit(node);
}
});

const handleExited = normalizedTransitionCallback(onExited);

const handleAddEndListener = (next) => {
if (addEndListener) {
// Old call signature before `react-transition-group` implemented `nodeRef`
addEndListener(nodeRef.current, next);
}
};

return (
<TransitionComponent
appear={appear}
in={inProp}
nodeRef={enableStrictModeCompat ? nodeRef : undefined}
onEnter={handleEnter}
onEntered={handleEntered}
onEntering={handleEntering}
onExit={handleExit}
onExited={handleExited}
onExiting={handleExiting}
addEndListener={handleAddEndListener}
timeout={timeout}
{...other}
>
{(state, childProps) => {
return React.cloneElement(children, {
style: {
opacity: 0,
visibility: state === 'exited' && !inProp ? 'hidden' : undefined,
...styles[state],
...style,
...children.props.style,
},
ref: handleRef,
...childProps,
});
}}
</TransitionComponent>
);
});
Empty file.
117 changes: 117 additions & 0 deletions packages/pigment-css-react/tests/useTheme/fixtures/useTheme.output.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,117 @@
import _pigmentCssReactTheme from 'useTheme';
export const Fade = React.forwardRef(function Fade(props, ref) {
const theme = _pigmentCssReactTheme(true);
const defaultTimeout = {
enter: theme.transitions.duration.enteringScreen,
exit: theme.transitions.duration.leavingScreen,
};
const {
addEndListener,
appear = true,
children,
easing,
in: inProp,
onEnter,
onEntered,
onEntering,
onExit,
onExited,
onExiting,
style,
timeout = defaultTimeout,
// eslint-disable-next-line react/prop-types
TransitionComponent = Transition,
...other
} = props;
const enableStrictModeCompat = true;
const nodeRef = React.useRef(null);
const handleRef = useForkRef(nodeRef, children.ref, ref);
const normalizedTransitionCallback = (callback) => (maybeIsAppearing) => {
if (callback) {
const node = nodeRef.current;

// onEnterXxx and onExitXxx callbacks have a different arguments.length value.
if (maybeIsAppearing === undefined) {
callback(node);
} else {
callback(node, maybeIsAppearing);
}
}
};
const handleEntering = normalizedTransitionCallback(onEntering);
const handleEnter = normalizedTransitionCallback((node, isAppearing) => {
reflow(node); // So the animation always start from the start.

const transitionProps = getTransitionProps(
{
style,
timeout,
easing,
},
{
mode: 'enter',
},
);
node.style.webkitTransition = theme.transitions.create('opacity', transitionProps);
node.style.transition = theme.transitions.create('opacity', transitionProps);
if (onEnter) {
onEnter(node, isAppearing);
}
});
const handleEntered = normalizedTransitionCallback(onEntered);
const handleExiting = normalizedTransitionCallback(onExiting);
const handleExit = normalizedTransitionCallback((node) => {
const transitionProps = getTransitionProps(
{
style,
timeout,
easing,
},
{
mode: 'exit',
},
);
node.style.webkitTransition = theme.transitions.create('opacity', transitionProps);
node.style.transition = theme.transitions.create('opacity', transitionProps);
if (onExit) {
onExit(node);
}
});
const handleExited = normalizedTransitionCallback(onExited);
const handleAddEndListener = (next) => {
if (addEndListener) {
// Old call signature before `react-transition-group` implemented `nodeRef`
addEndListener(nodeRef.current, next);
}
};
return (
<TransitionComponent
appear={appear}
in={inProp}
nodeRef={enableStrictModeCompat ? nodeRef : undefined}
onEnter={handleEnter}
onEntered={handleEntered}
onEntering={handleEntering}
onExit={handleExit}
onExited={handleExited}
onExiting={handleExiting}
addEndListener={handleAddEndListener}
timeout={timeout}
{...other}
>
{(state, childProps) => {
return React.cloneElement(children, {
style: {
opacity: 0,
visibility: state === 'exited' && !inProp ? 'hidden' : undefined,
...styles[state],
...style,
...children.props.style,
},
ref: handleRef,
...childProps,
});
}}
</TransitionComponent>
);
});
Loading

0 comments on commit 2e9c97b

Please sign in to comment.