react-native-css-interop
Advanced tools
Comparing version
export * from "./web/api"; | ||
export * from "./web/interopComponentsMap"; |
@@ -18,2 +18,3 @@ "use strict"; | ||
__exportStar(require("./web/api"), exports); | ||
__exportStar(require("./web/interopComponentsMap"), exports); | ||
//# sourceMappingURL=api.js.map |
"use strict"; | ||
"use client"; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
@@ -3,0 +4,0 @@ const react_native_1 = require("react-native"); |
@@ -5,33 +5,45 @@ "use strict"; | ||
function getNormalizeConfig(mapping) { | ||
const config = new Map(); | ||
const configs = []; | ||
for (const [source, options] of Object.entries(mapping)) { | ||
let target; | ||
let inlineProp; | ||
let propToRemove; | ||
let nativeStyleToProp; | ||
let removeTarget; | ||
if (!options) | ||
continue; | ||
if (typeof options === "boolean") { | ||
target = source; | ||
if (options === true) { | ||
target = [source]; | ||
} | ||
else if (typeof options === "string") { | ||
target = options; | ||
target = [options]; | ||
} | ||
else if (options.target === false) { | ||
target = source; | ||
nativeStyleToProp = options.nativeStyleToProp; | ||
removeTarget = true; | ||
target = [source]; | ||
propToRemove = source; | ||
nativeStyleToProp = parseNativeStyleToProp(options.nativeStyleToProp); | ||
} | ||
else { | ||
target = options.target === true ? source : options.target; | ||
nativeStyleToProp = options.nativeStyleToProp; | ||
target = options.target === true ? [source] : options.target.split("."); | ||
nativeStyleToProp = parseNativeStyleToProp(options.nativeStyleToProp); | ||
} | ||
config.set(target, { | ||
if (target.length === 1 && target[0] !== source) { | ||
inlineProp = target[0]; | ||
} | ||
configs.push({ | ||
nativeStyleToProp, | ||
source, | ||
target, | ||
removeTarget, | ||
inlineProp, | ||
propToRemove, | ||
}); | ||
} | ||
return Array.from(config.values()); | ||
return configs; | ||
} | ||
function parseNativeStyleToProp(nativeStyleToProp) { | ||
if (!nativeStyleToProp) | ||
return; | ||
return Object.entries(nativeStyleToProp).map(([key, value]) => { | ||
return [key, value === true ? [key] : value.split(".")]; | ||
}); | ||
} | ||
//# sourceMappingURL=config.js.map |
@@ -13,2 +13,3 @@ "use strict"; | ||
const appearance_observables_1 = require("./appearance-observables"); | ||
const utils_1 = require("./utils"); | ||
var stylesheet_1 = require("./stylesheet"); | ||
@@ -54,13 +55,5 @@ Object.defineProperty(exports, "StyleSheet", { enumerable: true, get: function () { return stylesheet_1.StyleSheet; } }); | ||
delete props[config.source]; | ||
let target = props[config.target]; | ||
if (Array.isArray(target)) { | ||
target.push(placeholder); | ||
} | ||
else if (target) { | ||
target = [target, placeholder]; | ||
} | ||
else { | ||
target = placeholder; | ||
} | ||
props[config.target] = target; | ||
(0, utils_1.assignToTarget)(props, placeholder, config, { | ||
objectMergeStyle: "toArray", | ||
}); | ||
} | ||
@@ -67,0 +60,0 @@ props.ref = ref; |
@@ -13,2 +13,3 @@ import { Appearance } from "react-native"; | ||
export declare function cssVariableObservable(value?: ColorSchemeVariableValue, { name }?: ObservableOptions<never>): { | ||
name: string | undefined; | ||
get(effect?: Effect): RuntimeValueDescriptor; | ||
@@ -15,0 +16,0 @@ set(value: ColorSchemeVariableValue | RuntimeValueDescriptor): void; |
@@ -40,2 +40,3 @@ "use strict"; | ||
return { | ||
name, | ||
get(effect) { | ||
@@ -42,0 +43,0 @@ return exports.colorScheme.get(effect) === "light" |
@@ -13,2 +13,3 @@ "use strict"; | ||
const shared_1 = require("../../shared"); | ||
const utils_1 = require("./utils"); | ||
function interop(component, configs, originalProps, ref) { | ||
@@ -19,5 +20,5 @@ const inheritedVariables = (0, react_1.useContext)(styles_1.VariableContext); | ||
const sharedState = (0, react_1.useState)({ | ||
initialRender: true, | ||
originalProps, | ||
props: {}, | ||
guardsEnabled: false, | ||
canUpgradeWarn: false, | ||
@@ -55,3 +56,3 @@ animated: render_component_1.UpgradeState.NONE, | ||
}, initReducer); | ||
if (sharedState.guardsEnabled) { | ||
if (!sharedState.initialRender) { | ||
if (state.declarationTracking.guards.some((guard) => guard(refs))) { | ||
@@ -71,10 +72,6 @@ dispatch({ | ||
const memoOutput = (0, react_1.useMemo)(() => { | ||
let variables = inheritedVariables instanceof Map | ||
? Object.fromEntries(inheritedVariables) | ||
: { ...inheritedVariables }; | ||
let containers = { ...inheritedContainers }; | ||
let variables = undefined; | ||
let containers = {}; | ||
const possiblyAnimatedProps = {}; | ||
const handlers = {}; | ||
let hasVariables = false; | ||
let hasContainer = false; | ||
let hasNullContainer = false; | ||
@@ -84,7 +81,6 @@ for (const state of states) { | ||
if (state.variables) { | ||
hasVariables = true; | ||
variables ||= {}; | ||
Object.assign(variables, state.variables); | ||
} | ||
if (state.containerNames !== undefined) { | ||
hasContainer = true; | ||
if (state.containerNames === false) { | ||
@@ -135,5 +131,19 @@ hasNullContainer = true; | ||
} | ||
let tvTimeout; | ||
handlers.onPress = (event) => { | ||
sharedState.originalProps?.onPress?.(event); | ||
if (react_native_1.Platform.isTV) { | ||
sharedState.active.set(true); | ||
clearTimeout(tvTimeout); | ||
tvTimeout = setTimeout(() => { | ||
sharedState.active.set(false); | ||
}, 50); | ||
} | ||
}; | ||
if (react_native_1.Platform.isTV) { | ||
handlers.onLongPress = (event) => { | ||
sharedState.originalProps?.onLongPress?.(event); | ||
sharedState.active.set(event.eventKeyAction === 0); | ||
}; | ||
} | ||
} | ||
@@ -151,26 +161,23 @@ if (sharedState.layout || sharedState.containers) { | ||
} | ||
let nextVariables; | ||
if (hasVariables) { | ||
nextVariables = variables; | ||
} | ||
else if (sharedState.variables !== render_component_1.UpgradeState.NONE) { | ||
nextVariables = inheritedVariables; | ||
} | ||
let nextContainers; | ||
if (hasNullContainer) { | ||
nextContainers = inheritedContainers; | ||
} | ||
else if (hasContainer) { | ||
nextContainers = containers; | ||
} | ||
else if (sharedState.containers !== render_component_1.UpgradeState.NONE) { | ||
nextContainers = inheritedContainers; | ||
} | ||
return { | ||
possiblyAnimatedProps, | ||
handlers, | ||
variables: nextVariables, | ||
containers: nextContainers, | ||
variables, | ||
containers: sharedState.containers && !hasNullContainer ? containers : undefined, | ||
}; | ||
}, states); | ||
const variables = (0, react_1.useMemo)(() => { | ||
return Object.assign({}, inheritedVariables instanceof Map | ||
? Object.fromEntries(inheritedVariables.entries()) | ||
: undefined, memoOutput.variables); | ||
}, [inheritedVariables, memoOutput.variables]); | ||
const containers = (0, react_1.useMemo)(() => { | ||
if (!memoOutput.containers) { | ||
return inheritedContainers; | ||
} | ||
return { | ||
...inheritedContainers, | ||
...memoOutput.containers, | ||
}; | ||
}, [inheritedContainers, memoOutput.containers]); | ||
(0, react_1.useEffect)(() => { | ||
@@ -185,4 +192,4 @@ return () => { | ||
sharedState.originalProps = originalProps; | ||
sharedState.guardsEnabled = true; | ||
return (0, render_component_1.renderComponent)(component, sharedState, { ...props, ...memoOutput.handlers, ref }, memoOutput.possiblyAnimatedProps, memoOutput.variables, memoOutput.containers); | ||
sharedState.initialRender = false; | ||
return (0, render_component_1.renderComponent)(component, sharedState, { ...props, ...memoOutput.handlers, ref }, memoOutput.possiblyAnimatedProps, variables, containers); | ||
} | ||
@@ -224,3 +231,3 @@ function initReducer({ dispatch, config, reducer, className, }) { | ||
variables: undefined, | ||
inline: refs.props?.[config.target], | ||
inline: (0, utils_1.getTargetValue)(refs.props, config), | ||
declarationTracking: { | ||
@@ -231,2 +238,3 @@ effect: previousState.declarationTracking.effect, | ||
}, | ||
transition: previousState.transition ? { ...defaultTransition } : undefined, | ||
}; | ||
@@ -237,3 +245,3 @@ if (action.type === "new-declarations") { | ||
state.declarationTracking.guards.push((refs) => !Object.is(refs.props?.[config.source], state.className) || | ||
!Object.is(refs.props?.[config.target], state.inline)); | ||
!Object.is((0, utils_1.getTargetValue)(refs.props, config), state.inline)); | ||
const normalRules = []; | ||
@@ -252,4 +260,4 @@ const importantRules = []; | ||
} | ||
if (config.source !== config.target && refs.props?.[config.target]) { | ||
collectInlineRules(state, refs, refs.props[config.target], state.declarationTracking.effect, normalRules, importantRules); | ||
if (config.inlineProp && refs.props?.[config.inlineProp]) { | ||
collectInlineRules(state, refs, refs.props[config.inlineProp], state.declarationTracking.effect, normalRules, importantRules); | ||
} | ||
@@ -366,3 +374,5 @@ state.normal = normalRules | ||
sharedValue.value = withRepeat(withSequence(...sequence), iterationCount.type === "infinite" ? -1 : iterationCount.value); | ||
setDeep(props, pathTokens, sharedValue); | ||
(0, utils_1.assignToTarget)(props, sharedValue, pathTokens, { | ||
allowTransformMerging: true, | ||
}); | ||
} | ||
@@ -376,5 +386,6 @@ } | ||
continue; | ||
props[state.config.target] ??= {}; | ||
for (const [animationKey, { pathTokens }] of keyframes.frames) { | ||
setDeep(props, pathTokens, state.sharedValues.get(animationKey)); | ||
(0, utils_1.assignToTarget)(props, state.sharedValues.get(animationKey), pathTokens, { | ||
allowTransformMerging: true, | ||
}); | ||
seenAnimatedProps.add(animationKey); | ||
@@ -420,36 +431,40 @@ } | ||
const { makeMutable, withTiming, withDelay, Easing } = require("react-native-reanimated"); | ||
if (!properties.includes("none")) { | ||
for (let index = 0; index < properties.length; index++) { | ||
const property = properties[index]; | ||
if (seenAnimatedProps.has(property)) | ||
continue; | ||
let sharedValue = state.sharedValues.get(property); | ||
let { value, defaultValue } = (0, resolve_value_1.resolveTransitionValue)(state, property); | ||
if (value === undefined && !sharedValue) { | ||
continue; | ||
} | ||
else if (!sharedValue) { | ||
const initialValue = Number(refs.sharedState.animated) < render_component_1.UpgradeState.UPGRADED && | ||
value !== undefined | ||
? value | ||
: defaultValue; | ||
sharedValue = makeMutable(initialValue); | ||
if (properties.length === 0 || properties.includes("none")) { | ||
return; | ||
} | ||
for (let index = 0; index < properties.length; index++) { | ||
const property = properties[index]; | ||
if (seenAnimatedProps.has(property)) | ||
continue; | ||
let sharedValue = state.sharedValues.get(property); | ||
let { value, defaultValue } = (0, resolve_value_1.resolveTransitionValue)(state, property); | ||
if (value === undefined && !sharedValue) { | ||
continue; | ||
} | ||
else if (refs.sharedState.initialRender) { | ||
const initialValue = value !== undefined ? value : defaultValue; | ||
sharedValue = makeMutable(initialValue); | ||
state.sharedValues.set(property, sharedValue); | ||
} | ||
else { | ||
if (!sharedValue) { | ||
sharedValue = makeMutable(defaultValue); | ||
state.sharedValues.set(property, sharedValue); | ||
} | ||
else { | ||
value ??= defaultValue; | ||
if (value !== sharedValue.value) { | ||
const duration = (0, resolve_value_1.timeToMS)(durations[index % durations.length]); | ||
const delay = (0, resolve_value_1.timeToMS)(delays[index % delays.length]); | ||
const easing = easingFunctions[index % easingFunctions.length]; | ||
sharedValue.value = withDelay(delay, withTiming(value, { | ||
duration, | ||
easing: (0, resolve_value_1.getEasing)(easing, Easing), | ||
})); | ||
} | ||
value ??= defaultValue; | ||
if (value !== sharedValue.value) { | ||
const duration = (0, resolve_value_1.timeToMS)(durations[index % durations.length]); | ||
const delay = (0, resolve_value_1.timeToMS)(delays[index % delays.length]); | ||
const easing = easingFunctions[index % easingFunctions.length]; | ||
sharedValue.value = withDelay(delay, withTiming(value, { | ||
duration, | ||
easing: (0, resolve_value_1.getEasing)(easing, Easing), | ||
})); | ||
} | ||
seenAnimatedProps.add(property); | ||
props.style ??= {}; | ||
setDeep(props.style, [property], sharedValue); | ||
} | ||
seenAnimatedProps.add(property); | ||
props.style ??= {}; | ||
(0, utils_1.assignToTarget)(props.style, sharedValue, [property], { | ||
allowTransformMerging: true, | ||
}); | ||
} | ||
@@ -472,6 +487,9 @@ } | ||
entry[1].value = value; | ||
props.style ??= {}; | ||
props.style?.[entry[0]] ?? | ||
state.styleLookup[entry[0]] ?? | ||
resolve_value_1.defaultValues[entry[0]]; | ||
setDeep(props.style, [entry[0]], entry[1]); | ||
(0, utils_1.assignToTarget)(props.style, entry[1], [entry[0]], { | ||
allowTransformMerging: true, | ||
}); | ||
} | ||
@@ -515,13 +533,14 @@ } | ||
return; | ||
for (let move of Object.entries(config.nativeStyleToProp)) { | ||
for (let move of config.nativeStyleToProp) { | ||
const source = move[0]; | ||
const sourceValue = props[config.target]?.[source]; | ||
if (sourceValue === undefined) | ||
const target = (0, utils_1.getTargetValue)(props, config); | ||
if (!target || !(source in target)) | ||
continue; | ||
const targetProp = move[1] === true ? move[0] : move[1]; | ||
props[targetProp] = sourceValue; | ||
delete props[config.target][source]; | ||
(0, utils_1.assignToTarget)(props, target[source], move[1], { | ||
allowTransformMerging: true, | ||
}); | ||
delete target[source]; | ||
} | ||
if (config.removeTarget) { | ||
delete props[config.target]; | ||
if (config.propToRemove) { | ||
delete props[config.propToRemove]; | ||
} | ||
@@ -545,16 +564,21 @@ } | ||
if (declaration.length === 2) { | ||
const prop = declaration[0] === "style" ? target : declaration[0]; | ||
if (typeof declaration[1] === "object") { | ||
props[prop] ??= {}; | ||
Object.assign(props[prop], declaration[1]); | ||
const paths = declaration[0] === "style" | ||
? target | ||
: Array.isArray(declaration[0]) | ||
? [...target.slice(0, -1), ...declaration[0]] | ||
: [...target.slice(0, -1), declaration[0]]; | ||
(0, utils_1.assignToTarget)(props, declaration[1], paths); | ||
} | ||
else { | ||
const isNativeStyleToProp = state.config.nativeStyleToProp?.some((value) => value[0] === declaration[0]); | ||
let paths; | ||
if (isNativeStyleToProp) { | ||
paths = [...target, declaration[0]]; | ||
} | ||
else if (declaration[1][0] === "style") { | ||
paths = [...target, ...declaration[1].slice(1)]; | ||
} | ||
else { | ||
props[prop] = declaration[1]; | ||
paths = [...target.slice(0, -1), ...declaration[1]]; | ||
} | ||
} | ||
else { | ||
const paths = [...declaration[1]]; | ||
if (target !== "style" && paths[0] === "style") { | ||
paths[0] = target; | ||
} | ||
if (typeof declaration[2] === "object" && declaration[2].delay) { | ||
@@ -566,4 +590,6 @@ const uniquePlaceHolder = {}; | ||
return; | ||
const value = (0, resolve_value_1.resolveValue)(state, refs, state.styleTracking, declaration[2], props[target]); | ||
setDeep(props, paths, value); | ||
const value = (0, resolve_value_1.resolveValue)(state, refs, state.styleTracking, declaration[2], (0, utils_1.getTargetValue)(props, state.config)); | ||
(0, utils_1.assignToTarget)(props, value, paths, { | ||
allowTransformMerging: true, | ||
}); | ||
lookup[declaration[0]] = value; | ||
@@ -573,4 +599,6 @@ }); | ||
else { | ||
const value = (0, resolve_value_1.resolveValue)(state, refs, state.styleTracking, declaration[2], props[target]); | ||
setDeep(props, paths, value); | ||
const value = (0, resolve_value_1.resolveValue)(state, refs, state.styleTracking, declaration[2], (0, utils_1.getTargetValue)(props, state.config)); | ||
(0, utils_1.assignToTarget)(props, value, paths, { | ||
allowTransformMerging: true, | ||
}); | ||
lookup[declaration[0]] = value; | ||
@@ -581,8 +609,5 @@ } | ||
else { | ||
if (typeof props[target] === "object") { | ||
Object.assign(props[target], declaration); | ||
} | ||
else { | ||
props[target] = { ...declaration }; | ||
} | ||
(0, utils_1.assignToTarget)(props, { ...declaration }, state.config, { | ||
objectMergeStyle: "assign", | ||
}); | ||
} | ||
@@ -624,19 +649,2 @@ } | ||
} | ||
const transformKeys = new Set([ | ||
"transform", | ||
"translateX", | ||
"translateY", | ||
"scale", | ||
"scaleX", | ||
"scaleY", | ||
"rotate", | ||
"rotateX", | ||
"rotateY", | ||
"rotateZ", | ||
"skewX", | ||
"skewY", | ||
"perspective", | ||
"matrix", | ||
"transformOrigin", | ||
]); | ||
function collectRules(state, refs, ruleSet, collection, key) { | ||
@@ -712,28 +720,2 @@ const rules = ruleSet[key]; | ||
}; | ||
function setDeep(target, paths, value) { | ||
const prop = paths[paths.length - 1]; | ||
for (let i = 0; i < paths.length - 1; i++) { | ||
const token = paths[i]; | ||
target[token] ??= {}; | ||
target = target[token]; | ||
} | ||
if (transformKeys.has(prop)) { | ||
if (target.transform) { | ||
const existing = target.transform.find((t) => Object.keys(t)[0] === prop); | ||
if (existing) { | ||
existing[prop] = value; | ||
} | ||
else { | ||
target.transform.push({ [prop]: value }); | ||
} | ||
} | ||
else { | ||
target.transform ??= []; | ||
target.transform.push({ [prop]: value }); | ||
} | ||
} | ||
else { | ||
target[prop] = value; | ||
} | ||
} | ||
function isDeepEqual(a, b) { | ||
@@ -740,0 +722,0 @@ if (a === b) |
@@ -18,2 +18,3 @@ "use strict"; | ||
const unit_observables_1 = require("./unit-observables"); | ||
const utils_1 = require("./utils"); | ||
function resolveValue(state, refs, tracking, descriptor, style) { | ||
@@ -266,3 +267,3 @@ switch (typeof descriptor) { | ||
value: state.styleLookup[property] ?? | ||
state.props?.[state.config.target]?.[property], | ||
(0, utils_1.getTargetValue)(state.props, state.config)?.[property], | ||
}; | ||
@@ -269,0 +270,0 @@ } |
@@ -35,5 +35,5 @@ "use strict"; | ||
if (styleWarnings) { | ||
if (globals_1.warnings.has(name)) | ||
return; | ||
globals_1.warnings.set(name, styleWarnings); | ||
if (!globals_1.warnings.has(name)) { | ||
globals_1.warnings.set(name, styleWarnings); | ||
} | ||
} | ||
@@ -67,7 +67,3 @@ return style; | ||
let obs = store instanceof Map ? store.get(name) : store[name]; | ||
if (!obs) { | ||
obs = (0, appearance_observables_1.cssVariableObservable)(undefined); | ||
store instanceof Map ? store.set(name, obs) : (store[name] = obs); | ||
} | ||
return obs.get(effect); | ||
return obs?.get(effect); | ||
} | ||
@@ -117,3 +113,3 @@ const getUniversalVariable = (name, effect) => { | ||
else { | ||
rootVariables.set(entry[0], (0, appearance_observables_1.cssVariableObservable)(entry[1])); | ||
rootVariables.set(entry[0], (0, appearance_observables_1.cssVariableObservable)(entry[1], { name: entry[0] })); | ||
} | ||
@@ -120,0 +116,0 @@ } |
@@ -47,5 +47,5 @@ import type { SharedValue } from "react-native-reanimated"; | ||
export type SharedState = { | ||
initialRender: boolean; | ||
originalProps: Record<string, any> | null; | ||
props: Record<string, any> | null; | ||
guardsEnabled: boolean; | ||
animated: number; | ||
@@ -52,0 +52,0 @@ variables: number; |
@@ -5,12 +5,7 @@ import { CssInterop } from "../../types"; | ||
export { rem } from "./rem"; | ||
export declare const interopComponents: Map<string | object, import("react").ComponentType<{}>>; | ||
export { useColorScheme } from "./useColorScheme"; | ||
export declare const cssInterop: CssInterop; | ||
export declare const remapProps: CssInterop; | ||
export declare function useColorScheme(): { | ||
colorScheme: "light" | "dark" | undefined; | ||
setColorScheme: (value: "light" | "dark" | "system") => void; | ||
toggleColorScheme: () => void; | ||
}; | ||
export declare const useUnstableNativeVariable: (name: string) => undefined; | ||
export declare function vars<T extends Record<`--${string}`, string | number>>(variables: T): Record<string, string>; | ||
export declare function useSafeAreaEnv(): {} | undefined; |
"use strict"; | ||
"use client"; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
exports.useUnstableNativeVariable = exports.remapProps = exports.cssInterop = exports.interopComponents = exports.rem = exports.colorScheme = exports.StyleSheet = void 0; | ||
exports.useColorScheme = useColorScheme; | ||
exports.useUnstableNativeVariable = exports.remapProps = exports.cssInterop = exports.useColorScheme = exports.rem = exports.colorScheme = exports.StyleSheet = void 0; | ||
exports.vars = vars; | ||
@@ -9,11 +9,13 @@ exports.useSafeAreaEnv = useSafeAreaEnv; | ||
const config_1 = require("../config"); | ||
const color_scheme_1 = require("./color-scheme"); | ||
const utils_1 = require("../native/utils"); | ||
var stylesheet_1 = require("./stylesheet"); | ||
Object.defineProperty(exports, "StyleSheet", { enumerable: true, get: function () { return stylesheet_1.StyleSheet; } }); | ||
var color_scheme_2 = require("./color-scheme"); | ||
Object.defineProperty(exports, "colorScheme", { enumerable: true, get: function () { return color_scheme_2.colorScheme; } }); | ||
var color_scheme_1 = require("./color-scheme"); | ||
Object.defineProperty(exports, "colorScheme", { enumerable: true, get: function () { return color_scheme_1.colorScheme; } }); | ||
var rem_1 = require("./rem"); | ||
Object.defineProperty(exports, "rem", { enumerable: true, get: function () { return rem_1.rem; } }); | ||
exports.interopComponents = new Map(); | ||
const interopComponentsMap_1 = require("./interopComponentsMap"); | ||
const ForwardRefSymbol = Symbol.for("react.forward_ref"); | ||
var useColorScheme_1 = require("./useColorScheme"); | ||
Object.defineProperty(exports, "useColorScheme", { enumerable: true, get: function () { return useColorScheme_1.useColorScheme; } }); | ||
const cssInterop = (baseComponent, mapping) => { | ||
@@ -27,21 +29,12 @@ const configs = (0, config_1.getNormalizeConfig)(mapping); | ||
for (const config of configs) { | ||
let newStyles = []; | ||
const source = props[config.source]; | ||
const target = props[config.target]; | ||
if (typeof source === "string" && source) { | ||
newStyles.push({ | ||
(0, utils_1.assignToTarget)(props, { | ||
$$css: true, | ||
[source]: source, | ||
}, config, { | ||
objectMergeStyle: "toArray", | ||
}); | ||
} | ||
delete props[config.source]; | ||
if (Array.isArray(target)) { | ||
newStyles.push(...target); | ||
} | ||
else if (target) { | ||
newStyles.push(target); | ||
} | ||
if (newStyles.length > 0) { | ||
props[config.target] = newStyles; | ||
} | ||
} | ||
@@ -64,3 +57,3 @@ if ("$$typeof" in baseComponent && | ||
interopComponent.displayName = `CssInterop.${baseComponent.displayName ?? baseComponent.name ?? "unknown"}`; | ||
exports.interopComponents.set(baseComponent, interopComponent); | ||
interopComponentsMap_1.interopComponents.set(baseComponent, interopComponent); | ||
return interopComponent; | ||
@@ -70,13 +63,2 @@ }; | ||
exports.remapProps = exports.cssInterop; | ||
function useColorScheme() { | ||
const [effect, setEffect] = (0, react_1.useState)(() => ({ | ||
run: () => setEffect((s) => ({ ...s })), | ||
dependencies: new Set(), | ||
})); | ||
return { | ||
colorScheme: color_scheme_1.colorScheme.get(effect), | ||
setColorScheme: color_scheme_1.colorScheme.set, | ||
toggleColorScheme: color_scheme_1.colorScheme.toggle, | ||
}; | ||
} | ||
const useUnstableNativeVariable = (name) => { | ||
@@ -83,0 +65,0 @@ if (process.env.NODE_ENV !== "production") { |
import type { MediaQuery, Animation, ContainerType, Time, EasingFunction, ContainerCondition, Declaration, SelectorComponent } from "lightningcss"; | ||
import type { ClassicComponentClass, ComponentClass, ComponentProps, ComponentType, ForwardRefExoticComponent, FunctionComponent, JSXElementConstructor } from "react"; | ||
import type { ClassicComponentClass, ComponentClass, ComponentProps, ComponentType, ForwardRefExoticComponent, FunctionComponent } from "react"; | ||
import type { ImageStyle, TextStyle, ViewStyle } from "react-native"; | ||
@@ -14,6 +14,7 @@ import type { Effect } from "./runtime/observable"; | ||
export type InteropComponentConfig = { | ||
target: string; | ||
target: string[]; | ||
inlineProp?: string; | ||
source: string; | ||
removeTarget?: true | undefined; | ||
nativeStyleToProp?: NativeStyleToProp<any>; | ||
propToRemove?: string; | ||
nativeStyleToProp?: Array<[string, string[]]>; | ||
}; | ||
@@ -40,20 +41,24 @@ export type CssToReactNativeRuntimeOptions = { | ||
} | ||
export type EnableCssInteropOptions<T extends keyof JSX.IntrinsicElements | JSXElementConstructor<any>> = Record<string, CSSInteropClassNamePropConfig<ComponentProps<T>>>; | ||
export type CssInterop = <const T extends ReactComponent<any>, const M extends EnableCssInteropOptions<any>>(component: T, mapping: EnableCssInteropOptions<T> & M) => ComponentType<ComponentProps<T> & CssInteropGeneratedProps<M>>; | ||
export type CSSInteropClassNamePropConfig<P> = undefined | boolean | (keyof P & string) | { | ||
target: (keyof P & string) | boolean; | ||
nativeStyleToProp?: NativeStyleToProp<P>; | ||
}; | ||
export type CssInteropGeneratedProps<T extends EnableCssInteropOptions<any>> = { | ||
[K in keyof T as K extends string ? T[K] extends undefined | false ? never : T[K] extends true | string ? K : T[K] extends { | ||
target: string | true; | ||
export type CssInterop = <const C extends ReactComponent<any>, const M extends EnableCssInteropOptions<C>>(component: C, mapping: M & EnableCssInteropOptions<C>) => ComponentType<ComponentProps<C> & { | ||
[K in keyof M as K extends string ? M[K] extends undefined | false ? never : M[K] extends true | FlattenComponentProps<C> ? K : M[K] extends { | ||
target: FlattenComponentProps<C> | true; | ||
} | { | ||
target: false; | ||
nativeStyleToProp: Record<string, true>; | ||
nativeStyleToProp: Record<string, unknown>; | ||
} ? K : never : never]?: string; | ||
}; | ||
export type NativeStyleToProp<P> = { | ||
[K in (keyof Style | "fill" | "stroke") & string]?: K extends keyof P ? (keyof P & string) | true : keyof P & string; | ||
}; | ||
export type JSXFunction = (type: React.ComponentType, props: Record<string, any> | undefined | null, key?: React.Key, isStaticChildren?: boolean, __source?: unknown, __self?: unknown) => React.ElementType; | ||
}>; | ||
export type EnableCssInteropOptions<C extends ReactComponent<any>> = Record<string, boolean | FlattenComponentProps<C> | { | ||
target: false; | ||
nativeStyleToProp: { | ||
[K in (keyof Style & string) | "fill" | "stroke"]?: K extends FlattenComponentProps<C> ? FlattenComponentProps<C> | true : FlattenComponentProps<C>; | ||
}; | ||
} | { | ||
target: FlattenComponentProps<C> | true; | ||
nativeStyleToProp?: { | ||
[K in (keyof Style & string) | "fill" | "stroke"]?: K extends FlattenComponentProps<C> ? FlattenComponentProps<C> | true : FlattenComponentProps<C>; | ||
}; | ||
}>; | ||
type FlattenComponentProps<C extends ReactComponent<any>> = FlattenObjectKeys<ComponentProps<C>>; | ||
type FlattenObjectKeys<T extends Record<string, unknown>, Depth extends number[] = [], MaxDepth extends number = 10, Key = keyof T> = Depth["length"] extends MaxDepth ? never : Key extends string ? unknown extends T[Key] ? Key | `${Key}.${string}` : NonNullable<T[Key]> extends Record<string, unknown> ? Key | `${Key}.${FlattenObjectKeys<NonNullable<T[Key]>, [...Depth, 0]>}` : Key : never; | ||
export type JSXFunction = (type: React.ComponentType, props: Record<string, any> | undefined | null, key?: React.Key, isStaticChildren?: boolean, __source?: unknown, __self?: unknown) => React.ReactNode; | ||
type OmitFirstTwo<T extends any[]> = T extends [any, any, ...infer R] ? R : never; | ||
@@ -127,3 +132,3 @@ export type JSXFunctionType = Parameters<JSXFunction>[0]; | ||
state: PropState; | ||
target: string; | ||
target: string[]; | ||
resetContext: boolean; | ||
@@ -130,0 +135,0 @@ requiresLayout: boolean; |
@@ -1,5 +0,5 @@ | ||
import { ComponentProps, ComponentType, ReactElement } from "react"; | ||
import { ComponentProps, ComponentRef, ForwardedRef, ReactElement } from "react"; | ||
import { RenderOptions as TLRenderOptions } from "@testing-library/react-native"; | ||
import { vh, vw } from "../../runtime/native/unit-observables"; | ||
import { CssToReactNativeRuntimeOptions, EnableCssInteropOptions, ReactComponent, Style, CssInteropGeneratedProps } from "../../types"; | ||
import { CssToReactNativeRuntimeOptions, EnableCssInteropOptions, ReactComponent, Style } from "../../types"; | ||
import { isReduceMotionEnabled } from "../../runtime/native/appearance-observables"; | ||
@@ -49,6 +49,6 @@ export * from "../../index"; | ||
}) => Array<ReactTestInstance>; | ||
UNSAFE_getByType: <P>(type: ComponentType<P>) => ReactTestInstance; | ||
UNSAFE_getAllByType: <P>(type: ComponentType<P>) => Array<ReactTestInstance>; | ||
UNSAFE_queryByType: <P>(type: ComponentType<P>) => ReactTestInstance | null; | ||
UNSAFE_queryAllByType: <P>(type: ComponentType<P>) => Array<ReactTestInstance>; | ||
UNSAFE_getByType: <P>(type: import("react").ComponentType<P>) => ReactTestInstance; | ||
UNSAFE_getAllByType: <P>(type: import("react").ComponentType<P>) => Array<ReactTestInstance>; | ||
UNSAFE_queryByType: <P>(type: import("react").ComponentType<P>) => ReactTestInstance | null; | ||
UNSAFE_queryAllByType: <P>(type: import("react").ComponentType<P>) => Array<ReactTestInstance>; | ||
getByA11yValue: import("@testing-library/react-native/build/queries/make-queries").GetByQuery<import("@testing-library/react-native/build/helpers/matchers/match-accessibility-value").AccessibilityValueMatcher, import("@testing-library/react-native/build/queries/options").CommonQueryOptions>; | ||
@@ -134,11 +134,7 @@ getAllByA11yValue: import("@testing-library/react-native/build/queries/make-queries").GetAllByQuery<import("@testing-library/react-native/build/helpers/matchers/match-accessibility-value").AccessibilityValueMatcher, import("@testing-library/react-native/build/queries/options").CommonQueryOptions>; | ||
export declare function getWarnings(): Map<string, import("../../types").ExtractionWarning[]>; | ||
export declare const createMockComponent: <const T extends ReactComponent<any>, const M extends EnableCssInteropOptions<any> = { | ||
className: "style"; | ||
}>(Component: T, mapping?: M) => ComponentType<ComponentProps<T> & CssInteropGeneratedProps<M>> & { | ||
mock: jest.Mock<import("react").ElementType<any, keyof import("react").JSX.IntrinsicElements>, [any, ref: any], any>; | ||
export declare function createMockComponent<const C extends ReactComponent<any>, const M extends EnableCssInteropOptions<C>>(Component: C, mapping: M & EnableCssInteropOptions<C>): import("react").ForwardRefExoticComponent<import("react").PropsWithoutRef<ComponentProps<C>> & import("react").RefAttributes<ComponentRef<C>>> & { | ||
mock: jest.Mock<import("react").ReactNode, [ComponentProps<C>, ref: ForwardedRef<ComponentRef<C>>], any>; | ||
}; | ||
export declare const createRemappedComponent: <const T extends ReactComponent<any>, const M extends EnableCssInteropOptions<any> = { | ||
className: "style"; | ||
}>(Component: T, mapping?: M) => ComponentType<ComponentProps<T> & CssInteropGeneratedProps<M>> & { | ||
mock: jest.Mock<import("react").ElementType<any, keyof import("react").JSX.IntrinsicElements>, [any, ref: any], any>; | ||
export declare function createRemappedComponent<const C extends ReactComponent<any>, const M extends EnableCssInteropOptions<C>>(Component: C, mapping: M & EnableCssInteropOptions<C>): import("react").ForwardRefExoticComponent<import("react").PropsWithoutRef<ComponentProps<C>> & import("react").RefAttributes<ComponentRef<C>>> & { | ||
mock: jest.Mock<import("react").ReactNode, [ComponentProps<C>, ref: ForwardedRef<ComponentRef<C>>], any>; | ||
}; | ||
@@ -145,0 +141,0 @@ export declare const resetComponents: () => void; |
@@ -17,5 +17,7 @@ "use strict"; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
exports.resetComponents = exports.createRemappedComponent = exports.createMockComponent = exports.native = exports.INTERNAL_SET = void 0; | ||
exports.resetComponents = exports.native = exports.INTERNAL_SET = void 0; | ||
exports.render = render; | ||
exports.getWarnings = getWarnings; | ||
exports.createMockComponent = createMockComponent; | ||
exports.createRemappedComponent = createRemappedComponent; | ||
exports.registerCSS = registerCSS; | ||
@@ -69,5 +71,3 @@ exports.setupAllComponents = setupAllComponents; | ||
} | ||
const createMockComponent = (Component, mapping = { | ||
className: "style", | ||
}) => { | ||
function createMockComponent(Component, mapping) { | ||
(0, api_1.cssInterop)(Component, mapping); | ||
@@ -80,7 +80,4 @@ const mock = jest.fn(({ ...props }, ref) => { | ||
}); | ||
}; | ||
exports.createMockComponent = createMockComponent; | ||
const createRemappedComponent = (Component, mapping = { | ||
className: "style", | ||
}) => { | ||
} | ||
function createRemappedComponent(Component, mapping) { | ||
(0, api_1.remapProps)(Component, mapping); | ||
@@ -93,4 +90,3 @@ const mock = jest.fn(({ ...props }, ref) => { | ||
}); | ||
}; | ||
exports.createRemappedComponent = createRemappedComponent; | ||
} | ||
const resetComponents = () => { | ||
@@ -97,0 +93,0 @@ api_1.interopComponents.clear(); |
{ | ||
"version": "0.0.0-20240903135743", | ||
"version": "0.0.0-20240914221114", | ||
"name": "react-native-css-interop", | ||
@@ -4,0 +4,0 @@ "description": "", |
@@ -151,2 +151,5 @@ import connect from "connect"; | ||
* This is also the only place the Metro exposes the MetroServer to the config. | ||
* | ||
* NOTE: This function is deprecated and should be replaced with unstable_devMiddleware, but no community CLI | ||
* supports it at time of writing. | ||
*/ | ||
@@ -153,0 +156,0 @@ enhanceMiddleware: (middleware, metroServer) => { |
export * from "./web/api"; | ||
export * from "./web/interopComponentsMap"; |
@@ -0,1 +1,3 @@ | ||
"use client"; | ||
import { | ||
@@ -2,0 +4,0 @@ ActivityIndicator, |
@@ -1,6 +0,2 @@ | ||
import { | ||
EnableCssInteropOptions, | ||
InteropComponentConfig, | ||
NativeStyleToProp, | ||
} from "../types"; | ||
import { EnableCssInteropOptions, InteropComponentConfig } from "../types"; | ||
@@ -10,17 +6,16 @@ export function getNormalizeConfig( | ||
): InteropComponentConfig[] { | ||
const config = new Map<string, InteropComponentConfig>(); | ||
const configs: InteropComponentConfig[] = []; | ||
for (const [source, options] of Object.entries(mapping) as Array< | ||
[string, EnableCssInteropOptions<any>[string]] | ||
>) { | ||
let target: string; | ||
let nativeStyleToProp: NativeStyleToProp<any> | undefined; | ||
let removeTarget: true | undefined; | ||
for (const [source, options] of Object.entries(mapping)) { | ||
let target: string[]; | ||
let inlineProp: string | undefined; | ||
let propToRemove: string | undefined; | ||
let nativeStyleToProp: InteropComponentConfig["nativeStyleToProp"]; | ||
if (!options) continue; | ||
if (typeof options === "boolean") { | ||
target = source; | ||
if (options === true) { | ||
target = [source]; | ||
} else if (typeof options === "string") { | ||
target = options; | ||
target = [options]; | ||
} else if (options.target === false) { | ||
@@ -32,19 +27,34 @@ /* | ||
*/ | ||
target = source; | ||
nativeStyleToProp = options.nativeStyleToProp; | ||
removeTarget = true; | ||
target = [source]; | ||
propToRemove = source; | ||
nativeStyleToProp = parseNativeStyleToProp(options.nativeStyleToProp); | ||
} else { | ||
target = options.target === true ? source : options.target; | ||
nativeStyleToProp = options.nativeStyleToProp; | ||
target = options.target === true ? [source] : options.target.split("."); | ||
nativeStyleToProp = parseNativeStyleToProp(options.nativeStyleToProp); | ||
} | ||
config.set(target, { | ||
if (target.length === 1 && target[0] !== source) { | ||
inlineProp = target[0]; | ||
} | ||
configs.push({ | ||
nativeStyleToProp, | ||
source, | ||
target, | ||
removeTarget, | ||
inlineProp, | ||
propToRemove, | ||
}); | ||
} | ||
return Array.from(config.values()); | ||
return configs; | ||
} | ||
function parseNativeStyleToProp( | ||
nativeStyleToProp?: Record<string, string | true>, | ||
): InteropComponentConfig["nativeStyleToProp"] { | ||
if (!nativeStyleToProp) return; | ||
return Object.entries(nativeStyleToProp).map(([key, value]) => { | ||
return [key, value === true ? [key] : value.split(".")]; | ||
}); | ||
} |
@@ -13,2 +13,3 @@ import { createElement, forwardRef, useContext, useState } from "react"; | ||
import { colorScheme } from "./appearance-observables"; | ||
import { assignToTarget } from "./utils"; | ||
@@ -79,13 +80,5 @@ export { StyleSheet } from "./stylesheet"; | ||
let target = props[config.target]; | ||
if (Array.isArray(target)) { | ||
target.push(placeholder); | ||
} else if (target) { | ||
target = [target, placeholder]; | ||
} else { | ||
target = placeholder; | ||
} | ||
props[config.target] = target; | ||
assignToTarget(props, placeholder, config, { | ||
objectMergeStyle: "toArray", | ||
}); | ||
} | ||
@@ -92,0 +85,0 @@ |
@@ -60,2 +60,3 @@ import { | ||
return { | ||
name, | ||
get(effect?: Effect) { | ||
@@ -62,0 +63,0 @@ return colorScheme.get(effect) === "light" |
@@ -9,3 +9,3 @@ import { | ||
} from "react"; | ||
import { LayoutChangeEvent, View } from "react-native"; | ||
import { LayoutChangeEvent, Platform, View } from "react-native"; | ||
import { | ||
@@ -20,2 +20,3 @@ ReactComponent, | ||
ExtractedTransition, | ||
ContainerRecord, | ||
} from "../../types"; | ||
@@ -51,2 +52,4 @@ import { containerContext } from "./globals"; | ||
import { DEFAULT_CONTAINER_NAME } from "../../shared"; | ||
import { assignToTarget, getTargetValue } from "./utils"; | ||
import { GestureHandlerEvent } from "react-native-reanimated/lib/typescript/reanimated2/hook"; | ||
@@ -76,5 +79,5 @@ export function interop( | ||
const sharedState = useState<SharedState>({ | ||
initialRender: true, | ||
originalProps, | ||
props: {}, | ||
guardsEnabled: false, | ||
canUpgradeWarn: false, | ||
@@ -146,3 +149,3 @@ animated: UpgradeState.NONE, | ||
// for that config has changed and needs to be updated | ||
if (sharedState.guardsEnabled) { | ||
if (!sharedState.initialRender) { | ||
if (state.declarationTracking.guards.some((guard) => guard(refs))) { | ||
@@ -169,15 +172,9 @@ // If needed, update the declarations | ||
const memoOutput = useMemo(() => { | ||
let variables = | ||
// RootVariables are a map, inheritedVariables are an object | ||
inheritedVariables instanceof Map | ||
? Object.fromEntries(inheritedVariables) | ||
: { ...inheritedVariables }; | ||
let variables = undefined; | ||
let containers: ContainerRecord = {}; | ||
let containers = { ...inheritedContainers }; | ||
const possiblyAnimatedProps: Record<string, any> = {}; | ||
const handlers: Record<string, any> = {}; | ||
const handlers: Record<string, (event: GestureHandlerEvent<any>) => void> = | ||
{}; | ||
let hasVariables = false; | ||
let hasContainer = false; | ||
let hasNullContainer = false; | ||
@@ -192,3 +189,3 @@ | ||
if (state.variables) { | ||
hasVariables = true; | ||
variables ||= {}; | ||
Object.assign(variables, state.variables); | ||
@@ -198,4 +195,2 @@ } | ||
if (state.containerNames !== undefined) { | ||
hasContainer = true; | ||
if (state.containerNames === false) { | ||
@@ -251,5 +246,31 @@ hasNullContainer = true; | ||
} | ||
handlers.onPress = (event: unknown) => { | ||
let tvTimeout: NodeJS.Timeout; | ||
handlers.onPress = (event) => { | ||
sharedState.originalProps?.onPress?.(event); | ||
/** | ||
* tvOS does not receive pressIn/pressOut | ||
* so we need to fake it via onPress or | ||
* handle in onLongPress | ||
*/ | ||
if (Platform.isTV) { | ||
sharedState.active!.set(true); | ||
clearTimeout(tvTimeout); | ||
tvTimeout = setTimeout(() => { | ||
sharedState.active!.set(false); | ||
}, 50); | ||
} | ||
}; | ||
/** | ||
* tvOS does not receive pressIn/pressOut | ||
* so we need to fake it via onPress or | ||
* handle in onLongPress | ||
*/ | ||
if (Platform.isTV) { | ||
handlers.onLongPress = (event) => { | ||
sharedState.originalProps?.onLongPress?.(event); | ||
sharedState.active!.set(event.eventKeyAction === 0); | ||
}; | ||
} | ||
} | ||
@@ -269,29 +290,32 @@ | ||
/** | ||
* Determine the next variables and containers. If something has upgraded, then we always need a value | ||
*/ | ||
let nextVariables: Record<string, any> | undefined; | ||
if (hasVariables) { | ||
nextVariables = variables; | ||
} else if (sharedState.variables !== UpgradeState.NONE) { | ||
nextVariables = inheritedVariables; | ||
} | ||
let nextContainers: Record<string, any> | undefined; | ||
if (hasNullContainer) { | ||
nextContainers = inheritedContainers; | ||
} else if (hasContainer) { | ||
nextContainers = containers; | ||
} else if (sharedState.containers !== UpgradeState.NONE) { | ||
nextContainers = inheritedContainers; | ||
} | ||
return { | ||
possiblyAnimatedProps, | ||
handlers, | ||
variables: nextVariables, | ||
containers: nextContainers, | ||
variables, | ||
containers: | ||
sharedState.containers && !hasNullContainer ? containers : undefined, | ||
}; | ||
}, states); | ||
const variables = useMemo(() => { | ||
return Object.assign( | ||
{}, | ||
inheritedVariables instanceof Map | ||
? Object.fromEntries(inheritedVariables.entries()) | ||
: undefined, | ||
memoOutput.variables, | ||
); | ||
}, [inheritedVariables, memoOutput.variables]); | ||
const containers = useMemo(() => { | ||
if (!memoOutput.containers) { | ||
return inheritedContainers; | ||
} | ||
return { | ||
...inheritedContainers, | ||
...memoOutput.containers, | ||
}; | ||
}, [inheritedContainers, memoOutput.containers]); | ||
// Cleanup the effects when the component is unmounted | ||
@@ -310,3 +334,3 @@ useEffect(() => { | ||
sharedState.originalProps = originalProps; | ||
sharedState.guardsEnabled = true; | ||
sharedState.initialRender = false; | ||
@@ -318,4 +342,4 @@ return renderComponent( | ||
memoOutput.possiblyAnimatedProps, | ||
memoOutput.variables, | ||
memoOutput.containers, | ||
variables, | ||
containers, | ||
); | ||
@@ -385,3 +409,3 @@ } | ||
// Reset the inline styles | ||
inline: refs.props?.[config.target], | ||
inline: getTargetValue(refs.props, config), | ||
// Keep the same effect, but reset the guards | ||
@@ -393,2 +417,5 @@ declarationTracking: { | ||
}, | ||
// If we previously had a transition, we need to keep it | ||
// So we 'reset' to the defaultTransition | ||
transition: previousState.transition ? { ...defaultTransition } : undefined, | ||
}; | ||
@@ -403,3 +430,3 @@ | ||
!Object.is(refs.props?.[config.source], state.className) || | ||
!Object.is(refs.props?.[config.target], state.inline), | ||
!Object.is(getTargetValue(refs.props, config), state.inline), | ||
); | ||
@@ -424,7 +451,7 @@ | ||
if (config.source !== config.target && refs.props?.[config.target]) { | ||
if (config.inlineProp && refs.props?.[config.inlineProp]) { | ||
collectInlineRules( | ||
state, | ||
refs, | ||
refs.props[config.target], | ||
refs.props[config.inlineProp], | ||
state.declarationTracking.effect, | ||
@@ -675,3 +702,5 @@ normalRules, | ||
setDeep(props, pathTokens, sharedValue); | ||
assignToTarget(props, sharedValue, pathTokens, { | ||
allowTransformMerging: true, | ||
}); | ||
} | ||
@@ -684,6 +713,11 @@ } | ||
props[state.config.target] ??= {}; | ||
for (const [animationKey, { pathTokens }] of keyframes.frames) { | ||
setDeep(props, pathTokens, state.sharedValues.get(animationKey)); | ||
assignToTarget( | ||
props, | ||
state.sharedValues.get(animationKey), | ||
pathTokens, | ||
{ | ||
allowTransformMerging: true, | ||
}, | ||
); | ||
seenAnimatedProps.add(animationKey); | ||
@@ -758,45 +792,50 @@ } | ||
*/ | ||
if (!properties.includes("none")) { | ||
for (let index = 0; index < properties.length; index++) { | ||
const property = properties[index]; | ||
if (seenAnimatedProps.has(property)) continue; | ||
let sharedValue = state.sharedValues.get(property); | ||
let { value, defaultValue } = resolveTransitionValue(state, property); | ||
if (properties.length === 0 || properties.includes("none")) { | ||
return; | ||
} | ||
if (value === undefined && !sharedValue) { | ||
// We have never seen this value, and its undefined so do nothing | ||
continue; | ||
} else if (!sharedValue) { | ||
// First time seeing this value. On the initial render don't transition, | ||
// otherwise transition from the default value | ||
const initialValue = | ||
Number(refs.sharedState.animated) < UpgradeState.UPGRADED && | ||
value !== undefined | ||
? value | ||
: defaultValue; | ||
for (let index = 0; index < properties.length; index++) { | ||
const property = properties[index]; | ||
if (seenAnimatedProps.has(property)) continue; | ||
let sharedValue = state.sharedValues.get(property); | ||
let { value, defaultValue } = resolveTransitionValue(state, property); | ||
sharedValue = makeMutable(initialValue); | ||
if (value === undefined && !sharedValue) { | ||
// We have never seen this value, and its undefined so do nothing | ||
continue; | ||
} else if (refs.sharedState.initialRender) { | ||
// On the initial render don't transition | ||
const initialValue = value !== undefined ? value : defaultValue; | ||
sharedValue = makeMutable(initialValue); | ||
state.sharedValues.set(property, sharedValue); | ||
} else { | ||
if (!sharedValue) { | ||
// First time seeing this value, but its not the initial render! | ||
// We need to create the sharedValue | ||
sharedValue = makeMutable(defaultValue); | ||
state.sharedValues.set(property, sharedValue); | ||
} else { | ||
// If the value is undefined or null, then it should be the default | ||
value ??= defaultValue; | ||
} | ||
if (value !== sharedValue.value) { | ||
const duration = timeToMS(durations[index % durations.length]); | ||
const delay = timeToMS(delays[index % delays.length]); | ||
const easing = easingFunctions[index % easingFunctions.length]; | ||
sharedValue.value = withDelay( | ||
delay, | ||
withTiming(value, { | ||
duration, | ||
easing: getEasing(easing, Easing), | ||
}), | ||
); | ||
} | ||
// If the value is undefined or null, then it should be the default | ||
value ??= defaultValue; | ||
if (value !== sharedValue.value) { | ||
const duration = timeToMS(durations[index % durations.length]); | ||
const delay = timeToMS(delays[index % delays.length]); | ||
const easing = easingFunctions[index % easingFunctions.length]; | ||
sharedValue.value = withDelay( | ||
delay, | ||
withTiming(value, { | ||
duration, | ||
easing: getEasing(easing, Easing), | ||
}), | ||
); | ||
} | ||
} | ||
seenAnimatedProps.add(property); | ||
props.style ??= {}; | ||
setDeep(props.style, [property], sharedValue); | ||
} | ||
seenAnimatedProps.add(property); | ||
props.style ??= {}; | ||
assignToTarget(props.style, sharedValue, [property], { | ||
allowTransformMerging: true, | ||
}); | ||
} | ||
@@ -824,6 +863,9 @@ } | ||
entry[1].value = value; | ||
props.style ??= {}; | ||
props.style?.[entry[0]] ?? | ||
state.styleLookup[entry[0]] ?? | ||
defaultValues[entry[0]]; | ||
setDeep(props.style, [entry[0]], entry[1]); | ||
assignToTarget(props.style, entry[1], [entry[0]], { | ||
allowTransformMerging: true, | ||
}); | ||
} | ||
@@ -872,13 +914,17 @@ } | ||
for (let move of Object.entries(config.nativeStyleToProp)) { | ||
for (let move of config.nativeStyleToProp) { | ||
const source = move[0]; | ||
const sourceValue = props[config.target]?.[source]; | ||
if (sourceValue === undefined) continue; | ||
const targetProp = move[1] === true ? move[0] : move[1]; | ||
props[targetProp] = sourceValue; | ||
delete props[config.target][source]; | ||
const target = getTargetValue(props, config); | ||
if (!target || !(source in target)) continue; | ||
assignToTarget(props, target[source], move[1], { | ||
allowTransformMerging: true, | ||
}); | ||
delete target[source]; | ||
} | ||
if (config.removeTarget) { | ||
delete props[config.target]; | ||
if (config.propToRemove) { | ||
delete props[config.propToRemove]; | ||
} | ||
@@ -915,14 +961,25 @@ } | ||
if (declaration.length === 2) { | ||
const prop = declaration[0] === "style" ? target : declaration[0]; | ||
if (typeof declaration[1] === "object") { | ||
props[prop] ??= {}; | ||
Object.assign(props[prop], declaration[1]); | ||
} else { | ||
props[prop] = declaration[1]; | ||
} | ||
const paths = | ||
declaration[0] === "style" | ||
? target | ||
: Array.isArray(declaration[0]) | ||
? [...target.slice(0, -1), ...declaration[0]] | ||
: [...target.slice(0, -1), declaration[0]]; | ||
assignToTarget(props, declaration[1], paths); | ||
} else { | ||
const paths = [...declaration[1]]; | ||
const isNativeStyleToProp = state.config.nativeStyleToProp?.some( | ||
(value) => value[0] === declaration[0], | ||
); | ||
if (target !== "style" && paths[0] === "style") { | ||
paths[0] = target; | ||
let paths: string[]; | ||
if (isNativeStyleToProp) { | ||
paths = [...target, declaration[0]]; | ||
} else if (declaration[1][0] === "style") { | ||
// The styles say they are "style", but they should go to the first target | ||
// If they don't say "style" then they have a @rn-move rule that has already moved them | ||
paths = [...target, ...declaration[1].slice(1)]; | ||
} else { | ||
paths = [...target.slice(0, -1), ...declaration[1]]; | ||
} | ||
@@ -942,5 +999,7 @@ | ||
declaration[2], | ||
props[target], | ||
getTargetValue(props, state.config), | ||
); | ||
setDeep(props, paths, value); | ||
assignToTarget(props, value, paths, { | ||
allowTransformMerging: true, | ||
}); | ||
lookup[declaration[0]] = value; | ||
@@ -954,5 +1013,7 @@ }); | ||
declaration[2], | ||
props[target], | ||
getTargetValue(props, state.config), | ||
); | ||
setDeep(props, paths, value); | ||
assignToTarget(props, value, paths, { | ||
allowTransformMerging: true, | ||
}); | ||
lookup[declaration[0]] = value; | ||
@@ -962,8 +1023,6 @@ } | ||
} else { | ||
if (typeof props[target] === "object") { | ||
Object.assign(props[target], declaration); | ||
} else { | ||
// Make sure we clone this, as it may be a frozen style object | ||
props[target] = { ...declaration }; | ||
} | ||
// Make sure we clone this, as it may be a frozen style object | ||
assignToTarget(props, { ...declaration }, state.config, { | ||
objectMergeStyle: "assign", | ||
}); | ||
} | ||
@@ -1003,20 +1062,2 @@ } | ||
const transformKeys = new Set([ | ||
"transform", | ||
"translateX", | ||
"translateY", | ||
"scale", | ||
"scaleX", | ||
"scaleY", | ||
"rotate", | ||
"rotateX", | ||
"rotateY", | ||
"rotateZ", | ||
"skewX", | ||
"skewY", | ||
"perspective", | ||
"matrix", | ||
"transformOrigin", | ||
]); | ||
function collectRules( | ||
@@ -1116,28 +1157,2 @@ state: ReducerState, | ||
function setDeep(target: Record<string, any>, paths: string[], value: any) { | ||
const prop = paths[paths.length - 1]; | ||
for (let i = 0; i < paths.length - 1; i++) { | ||
const token = paths[i]; | ||
target[token] ??= {}; | ||
target = target[token]; | ||
} | ||
if (transformKeys.has(prop)) { | ||
if (target.transform) { | ||
const existing = target.transform.find( | ||
(t: any) => Object.keys(t)[0] === prop, | ||
); | ||
if (existing) { | ||
existing[prop] = value; | ||
} else { | ||
target.transform.push({ [prop]: value }); | ||
} | ||
} else { | ||
target.transform ??= []; | ||
target.transform.push({ [prop]: value }); | ||
} | ||
} else { | ||
target[prop] = value; | ||
} | ||
} | ||
/** | ||
@@ -1144,0 +1159,0 @@ * Perform a DeepEqual comparison that cares about order |
@@ -12,2 +12,3 @@ import type { AnimatableValue } from "react-native-reanimated"; | ||
import { rem, vh, vw } from "./unit-observables"; | ||
import { getTargetValue } from "./utils"; | ||
@@ -446,3 +447,3 @@ /** | ||
state.styleLookup[property] ?? | ||
state.props?.[state.config.target]?.[property], | ||
getTargetValue(state.props, state.config)?.[property], | ||
}; | ||
@@ -449,0 +450,0 @@ } |
@@ -65,4 +65,5 @@ import { Appearance } from "react-native"; | ||
if (styleWarnings) { | ||
if (warnings.has(name)) return; | ||
warnings.set(name, styleWarnings); | ||
if (!warnings.has(name)) { | ||
warnings.set(name, styleWarnings); | ||
} | ||
} | ||
@@ -107,7 +108,3 @@ | ||
let obs = store instanceof Map ? store.get(name) : store[name]; | ||
if (!obs) { | ||
obs = cssVariableObservable(undefined); | ||
store instanceof Map ? store.set(name, obs) : (store[name] = obs); | ||
} | ||
return obs.get(effect); | ||
return obs?.get(effect); | ||
} | ||
@@ -158,3 +155,6 @@ | ||
} else { | ||
rootVariables.set(entry[0], cssVariableObservable(entry[1])); | ||
rootVariables.set( | ||
entry[0], | ||
cssVariableObservable(entry[1], { name: entry[0] }), | ||
); | ||
} | ||
@@ -161,0 +161,0 @@ } |
@@ -59,5 +59,5 @@ import type { SharedValue } from "react-native-reanimated"; | ||
export type SharedState = { | ||
initialRender: boolean; | ||
originalProps: Record<string, any> | null; | ||
props: Record<string, any> | null; | ||
guardsEnabled: boolean; | ||
animated: number; | ||
@@ -64,0 +64,0 @@ variables: number; |
@@ -1,6 +0,6 @@ | ||
import { Component, createElement, forwardRef, useState } from "react"; | ||
import { CssInterop, StyleProp, JSXFunction } from "../../types"; | ||
"use client"; | ||
import { Component, createElement, forwardRef } from "react"; | ||
import { CssInterop } from "../../types"; | ||
import { getNormalizeConfig } from "../config"; | ||
import { Effect } from "../observable"; | ||
import { colorScheme } from "./color-scheme"; | ||
import { assignToTarget } from "../native/utils"; | ||
@@ -10,10 +10,5 @@ export { StyleSheet } from "./stylesheet"; | ||
export { rem } from "./rem"; | ||
export const interopComponents = new Map< | ||
object | string, | ||
Parameters<JSXFunction>[0] | ||
>(); | ||
import { interopComponents } from "./interopComponentsMap"; | ||
const ForwardRefSymbol = Symbol.for("react.forward_ref"); | ||
export { useColorScheme } from "./useColorScheme"; | ||
export const cssInterop: CssInterop = (baseComponent, mapping): any => { | ||
@@ -38,25 +33,20 @@ const configs = getNormalizeConfig(mapping); | ||
for (const config of configs) { | ||
let newStyles: StyleProp = []; | ||
const source = props[config.source]; | ||
const target: StyleProp = props[config.target]; | ||
// Ensure we only add non-empty strings | ||
if (typeof source === "string" && source) { | ||
newStyles.push({ | ||
$$css: true, | ||
[source]: source, | ||
} as StyleProp); | ||
assignToTarget( | ||
props, | ||
{ | ||
$$css: true, | ||
[source]: source, | ||
}, | ||
config, | ||
{ | ||
objectMergeStyle: "toArray", | ||
}, | ||
); | ||
} | ||
delete props[config.source]; | ||
if (Array.isArray(target)) { | ||
newStyles.push(...target); | ||
} else if (target) { | ||
newStyles.push(target); | ||
} | ||
if (newStyles.length > 0) { | ||
props[config.target] = newStyles; | ||
} | ||
} | ||
@@ -91,15 +81,2 @@ | ||
export function useColorScheme() { | ||
const [effect, setEffect] = useState<Effect>(() => ({ | ||
run: () => setEffect((s) => ({ ...s })), | ||
dependencies: new Set(), | ||
})); | ||
return { | ||
colorScheme: colorScheme.get(effect), | ||
setColorScheme: colorScheme.set, | ||
toggleColorScheme: colorScheme.toggle, | ||
}; | ||
} | ||
export const useUnstableNativeVariable = (name: string) => { | ||
@@ -106,0 +83,0 @@ if (process.env.NODE_ENV !== "production") { |
118
src/types.ts
@@ -18,3 +18,2 @@ import type { | ||
FunctionComponent, | ||
JSXElementConstructor, | ||
} from "react"; | ||
@@ -39,6 +38,7 @@ import type { ImageStyle, TextStyle, ViewStyle } from "react-native"; | ||
export type InteropComponentConfig = { | ||
target: string; | ||
target: string[]; | ||
inlineProp?: string; | ||
source: string; | ||
removeTarget?: true | undefined; | ||
nativeStyleToProp?: NativeStyleToProp<any>; | ||
propToRemove?: string; | ||
nativeStyleToProp?: Array<[string, string[]]>; | ||
}; | ||
@@ -68,47 +68,77 @@ | ||
export type EnableCssInteropOptions< | ||
T extends keyof JSX.IntrinsicElements | JSXElementConstructor<any>, | ||
> = Record<string, CSSInteropClassNamePropConfig<ComponentProps<T>>>; | ||
export type CssInterop = < | ||
const T extends ReactComponent<any>, | ||
const M extends EnableCssInteropOptions<any>, | ||
const C extends ReactComponent<any>, | ||
const M extends EnableCssInteropOptions<C>, | ||
>( | ||
component: T, | ||
mapping: EnableCssInteropOptions<T> & M, | ||
) => ComponentType<ComponentProps<T> & CssInteropGeneratedProps<M>>; | ||
component: C, | ||
mapping: M & EnableCssInteropOptions<C>, | ||
) => ComponentType< | ||
ComponentProps<C> & { | ||
[K in keyof M as K extends string | ||
? M[K] extends undefined | false | ||
? never | ||
: M[K] extends true | FlattenComponentProps<C> | ||
? K | ||
: M[K] extends | ||
| { | ||
target: FlattenComponentProps<C> | true; | ||
} | ||
| { | ||
target: false; | ||
nativeStyleToProp: Record<string, unknown>; | ||
} | ||
? K | ||
: never | ||
: never]?: string; | ||
} | ||
>; | ||
export type CSSInteropClassNamePropConfig<P> = | ||
| undefined | ||
export type EnableCssInteropOptions<C extends ReactComponent<any>> = Record< | ||
string, | ||
| boolean | ||
| (keyof P & string) | ||
| FlattenComponentProps<C> | ||
| { | ||
target: (keyof P & string) | boolean; | ||
nativeStyleToProp?: NativeStyleToProp<P>; | ||
}; | ||
target: false; | ||
nativeStyleToProp: { | ||
[K in | ||
| (keyof Style & string) | ||
| "fill" | ||
| "stroke"]?: K extends FlattenComponentProps<C> | ||
? FlattenComponentProps<C> | true | ||
: FlattenComponentProps<C>; | ||
}; | ||
} | ||
| { | ||
target: FlattenComponentProps<C> | true; | ||
nativeStyleToProp?: { | ||
[K in | ||
| (keyof Style & string) | ||
| "fill" | ||
| "stroke"]?: K extends FlattenComponentProps<C> | ||
? FlattenComponentProps<C> | true | ||
: FlattenComponentProps<C>; | ||
}; | ||
} | ||
>; | ||
export type CssInteropGeneratedProps<T extends EnableCssInteropOptions<any>> = { | ||
[K in keyof T as K extends string | ||
? T[K] extends undefined | false | ||
? never | ||
: T[K] extends true | string | ||
? K | ||
: T[K] extends | ||
| { | ||
target: string | true; | ||
} | ||
| { | ||
target: false; | ||
nativeStyleToProp: Record<string, true>; | ||
} | ||
? K | ||
: never | ||
: never]?: string; | ||
}; | ||
type FlattenComponentProps<C extends ReactComponent<any>> = FlattenObjectKeys< | ||
ComponentProps<C> | ||
>; | ||
export type NativeStyleToProp<P> = { | ||
[K in (keyof Style | "fill" | "stroke") & string]?: K extends keyof P | ||
? (keyof P & string) | true | ||
: keyof P & string; | ||
}; | ||
type FlattenObjectKeys< | ||
T extends Record<string, unknown>, | ||
Depth extends number[] = [], | ||
MaxDepth extends number = 10, | ||
Key = keyof T, | ||
> = Depth["length"] extends MaxDepth | ||
? never | ||
: Key extends string | ||
? unknown extends T[Key] // If its unknown or any then allow for freeform string | ||
? Key | `${Key}.${string}` | ||
: NonNullable<T[Key]> extends Record<string, unknown> | ||
? | ||
| Key | ||
| `${Key}.${FlattenObjectKeys<NonNullable<T[Key]>, [...Depth, 0]>}` | ||
: Key | ||
: never; | ||
@@ -122,3 +152,3 @@ export type JSXFunction = ( | ||
__self?: unknown, | ||
) => React.ElementType; | ||
) => React.ReactNode; | ||
@@ -214,3 +244,3 @@ type OmitFirstTwo<T extends any[]> = T extends [any, any, ...infer R] | ||
state: PropState; | ||
target: string; | ||
target: string[]; | ||
resetContext: boolean; | ||
@@ -217,0 +247,0 @@ requiresLayout: boolean; |
Sorry, the diff of this file is too big to display
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is too big to display
Sorry, the diff of this file is not supported yet
Network access
Supply chain riskThis module accesses the network.
Found 1 instance in 1 package
Dynamic require
Supply chain riskDynamic require can indicate the package is performing dangerous or unsafe dynamic code execution.
Found 1 instance in 1 package
938816
10.49%273
20.8%19828
7.28%12
33.33%1
Infinity%