diff --git a/bun.lock b/bun.lock index 39948b0f..2494695e 100644 --- a/bun.lock +++ b/bun.lock @@ -9,7 +9,7 @@ "eslint": "9.39.1", "eslint-config-codemask": "2.2.1", "husky": "9.1.7", - "turbo": "2.6.3", + "turbo": "2.7.6", "typescript": "catalog:", }, }, @@ -126,7 +126,7 @@ }, "packages/uniwind": { "name": "uniwind", - "version": "1.2.2", + "version": "1.2.7", "dependencies": { "@tailwindcss/node": "4.1.17", "@tailwindcss/oxide": "4.1.17", @@ -2463,19 +2463,19 @@ "tslib": ["tslib@2.8.1", "", {}, "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w=="], - "turbo": ["turbo@2.6.3", "", { "optionalDependencies": { "turbo-darwin-64": "2.6.3", "turbo-darwin-arm64": "2.6.3", "turbo-linux-64": "2.6.3", "turbo-linux-arm64": "2.6.3", "turbo-windows-64": "2.6.3", "turbo-windows-arm64": "2.6.3" }, "bin": { "turbo": "bin/turbo" } }, "sha512-bf6YKUv11l5Xfcmg76PyWoy/e2vbkkxFNBGJSnfdSXQC33ZiUfutYh6IXidc5MhsnrFkWfdNNLyaRk+kHMLlwA=="], + "turbo": ["turbo@2.7.6", "", { "optionalDependencies": { "turbo-darwin-64": "2.7.6", "turbo-darwin-arm64": "2.7.6", "turbo-linux-64": "2.7.6", "turbo-linux-arm64": "2.7.6", "turbo-windows-64": "2.7.6", "turbo-windows-arm64": "2.7.6" }, "bin": { "turbo": "bin/turbo" } }, "sha512-PO9AvJLEsNLO+EYhF4zB+v10hOjsJe5kJW+S6tTbRv+TW7gf1Qer4mfjP9h3/y9h8ZiPvOrenxnEgDtFgaM5zw=="], - "turbo-darwin-64": ["turbo-darwin-64@2.6.3", "", { "os": "darwin", "cpu": "x64" }, "sha512-BlJJDc1CQ7SK5Y5qnl7AzpkvKSnpkfPmnA+HeU/sgny3oHZckPV2776ebO2M33CYDSor7+8HQwaodY++IINhYg=="], + "turbo-darwin-64": ["turbo-darwin-64@2.7.6", "", { "os": "darwin", "cpu": "x64" }, "sha512-bYu0qnWju2Ha3EbIkPCk1SMLT3sltKh1P/Jy5FER6BmH++H5z+T5MHh3W1Xoers9rk4N1VdKvog9FO1pxQyjhw=="], - "turbo-darwin-arm64": ["turbo-darwin-arm64@2.6.3", "", { "os": "darwin", "cpu": "arm64" }, "sha512-MwVt7rBKiOK7zdYerenfCRTypefw4kZCue35IJga9CH1+S50+KTiCkT6LBqo0hHeoH2iKuI0ldTF2a0aB72z3w=="], + "turbo-darwin-arm64": ["turbo-darwin-arm64@2.7.6", "", { "os": "darwin", "cpu": "arm64" }, "sha512-KCxTf3Y1hgNLYIWRLw8bwH8Zie9RyCGoxAlXYsCBI/YNqBSR+ZZK9KYzFxAqDaVaNvTwLFv3rJRGsXOFWg4+Uw=="], - "turbo-linux-64": ["turbo-linux-64@2.6.3", "", { "os": "linux", "cpu": "x64" }, "sha512-cqpcw+dXxbnPtNnzeeSyWprjmuFVpHJqKcs7Jym5oXlu/ZcovEASUIUZVN3OGEM6Y/OTyyw0z09tOHNt5yBAVg=="], + "turbo-linux-64": ["turbo-linux-64@2.7.6", "", { "os": "linux", "cpu": "x64" }, "sha512-vjoU8zIfNgvJR3cMitgw7inEoi6bmuVuFawDl5yKtxjAEhDktFdRBpGS3WojD4l3BklBbIK689ssXcGf21LxRA=="], - "turbo-linux-arm64": ["turbo-linux-arm64@2.6.3", "", { "os": "linux", "cpu": "arm64" }, "sha512-MterpZQmjXyr4uM7zOgFSFL3oRdNKeflY7nsjxJb2TklsYqiu3Z9pQ4zRVFFH8n0mLGna7MbQMZuKoWqqHb45w=="], + "turbo-linux-arm64": ["turbo-linux-arm64@2.7.6", "", { "os": "linux", "cpu": "arm64" }, "sha512-TcMpBvTqZf+1DptrVYLbZls7WY1UVNDTGaf0bo7/GCgWYv5eZHCVo4Td7kCJeDU4glbXg67REX0md0S0V6ghMg=="], - "turbo-windows-64": ["turbo-windows-64@2.6.3", "", { "os": "win32", "cpu": "x64" }, "sha512-biDU70v9dLwnBdLf+daoDlNJVvqOOP8YEjqNipBHzgclbQlXbsi6Gqqelp5er81Qo3BiRgmTNx79oaZQTPb07Q=="], + "turbo-windows-64": ["turbo-windows-64@2.7.6", "", { "os": "win32", "cpu": "x64" }, "sha512-1/MhkYldiihjneY8QnnDMbAkHXn/udTWSVYS94EMlkE9AShozsLTTOT1gDOpX06EfEW5njP09suhMvxbvwuwpQ=="], - "turbo-windows-arm64": ["turbo-windows-arm64@2.6.3", "", { "os": "win32", "cpu": "arm64" }, "sha512-dDHVKpSeukah3VsI/xMEKeTnV9V9cjlpFSUs4bmsUiLu3Yv2ENlgVEZv65wxbeE0bh0jjpmElDT+P1KaCxArQQ=="], + "turbo-windows-arm64": ["turbo-windows-arm64@2.7.6", "", { "os": "win32", "cpu": "arm64" }, "sha512-0wDVnUJLFAWm4ZzOQFDkbyyUqaszorTGf3Rdc22IRIyJTTLd6ajqdb+cWD89UZ1RKr953+PZR1gqgWQY4PDuhA=="], "type-check": ["type-check@0.4.0", "", { "dependencies": { "prelude-ls": "^1.2.1" } }, "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew=="], diff --git a/package.json b/package.json index d07ffad7..c4bd61c9 100644 --- a/package.json +++ b/package.json @@ -38,7 +38,7 @@ "eslint": "9.39.1", "eslint-config-codemask": "2.2.1", "husky": "9.1.7", - "turbo": "2.6.3" + "turbo": "2.7.6" }, "packageManager": "bun@1.3.2", "trustedDependencies": [ diff --git a/packages/uniwind/eslint.config.ts b/packages/uniwind/eslint.config.ts index c36981fd..b10a377b 100644 --- a/packages/uniwind/eslint.config.ts +++ b/packages/uniwind/eslint.config.ts @@ -17,6 +17,12 @@ export default [ 'nested-if/nested-if-statements': 'off', 'no-continue': 'off', '@typescript-eslint/array-type': 'off', + '@typescript-eslint/no-unused-vars': ['error', { + 'argsIgnorePattern': '^_', + 'varsIgnorePattern': '^_', + 'caughtErrorsIgnorePattern': '^_', + }], + 'no-bitwise': 'off', }, }, { diff --git a/packages/uniwind/src/components/native/ActivityIndicator.tsx b/packages/uniwind/src/components/native/ActivityIndicator.tsx index bbd30d93..a185448a 100644 --- a/packages/uniwind/src/components/native/ActivityIndicator.tsx +++ b/packages/uniwind/src/components/native/ActivityIndicator.tsx @@ -1,11 +1,10 @@ import { ActivityIndicator as RNActivityIndicator, ActivityIndicatorProps } from 'react-native' -import { useUniwindAccent } from '../../hooks' import { copyComponentProperties } from '../utils' import { useStyle } from './useStyle' export const ActivityIndicator = copyComponentProperties(RNActivityIndicator, (props: ActivityIndicatorProps) => { - const style = useStyle(props.className) - const color = useUniwindAccent(props.colorClassName) + const style = useStyle(props.className, props) + const color = useStyle(props.colorClassName, props).accentColor return ( { - const color = useUniwindAccent(props.colorClassName) + const color = useStyle(props.colorClassName, props).accentColor return ( ) => { - const style = useStyle(props.className) - const styleColumnWrapper = useStyle(props.columnWrapperClassName) - const styleContentContainer = useStyle(props.contentContainerClassName) - const styleListFooterComponent = useStyle(props.ListFooterComponentClassName) - const styleListHeaderComponent = useStyle(props.ListHeaderComponentClassName) - const endFillColor = useUniwindAccent(props.endFillColorClassName) + const style = useStyle(props.className, props) + const styleColumnWrapper = useStyle(props.columnWrapperClassName, props) + const styleContentContainer = useStyle(props.contentContainerClassName, props) + const styleListFooterComponent = useStyle(props.ListFooterComponentClassName, props) + const styleListHeaderComponent = useStyle(props.ListHeaderComponentClassName, props) + const endFillColor = useStyle(props.endFillColorClassName, props).accentColor const hasSingleColumn = !('numColumns' in props) || props.numColumns === 1 return ( diff --git a/packages/uniwind/src/components/native/Image.tsx b/packages/uniwind/src/components/native/Image.tsx index a0a7c4e0..678cdc4a 100644 --- a/packages/uniwind/src/components/native/Image.tsx +++ b/packages/uniwind/src/components/native/Image.tsx @@ -1,11 +1,10 @@ import { Image as RNImage, ImageProps } from 'react-native' -import { useUniwindAccent } from '../../hooks' import { copyComponentProperties } from '../utils' import { useStyle } from './useStyle' export const Image = copyComponentProperties(RNImage, (props: ImageProps) => { - const style = useStyle(props.className) - const tintColor = useUniwindAccent(props.tintColorClassName) + const style = useStyle(props.className, props) + const tintColor = useStyle(props.tintColorClassName, props).accentColor return ( { - const style = useStyle(props.className) - const imageStyle = useStyle(props.imageClassName) - const tintColor = useUniwindAccent(props.tintColorClassName) + const style = useStyle(props.className, props) + const imageStyle = useStyle(props.imageClassName, props) + const tintColor = useStyle(props.tintColorClassName, props).accentColor return ( }) => { - const style = useStyle(props.className) - const backgroundColor = useUniwindAccent(props.backgroundColorClassName) + const style = useStyle(props.className, props) + const backgroundColor = useStyle(props.backgroundColorClassName, props).accentColor return ( { - const style = useStyle(props.className) - const contentContainerStyle = useStyle(props.contentContainerClassName) + const style = useStyle(props.className, props) + const contentContainerStyle = useStyle(props.contentContainerClassName, props) return ( { - const style = useStyle(props.className) - const backdropColor = useUniwindAccent(props.backdropColorClassName) + const style = useStyle(props.className, props) + const backdropColor = useStyle(props.backdropColorClassName, props).accentColor return ( { - const style = useStyle(props.className, { - isDisabled: Boolean(props.disabled), - }) + const style = useStyle( + props.className, + props, + { + isDisabled: Boolean(props.disabled), + }, + ) return ( { - const style = useStyle(props.className) - const color = useUniwindAccent(props.colorsClassName) - const tintColor = useUniwindAccent(props.tintColorClassName) - const titleColor = useUniwindAccent(props.titleColorClassName) - const progressBackgroundColor = useUniwindAccent(props.progressBackgroundColorClassName) + const style = useStyle(props.className, props) + const color = useStyle(props.colorsClassName, props).accentColor + const tintColor = useStyle(props.tintColorClassName, props).accentColor + const titleColor = useStyle(props.titleColorClassName, props).accentColor + const progressBackgroundColor = useStyle(props.progressBackgroundColorClassName, props).accentColor return ( { - const style = useStyle(props.className) + const style = useStyle(props.className, props) return ( { - const style = useStyle(props.className) - const contentContainerStyle = useStyle(props.contentContainerClassName) - const endFillColor = useUniwindAccent(props.endFillColorClassName) + const style = useStyle(props.className, props) + const contentContainerStyle = useStyle(props.contentContainerClassName, props) + const endFillColor = useStyle(props.endFillColorClassName, props).accentColor return ( ) => { - const style = useStyle(props.className) - const contentContainerStyle = useStyle(props.contentContainerClassName) - const listFooterComponentStyle = useStyle(props.ListFooterComponentClassName) - const listHeaderComponentStyle = useStyle(props.ListHeaderComponentClassName) - const endFillColor = useUniwindAccent(props.endFillColorClassName) + const style = useStyle(props.className, props) + const contentContainerStyle = useStyle(props.contentContainerClassName, props) + const listFooterComponentStyle = useStyle(props.ListFooterComponentClassName, props) + const listHeaderComponentStyle = useStyle(props.ListHeaderComponentClassName, props) + const endFillColor = useStyle(props.endFillColorClassName, props).accentColor return ( const state = { isDisabled: Boolean(props.disabled), } satisfies ComponentState - const style = useStyle(props.className, state) - const trackColorOn = useUniwindAccent(props.trackColorOnClassName, state) - const trackColorOff = useUniwindAccent(props.trackColorOffClassName, state) - const thumbColor = useUniwindAccent(props.thumbColorClassName, state) - const ios_backgroundColor = useUniwindAccent(props.ios_backgroundColorClassName, state) + const style = useStyle(props.className, props, state) + const trackColorOn = useStyle(props.trackColorOnClassName, props, state).accentColor + const trackColorOff = useStyle(props.trackColorOffClassName, props, state).accentColor + const thumbColor = useStyle(props.thumbColorClassName, props, state).accentColor + const ios_backgroundColor = useStyle(props.ios_backgroundColorClassName, props, state).accentColor return ( { isPressed, isDisabled: Boolean(props.disabled), } satisfies ComponentState - const style = useStyle(props.className, state) - const selectionColor = useUniwindAccent(props.selectionColorClassName, state) + const style = useStyle(props.className, props, state) + const selectionColor = useStyle(props.selectionColorClassName, props, state).accentColor return ( { - const style = useStyle(props.className) + const style = useStyle(props.className, props) return ( ) => { - const style = useStyle(props.className) - const contentContainerStyle = useStyle(props.contentContainerClassName) - const listFooterComponentStyle = useStyle(props.ListFooterComponentClassName) - const listHeaderComponentStyle = useStyle(props.ListHeaderComponentClassName) - const endFillColor = useUniwindAccent(props.endFillColorClassName) + const style = useStyle(props.className, props) + const contentContainerStyle = useStyle(props.contentContainerClassName, props) + const listFooterComponentStyle = useStyle(props.ListFooterComponentClassName, props) + const listHeaderComponentStyle = useStyle(props.ListHeaderComponentClassName, props) + const endFillColor = useStyle(props.endFillColorClassName, props).accentColor return ( } - -export const useStyle = (className?: string, state?: ComponentState) => { +export const useStyle = (className: string | undefined, componentProps: Record, state?: ComponentState) => { + 'use no memo' const [_, rerender] = useReducer(() => ({}), {}) - const styleState = useMemo( - () => - className - ? UniwindStore.getStyles(className, { - isDisabled: state?.isDisabled, - isFocused: state?.isFocused, - isPressed: state?.isPressed, - }) - : emptyState, - [className, _, state?.isDisabled, state?.isFocused, state?.isPressed], - ) + const styleState = UniwindStore.getStyles(className, componentProps, state) useEffect(() => { if (__DEV__ || styleState.dependencies.length > 0) { @@ -27,7 +14,7 @@ export const useStyle = (className?: string, state?: ComponentState) => { return dispose } - }, [styleState]) + }, [styleState.dependencySum]) return styleState.styles } diff --git a/packages/uniwind/src/core/native/store.ts b/packages/uniwind/src/core/native/store.ts index a6d72887..62c6e337 100644 --- a/packages/uniwind/src/core/native/store.ts +++ b/packages/uniwind/src/core/native/store.ts @@ -10,8 +10,11 @@ import { UniwindRuntime } from './runtime' type StylesResult = { styles: RNStyle dependencies: Array + dependencySum: number } +const emptyState: StylesResult = { styles: {}, dependencies: [], dependencySum: 0 } + class UniwindStoreBuilder { runtime = UniwindRuntime vars = {} as Record @@ -20,12 +23,9 @@ class UniwindStoreBuilder { private cache = new Map() private generateStyleSheetCallbackResult: ReturnType | null = null - getStyles(className?: string, state?: ComponentState): StylesResult { + getStyles(className: string | undefined, componentProps?: Record, state?: ComponentState): StylesResult { if (className === undefined || className === '') { - return { - styles: {}, - dependencies: [], - } + return emptyState } const cacheKey = `${className}${state?.isDisabled ?? false}${state?.isFocused ?? false}${state?.isPressed ?? false}` @@ -34,14 +34,17 @@ class UniwindStoreBuilder { return this.cache.get(cacheKey)! } - const result = this.resolveStyles(className, state) + const result = this.resolveStyles(className, componentProps, state) - this.cache.set(cacheKey, result) - UniwindListener.subscribe( - () => this.cache.delete(cacheKey), - result.dependencies, - { once: true }, - ) + // Don't cache styles that depend on data attributes + if (!result.hasDataAttributes) { + this.cache.set(cacheKey, result) + UniwindListener.subscribe( + () => this.cache.delete(cacheKey), + result.dependencies, + { once: true }, + ) + } return result } @@ -80,10 +83,12 @@ class UniwindStoreBuilder { } } - private resolveStyles(classNames: string, state?: ComponentState) { + private resolveStyles(classNames: string, componentProps?: Record, state?: ComponentState) { const result = {} as Record let vars = this.vars + let hasDataAttributes = false const dependencies = new Set() + let dependencySum = 0 const bestBreakpoints = new Map() for (const className of classNames.split(' ')) { @@ -93,7 +98,14 @@ class UniwindStoreBuilder { for (const style of this.stylesheet[className] as Array