From 8268c585db1b7a27c843c8bb175a225d1bbd505e Mon Sep 17 00:00:00 2001 From: Rohit Singh Date: Fri, 15 Sep 2023 20:44:59 +0530 Subject: [PATCH 1/7] feat: animation drivers --- example/storybook/babel.config.js | 4 +- example/storybook/package.json | 2 +- example/storybook/src/components/nb.config.ts | 5 +- .../AnimationPlugin/AnimationPlugin.tsx | 22 +- example/storybook/tsconfig.json | 4 +- .../.gitignore | 0 .../.nvmrc | 0 .../CHANGELOG.md | 0 .../README.md | 0 .../babel.config.js | 0 .../package.json | 2 +- .../src/index.tsx | 210 +++++++++ .../src/propertyTokenMap.ts | 0 .../src/utils.ts | 0 .../tsconfig.json | 0 packages/animation-moti-driver/.gitignore | 28 ++ packages/animation-moti-driver/.nvmrc | 1 + packages/animation-moti-driver/CHANGELOG.md | 17 + packages/animation-moti-driver/README.md | 69 +++ .../animation-moti-driver/babel.config.js | 22 + packages/animation-moti-driver/package.json | 58 +++ packages/animation-moti-driver/src/index.tsx | 66 +++ packages/animation-moti-driver/tsconfig.json | 32 ++ packages/animation-plugin/src/index.tsx | 378 ---------------- packages/react/src/StyledProvider.tsx | 11 +- packages/react/src/createStyled.ts | 14 +- packages/react/src/index.ts | 14 +- .../src/plugins/animation-components.tsx | 64 +++ .../react/src/plugins/animation-resolver.tsx | 422 ++++++++++++++++++ packages/react/src/plugins/css-variables.tsx | 2 +- packages/react/src/plugins/font-resolver.tsx | 2 +- packages/react/src/plugins/index.tsx | 2 + packages/react/src/styled.tsx | 42 +- packages/react/src/types.ts | 23 + yarn.lock | 128 +++++- 35 files changed, 1207 insertions(+), 437 deletions(-) rename packages/{animation-plugin => animation-legend-motion-driver}/.gitignore (100%) rename packages/{animation-plugin => animation-legend-motion-driver}/.nvmrc (100%) rename packages/{animation-plugin => animation-legend-motion-driver}/CHANGELOG.md (100%) rename packages/{animation-plugin => animation-legend-motion-driver}/README.md (100%) rename packages/{animation-plugin => animation-legend-motion-driver}/babel.config.js (100%) rename packages/{animation-plugin => animation-legend-motion-driver}/package.json (95%) create mode 100644 packages/animation-legend-motion-driver/src/index.tsx rename packages/{animation-plugin => animation-legend-motion-driver}/src/propertyTokenMap.ts (100%) rename packages/{animation-plugin => animation-legend-motion-driver}/src/utils.ts (100%) rename packages/{animation-plugin => animation-legend-motion-driver}/tsconfig.json (100%) create mode 100644 packages/animation-moti-driver/.gitignore create mode 100644 packages/animation-moti-driver/.nvmrc create mode 100644 packages/animation-moti-driver/CHANGELOG.md create mode 100644 packages/animation-moti-driver/README.md create mode 100644 packages/animation-moti-driver/babel.config.js create mode 100644 packages/animation-moti-driver/package.json create mode 100644 packages/animation-moti-driver/src/index.tsx create mode 100644 packages/animation-moti-driver/tsconfig.json delete mode 100644 packages/animation-plugin/src/index.tsx create mode 100644 packages/react/src/plugins/animation-components.tsx create mode 100644 packages/react/src/plugins/animation-resolver.tsx diff --git a/example/storybook/babel.config.js b/example/storybook/babel.config.js index b27308285..c3c30e856 100644 --- a/example/storybook/babel.config.js +++ b/example/storybook/babel.config.js @@ -15,9 +15,9 @@ module.exports = function (api) { __dirname, '../../packages/react/src' ), - ['@gluestack-style/animation-plugin']: path.join( + ['@gluestack-style/legend-motion-driver']: path.join( __dirname, - '../../packages/animation-plugin/src' + '../../packages/animation-legend-motion-driver/src' ), // ['@gluestack-style/animation-plugin']: path.join( // __dirname, diff --git a/example/storybook/package.json b/example/storybook/package.json index 4fc488445..81f6f89b1 100644 --- a/example/storybook/package.json +++ b/example/storybook/package.json @@ -23,7 +23,6 @@ }, "dependencies": { "@expo/html-elements": "^0.4.2", - "@gluestack-style/animation-plugin": "latest", "@gluestack-style/react": "^0.2.11-alpha.0", "@gluestack-ui/actionsheet": "^0.2.16", "@gluestack-ui/alert-dialog": "^0.1.14", @@ -98,6 +97,7 @@ "react": "*", "react-dom": "*", "react-native": "*", + "@gluestack-style/legend-motion-driver": "*", "react-native-web": "*" }, "react-native-builder-bob": { diff --git a/example/storybook/src/components/nb.config.ts b/example/storybook/src/components/nb.config.ts index 684875eb0..2d3a163c1 100644 --- a/example/storybook/src/components/nb.config.ts +++ b/example/storybook/src/components/nb.config.ts @@ -1,5 +1,5 @@ -import { AnimationResolver } from '@gluestack-style/animation-plugin'; -import { createConfig } from '@gluestack-style/react'; +import { MotionAnimationDriver } from '@gluestack-style/legend-motion-driver'; +import { createConfig, AnimationResolver } from '@gluestack-style/react'; export const config = createConfig({ aliases: { @@ -715,6 +715,7 @@ export const config = createConfig({ // }, }, }, + plugins: [new AnimationResolver(MotionAnimationDriver)], components: { Box: { theme: { diff --git a/example/storybook/src/plugins/AnimationPlugin/AnimationPlugin.tsx b/example/storybook/src/plugins/AnimationPlugin/AnimationPlugin.tsx index 5e630e00e..91917e91e 100644 --- a/example/storybook/src/plugins/AnimationPlugin/AnimationPlugin.tsx +++ b/example/storybook/src/plugins/AnimationPlugin/AnimationPlugin.tsx @@ -1,17 +1,19 @@ import React from 'react'; import { Wrapper } from '../../components/Wrapper'; -import { Motion } from '@legendapp/motion'; import { Pressable, View } from 'react-native'; -import { FontResolver, styled } from '@gluestack-style/react'; -import { AnimationResolver } from '@gluestack-style/animation-plugin'; +import { + styled, + AnimatedImage, + AnimatedAnimatePresence, +} from '@gluestack-style/react'; const images = [require('./1.png'), require('./2.png'), require('./3.png')]; const Box = styled(View, {}); - const StyledMotionImage = styled( - Motion.Image, + AnimatedImage, { + // @ts-ignore ':animate': { zIndex: 1, x: 0, @@ -19,9 +21,7 @@ const StyledMotionImage = styled( }, }, {}, - { - plugins: [new AnimationResolver({})], - } + {} ); export function AnimationPlugin() { @@ -42,8 +42,7 @@ export function AnimationPlugin() { 'aspectRatio': 1 * 1.4, }} > - {/* @ts-ignore */} - + - {/* @ts-ignore */} - + { + if (props[variant]) { + variantProps[variant] = props[variant]; + // delete restProps[variant]; + } + }); + + return { + variantProps, + restProps, + }; +} + +function resolveVariantAnimationProps(variantProps: any, styledObject: any) { + let resolvedVariant = {}; + Object.keys(variantProps).forEach((variant) => { + const variantValue = variantProps[variant]; + const variantObject = styledObject?.variants?.[variant]?.[variantValue]; + + resolvedVariant = deepMerge(resolvedVariant, variantObject); + }); + + return resolvedVariant; +} + +const AnimatePresence = React.forwardRef( + ({ children, ...props }: any, ref?: any) => { + const ctx = useStyled(); + const clonedChildren: any = []; + const CONFIG = useMemo( + () => ({ + ...ctx.config, + propertyTokenMap, + }), + [ctx.config] + ); + + React.Children.toArray(children).forEach((child: any) => { + if (child?.type?.displayName === 'StyledComponent') { + let tokenizedAnimatedProps: any = {}; + const animationAliases = {}; + + const componentStyledObject = child?.type?.styled?.config; + const { variantProps, restProps } = getVariantProps( + child?.props, + componentStyledObject + ); + + const config = CONFIG; + + if (child.type.styled.resolvedProps) { + tokenizedAnimatedProps = child?.type?.styled?.resolvedProps; + } else { + const variantStyledObject = resolveVariantAnimationProps( + variantProps, + componentStyledObject + ); + const componentStyledObjectWithVariants = deepMergeObjects( + componentStyledObject, + variantStyledObject + ); + tokenizedAnimatedProps = tokenizeAnimationPropsFromConfig( + componentStyledObjectWithVariants, + config, + animationAliases + ); + + child.type.styled.resolvedProps = tokenizedAnimatedProps; + } + + const tokenizedSxAnimationProps: any = tokenizeAnimationPropsFromConfig( + child?.props?.sx, + config, + animationAliases + ); + + const mergedAnimatedProps = deepMergeObjects( + {}, + tokenizedSxAnimationProps, + tokenizedAnimatedProps + ); + + const clonedChild = React.cloneElement(child, { + exit: mergedAnimatedProps?.[':exit'], + ...restProps, + }); + clonedChildren.push(clonedChild); + } else { + clonedChildren.push(child); + } + }); + return ( + + {clonedChildren} + + ); + } +); +export class MotionAnimationDriver implements IAnimationDriverPlugin { + name: 'MotionAnimationDriver'; + engine = { ...Motion, AnimatePresence }; + config = { + aliases: { + ':animate': 'animate', + ':initial': 'initial', + ':exit': 'exit', + ':initialProps': 'initialProps', + ':animateProps': 'animateProps', + ':transition': 'transition', + ':transformOrigin': 'transformOrigin', + ':whileTap': 'whileTap', + ':whileHover': 'whileHover', + ':onAnimationComplete': 'onAnimationComplete', + } as const, + }; + + register(config: any) { + if (this.config) { + this.config.aliases = { + ...this.config?.aliases, + ...config?.aliases, + }; + + // @ts-ignore + this.config.tokens = { + // @ts-ignore + ...this.config?.tokens, + ...config?.tokens, + }; + + // @ts-ignore + this.config.ref = config?.ref; + } + } + + constructor(config?: IAnimationResolver) { + this.register(config); + this.name = 'MotionAnimationDriver'; + this.engine.AnimatePresence.defaultProps = { + ...this.engine.AnimatePresence.defaultProps, + config, + }; + } +} diff --git a/packages/animation-plugin/src/propertyTokenMap.ts b/packages/animation-legend-motion-driver/src/propertyTokenMap.ts similarity index 100% rename from packages/animation-plugin/src/propertyTokenMap.ts rename to packages/animation-legend-motion-driver/src/propertyTokenMap.ts diff --git a/packages/animation-plugin/src/utils.ts b/packages/animation-legend-motion-driver/src/utils.ts similarity index 100% rename from packages/animation-plugin/src/utils.ts rename to packages/animation-legend-motion-driver/src/utils.ts diff --git a/packages/animation-plugin/tsconfig.json b/packages/animation-legend-motion-driver/tsconfig.json similarity index 100% rename from packages/animation-plugin/tsconfig.json rename to packages/animation-legend-motion-driver/tsconfig.json diff --git a/packages/animation-moti-driver/.gitignore b/packages/animation-moti-driver/.gitignore new file mode 100644 index 000000000..3d3e2719a --- /dev/null +++ b/packages/animation-moti-driver/.gitignore @@ -0,0 +1,28 @@ +# See https://help.github.com/articles/ignoring-files/ for more about ignoring files. + +# dependencies +node_modules +.pnp +.pnp.js + +# misc +.DS_Store +*.pem + +# build +dist +lib + +# debug +npm-debug.log* +yarn-debug.log* +yarn-error.log* + +# local env files +.env.local +.env.development.local +.env.test.local +.env.production.local + +# turbo +.turbo diff --git a/packages/animation-moti-driver/.nvmrc b/packages/animation-moti-driver/.nvmrc new file mode 100644 index 000000000..5dbac1ed0 --- /dev/null +++ b/packages/animation-moti-driver/.nvmrc @@ -0,0 +1 @@ +v16.13.0 \ No newline at end of file diff --git a/packages/animation-moti-driver/CHANGELOG.md b/packages/animation-moti-driver/CHANGELOG.md new file mode 100644 index 000000000..41be139f9 --- /dev/null +++ b/packages/animation-moti-driver/CHANGELOG.md @@ -0,0 +1,17 @@ +# @gluestack-style/animation-plugin + +## 0.1.7 + +### Patch Changes + +- - Fixed transform array resoltion + +## 0.1.0 + +### Changes + +- Fix forward ref warning +- Fixed variant resolution +- Moved @gluestack-style/react to devDependency +- Add legend motion dependency +- Add variant support diff --git a/packages/animation-moti-driver/README.md b/packages/animation-moti-driver/README.md new file mode 100644 index 000000000..58e26e535 --- /dev/null +++ b/packages/animation-moti-driver/README.md @@ -0,0 +1,69 @@ +# @gluestack-style/animation-plugin + +## Installation + +To use `@gluestack-style/animation-plugin`, all you need to do is install the +`@gluestack-style/animation-plugin` package: + +```sh +$ yarn add @gluestack-style/animation-plugin + +# or + +$ npm i @gluestack-style/animation-plugin +``` + +## Usage + +You can initialize the Animation plugin by creating a new instance of the AnimationResolver class and providing it as an argument to the createStyled function. The `AnimationResolver` takes an optional `styledUtils` object that maps the styled utils object. Here's an example: + +```jsx +import { createStyled } from '@gluestack-style/react'; +import { AnimationResolver } from '@gluestack-style/animation-plugin'; + +const styled = createStyled([ + new AnimationResolver({ + aliases: { + ':initial': 'initial', + ':animate': 'animate', + ':exit': 'exit', + }, + }), +]); +``` + +In this example, we are creating a new instance of the AnimationResolver class, passing an object with the 'aliases' property as an argument. The aliases object maps the aliases :initial, :animate, and :exit to their corresponding animation props. + +## Example of creating a styled component: + +Once the plugin is initialized, you can use the styled function to create styled components with animation props. Here's an example: + +```jsx +const Box = styled(Motion.View, { + ':initial': { opacity: 0 }, + ':animate': { opacity: 1 }, + ':exit': { opacity: 0 }, +}); +``` + +The final internal styled object that will be resolved is: + +```jsx +styledObject = { + 'props': { + initial: { + opacity: 0, + }, + animate: { + opacity: 1, + }, + exit: { + opacity: 0, + }, + } + }, +}; +``` + +More guides on how to get started are available +[here](https://style.gluestack.io/). diff --git a/packages/animation-moti-driver/babel.config.js b/packages/animation-moti-driver/babel.config.js new file mode 100644 index 000000000..fda985a22 --- /dev/null +++ b/packages/animation-moti-driver/babel.config.js @@ -0,0 +1,22 @@ +module.exports = function (api) { + api.cache(true); + return { + presets: ['babel-preset-expo'], + plugins: [ + process.env.NODE_ENV !== 'production' + ? [ + 'module-resolver', + { + alias: { + // For development, we want to alias the library to the source + ['@gluestack-style/react']: path.join( + __dirname, + '../react/src/index' + ), + }, + }, + ] + : ['transform-remove-console'], + ], + }; +}; diff --git a/packages/animation-moti-driver/package.json b/packages/animation-moti-driver/package.json new file mode 100644 index 000000000..032764503 --- /dev/null +++ b/packages/animation-moti-driver/package.json @@ -0,0 +1,58 @@ +{ + "name": "@gluestack-style/moti-driver", + "version": "0.1.11", + "description": "A gluestack-style plugin for animation properties, utilizing animation libraries.", + "keywords": [ + "react", + "native", + "react-native", + "animation", + "transition" + ], + "main": "lib/commonjs/index", + "types": "lib/typescript/index.d.ts", + "module": "lib/module/index", + "react-native": "src/index", + "source": "src/index", + "typings": "lib/typescript/index.d.ts", + "scripts": { + "prepare": "bob build", + "release": "release-it", + "build": "bob build", + "clean": "rm -rf lib" + }, + "dependencies": { + "moti": "^0.26.0" + }, + "peerDependencies": { + "react-native-reanimated": ">=3.5.1", + "react-native-gesture-handler": ">=2.12.1" + }, + "devDependencies": { + "@gluestack-style/react": "^0.1.11", + "@types/react": "^18.0.22", + "@types/react-native": "^0.69.15", + "babel-plugin-transform-remove-console": "^6.9.4", + "react": "^18.1.0", + "react-dom": "^18.1.0", + "react-native": "^0.70.3", + "react-native-builder-bob": "^0.20.1", + "tsconfig": "*", + "typescript": "^4.7.4" + }, + "react-native-builder-bob": { + "source": "src", + "output": "lib", + "targets": [ + "commonjs", + [ + "module" + ], + "typescript" + ] + }, + "files": [ + "lib/", + "src/" + ] +} diff --git a/packages/animation-moti-driver/src/index.tsx b/packages/animation-moti-driver/src/index.tsx new file mode 100644 index 000000000..7cea3c537 --- /dev/null +++ b/packages/animation-moti-driver/src/index.tsx @@ -0,0 +1,66 @@ +import type { + // @ts-ignore + IAnimationDriverPlugin, + // @ts-ignore + IAnimationResolver, +} from '@gluestack-style/react'; + +import { + MotiImage, + MotiView, + MotiText, + MotiScrollView, + MotiSafeAreaView, + MotiProgressBar, +} from 'moti'; + +let Moti = { + Image: MotiImage, + View: MotiView, + Text: MotiText, + ScrollView: MotiScrollView, + SafeAreaView: MotiSafeAreaView, + ProgressBar: MotiProgressBar, +}; +export class MotiAnimationDriver implements IAnimationDriverPlugin { + name: 'MotiAnimationDriver'; + LibraryImports = Moti; + styledUtils = { + aliases: { + ':animate': 'animate', + ':initial': 'from', + ':exit': 'exit', + ':initialProps': 'initialProps', + ':animateProps': 'animateProps', + ':transition': 'transition', + ':transformOrigin': 'transformOrigin', + ':whileTap': 'whileTap', + ':whileHover': 'whileHover', + ':onAnimationComplete': 'onAnimationComplete', + } as const, + }; + + register(styledUtils: any) { + if (this.styledUtils) { + this.styledUtils.aliases = { + ...this.styledUtils?.aliases, + ...styledUtils?.aliases, + }; + + // @ts-ignore + this.styledUtils.tokens = { + // @ts-ignore + ...this.styledUtils?.tokens, + ...styledUtils?.tokens, + }; + + // @ts-ignore + this.styledUtils.ref = styledUtils?.ref; + } + } + + constructor(styledUtils?: IAnimationResolver) { + this.register(styledUtils); + this.name = 'MotiAnimationDriver'; + } +} diff --git a/packages/animation-moti-driver/tsconfig.json b/packages/animation-moti-driver/tsconfig.json new file mode 100644 index 000000000..7ebd787f0 --- /dev/null +++ b/packages/animation-moti-driver/tsconfig.json @@ -0,0 +1,32 @@ +{ + "include": ["./src"], + "exclude": ["node_modules", "example"], + "path": { + "@gluestack-style/react": ["../react/src"] + }, + "compilerOptions": { + "emitDeclarationOnly": true, + "noEmit": false, + "baseUrl": ".", + "declaration": true, + "allowUnreachableCode": false, + "allowUnusedLabels": true, + "esModuleInterop": true, + "importsNotUsedAsValues": "error", + "forceConsistentCasingInFileNames": true, + "jsx": "react", + "lib": ["esnext", "dom"], + "module": "esnext", + "moduleResolution": "node", + "noFallthroughCasesInSwitch": true, + "noImplicitReturns": true, + "noImplicitUseStrict": false, + "noStrictGenericChecks": false, + "noUnusedLocals": false, + "noUnusedParameters": true, + "resolveJsonModule": true, + "skipLibCheck": true, + "strict": true, + "target": "esnext" + } +} diff --git a/packages/animation-plugin/src/index.tsx b/packages/animation-plugin/src/index.tsx deleted file mode 100644 index fcc29a013..000000000 --- a/packages/animation-plugin/src/index.tsx +++ /dev/null @@ -1,378 +0,0 @@ -import React, { useMemo } from 'react'; -import type { IStyled, IStyledPlugin } from '@gluestack-style/react'; -import { useStyled } from '@gluestack-style/react'; -import { - deepMerge, - deepMergeObjects, - setObjectKeyValue, - resolvedTokenization, -} from './utils'; -import { AnimatePresence } from '@legendapp/motion'; -import { propertyTokenMap } from './propertyTokenMap'; - -function tokenizeAnimationPropsFromConfig( - props: any = {}, - config: any, - animationAliases: any, - path: any = [], - tokenizedAnimatedProps: any = {} -) { - for (const prop in props) { - if (Array.isArray(props[prop])) { - path.push(prop); - setObjectKeyValue(tokenizedAnimatedProps, path, props[prop]); - path.pop(); - } else if (animationAliases[prop]) { - path.push(prop); - const tokenizedValue = resolvedTokenization(props[prop], config); - setObjectKeyValue(tokenizedAnimatedProps, path, tokenizedValue); - path.pop(); - } else if (typeof props[prop] === 'object') { - path.push(prop); - const tokenizedValue = resolvedTokenization(props[prop], config); - setObjectKeyValue(tokenizedAnimatedProps, path, tokenizedValue); - // path.pop(); - tokenizeAnimationPropsFromConfig( - props[prop], - config, - animationAliases, - path, - tokenizedAnimatedProps - ); - path.pop(); - } else { - path.push(prop); - setObjectKeyValue(tokenizedAnimatedProps, path, props[prop]); - path.pop(); - } - } - - return tokenizedAnimatedProps; -} - -function getVariantProps(props: any, theme: any) { - const variantTypes = theme?.variants ? Object.keys(theme.variants) : []; - - const restProps = { ...props }; - - const variantProps: any = {}; - variantTypes?.forEach((variant) => { - if (props[variant]) { - variantProps[variant] = props[variant]; - // delete restProps[variant]; - } - }); - - return { - variantProps, - restProps, - }; -} - -function resolveVariantAnimationProps(variantProps: any, styledObject: any) { - let resolvedVariant = {}; - Object.keys(variantProps).forEach((variant) => { - const variantValue = variantProps[variant]; - const variantObject = styledObject?.variants?.[variant]?.[variantValue]; - - resolvedVariant = deepMerge(resolvedVariant, variantObject); - }); - - return resolvedVariant; -} - -export class AnimationResolver implements IStyledPlugin { - name: 'AnimationResolver'; - styledUtils = { - aliases: { - ':animate': 'animate', - ':initial': 'initial', - ':exit': 'exit', - ':initialProps': 'initialProps', - ':animateProps': 'animateProps', - ':transition': 'transition', - ':transformOrigin': 'transformOrigin', - ':whileTap': 'whileTap', - ':whileHover': 'whileHover', - ':onAnimationComplete': 'onAnimationComplete', - } as const, - }; - - register(styledUtils: any) { - if (this.styledUtils) { - this.styledUtils.aliases = { - ...this.styledUtils?.aliases, - ...styledUtils?.aliases, - }; - - // @ts-ignore - this.styledUtils.tokens = { - // @ts-ignore - ...this.styledUtils?.tokens, - ...styledUtils?.tokens, - }; - - // @ts-ignore - this.styledUtils.ref = styledUtils?.ref; - } - } - - constructor(styledUtils?: IStyled) { - this.register(styledUtils); - this.name = 'AnimationResolver'; - } - - #childrenExitPropsMap: any = {}; - - #extendedConfig: any = {}; - - inputMiddleWare

( - styledObj = {}, - shouldUpdateConfig: any = true - ): { - // @ts-ignore - [key in keyof typeof this.styledUtils.aliases]: P[(typeof this.styledUtils.aliases)[key]]; - } { - // this.#childrenExitPropsMap = deepClone(styledObj); - const resolvedAnimatedProps = this.updateStyledObject( - styledObj, - shouldUpdateConfig - ); - const resolvedStyledObjectWithAnimatedProps = deepMerge( - styledObj, - resolvedAnimatedProps - ); - - if (shouldUpdateConfig) { - // @ts-ignore - return styledObj; - } - - return resolvedStyledObjectWithAnimatedProps; - } - - updateStyledObject( - styledObject: any = {}, - shouldUpdateConfig: boolean, - resolvedStyledObject: any = {}, - keyPath: string[] = [] - ) { - const aliases = this.styledUtils?.aliases; - for (const prop in styledObject) { - if (typeof styledObject[prop] === 'object') { - keyPath.push(prop); - this.updateStyledObject( - styledObject[prop], - shouldUpdateConfig, - resolvedStyledObject, - keyPath - ); - keyPath.pop(); - } - - // @ts-ignore - if (aliases && aliases?.[prop]) { - if (shouldUpdateConfig) { - // this.#childrenExitPropsMap[prop] = styledObject[prop]; - setObjectKeyValue( - this.#childrenExitPropsMap, - [...keyPath, prop], - styledObject[prop] - ); - } - const value = styledObject[prop]; - // @ts-ignore - keyPath.push('props', aliases[prop]); - setObjectKeyValue(resolvedStyledObject, keyPath, value); - keyPath.pop(); - keyPath.pop(); - delete styledObject[prop]; - } - } - - return resolvedStyledObject; - } - - componentMiddleWare({ Component, ExtendedConfig }: any) { - const styledConfig = this.#childrenExitPropsMap; - - this.#childrenExitPropsMap = {}; - - const NewComponent = React.forwardRef((props: any, ref?: any) => { - const { sx, ...rest } = props; - - const styledContext = useStyled(); - const CONFIG = useMemo( - () => ({ - ...styledContext.config, - propertyTokenMap, - }), - [styledContext.config] - ); - this.#extendedConfig = CONFIG; - if (ExtendedConfig) { - this.#extendedConfig = deepMerge(CONFIG, ExtendedConfig); - } - - let tokenizedAnimatedProps: any = {}; - const { variantProps, restProps } = getVariantProps(rest, styledConfig); - const variantStyledObject = resolveVariantAnimationProps( - variantProps, - styledConfig - ); - const componentStyledObject = deepMerge( - variantStyledObject, - styledConfig - ); - - const animationAliases = this.styledUtils?.aliases; - - const config = this.#extendedConfig; - - tokenizedAnimatedProps = tokenizeAnimationPropsFromConfig( - componentStyledObject, - config, - animationAliases - ); - - const tokenizedSxAnimationProps: any = tokenizeAnimationPropsFromConfig( - sx, - config, - animationAliases - ); - - const mergedAnimatedProps = deepMerge( - tokenizedAnimatedProps, - tokenizedSxAnimationProps - ); - - const resolvedAnimatedStyledWithStyledObject = this.inputMiddleWare( - mergedAnimatedProps, - false - ); - - let isState = false; - - Object.keys(restProps?.states ?? {}).forEach((state: any) => { - isState = restProps.states[state] ? true : false; - }); - const animatedProps = !isState - ? // @ts-ignore - resolvedAnimatedStyledWithStyledObject?.props - : {}; - - return ( - - ); - }); - - if (NewComponent) { - //@ts-ignore - NewComponent.styled = {}; - //@ts-ignore - NewComponent.styled.config = {}; - //@ts-ignore - NewComponent.styled.config = styledConfig; - - //@ts-ignore - NewComponent.isStyledComponent = Component?.isStyledComponent; - //@ts-ignore - NewComponent.isComposedComponent = Component?.isComposedComponent; - - NewComponent.displayName = 'StyledComponent'; - return NewComponent; - } - } - - wrapperComponentMiddleWare() { - const AnimatedPresenceComp = React.forwardRef( - ({ children, ...props }: any, ref?: any) => { - const clonedChildren: any = []; - const styledContext = useStyled(); - const CONFIG = useMemo( - () => ({ - ...styledContext.config, - propertyTokenMap, - }), - [styledContext.config] - ); - - this.#extendedConfig = CONFIG; - - React.Children.toArray(children).forEach((child: any) => { - if (child?.type?.displayName === 'StyledComponent') { - let tokenizedAnimatedProps: any = {}; - const animationAliases = this.styledUtils?.aliases; - - const componentStyledObject = child?.type?.styled?.config; - const { variantProps, restProps } = getVariantProps( - child?.props, - componentStyledObject - ); - - const config = CONFIG; - - if (child.type.styled.resolvedProps) { - tokenizedAnimatedProps = child?.type?.styled?.resolvedProps; - } else { - const variantStyledObject = resolveVariantAnimationProps( - variantProps, - componentStyledObject - ); - const componentStyledObjectWithVariants = deepMergeObjects( - componentStyledObject, - variantStyledObject - ); - tokenizedAnimatedProps = tokenizeAnimationPropsFromConfig( - componentStyledObjectWithVariants, - config, - animationAliases - ); - - child.type.styled.resolvedProps = tokenizedAnimatedProps; - } - - const tokenizedSxAnimationProps: any = - tokenizeAnimationPropsFromConfig( - child?.props?.sx, - config, - animationAliases - ); - - const mergedAnimatedProps = deepMergeObjects( - {}, - tokenizedSxAnimationProps, - tokenizedAnimatedProps - ); - - const clonedChild = React.cloneElement(child, { - exit: mergedAnimatedProps?.[':exit'], - ...restProps, - }); - clonedChildren.push(clonedChild); - } else { - clonedChildren.push(child); - } - }); - - return ( - - {clonedChildren} - - ); - } - ); - - AnimatedPresenceComp.displayName = `AnimatePresence`; - - return { - Component: AnimatedPresenceComp, - AnimatePresence: AnimatedPresenceComp, - }; - } -} diff --git a/packages/react/src/StyledProvider.tsx b/packages/react/src/StyledProvider.tsx index 91fd4d689..2a07ae42e 100644 --- a/packages/react/src/StyledProvider.tsx +++ b/packages/react/src/StyledProvider.tsx @@ -85,12 +85,17 @@ export const StyledProvider: React.FC<{ setCurrentColorMode(currentColorMode); } + const [animationDriverData, setAnimationDriverData] = React.useState(); const globalStyleMap = config?.globalStyle && createGlobalStyles(config.globalStyle); - const contextValue = React.useMemo(() => { - return { config: currentConfig, globalStyle: globalStyleMap }; - }, [currentConfig, globalStyleMap]); + return { + config: currentConfig, + globalStyle: globalStyleMap, + animationDriverData, + setAnimationDriverData, + }; + }, [currentConfig, globalStyleMap, animationDriverData]); return ( diff --git a/packages/react/src/createStyled.ts b/packages/react/src/createStyled.ts index 7015d82b0..8fbcfe0a1 100644 --- a/packages/react/src/createStyled.ts +++ b/packages/react/src/createStyled.ts @@ -1,22 +1,10 @@ import { styled } from './styled'; import type { IComponentStyleConfig, ITheme } from './types'; -export interface IStyledPlugin { - styledUtils?: IStyled; - register(styledUtils: IStyled): void; - inputMiddleWare(styledObj: any): void; - componentMiddleWare?(props: any): void; -} - -export class IStyled { - aliases?: any; - tokens?: any; - ref?: any; -} - export const createStyled = (plugins: any) => { let styledComponent = ( Component: React.ComponentType

, + // @ts-ignore styledObject: ITheme, compConfig: IComponentStyleConfig = {}, extendedConfig: any = {} diff --git a/packages/react/src/index.ts b/packages/react/src/index.ts index 44e251821..7e4da6326 100644 --- a/packages/react/src/index.ts +++ b/packages/react/src/index.ts @@ -65,16 +65,24 @@ export { convertStyledToStyledVerbosed, convertSxToSxVerbosed, } from './convertSxToSxVerbosed'; -export type { Tokens, Aliases, AliasesProps, ICustomConfig } from './types'; +export type { + Tokens, + Aliases, + AliasesProps, + ICustomConfig, + IStyledPlugin, + IStyled, + IAnimationDriverPlugin, +} from './types'; export { createStyled } from './createStyled'; -export type { IStyledPlugin, IStyled } from './createStyled'; + export { createGlobalStylesWeb } from './createGlobalStylesWeb'; // export { styled }; // export { flush } from './utils/css-injector'; export { AsForwarder } from './AsForwarder'; -export { AddCssTokenVariables, FontResolver } from './plugins'; +export * from './plugins'; export { INTERNAL_updateCSSStyleInOrderedResolved } from './updateCSSStyleInOrderedResolved'; export { createConfig } from './createConfig'; diff --git a/packages/react/src/plugins/animation-components.tsx b/packages/react/src/plugins/animation-components.tsx new file mode 100644 index 000000000..65a8983df --- /dev/null +++ b/packages/react/src/plugins/animation-components.tsx @@ -0,0 +1,64 @@ +import React from 'react'; +import { useStyled } from '..'; + +const AnimatedText = (props: React.PropsWithChildren) => { + return ; +}; +const AnimatedView = (props: React.PropsWithChildren) => { + return ; +}; +const AnimatedPressable = (props: React.PropsWithChildren) => { + return ; +}; +const AnimatedImage = (props: React.PropsWithChildren) => { + return ; +}; +const AnimatedScrollView = (props: React.PropsWithChildren) => { + return ; +}; +const AnimatedSafeAreaView = (props: React.PropsWithChildren) => { + return ; +}; +const AnimatedFlatList = (props: React.PropsWithChildren) => { + return ; +}; +const AnimatedSectionList = (props: React.PropsWithChildren) => { + return ; +}; +const AnimatedAnimatePresence = React.forwardRef( + ({ ...props }: any, ref: any) => { + const ctx = useStyled(); + let animationDriverData = ctx.animationDriverData; + if (animationDriverData?.engine.AnimatePresence) { + return ( + + ); + } + return ; + } +); +AnimatedText.displayName = 'Gluestack-AnimatedResolver-AnimatedText'; +AnimatedView.displayName = 'Gluestack-AnimatedResolver-AnimatedView'; +AnimatedPressable.displayName = 'Gluestack-AnimatedResolver-AnimatedPressable'; +AnimatedImage.displayName = 'Gluestack-AnimatedResolver-AnimatedImage'; +AnimatedScrollView.displayName = + 'Gluestack-AnimatedResolver-AnimatedScrollView'; +AnimatedSafeAreaView.displayName = + 'Gluestack-AnimatedResolver-AnimatedSafeAreaView'; +AnimatedFlatList.displayName = 'Gluestack-AnimatedResolver-AnimatedFlatList'; +AnimatedSectionList.displayName = + 'Gluestack-AnimatedResolver-AnimatedSectionList'; +AnimatedAnimatePresence.displayName = + 'Gluestack-AnimatedResolver-AnimatedAnimatePresence'; + +export { + AnimatedText, + AnimatedView, + AnimatedPressable, + AnimatedImage, + AnimatedScrollView, + AnimatedSafeAreaView, + AnimatedFlatList, + AnimatedSectionList, + AnimatedAnimatePresence, +}; diff --git a/packages/react/src/plugins/animation-resolver.tsx b/packages/react/src/plugins/animation-resolver.tsx new file mode 100644 index 000000000..753fafe61 --- /dev/null +++ b/packages/react/src/plugins/animation-resolver.tsx @@ -0,0 +1,422 @@ +import React, { useEffect, useMemo } from 'react'; +import type { IAnimationDriverPlugin, IStyledPlugin } from '../types'; +import { useStyled } from '../StyledProvider'; +import { + deepMerge, + deepMergeObjects, + setObjectKeyValue, + resolvedTokenization, +} from '../utils'; +import { propertyTokenMap } from '../propertyTokenMap'; + +function tokenizeAnimationPropsFromConfig( + props: any = {}, + config: any, + animationAliases: any, + path: any = [], + tokenizedAnimatedProps: any = {} +) { + for (const prop in props) { + if (Array.isArray(props[prop])) { + path.push(prop); + setObjectKeyValue(tokenizedAnimatedProps, path, props[prop]); + path.pop(); + } else if (animationAliases[prop]) { + path.push(prop); + const tokenizedValue = resolvedTokenization(props[prop], config); + setObjectKeyValue(tokenizedAnimatedProps, path, tokenizedValue); + path.pop(); + } else if (typeof props[prop] === 'object') { + path.push(prop); + const tokenizedValue = resolvedTokenization(props[prop], config); + setObjectKeyValue(tokenizedAnimatedProps, path, tokenizedValue); + // path.pop(); + tokenizeAnimationPropsFromConfig( + props[prop], + config, + animationAliases, + path, + tokenizedAnimatedProps + ); + path.pop(); + } else { + path.push(prop); + setObjectKeyValue(tokenizedAnimatedProps, path, props[prop]); + path.pop(); + } + } + + return tokenizedAnimatedProps; +} + +function getVariantProps(props: any, theme: any) { + const variantTypes = theme?.variants ? Object.keys(theme.variants) : []; + + const restProps = { ...props }; + + const variantProps: any = {}; + variantTypes?.forEach((variant) => { + if (props[variant]) { + variantProps[variant] = props[variant]; + // delete restProps[variant]; + } + }); + + return { + variantProps, + restProps, + }; +} + +function resolveVariantAnimationProps(variantProps: any, styledObject: any) { + let resolvedVariant = {}; + Object.keys(variantProps).forEach((variant) => { + const variantValue = variantProps[variant]; + const variantObject = styledObject?.variants?.[variant]?.[variantValue]; + + resolvedVariant = deepMerge(resolvedVariant, variantObject); + }); + + return resolvedVariant; +} + +export class AnimationResolver implements IStyledPlugin { + name: 'AnimationResolver'; + componentDriver: IAnimationDriverPlugin; + config = { + aliases: {} as const, + tokens: {} as const, + }; + + AnimatePresenceComp = React.Fragment; + + register(config: any) { + if (this.config) { + this.config.aliases = { + ...this.config?.aliases, + ...config?.aliases, + }; + + this.config.tokens = { + ...this.config?.tokens, + ...config?.tokens, + }; + + // @ts-ignore + this.config.ref = config?.ref; + } + } + + constructor(ComponentDriverClass: IAnimationDriverPlugin, config: any = {}) { + // @ts-ignore + const componentDriver = new ComponentDriverClass(config); + this.name = 'AnimationResolver'; + this.componentDriver = componentDriver; + if (componentDriver.engine.AnimatePresence) { + this.AnimatePresenceComp = componentDriver.engine.AnimatePresence; + } + this.register(componentDriver.config); + } + + #childrenExitPropsMap: any = {}; + + #extendedConfig: any = {}; + + inputMiddleWare

( + styledObj = {}, + shouldUpdateConfig: any = true, + _?: boolean, + Component?: React.ComponentType + ): { + // @ts-ignore + [key in keyof typeof this.config.aliases]: P[(typeof this.config.aliases)[key]]; + } { + if ( + Component && + (Component.displayName?.startsWith( + 'Gluestack-AnimatedResolver-Animated' + ) || + // @ts-ignore + Component.isAnimatedComponent) + ) { + let AnimatedComponent = + this.componentDriver.engine[ + // @ts-ignore + Component.displayName?.replace( + 'Gluestack-AnimatedResolver-Animated', + '' + ) + ]; + + if (AnimatedComponent) { + AnimatedComponent.isAnimatedComponent = true; + } + if (!AnimatedComponent) { + AnimatedComponent = React.Fragment; + } + + // this.#childrenExitPropsMap = deepClone(styledObj); + const resolvedAnimatedProps = this.updateStyledObject( + styledObj, + shouldUpdateConfig + ); + + const resolvedStyledObjectWithAnimatedProps = deepMerge( + styledObj, + resolvedAnimatedProps + ); + + if (shouldUpdateConfig) { + // @ts-ignore + return [styledObj, shouldUpdateConfig, _, AnimatedComponent]; + } + + return [ + resolvedStyledObjectWithAnimatedProps, + shouldUpdateConfig, + _, + AnimatedComponent, + ]; + } + return [styledObj, shouldUpdateConfig, _, Component]; + } + + updateStyledObject( + styledObject: any = {}, + shouldUpdateConfig: boolean, + resolvedStyledObject: any = {}, + keyPath: string[] = [] + ) { + const aliases = this.config?.aliases; + for (const prop in styledObject) { + if (typeof styledObject[prop] === 'object') { + keyPath.push(prop); + this.updateStyledObject( + styledObject[prop], + shouldUpdateConfig, + resolvedStyledObject, + keyPath + ); + keyPath.pop(); + } + + // @ts-ignore + if (aliases && aliases?.[prop]) { + if (shouldUpdateConfig) { + // this.#childrenExitPropsMap[prop] = styledObject[prop]; + setObjectKeyValue( + this.#childrenExitPropsMap, + [...keyPath, prop], + styledObject[prop] + ); + } + const value = styledObject[prop]; + // @ts-ignore + keyPath.push('props', aliases[prop]); + setObjectKeyValue(resolvedStyledObject, keyPath, value); + keyPath.pop(); + keyPath.pop(); + delete styledObject[prop]; + } + } + return resolvedStyledObject; + } + + componentMiddleWare({ Component, ExtendedConfig }: any) { + if (Component && Component.isAnimatedComponent) { + const styledConfig = this.#childrenExitPropsMap; + + this.#childrenExitPropsMap = {}; + + const NewComponent = React.forwardRef((props: any, ref?: any) => { + const { sx, ...rest } = props; + + const styledContext = useStyled(); + useEffect(() => { + if (!styledContext.animationDriverData) { + styledContext.setAnimationDriverData(this.componentDriver); + } + }, [styledContext]); + const CONFIG = useMemo( + () => ({ + ...styledContext.config, + propertyTokenMap, + }), + [styledContext.config] + ); + this.#extendedConfig = CONFIG; + if (ExtendedConfig) { + this.#extendedConfig = deepMerge(CONFIG, ExtendedConfig); + } + + let tokenizedAnimatedProps: any = {}; + const { variantProps, restProps } = getVariantProps(rest, styledConfig); + const variantStyledObject = resolveVariantAnimationProps( + variantProps, + styledConfig + ); + const componentStyledObject = deepMerge( + variantStyledObject, + styledConfig + ); + + const animationAliases = this.config?.aliases; + + const config = this.#extendedConfig; + + tokenizedAnimatedProps = tokenizeAnimationPropsFromConfig( + componentStyledObject, + config, + animationAliases + ); + + const tokenizedSxAnimationProps: any = tokenizeAnimationPropsFromConfig( + sx, + config, + animationAliases + ); + + const mergedAnimatedProps = deepMerge( + tokenizedAnimatedProps, + tokenizedSxAnimationProps + ); + + // @ts-ignore + const [resolvedAnimatedStyledWithStyledObject, , ,] = + this.inputMiddleWare(mergedAnimatedProps, false, false, Component); + let isState = false; + + Object.keys(restProps?.states ?? {}).forEach((state: any) => { + isState = restProps.states[state] ? true : false; + }); + const animatedProps = !isState + ? // @ts-ignore + resolvedAnimatedStyledWithStyledObject?.props + : {}; + return ( + + ); + }); + + if (NewComponent) { + //@ts-ignore + NewComponent.styled = {}; + //@ts-ignore + NewComponent.styled.config = {}; + //@ts-ignore + NewComponent.styled.config = styledConfig; + + //@ts-ignore + NewComponent.isStyledComponent = Component?.isStyledComponent; + //@ts-ignore + NewComponent.isComposedComponent = Component?.isComposedComponent; + + NewComponent.displayName = 'StyledComponent'; + return NewComponent; + } + } else { + return Component; + } + } + + wrapperComponentMiddleWare(Component: React.ComponentType) { + if ( + Component && + Component.displayName?.startsWith('Gluestack-AnimatedResolver-Animated') + ) { + const AnimatedPresenceComp = React.forwardRef( + ({ children, ...props }: any, ref?: any) => { + const clonedChildren: any = []; + const styledContext = useStyled(); + const CONFIG = useMemo( + () => ({ + ...styledContext.config, + propertyTokenMap, + }), + [styledContext.config] + ); + + this.#extendedConfig = CONFIG; + + React.Children.toArray(children).forEach((child: any) => { + if (child?.type?.displayName === 'StyledComponent') { + let tokenizedAnimatedProps: any = {}; + const animationAliases = this.config?.aliases; + + const componentStyledObject = child?.type?.styled?.config; + const { variantProps, restProps } = getVariantProps( + child?.props, + componentStyledObject + ); + + const config = CONFIG; + + if (child.type.styled.resolvedProps) { + tokenizedAnimatedProps = child?.type?.styled?.resolvedProps; + } else { + const variantStyledObject = resolveVariantAnimationProps( + variantProps, + componentStyledObject + ); + const componentStyledObjectWithVariants = deepMergeObjects( + componentStyledObject, + variantStyledObject + ); + tokenizedAnimatedProps = tokenizeAnimationPropsFromConfig( + componentStyledObjectWithVariants, + config, + animationAliases + ); + + child.type.styled.resolvedProps = tokenizedAnimatedProps; + } + + const tokenizedSxAnimationProps: any = + tokenizeAnimationPropsFromConfig( + child?.props?.sx, + config, + animationAliases + ); + + const mergedAnimatedProps = deepMergeObjects( + {}, + tokenizedSxAnimationProps, + tokenizedAnimatedProps + ); + + const clonedChild = React.cloneElement(child, { + exit: mergedAnimatedProps?.[':exit'], + ...restProps, + }); + clonedChildren.push(clonedChild); + } else { + clonedChildren.push(child); + } + }); + + return ( + + {clonedChildren} + + ); + } + ); + AnimatedPresenceComp.displayName = `AnimatePresence`; + + return { + Component: AnimatedPresenceComp, + AnimatePresence: AnimatedPresenceComp, + }; + } else { + return { + Component: React.Fragment, + AnimatePresence: React.Fragment, + }; + } + } +} diff --git a/packages/react/src/plugins/css-variables.tsx b/packages/react/src/plugins/css-variables.tsx index b3a02520f..a10d5a012 100644 --- a/packages/react/src/plugins/css-variables.tsx +++ b/packages/react/src/plugins/css-variables.tsx @@ -1,4 +1,4 @@ -import type { IStyled, IStyledPlugin } from '../createStyled'; +import type { IStyled, IStyledPlugin } from '../types'; import { deepMerge } from '../utils'; import { injectGlobalCss } from '../utils/css-injector'; import React, { useMemo } from 'react'; diff --git a/packages/react/src/plugins/font-resolver.tsx b/packages/react/src/plugins/font-resolver.tsx index 0b26b20ea..eacc93e20 100644 --- a/packages/react/src/plugins/font-resolver.tsx +++ b/packages/react/src/plugins/font-resolver.tsx @@ -1,5 +1,5 @@ import React, { useMemo } from 'react'; -import type { IStyled, IStyledPlugin } from '../createStyled'; +import type { IStyled, IStyledPlugin } from '../types'; import { useStyled } from '../StyledProvider'; import { propertyTokenMap } from '../propertyTokenMap'; import { deepMerge, deepMergeObjects, setObjectKeyValue } from '../utils'; diff --git a/packages/react/src/plugins/index.tsx b/packages/react/src/plugins/index.tsx index cb7752818..871feaa62 100644 --- a/packages/react/src/plugins/index.tsx +++ b/packages/react/src/plugins/index.tsx @@ -1,2 +1,4 @@ export { AddCssTokenVariables } from './css-variables'; export { FontResolver } from './font-resolver'; +export { AnimationResolver } from './animation-resolver'; +export * from './animation-components'; diff --git a/packages/react/src/styled.tsx b/packages/react/src/styled.tsx index 7f092f35d..46f2221dd 100644 --- a/packages/react/src/styled.tsx +++ b/packages/react/src/styled.tsx @@ -1853,7 +1853,12 @@ export function styled( for (const pluginName in plugins) { // @ts-ignore - styledObj = plugins[pluginName]?.inputMiddleWare

(styledObj, true, true); + [styledObj, , , Component] = plugins[pluginName]?.inputMiddleWare

( + styledObj, + true, + true, + Component + ); } theme = styledObj; const sxConvertedObject = convertStyledToStyledVerbosed(theme); @@ -1865,6 +1870,10 @@ export function styled( ExtendedConfig, BUILD_TIME_PARAMS ); + + // @ts-ignore + StyledComponent.isAnimatedComponent = Component.isAnimatedComponent; + // @ts-ignore plugins?.reverse(); for (const pluginName in plugins) { @@ -1879,22 +1888,21 @@ export function styled( }); } } - - for (const pluginName in plugins) { - const compWrapper = - // @ts-ignore - typeof plugins[pluginName].wrapperComponentMiddleWare === 'function' - ? // @ts-ignore - plugins[pluginName].wrapperComponentMiddleWare() - : null; - - if (compWrapper) { - for (const key of Object.keys(compWrapper)) { - // @ts-ignore - StyledComponent[key] = compWrapper[key]; - } - } - } + // for (const pluginName in plugins) { + // const compWrapper = + // // @ts-ignore + // typeof plugins[pluginName].wrapperComponentMiddleWare === 'function' + // ? // @ts-ignore + // plugins[pluginName].wrapperComponentMiddleWare() + // : null; + + // if (compWrapper) { + // for (const key of Object.keys(compWrapper)) { + // // @ts-ignore + // StyledComponent[key] = compWrapper[key]; + // } + // } + // } return StyledComponent; } diff --git a/packages/react/src/types.ts b/packages/react/src/types.ts index 7f30d39f8..888b8b2ca 100644 --- a/packages/react/src/types.ts +++ b/packages/react/src/types.ts @@ -33,6 +33,29 @@ export type COLORMODES = 'dark' | 'light'; /*************************** CORE TYPES *************************************************/ +export interface IStyledPlugin { + config?: IStyled; + register(styledUtils: IStyled): void; + inputMiddleWare(styledObj: any): void; + componentMiddleWare?(props: any): void; +} +export interface IAnimationDriverPlugin { + config?: IStyled; + register(styledUtils: IStyled): void; + engine: any; +} + +export class IAnimationResolver { + aliases?: any; + tokens?: any; + ref?: any; +} +export class IStyled { + aliases?: any; + tokens?: any; + ref?: any; +} + export interface Tokens { colors?: { [key: GenericKey]: Record & {} }; sizes?: { [key: GenericKey]: Record & {} }; diff --git a/yarn.lock b/yarn.lock index f186a0857..77e65a95b 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1812,6 +1812,13 @@ resolved "https://registry.yarnpkg.com/@emotion/hash/-/hash-0.9.1.tgz#4ffb0055f7ef676ebc3a5a91fb621393294e2f43" integrity sha512-gJB6HLm5rYwSLI6PQa+X1t5CFGrv1J1TWG+sOyMCeKz2ojaj6Fnl/rZEspogG+cvqbt4AE/2eIyD2QfLKTBNlQ== +"@emotion/is-prop-valid@^0.8.2": + version "0.8.8" + resolved "https://registry.yarnpkg.com/@emotion/is-prop-valid/-/is-prop-valid-0.8.8.tgz#db28b1c4368a259b60a97311d6a952d4fd01ac1a" + integrity sha512-u5WtneEAr5IDG2Wv65yhunPSMLIpuKsbuOktRojfrEiEvRyC85LgPMZI63cr7NUqT8ZIGdSVg8ZKGxIug4lXcA== + dependencies: + "@emotion/memoize" "0.7.4" + "@emotion/is-prop-valid@^1.1.0", "@emotion/is-prop-valid@^1.2.1": version "1.2.1" resolved "https://registry.yarnpkg.com/@emotion/is-prop-valid/-/is-prop-valid-1.2.1.tgz#23116cf1ed18bfeac910ec6436561ecb1a3885cc" @@ -2464,6 +2471,13 @@ resolved "https://registry.yarnpkg.com/@gar/promisify/-/promisify-1.1.3.tgz#555193ab2e3bb3b6adc3d551c9c030d9e860daf6" integrity sha512-k2Ty1JcVojjJFwrg/ThKi2ujJ7XNLYaFGNB/bWT9wGR+oSMJHMa5w+CUq6p/pVrKeNNgA7pCqEcjSnHVoqJQFw== +"@gluestack-style/animation-plugin@^0.1.7": + version "0.1.12" + resolved "https://registry.yarnpkg.com/@gluestack-style/animation-plugin/-/animation-plugin-0.1.12.tgz#887b57097397817c31fef25c8c53af381c11633e" + integrity sha512-lkj8iY5JBnhroUkP5gWE1zEpocb5GAK/G9SxL8DdWYQrsWOFpXr/mF/K67Dbxiv/n4B9BVff7sNTAddju+4UAw== + dependencies: + "@legendapp/motion" "^2.2.0" + "@gluestack-style/animation-plugin@latest": version "0.1.7" resolved "https://registry.yarnpkg.com/@gluestack-style/animation-plugin/-/animation-plugin-0.1.7.tgz#c9feae42684a48f2b5ece932681837d4b3ce7104" @@ -2505,6 +2519,14 @@ inline-style-prefixer "^6.0.1" normalize-css-color "^1.0.2" +"@gluestack-style/react@^0.2.11-alpha.0", "@gluestack-style/react@^0.2.16", "@gluestack-style/react@^0.2.21": + version "0.2.47" + resolved "https://registry.yarnpkg.com/@gluestack-style/react/-/react-0.2.47.tgz#af219ad64a651cd5008ac3bfb90b0b1baacf23de" + integrity sha512-XewdaACOpEPSiyWGwil/qWQIbiKdIhWWs/icf1NSg3psouY43FpEpHq9ARGajtxueEACsnYM9nlhfo5Vy1ZfvQ== + dependencies: + inline-style-prefixer "^6.0.1" + normalize-css-color "^1.0.2" + "@gluestack-ui/actionsheet@^0.2.16", "@gluestack-ui/actionsheet@^0.2.7", "@gluestack-ui/actionsheet@latest": version "0.2.16" resolved "https://registry.yarnpkg.com/@gluestack-ui/actionsheet/-/actionsheet-0.2.16.tgz#052a733966c517450a3cd6f832932ccf77924867" @@ -3423,6 +3445,59 @@ resolved "https://registry.yarnpkg.com/@mdx-js/util/-/util-1.6.22.tgz#219dfd89ae5b97a8801f015323ffa4b62f45718b" integrity sha512-H1rQc1ZOHANWBvPcW+JpGwr+juXSxM8Q8YCkm3GhZd8REu1fHR3z99CErO1p9pkcfcxZnMdIZdIsXkOHY0NilA== +"@motionone/animation@^10.12.0": + version "10.15.1" + resolved "https://registry.yarnpkg.com/@motionone/animation/-/animation-10.15.1.tgz#4a85596c31cbc5100ae8eb8b34c459fb0ccf6807" + integrity sha512-mZcJxLjHor+bhcPuIFErMDNyrdb2vJur8lSfMCsuCB4UyV8ILZLvK+t+pg56erv8ud9xQGK/1OGPt10agPrCyQ== + dependencies: + "@motionone/easing" "^10.15.1" + "@motionone/types" "^10.15.1" + "@motionone/utils" "^10.15.1" + tslib "^2.3.1" + +"@motionone/dom@10.12.0": + version "10.12.0" + resolved "https://registry.yarnpkg.com/@motionone/dom/-/dom-10.12.0.tgz#ae30827fd53219efca4e1150a5ff2165c28351ed" + integrity sha512-UdPTtLMAktHiqV0atOczNYyDd/d8Cf5fFsd1tua03PqTwwCe/6lwhLSQ8a7TbnQ5SN0gm44N1slBfj+ORIhrqw== + dependencies: + "@motionone/animation" "^10.12.0" + "@motionone/generators" "^10.12.0" + "@motionone/types" "^10.12.0" + "@motionone/utils" "^10.12.0" + hey-listen "^1.0.8" + tslib "^2.3.1" + +"@motionone/easing@^10.15.1": + version "10.15.1" + resolved "https://registry.yarnpkg.com/@motionone/easing/-/easing-10.15.1.tgz#95cf3adaef34da6deebb83940d8143ede3deb693" + integrity sha512-6hIHBSV+ZVehf9dcKZLT7p5PEKHGhDwky2k8RKkmOvUoYP3S+dXsKupyZpqx5apjd9f+php4vXk4LuS+ADsrWw== + dependencies: + "@motionone/utils" "^10.15.1" + tslib "^2.3.1" + +"@motionone/generators@^10.12.0": + version "10.15.1" + resolved "https://registry.yarnpkg.com/@motionone/generators/-/generators-10.15.1.tgz#dc6abb11139d1bafe758a41c134d4c753a9b871c" + integrity sha512-67HLsvHJbw6cIbLA/o+gsm7h+6D4Sn7AUrB/GPxvujse1cGZ38F5H7DzoH7PhX+sjvtDnt2IhFYF2Zp1QTMKWQ== + dependencies: + "@motionone/types" "^10.15.1" + "@motionone/utils" "^10.15.1" + tslib "^2.3.1" + +"@motionone/types@^10.12.0", "@motionone/types@^10.15.1": + version "10.15.1" + resolved "https://registry.yarnpkg.com/@motionone/types/-/types-10.15.1.tgz#89441b54285012795cbba8612cbaa0fa420db3eb" + integrity sha512-iIUd/EgUsRZGrvW0jqdst8st7zKTzS9EsKkP+6c6n4MPZoQHwiHuVtTQLD6Kp0bsBLhNzKIBlHXponn/SDT4hA== + +"@motionone/utils@^10.12.0", "@motionone/utils@^10.15.1": + version "10.15.1" + resolved "https://registry.yarnpkg.com/@motionone/utils/-/utils-10.15.1.tgz#6b5f51bde75be88b5411e084310299050368a438" + integrity sha512-p0YncgU+iklvYr/Dq4NobTRdAPv9PveRDUXabPEeOjBLSO/1FNB2phNTZxOxpi1/GZwYpAoECEa0Wam+nsmhSw== + dependencies: + "@motionone/types" "^10.15.1" + hey-listen "^1.0.8" + tslib "^2.3.1" + "@mrmlnc/readdir-enhanced@^2.2.1": version "2.2.1" resolved "https://registry.yarnpkg.com/@mrmlnc/readdir-enhanced/-/readdir-enhanced-2.2.1.tgz#524af240d1a360527b730475ecfa1344aa540dde" @@ -11643,6 +11718,27 @@ fragment-cache@^0.2.1: dependencies: map-cache "^0.2.2" +framer-motion@^6.5.1: + version "6.5.1" + resolved "https://registry.yarnpkg.com/framer-motion/-/framer-motion-6.5.1.tgz#802448a16a6eb764124bf36d8cbdfa6dd6b931a7" + integrity sha512-o1BGqqposwi7cgDrtg0dNONhkmPsUFDaLcKXigzuTFC5x58mE8iyTazxSudFzmT6MEyJKfjjU8ItoMe3W+3fiw== + dependencies: + "@motionone/dom" "10.12.0" + framesync "6.0.1" + hey-listen "^1.0.8" + popmotion "11.0.3" + style-value-types "5.0.0" + tslib "^2.1.0" + optionalDependencies: + "@emotion/is-prop-valid" "^0.8.2" + +framesync@6.0.1: + version "6.0.1" + resolved "https://registry.yarnpkg.com/framesync/-/framesync-6.0.1.tgz#5e32fc01f1c42b39c654c35b16440e07a25d6f20" + integrity sha512-fUY88kXvGiIItgNC7wcTOl0SNRCVXMKSWW2Yzfmn7EKNc+MpCzcz9DhdHcdjbrtN3c6R4H5dTY2jiCpPdysEjA== + dependencies: + tslib "^2.1.0" + freeport-async@2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/freeport-async/-/freeport-async-2.0.0.tgz#6adf2ec0c629d11abff92836acd04b399135bab4" @@ -12427,6 +12523,11 @@ hex-color-regex@^1.1.0: resolved "https://registry.yarnpkg.com/hex-color-regex/-/hex-color-regex-1.1.0.tgz#4c06fccb4602fe2602b3c93df82d7e7dbf1a8a8e" integrity sha512-l9sfDFsuqtOqKDsQdqrMRk0U85RZc0RtOR9yPI7mRVOa4FsR/BVnZ0shmQRM96Ji99kYZP/7hn1cedc1+ApsTQ== +hey-listen@^1.0.8: + version "1.0.8" + resolved "https://registry.yarnpkg.com/hey-listen/-/hey-listen-1.0.8.tgz#8e59561ff724908de1aa924ed6ecc84a56a9aa68" + integrity sha512-COpmrF2NOg4TBWUJ5UVyaCU2A88wEMkUPK4hNqyCkqHbxT92BbvfjoSozkAIIm6XhicGlJHhFdullInrdhwU8Q== + hmac-drbg@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/hmac-drbg/-/hmac-drbg-1.0.1.tgz#d2745701025a6c775a6c545793ed502fc0c649a1" @@ -15871,6 +15972,13 @@ mkdirp@^1.0.3, mkdirp@^1.0.4: resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-1.0.4.tgz#3eb5ed62622756d79a5f0e2a221dfebad75c2f7e" integrity sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw== +moti@^0.26.0: + version "0.26.0" + resolved "https://registry.yarnpkg.com/moti/-/moti-0.26.0.tgz#863e70e61c9d597f4fd701e588f41963335b927f" + integrity sha512-430HDIwhPQi/DkMvocyAZGkAX3ibmbyF3Fj23GuhbTB+RUXYTOnbsvygr89ABJjllYuxx4Xjd2Z4Qab0Su5mcg== + dependencies: + framer-motion "^6.5.1" + move-concurrently@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/move-concurrently/-/move-concurrently-1.0.1.tgz#be2c005fda32e0b29af1f05d7c4b33214c701f92" @@ -17187,6 +17295,16 @@ polished@^4.2.2: dependencies: "@babel/runtime" "^7.17.8" +popmotion@11.0.3: + version "11.0.3" + resolved "https://registry.yarnpkg.com/popmotion/-/popmotion-11.0.3.tgz#565c5f6590bbcddab7a33a074bb2ba97e24b0cc9" + integrity sha512-Y55FLdj3UxkR7Vl3s7Qr4e9m0onSnP8W7d/xQLsoJM40vs6UKHFdygs6SWryasTZYqugMjm3BepCF4CWXDiHgA== + dependencies: + framesync "6.0.1" + hey-listen "^1.0.8" + style-value-types "5.0.0" + tslib "^2.1.0" + portfinder@^1.0.26: version "1.0.32" resolved "https://registry.yarnpkg.com/portfinder/-/portfinder-1.0.32.tgz#2fe1b9e58389712429dc2bea5beb2146146c7f81" @@ -20062,6 +20180,14 @@ style-to-object@0.3.0, style-to-object@^0.3.0: dependencies: inline-style-parser "0.1.1" +style-value-types@5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/style-value-types/-/style-value-types-5.0.0.tgz#76c35f0e579843d523187989da866729411fc8ad" + integrity sha512-08yq36Ikn4kx4YU6RD7jWEv27v4V+PUsOGa4n/as8Et3CuODMJQ00ENeAVXAeydX4Z2j1XHZF1K2sX4mGl18fA== + dependencies: + hey-listen "^1.0.8" + tslib "^2.1.0" + styled-components@^5.3.0: version "5.3.11" resolved "https://registry.yarnpkg.com/styled-components/-/styled-components-5.3.11.tgz#9fda7bf1108e39bf3f3e612fcc18170dedcd57a8" @@ -20685,7 +20811,7 @@ tslib@^1.8.1: resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.14.1.tgz#cf2d38bdc34a134bcaf1091c41f6619e2f672d00" integrity sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg== -tslib@^2.0.0, tslib@^2.0.1, tslib@^2.0.3, tslib@^2.1.0, tslib@^2.4.0: +tslib@^2.0.0, tslib@^2.0.1, tslib@^2.0.3, tslib@^2.1.0, tslib@^2.3.1, tslib@^2.4.0: version "2.6.2" resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.6.2.tgz#703ac29425e7b37cd6fd456e92404d46d1f3e4ae" integrity sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q== From 481f3fc01d8a4eccafca1e148b3ab8fcb5b8f4aa Mon Sep 17 00:00:00 2001 From: Rohit Singh Date: Thu, 21 Sep 2023 12:18:43 +0530 Subject: [PATCH 2/7] feat: animation plugin driver --- .../storybook/.ondevice/storybook.requires.js | 9 +- example/storybook/babel.config.js | 5 + example/storybook/package.json | 6 +- example/storybook/src/components/nb.config.ts | 1 + .../AnimationPlugin/AnimationPlugin.tsx | 18 +- example/storybook/tsconfig.json | 3 + packages/animation-moti-driver/src/index.tsx | 35 +-- packages/react/src/createConfig.ts | 22 +- packages/react/src/index.ts | 2 +- .../src/plugins/animation-components.tsx | 48 +++- .../react/src/plugins/animation-resolver.tsx | 56 ++++- packages/react/src/styled.tsx | 37 +-- packages/react/src/types.ts | 214 ++++++++++-------- yarn.lock | 155 ++++++++++++- 14 files changed, 441 insertions(+), 170 deletions(-) diff --git a/example/storybook/.ondevice/storybook.requires.js b/example/storybook/.ondevice/storybook.requires.js index 0c8a85310..885793009 100644 --- a/example/storybook/.ondevice/storybook.requires.js +++ b/example/storybook/.ondevice/storybook.requires.js @@ -7,7 +7,8 @@ import { addArgsEnhancer, clearDecorators, } from '@storybook/react-native'; - +import { config } from '../src/components/nb.config'; +console.log(Object.keys(config)); global.STORIES = [ { titlePrefix: '', @@ -52,8 +53,8 @@ const getStories = () => { // "./src/api/AsForwarder/AsForwarder.stories.tsx": require("../src/api/AsForwarder/AsForwarder.stories.tsx"), // "./src/api/ColorModeBasedStyles/ColorMode.stories.tsx": require("../src/api/ColorModeBasedStyles/ColorMode.stories.tsx"), // "./src/api/CompoundVariants/CompoundVarinats.stories.tsx": require("../src/api/CompoundVariants/CompoundVarinats.stories.tsx"), - './src/api/createStyled/createStyled.stories.tsx': require('../src/api/createStyled/createStyled.stories.tsx'), - './src/api/DescendantsStyles/ContextBasedStyles.stories.tsx': require('../src/api/DescendantsStyles/ContextBasedStyles.stories.tsx'), + // './src/api/createStyled/createStyled.stories.tsx': require('../src/api/createStyled/createStyled.stories.tsx'), + // './src/api/DescendantsStyles/ContextBasedStyles.stories.tsx': require('../src/api/DescendantsStyles/ContextBasedStyles.stories.tsx'), // "./src/api/MultipleProvder/MultipleProvider.stories.tsx": require("../src/api/MultipleProvder/MultipleProvider.stories.tsx"), // "./src/api/PlatformBasedStyles/PlatformBasedStyles.stories.tsx": require("../src/api/PlatformBasedStyles/PlatformBasedStyles.stories.tsx"), // "./src/api/PropertyResolver/PropertyResolver.stories.tsx": require("../src/api/PropertyResolver/PropertyResolver.stories.tsx"), @@ -65,7 +66,7 @@ const getStories = () => { // "./src/api/Typescript/Typescript.stories.tsx": require("../src/api/Typescript/Typescript.stories.tsx"), // "./src/api/UtilityFunctions/UtilityFunctions.stories.tsx": require("../src/api/UtilityFunctions/UtilityFunctions.stories.tsx"), // "./src/api/Variants/BaseStyleVariantSizes.stories.tsx": require("../src/api/Variants/BaseStyleVariantSizes.stories.tsx"), - // "./src/plugins/AnimationPlugin/Animation.stories.tsx": require("../src/plugins/AnimationPlugin/Animation.stories.tsx"), + './src/plugins/AnimationPlugin/Animation.stories.tsx': require('../src/plugins/AnimationPlugin/Animation.stories.tsx'), // "./src/plugins/CSSVariables/CSSVariables.stories.tsx": require("../src/plugins/CSSVariables/CSSVariables.stories.tsx"), // "./src/plugins/FontsPlugin/FontsPlugin.stories.tsx": require("../src/plugins/FontsPlugin/FontsPlugin.stories.tsx"), }; diff --git a/example/storybook/babel.config.js b/example/storybook/babel.config.js index c3c30e856..461ef719f 100644 --- a/example/storybook/babel.config.js +++ b/example/storybook/babel.config.js @@ -5,6 +5,7 @@ module.exports = function (api) { return { presets: ['babel-preset-expo'], plugins: [ + 'react-native-reanimated/plugin', process.env.NODE_ENV !== 'production' ? [ 'module-resolver', @@ -19,6 +20,10 @@ module.exports = function (api) { __dirname, '../../packages/animation-legend-motion-driver/src' ), + ['@gluestack-style/moti-driver']: path.join( + __dirname, + '../../packages/animation-moti-driver/src' + ), // ['@gluestack-style/animation-plugin']: path.join( // __dirname, // '../../packages/animation-plugin/src' diff --git a/example/storybook/package.json b/example/storybook/package.json index 81f6f89b1..f93316f9f 100644 --- a/example/storybook/package.json +++ b/example/storybook/package.json @@ -52,8 +52,11 @@ "expo-status-bar": "~1.4.2", "fs": "^0.0.1-security", "lucide-react-native": "^0.236.0", + "moti": "^0.26.0", "prism-react-renderer": "^1.3.5", "react": "^18.2.0", + "react-native-gesture-handler": "^2.13.1", + "react-native-reanimated": "~2.12.0", "react-native-safe-area-context": "^4.4.1", "react-native-svg": "13.4.0", "react-native-web": "^0.19.7", @@ -94,10 +97,11 @@ "typescript": "^5.1.6" }, "peerDependencies": { + "@gluestack-style/legend-motion-driver": "*", + "@gluestack-style/moti-driver": "*", "react": "*", "react-dom": "*", "react-native": "*", - "@gluestack-style/legend-motion-driver": "*", "react-native-web": "*" }, "react-native-builder-bob": { diff --git a/example/storybook/src/components/nb.config.ts b/example/storybook/src/components/nb.config.ts index 2d3a163c1..e325c5645 100644 --- a/example/storybook/src/components/nb.config.ts +++ b/example/storybook/src/components/nb.config.ts @@ -1,4 +1,5 @@ import { MotionAnimationDriver } from '@gluestack-style/legend-motion-driver'; +// import { MotiAnimationDriver } from '@gluestack-style/moti-driver'; import { createConfig, AnimationResolver } from '@gluestack-style/react'; export const config = createConfig({ diff --git a/example/storybook/src/plugins/AnimationPlugin/AnimationPlugin.tsx b/example/storybook/src/plugins/AnimationPlugin/AnimationPlugin.tsx index 91917e91e..ed91f0187 100644 --- a/example/storybook/src/plugins/AnimationPlugin/AnimationPlugin.tsx +++ b/example/storybook/src/plugins/AnimationPlugin/AnimationPlugin.tsx @@ -1,6 +1,6 @@ import React from 'react'; import { Wrapper } from '../../components/Wrapper'; -import { Pressable, View } from 'react-native'; +import { Pressable, View, Text } from 'react-native'; import { styled, AnimatedImage, @@ -8,12 +8,10 @@ import { } from '@gluestack-style/react'; const images = [require('./1.png'), require('./2.png'), require('./3.png')]; - const Box = styled(View, {}); const StyledMotionImage = styled( AnimatedImage, { - // @ts-ignore ':animate': { zIndex: 1, x: 0, @@ -49,10 +47,12 @@ export function AnimationPlugin() { height: '100%', position: 'absolute', }} + // For Mobile + // source={images[imageIndex]} + // For Web source={{ uri: images[imageIndex] }} key={`image-${imageIndex}-${Math.random()}`} sx={{ - // @ts-ignore ':initial': { x: xPosition, opacity: 0, @@ -63,7 +63,11 @@ export function AnimationPlugin() { opacity: 0, }, ':transition': { - x: { type: 'spring', stiffness: 200, damping: 23 }, + x: { + type: 'spring', + stiffness: 200, + damping: 23, + }, }, }} /> @@ -87,7 +91,7 @@ export function AnimationPlugin() { setImageIndex((prev) => (prev + 1) % images.length); }} > - {'‣'} + {'‣'} (prev - 1 + images.length) % images.length); }} > - {'‣'} + {'‣'} ); diff --git a/example/storybook/tsconfig.json b/example/storybook/tsconfig.json index 4ba5aa0d5..f0e2e0bfc 100644 --- a/example/storybook/tsconfig.json +++ b/example/storybook/tsconfig.json @@ -7,6 +7,9 @@ "@gluestack-style/legend-motion-driver": [ "../../packages/animation-legend-motion-driver/src" ], + "@gluestack-style/moti-driver": [ + "../../packages/animation-moti-driver/src" + ], "react-native": ["./node_modules/react-native-web"] }, "emitDeclarationOnly": true, diff --git a/packages/animation-moti-driver/src/index.tsx b/packages/animation-moti-driver/src/index.tsx index 7cea3c537..4d574f316 100644 --- a/packages/animation-moti-driver/src/index.tsx +++ b/packages/animation-moti-driver/src/index.tsx @@ -4,7 +4,8 @@ import type { // @ts-ignore IAnimationResolver, } from '@gluestack-style/react'; - +import 'react-native-reanimated'; +import 'react-native-gesture-handler'; import { MotiImage, MotiView, @@ -12,6 +13,7 @@ import { MotiScrollView, MotiSafeAreaView, MotiProgressBar, + AnimatePresence, } from 'moti'; let Moti = { @@ -21,11 +23,12 @@ let Moti = { ScrollView: MotiScrollView, SafeAreaView: MotiSafeAreaView, ProgressBar: MotiProgressBar, + AnimatePresence, }; export class MotiAnimationDriver implements IAnimationDriverPlugin { name: 'MotiAnimationDriver'; - LibraryImports = Moti; - styledUtils = { + engine = Moti; + config = { aliases: { ':animate': 'animate', ':initial': 'from', @@ -38,29 +41,33 @@ export class MotiAnimationDriver implements IAnimationDriverPlugin { ':whileHover': 'whileHover', ':onAnimationComplete': 'onAnimationComplete', } as const, + animatedPropMap: { + x: 'translateX', + y: 'translateY', + } as const, }; - register(styledUtils: any) { - if (this.styledUtils) { - this.styledUtils.aliases = { - ...this.styledUtils?.aliases, - ...styledUtils?.aliases, + register(config: any) { + if (this.config) { + this.config.aliases = { + ...this.config?.aliases, + ...config?.aliases, }; // @ts-ignore - this.styledUtils.tokens = { + this.config.tokens = { // @ts-ignore - ...this.styledUtils?.tokens, - ...styledUtils?.tokens, + ...this.config?.tokens, + ...config?.tokens, }; // @ts-ignore - this.styledUtils.ref = styledUtils?.ref; + this.config.ref = config?.ref; } } - constructor(styledUtils?: IAnimationResolver) { - this.register(styledUtils); + constructor(config?: IAnimationResolver) { + this.register(config); this.name = 'MotiAnimationDriver'; } } diff --git a/packages/react/src/createConfig.ts b/packages/react/src/createConfig.ts index 5aad9322b..07b66ddde 100644 --- a/packages/react/src/createConfig.ts +++ b/packages/react/src/createConfig.ts @@ -6,12 +6,14 @@ import { stableHash } from './stableHash'; import { propertyTokenMap } from './propertyTokenMap'; import { updateOrderUnResolvedMap } from './updateOrderUnResolvedMap'; -var globalPluginStore: any = []; - -function setGlobalPluginStore(plugins: Array) { - globalPluginStore.push(...plugins); +var globalPluginStore: never[] = []; +function setGlobalPluginStore(plugins: any) { + if (plugins) { + // @ts-ignore + globalPluginStore.push(...plugins); + } + return getGlobalPluginStore(); } - function getGlobalPluginStore() { return globalPluginStore; } @@ -25,7 +27,8 @@ export const createConfig = < //@ts-ignore T['tokens'], T['aliases'], - T['globalStyle'] + T['globalStyle'], + T['plugins'] > >( config: @@ -34,13 +37,14 @@ export const createConfig = < //@ts-ignore T['tokens'], T['aliases'], - T['globalStyle'] + T['globalStyle'], + T['plugins'] > ): T => { if (config.plugins) { - setGlobalPluginStore(config.plugins); + config.plugins = setGlobalPluginStore(config.plugins); } - delete config.plugins; + // delete config.plugins; if ( !config.components && diff --git a/packages/react/src/index.ts b/packages/react/src/index.ts index da61a9032..e1a6f4c6b 100644 --- a/packages/react/src/index.ts +++ b/packages/react/src/index.ts @@ -86,6 +86,6 @@ export { AsForwarder } from './AsForwarder'; export * from './plugins'; export { INTERNAL_updateCSSStyleInOrderedResolved } from './updateCSSStyleInOrderedResolved'; -export { createConfig } from './createConfig'; +export { createConfig, getInstalledPlugins } from './createConfig'; export * from './core'; export * from './hooks'; diff --git a/packages/react/src/plugins/animation-components.tsx b/packages/react/src/plugins/animation-components.tsx index 65a8983df..712491b3c 100644 --- a/packages/react/src/plugins/animation-components.tsx +++ b/packages/react/src/plugins/animation-components.tsx @@ -1,28 +1,60 @@ import React from 'react'; import { useStyled } from '..'; - -const AnimatedText = (props: React.PropsWithChildren) => { +import type { + ImageProps, + PressableProps, + ViewProps, + TextProps, + ScrollViewProps, + FlatListProps, + SectionListProps, +} from 'react-native'; +const AnimatedText = ( + props: TextProps & { + animationComponentGluestack: true; + } +) => { return ; }; -const AnimatedView = (props: React.PropsWithChildren) => { +const AnimatedView = ( + props: ViewProps & { + animationComponentGluestack: true; + } +) => { return ; }; -const AnimatedPressable = (props: React.PropsWithChildren) => { +const AnimatedPressable = ( + props: PressableProps & { + animationComponentGluestack: true; + } +) => { + // @ts-ignore return ; }; -const AnimatedImage = (props: React.PropsWithChildren) => { +const AnimatedImage = ( + props: ImageProps & { + animationComponentGluestack: true; + } +) => { + // @ts-ignore return ; }; -const AnimatedScrollView = (props: React.PropsWithChildren) => { +const AnimatedScrollView = ( + props: ScrollViewProps & { animationComponentGluestack: true } +) => { return ; }; const AnimatedSafeAreaView = (props: React.PropsWithChildren) => { return ; }; -const AnimatedFlatList = (props: React.PropsWithChildren) => { +const AnimatedFlatList = ( + props: FlatListProps & { animationComponentGluestack: true } +) => { return ; }; -const AnimatedSectionList = (props: React.PropsWithChildren) => { +const AnimatedSectionList = ( + props: SectionListProps & { animationComponentGluestack: true } +) => { return ; }; const AnimatedAnimatePresence = React.forwardRef( diff --git a/packages/react/src/plugins/animation-resolver.tsx b/packages/react/src/plugins/animation-resolver.tsx index 753fafe61..3d349ca31 100644 --- a/packages/react/src/plugins/animation-resolver.tsx +++ b/packages/react/src/plugins/animation-resolver.tsx @@ -84,30 +84,51 @@ export class AnimationResolver implements IStyledPlugin { name: 'AnimationResolver'; componentDriver: IAnimationDriverPlugin; config = { - aliases: {} as const, + aliases: { + ':animate': 'animate', + ':initial': 'initial', + ':exit': 'exit', + ':initialProps': 'initialProps', + ':animateProps': 'animateProps', + ':transition': 'transition', + ':transformOrigin': 'transformOrigin', + ':whileTap': 'whileTap', + ':whileHover': 'whileHover', + ':onAnimationComplete': 'onAnimationComplete', + } as const, tokens: {} as const, + animatedPropMap: {} as any, }; AnimatePresenceComp = React.Fragment; register(config: any) { if (this.config) { - this.config.aliases = { - ...this.config?.aliases, - ...config?.aliases, - }; - - this.config.tokens = { - ...this.config?.tokens, - ...config?.tokens, - }; + if (config?.aliases) { + this.config.aliases = { + ...this.config?.aliases, + ...config?.aliases, + }; + } + if (config?.tokens) { + this.config.tokens = { + ...this.config?.tokens, + ...config?.tokens, + }; + } + if (config?.animatedPropMap) { + this.config.animatedPropMap = { + ...this.config?.animatedPropMap, + ...config?.animatedPropMap, + }; + } // @ts-ignore this.config.ref = config?.ref; } } - constructor(ComponentDriverClass: IAnimationDriverPlugin, config: any = {}) { + constructor(ComponentDriverClass: any, config: any = {}) { // @ts-ignore const componentDriver = new ComponentDriverClass(config); this.name = 'AnimationResolver'; @@ -171,6 +192,7 @@ export class AnimationResolver implements IStyledPlugin { return [styledObj, shouldUpdateConfig, _, AnimatedComponent]; } + // @ts-ignore return [ resolvedStyledObjectWithAnimatedProps, shouldUpdateConfig, @@ -178,6 +200,7 @@ export class AnimationResolver implements IStyledPlugin { AnimatedComponent, ]; } + // @ts-ignore return [styledObj, shouldUpdateConfig, _, Component]; } @@ -188,6 +211,7 @@ export class AnimationResolver implements IStyledPlugin { keyPath: string[] = [] ) { const aliases = this.config?.aliases; + const animatedPropMap = this.config?.animatedPropMap; for (const prop in styledObject) { if (typeof styledObject[prop] === 'object') { keyPath.push(prop); @@ -218,10 +242,20 @@ export class AnimationResolver implements IStyledPlugin { keyPath.pop(); delete styledObject[prop]; } + + if (animatedPropMap && animatedPropMap[prop]) { + this.renameObjectKey(styledObject, prop, animatedPropMap[prop]); + } } return resolvedStyledObject; } + renameObjectKey(obj: any, from: string, to: string) { + obj[to] = obj[from]; + delete obj[from]; + return obj; + } + componentMiddleWare({ Component, ExtendedConfig }: any) { if (Component && Component.isAnimatedComponent) { const styledConfig = this.#childrenExitPropsMap; diff --git a/packages/react/src/styled.tsx b/packages/react/src/styled.tsx index a6466e45a..1035ea30e 100644 --- a/packages/react/src/styled.tsx +++ b/packages/react/src/styled.tsx @@ -990,14 +990,23 @@ export function verboseStyled( // styledIds: BUILD_TIME_STYLE_IDS = [], // sxHash: BUILD_TIME_sxHash = '', ...componentProps - }: Omit & - Partial< - ComponentProps - > & - Partial> & { - as?: any; - children?: any; - }, + }: Omit< + Omit & + Partial< + ComponentProps< + ITypeReactNativeStyles, + Variants, + P, + ComCon, + PluginType + > + > & + Partial> & { + as?: any; + children?: any; + }, + 'animationComponentGluestack' + >, ref: React.ForwardedRef

) => { const isClient = React.useRef(false); @@ -1860,11 +1869,11 @@ export function verboseStyled( return StyledComp; } -export function styled( +export function styled( Component: React.ComponentType

, - theme: ITheme, + theme: ITheme, componentStyleConfig?: IComponentStyleConfig, - ExtendedConfig?: ExtendedConfigType, + ExtendedConfig?: ExtendedConfigType, BUILD_TIME_PARAMS?: { orderedResolved: OrderedSXResolved; verbosedStyleIds: { @@ -1880,8 +1889,8 @@ export function styled( // process.env.NODE_ENV === 'development' && DEBUG_TAG ? false : false; let styledObj = theme; - // @ts-ignore - let plugins: PluginType = [...getInstalledPlugins()]; + let plugins = [...getInstalledPlugins()]; + if (ExtendedConfig?.plugins) { // @ts-ignore plugins = [...plugins, ...ExtendedConfig?.plugins]; @@ -1899,7 +1908,7 @@ export function styled( theme = styledObj; const sxConvertedObject = convertStyledToStyledVerbosed(theme); - let StyledComponent = verboseStyled( + let StyledComponent = verboseStyled( Component, sxConvertedObject, componentStyleConfig, diff --git a/packages/react/src/types.ts b/packages/react/src/types.ts index a22af2ea1..5a7add1aa 100644 --- a/packages/react/src/types.ts +++ b/packages/react/src/types.ts @@ -76,6 +76,7 @@ export type AliasesType = { [key: string]: keyof RNStyledProps; }; +export type GlobalPluginType = []; export type GenericAliases = {}; export type GenericGlobalStyle = { // variants: {}; @@ -101,12 +102,13 @@ export type ThemeStyles = Partial<{ export type GlueStackConfig< IToken extends Tokens, IGlobalAliases, - IGlobalStyle + IGlobalStyle, + PluginType = [] > = { tokens: IToken; aliases: IGlobalAliases; globalStyle?: GlobalStyles; - plugins?: Array; + plugins?: PluginType; themes?: ThemeStyles; components?: { [key: string]: { @@ -125,19 +127,22 @@ export type ComponentsThemeType = { export type InferConfig = Conf extends GlueStackConfig< infer A, infer C, - infer D + infer D, + infer B > - ? GlueStackConfig + ? GlueStackConfig : any; export type CreateGenericConfig = GlueStackConfig< Tokens, GenericAliases, - GenericGlobalStyle + GenericGlobalStyle, + GlobalPluginType >; // All Aliases export type Aliases = GSConfig['aliases']; +export type Plugins = GSConfig['plugins']; export type Components = GSConfig['components']; export type IMediaQueries = keyof GSConfig['tokens']['mediaQueries']; @@ -204,10 +209,10 @@ type PropsResolveType = { props?: Partial; }; type PropertyResolverType = PropsResolveType & ResolverType; -export type ExtendedConfigType = { +export type ExtendedConfigType = { propertyTokenMap?: PropertyTokenMapType; propertyResolver?: PropertyResolverType; - plugins?: T; + plugins?: Array; }; /*********************** GLOBAL STYLE TYPES ****************************************/ @@ -300,37 +305,44 @@ export type GlobalStyles = GlobalVariantSx< /*********************** USER THEME / SX TYPES ****************************************/ -export type ITheme = Partial< - //@ts-ignore - StyledThemeProps +export type ITheme = Partial< + StyledThemeProps< + Variants, + //@ts-ignore + P['style'], + P, + //@ts-ignore + P['animationComponentGluestack'] extends true ? Plugins : [] + > >; export type StyledThemeProps< Variants, GenericComponentStyles, GenericComponentProps, - PluginType - // @ts-ignore + PluginTypes > = SxProps< GenericComponentStyles, Variants & GlobalVariants, GenericComponentProps, '', '', - PluginType + PluginTypes > & { [Key in `@${IMediaQueries}`]: SxProps< GenericComponentStyles, Variants, GenericComponentProps, '', - Key + Key, + PluginTypes >; } & { variants: VariantType< Variants, GenericComponentStyles, - GenericComponentProps + GenericComponentProps, + PluginTypes >; // sizes?: SizeTypeNew; compoundVariants?: Array< @@ -338,10 +350,20 @@ export type StyledThemeProps< >; defaultProps?: { [Key in keyof MergeNested< - VariantType, + VariantType< + Variants, + GenericComponentStyles, + GenericComponentProps, + PluginTypes + >, GlobalVariants >]?: keyof MergeNested< - VariantType, + VariantType< + Variants, + GenericComponentStyles, + GenericComponentProps, + PluginTypes + >, GlobalVariants >[Key]; } & { [key: string]: any }; @@ -386,13 +408,22 @@ type PassingPropsType< } : {}; -// PluginPropsType< -// PluginType, -// GenericComponentProps, -// GenericComponentStyles, -// PLATFORM -// > - +type AnimatedPropsType = { + opacity: number | string; + x: number | string | {}; + y: number | string | {}; + scale: any; + scaleX: any; + scaleY: any; + skewX: any; + skewY: any; + perspective: any; + rotate: number; + rotateY: number; + rotateZ: number; + matrix: any; +}; +// componentDriver // eslint-disable-next-line @typescript-eslint/no-unused-vars type PluginPropsType< PluginType, @@ -403,26 +434,9 @@ type PluginPropsType< [key in keyof UnionToIntersection< // @ts-ignore ReturnType - >]: Partial< - UnionToIntersection< - // @ts-ignore - ReturnType - >[key] extends keyof GenericComponentProps - ? StylePropsType & - GenericComponentProps[UnionToIntersection< - // @ts-ignore - ReturnType - >[key]] - : UnionToIntersection< - // @ts-ignore - ReturnType - >[key] extends keyof GenericComponentStyles - ? GenericComponentStyles[UnionToIntersection< - // @ts-ignore - ReturnType - >[key]] - : any - >; + >]: Partial & + Partial> & + Partial; }; export type SxProps< @@ -430,8 +444,8 @@ export type SxProps< Variants = unknown, GenericComponentProps = unknown, PLATFORM = '', - MediaQuery = '' - // PluginType = [] + MediaQuery = '', + PluginType = [] > = Partial< StylePropsType & PassingPropsType< @@ -440,73 +454,87 @@ export type SxProps< GenericComponentProps, MediaQuery > -> & { - [Key in `_${COLORMODES}`]?: SxProps< - GenericComponentStyles, - Variants, - GenericComponentProps, - PLATFORM, - MediaQuery - >; -} & { - [Key in `:${IState}`]?: SxProps< - GenericComponentStyles, - Variants, - GenericComponentProps, - PLATFORM, - MediaQuery - >; -} & { - [Key in `_${PLATFORMS}`]?: SxProps< - GenericComponentStyles, - Variants, - GenericComponentProps, - Key, - MediaQuery - > & - PassingPropsType< +> & + Partial< + PluginPropsType< + PluginType, + GenericComponentProps, + GenericComponentStyles, + PLATFORM + > + > & { + [Key in `_${COLORMODES}`]?: SxProps< GenericComponentStyles, Variants, GenericComponentProps, - MediaQuery - > & - Partial<{ - [key: string]: any; - }>; -} & { - [Key in `_${string}`]?: SxProps< - RNStyledProps, - {}, - GenericComponentProps, - PLATFORM, - MediaQuery - > & - PassingPropsType< + PLATFORM, + MediaQuery, + PluginType + >; + } & { + [Key in `:${IState}`]?: SxProps< + GenericComponentStyles, + Variants, + GenericComponentProps, + PLATFORM, + MediaQuery, + PluginType + >; + } & { + [Key in `_${PLATFORMS}`]?: SxProps< GenericComponentStyles, + Variants, + GenericComponentProps, + Key, + MediaQuery, + PluginType + > & + PassingPropsType< + GenericComponentStyles, + Variants, + GenericComponentProps, + MediaQuery + > & + Partial<{ + [key: string]: any; + }>; + } & { + [Key in `_${string}`]?: SxProps< + RNStyledProps, {}, GenericComponentProps, - MediaQuery + PLATFORM, + MediaQuery, + PluginType > & - Partial<{ - [key: string]: any; - }>; -}; + PassingPropsType< + GenericComponentStyles, + {}, + GenericComponentProps, + MediaQuery + > & + Partial<{ + [key: string]: any; + }>; + }; export type VariantType< Variants, GenericComponentStyles, - GenericComponentProps + GenericComponentProps, + PluginTypes = [] > = | { [Key1 in keyof Variants]: { [Key in keyof Variants[Key1]]: Partial< - SxProps & { + SxProps & { [K in `@${IMediaQueries}`]?: SxProps< GenericComponentStyles, Variants, GenericComponentProps, '', - K + K, + PluginTypes >; } >; diff --git a/yarn.lock b/yarn.lock index 77e65a95b..37e42ba1e 100644 --- a/yarn.lock +++ b/yarn.lock @@ -187,6 +187,21 @@ "@babel/helper-split-export-declaration" "^7.22.6" semver "^6.3.1" +"@babel/helper-create-class-features-plugin@^7.22.15": + version "7.22.15" + resolved "https://registry.yarnpkg.com/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.22.15.tgz#97a61b385e57fe458496fad19f8e63b63c867de4" + integrity sha512-jKkwA59IXcvSaiK2UN45kKwSC9o+KuoXsBDvHvU/7BecYIp8GQ2UwrVvFgJASUT+hBnwJx6MhvMCuMzwZZ7jlg== + dependencies: + "@babel/helper-annotate-as-pure" "^7.22.5" + "@babel/helper-environment-visitor" "^7.22.5" + "@babel/helper-function-name" "^7.22.5" + "@babel/helper-member-expression-to-functions" "^7.22.15" + "@babel/helper-optimise-call-expression" "^7.22.5" + "@babel/helper-replace-supers" "^7.22.9" + "@babel/helper-skip-transparent-expression-wrappers" "^7.22.5" + "@babel/helper-split-export-declaration" "^7.22.6" + semver "^6.3.1" + "@babel/helper-create-regexp-features-plugin@^7.18.6", "@babel/helper-create-regexp-features-plugin@^7.22.5": version "7.22.9" resolved "https://registry.yarnpkg.com/@babel/helper-create-regexp-features-plugin/-/helper-create-regexp-features-plugin-7.22.9.tgz#9d8e61a8d9366fe66198f57c40565663de0825f6" @@ -226,6 +241,11 @@ resolved "https://registry.yarnpkg.com/@babel/helper-environment-visitor/-/helper-environment-visitor-7.22.5.tgz#f06dd41b7c1f44e1f8da6c4055b41ab3a09a7e98" integrity sha512-XGmhECfVA/5sAt+H+xpSg0mfrHq6FzNr9Oxh7PSEBBRUb/mL7Kz3NICXb194rCqAEdxkhPT1a88teizAFyvk8Q== +"@babel/helper-environment-visitor@^7.22.20": + version "7.22.20" + resolved "https://registry.yarnpkg.com/@babel/helper-environment-visitor/-/helper-environment-visitor-7.22.20.tgz#96159db61d34a29dba454c959f5ae4a649ba9167" + integrity sha512-zfedSIzFhat/gFhWfHtgWvlec0nqB9YEIVrpuwjruLlXfUSnA8cJB0miHKwqDnQ7d32aKo2xt88/xZptwxbfhA== + "@babel/helper-function-name@^7.22.5": version "7.22.5" resolved "https://registry.yarnpkg.com/@babel/helper-function-name/-/helper-function-name-7.22.5.tgz#ede300828905bb15e582c037162f99d5183af1be" @@ -241,6 +261,13 @@ dependencies: "@babel/types" "^7.22.5" +"@babel/helper-member-expression-to-functions@^7.22.15": + version "7.22.15" + resolved "https://registry.yarnpkg.com/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.22.15.tgz#b95a144896f6d491ca7863576f820f3628818621" + integrity sha512-qLNsZbgrNh0fDQBCPocSL8guki1hcPvltGDv/NxvUoABwFq7GkKSu1nRXeJkVZc+wJvne2E0RKQz+2SQrz6eAA== + dependencies: + "@babel/types" "^7.22.15" + "@babel/helper-member-expression-to-functions@^7.22.5": version "7.22.5" resolved "https://registry.yarnpkg.com/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.22.5.tgz#0a7c56117cad3372fbf8d2fb4bf8f8d64a1e76b2" @@ -255,6 +282,13 @@ dependencies: "@babel/types" "^7.22.5" +"@babel/helper-module-imports@^7.22.15": + version "7.22.15" + resolved "https://registry.yarnpkg.com/@babel/helper-module-imports/-/helper-module-imports-7.22.15.tgz#16146307acdc40cc00c3b2c647713076464bdbf0" + integrity sha512-0pYVBnDKZO2fnSPCrgM/6WMc7eS20Fbok+0r88fp+YtWVLZrp4CkafFGIp+W0VKw4a22sgebPT99y+FDNMdP4w== + dependencies: + "@babel/types" "^7.22.15" + "@babel/helper-module-transforms@^7.12.1", "@babel/helper-module-transforms@^7.22.5", "@babel/helper-module-transforms@^7.22.9", "@babel/helper-module-transforms@^7.9.0": version "7.22.9" resolved "https://registry.yarnpkg.com/@babel/helper-module-transforms/-/helper-module-transforms-7.22.9.tgz#92dfcb1fbbb2bc62529024f72d942a8c97142129" @@ -266,6 +300,17 @@ "@babel/helper-split-export-declaration" "^7.22.6" "@babel/helper-validator-identifier" "^7.22.5" +"@babel/helper-module-transforms@^7.22.15": + version "7.22.20" + resolved "https://registry.yarnpkg.com/@babel/helper-module-transforms/-/helper-module-transforms-7.22.20.tgz#da9edc14794babbe7386df438f3768067132f59e" + integrity sha512-dLT7JVWIUUxKOs1UnJUBR3S70YK+pKX6AbJgB2vMIvEkZkrfJDbYDJesnPshtKV4LhDOR3Oc5YULeDizRek+5A== + dependencies: + "@babel/helper-environment-visitor" "^7.22.20" + "@babel/helper-module-imports" "^7.22.15" + "@babel/helper-simple-access" "^7.22.5" + "@babel/helper-split-export-declaration" "^7.22.6" + "@babel/helper-validator-identifier" "^7.22.20" + "@babel/helper-optimise-call-expression@^7.22.5": version "7.22.5" resolved "https://registry.yarnpkg.com/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.22.5.tgz#f21531a9ccbff644fdd156b4077c16ff0c3f609e" @@ -327,11 +372,21 @@ resolved "https://registry.yarnpkg.com/@babel/helper-string-parser/-/helper-string-parser-7.22.5.tgz#533f36457a25814cf1df6488523ad547d784a99f" integrity sha512-mM4COjgZox8U+JcXQwPijIZLElkgEpO5rsERVDJTc2qfCDfERyob6k5WegS14SX18IIjv+XD+GrqNumY5JRCDw== +"@babel/helper-validator-identifier@^7.22.19", "@babel/helper-validator-identifier@^7.22.20": + version "7.22.20" + resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.22.20.tgz#c4ae002c61d2879e724581d96665583dbc1dc0e0" + integrity sha512-Y4OZ+ytlatR8AI+8KZfKuL5urKp7qey08ha31L8b3BwewJAoJamTzyvxPR/5D+KkdJCGPq/+8TukHBlY10FX9A== + "@babel/helper-validator-identifier@^7.22.5": version "7.22.5" resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.22.5.tgz#9544ef6a33999343c8740fa51350f30eeaaaf193" integrity sha512-aJXu+6lErq8ltp+JhkJUfk1MTGyuA4v7f3pA+BJ5HLfNC6nAQ0Cpi9uOquUj8Hehg0aUiHzWQbOVJGao6ztBAQ== +"@babel/helper-validator-option@^7.22.15": + version "7.22.15" + resolved "https://registry.yarnpkg.com/@babel/helper-validator-option/-/helper-validator-option-7.22.15.tgz#694c30dfa1d09a6534cdfcafbe56789d36aba040" + integrity sha512-bMn7RmyFjY/mdECUbgn9eoSY4vqvacUnS9i9vGAGttgFWesO6B4CYWA7XlpbWgBt71iv/hfbPlynohStqnu5hA== + "@babel/helper-validator-option@^7.22.5": version "7.22.5" resolved "https://registry.yarnpkg.com/@babel/helper-validator-option/-/helper-validator-option-7.22.5.tgz#de52000a15a177413c8234fa3a8af4ee8102d0ac" @@ -879,6 +934,15 @@ "@babel/helper-plugin-utils" "^7.22.5" "@babel/helper-simple-access" "^7.22.5" +"@babel/plugin-transform-modules-commonjs@^7.22.15": + version "7.22.15" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.22.15.tgz#b11810117ed4ee7691b29bd29fd9f3f98276034f" + integrity sha512-jWL4eh90w0HQOTKP2MoXXUpVxilxsB2Vl4ji69rSjS3EcZ/v4sBmn+A3NpepuJzBhOaEBbR7udonlHHn5DWidg== + dependencies: + "@babel/helper-module-transforms" "^7.22.15" + "@babel/helper-plugin-utils" "^7.22.5" + "@babel/helper-simple-access" "^7.22.5" + "@babel/plugin-transform-modules-systemjs@^7.22.5": version "7.22.11" resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-7.22.11.tgz#3386be5875d316493b517207e8f1931d93154bb1" @@ -928,6 +992,13 @@ "@babel/helper-plugin-utils" "^7.22.5" "@babel/plugin-syntax-numeric-separator" "^7.10.4" +"@babel/plugin-transform-object-assign@^7.16.7": + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-object-assign/-/plugin-transform-object-assign-7.22.5.tgz#290c1b9555dcea48bb2c29ad94237777600d04f9" + integrity sha512-iDhx9ARkXq4vhZ2CYOSnQXkmxkDgosLi3J8Z17mKz7LyzthtkdVchLD7WZ3aXeCuvJDOW3+1I5TpJmwIbF9MKQ== + dependencies: + "@babel/helper-plugin-utils" "^7.22.5" + "@babel/plugin-transform-object-rest-spread@^7.22.5": version "7.22.11" resolved "https://registry.yarnpkg.com/@babel/plugin-transform-object-rest-spread/-/plugin-transform-object-rest-spread-7.22.11.tgz#dbbb06ce783cd994a8f430d8cefa553e9b42ca62" @@ -1116,6 +1187,16 @@ "@babel/helper-plugin-utils" "^7.22.5" "@babel/plugin-syntax-typescript" "^7.22.5" +"@babel/plugin-transform-typescript@^7.22.15": + version "7.22.15" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-typescript/-/plugin-transform-typescript-7.22.15.tgz#15adef906451d86349eb4b8764865c960eb54127" + integrity sha512-1uirS0TnijxvQLnlv5wQBwOX3E1wCFX7ITv+9pBV2wKEk4K+M5tqDaoNXnTH8tjEIYHLO98MwiTWO04Ggz4XuA== + dependencies: + "@babel/helper-annotate-as-pure" "^7.22.5" + "@babel/helper-create-class-features-plugin" "^7.22.15" + "@babel/helper-plugin-utils" "^7.22.5" + "@babel/plugin-syntax-typescript" "^7.22.5" + "@babel/plugin-transform-unicode-escapes@^7.22.10": version "7.22.10" resolved "https://registry.yarnpkg.com/@babel/plugin-transform-unicode-escapes/-/plugin-transform-unicode-escapes-7.22.10.tgz#c723f380f40a2b2f57a62df24c9005834c8616d9" @@ -1274,6 +1355,17 @@ "@babel/plugin-transform-modules-commonjs" "^7.22.11" "@babel/plugin-transform-typescript" "^7.22.11" +"@babel/preset-typescript@^7.16.7": + version "7.22.15" + resolved "https://registry.yarnpkg.com/@babel/preset-typescript/-/preset-typescript-7.22.15.tgz#43db30516fae1d417d748105a0bc95f637239d48" + integrity sha512-HblhNmh6yM+cU4VwbBRpxFhxsTdfS1zsvH9W+gEjD0ARV9+8B4sNfpI6GuhePti84nuvhiwKS539jKPFHskA9A== + dependencies: + "@babel/helper-plugin-utils" "^7.22.5" + "@babel/helper-validator-option" "^7.22.15" + "@babel/plugin-syntax-jsx" "^7.22.5" + "@babel/plugin-transform-modules-commonjs" "^7.22.15" + "@babel/plugin-transform-typescript" "^7.22.15" + "@babel/register@^7.12.1", "@babel/register@^7.13.16": version "7.22.5" resolved "https://registry.yarnpkg.com/@babel/register/-/register-7.22.5.tgz#e4d8d0f615ea3233a27b5c6ada6750ee59559939" @@ -1347,6 +1439,15 @@ "@babel/helper-validator-identifier" "^7.22.5" to-fast-properties "^2.0.0" +"@babel/types@^7.22.15": + version "7.22.19" + resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.22.19.tgz#7425343253556916e440e662bb221a93ddb75684" + integrity sha512-P7LAw/LbojPzkgp5oznjE6tQEIWbp4PkkfrZDINTro9zgBRtI324/EYsiSI7lhPbpIQ+DCeR2NNmMWANGGfZsg== + dependencies: + "@babel/helper-string-parser" "^7.22.5" + "@babel/helper-validator-identifier" "^7.22.19" + to-fast-properties "^2.0.0" + "@base2/pretty-print-object@1.0.1": version "1.0.1" resolved "https://registry.yarnpkg.com/@base2/pretty-print-object/-/pretty-print-object-1.0.1.tgz#371ba8be66d556812dc7fb169ebc3c08378f69d4" @@ -1743,6 +1844,13 @@ resolved "https://registry.yarnpkg.com/@discoveryjs/json-ext/-/json-ext-0.5.7.tgz#1d572bfbbe14b7704e0ba0f39b74815b84870d70" integrity sha512-dBVuXR082gk3jsFp7Rd/JI4kytwGHecnCoTtXFb7DB6CNHp4rg5k1bhg0nWdLGLnOV71lmDzGQaLMy8iPLY0pw== +"@egjs/hammerjs@^2.0.17": + version "2.0.17" + resolved "https://registry.yarnpkg.com/@egjs/hammerjs/-/hammerjs-2.0.17.tgz#5dc02af75a6a06e4c2db0202cae38c9263895124" + integrity sha512-XQsZgjm2EcVUiZQf11UBJQfmZeEmOW8DpI1gsFeln6w0ae0ii4dMQEQ0kjl6DspdWX1aGY1/loyXnP0JS06e/A== + dependencies: + "@types/hammerjs" "^2.0.36" + "@emotion/babel-plugin@^11.11.0": version "11.11.0" resolved "https://registry.yarnpkg.com/@emotion/babel-plugin/-/babel-plugin-11.11.0.tgz#c2d872b6a7767a9d176d007f5b31f7d504bb5d6c" @@ -2519,14 +2627,6 @@ inline-style-prefixer "^6.0.1" normalize-css-color "^1.0.2" -"@gluestack-style/react@^0.2.11-alpha.0", "@gluestack-style/react@^0.2.16", "@gluestack-style/react@^0.2.21": - version "0.2.47" - resolved "https://registry.yarnpkg.com/@gluestack-style/react/-/react-0.2.47.tgz#af219ad64a651cd5008ac3bfb90b0b1baacf23de" - integrity sha512-XewdaACOpEPSiyWGwil/qWQIbiKdIhWWs/icf1NSg3psouY43FpEpHq9ARGajtxueEACsnYM9nlhfo5Vy1ZfvQ== - dependencies: - inline-style-prefixer "^6.0.1" - normalize-css-color "^1.0.2" - "@gluestack-ui/actionsheet@^0.2.16", "@gluestack-ui/actionsheet@^0.2.7", "@gluestack-ui/actionsheet@latest": version "0.2.16" resolved "https://registry.yarnpkg.com/@gluestack-ui/actionsheet/-/actionsheet-0.2.16.tgz#052a733966c517450a3cd6f832932ccf77924867" @@ -6135,6 +6235,11 @@ dependencies: "@types/node" "*" +"@types/hammerjs@^2.0.36": + version "2.0.42" + resolved "https://registry.yarnpkg.com/@types/hammerjs/-/hammerjs-2.0.42.tgz#d7a53edbc51b2c13a9a759c45d7b5e61243d7dba" + integrity sha512-Xxk14BrwHnGi0xlURPRb+Y0UNn2w3cTkeFm7pKMsYOaNgH/kabbJLhcBoNIodwsbTz7Z8KcWjtDvlGH0nc0U9w== + "@types/hast@^2.0.0": version "2.3.5" resolved "https://registry.yarnpkg.com/@types/hast/-/hast-2.3.5.tgz#08caac88b44d0fdd04dc17a19142355f43bd8a7a" @@ -6160,6 +6265,11 @@ resolved "https://registry.yarnpkg.com/@types/http-cache-semantics/-/http-cache-semantics-4.0.1.tgz#0ea7b61496902b95890dc4c3a116b60cb8dae812" integrity sha512-SZs7ekbP8CN0txVG2xVRH6EgKmEm31BOxA07vkFaETzZz1xh+cbt8BcI0slpymvwhx5dlFnQG2rTlPVQn+iRPQ== +"@types/invariant@^2.2.35": + version "2.2.35" + resolved "https://registry.yarnpkg.com/@types/invariant/-/invariant-2.2.35.tgz#cd3ebf581a6557452735688d8daba6cf0bd5a3be" + integrity sha512-DxX1V9P8zdJPYQat1gHyY0xj3efl8gnMVjiM9iCY6y27lj+PoQWkgjt8jDqmovPqULkKVpKRg8J36iQiA+EtEg== + "@types/is-ci@^3.0.0": version "3.0.0" resolved "https://registry.yarnpkg.com/@types/is-ci/-/is-ci-3.0.0.tgz#7e8910af6857601315592436f030aaa3ed9783c3" @@ -18263,6 +18373,17 @@ react-native-codegen@^0.70.6: jscodeshift "^0.13.1" nullthrows "^1.1.1" +react-native-gesture-handler@^2.13.1: + version "2.13.1" + resolved "https://registry.yarnpkg.com/react-native-gesture-handler/-/react-native-gesture-handler-2.13.1.tgz#bad89caacd62c4560b9953b02f85f37ee42d5d4c" + integrity sha512-hW454X7sjuiBN+lobqw63pmT3boAmTl5OP6zQLq83iEe4T6PcHZ9lxzgCrebtgmutY8cJfq9rM2dOUVh9WBcww== + dependencies: + "@egjs/hammerjs" "^2.0.17" + hoist-non-react-statics "^3.3.0" + invariant "^2.2.4" + lodash "^4.17.21" + prop-types "^15.7.2" + react-native-gradle-plugin@^0.70.3: version "0.70.3" resolved "https://registry.yarnpkg.com/react-native-gradle-plugin/-/react-native-gradle-plugin-0.70.3.tgz#cbcf0619cbfbddaa9128701aa2d7b4145f9c4fc8" @@ -18282,6 +18403,19 @@ react-native-modal-selector@^2.1.1: dependencies: prop-types "^15.5.10" +react-native-reanimated@~2.12.0: + version "2.12.0" + resolved "https://registry.yarnpkg.com/react-native-reanimated/-/react-native-reanimated-2.12.0.tgz#5821eecfb1769b1617a67a2d4dec12fdeedb2b6e" + integrity sha512-nrlPyw+Hx9u4iJhZk9PoTvDo/QmVAd+bo7OK9Tv3hveNEF9++5oig/g3Uv9V93shy9avTYGsUprUvAEt/xdzeQ== + dependencies: + "@babel/plugin-transform-object-assign" "^7.16.7" + "@babel/preset-typescript" "^7.16.7" + "@types/invariant" "^2.2.35" + invariant "^2.2.4" + lodash.isequal "^4.5.0" + setimmediate "^1.0.5" + string-hash-64 "^1.0.3" + react-native-safe-area-context@4.4.1: version "4.4.1" resolved "https://registry.yarnpkg.com/react-native-safe-area-context/-/react-native-safe-area-context-4.4.1.tgz#239c60b8a9a80eac70a38a822b04c0f1d15ffc01" @@ -19943,6 +20077,11 @@ string-argv@0.3.2: resolved "https://registry.yarnpkg.com/string-argv/-/string-argv-0.3.2.tgz#2b6d0ef24b656274d957d54e0a4bbf6153dc02b6" integrity sha512-aqD2Q0144Z+/RqG52NeHEkZauTAUWJO8c6yTftGJKO3Tja5tUgIfmIl6kExvhtxSDP7fXB6DvzkfMpCd/F3G+Q== +string-hash-64@^1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/string-hash-64/-/string-hash-64-1.0.3.tgz#0deb56df58678640db5c479ccbbb597aaa0de322" + integrity sha512-D5OKWKvDhyVWWn2x5Y9b+37NUllks34q1dCDhk/vYcso9fmhs+Tl3KR/gE4v5UNj2UA35cnX4KdVVGkG1deKqw== + string-hash@1.1.3: version "1.1.3" resolved "https://registry.yarnpkg.com/string-hash/-/string-hash-1.1.3.tgz#e8aafc0ac1855b4666929ed7dd1275df5d6c811b" From 458648913691d3a6760258a43b2570d7bf149377 Mon Sep 17 00:00:00 2001 From: Rohit Singh Date: Thu, 21 Sep 2023 12:42:28 +0530 Subject: [PATCH 3/7] feat: animation plugin driver --- example/storybook/babel.config.js | 8 +- example/storybook/src/components/nb.config.ts | 3 +- .../AnimationPlugin/AnimationPlugin.tsx | 4 +- example/storybook/tsconfig.json | 3 + packages/animation-resolver/.gitignore | 28 ++ packages/animation-resolver/.nvmrc | 1 + packages/animation-resolver/CHANGELOG.md | 17 + packages/animation-resolver/README.md | 69 ++++ packages/animation-resolver/babel.config.js | 22 ++ packages/animation-resolver/package.json | 52 +++ .../src/AnimatedComponents/index.tsx} | 2 +- packages/animation-resolver/src/index.tsx | 361 ++++++++++++++++++ .../src/propertyTokenMap.ts | 183 +++++++++ packages/animation-resolver/src/utils.ts | 231 +++++++++++ packages/animation-resolver/tsconfig.json | 32 ++ packages/react/src/createStyled.ts | 4 +- packages/react/src/plugins/index.tsx | 1 - 17 files changed, 1010 insertions(+), 11 deletions(-) create mode 100644 packages/animation-resolver/.gitignore create mode 100644 packages/animation-resolver/.nvmrc create mode 100644 packages/animation-resolver/CHANGELOG.md create mode 100644 packages/animation-resolver/README.md create mode 100644 packages/animation-resolver/babel.config.js create mode 100644 packages/animation-resolver/package.json rename packages/{react/src/plugins/animation-components.tsx => animation-resolver/src/AnimatedComponents/index.tsx} (98%) create mode 100644 packages/animation-resolver/src/index.tsx create mode 100644 packages/animation-resolver/src/propertyTokenMap.ts create mode 100644 packages/animation-resolver/src/utils.ts create mode 100644 packages/animation-resolver/tsconfig.json diff --git a/example/storybook/babel.config.js b/example/storybook/babel.config.js index 461ef719f..4c6947bc3 100644 --- a/example/storybook/babel.config.js +++ b/example/storybook/babel.config.js @@ -24,10 +24,10 @@ module.exports = function (api) { __dirname, '../../packages/animation-moti-driver/src' ), - // ['@gluestack-style/animation-plugin']: path.join( - // __dirname, - // '../../packages/animation-plugin/src' - // ), + ['@gluestack-style/animation-resolver']: path.join( + __dirname, + '../../packages/animation-resolver/src' + ), // ['@dank-style/react']: path.join( // __dirname, // '../../packages/react/src' diff --git a/example/storybook/src/components/nb.config.ts b/example/storybook/src/components/nb.config.ts index e325c5645..f372ceb17 100644 --- a/example/storybook/src/components/nb.config.ts +++ b/example/storybook/src/components/nb.config.ts @@ -1,6 +1,7 @@ +import { AnimationResolver } from '@gluestack-style/animation-resolver'; import { MotionAnimationDriver } from '@gluestack-style/legend-motion-driver'; // import { MotiAnimationDriver } from '@gluestack-style/moti-driver'; -import { createConfig, AnimationResolver } from '@gluestack-style/react'; +import { createConfig } from '@gluestack-style/react'; export const config = createConfig({ aliases: { diff --git a/example/storybook/src/plugins/AnimationPlugin/AnimationPlugin.tsx b/example/storybook/src/plugins/AnimationPlugin/AnimationPlugin.tsx index ed91f0187..73be6ad2e 100644 --- a/example/storybook/src/plugins/AnimationPlugin/AnimationPlugin.tsx +++ b/example/storybook/src/plugins/AnimationPlugin/AnimationPlugin.tsx @@ -1,11 +1,11 @@ import React from 'react'; import { Wrapper } from '../../components/Wrapper'; import { Pressable, View, Text } from 'react-native'; +import { styled } from '@gluestack-style/react'; import { - styled, AnimatedImage, AnimatedAnimatePresence, -} from '@gluestack-style/react'; +} from '@gluestack-style/animation-resolver'; const images = [require('./1.png'), require('./2.png'), require('./3.png')]; const Box = styled(View, {}); diff --git a/example/storybook/tsconfig.json b/example/storybook/tsconfig.json index f0e2e0bfc..c929feb75 100644 --- a/example/storybook/tsconfig.json +++ b/example/storybook/tsconfig.json @@ -10,6 +10,9 @@ "@gluestack-style/moti-driver": [ "../../packages/animation-moti-driver/src" ], + "@gluestack-style/animation-resolver": [ + "../../packages/animation-resolver/src" + ], "react-native": ["./node_modules/react-native-web"] }, "emitDeclarationOnly": true, diff --git a/packages/animation-resolver/.gitignore b/packages/animation-resolver/.gitignore new file mode 100644 index 000000000..3d3e2719a --- /dev/null +++ b/packages/animation-resolver/.gitignore @@ -0,0 +1,28 @@ +# See https://help.github.com/articles/ignoring-files/ for more about ignoring files. + +# dependencies +node_modules +.pnp +.pnp.js + +# misc +.DS_Store +*.pem + +# build +dist +lib + +# debug +npm-debug.log* +yarn-debug.log* +yarn-error.log* + +# local env files +.env.local +.env.development.local +.env.test.local +.env.production.local + +# turbo +.turbo diff --git a/packages/animation-resolver/.nvmrc b/packages/animation-resolver/.nvmrc new file mode 100644 index 000000000..5dbac1ed0 --- /dev/null +++ b/packages/animation-resolver/.nvmrc @@ -0,0 +1 @@ +v16.13.0 \ No newline at end of file diff --git a/packages/animation-resolver/CHANGELOG.md b/packages/animation-resolver/CHANGELOG.md new file mode 100644 index 000000000..41be139f9 --- /dev/null +++ b/packages/animation-resolver/CHANGELOG.md @@ -0,0 +1,17 @@ +# @gluestack-style/animation-plugin + +## 0.1.7 + +### Patch Changes + +- - Fixed transform array resoltion + +## 0.1.0 + +### Changes + +- Fix forward ref warning +- Fixed variant resolution +- Moved @gluestack-style/react to devDependency +- Add legend motion dependency +- Add variant support diff --git a/packages/animation-resolver/README.md b/packages/animation-resolver/README.md new file mode 100644 index 000000000..58e26e535 --- /dev/null +++ b/packages/animation-resolver/README.md @@ -0,0 +1,69 @@ +# @gluestack-style/animation-plugin + +## Installation + +To use `@gluestack-style/animation-plugin`, all you need to do is install the +`@gluestack-style/animation-plugin` package: + +```sh +$ yarn add @gluestack-style/animation-plugin + +# or + +$ npm i @gluestack-style/animation-plugin +``` + +## Usage + +You can initialize the Animation plugin by creating a new instance of the AnimationResolver class and providing it as an argument to the createStyled function. The `AnimationResolver` takes an optional `styledUtils` object that maps the styled utils object. Here's an example: + +```jsx +import { createStyled } from '@gluestack-style/react'; +import { AnimationResolver } from '@gluestack-style/animation-plugin'; + +const styled = createStyled([ + new AnimationResolver({ + aliases: { + ':initial': 'initial', + ':animate': 'animate', + ':exit': 'exit', + }, + }), +]); +``` + +In this example, we are creating a new instance of the AnimationResolver class, passing an object with the 'aliases' property as an argument. The aliases object maps the aliases :initial, :animate, and :exit to their corresponding animation props. + +## Example of creating a styled component: + +Once the plugin is initialized, you can use the styled function to create styled components with animation props. Here's an example: + +```jsx +const Box = styled(Motion.View, { + ':initial': { opacity: 0 }, + ':animate': { opacity: 1 }, + ':exit': { opacity: 0 }, +}); +``` + +The final internal styled object that will be resolved is: + +```jsx +styledObject = { + 'props': { + initial: { + opacity: 0, + }, + animate: { + opacity: 1, + }, + exit: { + opacity: 0, + }, + } + }, +}; +``` + +More guides on how to get started are available +[here](https://style.gluestack.io/). diff --git a/packages/animation-resolver/babel.config.js b/packages/animation-resolver/babel.config.js new file mode 100644 index 000000000..fda985a22 --- /dev/null +++ b/packages/animation-resolver/babel.config.js @@ -0,0 +1,22 @@ +module.exports = function (api) { + api.cache(true); + return { + presets: ['babel-preset-expo'], + plugins: [ + process.env.NODE_ENV !== 'production' + ? [ + 'module-resolver', + { + alias: { + // For development, we want to alias the library to the source + ['@gluestack-style/react']: path.join( + __dirname, + '../react/src/index' + ), + }, + }, + ] + : ['transform-remove-console'], + ], + }; +}; diff --git a/packages/animation-resolver/package.json b/packages/animation-resolver/package.json new file mode 100644 index 000000000..bc672469f --- /dev/null +++ b/packages/animation-resolver/package.json @@ -0,0 +1,52 @@ +{ + "name": "@gluestack-style/animation-resolver", + "version": "0.1.11", + "description": "A gluestack-style plugin for resolving animation properties, utilizing animation libraries.", + "keywords": [ + "react", + "native", + "react-native", + "animation", + "transition" + ], + "main": "lib/commonjs/index", + "types": "lib/typescript/index.d.ts", + "module": "lib/module/index", + "react-native": "src/index", + "source": "src/index", + "typings": "lib/typescript/index.d.ts", + "scripts": { + "prepare": "bob build", + "release": "release-it", + "build": "bob build", + "clean": "rm -rf lib" + }, + "dependencies": {}, + "devDependencies": { + "@gluestack-style/react": "^0.1.11", + "@types/react": "^18.0.22", + "@types/react-native": "^0.69.15", + "babel-plugin-transform-remove-console": "^6.9.4", + "react": "^18.1.0", + "react-dom": "^18.1.0", + "react-native": "^0.70.3", + "react-native-builder-bob": "^0.20.1", + "tsconfig": "*", + "typescript": "^4.7.4" + }, + "react-native-builder-bob": { + "source": "src", + "output": "lib", + "targets": [ + "commonjs", + [ + "module" + ], + "typescript" + ] + }, + "files": [ + "lib/", + "src/" + ] +} diff --git a/packages/react/src/plugins/animation-components.tsx b/packages/animation-resolver/src/AnimatedComponents/index.tsx similarity index 98% rename from packages/react/src/plugins/animation-components.tsx rename to packages/animation-resolver/src/AnimatedComponents/index.tsx index 712491b3c..729ae9703 100644 --- a/packages/react/src/plugins/animation-components.tsx +++ b/packages/animation-resolver/src/AnimatedComponents/index.tsx @@ -1,5 +1,5 @@ import React from 'react'; -import { useStyled } from '..'; +import { useStyled } from '@gluestack-style/react'; import type { ImageProps, PressableProps, diff --git a/packages/animation-resolver/src/index.tsx b/packages/animation-resolver/src/index.tsx new file mode 100644 index 000000000..d5fe5f3a3 --- /dev/null +++ b/packages/animation-resolver/src/index.tsx @@ -0,0 +1,361 @@ +import React, { useEffect, useMemo } from 'react'; +import type { + // @ts-ignore + IAnimationDriverPlugin, + IStyledPlugin, +} from '@gluestack-style/react'; +import { useStyled } from '@gluestack-style/react'; +import { deepMerge, setObjectKeyValue, resolvedTokenization } from './utils'; +import { propertyTokenMap } from './propertyTokenMap'; + +function tokenizeAnimationPropsFromConfig( + props: any = {}, + config: any, + animationAliases: any, + path: any = [], + tokenizedAnimatedProps: any = {} +) { + for (const prop in props) { + if (Array.isArray(props[prop])) { + path.push(prop); + setObjectKeyValue(tokenizedAnimatedProps, path, props[prop]); + path.pop(); + } else if (animationAliases[prop]) { + path.push(prop); + const tokenizedValue = resolvedTokenization(props[prop], config); + setObjectKeyValue(tokenizedAnimatedProps, path, tokenizedValue); + path.pop(); + } else if (typeof props[prop] === 'object') { + path.push(prop); + const tokenizedValue = resolvedTokenization(props[prop], config); + setObjectKeyValue(tokenizedAnimatedProps, path, tokenizedValue); + // path.pop(); + tokenizeAnimationPropsFromConfig( + props[prop], + config, + animationAliases, + path, + tokenizedAnimatedProps + ); + path.pop(); + } else { + path.push(prop); + setObjectKeyValue(tokenizedAnimatedProps, path, props[prop]); + path.pop(); + } + } + + return tokenizedAnimatedProps; +} + +function getVariantProps(props: any, theme: any) { + const variantTypes = theme?.variants ? Object.keys(theme.variants) : []; + + const restProps = { ...props }; + + const variantProps: any = {}; + variantTypes?.forEach((variant) => { + if (props[variant]) { + variantProps[variant] = props[variant]; + // delete restProps[variant]; + } + }); + + return { + variantProps, + restProps, + }; +} + +function resolveVariantAnimationProps(variantProps: any, styledObject: any) { + let resolvedVariant = {}; + Object.keys(variantProps).forEach((variant) => { + const variantValue = variantProps[variant]; + const variantObject = styledObject?.variants?.[variant]?.[variantValue]; + + resolvedVariant = deepMerge(resolvedVariant, variantObject); + }); + + return resolvedVariant; +} + +export class AnimationResolver implements IStyledPlugin { + name: 'AnimationResolver'; + componentDriver: IAnimationDriverPlugin; + config = { + aliases: { + ':animate': 'animate', + ':initial': 'initial', + ':exit': 'exit', + ':initialProps': 'initialProps', + ':animateProps': 'animateProps', + ':transition': 'transition', + ':transformOrigin': 'transformOrigin', + ':whileTap': 'whileTap', + ':whileHover': 'whileHover', + ':onAnimationComplete': 'onAnimationComplete', + } as const, + tokens: {} as const, + animatedPropMap: {} as any, + }; + + AnimatePresenceComp = React.Fragment; + + register(config: any) { + if (this.config) { + if (config?.aliases) { + this.config.aliases = { + ...this.config?.aliases, + ...config?.aliases, + }; + } + + if (config?.tokens) { + this.config.tokens = { + ...this.config?.tokens, + ...config?.tokens, + }; + } + if (config?.animatedPropMap) { + this.config.animatedPropMap = { + ...this.config?.animatedPropMap, + ...config?.animatedPropMap, + }; + } + // @ts-ignore + this.config.ref = config?.ref; + } + } + + constructor(ComponentDriverClass: any, config: any = {}) { + // @ts-ignore + const componentDriver = new ComponentDriverClass(config); + this.name = 'AnimationResolver'; + this.componentDriver = componentDriver; + if (componentDriver.engine.AnimatePresence) { + this.AnimatePresenceComp = componentDriver.engine.AnimatePresence; + } + this.register(componentDriver.config); + } + + #childrenExitPropsMap: any = {}; + + #extendedConfig: any = {}; + + inputMiddleWare

( + styledObj = {}, + shouldUpdateConfig: any = true, + _?: boolean, + Component?: React.ComponentType + ): { + // @ts-ignore + [key in keyof typeof this.config.aliases]: P[(typeof this.config.aliases)[key]]; + } { + if ( + Component && + (Component.displayName?.startsWith( + 'Gluestack-AnimatedResolver-Animated' + ) || + // @ts-ignore + Component.isAnimatedComponent) + ) { + let AnimatedComponent = + this.componentDriver.engine[ + // @ts-ignore + Component.displayName?.replace( + 'Gluestack-AnimatedResolver-Animated', + '' + ) + ]; + + if (AnimatedComponent) { + AnimatedComponent.isAnimatedComponent = true; + } + if (!AnimatedComponent) { + AnimatedComponent = React.Fragment; + } + + // this.#childrenExitPropsMap = deepClone(styledObj); + const resolvedAnimatedProps = this.updateStyledObject( + styledObj, + shouldUpdateConfig + ); + + const resolvedStyledObjectWithAnimatedProps = deepMerge( + styledObj, + resolvedAnimatedProps + ); + + if (shouldUpdateConfig) { + // @ts-ignore + return [styledObj, shouldUpdateConfig, _, AnimatedComponent]; + } + + // @ts-ignore + return [ + resolvedStyledObjectWithAnimatedProps, + shouldUpdateConfig, + _, + AnimatedComponent, + ]; + } + // @ts-ignore + return [styledObj, shouldUpdateConfig, _, Component]; + } + + updateStyledObject( + styledObject: any = {}, + shouldUpdateConfig: boolean, + resolvedStyledObject: any = {}, + keyPath: string[] = [] + ) { + const aliases = this.config?.aliases; + const animatedPropMap = this.config?.animatedPropMap; + for (const prop in styledObject) { + if (typeof styledObject[prop] === 'object') { + keyPath.push(prop); + this.updateStyledObject( + styledObject[prop], + shouldUpdateConfig, + resolvedStyledObject, + keyPath + ); + keyPath.pop(); + } + + // @ts-ignore + if (aliases && aliases?.[prop]) { + if (shouldUpdateConfig) { + // this.#childrenExitPropsMap[prop] = styledObject[prop]; + setObjectKeyValue( + this.#childrenExitPropsMap, + [...keyPath, prop], + styledObject[prop] + ); + } + const value = styledObject[prop]; + // @ts-ignore + keyPath.push('props', aliases[prop]); + setObjectKeyValue(resolvedStyledObject, keyPath, value); + keyPath.pop(); + keyPath.pop(); + delete styledObject[prop]; + } + + if (animatedPropMap && animatedPropMap[prop]) { + this.renameObjectKey(styledObject, prop, animatedPropMap[prop]); + } + } + return resolvedStyledObject; + } + + renameObjectKey(obj: any, from: string, to: string) { + obj[to] = obj[from]; + delete obj[from]; + return obj; + } + + componentMiddleWare({ Component, ExtendedConfig }: any) { + if (Component && Component.isAnimatedComponent) { + const styledConfig = this.#childrenExitPropsMap; + + this.#childrenExitPropsMap = {}; + + const NewComponent = React.forwardRef((props: any, ref?: any) => { + const { sx, ...rest } = props; + + const styledContext = useStyled(); + useEffect(() => { + if (!styledContext.animationDriverData) { + styledContext.setAnimationDriverData(this.componentDriver); + } + }, [styledContext]); + const CONFIG = useMemo( + () => ({ + ...styledContext.config, + propertyTokenMap, + }), + [styledContext.config] + ); + this.#extendedConfig = CONFIG; + if (ExtendedConfig) { + this.#extendedConfig = deepMerge(CONFIG, ExtendedConfig); + } + + let tokenizedAnimatedProps: any = {}; + const { variantProps, restProps } = getVariantProps(rest, styledConfig); + const variantStyledObject = resolveVariantAnimationProps( + variantProps, + styledConfig + ); + const componentStyledObject = deepMerge( + variantStyledObject, + styledConfig + ); + + const animationAliases = this.config?.aliases; + + const config = this.#extendedConfig; + + tokenizedAnimatedProps = tokenizeAnimationPropsFromConfig( + componentStyledObject, + config, + animationAliases + ); + + const tokenizedSxAnimationProps: any = tokenizeAnimationPropsFromConfig( + sx, + config, + animationAliases + ); + + const mergedAnimatedProps = deepMerge( + tokenizedAnimatedProps, + tokenizedSxAnimationProps + ); + + // @ts-ignore + const [resolvedAnimatedStyledWithStyledObject, , ,] = + this.inputMiddleWare(mergedAnimatedProps, false, false, Component); + let isState = false; + + Object.keys(restProps?.states ?? {}).forEach((state: any) => { + isState = restProps.states[state] ? true : false; + }); + const animatedProps = !isState + ? // @ts-ignore + resolvedAnimatedStyledWithStyledObject?.props + : {}; + return ( + + ); + }); + + if (NewComponent) { + //@ts-ignore + NewComponent.styled = {}; + //@ts-ignore + NewComponent.styled.config = {}; + //@ts-ignore + NewComponent.styled.config = styledConfig; + + //@ts-ignore + NewComponent.isStyledComponent = Component?.isStyledComponent; + //@ts-ignore + NewComponent.isComposedComponent = Component?.isComposedComponent; + + NewComponent.displayName = 'StyledComponent'; + return NewComponent; + } + } else { + return Component; + } + } +} + +export * from './AnimatedComponents'; diff --git a/packages/animation-resolver/src/propertyTokenMap.ts b/packages/animation-resolver/src/propertyTokenMap.ts new file mode 100644 index 000000000..0913ca47d --- /dev/null +++ b/packages/animation-resolver/src/propertyTokenMap.ts @@ -0,0 +1,183 @@ +const borderStyles = 'borderStyles'; +const borderWidths = 'borderWidths'; +const colors = 'colors'; +const mediaQueries = 'mediaQueries'; +const opacity = 'opacity'; +const fonts = 'fonts'; +const fontSizes = 'fontSizes'; +const fontWeights = 'fontWeights'; +const letterSpacings = 'letterSpacings'; +const lineHeights = 'lineHeights'; +const radii = 'radii'; +const shadows = 'shadows'; +// const sizes = 'sizes'; +const space = 'space'; +const transitions = 'transitions'; +const zIndices = 'zIndices'; +export const propertyTokenMap = { + gap: space, + gridGap: space, + columnGap: space, + gridColumnGap: space, + rowGap: space, + gridRowGap: space, + inset: space, + insetBlock: space, + insetBlockEnd: space, + insetBlockStart: space, + insetInline: space, + insetInlineEnd: space, + insetInlineStart: space, + margin: space, + marginTop: space, + marginRight: space, + marginBottom: space, + marginLeft: space, + marginBlock: space, + marginBlockEnd: space, + marginBlockStart: space, + marginInline: space, + marginInlineEnd: space, + marginInlineStart: space, + + marginHorizontal: space, + marginVertical: space, + padding: space, + paddingTop: space, + paddingRight: space, + paddingBottom: space, + paddingLeft: space, + + paddingBlock: space, + paddingBlockEnd: space, + paddingBlockStart: space, + paddingInline: space, + paddingInlineEnd: space, + paddingInlineStart: space, + + paddingHorizontal: space, + paddingVertical: space, + paddingStart: space, + paddingEnd: space, + + top: space, + right: space, + bottom: space, + left: space, + scrollMargin: space, + scrollMarginTop: space, + scrollMarginRight: space, + scrollMarginBottom: space, + scrollMarginLeft: space, + scrollMarginX: space, + scrollMarginY: space, + scrollMarginBlock: space, + scrollMarginBlockEnd: space, + scrollMarginBlockStart: space, + scrollMarginInline: space, + scrollMarginInlineEnd: space, + scrollMarginInlineStart: space, + scrollPadding: space, + scrollPaddingTop: space, + scrollPaddingRight: space, + scrollPaddingBottom: space, + scrollPaddingLeft: space, + scrollPaddingX: space, + scrollPaddingY: space, + scrollPaddingBlock: space, + scrollPaddingBlockEnd: space, + scrollPaddingBlockStart: space, + scrollPaddingInline: space, + scrollPaddingInlineEnd: space, + scrollPaddingInlineStart: space, + // shadowOffset: space, + shadowRadius: space, + elevation: space, + + fontSize: fontSizes, + + background: colors, + backgroundColor: colors, + backgroundImage: colors, + borderImage: colors, + border: colors, + borderBlock: colors, + borderBlockEnd: colors, + borderBlockStart: colors, + borderBottom: colors, + borderBottomColor: colors, + borderColor: colors, + borderInline: colors, + borderInlineEnd: colors, + borderInlineStart: colors, + borderLeft: colors, + borderLeftColor: colors, + borderRight: colors, + borderRightColor: colors, + borderTop: colors, + borderTopColor: colors, + caretColor: colors, + color: colors, + columnRuleColor: colors, + fill: colors, + outline: colors, + outlineColor: colors, + stroke: colors, + textDecorationColor: colors, + shadowColor: colors, + + shadowOpacity: opacity, + + shadow: shadows, + // Media Query + condition: mediaQueries, + + fontFamily: fonts, + + fontWeight: fontWeights, + + lineHeight: lineHeights, + + letterSpacing: letterSpacings, + + blockSize: space, + minBlockSize: space, + maxBlockSize: space, + inlineSize: space, + minInlineSize: space, + maxInlineSize: space, + width: space, + minWidth: space, + maxWidth: space, + height: space, + minHeight: space, + maxHeight: space, + flexBasis: space, + gridTemplateColumns: space, + gridTemplateRows: space, + + borderWidth: borderWidths, + borderTopWidth: borderWidths, + borderRightWidth: borderWidths, + borderBottomWidth: borderWidths, + borderLeftWidth: borderWidths, + + borderStyle: borderStyles, + borderTopStyle: borderStyles, + borderRightStyle: borderStyles, + borderBottomStyle: borderStyles, + borderLeftStyle: borderStyles, + + borderRadius: radii, + borderTopLeftRadius: radii, + borderTopRightRadius: radii, + borderBottomRightRadius: radii, + borderBottomLeftRadius: radii, + + boxShadow: colors, + textShadow: shadows, + + transition: transitions, + + zIndex: zIndices, +} as const; diff --git a/packages/animation-resolver/src/utils.ts b/packages/animation-resolver/src/utils.ts new file mode 100644 index 000000000..1c4be87d1 --- /dev/null +++ b/packages/animation-resolver/src/utils.ts @@ -0,0 +1,231 @@ +export const deepClone = (obj: any) => JSON.parse(JSON.stringify(obj)); + +export const deepMerge = (target: any = {}, source: any) => { + for (const key in source) { + if (source.hasOwnProperty(key)) { + if (typeof target[key] === 'object' && typeof source[key] === 'object') { + deepMerge(target[key], source[key]); + } else { + target[key] = source[key]; + } + } + } + return target; +}; + +export const setObjectKeyValue = (obj: any, keys: any, value: any) => { + let current = obj; + for (let i = 0; i < keys.length; i++) { + const key = keys[i]; + if (i === keys.length - 1) { + // we've reached the desired key, so update its value + current[key] = value; + } else { + // we're still traversing the object, so create the key if it doesn't exist + if (!current[key]) { + current[key] = {}; + } + current = current[key]; + } + } + return obj; +}; + +export function deepMergeObjects(...objects: any) { + const isObject = (obj: any) => obj && typeof obj === 'object'; + + return objects.reduce((prev: any, obj: any) => { + if (isObject(prev) && isObject(obj)) { + Object.keys(obj).forEach((key) => { + if (isObject(obj[key])) { + if (!prev[key] || !isObject(prev[key])) { + prev[key] = {}; + } + prev[key] = deepMerge(prev[key], obj[key]); + } else { + prev[key] = obj[key]; + } + }); + } + return prev; + }, {}); +} + +export function resolvedTokenization(props: any, config: any) { + const aliasedResolvedProps = resolveAliasesFromConfig(config, props); + const newProps = resolveTokensFromConfig(config, aliasedResolvedProps); + return newProps; +} + +export function resolveAliasesFromConfig(config: any, props: any) { + const aliasResolvedProps: any = {}; + + Object.keys(props).map((key) => { + if (config?.aliases?.[key]) { + aliasResolvedProps[config.aliases?.[key]] = props[key]; + } else { + aliasResolvedProps[key] = props[key]; + } + }); + return aliasResolvedProps; +} + +export function resolveTokensFromConfig(config: any, props: any) { + let newProps: any = {}; + + Object.keys(props).map((prop: any) => { + const value = props[prop]; + + newProps[prop] = getResolvedTokenValueFromConfig( + config, + props, + prop, + value + ); + }); + // console.log('&&&&&', newProps); + + return newProps; +} + +export function getResolvedTokenValueFromConfig( + config: any, + _props: any, + prop: any, + value: any +) { + let resolvedTokenValue = getTokenFromConfig(config, prop, value); + + // Special case for token ends with em on mobile + // This will work for lineHeight and letterSpacing + // console.log('hello from token ends with em on mobile', resolvedTokenValue); + // if ( + // typeof resolvedTokenValue === 'string' && + // resolvedTokenValue.endsWith('em') && + // Platform.OS !== 'web' + // ) { + // const fontSize = getTokenFromConfig(config, 'fontSize', props?.fontSize); + // resolvedTokenValue = + // parseFloat(resolvedTokenValue) * parseFloat(fontSize ?? BASE_FONT_SIZE); + // } + + return resolvedTokenValue; +} + +export const getTokenFromConfig = (config: any, prop: any, value: any) => { + const aliasTokenType = config.propertyTokenMap[prop]; + + // const tokenScale = config?.tokens?.[aliasTokenType]; + let token; + + // resolveStringToken(value, config, config.propertyTokenMap); + if (typeof value === 'string' && value.includes('$')) { + if (config.propertyResolver?.[prop]) { + let transformer = config.propertyResolver?.[prop]; + token = transformer(value, (value1: any, scale = aliasTokenType) => + resolveStringToken(value1, config, config.propertyTokenMap, prop, scale) + ); + } else { + token = resolveStringToken(value, config, config.propertyTokenMap, prop); + } + } else { + if (config.propertyResolver?.[prop]) { + let transformer = config.propertyResolver?.[prop]; + token = transformer(value, (value: any, scale = aliasTokenType) => { + if (typeof value === 'string' && value.includes('$')) { + return resolveStringToken( + value, + config, + config.propertyTokenMap, + prop, + scale + ); + } else { + return value; + } + }); + } else { + token = value; + } + // console.log(token, typeof token, prop, '******'); + } + + return token; +}; + +function isNumeric(str: string) { + return typeof str === 'number' ? true : false; + // return /^[-+]?[0-9]*\.?[0-9]+$/.test(str); +} +export function resolveStringToken( + string: string, + config: any, + tokenScaleMap: any, + propName: any, + scale?: any +) { + let typeofResult = 'string'; + const token_scale = scale ?? tokenScaleMap[propName]; + + const splitTokenBySpace = string.split(' '); + + const result: any = splitTokenBySpace.map((currentToken) => { + let splitCurrentToken = currentToken.split('$'); + + if (currentToken.startsWith('$')) { + splitCurrentToken = splitCurrentToken.slice(1); + } + + if (splitCurrentToken.length > 1) { + const tokenValue = getObjectProperty(config.tokens, splitCurrentToken); + typeofResult = typeof tokenValue; + return tokenValue; + } else { + if (tokenScaleMap[propName]) { + if (!config || !config.tokens) { + throw new Error( + 'You cannot use tokens without wrapping the component with StyledProvider. Please wrap the component with a StyledProvider and pass theme config.' + ); + } + if ( + config?.tokens[token_scale] && + config?.tokens[token_scale].hasOwnProperty(splitCurrentToken[0]) + ) { + const tokenValue = config?.tokens[token_scale][splitCurrentToken[0]]; + typeofResult = typeof tokenValue; + + if (typeof tokenValue !== 'undefined' && tokenValue !== null) { + return tokenValue; + } else { + return ''; + } + } + } + return splitCurrentToken[splitCurrentToken.length - 1]; + } + }); + + let finalResult = result; + + if (finalResult === '') { + return undefined; + } else { + finalResult = result.join(' '); + + if (isNumeric(finalResult) || typeofResult === 'number') { + return parseFloat(finalResult); + } else { + return finalResult; + } + } +} + +export const getObjectProperty = (object: any, keyPath: any) => { + if (!Array.isArray(keyPath)) { + keyPath = [keyPath]; + } + return keyPath.reduce( + (baseObj: any, key: any) => baseObj && baseObj[key], + object + ); +}; diff --git a/packages/animation-resolver/tsconfig.json b/packages/animation-resolver/tsconfig.json new file mode 100644 index 000000000..7ebd787f0 --- /dev/null +++ b/packages/animation-resolver/tsconfig.json @@ -0,0 +1,32 @@ +{ + "include": ["./src"], + "exclude": ["node_modules", "example"], + "path": { + "@gluestack-style/react": ["../react/src"] + }, + "compilerOptions": { + "emitDeclarationOnly": true, + "noEmit": false, + "baseUrl": ".", + "declaration": true, + "allowUnreachableCode": false, + "allowUnusedLabels": true, + "esModuleInterop": true, + "importsNotUsedAsValues": "error", + "forceConsistentCasingInFileNames": true, + "jsx": "react", + "lib": ["esnext", "dom"], + "module": "esnext", + "moduleResolution": "node", + "noFallthroughCasesInSwitch": true, + "noImplicitReturns": true, + "noImplicitUseStrict": false, + "noStrictGenericChecks": false, + "noUnusedLocals": false, + "noUnusedParameters": true, + "resolveJsonModule": true, + "skipLibCheck": true, + "strict": true, + "target": "esnext" + } +} diff --git a/packages/react/src/createStyled.ts b/packages/react/src/createStyled.ts index ee2d1652e..0dcd72cd3 100644 --- a/packages/react/src/createStyled.ts +++ b/packages/react/src/createStyled.ts @@ -2,9 +2,9 @@ import { styled } from './styled'; import type { IComponentStyleConfig, ITheme } from './types'; export const createStyled = (plugins: any) => { - let styledComponent = ( + let styledComponent = ( Component: React.ComponentType

, - styledObject: ITheme, + styledObject: ITheme, compConfig: IComponentStyleConfig = {}, extendedConfig: any = {} ) => { diff --git a/packages/react/src/plugins/index.tsx b/packages/react/src/plugins/index.tsx index 871feaa62..391b23e1d 100644 --- a/packages/react/src/plugins/index.tsx +++ b/packages/react/src/plugins/index.tsx @@ -1,4 +1,3 @@ export { AddCssTokenVariables } from './css-variables'; export { FontResolver } from './font-resolver'; export { AnimationResolver } from './animation-resolver'; -export * from './animation-components'; From d9cbae7aaeaa5e749c2c744f1437831c008be3b8 Mon Sep 17 00:00:00 2001 From: Rohit Singh Date: Thu, 21 Sep 2023 12:56:08 +0530 Subject: [PATCH 4/7] feat: animation plugin driver --- example/storybook/babel.config.js | 4 +- example/storybook/package.json | 5 +- example/storybook/src/components/nb.config.ts | 4 +- example/storybook/tsconfig.json | 4 +- .../package.json | 14 +- packages/animation-moti-driver/package.json | 15 +- packages/animation-resolver/package.json | 6 +- .../react/src/plugins/animation-resolver.tsx | 456 ------------------ packages/react/src/plugins/index.tsx | 1 - 9 files changed, 30 insertions(+), 479 deletions(-) delete mode 100644 packages/react/src/plugins/animation-resolver.tsx diff --git a/example/storybook/babel.config.js b/example/storybook/babel.config.js index 4c6947bc3..0b330301d 100644 --- a/example/storybook/babel.config.js +++ b/example/storybook/babel.config.js @@ -16,13 +16,13 @@ module.exports = function (api) { __dirname, '../../packages/react/src' ), - ['@gluestack-style/legend-motion-driver']: path.join( + ['@gluestack-style/legend-motion-animation-driver']: path.join( __dirname, '../../packages/animation-legend-motion-driver/src' ), ['@gluestack-style/moti-driver']: path.join( __dirname, - '../../packages/animation-moti-driver/src' + '../../packages/animation-moti-animation-driver/src' ), ['@gluestack-style/animation-resolver']: path.join( __dirname, diff --git a/example/storybook/package.json b/example/storybook/package.json index f93316f9f..04fb7dd3a 100644 --- a/example/storybook/package.json +++ b/example/storybook/package.json @@ -97,8 +97,9 @@ "typescript": "^5.1.6" }, "peerDependencies": { - "@gluestack-style/legend-motion-driver": "*", - "@gluestack-style/moti-driver": "*", + "@gluestack-style/legend-motion-animation-driver": "*", + "@gluestack-style/moti-animation-driver": "*", + "@gluestack-style/animation-resolver": "*", "react": "*", "react-dom": "*", "react-native": "*", diff --git a/example/storybook/src/components/nb.config.ts b/example/storybook/src/components/nb.config.ts index f372ceb17..e515d1e66 100644 --- a/example/storybook/src/components/nb.config.ts +++ b/example/storybook/src/components/nb.config.ts @@ -1,6 +1,6 @@ import { AnimationResolver } from '@gluestack-style/animation-resolver'; -import { MotionAnimationDriver } from '@gluestack-style/legend-motion-driver'; -// import { MotiAnimationDriver } from '@gluestack-style/moti-driver'; +import { MotionAnimationDriver } from '@gluestack-style/legend-motion-animation-driver'; +// import { MotiAnimationDriver } from '@gluestack-style/moti-animation-driver'; import { createConfig } from '@gluestack-style/react'; export const config = createConfig({ diff --git a/example/storybook/tsconfig.json b/example/storybook/tsconfig.json index c929feb75..6b68e3568 100644 --- a/example/storybook/tsconfig.json +++ b/example/storybook/tsconfig.json @@ -4,10 +4,10 @@ "baseUrl": ".", "paths": { "@gluestack-style/react": ["../../packages/react/src"], - "@gluestack-style/legend-motion-driver": [ + "@gluestack-style/legend-motion-animation-driver": [ "../../packages/animation-legend-motion-driver/src" ], - "@gluestack-style/moti-driver": [ + "@gluestack-style/moti-animation-driver": [ "../../packages/animation-moti-driver/src" ], "@gluestack-style/animation-resolver": [ diff --git a/packages/animation-legend-motion-driver/package.json b/packages/animation-legend-motion-driver/package.json index 06e1014e6..57c8c031d 100644 --- a/packages/animation-legend-motion-driver/package.json +++ b/packages/animation-legend-motion-driver/package.json @@ -1,12 +1,15 @@ { - "name": "@gluestack-style/legend-motion-driver", + "name": "@gluestack-style/legend-motion-animation-driver", "version": "0.0.1", - "description": "A gluestack-style plugin for animation properties, utilizing animation libraries.", + "description": "A gluestack-style plugin for animation support using legendapp motion library", "keywords": [ "react", "native", "react-native", "animation", + "legendapp", + "motion", + "driver", "transition" ], "main": "lib/commonjs/index", @@ -21,11 +24,12 @@ "build": "bob build", "clean": "rm -rf lib" }, - "dependencies": { - "@legendapp/motion": "^2.2.0" + "peerDependencies": { + "@legendapp/motion": ">=2.2.0", + "@gluestack-style/react": ">=0.2.49" }, "devDependencies": { - "@gluestack-style/react": "^0.1.11", + "@gluestack-style/react": ">=0.2.49", "@types/react": "^18.0.22", "@types/react-native": "^0.69.15", "babel-plugin-transform-remove-console": "^6.9.4", diff --git a/packages/animation-moti-driver/package.json b/packages/animation-moti-driver/package.json index 032764503..bc2a568a6 100644 --- a/packages/animation-moti-driver/package.json +++ b/packages/animation-moti-driver/package.json @@ -1,11 +1,13 @@ { - "name": "@gluestack-style/moti-driver", + "name": "@gluestack-style/moti-animation-driver", "version": "0.1.11", - "description": "A gluestack-style plugin for animation properties, utilizing animation libraries.", + "description": "A gluestack-style driver for using moti animation with animation resolver plugin.", "keywords": [ "react", "native", "react-native", + "moti", + "driver", "animation", "transition" ], @@ -21,15 +23,14 @@ "build": "bob build", "clean": "rm -rf lib" }, - "dependencies": { - "moti": "^0.26.0" - }, "peerDependencies": { + "moti": ">=0.26.0", "react-native-reanimated": ">=3.5.1", - "react-native-gesture-handler": ">=2.12.1" + "react-native-gesture-handler": ">=2.12.1", + "@gluestack-style/react": ">=0.2.49" }, "devDependencies": { - "@gluestack-style/react": "^0.1.11", + "@gluestack-style/react": "^0.2.49", "@types/react": "^18.0.22", "@types/react-native": "^0.69.15", "babel-plugin-transform-remove-console": "^6.9.4", diff --git a/packages/animation-resolver/package.json b/packages/animation-resolver/package.json index bc672469f..0aa52332b 100644 --- a/packages/animation-resolver/package.json +++ b/packages/animation-resolver/package.json @@ -21,9 +21,11 @@ "build": "bob build", "clean": "rm -rf lib" }, - "dependencies": {}, + "peerDependencies": { + "@gluestack-style/react": ">=0.2.49" + }, "devDependencies": { - "@gluestack-style/react": "^0.1.11", + "@gluestack-style/react": "^0.2.49", "@types/react": "^18.0.22", "@types/react-native": "^0.69.15", "babel-plugin-transform-remove-console": "^6.9.4", diff --git a/packages/react/src/plugins/animation-resolver.tsx b/packages/react/src/plugins/animation-resolver.tsx deleted file mode 100644 index 3d349ca31..000000000 --- a/packages/react/src/plugins/animation-resolver.tsx +++ /dev/null @@ -1,456 +0,0 @@ -import React, { useEffect, useMemo } from 'react'; -import type { IAnimationDriverPlugin, IStyledPlugin } from '../types'; -import { useStyled } from '../StyledProvider'; -import { - deepMerge, - deepMergeObjects, - setObjectKeyValue, - resolvedTokenization, -} from '../utils'; -import { propertyTokenMap } from '../propertyTokenMap'; - -function tokenizeAnimationPropsFromConfig( - props: any = {}, - config: any, - animationAliases: any, - path: any = [], - tokenizedAnimatedProps: any = {} -) { - for (const prop in props) { - if (Array.isArray(props[prop])) { - path.push(prop); - setObjectKeyValue(tokenizedAnimatedProps, path, props[prop]); - path.pop(); - } else if (animationAliases[prop]) { - path.push(prop); - const tokenizedValue = resolvedTokenization(props[prop], config); - setObjectKeyValue(tokenizedAnimatedProps, path, tokenizedValue); - path.pop(); - } else if (typeof props[prop] === 'object') { - path.push(prop); - const tokenizedValue = resolvedTokenization(props[prop], config); - setObjectKeyValue(tokenizedAnimatedProps, path, tokenizedValue); - // path.pop(); - tokenizeAnimationPropsFromConfig( - props[prop], - config, - animationAliases, - path, - tokenizedAnimatedProps - ); - path.pop(); - } else { - path.push(prop); - setObjectKeyValue(tokenizedAnimatedProps, path, props[prop]); - path.pop(); - } - } - - return tokenizedAnimatedProps; -} - -function getVariantProps(props: any, theme: any) { - const variantTypes = theme?.variants ? Object.keys(theme.variants) : []; - - const restProps = { ...props }; - - const variantProps: any = {}; - variantTypes?.forEach((variant) => { - if (props[variant]) { - variantProps[variant] = props[variant]; - // delete restProps[variant]; - } - }); - - return { - variantProps, - restProps, - }; -} - -function resolveVariantAnimationProps(variantProps: any, styledObject: any) { - let resolvedVariant = {}; - Object.keys(variantProps).forEach((variant) => { - const variantValue = variantProps[variant]; - const variantObject = styledObject?.variants?.[variant]?.[variantValue]; - - resolvedVariant = deepMerge(resolvedVariant, variantObject); - }); - - return resolvedVariant; -} - -export class AnimationResolver implements IStyledPlugin { - name: 'AnimationResolver'; - componentDriver: IAnimationDriverPlugin; - config = { - aliases: { - ':animate': 'animate', - ':initial': 'initial', - ':exit': 'exit', - ':initialProps': 'initialProps', - ':animateProps': 'animateProps', - ':transition': 'transition', - ':transformOrigin': 'transformOrigin', - ':whileTap': 'whileTap', - ':whileHover': 'whileHover', - ':onAnimationComplete': 'onAnimationComplete', - } as const, - tokens: {} as const, - animatedPropMap: {} as any, - }; - - AnimatePresenceComp = React.Fragment; - - register(config: any) { - if (this.config) { - if (config?.aliases) { - this.config.aliases = { - ...this.config?.aliases, - ...config?.aliases, - }; - } - - if (config?.tokens) { - this.config.tokens = { - ...this.config?.tokens, - ...config?.tokens, - }; - } - if (config?.animatedPropMap) { - this.config.animatedPropMap = { - ...this.config?.animatedPropMap, - ...config?.animatedPropMap, - }; - } - // @ts-ignore - this.config.ref = config?.ref; - } - } - - constructor(ComponentDriverClass: any, config: any = {}) { - // @ts-ignore - const componentDriver = new ComponentDriverClass(config); - this.name = 'AnimationResolver'; - this.componentDriver = componentDriver; - if (componentDriver.engine.AnimatePresence) { - this.AnimatePresenceComp = componentDriver.engine.AnimatePresence; - } - this.register(componentDriver.config); - } - - #childrenExitPropsMap: any = {}; - - #extendedConfig: any = {}; - - inputMiddleWare

( - styledObj = {}, - shouldUpdateConfig: any = true, - _?: boolean, - Component?: React.ComponentType - ): { - // @ts-ignore - [key in keyof typeof this.config.aliases]: P[(typeof this.config.aliases)[key]]; - } { - if ( - Component && - (Component.displayName?.startsWith( - 'Gluestack-AnimatedResolver-Animated' - ) || - // @ts-ignore - Component.isAnimatedComponent) - ) { - let AnimatedComponent = - this.componentDriver.engine[ - // @ts-ignore - Component.displayName?.replace( - 'Gluestack-AnimatedResolver-Animated', - '' - ) - ]; - - if (AnimatedComponent) { - AnimatedComponent.isAnimatedComponent = true; - } - if (!AnimatedComponent) { - AnimatedComponent = React.Fragment; - } - - // this.#childrenExitPropsMap = deepClone(styledObj); - const resolvedAnimatedProps = this.updateStyledObject( - styledObj, - shouldUpdateConfig - ); - - const resolvedStyledObjectWithAnimatedProps = deepMerge( - styledObj, - resolvedAnimatedProps - ); - - if (shouldUpdateConfig) { - // @ts-ignore - return [styledObj, shouldUpdateConfig, _, AnimatedComponent]; - } - - // @ts-ignore - return [ - resolvedStyledObjectWithAnimatedProps, - shouldUpdateConfig, - _, - AnimatedComponent, - ]; - } - // @ts-ignore - return [styledObj, shouldUpdateConfig, _, Component]; - } - - updateStyledObject( - styledObject: any = {}, - shouldUpdateConfig: boolean, - resolvedStyledObject: any = {}, - keyPath: string[] = [] - ) { - const aliases = this.config?.aliases; - const animatedPropMap = this.config?.animatedPropMap; - for (const prop in styledObject) { - if (typeof styledObject[prop] === 'object') { - keyPath.push(prop); - this.updateStyledObject( - styledObject[prop], - shouldUpdateConfig, - resolvedStyledObject, - keyPath - ); - keyPath.pop(); - } - - // @ts-ignore - if (aliases && aliases?.[prop]) { - if (shouldUpdateConfig) { - // this.#childrenExitPropsMap[prop] = styledObject[prop]; - setObjectKeyValue( - this.#childrenExitPropsMap, - [...keyPath, prop], - styledObject[prop] - ); - } - const value = styledObject[prop]; - // @ts-ignore - keyPath.push('props', aliases[prop]); - setObjectKeyValue(resolvedStyledObject, keyPath, value); - keyPath.pop(); - keyPath.pop(); - delete styledObject[prop]; - } - - if (animatedPropMap && animatedPropMap[prop]) { - this.renameObjectKey(styledObject, prop, animatedPropMap[prop]); - } - } - return resolvedStyledObject; - } - - renameObjectKey(obj: any, from: string, to: string) { - obj[to] = obj[from]; - delete obj[from]; - return obj; - } - - componentMiddleWare({ Component, ExtendedConfig }: any) { - if (Component && Component.isAnimatedComponent) { - const styledConfig = this.#childrenExitPropsMap; - - this.#childrenExitPropsMap = {}; - - const NewComponent = React.forwardRef((props: any, ref?: any) => { - const { sx, ...rest } = props; - - const styledContext = useStyled(); - useEffect(() => { - if (!styledContext.animationDriverData) { - styledContext.setAnimationDriverData(this.componentDriver); - } - }, [styledContext]); - const CONFIG = useMemo( - () => ({ - ...styledContext.config, - propertyTokenMap, - }), - [styledContext.config] - ); - this.#extendedConfig = CONFIG; - if (ExtendedConfig) { - this.#extendedConfig = deepMerge(CONFIG, ExtendedConfig); - } - - let tokenizedAnimatedProps: any = {}; - const { variantProps, restProps } = getVariantProps(rest, styledConfig); - const variantStyledObject = resolveVariantAnimationProps( - variantProps, - styledConfig - ); - const componentStyledObject = deepMerge( - variantStyledObject, - styledConfig - ); - - const animationAliases = this.config?.aliases; - - const config = this.#extendedConfig; - - tokenizedAnimatedProps = tokenizeAnimationPropsFromConfig( - componentStyledObject, - config, - animationAliases - ); - - const tokenizedSxAnimationProps: any = tokenizeAnimationPropsFromConfig( - sx, - config, - animationAliases - ); - - const mergedAnimatedProps = deepMerge( - tokenizedAnimatedProps, - tokenizedSxAnimationProps - ); - - // @ts-ignore - const [resolvedAnimatedStyledWithStyledObject, , ,] = - this.inputMiddleWare(mergedAnimatedProps, false, false, Component); - let isState = false; - - Object.keys(restProps?.states ?? {}).forEach((state: any) => { - isState = restProps.states[state] ? true : false; - }); - const animatedProps = !isState - ? // @ts-ignore - resolvedAnimatedStyledWithStyledObject?.props - : {}; - return ( - - ); - }); - - if (NewComponent) { - //@ts-ignore - NewComponent.styled = {}; - //@ts-ignore - NewComponent.styled.config = {}; - //@ts-ignore - NewComponent.styled.config = styledConfig; - - //@ts-ignore - NewComponent.isStyledComponent = Component?.isStyledComponent; - //@ts-ignore - NewComponent.isComposedComponent = Component?.isComposedComponent; - - NewComponent.displayName = 'StyledComponent'; - return NewComponent; - } - } else { - return Component; - } - } - - wrapperComponentMiddleWare(Component: React.ComponentType) { - if ( - Component && - Component.displayName?.startsWith('Gluestack-AnimatedResolver-Animated') - ) { - const AnimatedPresenceComp = React.forwardRef( - ({ children, ...props }: any, ref?: any) => { - const clonedChildren: any = []; - const styledContext = useStyled(); - const CONFIG = useMemo( - () => ({ - ...styledContext.config, - propertyTokenMap, - }), - [styledContext.config] - ); - - this.#extendedConfig = CONFIG; - - React.Children.toArray(children).forEach((child: any) => { - if (child?.type?.displayName === 'StyledComponent') { - let tokenizedAnimatedProps: any = {}; - const animationAliases = this.config?.aliases; - - const componentStyledObject = child?.type?.styled?.config; - const { variantProps, restProps } = getVariantProps( - child?.props, - componentStyledObject - ); - - const config = CONFIG; - - if (child.type.styled.resolvedProps) { - tokenizedAnimatedProps = child?.type?.styled?.resolvedProps; - } else { - const variantStyledObject = resolveVariantAnimationProps( - variantProps, - componentStyledObject - ); - const componentStyledObjectWithVariants = deepMergeObjects( - componentStyledObject, - variantStyledObject - ); - tokenizedAnimatedProps = tokenizeAnimationPropsFromConfig( - componentStyledObjectWithVariants, - config, - animationAliases - ); - - child.type.styled.resolvedProps = tokenizedAnimatedProps; - } - - const tokenizedSxAnimationProps: any = - tokenizeAnimationPropsFromConfig( - child?.props?.sx, - config, - animationAliases - ); - - const mergedAnimatedProps = deepMergeObjects( - {}, - tokenizedSxAnimationProps, - tokenizedAnimatedProps - ); - - const clonedChild = React.cloneElement(child, { - exit: mergedAnimatedProps?.[':exit'], - ...restProps, - }); - clonedChildren.push(clonedChild); - } else { - clonedChildren.push(child); - } - }); - - return ( - - {clonedChildren} - - ); - } - ); - AnimatedPresenceComp.displayName = `AnimatePresence`; - - return { - Component: AnimatedPresenceComp, - AnimatePresence: AnimatedPresenceComp, - }; - } else { - return { - Component: React.Fragment, - AnimatePresence: React.Fragment, - }; - } - } -} diff --git a/packages/react/src/plugins/index.tsx b/packages/react/src/plugins/index.tsx index 391b23e1d..cb7752818 100644 --- a/packages/react/src/plugins/index.tsx +++ b/packages/react/src/plugins/index.tsx @@ -1,3 +1,2 @@ export { AddCssTokenVariables } from './css-variables'; export { FontResolver } from './font-resolver'; -export { AnimationResolver } from './animation-resolver'; From 13cab4506243d0f4dd882f001635e778edc79ffd Mon Sep 17 00:00:00 2001 From: Rohit Singh Date: Thu, 21 Sep 2023 13:29:17 +0530 Subject: [PATCH 5/7] v0.0.1 --- packages/animation-resolver/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/animation-resolver/package.json b/packages/animation-resolver/package.json index 0aa52332b..f8c3f7903 100644 --- a/packages/animation-resolver/package.json +++ b/packages/animation-resolver/package.json @@ -1,6 +1,6 @@ { "name": "@gluestack-style/animation-resolver", - "version": "0.1.11", + "version": "0.0.1", "description": "A gluestack-style plugin for resolving animation properties, utilizing animation libraries.", "keywords": [ "react", From 26f4b74aa13dd26dcd21bd4eff6d5e1371b27196 Mon Sep 17 00:00:00 2001 From: Rohit Singh Date: Thu, 21 Sep 2023 13:29:29 +0530 Subject: [PATCH 6/7] feat: animation plugin driver --- packages/animation-moti-driver/package.json | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/animation-moti-driver/package.json b/packages/animation-moti-driver/package.json index bc2a568a6..db1ccca24 100644 --- a/packages/animation-moti-driver/package.json +++ b/packages/animation-moti-driver/package.json @@ -1,6 +1,6 @@ { "name": "@gluestack-style/moti-animation-driver", - "version": "0.1.11", + "version": "0.0.1", "description": "A gluestack-style driver for using moti animation with animation resolver plugin.", "keywords": [ "react", @@ -24,10 +24,10 @@ "clean": "rm -rf lib" }, "peerDependencies": { + "@gluestack-style/react": ">=0.2.49", "moti": ">=0.26.0", - "react-native-reanimated": ">=3.5.1", "react-native-gesture-handler": ">=2.12.1", - "@gluestack-style/react": ">=0.2.49" + "react-native-reanimated": ">=3.5.1" }, "devDependencies": { "@gluestack-style/react": "^0.2.49", From eb927984aae2ac96c0ab34845c5a70275bf28269 Mon Sep 17 00:00:00 2001 From: ankit-tailor Date: Tue, 26 Sep 2023 16:22:46 +0530 Subject: [PATCH 7/7] fix: type error for components without plugin --- packages/react/src/styled.tsx | 12 +--- packages/react/src/types.ts | 104 ++++++++++++++++++---------------- 2 files changed, 56 insertions(+), 60 deletions(-) diff --git a/packages/react/src/styled.tsx b/packages/react/src/styled.tsx index bd918ca5e..6168021a1 100644 --- a/packages/react/src/styled.tsx +++ b/packages/react/src/styled.tsx @@ -823,7 +823,7 @@ const getStyleIdsFromMap = ( return componentStyleObject; }; -export function verboseStyled( +export function verboseStyled( Component: React.ComponentType

, theme: Partial>, componentStyleConfig: IComponentStyleConfig = {}, @@ -992,15 +992,7 @@ export function verboseStyled( ...componentProps }: Omit< Omit & - Partial< - ComponentProps< - ITypeReactNativeStyles, - Variants, - P, - ComCon, - PluginType - > - > & + Partial> & Partial> & { as?: any; children?: any; diff --git a/packages/react/src/types.ts b/packages/react/src/types.ts index 21b0c641b..57ec92d03 100644 --- a/packages/react/src/types.ts +++ b/packages/react/src/types.ts @@ -307,8 +307,8 @@ export type ITheme = Partial< 'animationComponentGluestack' extends keyof P ? P['animationComponentGluestack'] extends true ? Plugins - : unknown - : unknown + : [] + : [] > >; @@ -444,7 +444,7 @@ export type SxProps< GenericComponentProps = unknown, PLATFORM = '', MediaQuery = '', - PluginType = unknown + PluginType = [] > = | Partial< StylePropsType & @@ -736,53 +736,57 @@ export interface GSConfig /********************* COMPONENT PROPS TYPE *****************************************/ -export type ComponentProps< - GenericComponentStyles, - Variants, - P, - ComCon, - PluginType -> = SxStyleProps & { - states?: { - [K in IState]?: boolean; - }; -} & (GSConfig['globalStyle'] extends object - ? { - [Key in keyof MergeNestedThree< - GlobalVariants, - Variants, - // @ts-ignore - Components[`${ComCon}`]['theme']['variants'] - >]?: keyof MergeNestedThree< - GlobalVariants, - Variants, - // @ts-ignore - Components[`${ComCon}`]['theme']['variants'] - >[Key] extends 'true' | 'false' - ? boolean - : keyof MergeNestedThree< - GlobalVariants, - Variants, - // @ts-ignore - Components[`${ComCon}`]['theme']['variants'] - >[Key]; - } & Omit - : { - [Key in keyof MergeNested< - Variants, - // @ts-ignore - Components[`${ComCon}`]['theme']['variants'] - >]?: keyof MergeNested< - Variants, // @ts-ignore - Components[`${ComCon}`]['theme']['variants'] - >[Key] extends 'true' | 'false' - ? boolean - : keyof MergeNested< - Variants, - // @ts-ignore - Components[`${ComCon}`]['theme']['variants'] - >[Key]; - }); +export type ComponentProps = + SxStyleProps< + GenericComponentStyles, + Variants, + P, + 'animationComponentGluestack' extends keyof P + ? P['animationComponentGluestack'] extends true + ? Plugins + : [] + : [] + > & { + states?: { + [K in IState]?: boolean; + }; + } & (GSConfig['globalStyle'] extends object + ? { + [Key in keyof MergeNestedThree< + GlobalVariants, + Variants, + // @ts-ignore + Components[`${ComCon}`]['theme']['variants'] + >]?: keyof MergeNestedThree< + GlobalVariants, + Variants, + // @ts-ignore + Components[`${ComCon}`]['theme']['variants'] + >[Key] extends 'true' | 'false' + ? boolean + : keyof MergeNestedThree< + GlobalVariants, + Variants, + // @ts-ignore + Components[`${ComCon}`]['theme']['variants'] + >[Key]; + } & Omit + : { + [Key in keyof MergeNested< + Variants, + // @ts-ignore + Components[`${ComCon}`]['theme']['variants'] + >]?: keyof MergeNested< + Variants, // @ts-ignore + Components[`${ComCon}`]['theme']['variants'] + >[Key] extends 'true' | 'false' + ? boolean + : keyof MergeNested< + Variants, + // @ts-ignore + Components[`${ComCon}`]['theme']['variants'] + >[Key]; + }); export type UtilityProps = TokenizedRNStyleProps< GetRNStyles