Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import * as React from 'react';
import { View } from 'react-native';
import { View, type ViewStyle } from 'react-native';

import { Text } from '@fluentui/react-native';
import { Shadow, getShadowTokenStyleSet } from '@fluentui-react-native/experimental-shadow';
Expand Down Expand Up @@ -42,7 +42,7 @@ const ShadowTestBox: React.FunctionComponent<ShadowTestBoxProps> = (props: Shado

return (
<Shadow shadowToken={props.shadowToken}>
<View style={mergeStyles(themedStyles.shadowTestBox, backgroundColor)}>
<View style={mergeStyles<ViewStyle>(themedStyles.shadowTestBox, backgroundColor)}>
<Text variant="bodySemibold" color={textColor}>
{props.shadowDepthText}
</Text>
Expand All @@ -54,7 +54,7 @@ const ShadowTestBox: React.FunctionComponent<ShadowTestBoxProps> = (props: Shado

export const ShadowDepthTestSection: React.FunctionComponent = () => {
const theme = useFluentTheme();
const backgroundViewStyle = shadowTestPageStyles(theme).backgroundColor;
const backgroundViewStyle = mergeStyles<ViewStyle>(shadowTestPageStyles(theme).backgroundColor);

return (
<View style={backgroundViewStyle}>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import * as React from 'react';
import { View } from 'react-native';
import { View, type ViewStyle } from 'react-native';

import { Text } from '@fluentui/react-native';
import { Shadow } from '@fluentui-react-native/experimental-shadow';
Expand Down Expand Up @@ -66,23 +66,23 @@ export const ShadowWithDifferentPropsTestSection: React.FunctionComponent = () =
</View>
</Shadow>
<Shadow shadowToken={theme.shadows.shadow16}>
<View style={mergeStyles(themedStyles.defaultShadowTestBoxPropsWithoutSpacing, themedStyles.paddingTestProps)}>
<View style={mergeStyles<ViewStyle>(themedStyles.defaultShadowTestBoxPropsWithoutSpacing, themedStyles.paddingTestProps)}>
<Text variant="bodySemibold">padding only</Text>
</View>
</Shadow>
<Shadow shadowToken={theme.shadows.shadow16}>
<View style={mergeStyles(themedStyles.defaultShadowTestBoxPropsWithoutSpacing, themedStyles.marginTestProps)}>
<View style={mergeStyles<ViewStyle>(themedStyles.defaultShadowTestBoxPropsWithoutSpacing, themedStyles.marginTestProps)}>
<Text variant="bodySemibold">margins only</Text>
</View>
</Shadow>
<Shadow shadowToken={theme.shadows.shadow16}>
<View style={mergeStyles(themedStyles.defaultShadowTestBoxProps, themedStyles.borderRadiusTestProps)}>
<View style={mergeStyles<ViewStyle>(themedStyles.defaultShadowTestBoxProps, themedStyles.borderRadiusTestProps)}>
<Text variant="bodySemibold">borderRadius: 8</Text>
</View>
</Shadow>
<Shadow shadowToken={theme.shadows.shadow16}>
<View
style={mergeStyles(
style={mergeStyles<ViewStyle>(
themedStyles.defaultShadowTestBoxProps,
themedStyles.borderRadiusTestProps,
themedStyles.borderWidthTestProps,
Expand All @@ -92,22 +92,22 @@ export const ShadowWithDifferentPropsTestSection: React.FunctionComponent = () =
</View>
</Shadow>
<Shadow shadowToken={theme.shadows.shadow16}>
<View style={mergeStyles(themedStyles.defaultShadowTestBoxProps, themedStyles.offsetTestProps)}>
<View style={mergeStyles<ViewStyle>(themedStyles.defaultShadowTestBoxProps, themedStyles.offsetTestProps)}>
<Text variant="bodySemibold">start: 10</Text>
</View>
</Shadow>
<Shadow shadowToken={theme.shadows.shadow16}>
<View style={mergeStyles(themedStyles.defaultShadowTestBoxProps, themedStyles.alignItemsTestProps)}>
<View style={mergeStyles<ViewStyle>(themedStyles.defaultShadowTestBoxProps, themedStyles.alignItemsTestProps)}>
<Text variant="bodySemibold">alignItems: flex-start</Text>
</View>
</Shadow>
<Shadow shadowToken={theme.shadows.shadow16}>
<View style={mergeStyles(themedStyles.defaultShadowTestBoxProps, themedStyles.flexDirectionTestProps)}>
<View style={mergeStyles<ViewStyle>(themedStyles.defaultShadowTestBoxProps, themedStyles.flexDirectionTestProps)}>
<Text variant="bodySemibold">flexDirection: row</Text>
</View>
</Shadow>
<Shadow shadowToken={theme.shadows.shadow16}>
<View style={mergeStyles(themedStyles.defaultShadowTestBoxProps, themedStyles.flexWrapTestProps)}>
<View style={mergeStyles<ViewStyle>(themedStyles.defaultShadowTestBoxProps, themedStyles.flexWrapTestProps)}>
<Text variant="bodySemibold">flexWrap: wrap</Text>
</View>
</Shadow>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"type": "patch",
"comment": "add template params to mergeStyles for stronger result typing",
"packageName": "@fluentui-react-native/divider",
"email": "jasonmo@microsoft.com",
"dependentChangeType": "patch"
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"type": "patch",
"comment": "add template params to mergeStyles for stronger result typing",
"packageName": "@fluentui-react-native/experimental-shadow",
"email": "jasonmo@microsoft.com",
"dependentChangeType": "patch"
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"type": "patch",
"comment": "add template params to mergeStyles for stronger result typing",
"packageName": "@fluentui-react-native/framework-base",
"email": "jasonmo@microsoft.com",
"dependentChangeType": "patch"
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"type": "patch",
"comment": "add template params to mergeStyles for stronger result typing",
"packageName": "@fluentui-react-native/framework",
"email": "jasonmo@microsoft.com",
"dependentChangeType": "patch"
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"type": "patch",
"comment": "add template params to mergeStyles for stronger result typing",
"packageName": "@fluentui-react-native/icon",
"email": "jasonmo@microsoft.com",
"dependentChangeType": "patch"
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"type": "patch",
"comment": "add template params to mergeStyles for stronger result typing",
"packageName": "@fluentui-react-native/overflow",
"email": "jasonmo@microsoft.com",
"dependentChangeType": "patch"
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"type": "patch",
"comment": "add template params to mergeStyles for stronger result typing",
"packageName": "@fluentui-react-native/tablist",
"email": "jasonmo@microsoft.com",
"dependentChangeType": "patch"
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"type": "patch",
"comment": "add template params to mergeStyles for stronger result typing",
"packageName": "@fluentui-react-native/tester-core",
"email": "jasonmo@microsoft.com",
"dependentChangeType": "patch"
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"type": "patch",
"comment": "add template params to mergeStyles for stronger result typing",
"packageName": "@fluentui-react-native/text",
"email": "jasonmo@microsoft.com",
"dependentChangeType": "patch"
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"type": "patch",
"comment": "add template params to mergeStyles for stronger result typing",
"packageName": "@fluentui-react-native/use-slot",
"email": "jasonmo@microsoft.com",
"dependentChangeType": "patch"
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"type": "patch",
"comment": "add template params to mergeStyles for stronger result typing",
"packageName": "@fluentui-react-native/use-tokens",
"email": "jasonmo@microsoft.com",
"dependentChangeType": "patch"
}
2 changes: 1 addition & 1 deletion docs/pages/Theming/Tokens/UsageWithComponentTokens.md
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ export const Component = (props: TProps) => {
const theme = useFluentTheme();
const [tokens, cache] = useTokens(theme);
const [tokenStyle] = cache(() => ({ ...tokens }), []);
const mergedStyles = mergeStyles(tokenStyle, style);
const mergedStyles = mergeStyles<TextStyle>(tokenStyle, style);

return (
<Text {...rest} style={mergedStyles}>
Expand Down
4 changes: 2 additions & 2 deletions packages/components/Divider/src/Divider.styling.ts
Original file line number Diff line number Diff line change
Expand Up @@ -180,7 +180,7 @@ function getRootStyleWorker(rootStyle: StyleProp<ViewStyle>, isVertical: boolean
if (isVertical) {
minHeight = hasContent ? 84 : globalTokens.size200;
}
return mergeStyles(rootStyle, { minHeight });
return mergeStyles<ViewStyle>(rootStyle, { minHeight });
}

/**
Expand All @@ -190,7 +190,7 @@ function getRootStyleWorker(rootStyle: StyleProp<ViewStyle>, isVertical: boolean
export const getBeforeLineStyle = memoize(getBeforeLineStyleWorker);
function getBeforeLineStyleWorker(beforeLineStyle: StyleProp<ViewStyle>, hasContent: boolean): StyleProp<ViewStyle> {
if (!hasContent) {
return mergeStyles(beforeLineStyle, { flex: 1 });
return mergeStyles<ViewStyle>(beforeLineStyle, { flex: 1 });
}
return beforeLineStyle;
}
2 changes: 1 addition & 1 deletion packages/components/Icon/src/FontIcon/useFontIcon.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ export const useFontIcon = (props: FontIconProps): FontIconProps => {
[color, fontSize, fontFamily],
)[0];

const mergedStyle = mergeStyles(style, styleOrig);
const mergedStyle = mergeStyles<TextStyle>(style, styleOrig);

return {
accessible: accessible ?? true,
Expand Down
3 changes: 2 additions & 1 deletion packages/components/Icon/src/SvgIcon/useSvgIcon.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { getMemoCache, mergeStyles } from '@fluentui-react-native/framework';

import type { SvgIconProps } from './SvgIcon.types';
import type { ImageStyle } from 'react-native';

const rasterImageStyleCache = getMemoCache();

Expand All @@ -9,7 +10,7 @@ export const useSvgIcon = (props: SvgIconProps): SvgIconProps => {
return {
accessible: accessible ?? true,
height,
style: mergeStyles(style, rasterImageStyleCache({ width, height }, [width, height])[0]),
style: mergeStyles<ImageStyle>(style, rasterImageStyleCache({ width, height }, [width, height])[0]),
width,
...rest,
};
Expand Down
6 changes: 3 additions & 3 deletions packages/components/Icon/src/legacy/Icon.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { Image, Platform, View } from 'react-native';
import type { ImageStyle, TextStyle } from 'react-native';
import type { ImageStyle, TextStyle, ViewStyle } from 'react-native';

import { mergeStyles, useFluentTheme } from '@fluentui-react-native/framework';
import { stagedComponent, mergeProps, getMemoCache, getTypedMemoCache } from '@fluentui-react-native/framework';
Expand All @@ -13,7 +13,7 @@ const rasterImageStyleCache = getTypedMemoCache<ImageStyle>();

function renderRasterImage(iconProps: IconProps) {
const { width, height, color } = iconProps;
const style = mergeStyles(
const style = mergeStyles<ImageStyle>(
iconProps.style,
rasterImageStyleCache({ width: width, height: height, tintColor: color }, [width, height, color])[0],
);
Expand Down Expand Up @@ -68,7 +68,7 @@ function renderFontIcon(iconProps: IconProps) {
function renderSvg(iconProps: IconProps) {
const svgIconProps: SvgIconProps = iconProps.svgSource;
const { accessible, accessibilityLabel, width, height, color } = iconProps;
const style = mergeStyles(iconProps.style, rasterImageStyleCache({ width, height }, [width, height])[0]);
const style = mergeStyles<ViewStyle>(iconProps.style, rasterImageStyleCache({ width, height }, [width, height])[0]);

const svgProps: SvgProps = { width, height, color };
if (svgIconProps.viewBox) {
Expand Down
2 changes: 1 addition & 1 deletion packages/components/TabList/src/TabList/useTabList.ts
Original file line number Diff line number Diff line change
Expand Up @@ -123,7 +123,7 @@ export const useTabList = (props: TabListProps): TabListInfo => {

const updateStyles = React.useCallback(
(update: AnimatedIndicatorStyles) => {
setUserDefinedAnimatedIndicatorStyles((prev) => mergeStyles(prev, update));
setUserDefinedAnimatedIndicatorStyles((prev) => mergeStyles<AnimatedIndicatorStyles>(prev, update));
},
[setUserDefinedAnimatedIndicatorStyles],
);
Expand Down
4 changes: 2 additions & 2 deletions packages/components/Text/src/Text.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
/** @jsxImportSource @fluentui-react-native/framework-base */
import React from 'react';
import { I18nManager, Platform, Text as RNText } from 'react-native';
import { I18nManager, Platform, Text as RNText, type TextStyle } from 'react-native';

import type { UseTokens, FontWeightValue } from '@fluentui-react-native/framework';
import { fontStyles, useFluentTheme, mergeStyles, compressible, patchTokens } from '@fluentui-react-native/framework';
Expand Down Expand Up @@ -137,7 +137,7 @@ export const Text = compressible<TextProps, TextTokens>((props: TextProps, useTo
...maxFontSizeScaleAdjustment,
onPress,
numberOfLines: numberOfLines ?? (truncate || !wrap ? 1 : 0),
style: mergeStyles(tokenStyle, props.style, extra?.style),
style: mergeStyles<TextStyle>(tokenStyle, props.style, extra?.style),
};

// RN TextStyle doesn't recognize these properties.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ export const OverflowItem = stagedComponent<OverflowItemProps>((userProps: Overf
}

// Assume that the child can accept ViewProps.
const viewStyles = mergeStyles(child.props.style, mergedProps.style);
const viewStyles = mergeStyles<ViewStyle>(child.props.style, mergedProps.style);
const viewProps = getOverflowItemProps(mergedProps, viewStyles);

const clone = React.cloneElement(child, viewProps);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,7 @@ export function useOverflowItem(props: OverflowItemProps): OverflowItemInfo {
if (!dontHideBeforeReady && !layoutDone) {
stylesToMerge.push({ opacity: 0 });
}
return mergeStyles(...stylesToMerge);
return mergeStyles<ViewStyle>(...stylesToMerge);
}, [controlledSize, dontHideBeforeReady, layoutDone, props.style]);

// Visibility being set extends past the general itemVisibility controlled by the Overflow state.
Expand Down
4 changes: 2 additions & 2 deletions packages/experimental/Shadow/src/__tests__/Shadow.test.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import * as React from 'react';
import { Text, View } from 'react-native';
import { Text, View, type ViewStyle } from 'react-native';

import { mergeStyles, useFluentTheme } from '@fluentui-react-native/framework';
import { Pressable } from '@fluentui-react-native/pressable';
Expand Down Expand Up @@ -46,7 +46,7 @@ const TestShadowOnChildViewWithProps: React.FunctionComponent<ShadowOnChildViewW
const theme = useFluentTheme();
return (
<Shadow shadowToken={theme.shadows['shadow16']}>
<View style={mergeStyles(props.childViewStyleProps, backgroundColor)}>
<View style={mergeStyles<ViewStyle>(props.childViewStyleProps, backgroundColor)}>
<Text>{JSON.stringify(props.childViewStyleProps)}</Text>
</View>
</Shadow>
Expand Down
2 changes: 1 addition & 1 deletion packages/framework-base/src/memo-cache/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -134,7 +134,7 @@ export const MyComponent = (props: IMyComponentProps) => {
}, [theme]);

// merge the styles if a style is passed in via props, caching the union to ensure consistent object identity
newProps.style = newProps.style ? themeLocalCache(() => mergeStyles(style, newProps.style), [newProps.style])[0] : style;
newProps.style = newProps.style ? themeLocalCache(() => mergeStyles<MyStyleType>(style, newProps.style), [newProps.style])[0] : style;

return <InnerControl {...newProps} />;
};
Expand Down
17 changes: 10 additions & 7 deletions packages/framework-base/src/merge-props/mergeStyles.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,32 +9,35 @@ import type { StyleProp } from './mergeStyles.types';
*
* @param style - StyleProp<TStyle> to flatten, this can be a TStyle or an array
*/
export function flattenStyle(style: StyleProp<object>): object {
return Array.isArray(style) ? immutableMerge(...style.map((v) => flattenStyle(v))) : style || {};
export function flattenStyle<T extends object>(style: StyleProp<T>): T {
return Array.isArray(style) ? immutableMerge<T>(...style.map((v) => flattenStyle(v))) : ((style || {}) as T);
}

/**
* Merge styles together into a single flat object and optionally finalize them, can also be used to finalize a single style
*
* @param styles - array of styles to merge together. The styles will be flattened as part of the process
*/
export function mergeAndFlattenStyles(...styles: StyleProp<object>[]): object | undefined {
export function mergeAndFlattenStyles<T extends object>(...styles: StyleProp<object>[]): T | undefined {
// baseline merge and flatten the objects
return immutableMerge(
...styles.map((styleProp: StyleProp<object>) => {
return flattenStyle(styleProp);
}),
);
) as T;
}

const _styleCache = getMemoCache();

export function mergeStyles(...styles: StyleProp<object>[]): object | undefined {
/**
* Function overloads to allow merging of styles of different types
*/
export function mergeStyles<T extends object = object>(...styles: StyleProp<object>[]): T | undefined {
// filter the style set to just objects (which might be arrays or plain style objects)
const inputs = styles.filter((s) => typeof s === 'object') as object[];

// now memo the results if there is more than one element or if the one element is an array
return inputs.length > 1 || (inputs.length === 1 && Array.isArray(inputs[0]))
? _styleCache(() => mergeAndFlattenStyles(undefined, ...inputs), inputs)[0]
: inputs[0] || {};
? _styleCache(() => mergeAndFlattenStyles<T>(undefined, ...inputs), inputs)[0]
: ((inputs[0] || {}) as T);
}
2 changes: 1 addition & 1 deletion packages/framework/framework/src/compressible.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ const VariantText = compressible<VariantTextProps, VariantTextTokens>(
// now apply the alternate layer tokens as appropriate
[tokens, cache] = applyTokenLayers(tokens, ['normal', 'header', 'caption'], cache, (layer) => layer === variant);
// merge styles together with what is passed in
const mergedStyle = mergeStyles({ color: tokens.color, fontSize: tokens.fontSize, fontWeight: tokens.fontWeight }, style);
const mergedStyle = mergeStyles<TextStyle>({ color: tokens.color, fontSize: tokens.fontSize, fontWeight: tokens.fontWeight }, style);
// now get the slot
const InnerText = useSlot<TextProps>(Text, { ...rest, style: mergedStyle });

Expand Down
2 changes: 1 addition & 1 deletion packages/framework/use-slot/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ const StyledText = (props: React.PropsWithChildren<TextWithOverrideProps>) => {
const { children, override, ...rest } = props;

// create a merged set of props. The mergeStyle utility here will avoid creating unnecessary permutations of styles
const mergedProps = { ...rest, style: mergeStyles(baseStyle, rest.style) };
const mergedProps = { ...rest, style: mergeStyles<TextStyle>(baseStyle, rest.style) };

// create a slot that can be used to render, props passed in here will be remembered in render. If override is set the slot will be changed, otherwise Text will be used
const InnerText = useSlot(override || Text, mergedProps);
Expand Down
Loading
Loading