diff --git a/.gitignore b/.gitignore index 3670bc7d..2907fdce 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,5 @@ .cache +.expo .next build coverage diff --git a/apps/animated-example/app.json b/apps/animated-example/app.json new file mode 100644 index 00000000..b905d9ee --- /dev/null +++ b/apps/animated-example/app.json @@ -0,0 +1,33 @@ +{ + "expo": { + "name": "animated-example", + "slug": "animated-example", + "version": "1.0.0", + "orientation": "portrait", + "icon": "./assets/icon.png", + "userInterfaceStyle": "light", + "newArchEnabled": true, + "splash": { + "image": "./assets/splash.png", + "resizeMode": "contain", + "backgroundColor": "#1a1a2e" + }, + "assetBundlePatterns": [ + "**/*" + ], + "android": { + "adaptiveIcon": { + "foregroundImage": "./assets/adaptive-icon.png", + "backgroundColor": "#ffffff" + } + }, + "ios": { + "supportsTablet": true, + "bundleIdentifier": "com.anonymous.animated-example" + }, + "web": { + "bundler": "metro", + "favicon": "./assets/favicon.png" + } + } +} diff --git a/apps/animated-example/assets/adaptive-icon.png b/apps/animated-example/assets/adaptive-icon.png new file mode 100644 index 00000000..03d6f6b6 Binary files /dev/null and b/apps/animated-example/assets/adaptive-icon.png differ diff --git a/apps/animated-example/assets/favicon.png b/apps/animated-example/assets/favicon.png new file mode 100644 index 00000000..e75f697b Binary files /dev/null and b/apps/animated-example/assets/favicon.png differ diff --git a/apps/animated-example/assets/icon.png b/apps/animated-example/assets/icon.png new file mode 100644 index 00000000..a0b1526f Binary files /dev/null and b/apps/animated-example/assets/icon.png differ diff --git a/apps/animated-example/assets/splash.png b/apps/animated-example/assets/splash.png new file mode 100644 index 00000000..0e89705a Binary files /dev/null and b/apps/animated-example/assets/splash.png differ diff --git a/apps/animated-example/babel.config.js b/apps/animated-example/babel.config.js new file mode 100644 index 00000000..e00459c9 --- /dev/null +++ b/apps/animated-example/babel.config.js @@ -0,0 +1,39 @@ +/** + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +const reactStrictPreset = require('react-strict-dom/babel-preset'); + +function getPlatform(caller) { + return caller && caller.platform; +} + +function getIsDev(caller) { + if (caller?.isDev != null) return caller.isDev; + // https://babeljs.io/docs/options#envname + return ( + process.env.BABEL_ENV === 'development' || + process.env.NODE_ENV === 'development' + ); +} + +module.exports = function (api) { + //api.cache(true); + + const platform = api.caller(getPlatform); + const dev = api.caller(getIsDev); + + const plugins = []; + const presets = [ + 'babel-preset-expo', + [reactStrictPreset, { debug: true, dev, platform }] + ]; + + return { + plugins, + presets + }; +}; diff --git a/apps/animated-example/metro.config.js b/apps/animated-example/metro.config.js new file mode 100644 index 00000000..29d51f46 --- /dev/null +++ b/apps/animated-example/metro.config.js @@ -0,0 +1,16 @@ +/** + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +// Learn more https://docs.expo.dev/guides/monorepos +const { getDefaultConfig } = require('expo/metro-config'); + +// Find the project and workspace directories +const projectRoot = __dirname; + +const config = getDefaultConfig(projectRoot); + +module.exports = config; diff --git a/apps/animated-example/package.json b/apps/animated-example/package.json new file mode 100644 index 00000000..2f9834b8 --- /dev/null +++ b/apps/animated-example/package.json @@ -0,0 +1,30 @@ +{ + "private": true, + "name": "animated-example", + "version": "0.0.1", + "main": "./src/app/index.js", + "scripts": { + "dev": "expo start --clear", + "dev:android": "expo start --android --clear", + "dev:ios": "expo start --ios --clear", + "dev:web": "expo start --web --clear", + "android": "expo run:android", + "ios": "expo run:ios" + }, + "dependencies": { + "@expo/metro-runtime": "~5.0.4", + "expo": "^53.0.11", + "expo-build-properties": "~0.14.6", + "expo-status-bar": "~2.2.3", + "react": "~19.0.0", + "react-dom": "~19.0.0", + "react-native": "~0.79.5", + "react-native-web": "~0.20.0", + "react-strict-dom": "0.0.54", + "react-strict-animated": "0.0.55", + "react-native-safe-area-context": "5.4.0" + }, + "devDependencies": { + "@babel/core": "^7.27.3" + } +} diff --git a/apps/animated-example/postcss.config.js b/apps/animated-example/postcss.config.js new file mode 100644 index 00000000..de2f9fd0 --- /dev/null +++ b/apps/animated-example/postcss.config.js @@ -0,0 +1,19 @@ +/** + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +module.exports = { + plugins: [ + require('react-strict-dom/postcss-plugin')({ + include: [ + 'src/**/*.{js,jsx,mjs,ts,tsx}', + '../../node_modules/example-ui/**/*.jsx', + '../../node_modules/react-strict-animated/**/*.js' + ] + }), + require('autoprefixer') + ] +}; diff --git a/apps/animated-example/src/app/index.js b/apps/animated-example/src/app/index.js new file mode 100644 index 00000000..105cc09f --- /dev/null +++ b/apps/animated-example/src/app/index.js @@ -0,0 +1,24 @@ +/** + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +// Required for CSS to work on Expo Web. +import './strict.css'; +// Required for Fast Refresh to work on Expo Web +import '@expo/metro-runtime'; + +import { LogBox } from 'react-native'; +import { registerRootComponent } from 'expo'; +import App from '../components/App'; + +if (LogBox != null) { + LogBox.ignoreLogs([ + // /React Strict DOM: .*/, + // /Failed prop type: .*/, + ]); +} + +registerRootComponent(App); diff --git a/apps/animated-example/src/app/strict.css b/apps/animated-example/src/app/strict.css new file mode 100644 index 00000000..a0d2b13a --- /dev/null +++ b/apps/animated-example/src/app/strict.css @@ -0,0 +1,29 @@ +/** + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +html, body { + height: initial; +} + +html { + font-family: system-ui, -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, 'Open Sans', 'Helvetica Neue', sans-serif; +} + +body { + overflow: auto; +} + +#root { + flex-direction: column; +} + +/** + * This directive is used by the react-strict-dom postcss plugin. + * It is automatically replaced with generated CSS during builds. + */ + +@react-strict-dom; diff --git a/apps/animated-example/src/components/App.js b/apps/animated-example/src/components/App.js new file mode 100644 index 00000000..804b296a --- /dev/null +++ b/apps/animated-example/src/components/App.js @@ -0,0 +1,202 @@ +/** + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +import * as React from 'react'; +import { css, html } from 'react-strict-dom'; +import { SafeAreaProvider } from 'react-native-safe-area-context'; + +import RootLayout from './RootLayout'; +import TimingDemo from './demos/TimingDemo'; +import SpringDemo from './demos/SpringDemo'; +import ParallelDemo from './demos/ParallelDemo'; +import SequenceDemo from './demos/SequenceDemo'; +import DelayDemo from './demos/DelayDemo'; +import InterpolationDemo from './demos/InterpolationDemo'; +import TransformDemo from './demos/TransformDemo'; +import CombinedTransformDemo from './demos/CombinedTransformDemo'; +import InterruptibleDemo from './demos/InterruptibleDemo'; + +const demos = [ + { + name: 'Timing', + description: 'Animate opacity with duration-based easing', + component: TimingDemo, + color: '#3B82F6' + }, + { + name: 'Spring', + description: 'Physics-based spring animation', + component: SpringDemo, + color: '#8B5CF6' + }, + { + name: 'Parallel', + description: 'Multiple animations running together', + component: ParallelDemo, + color: '#EF4444' + }, + { + name: 'Sequence', + description: 'Chained animations in order', + component: SequenceDemo, + color: '#14B8A6' + }, + { + name: 'Delay', + description: 'Staggered animations with delays', + component: DelayDemo, + color: '#F59E0B' + }, + { + name: 'Interpolation', + description: 'Map values to rotation and scale', + component: InterpolationDemo, + color: '#EC4899' + }, + { + name: 'Transform', + description: 'Rotation transform animation', + component: TransformDemo, + color: '#F97316' + }, + { + name: 'Combined', + description: 'Multiple transforms animated together', + component: CombinedTransformDemo, + color: '#10B981' + }, + { + name: 'Interruptible', + description: 'Stop and restart animations mid-flight', + component: InterruptibleDemo, + color: '#A855F7' + } +]; + +console.log(RootLayout); + +export default function App() { + return ( + + + + + + + React Strict Animated + + A declarative animation library for react-strict-dom + + + + {demos.map((demo) => ( + + + + {demo.name} + + + {demo.description} + + + + + + ))} + + + + + + + ); +} + +const styles = css.create({ + root: { + backgroundColor: '#1a1a2e' + }, + container: { + minHeight: '100vh', + padding: 32 + }, + header: { + textAlign: 'center', + marginBottom: 48, + paddingTop: 24 + }, + title: { + fontSize: 42, + fontWeight: '700', + color: '#ffffff', + marginBottom: 12, + letterSpacing: -1 + }, + subtitle: { + fontSize: 18, + color: 'rgba(255, 255, 255, 0.6)', + fontWeight: '400' + }, + demoGrid: { + display: 'flex', + flexDirection: 'row', + flexWrap: 'wrap', + justifyContent: 'center', + gap: 24, + maxWidth: 1400, + marginInline: 'auto' + }, + demoCard: { + display: 'flex', + flexDirection: 'column', + backgroundColor: 'rgba(255, 255, 255, 0.05)', + borderRadius: 20, + padding: 24, + width: 320, + minHeight: 280, + borderWidth: 1, + borderStyle: 'solid', + borderColor: 'rgba(255, 255, 255, 0.1)' + }, + cardHeader: { + display: 'flex', + flexDirection: 'row', + alignItems: 'center', + gap: 10, + marginBottom: 8 + }, + colorDot: { + width: 10, + height: 10, + borderRadius: 5 + }, + demoTitle: { + fontSize: 20, + fontWeight: '600', + color: '#ffffff' + }, + demoDescription: { + fontSize: 14, + color: 'rgba(255, 255, 255, 0.5)', + marginBottom: 24, + lineHeight: 1.4 + }, + demoContent: { + display: 'flex', + flexDirection: 'column', + alignItems: 'center', + justifyContent: 'center', + flex: 1, + paddingTop: 16, + rowGap: 16 + } +}); diff --git a/apps/animated-example/src/components/RootLayout.native.js b/apps/animated-example/src/components/RootLayout.native.js new file mode 100644 index 00000000..30b65071 --- /dev/null +++ b/apps/animated-example/src/components/RootLayout.native.js @@ -0,0 +1,18 @@ +/** + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +import * as React from 'react'; +import { ScrollView } from 'react-native'; +import { SafeAreaView } from 'react-native-safe-area-context'; + +export default function RootLayout(props) { + return ( + + {props.children} + + ); +} diff --git a/apps/animated-example/src/components/RootLayout.web.js b/apps/animated-example/src/components/RootLayout.web.js new file mode 100644 index 00000000..ea25ab23 --- /dev/null +++ b/apps/animated-example/src/components/RootLayout.web.js @@ -0,0 +1,10 @@ +/** + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +export default function RootLayout(props) { + return props.children; +} diff --git a/apps/animated-example/src/components/demos/CombinedTransformDemo.js b/apps/animated-example/src/components/demos/CombinedTransformDemo.js new file mode 100644 index 00000000..c38a7c62 --- /dev/null +++ b/apps/animated-example/src/components/demos/CombinedTransformDemo.js @@ -0,0 +1,101 @@ +/** + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +import * as React from 'react'; +import { css, html } from 'react-strict-dom'; +import { animated, Animation } from 'react-strict-animated'; + +export default function CombinedTransformDemo() { + const translateX = Animation.useValue(0); + const translateY = Animation.useValue(0); + const scale = Animation.useValue(1); + const rotate = Animation.useValue(0); + const [isAnimating, setIsAnimating] = React.useState(false); + + const rotateInterpolated = Animation.interpolate(rotate, { + inputRange: [0, 1], + outputRange: ['0deg', '180deg'] + }); + + const runAnimation = () => { + if (isAnimating) return; + setIsAnimating(true); + + Animation.sequence([ + Animation.parallel([ + Animation.timing(translateX, { toValue: 25, duration: 700 }), + Animation.timing(translateY, { toValue: -18, duration: 700 }), + Animation.timing(scale, { toValue: 1.25, duration: 700 }), + Animation.timing(rotate, { toValue: 1, duration: 700 }) + ]), + Animation.parallel([ + Animation.timing(translateX, { toValue: 0, duration: 700 }), + Animation.timing(translateY, { toValue: 0, duration: 700 }), + Animation.timing(scale, { toValue: 1, duration: 700 }), + Animation.timing(rotate, { toValue: 0, duration: 700 }) + ]) + ]).start(() => { + setIsAnimating(false); + }); + }; + + return ( + <> + + + + + + {isAnimating ? 'Animating...' : 'Combined Transform'} + + + + ); +} + +const styles = css.create({ + boxContainer: { + display: 'flex', + height: 100, + alignItems: 'center', + justifyContent: 'center' + }, + box: { + width: 50, + height: 50, + backgroundColor: '#10B981', + borderRadius: 12 + }, + button: { + display: 'flex', + backgroundColor: 'rgba(16, 185, 129, 0.2)', + borderWidth: 1, + borderStyle: 'solid', + borderColor: 'rgba(16, 185, 129, 0.4)', + paddingBlock: 10, + paddingInline: 20, + borderRadius: 10, + cursor: 'pointer', + alignItems: 'center', + justifyContent: 'center' + }, + buttonText: { + color: '#10B981', + fontWeight: '600', + fontSize: 14 + } +}); diff --git a/apps/animated-example/src/components/demos/DelayDemo.js b/apps/animated-example/src/components/demos/DelayDemo.js new file mode 100644 index 00000000..33e6839f --- /dev/null +++ b/apps/animated-example/src/components/demos/DelayDemo.js @@ -0,0 +1,124 @@ +/** + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +import * as React from 'react'; +import { css, html } from 'react-strict-dom'; +import { animated, Animation } from 'react-strict-animated'; + +export default function DelayDemo() { + const opacity1 = Animation.useValue(0.2); + const opacity2 = Animation.useValue(0.2); + const opacity3 = Animation.useValue(0.2); + const [isAnimating, setIsAnimating] = React.useState(false); + + const runAnimation = () => { + if (isAnimating) return; + setIsAnimating(true); + + Animation.sequence([ + Animation.parallel([ + Animation.sequence([ + Animation.timing(opacity1, { toValue: 1, duration: 300 }), + Animation.delay(700) + ]), + Animation.sequence([ + Animation.delay(200), + Animation.timing(opacity2, { toValue: 1, duration: 300 }), + Animation.delay(500) + ]), + Animation.sequence([ + Animation.delay(400), + Animation.timing(opacity3, { toValue: 1, duration: 300 }), + Animation.delay(300) + ]) + ]), + Animation.parallel([ + Animation.timing(opacity1, { toValue: 0.2, duration: 400 }), + Animation.timing(opacity2, { toValue: 0.2, duration: 400 }), + Animation.timing(opacity3, { toValue: 0.2, duration: 400 }) + ]) + ]).start(() => { + setIsAnimating(false); + }); + }; + + return ( + <> + + + + + + + + + + {isAnimating ? 'Staggering...' : 'Staggered Fade'} + + + + ); +} + +const styles = css.create({ + boxContainer: { + display: 'flex', + height: 100, + alignItems: 'center', + justifyContent: 'center' + }, + row: { + display: 'flex', + flexDirection: 'row', + gap: 16 + }, + circle1: { + width: 36, + height: 36, + borderRadius: 18, + backgroundColor: '#EF4444' + }, + circle2: { + width: 36, + height: 36, + borderRadius: 18, + backgroundColor: '#F59E0B' + }, + circle3: { + width: 36, + height: 36, + borderRadius: 18, + backgroundColor: '#22C55E' + }, + button: { + display: 'flex', + backgroundColor: 'rgba(245, 158, 11, 0.2)', + borderWidth: 1, + borderStyle: 'solid', + borderColor: 'rgba(245, 158, 11, 0.4)', + paddingBlock: 10, + paddingInline: 20, + borderRadius: 10, + cursor: 'pointer', + alignItems: 'center', + justifyContent: 'center' + }, + buttonText: { + color: '#F59E0B', + fontWeight: '600', + fontSize: 14 + } +}); diff --git a/apps/animated-example/src/components/demos/InterpolationDemo.js b/apps/animated-example/src/components/demos/InterpolationDemo.js new file mode 100644 index 00000000..7c7fd9e0 --- /dev/null +++ b/apps/animated-example/src/components/demos/InterpolationDemo.js @@ -0,0 +1,87 @@ +/** + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +import * as React from 'react'; +import { css, html } from 'react-strict-dom'; +import { animated, Animation } from 'react-strict-animated'; + +export default function InterpolationDemo() { + const progress = Animation.useValue(0); + const [isAnimating, setIsAnimating] = React.useState(false); + + const rotate = Animation.interpolate(progress, { + inputRange: [0, 1], + outputRange: ['0deg', '360deg'] + }); + + const scale = Animation.interpolate(progress, { + inputRange: [0, 0.5, 1], + outputRange: [1, 1.25, 1] + }); + + const runAnimation = () => { + if (isAnimating) return; + setIsAnimating(true); + + Animation.timing(progress, { + toValue: 1, + duration: 1200 + }).start(() => { + progress.setValue(0); + setIsAnimating(false); + }); + }; + + return ( + <> + + + + + + {isAnimating ? 'Interpolating...' : 'Interpolate'} + + + + ); +} + +const styles = css.create({ + boxContainer: { + display: 'flex', + height: 100, + alignItems: 'center', + justifyContent: 'center' + }, + box: { + width: 55, + height: 55, + backgroundColor: '#EC4899', + borderRadius: 12 + }, + button: { + display: 'flex', + backgroundColor: 'rgba(236, 72, 153, 0.2)', + borderWidth: 1, + borderStyle: 'solid', + borderColor: 'rgba(236, 72, 153, 0.4)', + paddingBlock: 10, + paddingInline: 20, + borderRadius: 10, + cursor: 'pointer', + alignItems: 'center', + justifyContent: 'center' + }, + buttonText: { + color: '#EC4899', + fontWeight: '600', + fontSize: 14 + } +}); diff --git a/apps/animated-example/src/components/demos/InterruptibleDemo.js b/apps/animated-example/src/components/demos/InterruptibleDemo.js new file mode 100644 index 00000000..f1299b43 --- /dev/null +++ b/apps/animated-example/src/components/demos/InterruptibleDemo.js @@ -0,0 +1,95 @@ +/** + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +import * as React from 'react'; +import { css, html } from 'react-strict-dom'; +import { animated, Animation } from 'react-strict-animated'; + +export default function InterruptibleDemo() { + const translateX = Animation.useValue(0); + const [isLeft, setIsLeft] = React.useState(true); + + const toggle = () => { + const targetX = isLeft ? 80 : 0; + setIsLeft(!isLeft); + + // Starting a new animation automatically interrupts any running animation + // on the same value and starts from the current position + Animation.spring(translateX, { + toValue: targetX, + stiffness: 180, + damping: 12 + }).start(); + }; + + return ( + <> + + + + + + Tap rapidly to interrupt + + + {isLeft ? 'Move Right' : 'Move Left'} + + + + ); +} + +const styles = css.create({ + boxContainer: { + display: 'flex', + height: 100, + alignItems: 'center', + justifyContent: 'center' + }, + track: { + width: 120, + height: 44, + backgroundColor: 'rgba(168, 85, 247, 0.15)', + borderRadius: 22, + padding: 4, + display: 'flex', + alignItems: 'center' + }, + box: { + width: 36, + height: 36, + backgroundColor: '#A855F7', + borderRadius: 18 + }, + hint: { + fontSize: 12, + color: 'rgba(168, 85, 247, 0.7)', + textAlign: 'center', + marginBottom: 8 + }, + button: { + display: 'flex', + backgroundColor: 'rgba(168, 85, 247, 0.2)', + borderWidth: 1, + borderStyle: 'solid', + borderColor: 'rgba(168, 85, 247, 0.4)', + paddingBlock: 10, + paddingInline: 20, + borderRadius: 10, + cursor: 'pointer', + alignItems: 'center', + justifyContent: 'center' + }, + buttonText: { + color: '#A855F7', + fontWeight: '600', + fontSize: 14 + } +}); diff --git a/apps/animated-example/src/components/demos/ParallelDemo.js b/apps/animated-example/src/components/demos/ParallelDemo.js new file mode 100644 index 00000000..a3ac900d --- /dev/null +++ b/apps/animated-example/src/components/demos/ParallelDemo.js @@ -0,0 +1,89 @@ +/** + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +import * as React from 'react'; +import { css, html } from 'react-strict-dom'; +import { animated, Animation } from 'react-strict-animated'; + +export default function ParallelDemo() { + const translateX = Animation.useValue(0); + const translateY = Animation.useValue(0); + const opacity = Animation.useValue(1); + const [isAnimating, setIsAnimating] = React.useState(false); + + const runAnimation = () => { + if (isAnimating) return; + setIsAnimating(true); + + Animation.sequence([ + Animation.parallel([ + Animation.timing(translateX, { toValue: 40, duration: 600 }), + Animation.timing(translateY, { toValue: -25, duration: 600 }), + Animation.timing(opacity, { toValue: 0.5, duration: 600 }) + ]), + Animation.parallel([ + Animation.timing(translateX, { toValue: 0, duration: 600 }), + Animation.timing(translateY, { toValue: 0, duration: 600 }), + Animation.timing(opacity, { toValue: 1, duration: 600 }) + ]) + ]).start(() => { + setIsAnimating(false); + }); + }; + + return ( + <> + + + + + + {isAnimating ? 'Moving...' : 'Move Diagonally'} + + + + ); +} + +const styles = css.create({ + boxContainer: { + display: 'flex', + height: 100, + alignItems: 'center', + justifyContent: 'center' + }, + box: { + width: 55, + height: 55, + backgroundColor: '#EF4444', + borderRadius: 12 + }, + button: { + display: 'flex', + backgroundColor: 'rgba(239, 68, 68, 0.2)', + borderWidth: 1, + borderStyle: 'solid', + borderColor: 'rgba(239, 68, 68, 0.4)', + paddingBlock: 10, + paddingInline: 20, + borderRadius: 10, + cursor: 'pointer', + alignItems: 'center', + justifyContent: 'center' + }, + buttonText: { + color: '#EF4444', + fontWeight: '600', + fontSize: 14 + } +}); diff --git a/apps/animated-example/src/components/demos/SequenceDemo.js b/apps/animated-example/src/components/demos/SequenceDemo.js new file mode 100644 index 00000000..761cd69e --- /dev/null +++ b/apps/animated-example/src/components/demos/SequenceDemo.js @@ -0,0 +1,79 @@ +/** + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +import * as React from 'react'; +import { css, html } from 'react-strict-dom'; +import { animated, Animation } from 'react-strict-animated'; + +export default function SequenceDemo() { + const translateX = Animation.useValue(0); + const [isAnimating, setIsAnimating] = React.useState(false); + + const runAnimation = () => { + if (isAnimating) return; + setIsAnimating(true); + + Animation.parallel([ + Animation.sequence([ + Animation.timing(translateX, { toValue: 60, duration: 350 }), + Animation.timing(translateX, { toValue: -60, duration: 700 }), + Animation.timing(translateX, { toValue: 0, duration: 350 }) + ]) + ]).start(() => { + setIsAnimating(false); + }); + }; + + return ( + <> + + + + + + {isAnimating ? 'Moving...' : 'Move in Sequence'} + + + + ); +} + +const styles = css.create({ + boxContainer: { + display: 'flex', + height: 100, + alignItems: 'center', + justifyContent: 'center' + }, + box: { + width: 55, + height: 55, + backgroundColor: '#14B8A6', + borderRadius: 12 + }, + button: { + display: 'flex', + backgroundColor: 'rgba(20, 184, 166, 0.2)', + borderWidth: 1, + borderStyle: 'solid', + borderColor: 'rgba(20, 184, 166, 0.4)', + paddingBlock: 10, + paddingInline: 20, + borderRadius: 10, + cursor: 'pointer', + alignItems: 'center', + justifyContent: 'center' + }, + buttonText: { + color: '#14B8A6', + fontWeight: '600', + fontSize: 14 + } +}); diff --git a/apps/animated-example/src/components/demos/SpringDemo.js b/apps/animated-example/src/components/demos/SpringDemo.js new file mode 100644 index 00000000..fcf8197d --- /dev/null +++ b/apps/animated-example/src/components/demos/SpringDemo.js @@ -0,0 +1,84 @@ +/** + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +import * as React from 'react'; +import { css, html } from 'react-strict-dom'; +import { animated, Animation } from 'react-strict-animated'; + +export default function SpringDemo() { + const scale = Animation.useValue(1); + const [isAnimating, setIsAnimating] = React.useState(false); + + const runAnimation = () => { + if (isAnimating) return; + setIsAnimating(true); + + Animation.sequence([ + Animation.spring(scale, { + toValue: 1.4, + stiffness: 300, + damping: 10 + }), + Animation.spring(scale, { + toValue: 1, + stiffness: 300, + damping: 10 + }) + ]).start(() => { + setIsAnimating(false); + }); + }; + + return ( + <> + + + + + + {isAnimating ? 'Bouncing...' : 'Spring Bounce'} + + + + ); +} + +const styles = css.create({ + boxContainer: { + display: 'flex', + height: 100, + alignItems: 'center', + justifyContent: 'center' + }, + box: { + width: 60, + height: 60, + backgroundColor: '#8B5CF6', + borderRadius: 12 + }, + button: { + display: 'flex', + backgroundColor: 'rgba(139, 92, 246, 0.2)', + borderWidth: 1, + borderStyle: 'solid', + borderColor: 'rgba(139, 92, 246, 0.4)', + paddingBlock: 10, + paddingInline: 20, + borderRadius: 10, + cursor: 'pointer', + alignItems: 'center', + justifyContent: 'center' + }, + buttonText: { + color: '#8B5CF6', + fontWeight: '600', + fontSize: 14 + } +}); diff --git a/apps/animated-example/src/components/demos/TimingDemo.js b/apps/animated-example/src/components/demos/TimingDemo.js new file mode 100644 index 00000000..8427ab15 --- /dev/null +++ b/apps/animated-example/src/components/demos/TimingDemo.js @@ -0,0 +1,74 @@ +/** + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +import * as React from 'react'; +import { css, html } from 'react-strict-dom'; +import { animated, Animation } from 'react-strict-animated'; + +export default function TimingDemo() { + const opacity = Animation.useValue(0.3); + const [isAnimating, setIsAnimating] = React.useState(false); + + const runAnimation = () => { + if (isAnimating) return; + setIsAnimating(true); + + Animation.sequence([ + Animation.timing(opacity, { toValue: 1, duration: 500 }), + Animation.timing(opacity, { toValue: 0.3, duration: 500 }) + ]).start(() => { + setIsAnimating(false); + }); + }; + + return ( + <> + + + + + + {isAnimating ? 'Animating...' : 'Fade In/Out'} + + + + ); +} + +const styles = css.create({ + boxContainer: { + display: 'flex', + height: 100, + alignItems: 'center', + justifyContent: 'center' + }, + box: { + width: 70, + height: 70, + backgroundColor: '#3B82F6', + borderRadius: 12, + opacity: 0.3 + }, + button: { + display: 'flex', + backgroundColor: 'rgba(59, 130, 246, 0.2)', + borderWidth: 1, + borderStyle: 'solid', + borderColor: 'rgba(59, 130, 246, 0.4)', + paddingBlock: 10, + paddingInline: 20, + borderRadius: 10, + cursor: 'pointer', + alignItems: 'center', + justifyContent: 'center' + }, + buttonText: { + color: '#3B82F6', + fontWeight: '600', + fontSize: 14 + } +}); diff --git a/apps/animated-example/src/components/demos/TransformDemo.js b/apps/animated-example/src/components/demos/TransformDemo.js new file mode 100644 index 00000000..eeee56fa --- /dev/null +++ b/apps/animated-example/src/components/demos/TransformDemo.js @@ -0,0 +1,82 @@ +/** + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +import * as React from 'react'; +import { css, html } from 'react-strict-dom'; +import { animated, Animation } from 'react-strict-animated'; + +export default function TransformDemo() { + const rotate = Animation.useValue(0); + const [isAnimating, setIsAnimating] = React.useState(false); + + const rotateInterpolated = Animation.interpolate(rotate, { + inputRange: [0, 1], + outputRange: ['0deg', '360deg'] + }); + + const runAnimation = () => { + if (isAnimating) return; + setIsAnimating(true); + + Animation.timing(rotate, { + toValue: 1, + duration: 800 + }).start(() => { + rotate.setValue(0); + setIsAnimating(false); + }); + }; + + return ( + <> + + + + + + {isAnimating ? 'Rotating...' : 'Rotate 360°'} + + + + ); +} + +const styles = css.create({ + boxContainer: { + display: 'flex', + height: 100, + alignItems: 'center', + justifyContent: 'center' + }, + box: { + width: 55, + height: 55, + backgroundColor: '#F97316', + borderRadius: 12 + }, + button: { + display: 'flex', + backgroundColor: 'rgba(249, 115, 22, 0.2)', + borderWidth: 1, + borderStyle: 'solid', + borderColor: 'rgba(249, 115, 22, 0.4)', + paddingBlock: 10, + paddingInline: 20, + borderRadius: 10, + cursor: 'pointer', + alignItems: 'center', + justifyContent: 'center' + }, + buttonText: { + color: '#F97316', + fontWeight: '600', + fontSize: 14 + } +}); diff --git a/package-lock.json b/package-lock.json index 7b795bee..44839ccb 100644 --- a/package-lock.json +++ b/package-lock.json @@ -51,6 +51,75 @@ "npm": ">=10.0.0" } }, + "apps/animated-example": { + "version": "0.0.1", + "dependencies": { + "@expo/metro-runtime": "~5.0.4", + "expo": "^53.0.11", + "expo-build-properties": "~0.14.6", + "expo-status-bar": "~2.2.3", + "react": "~19.0.0", + "react-dom": "~19.0.0", + "react-native": "~0.79.5", + "react-native-safe-area-context": "5.4.0", + "react-native-web": "~0.20.0", + "react-strict-animated": "0.0.55", + "react-strict-dom": "0.0.54" + }, + "devDependencies": { + "@babel/core": "^7.27.3" + } + }, + "apps/animated-example/node_modules/glob-parent": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", + "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", + "license": "ISC", + "dependencies": { + "is-glob": "^4.0.3" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "apps/animated-example/node_modules/postcss-react-strict-dom": { + "version": "0.0.54", + "resolved": "https://registry.npmjs.org/postcss-react-strict-dom/-/postcss-react-strict-dom-0.0.54.tgz", + "integrity": "sha512-z2TiEQ9hC5XL1SZvJrbI9P2idMxoljpN5ipJlda1/ckLQrD6708y/1wES+HoLTL0ndrLc4rNCY14gy6Nqx9clQ==", + "license": "MIT", + "dependencies": { + "@babel/core": "^7.26.8", + "@stylexjs/babel-plugin": "^0.15.4", + "fast-glob": "^3.3.2", + "glob-parent": "^6.0.2", + "is-glob": "^4.0.3" + }, + "peerDependencies": { + "postcss": "^8.4.49" + } + }, + "apps/animated-example/node_modules/react-strict-dom": { + "version": "0.0.54", + "resolved": "https://registry.npmjs.org/react-strict-dom/-/react-strict-dom-0.0.54.tgz", + "integrity": "sha512-Nzl8kF6k0VxnUeFFXIl55Eoq2OwA3OVRH+Hqj5a9pKDN9sbakt2IjEXfcLM0CodMmV6rJ98iTA7WPkjrGMiWLQ==", + "license": "MIT", + "dependencies": { + "@babel/helper-module-imports": "^7.24.7", + "@stylexjs/babel-plugin": "^0.15.4", + "@stylexjs/stylex": "^0.15.4", + "postcss-react-strict-dom": "0.0.54", + "postcss-value-parser": "^4.1.0", + "styleq": "^0.2.1" + }, + "engines": { + "node": ">=20.11.0" + }, + "peerDependencies": { + "react": "^19.0.0", + "react-dom": "^19.0.0", + "react-native": ">=0.79.5" + } + }, "apps/example-ui": { "version": "0.0.55" }, @@ -393,6 +462,7 @@ "resolved": "https://registry.npmjs.org/react/-/react-19.1.0.tgz", "integrity": "sha512-FS+XFBNvn3GTAWq26joslQgWNoFu08F4kl0J4CgdNKADkdSGXQyTCnKteIAJy96Br6YbpEU1LSzV5dYtjMkMDg==", "license": "MIT", + "peer": true, "engines": { "node": ">=0.10.0" } @@ -580,6 +650,7 @@ "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.39.1.tgz", "integrity": "sha512-BhHmn2yNOFA9H9JmmIVKJmd288g9hrVRDkdoIgRCRuSySRUHH7r/DI6aAXW9T1WwUuY3DFgrcaqB+deURBLR5g==", "dev": true, + "peer": true, "dependencies": { "@eslint-community/eslint-utils": "^4.8.0", "@eslint-community/regexpp": "^4.12.1", @@ -825,6 +896,7 @@ "version": "4.0.3", "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz", "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", + "peer": true, "engines": { "node": ">=12" }, @@ -836,6 +908,7 @@ "version": "19.2.1", "resolved": "https://registry.npmjs.org/react/-/react-19.2.1.tgz", "integrity": "sha512-DGrYcCWK7tvYMnWh79yrPHt+vdx9tY+1gPZa7nJQtO/p8bLTDaHp4dzwEhQB7pZ4Xe3ok4XKuEPrVuc+wlpkmw==", + "peer": true, "engines": { "node": ">=0.10.0" } @@ -877,6 +950,7 @@ "version": "7.2.6", "resolved": "https://registry.npmjs.org/vite/-/vite-7.2.6.tgz", "integrity": "sha512-tI2l/nFHC5rLh7+5+o7QjKjSR04ivXDF4jcgV0f/bTQ+OJiITy5S6gaynVsEM+7RqzufMnVbIon6Sr5x1SDYaQ==", + "peer": true, "dependencies": { "esbuild": "^0.25.0", "fdir": "^6.5.0", @@ -1122,6 +1196,7 @@ "resolved": "https://registry.npmjs.org/@algolia/client-search/-/client-search-5.29.0.tgz", "integrity": "sha512-cZ0Iq3OzFUPpgszzDr1G1aJV5UMIZ4VygJ2Az252q4Rdf5cQMhYEIKArWY/oUjMhQmosM8ygOovNq7gvA9CdCg==", "license": "MIT", + "peer": true, "dependencies": { "@algolia/client-common": "5.29.0", "@algolia/requester-browser-xhr": "5.29.0", @@ -1298,6 +1373,7 @@ "version": "7.28.5", "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.28.5.tgz", "integrity": "sha512-e7jT4DxYvIDLk1ZHmU/m/mB19rex9sv0c2ftBtjSBv+kVM/902eh0fINUzD7UwLLNR+jU585GxUJ8/EBfAM5fw==", + "peer": true, "dependencies": { "@babel/code-frame": "^7.27.1", "@babel/generator": "^7.28.5", @@ -3505,6 +3581,7 @@ } ], "license": "MIT", + "peer": true, "engines": { "node": ">=18" }, @@ -3527,6 +3604,7 @@ } ], "license": "MIT", + "peer": true, "engines": { "node": ">=18" } @@ -3607,6 +3685,7 @@ "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-7.1.0.tgz", "integrity": "sha512-8sLjZwK0R+JlxlYcTuVnyT2v+htpdrjDOKuMcOVdYjt52Lh8hWRYpxBPoKx/Zg+bcjc3wx6fmQevMmUztS/ccA==", "license": "MIT", + "peer": true, "dependencies": { "cssesc": "^3.0.0", "util-deprecate": "^1.0.2" @@ -3970,6 +4049,7 @@ "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-7.1.0.tgz", "integrity": "sha512-8sLjZwK0R+JlxlYcTuVnyT2v+htpdrjDOKuMcOVdYjt52Lh8hWRYpxBPoKx/Zg+bcjc3wx6fmQevMmUztS/ccA==", "license": "MIT", + "peer": true, "dependencies": { "cssesc": "^3.0.0", "util-deprecate": "^1.0.2" @@ -4970,6 +5050,7 @@ "resolved": "https://registry.npmjs.org/@docusaurus/plugin-content-docs/-/plugin-content-docs-3.8.1.tgz", "integrity": "sha512-oByRkSZzeGNQByCMaX+kif5Nl2vmtj2IHQI2fWjCfCootsdKZDPFLonhIp5s3IGJO7PLUfe0POyw0Xh/RrGXJA==", "license": "MIT", + "peer": true, "dependencies": { "@docusaurus/core": "3.8.1", "@docusaurus/logger": "3.8.1", @@ -8313,6 +8394,7 @@ "resolved": "https://registry.npmjs.org/@mdx-js/react/-/react-3.1.0.tgz", "integrity": "sha512-QjHtSaoameoalGnKDT3FoIl4+9RwyTmo9ZJGBdLOks/YOiWHoRDI3PUwEzOE7kEmGcV3AFcp9K6dYu9rEuKLAQ==", "license": "MIT", + "peer": true, "dependencies": { "@types/mdx": "^2.0.0" }, @@ -9464,6 +9546,32 @@ "micromark-util-symbol": "^1.0.1" } }, + "node_modules/@stylexjs/babel-plugin": { + "version": "0.15.4", + "resolved": "https://registry.npmjs.org/@stylexjs/babel-plugin/-/babel-plugin-0.15.4.tgz", + "integrity": "sha512-QfL2j3VCU+KTyIEoPBhdwq8KW1Gv0FVvwSzfjjdQ0J2Ei/DJBx2AXVsDYzBcVw0WI+KsEfQUHFZ1Fk2t1U/9Iw==", + "license": "MIT", + "dependencies": { + "@babel/core": "^7.26.8", + "@babel/helper-module-imports": "^7.25.9", + "@babel/traverse": "^7.26.8", + "@babel/types": "^7.26.8", + "@dual-bundle/import-meta-resolve": "^4.1.0", + "@stylexjs/stylex": "0.15.4", + "postcss-value-parser": "^4.1.0" + } + }, + "node_modules/@stylexjs/stylex": { + "version": "0.15.4", + "resolved": "https://registry.npmjs.org/@stylexjs/stylex/-/stylex-0.15.4.tgz", + "integrity": "sha512-UQT75p3qxwCIsVpH87YfgHvzWGDyMbQcFKhguj4noBZCc+npEh5h7J4BIHMgrxpyJa9mislLfXv+M5TmP2YH5A==", + "license": "MIT", + "dependencies": { + "css-mediaquery": "^0.1.2", + "invariant": "^2.2.4", + "styleq": "0.2.1" + } + }, "node_modules/@svgr/babel-plugin-add-jsx-attribute": { "version": "8.0.0", "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-add-jsx-attribute/-/babel-plugin-add-jsx-attribute-8.0.0.tgz", @@ -9623,6 +9731,7 @@ "resolved": "https://registry.npmjs.org/@svgr/core/-/core-8.1.0.tgz", "integrity": "sha512-8QqtOQT5ACVlmsvKOJNEaWmRPmcojMOzCz4Hs2BGG/toAp/K38LcsMRyLp349glq5AzJbCEeimEoxaX6v/fLrA==", "license": "MIT", + "peer": true, "dependencies": { "@babel/core": "^7.21.3", "@svgr/babel-preset": "8.1.0", @@ -9772,7 +9881,6 @@ "integrity": "sha512-pemlzrSESWbdAloYml3bAJMEfNh1Z7EduzqPKprCH5S341frlpYnUEW0H72dLxa6IsYr+mPno20GiSm+h9dEdQ==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@babel/code-frame": "^7.10.4", "@babel/runtime": "^7.12.5", @@ -9793,7 +9901,6 @@ "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", "dev": true, "license": "MIT", - "peer": true, "engines": { "node": ">=10" }, @@ -9807,7 +9914,6 @@ "integrity": "sha512-Qb1gy5OrP5+zDf2Bvnzdl3jsTf1qXVMazbvCoKhtKqVs4/YK4ozX4gKQJJVyNe+cajNPn0KoC0MC3FUmaHWEmQ==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "ansi-regex": "^5.0.1", "ansi-styles": "^5.0.0", @@ -9822,8 +9928,7 @@ "resolved": "https://registry.npmjs.org/react-is/-/react-is-17.0.2.tgz", "integrity": "sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==", "dev": true, - "license": "MIT", - "peer": true + "license": "MIT" }, "node_modules/@testing-library/react": { "version": "16.3.0", @@ -9888,14 +9993,14 @@ "resolved": "https://registry.npmjs.org/@types/aria-query/-/aria-query-5.0.4.tgz", "integrity": "sha512-rfT93uj5s0PRL7EzccGMs3brplhcrghnDoV26NqKhCAS1hVo+WdNsPvE/yb6ilfr5hi2MEk6d5EWJTKdxg8jVw==", "dev": true, - "license": "MIT", - "peer": true + "license": "MIT" }, "node_modules/@types/babel__core": { "version": "7.20.5", "resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.20.5.tgz", "integrity": "sha512-qoQprZvz5wQFJwMDqeseRXWv3rqMvhgpbXFfVyWhbx9X47POIA6i/+dXefEmZKoAgOaTdaIgNSMqMIU61yRyzA==", "license": "MIT", + "peer": true, "dependencies": { "@babel/parser": "^7.20.7", "@babel/types": "^7.20.7", @@ -10242,6 +10347,7 @@ "version": "19.2.7", "resolved": "https://registry.npmjs.org/@types/react/-/react-19.2.7.tgz", "integrity": "sha512-MWtvHrGZLFttgeEj28VXHxpmwYbor/ATPYbBfSFZEIRK0ecCFLl2Qo55z52Hss+UV9CRN7trSeq1zbgx7YDWWg==", + "peer": true, "dependencies": { "csstype": "^3.2.2" } @@ -11447,6 +11553,7 @@ "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.15.0.tgz", "integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==", "license": "MIT", + "peer": true, "bin": { "acorn": "bin/acorn" }, @@ -11530,6 +11637,7 @@ "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", "license": "MIT", + "peer": true, "dependencies": { "fast-deep-equal": "^3.1.1", "fast-json-stable-stringify": "^2.0.0", @@ -11594,6 +11702,7 @@ "resolved": "https://registry.npmjs.org/algoliasearch/-/algoliasearch-5.29.0.tgz", "integrity": "sha512-E2l6AlTWGznM2e7vEE6T6hzObvEyXukxMOlBmVlMyixZyK1umuO/CiVc6sDBbzVH0oEviCE5IfVY1oZBmccYPQ==", "license": "MIT", + "peer": true, "dependencies": { "@algolia/client-abtesting": "5.29.0", "@algolia/client-analytics": "5.29.0", @@ -11625,6 +11734,10 @@ "algoliasearch": ">= 3.1 < 6" } }, + "node_modules/animated-example": { + "resolved": "apps/animated-example", + "link": true + }, "node_modules/anser": { "version": "1.4.10", "resolved": "https://registry.npmjs.org/anser/-/anser-1.4.10.tgz", @@ -11766,7 +11879,6 @@ "integrity": "sha512-b0P0sZPKtyu8HkeRAfCq0IfURZK+SuwMjY1UXGBU27wpAiTwQAIlq56IbIO+ytk/JjS1fMR14ee5WBBfKi5J6A==", "dev": true, "license": "Apache-2.0", - "peer": true, "dependencies": { "dequal": "^2.0.3" } @@ -12788,6 +12900,7 @@ "url": "https://github.com/sponsors/ai" } ], + "peer": true, "dependencies": { "baseline-browser-mapping": "^2.9.0", "caniuse-lite": "^1.0.30001759", @@ -14184,6 +14297,7 @@ "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-7.1.0.tgz", "integrity": "sha512-8sLjZwK0R+JlxlYcTuVnyT2v+htpdrjDOKuMcOVdYjt52Lh8hWRYpxBPoKx/Zg+bcjc3wx6fmQevMmUztS/ccA==", "license": "MIT", + "peer": true, "dependencies": { "cssesc": "^3.0.0", "util-deprecate": "^1.0.2" @@ -15233,8 +15347,7 @@ "resolved": "https://registry.npmjs.org/dom-accessibility-api/-/dom-accessibility-api-0.5.16.tgz", "integrity": "sha512-X7BJ2yElsnOJ30pZF4uIIDfBEVgF4XEBxL9Bxhy6dnrm5hkzqmsWHGTiHqRiITNhMyFLyAiWndIJP7Z1NTteDg==", "dev": true, - "license": "MIT", - "peer": true + "license": "MIT" }, "node_modules/dom-converter": { "version": "0.2.0", @@ -15847,6 +15960,7 @@ "integrity": "sha512-ypowyDxpVSYpkXr9WPv2PAZCtNip1Mv5KTW0SCurXv/9iOpcrH9PaqUElksqEB6pChqHGDRCFTyrZlGhnLNGiA==", "deprecated": "This version is no longer supported. Please see https://eslint.org/version-support for other options.", "license": "MIT", + "peer": true, "dependencies": { "@eslint-community/eslint-utils": "^4.2.0", "@eslint-community/regexpp": "^4.6.1", @@ -15961,6 +16075,7 @@ "integrity": "sha512-VGMpFQGUQWYT9LfnPcX8ouFojyrZ/2w3K5BucvxL/spdNehccKhB4jUyB1yBCXpr2XFm0jkECxgrpXBW2ipoAw==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@typescript-eslint/scope-manager": "8.44.0", "@typescript-eslint/types": "8.44.0", @@ -16253,6 +16368,7 @@ "integrity": "sha512-whOE1HFo/qJDyX4SnXzP4N6zOWn79WhnCUY/iDR0mPfQZO8wcYE4JClzI2oZrhBnnMUCBCHZhO6VQyoBU95mZA==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@rtsao/scc": "^1.1.0", "array-includes": "^3.1.9", @@ -16944,6 +17060,7 @@ "resolved": "https://registry.npmjs.org/expo/-/expo-53.0.22.tgz", "integrity": "sha512-sJ2I4W/e5iiM4u/wYCe3qmW4D7WPCRqByPDD0hJcdYNdjc9HFFFdO4OAudZVyC/MmtoWZEIH5kTJP1cw9FjzYA==", "license": "MIT", + "peer": true, "dependencies": { "@babel/runtime": "^7.20.0", "@expo/cli": "0.24.21", @@ -17082,6 +17199,7 @@ "resolved": "https://registry.npmjs.org/expo-font/-/expo-font-13.3.2.tgz", "integrity": "sha512-wUlMdpqURmQ/CNKK/+BIHkDA5nGjMqNlYmW0pJFXY/KE/OG80Qcavdu2sHsL4efAIiNGvYdBS10WztuQYU4X0A==", "license": "MIT", + "peer": true, "dependencies": { "fontfaceobserver": "^2.1.0" }, @@ -19066,6 +19184,7 @@ "integrity": "sha512-3ljktN2ek+bRRsPAcMeqMEJou6s2MRe6VuLkLsXDXuVrJfRZ7V2VUw41T9uAt9lcA2xaJP4yykYAnMg15nsRPw==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "esrecurse": "^4.3.0", "hermes-estree": "0.32.1", @@ -22720,7 +22839,6 @@ "integrity": "sha512-h5bgJWpxJNswbU7qCrV0tIKQCaS3blPDrqKWx+QxzuzL1zGUzij9XCWLrSLsJPu5t+eWA/ycetzYAO5IOMcWAQ==", "dev": true, "license": "MIT", - "peer": true, "bin": { "lz-string": "bin/bin.js" } @@ -27414,6 +27532,7 @@ } ], "license": "MIT", + "peer": true, "dependencies": { "nanoid": "^3.3.11", "picocolors": "^1.1.1", @@ -28329,6 +28448,7 @@ "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-7.1.0.tgz", "integrity": "sha512-8sLjZwK0R+JlxlYcTuVnyT2v+htpdrjDOKuMcOVdYjt52Lh8hWRYpxBPoKx/Zg+bcjc3wx6fmQevMmUztS/ccA==", "license": "MIT", + "peer": true, "dependencies": { "cssesc": "^3.0.0", "util-deprecate": "^1.0.2" @@ -28881,6 +29001,7 @@ "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.6.0.tgz", "integrity": "sha512-ujSB9uXHJKzM/2GBuE0hBOUgC77CN3Bnpqa+g80bkv3T3A93wL/xlzDATHhnhkzifz/UE2SNOvmbTz5hSkDlHw==", "license": "MIT", + "peer": true, "bin": { "prettier": "bin/prettier.cjs" }, @@ -28897,6 +29018,7 @@ "integrity": "sha512-B5vzcDyTA/T0R7LGMSkLTp3VtRCEe1NItzsM6L/4gDOBGzDDMMMOwxRxogwL9xL07GPBOJrzlggwFaSQOhLVLw==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "hermes-estree": "0.25.0", "hermes-parser": "0.25.0", @@ -29302,6 +29424,7 @@ "resolved": "https://registry.npmjs.org/react/-/react-19.0.0.tgz", "integrity": "sha512-V8AVnmPIICiWpGfm6GLzCR/W5FXLchHop40W4nXBmdlEceh16rCN8O8LNWm5bh5XUX91fh7KpA+W0TgMKmgTpQ==", "license": "MIT", + "peer": true, "engines": { "node": ">=0.10.0" } @@ -29342,6 +29465,7 @@ "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-19.0.0.tgz", "integrity": "sha512-4GV5sHFG0e/0AD4X+ySy6UJd3jVl1iNsNHdpad0qhABJ11twS3TTBnseqsKurKcsNqCEFeGL3uLpVChpIO3QfQ==", "license": "MIT", + "peer": true, "dependencies": { "scheduler": "^0.25.0" }, @@ -29403,6 +29527,7 @@ "resolved": "https://registry.npmjs.org/@docusaurus/react-loadable/-/react-loadable-6.0.0.tgz", "integrity": "sha512-YMMxTUQV/QFSnbgrP3tjDzLHRg7vsbMn8e9HAa8o/1iXoiomo48b7sk/kkmWEuWNDPJVlKSJRB6Y2fHqdJk+SQ==", "license": "MIT", + "peer": true, "dependencies": { "@types/react": "*" }, @@ -29431,6 +29556,7 @@ "resolved": "https://registry.npmjs.org/react-native/-/react-native-0.79.5.tgz", "integrity": "sha512-jVihwsE4mWEHZ9HkO1J2eUZSwHyDByZOqthwnGrVZCh6kTQBCm4v8dicsyDa6p0fpWNE5KicTcpX/XXl0ASJFg==", "license": "MIT", + "peer": true, "dependencies": { "@jest/create-cache-key-function": "^29.7.0", "@react-native/assets-registry": "0.79.5", @@ -29505,6 +29631,15 @@ "react-native": "*" } }, + "node_modules/react-native-safe-area-context": { + "version": "5.4.0", + "resolved": "https://registry.npmjs.org/react-native-safe-area-context/-/react-native-safe-area-context-5.4.0.tgz", + "integrity": "sha512-JaEThVyJcLhA+vU0NU8bZ0a1ih6GiF4faZ+ArZLqpYbL6j7R3caRqj+mE3lEtKCuHgwjLg3bCxLL1GPUJZVqUA==", + "peerDependencies": { + "react": "*", + "react-native": "*" + } + }, "node_modules/react-native-web": { "version": "0.20.0", "resolved": "https://registry.npmjs.org/react-native-web/-/react-native-web-0.20.0.tgz", @@ -29662,6 +29797,7 @@ "resolved": "https://registry.npmjs.org/react-router/-/react-router-5.3.4.tgz", "integrity": "sha512-Ys9K+ppnJah3QuaRiLxk+jDWOR1MekYQrlytiXxC1RyfbdsZkS5pvKAzCCr031xHixZwpnsYNT5xysdFHQaYsA==", "license": "MIT", + "peer": true, "dependencies": { "@babel/runtime": "^7.12.13", "history": "^4.9.0", @@ -31082,6 +31218,7 @@ "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.17.1.tgz", "integrity": "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==", "license": "MIT", + "peer": true, "dependencies": { "fast-deep-equal": "^3.1.3", "fast-uri": "^3.0.1", @@ -33070,6 +33207,7 @@ "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz", "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", "license": "MIT", + "peer": true, "engines": { "node": ">=12" }, @@ -33489,6 +33627,7 @@ "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.3.2.tgz", "integrity": "sha512-6l+RyNy7oAHDfxC4FzSJcz9vnjTKxrLpDG5M2Vu4SHRVNg6xzqZp6LYSR9zjqQTu8DU/f5xwxUdADOkbrIX2gQ==", "license": "Apache-2.0", + "peer": true, "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" @@ -33554,6 +33693,7 @@ "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.48.1.tgz", "integrity": "sha512-PC0PDZfJg8sP7cmKe6L3QIL8GZwU5aRvUFedqSIpw3B+QjRSUZeeITC2M5XKeMXEzL6wccN196iy3JLwKNvDVA==", "dev": true, + "peer": true, "dependencies": { "@typescript-eslint/scope-manager": "8.48.1", "@typescript-eslint/types": "8.48.1", @@ -34519,6 +34659,7 @@ "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.99.9.tgz", "integrity": "sha512-brOPwM3JnmOa+7kd3NsmOUOwbDAj8FT9xDsG3IW0MgbN9yZV7Oi/s/+MNQ/EcSMqw7qfoRyXPoeEWT8zLVdVGg==", "license": "MIT", + "peer": true, "dependencies": { "@types/eslint-scope": "^3.7.7", "@types/estree": "^1.0.6", @@ -35358,6 +35499,7 @@ "integrity": "sha512-2eHWfjaoXgTBC2jNM1LRef62VQa0umtvRiDSk6HSzW7RvS5YtkabJrwYLLEKWBc8a5U2PTSCs+dJjUTJdlHsWQ==", "devOptional": true, "license": "ISC", + "peer": true, "engines": { "node": ">= 14" } @@ -35446,6 +35588,7 @@ "integrity": "sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ==", "dev": true, "license": "MIT", + "peer": true, "funding": { "url": "https://github.com/sponsors/colinhacks" } @@ -35541,32 +35684,6 @@ "postcss": "^8.4.49" } }, - "packages/postcss-react-strict-dom/node_modules/@stylexjs/babel-plugin": { - "version": "0.15.4", - "resolved": "https://registry.npmjs.org/@stylexjs/babel-plugin/-/babel-plugin-0.15.4.tgz", - "integrity": "sha512-QfL2j3VCU+KTyIEoPBhdwq8KW1Gv0FVvwSzfjjdQ0J2Ei/DJBx2AXVsDYzBcVw0WI+KsEfQUHFZ1Fk2t1U/9Iw==", - "license": "MIT", - "dependencies": { - "@babel/core": "^7.26.8", - "@babel/helper-module-imports": "^7.25.9", - "@babel/traverse": "^7.26.8", - "@babel/types": "^7.26.8", - "@dual-bundle/import-meta-resolve": "^4.1.0", - "@stylexjs/stylex": "0.15.4", - "postcss-value-parser": "^4.1.0" - } - }, - "packages/postcss-react-strict-dom/node_modules/@stylexjs/stylex": { - "version": "0.15.4", - "resolved": "https://registry.npmjs.org/@stylexjs/stylex/-/stylex-0.15.4.tgz", - "integrity": "sha512-UQT75p3qxwCIsVpH87YfgHvzWGDyMbQcFKhguj4noBZCc+npEh5h7J4BIHMgrxpyJa9mislLfXv+M5TmP2YH5A==", - "license": "MIT", - "dependencies": { - "css-mediaquery": "^0.1.2", - "invariant": "^2.2.4", - "styleq": "0.2.1" - } - }, "packages/postcss-react-strict-dom/node_modules/glob-parent": { "version": "6.0.2", "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", @@ -35632,32 +35749,6 @@ "react-native": ">=0.79.5" } }, - "packages/react-strict-dom/node_modules/@stylexjs/babel-plugin": { - "version": "0.15.4", - "resolved": "https://registry.npmjs.org/@stylexjs/babel-plugin/-/babel-plugin-0.15.4.tgz", - "integrity": "sha512-QfL2j3VCU+KTyIEoPBhdwq8KW1Gv0FVvwSzfjjdQ0J2Ei/DJBx2AXVsDYzBcVw0WI+KsEfQUHFZ1Fk2t1U/9Iw==", - "license": "MIT", - "dependencies": { - "@babel/core": "^7.26.8", - "@babel/helper-module-imports": "^7.25.9", - "@babel/traverse": "^7.26.8", - "@babel/types": "^7.26.8", - "@dual-bundle/import-meta-resolve": "^4.1.0", - "@stylexjs/stylex": "0.15.4", - "postcss-value-parser": "^4.1.0" - } - }, - "packages/react-strict-dom/node_modules/@stylexjs/stylex": { - "version": "0.15.4", - "resolved": "https://registry.npmjs.org/@stylexjs/stylex/-/stylex-0.15.4.tgz", - "integrity": "sha512-UQT75p3qxwCIsVpH87YfgHvzWGDyMbQcFKhguj4noBZCc+npEh5h7J4BIHMgrxpyJa9mislLfXv+M5TmP2YH5A==", - "license": "MIT", - "dependencies": { - "css-mediaquery": "^0.1.2", - "invariant": "^2.2.4", - "styleq": "0.2.1" - } - }, "packages/scripts": { "name": "react-strict-dom-scripts", "version": "0.0.55",