diff --git a/change/@fluentui-react-native-android-theme-03453f4a-1c9c-4c5e-89bb-55fd61335f12.json b/change/@fluentui-react-native-android-theme-03453f4a-1c9c-4c5e-89bb-55fd61335f12.json new file mode 100644 index 0000000000..c1a15f45bd --- /dev/null +++ b/change/@fluentui-react-native-android-theme-03453f4a-1c9c-4c5e-89bb-55fd61335f12.json @@ -0,0 +1,7 @@ +{ + "type": "patch", + "comment": "Move platform theming logic from button component into theme", + "packageName": "@fluentui-react-native/android-theme", + "email": "30809111+acoates-ms@users.noreply.github.com", + "dependentChangeType": "patch" +} diff --git a/change/@fluentui-react-native-apple-theme-cff4f801-872c-4c62-928d-4d612fcfa8ca.json b/change/@fluentui-react-native-apple-theme-cff4f801-872c-4c62-928d-4d612fcfa8ca.json new file mode 100644 index 0000000000..7b3eb41006 --- /dev/null +++ b/change/@fluentui-react-native-apple-theme-cff4f801-872c-4c62-928d-4d612fcfa8ca.json @@ -0,0 +1,7 @@ +{ + "type": "patch", + "comment": "Move platform theming logic from button component into theme", + "packageName": "@fluentui-react-native/apple-theme", + "email": "30809111+acoates-ms@users.noreply.github.com", + "dependentChangeType": "patch" +} diff --git a/change/@fluentui-react-native-button-ea7f7a18-e30a-44e1-acfc-119e1cbf4cc2.json b/change/@fluentui-react-native-button-ea7f7a18-e30a-44e1-acfc-119e1cbf4cc2.json new file mode 100644 index 0000000000..fbbc5a445e --- /dev/null +++ b/change/@fluentui-react-native-button-ea7f7a18-e30a-44e1-acfc-119e1cbf4cc2.json @@ -0,0 +1,7 @@ +{ + "type": "patch", + "comment": "Move platform theming logic from button component into theme", + "packageName": "@fluentui-react-native/button", + "email": "30809111+acoates-ms@users.noreply.github.com", + "dependentChangeType": "patch" +} diff --git a/change/@fluentui-react-native-default-theme-7bbf18ff-9471-4960-9f7d-f10ac98fc092.json b/change/@fluentui-react-native-default-theme-7bbf18ff-9471-4960-9f7d-f10ac98fc092.json new file mode 100644 index 0000000000..ce56f13b6a --- /dev/null +++ b/change/@fluentui-react-native-default-theme-7bbf18ff-9471-4960-9f7d-f10ac98fc092.json @@ -0,0 +1,7 @@ +{ + "type": "patch", + "comment": "Move platform theming logic from button component into theme", + "packageName": "@fluentui-react-native/default-theme", + "email": "30809111+acoates-ms@users.noreply.github.com", + "dependentChangeType": "patch" +} diff --git a/change/@fluentui-react-native-theme-tokens-f2d0f959-a06a-406c-93ee-f6936d7cfc9b.json b/change/@fluentui-react-native-theme-tokens-f2d0f959-a06a-406c-93ee-f6936d7cfc9b.json new file mode 100644 index 0000000000..47b59d6ad3 --- /dev/null +++ b/change/@fluentui-react-native-theme-tokens-f2d0f959-a06a-406c-93ee-f6936d7cfc9b.json @@ -0,0 +1,7 @@ +{ + "type": "patch", + "comment": "Move platform theming logic from button component into theme", + "packageName": "@fluentui-react-native/theme-tokens", + "email": "30809111+acoates-ms@users.noreply.github.com", + "dependentChangeType": "patch" +} diff --git a/change/@fluentui-react-native-win32-theme-3c9bf79a-4eba-490d-b712-352ccc9888c0.json b/change/@fluentui-react-native-win32-theme-3c9bf79a-4eba-490d-b712-352ccc9888c0.json new file mode 100644 index 0000000000..b531bd135e --- /dev/null +++ b/change/@fluentui-react-native-win32-theme-3c9bf79a-4eba-490d-b712-352ccc9888c0.json @@ -0,0 +1,7 @@ +{ + "type": "patch", + "comment": "Move platform theming logic from button component into theme", + "packageName": "@fluentui-react-native/win32-theme", + "email": "30809111+acoates-ms@users.noreply.github.com", + "dependentChangeType": "patch" +} diff --git a/packages/components/Button/src/Button.styling.ts b/packages/components/Button/src/Button.styling.ts index dc98c68ec9..48a8e5265b 100644 --- a/packages/components/Button/src/Button.styling.ts +++ b/packages/components/Button/src/Button.styling.ts @@ -8,10 +8,7 @@ import { borderStyles, layoutStyles, fontStyles } from '@fluentui-react-native/t import type { FontTokens } from '@fluentui-react-native/tokens'; import { buttonName } from './Button.types'; -import type { ButtonTokens, ButtonSlotProps, ButtonProps, ButtonSize, ButtonAppearance } from './Button.types'; -import { defaultButtonColorTokens } from './ButtonColorTokens'; -import { defaultButtonFontTokens } from './ButtonFontTokens'; -import { defaultButtonTokens } from './ButtonTokens'; +import type { ButtonTokens, ButtonSlotProps, ButtonProps } from './Button.types'; export const buttonStates: (keyof ButtonTokens)[] = [ 'block', @@ -34,7 +31,7 @@ export const buttonStates: (keyof ButtonTokens)[] = [ ]; export const stylingSettings: UseStylingOptions = { - tokens: [defaultButtonTokens, defaultButtonFontTokens, defaultButtonColorTokens, buttonName], + tokens: [buttonName], states: buttonStates, slotProps: { ...(Platform.OS === 'android' && { @@ -105,38 +102,6 @@ export const stylingSettings: UseStylingOptions { - if (Platform.OS === 'windows') { - return 'medium'; - } else if ((Platform.OS as any) === 'win32') { - return 'small'; - } - - return 'medium'; -}; - -export const getPlatformSpecificAppearance = (appearance: ButtonAppearance): ButtonAppearance => { - // Mobile platforms do not have seperate styling when no appearance is passed. - const hasDifferentDefaultAppearance = !(Platform.OS === 'android' || Platform.OS === 'ios'); - - switch (appearance) { - case 'accent': // Included to cover Mobile platform naming guidelines, maps to 'primary'. - return 'primary'; - - case 'primary': - case 'subtle': - case 'outline': // 'Outline' exists only for Mobile platforms, default picked on other platforms. - return appearance; - - default: - if (hasDifferentDefaultAppearance) { - return null; - } else { - return 'primary'; - } - } -}; - export const contentStyling = (tokens: ButtonTokens, theme: Theme, contentColor: ColorValue, fontStylesTokens: FontTokens) => { const textAdjustment = getTextMarginAdjustment(); const spacingIconContentBefore = tokens.spacingIconContentBefore diff --git a/packages/components/Button/src/Button.tsx b/packages/components/Button/src/Button.tsx index 7e498fbf9a..34efbed4e4 100644 --- a/packages/components/Button/src/Button.tsx +++ b/packages/components/Button/src/Button.tsx @@ -6,13 +6,15 @@ import { Platform, Pressable, View } from 'react-native'; import { ActivityIndicator } from '@fluentui-react-native/experimental-activity-indicator'; import type { UseSlots } from '@fluentui-react-native/framework'; import { compose, memoize, mergeProps, withSlots } from '@fluentui-react-native/framework'; +import { useFluentTheme } from '@fluentui-react-native/framework'; import { Icon, createIconProps } from '@fluentui-react-native/icon'; import type { IPressableState } from '@fluentui-react-native/interactive-hooks'; import { TextV1 as Text } from '@fluentui-react-native/text'; +import type { Theme } from '@fluentui-react-native/theme-types'; -import { stylingSettings, getDefaultSize, getPlatformSpecificAppearance } from './Button.styling'; +import { stylingSettings } from './Button.styling'; import { buttonName } from './Button.types'; -import type { ButtonType, ButtonProps } from './Button.types'; +import type { ButtonType, ButtonProps, ButtonTokens } from './Button.types'; import { extractOuterStylePropsAndroid } from './ExtractStyle.android'; import { useButton } from './useButton'; @@ -22,15 +24,18 @@ import { useButton } from './useButton'; * @param layer The name of the state that is being checked for * @param state The current state of the button * @param userProps The props that were passed into the button + * @param theme The theme * @returns Whether the styles that are assigned to the layer should be applied to the button */ -export const buttonLookup = (layer: string, state: IPressableState, userProps: ButtonProps): boolean => { +export const buttonLookup = (layer: string, state: IPressableState, userProps: ButtonProps, theme: Theme): boolean => { + const size = userProps['size'] ?? (theme?.components?.['Button'] as ButtonTokens)?.size ?? 'medium'; + const getPlatformSpecificAppearance = (theme?.components?.['Button'] as ButtonTokens)?.getPlatformSpecificAppearance; + const appearance = getPlatformSpecificAppearance ? getPlatformSpecificAppearance(userProps['appearance']) : null; return ( state[layer] || userProps[layer] || - layer === getPlatformSpecificAppearance(userProps['appearance']) || - layer === userProps['size'] || - (!userProps['size'] && layer === getDefaultSize()) || + layer === appearance || + layer === size || layer === userProps['shape'] || (!userProps['shape'] && layer === 'rounded') || (layer === 'hovered' && state[layer] && !userProps.loading) || @@ -52,10 +57,11 @@ export const Button = compose({ }, useRender: (userProps: ButtonProps, useSlots: UseSlots) => { const button = useButton(userProps); + const theme = useFluentTheme(); const iconProps = createIconProps(userProps.icon); // grab the styled slots - const Slots = useSlots(userProps, (layer) => buttonLookup(layer, button.state, userProps)); + const Slots = useSlots(userProps, (layer) => buttonLookup(layer, button.state, userProps, theme)); // now return the handler for finishing render return (final: ButtonProps, ...children: React.ReactNode[]) => { diff --git a/packages/components/Button/src/Button.types.ts b/packages/components/Button/src/Button.types.ts index 78ac37f033..7bcb5398a2 100644 --- a/packages/components/Button/src/Button.types.ts +++ b/packages/components/Button/src/Button.types.ts @@ -66,6 +66,11 @@ export interface ButtonCoreTokens extends LayoutTokens, FontTokens, IBorderToken borderInnerWidth?: number; borderInnerRadius?: number; borderInnerStyle?: ViewStyle['borderStyle']; + + /** + * The default size of the button + */ + size?: ButtonSize; } export interface ButtonTokens extends ButtonCoreTokens { @@ -89,6 +94,11 @@ export interface ButtonTokens extends ButtonCoreTokens { circular?: ButtonTokens; square?: ButtonTokens; hasIconAfter?: ButtonTokens; + + /** + * Returns the default appearance type, and can remap ButtonAppearances to other Appearances for this theme + */ + getPlatformSpecificAppearance?: (appearance: ButtonAppearance) => ButtonAppearance; } export interface ButtonCoreProps extends Omit { diff --git a/packages/components/Button/src/ButtonColorTokens.android.ts b/packages/components/Button/src/ButtonColorTokens.android.ts deleted file mode 100644 index 077f8f97bb..0000000000 --- a/packages/components/Button/src/ButtonColorTokens.android.ts +++ /dev/null @@ -1,81 +0,0 @@ -import type { Theme } from '@fluentui-react-native/framework'; -import type { TokenSettings } from '@fluentui-react-native/use-styling'; - -import type { ButtonTokens } from './Button.types'; - -export const defaultButtonColorTokens: TokenSettings = (t: Theme) => - ({ - /** Android does not have a different styles for 'default' button. - * 'primary', 'accent' and if no appearance is mentioned, picks this. - */ - backgroundColor: t.colors.brandBackground, - rippleColor: '#D4D4D4', - color: t.colors.neutralForegroundOnColor, - iconColor: t.colors.neutralForegroundOnColor, - disabled: { - backgroundColor: t.colors.neutralBackground5, - color: t.colors.neutralForegroundDisabled1, - iconColor: t.colors.neutralForegroundDisabled1, - }, - pressed: { - backgroundColor: t.colors.brandBackgroundPressed, - color: t.colors.neutralForegroundOnColor, - iconColor: t.colors.neutralForegroundOnColor, - }, - focused: { - backgroundColor: t.colors.brandBackground, - color: t.colors.neutralForegroundOnColor, - borderColor: t.colors.strokeFocus2, - borderInnerColor: t.colors.strokeFocus1, - iconColor: t.colors.neutralForegroundOnColor, - }, - subtle: { - backgroundColor: 'transparent', - rippleColor: '#D4D4D4', - color: t.colors.brandForeground1, - iconColor: t.colors.brandForeground1, - disabled: { - backgroundColor: 'transparent', - color: t.colors.neutralForegroundDisabled1, - iconColor: t.colors.neutralForegroundDisabled1, - }, - pressed: { - backgroundColor: 'transparent', - color: t.colors.brandForeground1Pressed, - iconColor: t.colors.brandForeground1Pressed, - }, - focused: { - backgroundColor: 'transparent', - borderColor: t.colors.strokeFocus2, - borderInnerColor: t.colors.strokeFocus1, - color: t.colors.brandForeground1, - iconColor: t.colors.brandForeground1, - }, - }, - outline: { - backgroundColor: 'transparent', - rippleColor: '#D4D4D4', - color: t.colors.brandForeground1, - iconColor: t.colors.brandForeground1, - borderColor: t.colors.brandStroke1, - disabled: { - backgroundColor: 'transparent', - color: t.colors.neutralForegroundDisabled1, - iconColor: t.colors.neutralForegroundDisabled1, - borderColor: t.colors.neutralStrokeDisabled, - }, - pressed: { - backgroundColor: 'transparent', - color: t.colors.brandForeground1Pressed, - iconColor: t.colors.brandForeground1Pressed, - borderColor: t.colors.brandStroke1Pressed, - }, - focused: { - backgroundColor: 'transparent', - borderColor: t.colors.strokeFocus2, - borderInnerColor: t.colors.strokeFocus1, - color: t.colors.brandForeground1, - iconColor: t.colors.brandForeground1, - }, - }, - } as ButtonTokens); diff --git a/packages/components/Button/src/ButtonColorTokens.macos.ts b/packages/components/Button/src/ButtonColorTokens.macos.ts deleted file mode 100644 index acb09755b9..0000000000 --- a/packages/components/Button/src/ButtonColorTokens.macos.ts +++ /dev/null @@ -1,96 +0,0 @@ -import type { Theme } from '@fluentui-react-native/framework'; -import type { TokenSettings } from '@fluentui-react-native/use-styling'; - -import type { ButtonTokens } from './Button.types'; - -export const defaultButtonColorTokens: TokenSettings = (t: Theme) => - ({ - backgroundColor: t.colors.defaultBackground, - color: t.colors.defaultContent, - borderColor: t.colors.defaultBorder, - iconColor: t.colors.defaultContent, - disabled: { - backgroundColor: t.colors.defaultDisabledBackground, - color: t.colors.defaultDisabledContent, - borderColor: t.colors.defaultDisabledBorder, - iconColor: t.colors.defaultDisabledIcon, - }, - hovered: { - backgroundColor: t.colors.defaultBackground, - color: t.colors.defaultContent, - borderColor: t.colors.defaultBorder, - iconColor: t.colors.defaultHoveredIcon, - }, - pressed: { - backgroundColor: t.colors.defaultPressedBackground, - color: t.colors.defaultPressedContent, - borderColor: t.colors.defaultBorder, - iconColor: t.colors.defaultPressedIcon, - }, - focused: { - backgroundColor: t.colors.defaultFocusedBackground, - color: t.colors.defaultFocusedContent, - borderColor: t.colors.defaultFocusedBorder, - icon: t.colors.defaultFocusedIcon, - }, - primary: { - backgroundColor: t.colors.brandBackground, - color: t.colors.neutralForegroundOnBrand, - borderColor: t.colors.brandStroke1, - iconColor: t.colors.neutralForegroundOnBrand, - disabled: { - backgroundColor: t.colors.neutralBackgroundDisabled, - color: t.colors.neutralForegroundDisabled, - borderColor: t.colors.neutralStrokeDisabled, - iconColor: t.colors.neutralForegroundDisabled, - }, - hovered: { - backgroundColor: t.colors.brandBackgroundHover, - color: t.colors.neutralForegroundOnBrandHover, - borderColor: t.colors.brandBackgroundHover, - iconColor: t.colors.neutralForegroundOnBrandHover, - }, - pressed: { - backgroundColor: t.colors.brandBackgroundPressed, - color: t.colors.neutralForegroundOnBrandPressed, - borderColor: t.colors.brandBackgroundPressed, - iconColor: t.colors.neutralForegroundOnBrandPressed, - }, - focused: { - backgroundColor: t.colors.brandBackgroundHover, - color: t.colors.neutralForegroundOnBrandHover, - borderColor: t.colors.strokeFocus2, - iconColor: t.colors.neutralForegroundOnBrandHover, - }, - }, - subtle: { - backgroundColor: t.colors.ghostBackground, - color: t.colors.ghostContent, - borderColor: t.colors.ghostBorder, - iconColor: t.colors.ghostIcon, - disabled: { - color: t.colors.ghostDisabledContent, - borderColor: t.colors.ghostDisabledBorder, - backgroundColor: t.colors.ghostDisabledBackground, - iconColor: t.colors.ghostDisabledIcon, - }, - hovered: { - backgroundColor: t.colors.ghostHoveredBackground, - color: t.colors.ghostHoveredContent, - borderColor: t.colors.ghostHoveredBorder, - iconColor: t.colors.ghostHoveredIcon, - }, - pressed: { - backgroundColor: t.colors.ghostPressedBackground, - borderColor: t.colors.ghostPressedBorder, - color: t.colors.ghostPressedContent, - icon: t.colors.ghostPressedIcon, - }, - focused: { - borderColor: t.colors.ghostFocusedBorder, - backgroundColor: t.colors.ghostFocusedBackground, - color: t.colors.ghostFocusedContent, - icon: t.colors.ghostFocusedIcon, - }, - }, - } as ButtonTokens); diff --git a/packages/components/Button/src/ButtonColorTokens.ts b/packages/components/Button/src/ButtonColorTokens.ts deleted file mode 100644 index b2a6f4461a..0000000000 --- a/packages/components/Button/src/ButtonColorTokens.ts +++ /dev/null @@ -1,88 +0,0 @@ -import type { Theme } from '@fluentui-react-native/framework'; -import type { TokenSettings } from '@fluentui-react-native/use-styling'; - -import type { ButtonTokens } from './Button.types'; - -export const defaultButtonColorTokens: TokenSettings = (t: Theme) => - ({ - backgroundColor: t.colors.buttonBackground, - color: t.colors.buttonText, - borderColor: t.colors.buttonBorder, - iconColor: t.colors.buttonIcon, - disabled: { - backgroundColor: t.colors.defaultDisabledBackground, - color: t.colors.defaultDisabledContent, - borderColor: t.colors.defaultDisabledBorder, - iconColor: t.colors.defaultDisabledIcon, - }, - hovered: { - backgroundColor: t.colors.defaultHoveredBackground, - color: t.colors.defaultHoveredContent, - borderColor: t.colors.defaultHoveredBorder, - iconColor: t.colors.defaultHoveredIcon, - }, - pressed: { - backgroundColor: t.colors.defaultPressedBackground, - color: t.colors.defaultPressedContent, - borderColor: t.colors.defaultPressedBorder, - iconColor: t.colors.defaultPressedIcon, - }, - focused: { - backgroundColor: t.colors.defaultFocusedBackground, - color: t.colors.defaultFocusedContent, - borderColor: t.colors.defaultFocusedBorder, - icon: t.colors.defaultFocusedIcon, - }, - primary: { - backgroundColor: t.colors.brandBackground, - color: t.colors.neutralForegroundOnColor, - borderColor: t.colors.brandStroke1, - iconColor: t.colors.neutralForegroundOnColor, - disabled: { - backgroundColor: t.colors.brandBackgroundDisabled, - color: t.colors.neutralForegroundDisabled1, - iconColor: t.colors.neutralForegroundDisabled1, - }, - pressed: { - backgroundColor: t.colors.brandBackgroundPressed, - color: t.colors.neutralForegroundOnColor, - iconColor: t.colors.neutralForegroundOnColor, - }, - focused: { - backgroundColor: t.colors.brandBackground, - color: t.colors.neutralForegroundOnColor, - borderColor: t.colors.strokeFocus2, - iconColor: t.colors.neutralForegroundOnColor, - }, - }, - subtle: { - backgroundColor: t.colors.ghostBackground, - color: t.colors.ghostContent, - borderColor: t.colors.ghostBorder, - iconColor: t.colors.ghostIcon, - disabled: { - color: t.colors.ghostDisabledContent, - borderColor: t.colors.ghostDisabledBorder, - backgroundColor: t.colors.ghostDisabledBackground, - iconColor: t.colors.ghostDisabledIcon, - }, - hovered: { - backgroundColor: t.colors.ghostHoveredBackground, - color: t.colors.ghostHoveredContent, - borderColor: t.colors.ghostHoveredBorder, - iconColor: t.colors.ghostHoveredIcon, - }, - pressed: { - backgroundColor: t.colors.ghostPressedBackground, - borderColor: t.colors.ghostPressedBorder, - color: t.colors.ghostPressedContent, - icon: t.colors.ghostPressedIcon, - }, - focused: { - borderColor: t.colors.ghostFocusedBorder, - backgroundColor: t.colors.ghostFocusedBackground, - color: t.colors.ghostFocusedContent, - icon: t.colors.ghostFocusedIcon, - }, - }, - } as ButtonTokens); diff --git a/packages/components/Button/src/ButtonFontTokens.android.ts b/packages/components/Button/src/ButtonFontTokens.android.ts deleted file mode 100644 index fa93871e5f..0000000000 --- a/packages/components/Button/src/ButtonFontTokens.android.ts +++ /dev/null @@ -1,23 +0,0 @@ -import type { Theme } from '@fluentui-react-native/framework'; -import type { TokenSettings } from '@fluentui-react-native/use-styling'; - -import type { ButtonTokens } from './Button.types'; - -export const defaultButtonFontTokens: TokenSettings = (t: Theme) => - ({ - medium: { - fontSize: t.typography.variants.body2Strong.size, - fontFamily: t.typography.variants.body2Strong.face, - fontWeight: t.typography.variants.body2Strong.weight, - }, - small: { - fontSize: t.typography.variants.body2Strong.size, - fontFamily: t.typography.variants.body2Strong.face, - fontWeight: t.typography.variants.body2Strong.weight, - }, - large: { - fontSize: t.typography.variants.body1Strong.size, - fontFamily: t.typography.variants.body1Strong.face, - fontWeight: t.typography.variants.body1Strong.weight, - }, - } as ButtonTokens); diff --git a/packages/components/Button/src/ButtonFontTokens.ios.ts b/packages/components/Button/src/ButtonFontTokens.ios.ts deleted file mode 100644 index 664da18b12..0000000000 --- a/packages/components/Button/src/ButtonFontTokens.ios.ts +++ /dev/null @@ -1,23 +0,0 @@ -import type { Theme } from '@fluentui-react-native/framework'; -import type { TokenSettings } from '@fluentui-react-native/use-styling'; - -import type { ButtonTokens } from './Button.types'; - -export const defaultButtonFontTokens: TokenSettings = (t: Theme) => - ({ - medium: { - fontSize: t.typography.variants.caption1Strong.size, - fontFamily: t.typography.variants.caption1Strong.face, - fontWeight: t.typography.variants.caption1Strong.weight, - }, - small: { - fontSize: t.typography.variants.caption1Strong.size, - fontFamily: t.typography.variants.caption1Strong.face, - fontWeight: t.typography.variants.caption1Strong.weight, - }, - large: { - fontSize: t.typography.variants.body1Strong.size, - fontFamily: t.typography.variants.body1Strong.face, - fontWeight: t.typography.variants.body1Strong.weight, - }, - } as ButtonTokens); diff --git a/packages/components/Button/src/ButtonFontTokens.macos.ts b/packages/components/Button/src/ButtonFontTokens.macos.ts deleted file mode 100644 index 47bcb99859..0000000000 --- a/packages/components/Button/src/ButtonFontTokens.macos.ts +++ /dev/null @@ -1,21 +0,0 @@ -import type { Theme } from '@fluentui-react-native/framework'; -import type { TokenSettings } from '@fluentui-react-native/use-styling'; - -import type { ButtonTokens } from './Button.types'; - -export const defaultButtonFontTokens: TokenSettings = (_t: Theme) => - ({ - medium: { - hasContent: { - variant: 'bodyStandard', - }, - }, - small: { - hasContent: { - variant: 'secondaryStandard', - }, - }, - large: { - variant: 'subheaderSemibold', - }, - } as ButtonTokens); diff --git a/packages/components/Button/src/ButtonFontTokens.ts b/packages/components/Button/src/ButtonFontTokens.ts deleted file mode 100644 index 2208b003ce..0000000000 --- a/packages/components/Button/src/ButtonFontTokens.ts +++ /dev/null @@ -1,21 +0,0 @@ -import type { Theme } from '@fluentui-react-native/framework'; -import type { TokenSettings } from '@fluentui-react-native/use-styling'; - -import type { ButtonTokens } from './Button.types'; - -export const defaultButtonFontTokens: TokenSettings = (_t: Theme) => - ({ - medium: { - hasContent: { - variant: 'bodySemibold', - }, - }, - small: { - hasContent: { - variant: 'secondaryStandard', - }, - }, - large: { - variant: 'subheaderSemibold', - }, - } as ButtonTokens); diff --git a/packages/components/Button/src/ButtonFontTokens.win32.ts b/packages/components/Button/src/ButtonFontTokens.win32.ts deleted file mode 100644 index 0d89579b40..0000000000 --- a/packages/components/Button/src/ButtonFontTokens.win32.ts +++ /dev/null @@ -1,30 +0,0 @@ -import type { Theme } from '@fluentui-react-native/framework'; -import { globalTokens } from '@fluentui-react-native/theme-tokens'; -import type { TokenSettings } from '@fluentui-react-native/use-styling'; - -import type { ButtonTokens } from './Button.types'; - -export const defaultButtonFontTokens: TokenSettings = (t: Theme) => - ({ - medium: { - hasContent: { - fontFamily: t.typography.families.secondary, - fontSize: globalTokens.font.size300, - fontWeight: globalTokens.font.weight.semibold, - }, - }, - small: { - hasContent: { - fontFamily: t.typography.families.primary, - fontSize: globalTokens.font.size200, - fontWeight: globalTokens.font.weight.regular, - }, - }, - large: { - hasContent: { - fontFamily: t.typography.families.secondary, - fontSize: globalTokens.font.size400, - fontWeight: globalTokens.font.weight.semibold, - }, - }, - } as ButtonTokens); diff --git a/packages/components/Button/src/ButtonTokens.android.ts b/packages/components/Button/src/ButtonTokens.android.ts deleted file mode 100644 index d8cc066604..0000000000 --- a/packages/components/Button/src/ButtonTokens.android.ts +++ /dev/null @@ -1,83 +0,0 @@ -import type { Theme } from '@fluentui-react-native/framework'; -import { globalTokens } from '@fluentui-react-native/theme-tokens'; -import type { TokenSettings } from '@fluentui-react-native/use-styling'; - -import type { ButtonTokens } from './Button.types'; - -export const defaultButtonTokens: TokenSettings = () => - ({ - focused: { - borderWidth: globalTokens.stroke.width20, - borderInnerWidth: globalTokens.stroke.width10, - }, - subtle: { - focused: { - borderWidth: globalTokens.stroke.width20, - borderInnerWidth: globalTokens.stroke.width10, - }, - }, - outline: { - borderWidth: globalTokens.stroke.width10, - disabled: { - borderWidth: globalTokens.stroke.width10, - }, - pressed: { - borderWidth: globalTokens.stroke.width10, - }, - focused: { - borderWidth: globalTokens.stroke.width20, - borderInnerWidth: globalTokens.stroke.width10, - }, - }, - block: { - width: '100%', - }, - large: { - paddingHorizontal: globalTokens.size200, - borderRadius: globalTokens.corner.radius80, - iconSize: 20, - outline: { - borderWidth: globalTokens.stroke.width10, - iconSize: 20, - }, - spacingIconContentBefore: globalTokens.size80, - spacingIconContentAfter: globalTokens.size80, - minHeight: 48, - minWidth: 36, - }, - medium: { - paddingHorizontal: globalTokens.size120, - borderRadius: globalTokens.corner.radius40, - iconSize: 20, - outline: { - borderWidth: globalTokens.stroke.width10, - iconSize: 20, - }, - spacingIconContentBefore: globalTokens.size80, - spacingIconContentAfter: globalTokens.size80, - minHeight: 36, - minWidth: 36, - }, - small: { - paddingHorizontal: globalTokens.size80, - borderRadius: globalTokens.corner.radius40, - iconSize: 16, - outline: { - borderWidth: globalTokens.stroke.width10, - iconSize: 16, - }, - spacingIconContentBefore: globalTokens.size40, - spacingIconContentAfter: globalTokens.size40, - minHeight: 28, - minWidth: 28, - }, - rounded: { - borderRadius: globalTokens.corner.radius40, - }, - circular: { - borderRadius: globalTokens.corner.radiusCircular, - }, - square: { - borderRadius: globalTokens.corner.radiusNone, - }, - } as ButtonTokens); diff --git a/packages/components/Button/src/ButtonTokens.ios.ts b/packages/components/Button/src/ButtonTokens.ios.ts deleted file mode 100644 index 60f50babd9..0000000000 --- a/packages/components/Button/src/ButtonTokens.ios.ts +++ /dev/null @@ -1,76 +0,0 @@ -import type { Theme } from '@fluentui-react-native/framework'; -import { globalTokens } from '@fluentui-react-native/theme-tokens'; -import type { TokenSettings } from '@fluentui-react-native/use-styling'; - -import type { ButtonTokens } from './Button.types'; - -export const defaultButtonTokens: TokenSettings = () => - ({ - block: { - width: '100%', - }, - medium: { - paddingHorizontal: globalTokens.size120, - borderWidth: globalTokens.stroke.width10, - borderRadius: globalTokens.corner.radius80, - minHeight: 40, - iconSize: 20, - focused: { - borderWidth: 0, - }, - hasContent: { - minWidth: 96, - hasIconAfter: { - spacingIconContentAfter: globalTokens.size80, - }, - hasIconBefore: { - spacingIconContentBefore: globalTokens.size80, - }, - }, - }, - small: { - paddingHorizontal: globalTokens.size60, - borderWidth: globalTokens.stroke.width10, - borderRadius: globalTokens.corner.radius80, - minHeight: 28, - iconSize: 16, - focused: { - borderWidth: 0, - }, - hasContent: { - minWidth: 64, - minHeight: 28, - hasIconAfter: { - spacingIconContentAfter: globalTokens.size40, - }, - hasIconBefore: { - spacingIconContentBefore: globalTokens.size40, - }, - }, - }, - large: { - paddingHorizontal: globalTokens.size160, - borderWidth: globalTokens.stroke.width10, - iconSize: 20, - borderRadius: globalTokens.corner.radius120, - minHeight: 52, - focused: { - borderWidth: 0, - }, - hasContent: { - minWidth: 96, - hasIconAfter: { - spacingIconContentAfter: globalTokens.size80, - }, - hasIconBefore: { - spacingIconContentBefore: globalTokens.size80, - }, - }, - }, - circular: { - borderRadius: globalTokens.corner.radiusCircular, - }, - square: { - borderRadius: globalTokens.corner.radiusNone, - }, - } as ButtonTokens); diff --git a/packages/components/Button/src/ButtonTokens.ts b/packages/components/Button/src/ButtonTokens.ts deleted file mode 100644 index fe6b6e7d35..0000000000 --- a/packages/components/Button/src/ButtonTokens.ts +++ /dev/null @@ -1,88 +0,0 @@ -import type { Theme } from '@fluentui-react-native/framework'; -import { globalTokens } from '@fluentui-react-native/theme-tokens'; -import type { TokenSettings } from '@fluentui-react-native/use-styling'; - -import type { ButtonTokens } from './Button.types'; - -export const defaultButtonTokens: TokenSettings = () => - ({ - block: { - width: '100%', - }, - medium: { - padding: globalTokens.size60 - globalTokens.stroke.width10, - borderWidth: globalTokens.stroke.width10, - iconSize: 16, - focused: { - borderWidth: 0, - padding: globalTokens.size60, - }, - hasContent: { - minWidth: 96, - paddingHorizontal: globalTokens.size120 - globalTokens.stroke.width10, - hasIconAfter: { - spacingIconContentAfter: globalTokens.size60, - }, - hasIconBefore: { - spacingIconContentBefore: globalTokens.size60, - }, - focused: { - paddingHorizontal: globalTokens.size120, - }, - }, - }, - small: { - padding: globalTokens.size40 - globalTokens.stroke.width10, - borderWidth: globalTokens.stroke.width10, - iconSize: 16, - focused: { - borderWidth: 0, - padding: globalTokens.size40, - }, - hasContent: { - minWidth: 64, - minHeight: 24, - paddingHorizontal: globalTokens.size80 - globalTokens.stroke.width10, - hasIconAfter: { - spacingIconContentAfter: globalTokens.size40, - }, - hasIconBefore: { - spacingIconContentBefore: globalTokens.size40, - }, - focused: { - paddingHorizontal: globalTokens.size80, - }, - }, - }, - large: { - padding: globalTokens.size80 - globalTokens.stroke.width10, - borderWidth: globalTokens.stroke.width10, - iconSize: 20, - focused: { - borderWidth: 0, - padding: globalTokens.size80, - }, - hasContent: { - minWidth: 96, - paddingHorizontal: globalTokens.size160 - globalTokens.stroke.width10, - hasIconAfter: { - spacingIconContentAfter: globalTokens.size60, - }, - hasIconBefore: { - spacingIconContentBefore: globalTokens.size60, - }, - focused: { - paddingHorizontal: globalTokens.size160, - }, - }, - }, - rounded: { - borderRadius: globalTokens.corner.radius40, - }, - circular: { - borderRadius: globalTokens.corner.radiusCircular, - }, - square: { - borderRadius: globalTokens.corner.radiusNone, - }, - } as ButtonTokens); diff --git a/packages/components/Button/src/ButtonTokens.win32.ts b/packages/components/Button/src/ButtonTokens.win32.ts deleted file mode 100644 index fcb96da30b..0000000000 --- a/packages/components/Button/src/ButtonTokens.win32.ts +++ /dev/null @@ -1,166 +0,0 @@ -import type { Theme } from '@fluentui-react-native/framework'; -import { globalTokens } from '@fluentui-react-native/theme-tokens'; -import { isHighContrast } from '@fluentui-react-native/theming-utils'; -import type { TokenSettings } from '@fluentui-react-native/use-styling'; - -import type { ButtonTokens } from './Button.types'; - -export const defaultButtonTokens: TokenSettings = (theme: Theme) => - ({ - borderWidth: globalTokens.stroke.width10, - borderInnerWidth: globalTokens.stroke.width10, - block: { - width: '100%', - }, - medium: { - padding: globalTokens.size80 - globalTokens.stroke.width10, - iconSize: 16, - focused: { - borderWidth: 0, - padding: globalTokens.size80, - }, - primary: !isHighContrast(theme) && { - focused: { - borderWidth: globalTokens.stroke.width20, - padding: globalTokens.size80 - globalTokens.stroke.width20, - }, - square: { - focused: { - borderWidth: globalTokens.stroke.width10, - padding: globalTokens.size80 - globalTokens.stroke.width10, - }, - }, - }, - hasContent: { - minWidth: 96, - padding: globalTokens.size60 - globalTokens.stroke.width10, - paddingHorizontal: globalTokens.size120 - globalTokens.stroke.width10, - hasIconAfter: { - spacingIconContentAfter: globalTokens.size80, - }, - hasIconBefore: { - spacingIconContentBefore: globalTokens.size80, - }, - focused: { - padding: globalTokens.size60, - paddingHorizontal: globalTokens.size120, - }, - primary: !isHighContrast(theme) && { - focused: { - padding: globalTokens.size60 - globalTokens.stroke.width20, - paddingHorizontal: globalTokens.size120 - globalTokens.stroke.width20, - }, - square: { - focused: { - padding: globalTokens.size60 - globalTokens.stroke.width10, - paddingHorizontal: globalTokens.size120 - globalTokens.stroke.width10, - }, - }, - }, - }, - }, - small: { - padding: globalTokens.size40 - globalTokens.stroke.width10, - iconSize: 16, - focused: { - borderWidth: 0, - padding: globalTokens.size40, - }, - primary: !isHighContrast(theme) && { - focused: { - borderWidth: globalTokens.stroke.width20, - padding: globalTokens.size40 - globalTokens.stroke.width20, - }, - square: { - focused: { - borderWidth: globalTokens.stroke.width10, - padding: globalTokens.size40 - globalTokens.stroke.width10, - }, - }, - }, - hasContent: { - minWidth: 64, - minHeight: 24, - paddingHorizontal: globalTokens.size80 - globalTokens.stroke.width10, - hasIconAfter: { - spacingIconContentAfter: globalTokens.size40, - }, - hasIconBefore: { - spacingIconContentBefore: globalTokens.size40, - }, - focused: { - paddingHorizontal: globalTokens.size80, - }, - primary: !isHighContrast(theme) && { - focused: { - paddingHorizontal: globalTokens.size80 - globalTokens.stroke.width20, - }, - square: { - focused: { - paddingHorizontal: globalTokens.size80 - globalTokens.stroke.width10, - }, - }, - }, - }, - }, - large: { - padding: globalTokens.size100 - globalTokens.stroke.width10, - iconSize: 20, - focused: { - borderWidth: 0, - padding: globalTokens.size100, - }, - primary: !isHighContrast(theme) && { - focused: { - borderWidth: globalTokens.stroke.width20, - padding: globalTokens.size100 - globalTokens.stroke.width20, - }, - square: { - focused: { - borderWidth: globalTokens.stroke.width10, - padding: globalTokens.size100 - globalTokens.stroke.width10, - }, - }, - }, - hasContent: { - minWidth: 96, - minHeight: 40, - padding: globalTokens.size80 - globalTokens.stroke.width10, - paddingHorizontal: globalTokens.size160 - globalTokens.stroke.width10, - hasIconAfter: { - spacingIconContentAfter: globalTokens.size60, - }, - hasIconBefore: { - spacingIconContentBefore: globalTokens.size60, - }, - focused: { - padding: globalTokens.size80, - paddingHorizontal: globalTokens.size160, - }, - primary: !isHighContrast(theme) && { - focused: { - padding: globalTokens.size80 - globalTokens.stroke.width20, - paddingHorizontal: globalTokens.size160 - globalTokens.stroke.width20, - }, - square: { - focused: { - padding: globalTokens.size80 - globalTokens.stroke.width10, - paddingHorizontal: globalTokens.size160 - globalTokens.stroke.width10, - }, - }, - }, - }, - }, - rounded: { - borderRadius: globalTokens.corner.radius40, - borderInnerRadius: globalTokens.corner.radius40 - 1, // reduce the rounding so that the curvature matches - }, - circular: { - borderRadius: globalTokens.corner.radiusCircular, - borderInnerRadius: globalTokens.corner.radiusCircular - 1, // reduce the rounding so that the curvature matches - }, - square: { - borderRadius: globalTokens.corner.radiusNone, - borderInnerRadius: globalTokens.corner.radiusNone, - }, - } as ButtonTokens); diff --git a/packages/components/Button/src/CompoundButton/CompoundButton.styling.ts b/packages/components/Button/src/CompoundButton/CompoundButton.styling.ts index 1b72d0d3e0..45f73566f3 100644 --- a/packages/components/Button/src/CompoundButton/CompoundButton.styling.ts +++ b/packages/components/Button/src/CompoundButton/CompoundButton.styling.ts @@ -4,22 +4,10 @@ import { borderStyles, fontStyles, layoutStyles } from '@fluentui-react-native/t import { compoundButtonName } from './CompoundButton.types'; import type { CompoundButtonTokens, CompoundButtonSlotProps, CompoundButtonProps } from './CompoundButton.types'; -import { defaultCompoundButtonColorTokens } from './CompoundButtonColorTokens'; -import { defaultCompoundButtonFontTokens } from './CompoundButtonFontTokens'; -import { defaultCompoundButtonTokens } from './CompoundButtonTokens'; import { buttonStates, contentStyling } from '../Button.styling'; -import { defaultButtonColorTokens } from '../ButtonColorTokens'; -import { defaultButtonTokens } from '../ButtonTokens'; export const stylingSettings: UseStylingOptions = { - tokens: [ - defaultButtonTokens, - defaultButtonColorTokens, - defaultCompoundButtonTokens, - defaultCompoundButtonFontTokens, - defaultCompoundButtonColorTokens, - compoundButtonName, - ], + tokens: [compoundButtonName], states: buttonStates, slotProps: { root: buildProps( diff --git a/packages/components/Button/src/CompoundButton/CompoundButton.tsx b/packages/components/Button/src/CompoundButton/CompoundButton.tsx index c48c0e17f1..ccec491ea1 100644 --- a/packages/components/Button/src/CompoundButton/CompoundButton.tsx +++ b/packages/components/Button/src/CompoundButton/CompoundButton.tsx @@ -5,7 +5,7 @@ import { Platform, Pressable, View } from 'react-native'; import { ActivityIndicator } from '@fluentui-react-native/experimental-activity-indicator'; import type { UseSlots } from '@fluentui-react-native/framework'; -import { compose, mergeProps, withSlots } from '@fluentui-react-native/framework'; +import { compose, mergeProps, useFluentTheme, withSlots } from '@fluentui-react-native/framework'; import { Icon, createIconProps } from '@fluentui-react-native/icon'; import { TextV1 as Text } from '@fluentui-react-native/text'; @@ -29,9 +29,10 @@ export const CompoundButton = compose({ useRender: (userProps: CompoundButtonProps, useSlots: UseSlots) => { const button = useButton(userProps); const iconProps = createIconProps(userProps.icon); + const theme = useFluentTheme(); // grab the styled slots - const Slots = useSlots(userProps, (layer) => buttonLookup(layer, button.state, userProps)); + const Slots = useSlots(userProps, (layer) => buttonLookup(layer, button.state, userProps, theme)); // now return the handler for finishing render return (final: CompoundButtonProps, ...children: React.ReactNode[]) => { diff --git a/packages/components/Button/src/FAB/FAB.styling.ts b/packages/components/Button/src/FAB/FAB.styling.ts index 35b4209907..6f58f52554 100644 --- a/packages/components/Button/src/FAB/FAB.styling.ts +++ b/packages/components/Button/src/FAB/FAB.styling.ts @@ -7,13 +7,11 @@ import { borderStyles, layoutStyles, fontStyles, shadowStyles } from '@fluentui- import { fabName } from './FAB.types'; import type { FABProps, FABSlotProps, FABTokens } from './FAB.types'; -import { defaultFABColorTokens } from './FABColorTokens'; -import { defaultFABTokens } from './FABTokens'; export const FABStates: (keyof FABTokens)[] = ['focused', 'pressed', 'subtle', 'disabled', 'large', 'small', 'hasContent']; export const stylingSettings: UseStylingOptions = { - tokens: [defaultFABTokens, defaultFABColorTokens, fabName], + tokens: [fabName], states: FABStates, slotProps: { ...(Platform.OS === 'android' && { diff --git a/packages/components/Button/src/FAB/FABTokens.ios.ts b/packages/components/Button/src/FAB/FABTokens.ios.ts deleted file mode 100644 index 20862dbcbe..0000000000 --- a/packages/components/Button/src/FAB/FABTokens.ios.ts +++ /dev/null @@ -1,79 +0,0 @@ -import type { Theme } from '@fluentui-react-native/framework'; -import { globalTokens } from '@fluentui-react-native/theme-tokens'; -import type { TokenSettings } from '@fluentui-react-native/use-styling'; - -import type { FABTokens } from './FAB.types'; - -export const defaultFABTokens: TokenSettings = (t: Theme) => - ({ - shadowToken: t.shadows.shadow8, - disabled: { - shadowToken: t.shadows.shadow2, - }, - pressed: { - shadowToken: t.shadows.shadow2, - }, - focused: { - shadowToken: t.shadows.shadow2, - borderWidth: globalTokens.stroke.width20, - borderInnerWidth: globalTokens.stroke.width10, - }, - subtle: { - shadowToken: t.shadows.shadow8, - disabled: { - shadowToken: t.shadows.shadow2, - }, - pressed: { - shadowToken: t.shadows.shadow2, - }, - focused: { - shadowToken: t.shadows.shadow2, - borderWidth: globalTokens.stroke.width20, - borderInnerWidth: globalTokens.stroke.width10, - }, - }, - large: { - borderRadius: globalTokens.corner.radiusCircular, - iconSize: 24, - minHeight: 56, - minWidth: 56, - paddingHorizontal: globalTokens.size160, - paddingVertical: globalTokens.size160, - spacingIconContentBefore: 0, - hasContent: { - borderRadius: globalTokens.corner.radiusCircular, - iconSize: 24, - fontSize: t.typography.variants.body1Strong.size, - fontFamily: t.typography.variants.body1Strong.face, - fontWeight: t.typography.variants.body1Strong.weight, - minHeight: 56, - minWidth: 56, - paddingStart: globalTokens.size160, - paddingEnd: globalTokens.size200, - paddingVertical: globalTokens.size160, - spacingIconContentBefore: globalTokens.size80, - }, - }, - small: { - borderRadius: globalTokens.corner.radiusCircular, - iconSize: 20, - minHeight: 44, - minWidth: 44, - paddingHorizontal: globalTokens.size120, - paddingVertical: globalTokens.size120, - spacingIconContentBefore: 0, - hasContent: { - borderRadius: globalTokens.corner.radiusCircular, - iconSize: 20, - fontSize: t.typography.variants.body2Strong.size, - fontFamily: t.typography.variants.body2Strong.face, - fontWeight: t.typography.variants.body2Strong.weight, - minHeight: 48, - minWidth: 48, - paddingHorizontal: globalTokens.size120, - paddingStart: globalTokens.size120, - paddingEnd: globalTokens.size160, - spacingIconContentBefore: globalTokens.size80, - }, - }, - } as FABTokens); diff --git a/packages/components/Button/src/FAB/FABTokens.ts b/packages/components/Button/src/FAB/FABTokens.ts deleted file mode 100644 index 3a00dbd75c..0000000000 --- a/packages/components/Button/src/FAB/FABTokens.ts +++ /dev/null @@ -1,79 +0,0 @@ -import type { Theme } from '@fluentui-react-native/framework'; -import { globalTokens } from '@fluentui-react-native/theme-tokens'; -import type { TokenSettings } from '@fluentui-react-native/use-styling'; - -import type { FABTokens } from './FAB.types'; - -export const defaultFABTokens: TokenSettings = (t: Theme) => - ({ - elevation: t.shadows.shadow8.key.blur, - disabled: { - elevation: 0, - }, - pressed: { - elevation: t.shadows.shadow2.key.blur, - }, - focused: { - elevation: t.shadows.shadow2.key.blur, - borderWidth: globalTokens.stroke.width20, - borderInnerWidth: globalTokens.stroke.width10, - }, - subtle: { - elevation: t.shadows.shadow8.key.blur, - disabled: { - elevation: 0, - }, - pressed: { - elevation: t.shadows.shadow2.key.blur, - }, - focused: { - elevation: t.shadows.shadow2.key.blur, - borderWidth: globalTokens.stroke.width20, - borderInnerWidth: globalTokens.stroke.width10, - }, - }, - large: { - borderRadius: globalTokens.corner.radiusCircular, - iconSize: 24, - minHeight: 56, - minWidth: 56, - paddingHorizontal: globalTokens.size160, - paddingVertical: globalTokens.size160, - spacingIconContentBefore: 0, - hasContent: { - borderRadius: globalTokens.corner.radiusCircular, - iconSize: 24, - fontSize: t.typography.variants.body1Strong.size, - fontFamily: t.typography.variants.body1Strong.face, - fontWeight: t.typography.variants.body1Strong.weight, - minHeight: 56, - minWidth: 56, - paddingStart: globalTokens.size160, - paddingEnd: globalTokens.size200, - paddingVertical: globalTokens.size160, - spacingIconContentBefore: globalTokens.size80, - }, - }, - small: { - borderRadius: globalTokens.corner.radiusCircular, - iconSize: 20, - minHeight: 44, - minWidth: 44, - paddingHorizontal: globalTokens.size120, - paddingVertical: globalTokens.size120, - spacingIconContentBefore: 0, - hasContent: { - borderRadius: globalTokens.corner.radiusCircular, - iconSize: 20, - fontSize: t.typography.variants.body2Strong.size, - fontFamily: t.typography.variants.body2Strong.face, - fontWeight: t.typography.variants.body2Strong.weight, - minHeight: 44, - minWidth: 44, - paddingHorizontal: globalTokens.size120, - paddingStart: globalTokens.size120, - paddingEnd: globalTokens.size160, - spacingIconContentBefore: globalTokens.size80, - }, - }, - } as FABTokens); diff --git a/packages/components/Button/src/ToggleButton/ToggleButton.styling.ts b/packages/components/Button/src/ToggleButton/ToggleButton.styling.ts index fa6ef29c89..7ae4d8d993 100644 --- a/packages/components/Button/src/ToggleButton/ToggleButton.styling.ts +++ b/packages/components/Button/src/ToggleButton/ToggleButton.styling.ts @@ -4,14 +4,10 @@ import { borderStyles, layoutStyles, fontStyles } from '@fluentui-react-native/t import { toggleButtonName } from './ToggleButton.types'; import type { ToggleButtonTokens, ToggleButtonSlotProps, ToggleButtonProps } from './ToggleButton.types'; -import { defaultToggleButtonColorTokens } from './ToggleButtonColorTokens'; import { buttonStates, contentStyling } from '../Button.styling'; -import { defaultButtonColorTokens } from '../ButtonColorTokens'; -import { defaultButtonFontTokens } from '../ButtonFontTokens'; -import { defaultButtonTokens } from '../ButtonTokens'; export const stylingSettings: UseStylingOptions = { - tokens: [defaultButtonTokens, defaultButtonFontTokens, defaultButtonColorTokens, defaultToggleButtonColorTokens, toggleButtonName], + tokens: [toggleButtonName], states: ['checked', ...buttonStates], slotProps: { root: buildProps( diff --git a/packages/components/Button/src/ToggleButton/ToggleButton.tsx b/packages/components/Button/src/ToggleButton/ToggleButton.tsx index cae9858d0b..0d1662fdba 100644 --- a/packages/components/Button/src/ToggleButton/ToggleButton.tsx +++ b/packages/components/Button/src/ToggleButton/ToggleButton.tsx @@ -5,7 +5,7 @@ import { Platform, Pressable, View } from 'react-native'; import { ActivityIndicator } from '@fluentui-react-native/experimental-activity-indicator'; import type { UseSlots } from '@fluentui-react-native/framework'; -import { compose, mergeProps, withSlots } from '@fluentui-react-native/framework'; +import { compose, mergeProps, useFluentTheme, withSlots } from '@fluentui-react-native/framework'; import { Icon, createIconProps } from '@fluentui-react-native/icon'; import { TextV1 as Text } from '@fluentui-react-native/text'; @@ -27,9 +27,10 @@ export const ToggleButton = compose({ useRender: (userProps: ToggleButtonProps, useSlots: UseSlots) => { const iconProps = createIconProps(userProps.icon); const toggleButton = useToggleButton(userProps); + const theme = useFluentTheme(); // grab the styled slots - const Slots = useSlots(userProps, (layer) => buttonLookup(layer, toggleButton.state, userProps)); + const Slots = useSlots(userProps, (layer) => buttonLookup(layer, toggleButton.state, userProps, theme)); // now return the handler for finishing render return (final: ToggleButtonProps, ...children: React.ReactNode[]) => { diff --git a/packages/theming/android-theme/package.json b/packages/theming/android-theme/package.json index 92115981f3..822876bccc 100644 --- a/packages/theming/android-theme/package.json +++ b/packages/theming/android-theme/package.json @@ -31,6 +31,7 @@ "author": "", "license": "MIT", "dependencies": { + "@fluentui-react-native/immutable-merge": "workspace:*", "@fluentui-react-native/memo-cache": "workspace:*", "@fluentui-react-native/theme": "workspace:*", "@fluentui-react-native/theme-tokens": "workspace:*", @@ -38,6 +39,7 @@ "@fluentui-react-native/theming-utils": "workspace:*" }, "devDependencies": { + "@fluentui-react-native/button": "workspace:*", "@fluentui-react-native/eslint-config-rules": "workspace:*", "@fluentui-react-native/scripts": "workspace:*", "@react-native/babel-preset": "^0.73.0", diff --git a/packages/theming/android-theme/src/__tests__/__snapshots__/android-theme.test.ts.snap b/packages/theming/android-theme/src/__tests__/__snapshots__/android-theme.test.ts.snap index 2eb72e88cf..acb432095a 100644 --- a/packages/theming/android-theme/src/__tests__/__snapshots__/android-theme.test.ts.snap +++ b/packages/theming/android-theme/src/__tests__/__snapshots__/android-theme.test.ts.snap @@ -224,6 +224,141 @@ exports[`createAndroidTheme test option { appearance: 'dark', defaultAppearance: "warningForeground2": "#feee66", }, "components": { + "Button": { + "backgroundColor": "#479ef5", + "block": { + "width": "100%", + }, + "circular": { + "borderRadius": 9999, + }, + "color": "#000000", + "disabled": { + "backgroundColor": "#3d3d3d", + "color": "#5c5c5c", + "iconColor": "#5c5c5c", + }, + "focused": { + "backgroundColor": "#479ef5", + "borderInnerWidth": 1, + "borderWidth": 2, + "color": "#000000", + "iconColor": "#000000", + }, + "iconColor": "#000000", + "large": { + "borderRadius": 8, + "fontFamily": "Roboto", + "fontSize": 16, + "fontWeight": "500", + "iconSize": 20, + "minHeight": 48, + "minWidth": 36, + "outline": { + "borderWidth": 1, + "iconSize": 20, + }, + "paddingHorizontal": 20, + "spacingIconContentAfter": 8, + "spacingIconContentBefore": 8, + }, + "medium": { + "borderRadius": 4, + "fontFamily": "Roboto", + "fontSize": 14, + "fontWeight": "500", + "iconSize": 20, + "minHeight": 36, + "minWidth": 36, + "outline": { + "borderWidth": 1, + "iconSize": 20, + }, + "paddingHorizontal": 12, + "spacingIconContentAfter": 8, + "spacingIconContentBefore": 8, + }, + "outline": { + "backgroundColor": "transparent", + "borderColor": "#479ef5", + "borderWidth": 1, + "color": "#479ef5", + "disabled": { + "backgroundColor": "transparent", + "borderColor": "#424242", + "borderWidth": 1, + "color": "#5c5c5c", + "iconColor": "#5c5c5c", + }, + "focused": { + "backgroundColor": "transparent", + "borderInnerWidth": 1, + "borderWidth": 2, + "color": "#479ef5", + "iconColor": "#479ef5", + }, + "iconColor": "#479ef5", + "pressed": { + "backgroundColor": "transparent", + "borderColor": "#96c6fa", + "borderWidth": 1, + "color": "#96c6fa", + "iconColor": "#96c6fa", + }, + "rippleColor": "#D4D4D4", + }, + "pressed": { + "backgroundColor": "#96c6fa", + "color": "#000000", + "iconColor": "#000000", + }, + "rippleColor": "#D4D4D4", + "rounded": { + "borderRadius": 4, + }, + "small": { + "borderRadius": 4, + "fontFamily": "Roboto", + "fontSize": 14, + "fontWeight": "500", + "iconSize": 16, + "minHeight": 28, + "minWidth": 28, + "outline": { + "borderWidth": 1, + "iconSize": 16, + }, + "paddingHorizontal": 8, + "spacingIconContentAfter": 4, + "spacingIconContentBefore": 4, + }, + "square": { + "borderRadius": 0, + }, + "subtle": { + "backgroundColor": "transparent", + "color": "#479ef5", + "disabled": { + "backgroundColor": "transparent", + "color": "#5c5c5c", + "iconColor": "#5c5c5c", + }, + "focused": { + "backgroundColor": "transparent", + "borderInnerWidth": 1, + "borderWidth": 2, + "color": "#479ef5", + "iconColor": "#479ef5", + }, + "iconColor": "#479ef5", + "pressed": { + "backgroundColor": "transparent", + "color": "#96c6fa", + "iconColor": "#96c6fa", + }, + "rippleColor": "#D4D4D4", + }, + }, "Checkbox": { "checkbox": { "style": { @@ -813,6 +948,141 @@ exports[`createAndroidTheme test option { appearance: 'darkElevated', defaultApp "warningForeground2": "#feee66", }, "components": { + "Button": { + "backgroundColor": "#479ef5", + "block": { + "width": "100%", + }, + "circular": { + "borderRadius": 9999, + }, + "color": "#000000", + "disabled": { + "backgroundColor": "#3d3d3d", + "color": "#5c5c5c", + "iconColor": "#5c5c5c", + }, + "focused": { + "backgroundColor": "#479ef5", + "borderInnerWidth": 1, + "borderWidth": 2, + "color": "#000000", + "iconColor": "#000000", + }, + "iconColor": "#000000", + "large": { + "borderRadius": 8, + "fontFamily": "Roboto", + "fontSize": 16, + "fontWeight": "500", + "iconSize": 20, + "minHeight": 48, + "minWidth": 36, + "outline": { + "borderWidth": 1, + "iconSize": 20, + }, + "paddingHorizontal": 20, + "spacingIconContentAfter": 8, + "spacingIconContentBefore": 8, + }, + "medium": { + "borderRadius": 4, + "fontFamily": "Roboto", + "fontSize": 14, + "fontWeight": "500", + "iconSize": 20, + "minHeight": 36, + "minWidth": 36, + "outline": { + "borderWidth": 1, + "iconSize": 20, + }, + "paddingHorizontal": 12, + "spacingIconContentAfter": 8, + "spacingIconContentBefore": 8, + }, + "outline": { + "backgroundColor": "transparent", + "borderColor": "#479ef5", + "borderWidth": 1, + "color": "#479ef5", + "disabled": { + "backgroundColor": "transparent", + "borderColor": "#424242", + "borderWidth": 1, + "color": "#5c5c5c", + "iconColor": "#5c5c5c", + }, + "focused": { + "backgroundColor": "transparent", + "borderInnerWidth": 1, + "borderWidth": 2, + "color": "#479ef5", + "iconColor": "#479ef5", + }, + "iconColor": "#479ef5", + "pressed": { + "backgroundColor": "transparent", + "borderColor": "#96c6fa", + "borderWidth": 1, + "color": "#96c6fa", + "iconColor": "#96c6fa", + }, + "rippleColor": "#D4D4D4", + }, + "pressed": { + "backgroundColor": "#96c6fa", + "color": "#000000", + "iconColor": "#000000", + }, + "rippleColor": "#D4D4D4", + "rounded": { + "borderRadius": 4, + }, + "small": { + "borderRadius": 4, + "fontFamily": "Roboto", + "fontSize": 14, + "fontWeight": "500", + "iconSize": 16, + "minHeight": 28, + "minWidth": 28, + "outline": { + "borderWidth": 1, + "iconSize": 16, + }, + "paddingHorizontal": 8, + "spacingIconContentAfter": 4, + "spacingIconContentBefore": 4, + }, + "square": { + "borderRadius": 0, + }, + "subtle": { + "backgroundColor": "transparent", + "color": "#479ef5", + "disabled": { + "backgroundColor": "transparent", + "color": "#5c5c5c", + "iconColor": "#5c5c5c", + }, + "focused": { + "backgroundColor": "transparent", + "borderInnerWidth": 1, + "borderWidth": 2, + "color": "#479ef5", + "iconColor": "#479ef5", + }, + "iconColor": "#479ef5", + "pressed": { + "backgroundColor": "transparent", + "color": "#96c6fa", + "iconColor": "#96c6fa", + }, + "rippleColor": "#D4D4D4", + }, + }, "Checkbox": { "checkbox": { "style": { @@ -1402,6 +1672,141 @@ exports[`createAndroidTheme test option { appearance: 'dynamic', defaultAppearan "warningForeground2": "#feee66", }, "components": { + "Button": { + "backgroundColor": "#479ef5", + "block": { + "width": "100%", + }, + "circular": { + "borderRadius": 9999, + }, + "color": "#000000", + "disabled": { + "backgroundColor": "#3d3d3d", + "color": "#5c5c5c", + "iconColor": "#5c5c5c", + }, + "focused": { + "backgroundColor": "#479ef5", + "borderInnerWidth": 1, + "borderWidth": 2, + "color": "#000000", + "iconColor": "#000000", + }, + "iconColor": "#000000", + "large": { + "borderRadius": 8, + "fontFamily": "Roboto", + "fontSize": 16, + "fontWeight": "500", + "iconSize": 20, + "minHeight": 48, + "minWidth": 36, + "outline": { + "borderWidth": 1, + "iconSize": 20, + }, + "paddingHorizontal": 20, + "spacingIconContentAfter": 8, + "spacingIconContentBefore": 8, + }, + "medium": { + "borderRadius": 4, + "fontFamily": "Roboto", + "fontSize": 14, + "fontWeight": "500", + "iconSize": 20, + "minHeight": 36, + "minWidth": 36, + "outline": { + "borderWidth": 1, + "iconSize": 20, + }, + "paddingHorizontal": 12, + "spacingIconContentAfter": 8, + "spacingIconContentBefore": 8, + }, + "outline": { + "backgroundColor": "transparent", + "borderColor": "#479ef5", + "borderWidth": 1, + "color": "#479ef5", + "disabled": { + "backgroundColor": "transparent", + "borderColor": "#424242", + "borderWidth": 1, + "color": "#5c5c5c", + "iconColor": "#5c5c5c", + }, + "focused": { + "backgroundColor": "transparent", + "borderInnerWidth": 1, + "borderWidth": 2, + "color": "#479ef5", + "iconColor": "#479ef5", + }, + "iconColor": "#479ef5", + "pressed": { + "backgroundColor": "transparent", + "borderColor": "#96c6fa", + "borderWidth": 1, + "color": "#96c6fa", + "iconColor": "#96c6fa", + }, + "rippleColor": "#D4D4D4", + }, + "pressed": { + "backgroundColor": "#96c6fa", + "color": "#000000", + "iconColor": "#000000", + }, + "rippleColor": "#D4D4D4", + "rounded": { + "borderRadius": 4, + }, + "small": { + "borderRadius": 4, + "fontFamily": "Roboto", + "fontSize": 14, + "fontWeight": "500", + "iconSize": 16, + "minHeight": 28, + "minWidth": 28, + "outline": { + "borderWidth": 1, + "iconSize": 16, + }, + "paddingHorizontal": 8, + "spacingIconContentAfter": 4, + "spacingIconContentBefore": 4, + }, + "square": { + "borderRadius": 0, + }, + "subtle": { + "backgroundColor": "transparent", + "color": "#479ef5", + "disabled": { + "backgroundColor": "transparent", + "color": "#5c5c5c", + "iconColor": "#5c5c5c", + }, + "focused": { + "backgroundColor": "transparent", + "borderInnerWidth": 1, + "borderWidth": 2, + "color": "#479ef5", + "iconColor": "#479ef5", + }, + "iconColor": "#479ef5", + "pressed": { + "backgroundColor": "transparent", + "color": "#96c6fa", + "iconColor": "#96c6fa", + }, + "rippleColor": "#D4D4D4", + }, + }, "Checkbox": { "checkbox": { "style": { @@ -1991,6 +2396,141 @@ exports[`createAndroidTheme test option { appearance: 'highContrast', defaultApp "warningForeground2": "#feee66", }, "components": { + "Button": { + "backgroundColor": "#479ef5", + "block": { + "width": "100%", + }, + "circular": { + "borderRadius": 9999, + }, + "color": "#000000", + "disabled": { + "backgroundColor": "#3d3d3d", + "color": "#5c5c5c", + "iconColor": "#5c5c5c", + }, + "focused": { + "backgroundColor": "#479ef5", + "borderInnerWidth": 1, + "borderWidth": 2, + "color": "#000000", + "iconColor": "#000000", + }, + "iconColor": "#000000", + "large": { + "borderRadius": 8, + "fontFamily": "Roboto", + "fontSize": 16, + "fontWeight": "500", + "iconSize": 20, + "minHeight": 48, + "minWidth": 36, + "outline": { + "borderWidth": 1, + "iconSize": 20, + }, + "paddingHorizontal": 20, + "spacingIconContentAfter": 8, + "spacingIconContentBefore": 8, + }, + "medium": { + "borderRadius": 4, + "fontFamily": "Roboto", + "fontSize": 14, + "fontWeight": "500", + "iconSize": 20, + "minHeight": 36, + "minWidth": 36, + "outline": { + "borderWidth": 1, + "iconSize": 20, + }, + "paddingHorizontal": 12, + "spacingIconContentAfter": 8, + "spacingIconContentBefore": 8, + }, + "outline": { + "backgroundColor": "transparent", + "borderColor": "#479ef5", + "borderWidth": 1, + "color": "#479ef5", + "disabled": { + "backgroundColor": "transparent", + "borderColor": "#424242", + "borderWidth": 1, + "color": "#5c5c5c", + "iconColor": "#5c5c5c", + }, + "focused": { + "backgroundColor": "transparent", + "borderInnerWidth": 1, + "borderWidth": 2, + "color": "#479ef5", + "iconColor": "#479ef5", + }, + "iconColor": "#479ef5", + "pressed": { + "backgroundColor": "transparent", + "borderColor": "#96c6fa", + "borderWidth": 1, + "color": "#96c6fa", + "iconColor": "#96c6fa", + }, + "rippleColor": "#D4D4D4", + }, + "pressed": { + "backgroundColor": "#96c6fa", + "color": "#000000", + "iconColor": "#000000", + }, + "rippleColor": "#D4D4D4", + "rounded": { + "borderRadius": 4, + }, + "small": { + "borderRadius": 4, + "fontFamily": "Roboto", + "fontSize": 14, + "fontWeight": "500", + "iconSize": 16, + "minHeight": 28, + "minWidth": 28, + "outline": { + "borderWidth": 1, + "iconSize": 16, + }, + "paddingHorizontal": 8, + "spacingIconContentAfter": 4, + "spacingIconContentBefore": 4, + }, + "square": { + "borderRadius": 0, + }, + "subtle": { + "backgroundColor": "transparent", + "color": "#479ef5", + "disabled": { + "backgroundColor": "transparent", + "color": "#5c5c5c", + "iconColor": "#5c5c5c", + }, + "focused": { + "backgroundColor": "transparent", + "borderInnerWidth": 1, + "borderWidth": 2, + "color": "#479ef5", + "iconColor": "#479ef5", + }, + "iconColor": "#479ef5", + "pressed": { + "backgroundColor": "transparent", + "color": "#96c6fa", + "iconColor": "#96c6fa", + }, + "rippleColor": "#D4D4D4", + }, + }, "Checkbox": { "checkbox": { "style": { @@ -2580,6 +3120,141 @@ exports[`createAndroidTheme test option { appearance: 'light', defaultAppearance "warningForeground2": "#817400", }, "components": { + "Button": { + "backgroundColor": "#0f6cbd", + "block": { + "width": "100%", + }, + "circular": { + "borderRadius": 9999, + }, + "color": "#ffffff", + "disabled": { + "backgroundColor": "#f0f0f0", + "color": "#bdbdbd", + "iconColor": "#bdbdbd", + }, + "focused": { + "backgroundColor": "#0f6cbd", + "borderInnerWidth": 1, + "borderWidth": 2, + "color": "#ffffff", + "iconColor": "#ffffff", + }, + "iconColor": "#ffffff", + "large": { + "borderRadius": 8, + "fontFamily": "Roboto", + "fontSize": 16, + "fontWeight": "500", + "iconSize": 20, + "minHeight": 48, + "minWidth": 36, + "outline": { + "borderWidth": 1, + "iconSize": 20, + }, + "paddingHorizontal": 20, + "spacingIconContentAfter": 8, + "spacingIconContentBefore": 8, + }, + "medium": { + "borderRadius": 4, + "fontFamily": "Roboto", + "fontSize": 14, + "fontWeight": "500", + "iconSize": 20, + "minHeight": 36, + "minWidth": 36, + "outline": { + "borderWidth": 1, + "iconSize": 20, + }, + "paddingHorizontal": 12, + "spacingIconContentAfter": 8, + "spacingIconContentBefore": 8, + }, + "outline": { + "backgroundColor": "transparent", + "borderColor": "#0f6cbd", + "borderWidth": 1, + "color": "#0f6cbd", + "disabled": { + "backgroundColor": "transparent", + "borderColor": "#e0e0e0", + "borderWidth": 1, + "color": "#bdbdbd", + "iconColor": "#bdbdbd", + }, + "focused": { + "backgroundColor": "transparent", + "borderInnerWidth": 1, + "borderWidth": 2, + "color": "#0f6cbd", + "iconColor": "#0f6cbd", + }, + "iconColor": "#0f6cbd", + "pressed": { + "backgroundColor": "transparent", + "borderColor": "#0e4775", + "borderWidth": 1, + "color": "#0e4775", + "iconColor": "#0e4775", + }, + "rippleColor": "#D4D4D4", + }, + "pressed": { + "backgroundColor": "#0e4775", + "color": "#ffffff", + "iconColor": "#ffffff", + }, + "rippleColor": "#D4D4D4", + "rounded": { + "borderRadius": 4, + }, + "small": { + "borderRadius": 4, + "fontFamily": "Roboto", + "fontSize": 14, + "fontWeight": "500", + "iconSize": 16, + "minHeight": 28, + "minWidth": 28, + "outline": { + "borderWidth": 1, + "iconSize": 16, + }, + "paddingHorizontal": 8, + "spacingIconContentAfter": 4, + "spacingIconContentBefore": 4, + }, + "square": { + "borderRadius": 0, + }, + "subtle": { + "backgroundColor": "transparent", + "color": "#0f6cbd", + "disabled": { + "backgroundColor": "transparent", + "color": "#bdbdbd", + "iconColor": "#bdbdbd", + }, + "focused": { + "backgroundColor": "transparent", + "borderInnerWidth": 1, + "borderWidth": 2, + "color": "#0f6cbd", + "iconColor": "#0f6cbd", + }, + "iconColor": "#0f6cbd", + "pressed": { + "backgroundColor": "transparent", + "color": "#0e4775", + "iconColor": "#0e4775", + }, + "rippleColor": "#D4D4D4", + }, + }, "Checkbox": { "checkbox": { "style": { diff --git a/packages/theming/android-theme/src/__tests__/android-theme.test.ts b/packages/theming/android-theme/src/__tests__/android-theme.test.ts index b340c5c3f0..efd3025977 100644 --- a/packages/theming/android-theme/src/__tests__/android-theme.test.ts +++ b/packages/theming/android-theme/src/__tests__/android-theme.test.ts @@ -1,6 +1,12 @@ +import type { ButtonTokens, FABTokens } from '@fluentui-react-native/button'; import type { ThemeOptions } from '@fluentui-react-native/theme-types'; import { getAndroidTheme } from '../androidTheme'; +import { defaultButtonColorTokens } from '../components/Button/ButtonColorTokens'; +import { defaultButtonFontTokens } from '../components/Button/ButtonFontTokens'; +import { defaultButtonTokens } from '../components/Button/ButtonTokens'; +import { defaultFABColorTokens } from '../components/Button/FABColorTokens'; +import { defaultFABTokens } from '../components/Button/FABTokens'; import { createAndroidTheme } from '../createAndroidTheme'; const defaultAppearance = 'light'; @@ -33,3 +39,20 @@ it.concurrent.each(themeOptions)('createAndroidTheme test option %o', (option: T const theme = createAndroidTheme(option).theme; expect(theme).toMatchSnapshot(); }); + +describe('verify types', () => { + it('Button types', () => { + const officeTheme = getAndroidTheme('light'); + const colorTokens: ButtonTokens = defaultButtonColorTokens(officeTheme); + expect(colorTokens).toBeTruthy(); + const fontTokens: ButtonTokens = defaultButtonFontTokens(officeTheme); + expect(fontTokens).toBeTruthy(); + const tokens: ButtonTokens = defaultButtonTokens(officeTheme); + expect(tokens).toBeTruthy(); + + const fabTokens: FABTokens = defaultFABTokens(officeTheme); + expect(fabTokens).toBeTruthy(); + const fabColorTokens: FABTokens = defaultFABColorTokens(officeTheme); + expect(fabColorTokens).toBeTruthy(); + }); +}); diff --git a/packages/theming/android-theme/src/components/Button/ButtonColorTokens.ts b/packages/theming/android-theme/src/components/Button/ButtonColorTokens.ts new file mode 100644 index 0000000000..8e737b7689 --- /dev/null +++ b/packages/theming/android-theme/src/components/Button/ButtonColorTokens.ts @@ -0,0 +1,77 @@ +import type { Theme } from '@fluentui-react-native/theme-types'; + +export const defaultButtonColorTokens = (t: Theme) => ({ + /** Android does not have a different styles for 'default' button. + * 'primary', 'accent' and if no appearance is mentioned, picks this. + */ + backgroundColor: t.colors.brandBackground, + rippleColor: '#D4D4D4', + color: t.colors.neutralForegroundOnColor, + iconColor: t.colors.neutralForegroundOnColor, + disabled: { + backgroundColor: t.colors.neutralBackground5, + color: t.colors.neutralForegroundDisabled1, + iconColor: t.colors.neutralForegroundDisabled1, + }, + pressed: { + backgroundColor: t.colors.brandBackgroundPressed, + color: t.colors.neutralForegroundOnColor, + iconColor: t.colors.neutralForegroundOnColor, + }, + focused: { + backgroundColor: t.colors.brandBackground, + color: t.colors.neutralForegroundOnColor, + borderColor: t.colors.strokeFocus2, + borderInnerColor: t.colors.strokeFocus1, + iconColor: t.colors.neutralForegroundOnColor, + }, + subtle: { + backgroundColor: 'transparent', + rippleColor: '#D4D4D4', + color: t.colors.brandForeground1, + iconColor: t.colors.brandForeground1, + disabled: { + backgroundColor: 'transparent', + color: t.colors.neutralForegroundDisabled1, + iconColor: t.colors.neutralForegroundDisabled1, + }, + pressed: { + backgroundColor: 'transparent', + color: t.colors.brandForeground1Pressed, + iconColor: t.colors.brandForeground1Pressed, + }, + focused: { + backgroundColor: 'transparent', + borderColor: t.colors.strokeFocus2, + borderInnerColor: t.colors.strokeFocus1, + color: t.colors.brandForeground1, + iconColor: t.colors.brandForeground1, + }, + }, + outline: { + backgroundColor: 'transparent', + rippleColor: '#D4D4D4', + color: t.colors.brandForeground1, + iconColor: t.colors.brandForeground1, + borderColor: t.colors.brandStroke1, + disabled: { + backgroundColor: 'transparent', + color: t.colors.neutralForegroundDisabled1, + iconColor: t.colors.neutralForegroundDisabled1, + borderColor: t.colors.neutralStrokeDisabled, + }, + pressed: { + backgroundColor: 'transparent', + color: t.colors.brandForeground1Pressed, + iconColor: t.colors.brandForeground1Pressed, + borderColor: t.colors.brandStroke1Pressed, + }, + focused: { + backgroundColor: 'transparent', + borderColor: t.colors.strokeFocus2, + borderInnerColor: t.colors.strokeFocus1, + color: t.colors.brandForeground1, + iconColor: t.colors.brandForeground1, + }, + }, +}); diff --git a/packages/theming/android-theme/src/components/Button/ButtonFontTokens.ts b/packages/theming/android-theme/src/components/Button/ButtonFontTokens.ts new file mode 100644 index 0000000000..38cc1a16f7 --- /dev/null +++ b/packages/theming/android-theme/src/components/Button/ButtonFontTokens.ts @@ -0,0 +1,19 @@ +import type { Theme } from '@fluentui-react-native/theme-types'; + +export const defaultButtonFontTokens = (t: Theme) => ({ + medium: { + fontSize: t.typography.variants.body2Strong.size, + fontFamily: t.typography.variants.body2Strong.face, + fontWeight: t.typography.variants.body2Strong.weight, + }, + small: { + fontSize: t.typography.variants.body2Strong.size, + fontFamily: t.typography.variants.body2Strong.face, + fontWeight: t.typography.variants.body2Strong.weight, + }, + large: { + fontSize: t.typography.variants.body1Strong.size, + fontFamily: t.typography.variants.body1Strong.face, + fontWeight: t.typography.variants.body1Strong.weight, + }, +}); diff --git a/packages/theming/android-theme/src/components/Button/ButtonTheme.ts b/packages/theming/android-theme/src/components/Button/ButtonTheme.ts new file mode 100644 index 0000000000..b6c36878c0 --- /dev/null +++ b/packages/theming/android-theme/src/components/Button/ButtonTheme.ts @@ -0,0 +1,22 @@ +import { immutableMerge } from '@fluentui-react-native/immutable-merge'; +import type { Theme } from '@fluentui-react-native/theme-types'; + +import { defaultButtonColorTokens } from './ButtonColorTokens'; +import { defaultButtonFontTokens } from './ButtonFontTokens'; +import { defaultButtonTokens } from './ButtonTokens'; +import { defaultFABColorTokens } from './FABColorTokens'; +import { defaultFABTokens } from './FABTokens'; +import { defaultToggleButtonColorTokens } from './ToggleButtonColorTokens'; + +export const defaultButtonTheme = (theme: Theme) => ({ + components: { + Button: immutableMerge(defaultButtonColorTokens(theme), defaultButtonFontTokens(theme), defaultButtonTokens(theme)), + FAB: immutableMerge(defaultFABTokens(theme), defaultFABColorTokens(theme)), + ToggleButton: immutableMerge( + defaultButtonTokens(theme), + defaultButtonFontTokens(theme), + defaultButtonColorTokens(theme), + defaultToggleButtonColorTokens(theme), + ), + }, +}); diff --git a/packages/theming/android-theme/src/components/Button/ButtonTokens.ts b/packages/theming/android-theme/src/components/Button/ButtonTokens.ts new file mode 100644 index 0000000000..42a9dbcd0f --- /dev/null +++ b/packages/theming/android-theme/src/components/Button/ButtonTokens.ts @@ -0,0 +1,95 @@ +import type { DimensionValue } from 'react-native'; + +import { globalTokensAndroid as globalTokens } from '@fluentui-react-native/theme-tokens'; +import type { Theme } from '@fluentui-react-native/theme-types'; + +export const defaultButtonTokens = (_t: Theme) => ({ + focused: { + borderWidth: globalTokens.stroke.width20, + borderInnerWidth: globalTokens.stroke.width10, + }, + subtle: { + focused: { + borderWidth: globalTokens.stroke.width20, + borderInnerWidth: globalTokens.stroke.width10, + }, + }, + outline: { + borderWidth: globalTokens.stroke.width10, + disabled: { + borderWidth: globalTokens.stroke.width10, + }, + pressed: { + borderWidth: globalTokens.stroke.width10, + }, + focused: { + borderWidth: globalTokens.stroke.width20, + borderInnerWidth: globalTokens.stroke.width10, + }, + }, + block: { + width: '100%' as DimensionValue, + }, + large: { + paddingHorizontal: globalTokens.size200, + borderRadius: globalTokens.corner.radius80, + iconSize: 20, + outline: { + borderWidth: globalTokens.stroke.width10, + iconSize: 20, + }, + spacingIconContentBefore: globalTokens.size80, + spacingIconContentAfter: globalTokens.size80, + minHeight: 48, + minWidth: 36, + }, + medium: { + paddingHorizontal: globalTokens.size120, + borderRadius: globalTokens.corner.radius40, + iconSize: 20, + outline: { + borderWidth: globalTokens.stroke.width10, + iconSize: 20, + }, + spacingIconContentBefore: globalTokens.size80, + spacingIconContentAfter: globalTokens.size80, + minHeight: 36, + minWidth: 36, + }, + small: { + paddingHorizontal: globalTokens.size80, + borderRadius: globalTokens.corner.radius40, + iconSize: 16, + outline: { + borderWidth: globalTokens.stroke.width10, + iconSize: 16, + }, + spacingIconContentBefore: globalTokens.size40, + spacingIconContentAfter: globalTokens.size40, + minHeight: 28, + minWidth: 28, + }, + rounded: { + borderRadius: globalTokens.corner.radius40, + }, + circular: { + borderRadius: globalTokens.corner.radiusCircular, + }, + square: { + borderRadius: globalTokens.corner.radiusNone, + }, + getPlatformSpecificAppearance: (appearance): 'primary' | 'subtle' | 'outline' => { + switch (appearance) { + case 'accent': // Included to cover Mobile platform naming guidelines, maps to 'primary'. + return 'primary'; + + case 'primary': + case 'subtle': + case 'outline': // 'Outline' exists only for Mobile platforms, default picked on other platforms. + return appearance; + + default: + return 'primary'; + } + }, +}); diff --git a/packages/components/Button/src/FAB/FABColorTokens.ts b/packages/theming/android-theme/src/components/Button/FABColorTokens.ts similarity index 85% rename from packages/components/Button/src/FAB/FABColorTokens.ts rename to packages/theming/android-theme/src/components/Button/FABColorTokens.ts index 158ed5788f..c0c232fec6 100644 --- a/packages/components/Button/src/FAB/FABColorTokens.ts +++ b/packages/theming/android-theme/src/components/Button/FABColorTokens.ts @@ -1,9 +1,6 @@ -import type { Theme } from '@fluentui-react-native/framework'; -import type { TokenSettings } from '@fluentui-react-native/use-styling'; +import type { Theme } from '@fluentui-react-native/theme-types'; -import type { FABTokens } from './FAB.types'; - -export const defaultFABColorTokens: TokenSettings = (t: Theme): FABTokens => ({ +export const defaultFABColorTokens = (t: Theme) => ({ // Default coloring same as 'primary' or 'accent' backgroundColor: t.colors.brandBackground, color: t.colors.neutralForegroundOnColor, diff --git a/packages/theming/android-theme/src/components/Button/FABTokens.ts b/packages/theming/android-theme/src/components/Button/FABTokens.ts new file mode 100644 index 0000000000..9f01c3de52 --- /dev/null +++ b/packages/theming/android-theme/src/components/Button/FABTokens.ts @@ -0,0 +1,75 @@ +import { globalTokensAndroid as globalTokens } from '@fluentui-react-native/theme-tokens'; +import type { Theme } from '@fluentui-react-native/theme-types'; + +export const defaultFABTokens = (t: Theme) => ({ + elevation: t.shadows.shadow8.key.blur, + disabled: { + elevation: 0, + }, + pressed: { + elevation: t.shadows.shadow2.key.blur, + }, + focused: { + elevation: t.shadows.shadow2.key.blur, + borderWidth: globalTokens.stroke.width20, + borderInnerWidth: globalTokens.stroke.width10, + }, + subtle: { + elevation: t.shadows.shadow8.key.blur, + disabled: { + elevation: 0, + }, + pressed: { + elevation: t.shadows.shadow2.key.blur, + }, + focused: { + elevation: t.shadows.shadow2.key.blur, + borderWidth: globalTokens.stroke.width20, + borderInnerWidth: globalTokens.stroke.width10, + }, + }, + large: { + borderRadius: globalTokens.corner.radiusCircular, + iconSize: 24, + minHeight: 56, + minWidth: 56, + paddingHorizontal: globalTokens.size160, + paddingVertical: globalTokens.size160, + spacingIconContentBefore: 0, + hasContent: { + borderRadius: globalTokens.corner.radiusCircular, + iconSize: 24, + fontSize: t.typography.variants.body1Strong.size, + fontFamily: t.typography.variants.body1Strong.face, + fontWeight: t.typography.variants.body1Strong.weight, + minHeight: 56, + minWidth: 56, + paddingStart: globalTokens.size160, + paddingEnd: globalTokens.size200, + paddingVertical: globalTokens.size160, + spacingIconContentBefore: globalTokens.size80, + }, + }, + small: { + borderRadius: globalTokens.corner.radiusCircular, + iconSize: 20, + minHeight: 44, + minWidth: 44, + paddingHorizontal: globalTokens.size120, + paddingVertical: globalTokens.size120, + spacingIconContentBefore: 0, + hasContent: { + borderRadius: globalTokens.corner.radiusCircular, + iconSize: 20, + fontSize: t.typography.variants.body2Strong.size, + fontFamily: t.typography.variants.body2Strong.face, + fontWeight: t.typography.variants.body2Strong.weight, + minHeight: 44, + minWidth: 44, + paddingHorizontal: globalTokens.size120, + paddingStart: globalTokens.size120, + paddingEnd: globalTokens.size160, + spacingIconContentBefore: globalTokens.size80, + }, + }, +}); diff --git a/packages/components/Button/src/ToggleButton/ToggleButtonColorTokens.ts b/packages/theming/android-theme/src/components/Button/ToggleButtonColorTokens.ts similarity index 64% rename from packages/components/Button/src/ToggleButton/ToggleButtonColorTokens.ts rename to packages/theming/android-theme/src/components/Button/ToggleButtonColorTokens.ts index a1a5670ae9..8822ac0a4c 100644 --- a/packages/components/Button/src/ToggleButton/ToggleButtonColorTokens.ts +++ b/packages/theming/android-theme/src/components/Button/ToggleButtonColorTokens.ts @@ -1,9 +1,6 @@ -import type { Theme } from '@fluentui-react-native/framework'; -import type { TokenSettings } from '@fluentui-react-native/use-styling'; +import type { Theme } from '@fluentui-react-native/theme-types'; -import type { ToggleButtonTokens } from './ToggleButton.types'; - -export const defaultToggleButtonColorTokens: TokenSettings = (t: Theme): ToggleButtonTokens => ({ +export const defaultToggleButtonColorTokens = (t: Theme) => ({ checked: { color: t.colors.defaultCheckedContent, backgroundColor: t.colors.defaultCheckedBackground, diff --git a/packages/theming/android-theme/src/createAndroidTheme.ts b/packages/theming/android-theme/src/createAndroidTheme.ts index 7ca543861c..45a61d6362 100644 --- a/packages/theming/android-theme/src/createAndroidTheme.ts +++ b/packages/theming/android-theme/src/createAndroidTheme.ts @@ -1,19 +1,24 @@ import { Appearance } from 'react-native'; import { ThemeReference } from '@fluentui-react-native/theme'; -import type { Theme, ThemeOptions } from '@fluentui-react-native/theme-types'; +import type { Theme, PartialTheme, ThemeOptions } from '@fluentui-react-native/theme-types'; import { getAndroidTheme } from './androidTheme'; +import { defaultButtonTheme } from './components/Button/ButtonTheme'; export function createAndroidTheme(options: ThemeOptions = {}): ThemeReference { - const themeRef = new ThemeReference({} as Theme, () => { - // Stub out HC and darkElevated on Android - const current = - options.appearance === 'dynamic' || options.appearance === 'highContrast' || options.appearance === 'darkElevated' - ? (Appearance && Appearance.getColorScheme()) || 'light' - : options.appearance; - return getAndroidTheme(current); - }); + const themeRef = new ThemeReference( + {} as Theme, + () => { + // Stub out HC and darkElevated on Android + const current = + options.appearance === 'dynamic' || options.appearance === 'highContrast' || options.appearance === 'darkElevated' + ? (Appearance && Appearance.getColorScheme()) || 'light' + : options.appearance; + return getAndroidTheme(current); + }, + defaultButtonTheme, + ); if (Appearance && options.appearance === 'dynamic') { Appearance.addChangeListener(() => { diff --git a/packages/theming/apple-theme/package.json b/packages/theming/apple-theme/package.json index 124cc85c26..01090fc2a4 100644 --- a/packages/theming/apple-theme/package.json +++ b/packages/theming/apple-theme/package.json @@ -35,6 +35,7 @@ "@fluentui-react-native/design-tokens-ios": "^0.53.0", "@fluentui-react-native/design-tokens-macos": "^0.53.0", "@fluentui-react-native/experimental-appearance-additions": "workspace:*", + "@fluentui-react-native/immutable-merge": "workspace:*", "@fluentui-react-native/memo-cache": "workspace:*", "@fluentui-react-native/theme": "workspace:*", "@fluentui-react-native/theme-tokens": "workspace:*", @@ -43,6 +44,7 @@ "assert-never": "^1.2.1" }, "devDependencies": { + "@fluentui-react-native/button": "workspace:*", "@fluentui-react-native/eslint-config-rules": "workspace:*", "@fluentui-react-native/scripts": "workspace:*", "@react-native/babel-preset": "^0.73.0", diff --git a/packages/theming/apple-theme/src/__tests__/__snapshots__/apple-theme.test.ts.snap b/packages/theming/apple-theme/src/__tests__/__snapshots__/apple-theme.test.ts.snap index e301af499d..f339f9b248 100644 --- a/packages/theming/apple-theme/src/__tests__/__snapshots__/apple-theme.test.ts.snap +++ b/packages/theming/apple-theme/src/__tests__/__snapshots__/apple-theme.test.ts.snap @@ -2,6 +2,9 @@ exports[`createAppleTheme test 1`] = ` { + "backgroundColor": "#ffffff", + "borderColor": "#d6d6d6", + "color": "#000000", "colors": { "accentButtonBackground": { "dynamic": { @@ -610,9 +613,72 @@ exports[`createAppleTheme test 1`] = ` }, }, }, + "disabled": { + "backgroundColor": "#ffffff", + "borderColor": "#d6d6d6", + "color": "#757575", + "iconColor": "#616161", + }, + "focused": { + "backgroundColor": "#ffffff", + "borderColor": "#d6d6d6", + "color": "#000000", + "icon": "#616161", + }, "host": { "appearance": "dynamic", }, + "hovered": { + "backgroundColor": "#ffffff", + "borderColor": "#d6d6d6", + "color": "#000000", + "iconColor": "#616161", + }, + "iconColor": "#000000", + "large": { + "variant": "subheaderSemibold", + }, + "medium": { + "hasContent": { + "variant": "bodyStandard", + }, + }, + "pressed": { + "backgroundColor": "#ebebeb", + "borderColor": "#d6d6d6", + "color": "#000000", + "iconColor": "#616161", + }, + "primary": { + "backgroundColor": "#0078d4", + "borderColor": "#0078d4", + "color": "#ffffff", + "disabled": { + "backgroundColor": "#f0f0f0", + "borderColor": "#e0e0e0", + "color": "#757575", + "iconColor": "#757575", + }, + "focused": { + "backgroundColor": "#106ebe", + "borderColor": "#000000", + "color": "#ffffff", + "iconColor": "#ffffff", + }, + "hovered": { + "backgroundColor": "#106ebe", + "borderColor": "#106ebe", + "color": "#ffffff", + "iconColor": "#ffffff", + }, + "iconColor": "#ffffff", + "pressed": { + "backgroundColor": "#106ebe", + "borderColor": "#106ebe", + "color": "#ffffff", + "iconColor": "#ffffff", + }, + }, "shadows": { "shadow16": { "ambient": { @@ -783,6 +849,11 @@ exports[`createAppleTheme test 1`] = ` }, }, }, + "small": { + "hasContent": { + "variant": "secondaryStandard", + }, + }, "spacing": { "l1": "20px", "l2": "24px", @@ -790,6 +861,78 @@ exports[`createAppleTheme test 1`] = ` "s1": "12px", "s2": "8px", }, + "subtle": { + "backgroundColor": "transparent", + "borderColor": "transparent", + "color": { + "dynamic": { + "dark": "#1890F1", + "highContrastDark": undefined, + "highContrastLight": undefined, + "light": "#0078D4", + }, + }, + "disabled": { + "backgroundColor": "transparent", + "borderColor": "transparent", + "color": "#757575", + "iconColor": "#757575", + }, + "focused": { + "backgroundColor": "transparent", + "borderColor": "transparent", + "color": { + "dynamic": { + "dark": "#1890F1", + "highContrastDark": undefined, + "highContrastLight": undefined, + "light": "#0078D4", + }, + }, + "icon": { + "dynamic": { + "dark": "#1890F1", + "highContrastDark": undefined, + "highContrastLight": undefined, + "light": "#0078D4", + }, + }, + }, + "hovered": { + "backgroundColor": "transparent", + "borderColor": "transparent", + "color": { + "dynamic": { + "dark": "#1890F1", + "highContrastDark": undefined, + "highContrastLight": undefined, + "light": "#0078D4", + }, + }, + "iconColor": { + "dynamic": { + "dark": "#1890F1", + "highContrastDark": undefined, + "highContrastLight": undefined, + "light": "#0078D4", + }, + }, + }, + "iconColor": { + "dynamic": { + "dark": "#1890F1", + "highContrastDark": undefined, + "highContrastLight": undefined, + "light": "#0078D4", + }, + }, + "pressed": { + "backgroundColor": "transparent", + "borderColor": "transparent", + "color": "#004578", + "icon": "#616161", + }, + }, "typography": { "families": { "cursive": "System", diff --git a/packages/theming/apple-theme/src/__tests__/apple-theme.test.ts b/packages/theming/apple-theme/src/__tests__/apple-theme.test.ts index 5fdb211ef7..6b95a2dde6 100644 --- a/packages/theming/apple-theme/src/__tests__/apple-theme.test.ts +++ b/packages/theming/apple-theme/src/__tests__/apple-theme.test.ts @@ -1,6 +1,12 @@ +import type { ButtonTokens, FABTokens } from '@fluentui-react-native/button'; import type { AppearanceOptions } from '@fluentui-react-native/theme-types'; import { getIsHighContrast, setIsHighContrast } from '../appleHighContrast.macos'; +import { defaultButtonColorTokens } from '../components/Button/ButtonColorTokens'; +import { defaultButtonFontTokens } from '../components/Button/ButtonFontTokens'; +import { defaultButtonTokens } from '../components/Button/ButtonTokens'; +import { defaultFABColorTokens } from '../components/Button/FABColorTokens.ios'; +import { defaultFABTokens } from '../components/Button/FABTokens.ios'; import { createAppleTheme } from '../createAppleTheme'; import { createMacOSColorAliasTokens, createMacOSShadowAliasTokens } from '../createMacOSAliasTokens'; @@ -65,3 +71,20 @@ it.concurrent.each(macOSAliasTokensTable)( } }, ); + +describe('verify types', () => { + it('Button types', () => { + const officeTheme = createAppleTheme().theme; + const colorTokens: ButtonTokens = defaultButtonColorTokens(officeTheme); + expect(colorTokens).toBeTruthy(); + const fontTokens: ButtonTokens = defaultButtonFontTokens(officeTheme); + expect(fontTokens).toBeTruthy(); + const tokens: ButtonTokens = defaultButtonTokens(officeTheme); + expect(tokens).toBeTruthy(); + + const fabColorTokens: FABTokens = defaultFABColorTokens(officeTheme); + expect(fabColorTokens).toBeTruthy(); + const fabTokens: FABTokens = defaultFABTokens(officeTheme); + expect(fabTokens).toBeTruthy(); + }); +}); diff --git a/packages/theming/apple-theme/src/components/Button/ButtonColorTokens.macos.ts b/packages/theming/apple-theme/src/components/Button/ButtonColorTokens.macos.ts new file mode 100644 index 0000000000..05b0ae1349 --- /dev/null +++ b/packages/theming/apple-theme/src/components/Button/ButtonColorTokens.macos.ts @@ -0,0 +1,92 @@ +import type { Theme } from '@fluentui-react-native/theme-types'; + +export const defaultButtonColorTokens = (t: Theme) => ({ + backgroundColor: t.colors.defaultBackground, + color: t.colors.defaultContent, + borderColor: t.colors.defaultBorder, + iconColor: t.colors.defaultContent, + disabled: { + backgroundColor: t.colors.defaultDisabledBackground, + color: t.colors.defaultDisabledContent, + borderColor: t.colors.defaultDisabledBorder, + iconColor: t.colors.defaultDisabledIcon, + }, + hovered: { + backgroundColor: t.colors.defaultBackground, + color: t.colors.defaultContent, + borderColor: t.colors.defaultBorder, + iconColor: t.colors.defaultHoveredIcon, + }, + pressed: { + backgroundColor: t.colors.defaultPressedBackground, + color: t.colors.defaultPressedContent, + borderColor: t.colors.defaultBorder, + iconColor: t.colors.defaultPressedIcon, + }, + focused: { + backgroundColor: t.colors.defaultFocusedBackground, + color: t.colors.defaultFocusedContent, + borderColor: t.colors.defaultFocusedBorder, + icon: t.colors.defaultFocusedIcon, + }, + primary: { + backgroundColor: t.colors.brandBackground, + color: t.colors.neutralForegroundOnBrand, + borderColor: t.colors.brandStroke1, + iconColor: t.colors.neutralForegroundOnBrand, + disabled: { + backgroundColor: t.colors.neutralBackgroundDisabled, + color: t.colors.neutralForegroundDisabled, + borderColor: t.colors.neutralStrokeDisabled, + iconColor: t.colors.neutralForegroundDisabled, + }, + hovered: { + backgroundColor: t.colors.brandBackgroundHover, + color: t.colors.neutralForegroundOnBrandHover, + borderColor: t.colors.brandBackgroundHover, + iconColor: t.colors.neutralForegroundOnBrandHover, + }, + pressed: { + backgroundColor: t.colors.brandBackgroundPressed, + color: t.colors.neutralForegroundOnBrandPressed, + borderColor: t.colors.brandBackgroundPressed, + iconColor: t.colors.neutralForegroundOnBrandPressed, + }, + focused: { + backgroundColor: t.colors.brandBackgroundHover, + color: t.colors.neutralForegroundOnBrandHover, + borderColor: t.colors.strokeFocus2, + iconColor: t.colors.neutralForegroundOnBrandHover, + }, + }, + subtle: { + backgroundColor: t.colors.ghostBackground, + color: t.colors.ghostContent, + borderColor: t.colors.ghostBorder, + iconColor: t.colors.ghostIcon, + disabled: { + color: t.colors.ghostDisabledContent, + borderColor: t.colors.ghostDisabledBorder, + backgroundColor: t.colors.ghostDisabledBackground, + iconColor: t.colors.ghostDisabledIcon, + }, + hovered: { + backgroundColor: t.colors.ghostHoveredBackground, + color: t.colors.ghostHoveredContent, + borderColor: t.colors.ghostHoveredBorder, + iconColor: t.colors.ghostHoveredIcon, + }, + pressed: { + backgroundColor: t.colors.ghostPressedBackground, + borderColor: t.colors.ghostPressedBorder, + color: t.colors.ghostPressedContent, + icon: t.colors.ghostPressedIcon, + }, + focused: { + borderColor: t.colors.ghostFocusedBorder, + backgroundColor: t.colors.ghostFocusedBackground, + color: t.colors.ghostFocusedContent, + icon: t.colors.ghostFocusedIcon, + }, + }, +}); diff --git a/packages/theming/apple-theme/src/components/Button/ButtonColorTokens.ts b/packages/theming/apple-theme/src/components/Button/ButtonColorTokens.ts new file mode 100644 index 0000000000..82404a847c --- /dev/null +++ b/packages/theming/apple-theme/src/components/Button/ButtonColorTokens.ts @@ -0,0 +1,3 @@ +import type { Theme } from '@fluentui-react-native/theme-types'; + +export const defaultButtonColorTokens = (_t: Theme) => ({}); diff --git a/packages/theming/apple-theme/src/components/Button/ButtonFontTokens.ios.ts b/packages/theming/apple-theme/src/components/Button/ButtonFontTokens.ios.ts new file mode 100644 index 0000000000..41f9991be3 --- /dev/null +++ b/packages/theming/apple-theme/src/components/Button/ButtonFontTokens.ios.ts @@ -0,0 +1,19 @@ +import type { Theme } from '@fluentui-react-native/theme-types'; + +export const defaultButtonFontTokens = (t: Theme) => ({ + medium: { + fontSize: t.typography.variants.caption1Strong!.size, + fontFamily: t.typography.variants.caption1Strong!.face, + fontWeight: t.typography.variants.caption1Strong!.weight, + }, + small: { + fontSize: t.typography.variants.caption1Strong!.size, + fontFamily: t.typography.variants.caption1Strong!.face, + fontWeight: t.typography.variants.caption1Strong!.weight, + }, + large: { + fontSize: t.typography.variants.body1Strong!.size, + fontFamily: t.typography.variants.body1Strong!.face, + fontWeight: t.typography.variants.body1Strong!.weight, + }, +}); diff --git a/packages/theming/apple-theme/src/components/Button/ButtonFontTokens.macos.ts b/packages/theming/apple-theme/src/components/Button/ButtonFontTokens.macos.ts new file mode 100644 index 0000000000..2624ec74f8 --- /dev/null +++ b/packages/theming/apple-theme/src/components/Button/ButtonFontTokens.macos.ts @@ -0,0 +1,17 @@ +import type { Theme } from '@fluentui-react-native/theme-types'; + +export const defaultButtonFontTokens = (_t: Theme) => ({ + medium: { + hasContent: { + variant: 'bodyStandard', + }, + }, + small: { + hasContent: { + variant: 'secondaryStandard', + }, + }, + large: { + variant: 'subheaderSemibold', + }, +}); diff --git a/packages/theming/apple-theme/src/components/Button/ButtonFontTokens.ts b/packages/theming/apple-theme/src/components/Button/ButtonFontTokens.ts new file mode 100644 index 0000000000..8b2ea33d78 --- /dev/null +++ b/packages/theming/apple-theme/src/components/Button/ButtonFontTokens.ts @@ -0,0 +1,3 @@ +import type { Theme } from '@fluentui-react-native/theme-types'; + +export const defaultButtonFontTokens = (_t: Theme) => ({}); diff --git a/packages/theming/apple-theme/src/components/Button/ButtonTheme.ios.ts b/packages/theming/apple-theme/src/components/Button/ButtonTheme.ios.ts new file mode 100644 index 0000000000..b269bd0972 --- /dev/null +++ b/packages/theming/apple-theme/src/components/Button/ButtonTheme.ios.ts @@ -0,0 +1,34 @@ +import { immutableMerge } from '@fluentui-react-native/immutable-merge'; +import type { Theme } from '@fluentui-react-native/theme-types'; + +import { defaultButtonColorTokens } from './ButtonColorTokens'; +import { defaultButtonFontTokens } from './ButtonFontTokens'; +import { defaultButtonTokens } from './ButtonTokens'; +import { defaultCompoundButtonColorTokens } from './CompoundButtonColorTokens'; +import { defaultCompoundButtonFontTokens } from './CompoundButtonFontTokens'; +import { defaultCompoundButtonTokens } from './CompoundButtonTokens'; +import { defaultFABColorTokens } from './FABColorTokens.ios'; +import { defaultFABTokens } from './FABTokens.ios'; +import { defaultToggleButtonColorTokens } from './ToggleButtonColorTokens'; + +export const defaultButtonTheme = (theme: Theme) => ({ + components: { + Button: immutableMerge(defaultButtonColorTokens(theme), defaultButtonFontTokens(theme), defaultButtonTokens(theme)), + CompoundButton: immutableMerge( + defaultButtonTokens(theme), + defaultButtonColorTokens(theme), + defaultCompoundButtonColorTokens(theme), + defaultCompoundButtonFontTokens(theme), + defaultCompoundButtonTokens(theme), + ), + + FAB: immutableMerge(defaultFABTokens(theme), defaultFABColorTokens(theme)), + + ToggleButton: immutableMerge( + defaultButtonTokens(theme), + defaultButtonFontTokens(theme), + defaultButtonColorTokens(theme), + defaultToggleButtonColorTokens(theme), + ), + }, +}); diff --git a/packages/theming/apple-theme/src/components/Button/ButtonTheme.ts b/packages/theming/apple-theme/src/components/Button/ButtonTheme.ts new file mode 100644 index 0000000000..ec13f389e6 --- /dev/null +++ b/packages/theming/apple-theme/src/components/Button/ButtonTheme.ts @@ -0,0 +1,30 @@ +import { immutableMerge } from '@fluentui-react-native/immutable-merge'; +import type { Theme } from '@fluentui-react-native/theme-types'; + +import { defaultButtonColorTokens } from './ButtonColorTokens'; +import { defaultButtonFontTokens } from './ButtonFontTokens'; +import { defaultButtonTokens } from './ButtonTokens'; +import { defaultCompoundButtonColorTokens } from './CompoundButtonColorTokens'; +import { defaultCompoundButtonFontTokens } from './CompoundButtonFontTokens'; +import { defaultCompoundButtonTokens } from './CompoundButtonTokens'; +import { defaultToggleButtonColorTokens } from './ToggleButtonColorTokens'; + +export const defaultButtonTheme = (theme: Theme) => ({ + components: { + Button: immutableMerge(defaultButtonColorTokens(theme), defaultButtonFontTokens(theme), defaultButtonTokens(theme)), + CompoundButton: immutableMerge( + defaultButtonTokens(theme), + defaultButtonColorTokens(theme), + defaultCompoundButtonColorTokens(theme), + defaultCompoundButtonFontTokens(theme), + defaultCompoundButtonTokens(theme), + ), + + ToggleButton: immutableMerge( + defaultButtonTokens(theme), + defaultButtonFontTokens(theme), + defaultButtonColorTokens(theme), + defaultToggleButtonColorTokens(theme), + ), + }, +}); diff --git a/packages/theming/apple-theme/src/components/Button/ButtonTokens.ios.ts b/packages/theming/apple-theme/src/components/Button/ButtonTokens.ios.ts new file mode 100644 index 0000000000..af417744a4 --- /dev/null +++ b/packages/theming/apple-theme/src/components/Button/ButtonTokens.ios.ts @@ -0,0 +1,88 @@ +import type { DimensionValue } from 'react-native'; + +import { globalTokensIOS as globalTokens } from '@fluentui-react-native/theme-tokens'; +import type { Theme } from '@fluentui-react-native/theme-types'; + +export const defaultButtonTokens = (_t: Theme) => ({ + block: { + width: '100%' as DimensionValue, + }, + medium: { + paddingHorizontal: globalTokens.size120, + borderWidth: globalTokens.stroke.width10, + borderRadius: globalTokens.corner.radius80, + minHeight: 40, + iconSize: 20, + focused: { + borderWidth: 0, + }, + hasContent: { + minWidth: 96, + hasIconAfter: { + spacingIconContentAfter: globalTokens.size80, + }, + hasIconBefore: { + spacingIconContentBefore: globalTokens.size80, + }, + }, + }, + small: { + paddingHorizontal: globalTokens.size60, + borderWidth: globalTokens.stroke.width10, + borderRadius: globalTokens.corner.radius80, + minHeight: 28, + iconSize: 16, + focused: { + borderWidth: 0, + }, + hasContent: { + minWidth: 64, + minHeight: 28, + hasIconAfter: { + spacingIconContentAfter: globalTokens.size40, + }, + hasIconBefore: { + spacingIconContentBefore: globalTokens.size40, + }, + }, + }, + large: { + paddingHorizontal: globalTokens.size160, + borderWidth: globalTokens.stroke.width10, + iconSize: 20, + borderRadius: globalTokens.corner.radius120, + minHeight: 52, + focused: { + borderWidth: 0, + }, + hasContent: { + minWidth: 96, + hasIconAfter: { + spacingIconContentAfter: globalTokens.size80, + }, + hasIconBefore: { + spacingIconContentBefore: globalTokens.size80, + }, + }, + }, + circular: { + borderRadius: globalTokens.corner.radiusCircular, + }, + square: { + borderRadius: globalTokens.corner.radiusNone, + }, + getPlatformSpecificAppearance: (appearance): 'accent' | 'primary' | 'subtle' | 'outline' => { + switch (appearance) { + case 'accent': // Included to cover Mobile platform naming guidelines, maps to 'primary'. + return 'primary'; + + case 'primary': + case 'subtle': + case 'outline': // 'Outline' exists only for Mobile platforms, default picked on other platforms. + return appearance; + + default: + return 'primary'; + } + }, +}); diff --git a/packages/theming/apple-theme/src/components/Button/ButtonTokens.ts b/packages/theming/apple-theme/src/components/Button/ButtonTokens.ts new file mode 100644 index 0000000000..e4f0444f66 --- /dev/null +++ b/packages/theming/apple-theme/src/components/Button/ButtonTokens.ts @@ -0,0 +1,100 @@ +import type { DimensionValue } from 'react-native'; + +import { globalTokens } from '@fluentui-react-native/theme-tokens'; +import type { Theme } from '@fluentui-react-native/theme-types'; + +export const defaultButtonTokens = (_t: Theme) => ({ + block: { + width: '100%' as DimensionValue, + }, + medium: { + padding: globalTokens.size60 - globalTokens.stroke.width10, + borderWidth: globalTokens.stroke.width10, + iconSize: 16, + focused: { + borderWidth: 0, + padding: globalTokens.size60, + }, + hasContent: { + minWidth: 96, + paddingHorizontal: globalTokens.size120 - globalTokens.stroke.width10, + hasIconAfter: { + spacingIconContentAfter: globalTokens.size60, + }, + hasIconBefore: { + spacingIconContentBefore: globalTokens.size60, + }, + focused: { + paddingHorizontal: globalTokens.size120, + }, + }, + }, + small: { + padding: globalTokens.size40 - globalTokens.stroke.width10, + borderWidth: globalTokens.stroke.width10, + iconSize: 16, + focused: { + borderWidth: 0, + padding: globalTokens.size40, + }, + hasContent: { + minWidth: 64, + minHeight: 24, + paddingHorizontal: globalTokens.size80 - globalTokens.stroke.width10, + hasIconAfter: { + spacingIconContentAfter: globalTokens.size40, + }, + hasIconBefore: { + spacingIconContentBefore: globalTokens.size40, + }, + focused: { + paddingHorizontal: globalTokens.size80, + }, + }, + }, + large: { + padding: globalTokens.size80 - globalTokens.stroke.width10, + borderWidth: globalTokens.stroke.width10, + iconSize: 20, + focused: { + borderWidth: 0, + padding: globalTokens.size80, + }, + hasContent: { + minWidth: 96, + paddingHorizontal: globalTokens.size160 - globalTokens.stroke.width10, + hasIconAfter: { + spacingIconContentAfter: globalTokens.size60, + }, + hasIconBefore: { + spacingIconContentBefore: globalTokens.size60, + }, + focused: { + paddingHorizontal: globalTokens.size160, + }, + }, + }, + rounded: { + borderRadius: globalTokens.corner.radius40, + }, + circular: { + borderRadius: globalTokens.corner.radiusCircular, + }, + square: { + borderRadius: globalTokens.corner.radiusNone, + }, + getPlatformSpecificAppearance: (appearance): 'primary' | 'subtle' | 'outline' | null => { + switch (appearance) { + case 'accent': // Included to cover Mobile platform naming guidelines, maps to 'primary'. + return 'primary'; + + case 'primary': + case 'subtle': + case 'outline': // 'Outline' exists only for Mobile platforms, default picked on other platforms. + return appearance; + + default: + return null; + } + }, +}); diff --git a/packages/components/Button/src/CompoundButton/CompoundButtonColorTokens.ts b/packages/theming/apple-theme/src/components/Button/CompoundButtonColorTokens.ts similarity index 75% rename from packages/components/Button/src/CompoundButton/CompoundButtonColorTokens.ts rename to packages/theming/apple-theme/src/components/Button/CompoundButtonColorTokens.ts index 6e739edfee..c87c62bc25 100644 --- a/packages/components/Button/src/CompoundButton/CompoundButtonColorTokens.ts +++ b/packages/theming/apple-theme/src/components/Button/CompoundButtonColorTokens.ts @@ -1,9 +1,6 @@ -import type { Theme } from '@fluentui-react-native/framework'; -import type { TokenSettings } from '@fluentui-react-native/use-styling'; +import type { Theme } from '@fluentui-react-native/theme-types'; -import type { CompoundButtonTokens } from './CompoundButton.types'; - -export const defaultCompoundButtonColorTokens: TokenSettings = (t: Theme): CompoundButtonTokens => ({ +export const defaultCompoundButtonColorTokens = (t: Theme) => ({ secondaryContentColor: t.colors.defaultSecondaryContent, hovered: { secondaryContentColor: t.colors.defaultHoveredSecondaryContent, diff --git a/packages/components/Button/src/CompoundButton/CompoundButtonFontTokens.ts b/packages/theming/apple-theme/src/components/Button/CompoundButtonFontTokens.ts similarity index 54% rename from packages/components/Button/src/CompoundButton/CompoundButtonFontTokens.ts rename to packages/theming/apple-theme/src/components/Button/CompoundButtonFontTokens.ts index d278051a82..f824ac4879 100644 --- a/packages/components/Button/src/CompoundButton/CompoundButtonFontTokens.ts +++ b/packages/theming/apple-theme/src/components/Button/CompoundButtonFontTokens.ts @@ -1,9 +1,6 @@ -import type { Theme } from '@fluentui-react-native/framework'; -import type { TokenSettings } from '@fluentui-react-native/use-styling'; +import type { Theme } from '@fluentui-react-native/theme-types'; -import type { CompoundButtonTokens } from './CompoundButton.types'; - -export const defaultCompoundButtonFontTokens: TokenSettings = (_t: Theme): CompoundButtonTokens => ({ +export const defaultCompoundButtonFontTokens = (_t: Theme) => ({ medium: { hasContent: { variant: 'bodySemibold', diff --git a/packages/components/Button/src/CompoundButton/CompoundButtonTokens.ts b/packages/theming/apple-theme/src/components/Button/CompoundButtonTokens.ts similarity index 87% rename from packages/components/Button/src/CompoundButton/CompoundButtonTokens.ts rename to packages/theming/apple-theme/src/components/Button/CompoundButtonTokens.ts index 6fcb3af8d7..04400e847d 100644 --- a/packages/components/Button/src/CompoundButton/CompoundButtonTokens.ts +++ b/packages/theming/apple-theme/src/components/Button/CompoundButtonTokens.ts @@ -1,10 +1,7 @@ -import type { Theme } from '@fluentui-react-native/framework'; import { globalTokens } from '@fluentui-react-native/theme-tokens'; -import type { TokenSettings } from '@fluentui-react-native/use-styling'; +import type { Theme } from '@fluentui-react-native/theme-types'; -import type { CompoundButtonTokens } from './CompoundButton.types'; - -export const defaultCompoundButtonTokens: TokenSettings = (): CompoundButtonTokens => ({ +export const defaultCompoundButtonTokens = (_t: Theme) => ({ medium: { padding: globalTokens.size120 - globalTokens.stroke.width10, focused: { diff --git a/packages/components/Button/src/FAB/FABColorTokens.ios.ts b/packages/theming/apple-theme/src/components/Button/FABColorTokens.ios.ts similarity index 84% rename from packages/components/Button/src/FAB/FABColorTokens.ios.ts rename to packages/theming/apple-theme/src/components/Button/FABColorTokens.ios.ts index 35401ba006..bf320a50b1 100644 --- a/packages/components/Button/src/FAB/FABColorTokens.ios.ts +++ b/packages/theming/apple-theme/src/components/Button/FABColorTokens.ios.ts @@ -1,9 +1,6 @@ -import type { Theme } from '@fluentui-react-native/framework'; -import type { TokenSettings } from '@fluentui-react-native/use-styling'; +import type { Theme } from '@fluentui-react-native/theme-types'; -import type { FABTokens } from './FAB.types'; - -export const defaultFABColorTokens: TokenSettings = (t: Theme): FABTokens => ({ +export const defaultFABColorTokens = (t: Theme) => ({ // Default coloring same as 'primary' or 'accent' backgroundColor: t.colors.brandBackground, color: t.colors.neutralForegroundOnColor, diff --git a/packages/theming/apple-theme/src/components/Button/FABTokens.ios.ts b/packages/theming/apple-theme/src/components/Button/FABTokens.ios.ts new file mode 100644 index 0000000000..1553d6dbf8 --- /dev/null +++ b/packages/theming/apple-theme/src/components/Button/FABTokens.ios.ts @@ -0,0 +1,75 @@ +import { globalTokensIOS as globalTokens } from '@fluentui-react-native/theme-tokens'; +import type { Theme } from '@fluentui-react-native/theme-types'; + +export const defaultFABTokens = (t: Theme) => ({ + shadowToken: t.shadows.shadow8, + disabled: { + shadowToken: t.shadows.shadow2, + }, + pressed: { + shadowToken: t.shadows.shadow2, + }, + focused: { + shadowToken: t.shadows.shadow2, + borderWidth: globalTokens.stroke.width20, + borderInnerWidth: globalTokens.stroke.width10, + }, + subtle: { + shadowToken: t.shadows.shadow8, + disabled: { + shadowToken: t.shadows.shadow2, + }, + pressed: { + shadowToken: t.shadows.shadow2, + }, + focused: { + shadowToken: t.shadows.shadow2, + borderWidth: globalTokens.stroke.width20, + borderInnerWidth: globalTokens.stroke.width10, + }, + }, + large: { + borderRadius: globalTokens.corner.radiusCircular, + iconSize: 24, + minHeight: 56, + minWidth: 56, + paddingHorizontal: globalTokens.size160, + paddingVertical: globalTokens.size160, + spacingIconContentBefore: 0, + hasContent: { + borderRadius: globalTokens.corner.radiusCircular, + iconSize: 24, + fontSize: t.typography.variants.body1Strong.size, + fontFamily: t.typography.variants.body1Strong.face, + fontWeight: t.typography.variants.body1Strong.weight, + minHeight: 56, + minWidth: 56, + paddingStart: globalTokens.size160, + paddingEnd: globalTokens.size200, + paddingVertical: globalTokens.size160, + spacingIconContentBefore: globalTokens.size80, + }, + }, + small: { + borderRadius: globalTokens.corner.radiusCircular, + iconSize: 20, + minHeight: 44, + minWidth: 44, + paddingHorizontal: globalTokens.size120, + paddingVertical: globalTokens.size120, + spacingIconContentBefore: 0, + hasContent: { + borderRadius: globalTokens.corner.radiusCircular, + iconSize: 20, + fontSize: t.typography.variants.body2Strong.size, + fontFamily: t.typography.variants.body2Strong.face, + fontWeight: t.typography.variants.body2Strong.weight, + minHeight: 48, + minWidth: 48, + paddingHorizontal: globalTokens.size120, + paddingStart: globalTokens.size120, + paddingEnd: globalTokens.size160, + spacingIconContentBefore: globalTokens.size80, + }, + }, +}); diff --git a/packages/theming/apple-theme/src/components/Button/ToggleButtonColorTokens.ts b/packages/theming/apple-theme/src/components/Button/ToggleButtonColorTokens.ts new file mode 100644 index 0000000000..8822ac0a4c --- /dev/null +++ b/packages/theming/apple-theme/src/components/Button/ToggleButtonColorTokens.ts @@ -0,0 +1,21 @@ +import type { Theme } from '@fluentui-react-native/theme-types'; + +export const defaultToggleButtonColorTokens = (t: Theme) => ({ + checked: { + color: t.colors.defaultCheckedContent, + backgroundColor: t.colors.defaultCheckedBackground, + hovered: { + color: t.colors.defaultCheckedHoveredContent, + backgroundColor: t.colors.defaultCheckedHoveredBackground, + }, + subtle: { + color: t.colors.ghostCheckedContent, + backgroundColor: t.colors.ghostCheckedBackground, + hovered: { + color: t.colors.ghostCheckedHoveredContent, + backgroundColor: t.colors.ghostCheckedHoveredBackground, + borderColor: t.colors.ghostCheckedHoveredBorder, + }, + }, + }, +}); diff --git a/packages/theming/apple-theme/src/createAppleTheme.ios.ts b/packages/theming/apple-theme/src/createAppleTheme.ios.ts index a165986345..1215ee6d33 100644 --- a/packages/theming/apple-theme/src/createAppleTheme.ios.ts +++ b/packages/theming/apple-theme/src/createAppleTheme.ios.ts @@ -2,16 +2,21 @@ import { Appearance, NativeEventEmitter } from 'react-native'; import { NativeAppearanceAdditions } from '@fluentui-react-native/experimental-appearance-additions'; import { ThemeReference } from '@fluentui-react-native/theme'; -import type { Theme } from '@fluentui-react-native/theme-types'; +import type { PartialTheme, Theme } from '@fluentui-react-native/theme-types'; import { getBaseAppleThemeIOS } from './appleTheme.ios'; +import { defaultButtonTheme } from './components/Button/ButtonTheme'; export function createAppleTheme(): ThemeReference { - const appleThemeReference = new ThemeReference({} as Theme, () => { - const isLightMode = Appearance.getColorScheme() === 'light'; - const isElevated = NativeAppearanceAdditions.userInterfaceLevel() === 'elevated'; - return getBaseAppleThemeIOS(isLightMode, isElevated); - }); + const appleThemeReference = new ThemeReference( + {} as Theme, + () => { + const isLightMode = Appearance.getColorScheme() === 'light'; + const isElevated = NativeAppearanceAdditions.userInterfaceLevel() === 'elevated'; + return getBaseAppleThemeIOS(isLightMode, isElevated); + }, + defaultButtonTheme, + ); Appearance.addChangeListener(() => { appleThemeReference.invalidate(); diff --git a/packages/theming/apple-theme/src/createAppleTheme.macos.ts b/packages/theming/apple-theme/src/createAppleTheme.macos.ts index 44a30f8ec5..0803d8bfc2 100644 --- a/packages/theming/apple-theme/src/createAppleTheme.macos.ts +++ b/packages/theming/apple-theme/src/createAppleTheme.macos.ts @@ -1,21 +1,27 @@ import { Appearance } from 'react-native'; import { ThemeReference } from '@fluentui-react-native/theme'; -import type { Theme } from '@fluentui-react-native/theme-types'; +import type { PartialTheme } from '@fluentui-react-native/theme-types'; +import { type Theme } from '@fluentui-react-native/theme-types'; import { getCurrentAppearance } from '@fluentui-react-native/theming-utils'; import { AccessibilityInfo } from 'react-native-macos'; import { setIsHighContrast } from './appleHighContrast.macos'; import { getBaseAppleThemeMacOS } from './appleTheme.macos'; +import { defaultButtonTheme } from './components/Button/ButtonTheme'; let appleThemeReference: ThemeReference; export function createAppleTheme(): ThemeReference { - appleThemeReference = new ThemeReference({} as Theme, () => { - const appearance = Appearance.getColorScheme(); - const mode = getCurrentAppearance(appearance, 'light'); - return getBaseAppleThemeMacOS(mode); - }); + appleThemeReference = new ThemeReference( + {} as Theme, + () => { + const appearance = Appearance.getColorScheme(); + const mode = getCurrentAppearance(appearance, 'light'); + return getBaseAppleThemeMacOS(mode); + }, + defaultButtonTheme, + ); // Fetch initial system settings for high contrast mode highContrastHandler(); // Invalidate theme and set prop when high contrast setting changes diff --git a/packages/theming/default-theme/package.json b/packages/theming/default-theme/package.json index 32426b1120..c8dc238ad0 100644 --- a/packages/theming/default-theme/package.json +++ b/packages/theming/default-theme/package.json @@ -31,6 +31,7 @@ "author": "", "license": "MIT", "dependencies": { + "@fluentui-react-native/immutable-merge": "workspace:*", "@fluentui-react-native/memo-cache": "workspace:*", "@fluentui-react-native/theme": "workspace:*", "@fluentui-react-native/theme-tokens": "workspace:*", diff --git a/packages/theming/default-theme/src/components/Button/ButtonColorTokens.ts b/packages/theming/default-theme/src/components/Button/ButtonColorTokens.ts new file mode 100644 index 0000000000..0ebca6c88d --- /dev/null +++ b/packages/theming/default-theme/src/components/Button/ButtonColorTokens.ts @@ -0,0 +1,84 @@ +import type { Theme } from '@fluentui-react-native/theme-types'; + +export const defaultButtonColorTokens = (t: Theme) => ({ + backgroundColor: t.colors.buttonBackground, + color: t.colors.buttonText, + borderColor: t.colors.buttonBorder, + iconColor: t.colors.buttonIcon, + disabled: { + backgroundColor: t.colors.defaultDisabledBackground, + color: t.colors.defaultDisabledContent, + borderColor: t.colors.defaultDisabledBorder, + iconColor: t.colors.defaultDisabledIcon, + }, + hovered: { + backgroundColor: t.colors.defaultHoveredBackground, + color: t.colors.defaultHoveredContent, + borderColor: t.colors.defaultHoveredBorder, + iconColor: t.colors.defaultHoveredIcon, + }, + pressed: { + backgroundColor: t.colors.defaultPressedBackground, + color: t.colors.defaultPressedContent, + borderColor: t.colors.defaultPressedBorder, + iconColor: t.colors.defaultPressedIcon, + }, + focused: { + backgroundColor: t.colors.defaultFocusedBackground, + color: t.colors.defaultFocusedContent, + borderColor: t.colors.defaultFocusedBorder, + icon: t.colors.defaultFocusedIcon, + }, + primary: { + backgroundColor: t.colors.brandBackground, + color: t.colors.neutralForegroundOnColor, + borderColor: t.colors.brandStroke1, + iconColor: t.colors.neutralForegroundOnColor, + disabled: { + backgroundColor: t.colors.brandBackgroundDisabled, + color: t.colors.neutralForegroundDisabled1, + iconColor: t.colors.neutralForegroundDisabled1, + }, + pressed: { + backgroundColor: t.colors.brandBackgroundPressed, + color: t.colors.neutralForegroundOnColor, + iconColor: t.colors.neutralForegroundOnColor, + }, + focused: { + backgroundColor: t.colors.brandBackground, + color: t.colors.neutralForegroundOnColor, + borderColor: t.colors.strokeFocus2, + iconColor: t.colors.neutralForegroundOnColor, + }, + }, + subtle: { + backgroundColor: t.colors.ghostBackground, + color: t.colors.ghostContent, + borderColor: t.colors.ghostBorder, + iconColor: t.colors.ghostIcon, + disabled: { + color: t.colors.ghostDisabledContent, + borderColor: t.colors.ghostDisabledBorder, + backgroundColor: t.colors.ghostDisabledBackground, + iconColor: t.colors.ghostDisabledIcon, + }, + hovered: { + backgroundColor: t.colors.ghostHoveredBackground, + color: t.colors.ghostHoveredContent, + borderColor: t.colors.ghostHoveredBorder, + iconColor: t.colors.ghostHoveredIcon, + }, + pressed: { + backgroundColor: t.colors.ghostPressedBackground, + borderColor: t.colors.ghostPressedBorder, + color: t.colors.ghostPressedContent, + icon: t.colors.ghostPressedIcon, + }, + focused: { + borderColor: t.colors.ghostFocusedBorder, + backgroundColor: t.colors.ghostFocusedBackground, + color: t.colors.ghostFocusedContent, + icon: t.colors.ghostFocusedIcon, + }, + }, +}); diff --git a/packages/components/Button/src/ButtonColorTokens.windows.ts b/packages/theming/default-theme/src/components/Button/ButtonColorTokens.windows.ts similarity index 94% rename from packages/components/Button/src/ButtonColorTokens.windows.ts rename to packages/theming/default-theme/src/components/Button/ButtonColorTokens.windows.ts index 17de26c304..08a2e8c5ad 100644 --- a/packages/components/Button/src/ButtonColorTokens.windows.ts +++ b/packages/theming/default-theme/src/components/Button/ButtonColorTokens.windows.ts @@ -1,12 +1,9 @@ import { PlatformColor } from 'react-native'; -import type { Theme } from '@fluentui-react-native/framework'; +import type { Theme } from '@fluentui-react-native/theme-types'; import { isHighContrast } from '@fluentui-react-native/theming-utils'; -import type { TokenSettings } from '@fluentui-react-native/use-styling'; -import type { ButtonTokens } from './Button.types'; - -export const defaultButtonColorTokens: TokenSettings = (t: Theme) => { +export const defaultButtonColorTokens = (t: Theme) => { if (isHighContrast(t)) { return highContrastColors; } @@ -100,7 +97,7 @@ export const defaultButtonColorTokens: TokenSettings = (t: iconColor: t.colors.neutralForeground1Hover, }, }, - } as ButtonTokens; + }; }; const highContrastColors = { diff --git a/packages/theming/default-theme/src/components/Button/ButtonFontTokens.ts b/packages/theming/default-theme/src/components/Button/ButtonFontTokens.ts new file mode 100644 index 0000000000..1077077d91 --- /dev/null +++ b/packages/theming/default-theme/src/components/Button/ButtonFontTokens.ts @@ -0,0 +1,19 @@ +import type { Theme } from '@fluentui-react-native/theme-types'; + +type fontVariantType = 'secondaryStandard' | 'bodyStandard'; + +export const defaultButtonFontTokens = (_t: Theme) => ({ + medium: { + hasContent: { + variant: 'bodySemibold' as fontVariantType, + }, + }, + small: { + hasContent: { + variant: 'secondaryStandard' as fontVariantType, + }, + }, + large: { + variant: 'subheaderSemibold' as fontVariantType, + }, +}); diff --git a/packages/theming/default-theme/src/components/Button/ButtonTheme.ts b/packages/theming/default-theme/src/components/Button/ButtonTheme.ts new file mode 100644 index 0000000000..45835b5277 --- /dev/null +++ b/packages/theming/default-theme/src/components/Button/ButtonTheme.ts @@ -0,0 +1,29 @@ +import { immutableMerge } from '@fluentui-react-native/immutable-merge'; +import type { Theme } from '@fluentui-react-native/theme-types'; + +import { defaultButtonColorTokens } from './ButtonColorTokens'; +import { defaultButtonFontTokens } from './ButtonFontTokens'; +import { defaultButtonTokens } from './ButtonTokens'; +import { defaultCompoundButtonColorTokens } from './CompoundButtonColorTokens'; +import { defaultCompoundButtonFontTokens } from './CompoundButtonFontTokens'; +import { defaultCompoundButtonTokens } from './CompoundButtonTokens'; +import { defaultToggleButtonColorTokens } from './ToggleButtonColorTokens'; + +export const defaultButtonTheme = (theme: Theme) => ({ + components: { + Button: immutableMerge(defaultButtonColorTokens(theme), defaultButtonFontTokens(theme), defaultButtonTokens(theme)), + CompoundButton: immutableMerge( + defaultButtonTokens(theme), + defaultButtonColorTokens(theme), + defaultCompoundButtonColorTokens(theme), + defaultCompoundButtonFontTokens(theme), + defaultCompoundButtonTokens(theme), + ), + ToggleButton: immutableMerge( + defaultButtonTokens(theme), + defaultButtonFontTokens(theme), + defaultButtonColorTokens(theme), + defaultToggleButtonColorTokens(theme), + ), + }, +}); diff --git a/packages/theming/default-theme/src/components/Button/ButtonTokens.ts b/packages/theming/default-theme/src/components/Button/ButtonTokens.ts new file mode 100644 index 0000000000..5a044c9068 --- /dev/null +++ b/packages/theming/default-theme/src/components/Button/ButtonTokens.ts @@ -0,0 +1,101 @@ +import type { DimensionValue } from 'react-native'; + +import { globalTokens } from '@fluentui-react-native/theme-tokens'; +import type { Theme } from '@fluentui-react-native/theme-types'; + +export const defaultButtonTokens = (_t: Theme) => ({ + block: { + width: '100%' as DimensionValue, + }, + medium: { + padding: globalTokens.size60 - globalTokens.stroke.width10, + borderWidth: globalTokens.stroke.width10, + iconSize: 16, + focused: { + borderWidth: 0, + padding: globalTokens.size60, + }, + hasContent: { + minWidth: 96, + paddingHorizontal: globalTokens.size120 - globalTokens.stroke.width10, + hasIconAfter: { + spacingIconContentAfter: globalTokens.size60, + }, + hasIconBefore: { + spacingIconContentBefore: globalTokens.size60, + }, + focused: { + paddingHorizontal: globalTokens.size120, + }, + }, + }, + small: { + padding: globalTokens.size40 - globalTokens.stroke.width10, + borderWidth: globalTokens.stroke.width10, + iconSize: 16, + focused: { + borderWidth: 0, + padding: globalTokens.size40, + }, + hasContent: { + minWidth: 64, + minHeight: 24, + paddingHorizontal: globalTokens.size80 - globalTokens.stroke.width10, + hasIconAfter: { + spacingIconContentAfter: globalTokens.size40, + }, + hasIconBefore: { + spacingIconContentBefore: globalTokens.size40, + }, + focused: { + paddingHorizontal: globalTokens.size80, + }, + }, + }, + large: { + padding: globalTokens.size80 - globalTokens.stroke.width10, + borderWidth: globalTokens.stroke.width10, + iconSize: 20, + focused: { + borderWidth: 0, + padding: globalTokens.size80, + }, + hasContent: { + minWidth: 96, + paddingHorizontal: globalTokens.size160 - globalTokens.stroke.width10, + hasIconAfter: { + spacingIconContentAfter: globalTokens.size60, + }, + hasIconBefore: { + spacingIconContentBefore: globalTokens.size60, + }, + focused: { + paddingHorizontal: globalTokens.size160, + }, + }, + }, + rounded: { + borderRadius: globalTokens.corner.radius40, + }, + circular: { + borderRadius: globalTokens.corner.radiusCircular, + }, + square: { + borderRadius: globalTokens.corner.radiusNone, + }, + + getPlatformSpecificAppearance: (appearance): 'primary' | 'subtle' | 'outline' | null => { + switch (appearance) { + case 'accent': // Included to cover Mobile platform naming guidelines, maps to 'primary'. + return 'primary'; + + case 'primary': + case 'subtle': + case 'outline': // 'Outline' exists only for Mobile platforms, default picked on other platforms. + return appearance; + + default: + return null; + } + }, +}); diff --git a/packages/theming/default-theme/src/components/Button/CompoundButtonColorTokens.ts b/packages/theming/default-theme/src/components/Button/CompoundButtonColorTokens.ts new file mode 100644 index 0000000000..c87c62bc25 --- /dev/null +++ b/packages/theming/default-theme/src/components/Button/CompoundButtonColorTokens.ts @@ -0,0 +1,38 @@ +import type { Theme } from '@fluentui-react-native/theme-types'; + +export const defaultCompoundButtonColorTokens = (t: Theme) => ({ + secondaryContentColor: t.colors.defaultSecondaryContent, + hovered: { + secondaryContentColor: t.colors.defaultHoveredSecondaryContent, + }, + focused: { + secondaryContentColor: t.colors.defaultFocusedSecondaryContent, + }, + pressed: { + secondaryContentColor: t.colors.defaultPressedSecondaryContent, + }, + primary: { + secondaryContentColor: t.colors.brandedSecondaryContent, + hovered: { + secondaryContentColor: t.colors.brandedHoveredSecondaryContent, + }, + focused: { + secondaryContentColor: t.colors.brandedFocusedSecondaryContent, + }, + pressed: { + secondaryContentColor: t.colors.brandedPressedSecondaryContent, + }, + }, + subtle: { + secondaryContentColor: t.colors.ghostSecondaryContent, + hovered: { + secondaryContentColor: t.colors.ghostHoveredSecondaryContent, + }, + focused: { + secondaryContentColor: t.colors.ghostFocusedSecondaryContent, + }, + pressed: { + secondaryContentColor: t.colors.ghostPressedSecondaryContent, + }, + }, +}); diff --git a/packages/components/Button/src/CompoundButton/CompoundButtonColorTokens.windows.ts b/packages/theming/default-theme/src/components/Button/CompoundButtonColorTokens.windows.ts similarity index 84% rename from packages/components/Button/src/CompoundButton/CompoundButtonColorTokens.windows.ts rename to packages/theming/default-theme/src/components/Button/CompoundButtonColorTokens.windows.ts index 9e9b64bde6..c502a8474b 100644 --- a/packages/components/Button/src/CompoundButton/CompoundButtonColorTokens.windows.ts +++ b/packages/theming/default-theme/src/components/Button/CompoundButtonColorTokens.windows.ts @@ -1,12 +1,9 @@ import { PlatformColor } from 'react-native'; -import type { Theme } from '@fluentui-react-native/framework'; +import type { Theme } from '@fluentui-react-native/theme-types'; import { isHighContrast } from '@fluentui-react-native/theming-utils'; -import type { TokenSettings } from '@fluentui-react-native/use-styling'; -import type { CompoundButtonTokens } from './CompoundButton.types'; - -export const defaultCompoundButtonColorTokens: TokenSettings = (t: Theme): CompoundButtonTokens => { +export const defaultCompoundButtonColorTokens = (t: Theme) => { if (isHighContrast(t)) { return highContrastColors; } diff --git a/packages/theming/default-theme/src/components/Button/CompoundButtonFontTokens.ts b/packages/theming/default-theme/src/components/Button/CompoundButtonFontTokens.ts new file mode 100644 index 0000000000..eeacb7e2d1 --- /dev/null +++ b/packages/theming/default-theme/src/components/Button/CompoundButtonFontTokens.ts @@ -0,0 +1,24 @@ +import type { Theme } from '@fluentui-react-native/theme-types'; + +type fontVariantType = 'secondaryStandard' | 'bodyStandard'; + +export const defaultCompoundButtonFontTokens = (_t: Theme) => ({ + medium: { + hasContent: { + variant: 'bodySemibold' as fontVariantType, + secondaryContentFont: { variant: 'secondaryStandard' as fontVariantType }, + }, + }, + small: { + hasContent: { + variant: 'bodyStandard' as fontVariantType, + secondaryContentFont: { variant: 'secondaryStandard' as fontVariantType }, + }, + }, + large: { + hasContent: { + variant: 'subheaderSemibold' as fontVariantType, + secondaryContentFont: { variant: 'bodyStandard' as fontVariantType }, + }, + }, +}); diff --git a/packages/theming/default-theme/src/components/Button/CompoundButtonTokens.ts b/packages/theming/default-theme/src/components/Button/CompoundButtonTokens.ts new file mode 100644 index 0000000000..04400e847d --- /dev/null +++ b/packages/theming/default-theme/src/components/Button/CompoundButtonTokens.ts @@ -0,0 +1,80 @@ +import { globalTokens } from '@fluentui-react-native/theme-tokens'; +import type { Theme } from '@fluentui-react-native/theme-types'; + +export const defaultCompoundButtonTokens = (_t: Theme) => ({ + medium: { + padding: globalTokens.size120 - globalTokens.stroke.width10, + focused: { + padding: globalTokens.size120, + }, + hasContent: { + paddingHorizontal: globalTokens.size120 - globalTokens.stroke.width10, + minWidth: 96, + focused: { + paddingHorizontal: globalTokens.size120, + }, + hasIconAfter: { + spacingIconContentAfter: globalTokens.size120, + }, + hasIconBefore: { + spacingIconContentBefore: globalTokens.size120, + }, + circular: { + paddingHorizontal: globalTokens.size160 - globalTokens.stroke.width10, + focused: { + paddingHorizontal: globalTokens.size160, + }, + }, + }, + }, + small: { + padding: globalTokens.size80 - globalTokens.stroke.width10, + focused: { + padding: globalTokens.size80, + }, + hasContent: { + paddingHorizontal: globalTokens.size80 - globalTokens.stroke.width10, + minWidth: 64, + focused: { + paddingHorizontal: globalTokens.size80, + }, + hasIconAfter: { + spacingIconContentAfter: globalTokens.size80, + }, + hasIconBefore: { + spacingIconContentBefore: globalTokens.size80, + }, + circular: { + paddingHorizontal: globalTokens.size120 - globalTokens.stroke.width10, + focused: { + paddingHorizontal: globalTokens.size120, + }, + }, + }, + }, + large: { + padding: globalTokens.size160 - globalTokens.stroke.width10, + focused: { + padding: globalTokens.size160, + }, + hasContent: { + paddingHorizontal: globalTokens.size160 - globalTokens.stroke.width10, + minWidth: 96, + focused: { + paddingHorizontal: globalTokens.size160, + }, + hasIconAfter: { + spacingIconContentAfter: globalTokens.size160, + }, + hasIconBefore: { + spacingIconContentBefore: globalTokens.size160, + }, + circular: { + paddingHorizontal: globalTokens.size200 - globalTokens.stroke.width10, + focused: { + paddingHorizontal: globalTokens.size200, + }, + }, + }, + }, +}); diff --git a/packages/theming/default-theme/src/components/Button/ToggleButtonColorTokens.ts b/packages/theming/default-theme/src/components/Button/ToggleButtonColorTokens.ts new file mode 100644 index 0000000000..8822ac0a4c --- /dev/null +++ b/packages/theming/default-theme/src/components/Button/ToggleButtonColorTokens.ts @@ -0,0 +1,21 @@ +import type { Theme } from '@fluentui-react-native/theme-types'; + +export const defaultToggleButtonColorTokens = (t: Theme) => ({ + checked: { + color: t.colors.defaultCheckedContent, + backgroundColor: t.colors.defaultCheckedBackground, + hovered: { + color: t.colors.defaultCheckedHoveredContent, + backgroundColor: t.colors.defaultCheckedHoveredBackground, + }, + subtle: { + color: t.colors.ghostCheckedContent, + backgroundColor: t.colors.ghostCheckedBackground, + hovered: { + color: t.colors.ghostCheckedHoveredContent, + backgroundColor: t.colors.ghostCheckedHoveredBackground, + borderColor: t.colors.ghostCheckedHoveredBorder, + }, + }, + }, +}); diff --git a/packages/components/Button/src/ToggleButton/ToggleButtonColorTokens.windows.ts b/packages/theming/default-theme/src/components/Button/ToggleButtonColorTokens.windows.ts similarity index 76% rename from packages/components/Button/src/ToggleButton/ToggleButtonColorTokens.windows.ts rename to packages/theming/default-theme/src/components/Button/ToggleButtonColorTokens.windows.ts index cfcc4338fc..37d2996d02 100644 --- a/packages/components/Button/src/ToggleButton/ToggleButtonColorTokens.windows.ts +++ b/packages/theming/default-theme/src/components/Button/ToggleButtonColorTokens.windows.ts @@ -1,12 +1,9 @@ import { PlatformColor } from 'react-native'; -import type { Theme } from '@fluentui-react-native/framework'; +import type { Theme } from '@fluentui-react-native/theme-types'; import { isHighContrast } from '@fluentui-react-native/theming-utils'; -import type { TokenSettings } from '@fluentui-react-native/use-styling'; -import type { ToggleButtonTokens } from './ToggleButton.types'; - -export const defaultToggleButtonColorTokens: TokenSettings = (t: Theme): ToggleButtonTokens => { +export const defaultToggleButtonColorTokens = (t: Theme) => { if (isHighContrast(t)) { return highContrastColors; } diff --git a/packages/theming/default-theme/src/createDefaultTheme.ts b/packages/theming/default-theme/src/createDefaultTheme.ts index ef5034d39f..7feae70bf6 100644 --- a/packages/theming/default-theme/src/createDefaultTheme.ts +++ b/packages/theming/default-theme/src/createDefaultTheme.ts @@ -1,28 +1,33 @@ import { Appearance } from 'react-native'; import { ThemeReference } from '@fluentui-react-native/theme'; -import type { Theme, ThemeOptions } from '@fluentui-react-native/theme-types'; +import type { PartialTheme, Theme, ThemeOptions } from '@fluentui-react-native/theme-types'; import { getCurrentAppearance } from '@fluentui-react-native/theming-utils'; import assertNever from 'assert-never'; +import { defaultButtonTheme } from './components/Button/ButtonTheme'; import { defaultFluentDarkTheme, defaultFluentHighConstrastTheme, defaultFluentTheme } from './defaultTheme'; export function createDefaultTheme(options: ThemeOptions = {}): ThemeReference { - const themeRef = new ThemeReference({} as Theme, () => { - const current = getCurrentAppearance(options.appearance, options.defaultAppearance || 'light'); - switch (current) { - case 'light': - return defaultFluentTheme; - case 'dark': - return defaultFluentDarkTheme; - case 'darkElevated': - return defaultFluentDarkTheme; - case 'highContrast': - return defaultFluentHighConstrastTheme; - default: - assertNever(current); - } - }); + const themeRef = new ThemeReference( + {} as Theme, + () => { + const current = getCurrentAppearance(options.appearance, options.defaultAppearance || 'light'); + switch (current) { + case 'light': + return defaultFluentTheme; + case 'dark': + return defaultFluentDarkTheme; + case 'darkElevated': + return defaultFluentDarkTheme; + case 'highContrast': + return defaultFluentHighConstrastTheme; + default: + assertNever(current); + } + }, + defaultButtonTheme, + ); if (Appearance && options.appearance === 'dynamic') { Appearance.addChangeListener(() => { diff --git a/packages/theming/theme-tokens/src/index.ts b/packages/theming/theme-tokens/src/index.ts index 2832ce3c03..89d2ae8439 100644 --- a/packages/theming/theme-tokens/src/index.ts +++ b/packages/theming/theme-tokens/src/index.ts @@ -1,2 +1,5 @@ export { default as globalTokens } from './tokens-global'; export { getAliasTokens, getShadowTokens } from './getTokens'; +export { default as globalTokensWin32 } from './tokens-global.win32'; +export { default as globalTokensIOS } from './tokens-global.ios'; +export { default as globalTokensAndroid } from './tokens-global.android'; diff --git a/packages/theming/win32-theme/package.json b/packages/theming/win32-theme/package.json index 0a9d19e35c..e518439cd8 100644 --- a/packages/theming/win32-theme/package.json +++ b/packages/theming/win32-theme/package.json @@ -33,6 +33,7 @@ "dependencies": { "@fluentui-react-native/default-theme": "workspace:*", "@fluentui-react-native/design-tokens-win32": "^0.53.0", + "@fluentui-react-native/immutable-merge": "workspace:*", "@fluentui-react-native/memo-cache": "workspace:*", "@fluentui-react-native/theme": "workspace:*", "@fluentui-react-native/theme-tokens": "workspace:*", @@ -41,8 +42,10 @@ "tslib": "^2.3.1" }, "devDependencies": { + "@fluentui-react-native/button": "workspace:*", "@fluentui-react-native/eslint-config-rules": "workspace:*", "@fluentui-react-native/scripts": "workspace:*", + "@fluentui-react-native/use-styling": "workspace:*", "@office-iss/react-native-win32": "^0.73.0", "@react-native/babel-preset": "^0.73.0", "@react-native/metro-config": "^0.73.0", diff --git a/packages/theming/win32-theme/src/__tests__/__snapshots__/win32-theme.test.ts.snap b/packages/theming/win32-theme/src/__tests__/__snapshots__/win32-theme.test.ts.snap index be86785d83..6830c3c81f 100644 --- a/packages/theming/win32-theme/src/__tests__/__snapshots__/win32-theme.test.ts.snap +++ b/packages/theming/win32-theme/src/__tests__/__snapshots__/win32-theme.test.ts.snap @@ -2714,6 +2714,265 @@ exports[`createOfficeTheme test 1`] = ` "warningForeground3": "#fde300", "warningForegroundInverted": "#fef7b2", }, + "components": { + "Button": { + "backgroundColor": "antiquewhite", + "block": { + "width": "100%", + }, + "borderColor": "#969696", + "borderInnerWidth": 1, + "borderWidth": 1, + "circular": { + "borderInnerRadius": 9998, + "borderRadius": 9999, + }, + "color": "#262626", + "disabled": { + "backgroundColor": "#E6E6E6", + "borderColor": "#D2D2D2", + "color": "#B1B1B1", + "iconColor": "#B1B1B1", + }, + "focused": { + "backgroundColor": "#D2D2D2", + "borderColor": "#00000000", + "color": "#262626", + "iconColor": "#262626", + }, + "hovered": { + "backgroundColor": "#D2D2D2", + "borderColor": "#969696", + "color": "#262626", + "iconColor": "#262626", + }, + "iconColor": "#262626", + "large": { + "focused": { + "borderWidth": 0, + "padding": 10, + }, + "hasContent": { + "focused": { + "padding": 8, + "paddingHorizontal": 16, + }, + "fontFamily": "Segoe UI Semibold", + "fontSize": 16, + "fontWeight": "600", + "hasIconAfter": { + "spacingIconContentAfter": 6, + }, + "hasIconBefore": { + "spacingIconContentBefore": 6, + }, + "minHeight": 40, + "minWidth": 96, + "padding": 7, + "paddingHorizontal": 15, + "primary": { + "focused": { + "padding": 6, + "paddingHorizontal": 14, + }, + "square": { + "focused": { + "padding": 7, + "paddingHorizontal": 15, + }, + }, + }, + }, + "iconSize": 20, + "padding": 9, + "primary": { + "focused": { + "borderWidth": 2, + "padding": 8, + }, + "square": { + "focused": { + "borderWidth": 1, + "padding": 9, + }, + }, + }, + }, + "medium": { + "focused": { + "borderWidth": 0, + "padding": 8, + }, + "hasContent": { + "focused": { + "padding": 6, + "paddingHorizontal": 12, + }, + "fontFamily": "Segoe UI Semibold", + "fontSize": 14, + "fontWeight": "600", + "hasIconAfter": { + "spacingIconContentAfter": 8, + }, + "hasIconBefore": { + "spacingIconContentBefore": 8, + }, + "minWidth": 96, + "padding": 5, + "paddingHorizontal": 11, + "primary": { + "focused": { + "padding": 4, + "paddingHorizontal": 10, + }, + "square": { + "focused": { + "padding": 5, + "paddingHorizontal": 11, + }, + }, + }, + }, + "iconSize": 16, + "padding": 7, + "primary": { + "focused": { + "borderWidth": 2, + "padding": 6, + }, + "square": { + "focused": { + "borderWidth": 1, + "padding": 7, + }, + }, + }, + }, + "pressed": { + "backgroundColor": "#B1B1B1", + "borderColor": "#969696", + "color": "#262626", + "iconColor": "#262626", + }, + "primary": { + "backgroundColor": "#D83B01", + "borderColor": "#D83B01", + "color": "#FFFFFF", + "disabled": { + "backgroundColor": "#E6E6E6", + "borderColor": "#D2D2D2", + "color": "#B1B1B1", + "iconColor": "#B1B1B1", + }, + "focused": { + "backgroundColor": "#F29F71", + "borderColor": "#969696", + "borderInnerColor": "#ffffff", + "color": "#FFFFFF", + "iconColor": "#FFFFFF", + }, + "hovered": { + "backgroundColor": "#F29F71", + "borderColor": "#F29F71", + "color": "#FFFFFF", + "iconColor": "#FFFFFF", + }, + "iconColor": "#FFFFFF", + "pressed": { + "backgroundColor": "#A22C01", + "borderColor": "#A22C01", + "color": "#FFFFFF", + "iconColor": "#FFFFFF", + }, + }, + "rounded": { + "borderInnerRadius": 3, + "borderRadius": 4, + }, + "size": "small", + "small": { + "focused": { + "borderWidth": 0, + "padding": 4, + }, + "hasContent": { + "focused": { + "paddingHorizontal": 8, + }, + "fontFamily": "Segoe UI", + "fontSize": 12, + "fontWeight": "400", + "hasIconAfter": { + "spacingIconContentAfter": 4, + }, + "hasIconBefore": { + "spacingIconContentBefore": 4, + }, + "minHeight": 24, + "minWidth": 64, + "paddingHorizontal": 7, + "primary": { + "focused": { + "paddingHorizontal": 6, + }, + "square": { + "focused": { + "paddingHorizontal": 7, + }, + }, + }, + }, + "iconSize": 16, + "padding": 3, + "primary": { + "focused": { + "borderWidth": 2, + "padding": 2, + }, + "square": { + "focused": { + "borderWidth": 1, + "padding": 3, + }, + }, + }, + }, + "square": { + "borderInnerRadius": 0, + "borderRadius": 0, + }, + "subtle": { + "backgroundColor": "#00000000", + "borderColor": "#00000000", + "color": "#262626", + "disabled": { + "backgroundColor": "#00000000", + "borderColor": "#00000000", + "color": "#B1B1B1", + "iconColor": "#B1B1B1", + }, + "focused": { + "backgroundColor": "#f5f5f5", + "borderColor": "#00000000", + "color": "#262626", + "iconColor": "#262626", + }, + "hovered": { + "backgroundColor": "#f5f5f5", + "borderColor": "#f5f5f5", + "color": "#262626", + "iconColor": "#262626", + }, + "iconColor": "#262626", + "pressed": { + "backgroundColor": "#e0e0e0", + "borderColor": "#e0e0e0", + "color": "#262626", + "iconColor": "#262626", + }, + }, + }, + }, "host": { "appearance": "light", "colors": { diff --git a/packages/theming/win32-theme/src/__tests__/win32-theme.test.ts b/packages/theming/win32-theme/src/__tests__/win32-theme.test.ts index 1f4dc84c4e..449520c0f5 100644 --- a/packages/theming/win32-theme/src/__tests__/win32-theme.test.ts +++ b/packages/theming/win32-theme/src/__tests__/win32-theme.test.ts @@ -1,3 +1,11 @@ +import type { ButtonTokens, CompoundButtonTokens } from '@fluentui-react-native/button'; + +import { defaultButtonColorTokens } from '../components/Button/ButtonColorTokens'; +import { defaultButtonFontTokens } from '../components/Button/ButtonFontTokens'; +import { defaultButtonTokens } from '../components/Button/ButtonTokens'; +import { defaultCompoundButtonColorTokens } from '../components/Button/CompoundButtonColorTokens'; +import { defaultCompoundButtonFontTokens } from '../components/Button/CompoundButtonFontTokens'; +import { defaultCompoundButtonTokens } from '../components/Button/CompoundButtonTokens'; import { createBrandedThemeWithAlias, getCurrentBrandAliasTokens } from '../createBrandedThemeWithAlias'; import { createFontAliasTokens } from '../createFontAliasTokens'; import { createOfficeColorAliasTokens, createOfficeShadowAliasTokens } from '../createOfficeAliasTokens'; @@ -91,3 +99,25 @@ describe('getCurrentBrandAliasTokens test', () => { expect(brandAliasTokens).toMatchSnapshot(); }); }); + +describe('verify types', () => { + it('Button types', () => { + const officeTheme = createOfficeTheme({ paletteName: 'TaskPane', appearance: 'light' }).theme; + const colorTokens: ButtonTokens = defaultButtonColorTokens(officeTheme); + expect(colorTokens).toBeTruthy(); + const fontTokens: ButtonTokens = defaultButtonFontTokens(officeTheme); + expect(fontTokens).toBeTruthy(); + const tokens: ButtonTokens = defaultButtonTokens(officeTheme); + expect(tokens).toBeTruthy(); + }); + + it('CompoundButton types', () => { + const officeTheme = createOfficeTheme({ paletteName: 'TaskPane', appearance: 'light' }).theme; + const compoundColorTokens: CompoundButtonTokens = defaultCompoundButtonColorTokens(officeTheme); + expect(compoundColorTokens).toBeTruthy(); + const compoundButtonFontTokens: CompoundButtonTokens = defaultCompoundButtonFontTokens(officeTheme); + expect(compoundButtonFontTokens).toBeTruthy(); + const compoundButtonTokens: CompoundButtonTokens = defaultCompoundButtonTokens(officeTheme); + expect(compoundButtonTokens).toBeTruthy(); + }); +}); diff --git a/packages/components/Button/src/ButtonColorTokens.win32.ts b/packages/theming/win32-theme/src/components/Button/ButtonColorTokens.ts similarity index 93% rename from packages/components/Button/src/ButtonColorTokens.win32.ts rename to packages/theming/win32-theme/src/components/Button/ButtonColorTokens.ts index 2a40e43d04..9df2cfda7e 100644 --- a/packages/components/Button/src/ButtonColorTokens.win32.ts +++ b/packages/theming/win32-theme/src/components/Button/ButtonColorTokens.ts @@ -1,12 +1,9 @@ import { PlatformColor } from 'react-native'; -import type { Theme } from '@fluentui-react-native/framework'; +import type { Theme } from '@fluentui-react-native/theme-types'; import { isHighContrast } from '@fluentui-react-native/theming-utils'; -import type { TokenSettings } from '@fluentui-react-native/use-styling'; -import type { ButtonTokens } from './Button.types'; - -export const defaultButtonColorTokens: TokenSettings = (t: Theme) => { +export const defaultButtonColorTokens = (t: Theme) => { if (isHighContrast(t)) { return highContrastColors; } @@ -101,7 +98,7 @@ export const defaultButtonColorTokens: TokenSettings = (t: iconColor: t.colors.neutralForeground1Hover, }, }, - } as ButtonTokens; + }; }; const highContrastColors = { diff --git a/packages/theming/win32-theme/src/components/Button/ButtonFontTokens.ts b/packages/theming/win32-theme/src/components/Button/ButtonFontTokens.ts new file mode 100644 index 0000000000..fbf6153875 --- /dev/null +++ b/packages/theming/win32-theme/src/components/Button/ButtonFontTokens.ts @@ -0,0 +1,27 @@ +import { globalTokensWin32 as globalTokens } from '@fluentui-react-native/theme-tokens'; +import type { Theme } from '@fluentui-react-native/theme-types'; +import type { FontWeight } from '@fluentui-react-native/theme-types'; + +export const defaultButtonFontTokens = (t: Theme) => ({ + medium: { + hasContent: { + fontFamily: t.typography.families.secondary, + fontSize: globalTokens.font.size300, + fontWeight: globalTokens.font.weight.semibold as FontWeight, + }, + }, + small: { + hasContent: { + fontFamily: t.typography.families.primary, + fontSize: globalTokens.font.size200, + fontWeight: globalTokens.font.weight.regular as FontWeight, + }, + }, + large: { + hasContent: { + fontFamily: t.typography.families.secondary, + fontSize: globalTokens.font.size400, + fontWeight: globalTokens.font.weight.semibold as FontWeight, + }, + }, +}); diff --git a/packages/theming/win32-theme/src/components/Button/ButtonTheme.ts b/packages/theming/win32-theme/src/components/Button/ButtonTheme.ts new file mode 100644 index 0000000000..45835b5277 --- /dev/null +++ b/packages/theming/win32-theme/src/components/Button/ButtonTheme.ts @@ -0,0 +1,29 @@ +import { immutableMerge } from '@fluentui-react-native/immutable-merge'; +import type { Theme } from '@fluentui-react-native/theme-types'; + +import { defaultButtonColorTokens } from './ButtonColorTokens'; +import { defaultButtonFontTokens } from './ButtonFontTokens'; +import { defaultButtonTokens } from './ButtonTokens'; +import { defaultCompoundButtonColorTokens } from './CompoundButtonColorTokens'; +import { defaultCompoundButtonFontTokens } from './CompoundButtonFontTokens'; +import { defaultCompoundButtonTokens } from './CompoundButtonTokens'; +import { defaultToggleButtonColorTokens } from './ToggleButtonColorTokens'; + +export const defaultButtonTheme = (theme: Theme) => ({ + components: { + Button: immutableMerge(defaultButtonColorTokens(theme), defaultButtonFontTokens(theme), defaultButtonTokens(theme)), + CompoundButton: immutableMerge( + defaultButtonTokens(theme), + defaultButtonColorTokens(theme), + defaultCompoundButtonColorTokens(theme), + defaultCompoundButtonFontTokens(theme), + defaultCompoundButtonTokens(theme), + ), + ToggleButton: immutableMerge( + defaultButtonTokens(theme), + defaultButtonFontTokens(theme), + defaultButtonColorTokens(theme), + defaultToggleButtonColorTokens(theme), + ), + }, +}); diff --git a/packages/theming/win32-theme/src/components/Button/ButtonTokens.ts b/packages/theming/win32-theme/src/components/Button/ButtonTokens.ts new file mode 100644 index 0000000000..1bb9bb415c --- /dev/null +++ b/packages/theming/win32-theme/src/components/Button/ButtonTokens.ts @@ -0,0 +1,182 @@ +import type { DimensionValue } from 'react-native'; + +import { globalTokensWin32 as globalTokens } from '@fluentui-react-native/theme-tokens'; +import type { Theme } from '@fluentui-react-native/theme-types'; +import { isHighContrast } from '@fluentui-react-native/theming-utils'; + +type ButtonSize = 'small'; + +export const defaultButtonTokens = (theme: Theme) => ({ + size: 'small' as ButtonSize, + borderWidth: globalTokens.stroke.width10, + borderInnerWidth: globalTokens.stroke.width10, + block: { + width: '100%' as DimensionValue, + }, + medium: { + padding: globalTokens.size80 - globalTokens.stroke.width10, + iconSize: 16, + focused: { + borderWidth: 0, + padding: globalTokens.size80, + }, + primary: !isHighContrast(theme) && { + focused: { + borderWidth: globalTokens.stroke.width20, + padding: globalTokens.size80 - globalTokens.stroke.width20, + }, + square: { + focused: { + borderWidth: globalTokens.stroke.width10, + padding: globalTokens.size80 - globalTokens.stroke.width10, + }, + }, + }, + hasContent: { + minWidth: 96, + padding: globalTokens.size60 - globalTokens.stroke.width10, + paddingHorizontal: globalTokens.size120 - globalTokens.stroke.width10, + hasIconAfter: { + spacingIconContentAfter: globalTokens.size80, + }, + hasIconBefore: { + spacingIconContentBefore: globalTokens.size80, + }, + focused: { + padding: globalTokens.size60, + paddingHorizontal: globalTokens.size120, + }, + primary: !isHighContrast(theme) && { + focused: { + padding: globalTokens.size60 - globalTokens.stroke.width20, + paddingHorizontal: globalTokens.size120 - globalTokens.stroke.width20, + }, + square: { + focused: { + padding: globalTokens.size60 - globalTokens.stroke.width10, + paddingHorizontal: globalTokens.size120 - globalTokens.stroke.width10, + }, + }, + }, + }, + }, + small: { + padding: globalTokens.size40 - globalTokens.stroke.width10, + iconSize: 16, + focused: { + borderWidth: 0, + padding: globalTokens.size40, + }, + primary: !isHighContrast(theme) && { + focused: { + borderWidth: globalTokens.stroke.width20, + padding: globalTokens.size40 - globalTokens.stroke.width20, + }, + square: { + focused: { + borderWidth: globalTokens.stroke.width10, + padding: globalTokens.size40 - globalTokens.stroke.width10, + }, + }, + }, + hasContent: { + minWidth: 64, + minHeight: 24, + paddingHorizontal: globalTokens.size80 - globalTokens.stroke.width10, + hasIconAfter: { + spacingIconContentAfter: globalTokens.size40, + }, + hasIconBefore: { + spacingIconContentBefore: globalTokens.size40, + }, + focused: { + paddingHorizontal: globalTokens.size80, + }, + primary: !isHighContrast(theme) && { + focused: { + paddingHorizontal: globalTokens.size80 - globalTokens.stroke.width20, + }, + square: { + focused: { + paddingHorizontal: globalTokens.size80 - globalTokens.stroke.width10, + }, + }, + }, + }, + }, + large: { + padding: globalTokens.size100 - globalTokens.stroke.width10, + iconSize: 20, + focused: { + borderWidth: 0, + padding: globalTokens.size100, + }, + primary: !isHighContrast(theme) && { + focused: { + borderWidth: globalTokens.stroke.width20, + padding: globalTokens.size100 - globalTokens.stroke.width20, + }, + square: { + focused: { + borderWidth: globalTokens.stroke.width10, + padding: globalTokens.size100 - globalTokens.stroke.width10, + }, + }, + }, + hasContent: { + minWidth: 96, + minHeight: 40, + padding: globalTokens.size80 - globalTokens.stroke.width10, + paddingHorizontal: globalTokens.size160 - globalTokens.stroke.width10, + hasIconAfter: { + spacingIconContentAfter: globalTokens.size60, + }, + hasIconBefore: { + spacingIconContentBefore: globalTokens.size60, + }, + focused: { + padding: globalTokens.size80, + paddingHorizontal: globalTokens.size160, + }, + primary: !isHighContrast(theme) && { + focused: { + padding: globalTokens.size80 - globalTokens.stroke.width20, + paddingHorizontal: globalTokens.size160 - globalTokens.stroke.width20, + }, + square: { + focused: { + padding: globalTokens.size80 - globalTokens.stroke.width10, + paddingHorizontal: globalTokens.size160 - globalTokens.stroke.width10, + }, + }, + }, + }, + }, + rounded: { + borderRadius: globalTokens.corner.radius40, + borderInnerRadius: globalTokens.corner.radius40 - 1, // reduce the rounding so that the curvature matches + }, + circular: { + borderRadius: globalTokens.corner.radiusCircular, + borderInnerRadius: globalTokens.corner.radiusCircular - 1, // reduce the rounding so that the curvature matches + }, + square: { + borderRadius: globalTokens.corner.radiusNone, + borderInnerRadius: globalTokens.corner.radiusNone, + }, + + getPlatformSpecificAppearance: (appearance): 'primary' | 'subtle' | 'outline' | null => { + switch (appearance) { + case 'accent': // Included to cover Mobile platform naming guidelines, maps to 'primary'. + return 'primary'; + + case 'primary': + case 'subtle': + case 'outline': // 'Outline' exists only for Mobile platforms, default picked on other platforms. + return appearance; + + default: + return null; + } + }, +}); diff --git a/packages/components/Button/src/CompoundButton/CompoundButtonColorTokens.win32.ts b/packages/theming/win32-theme/src/components/Button/CompoundButtonColorTokens.ts similarity index 83% rename from packages/components/Button/src/CompoundButton/CompoundButtonColorTokens.win32.ts rename to packages/theming/win32-theme/src/components/Button/CompoundButtonColorTokens.ts index 2c7e801814..98b0463337 100644 --- a/packages/components/Button/src/CompoundButton/CompoundButtonColorTokens.win32.ts +++ b/packages/theming/win32-theme/src/components/Button/CompoundButtonColorTokens.ts @@ -1,12 +1,9 @@ import { PlatformColor } from 'react-native'; -import type { Theme } from '@fluentui-react-native/framework'; +import type { Theme } from '@fluentui-react-native/theme-types'; import { isHighContrast } from '@fluentui-react-native/theming-utils'; -import type { TokenSettings } from '@fluentui-react-native/use-styling'; -import type { CompoundButtonTokens } from './CompoundButton.types'; - -export const defaultCompoundButtonColorTokens: TokenSettings = (t: Theme): CompoundButtonTokens => { +export const defaultCompoundButtonColorTokens = (t: Theme) => { if (isHighContrast(t)) { return highContrastColors; } diff --git a/packages/components/Button/src/CompoundButton/CompoundButtonFontTokens.win32.ts b/packages/theming/win32-theme/src/components/Button/CompoundButtonFontTokens.ts similarity index 78% rename from packages/components/Button/src/CompoundButton/CompoundButtonFontTokens.win32.ts rename to packages/theming/win32-theme/src/components/Button/CompoundButtonFontTokens.ts index e4785d73c0..c67b0a28cb 100644 --- a/packages/components/Button/src/CompoundButton/CompoundButtonFontTokens.win32.ts +++ b/packages/theming/win32-theme/src/components/Button/CompoundButtonFontTokens.ts @@ -1,10 +1,7 @@ -import type { FontWeightValue, Theme } from '@fluentui-react-native/framework'; -import { globalTokens } from '@fluentui-react-native/theme-tokens'; -import type { TokenSettings } from '@fluentui-react-native/use-styling'; +import { globalTokensWin32 as globalTokens } from '@fluentui-react-native/theme-tokens'; +import type { FontWeightValue, Theme } from '@fluentui-react-native/theme-types'; -import type { CompoundButtonTokens } from './CompoundButton.types'; - -export const defaultCompoundButtonFontTokens: TokenSettings = (t: Theme): CompoundButtonTokens => ({ +export const defaultCompoundButtonFontTokens = (t: Theme) => ({ medium: { hasContent: { fontFamily: t.typography.families.secondary, diff --git a/packages/components/Button/src/CompoundButton/CompoundButtonTokens.win32.ts b/packages/theming/win32-theme/src/components/Button/CompoundButtonTokens.ts similarity index 92% rename from packages/components/Button/src/CompoundButton/CompoundButtonTokens.win32.ts rename to packages/theming/win32-theme/src/components/Button/CompoundButtonTokens.ts index d474eba9bb..9d42dbbf59 100644 --- a/packages/components/Button/src/CompoundButton/CompoundButtonTokens.win32.ts +++ b/packages/theming/win32-theme/src/components/Button/CompoundButtonTokens.ts @@ -1,11 +1,8 @@ -import type { Theme } from '@fluentui-react-native/framework'; -import { globalTokens } from '@fluentui-react-native/theme-tokens'; +import { globalTokensWin32 as globalTokens } from '@fluentui-react-native/theme-tokens'; +import type { Theme } from '@fluentui-react-native/theme-types'; import { isHighContrast } from '@fluentui-react-native/theming-utils'; -import type { TokenSettings } from '@fluentui-react-native/use-styling'; -import type { CompoundButtonTokens } from './CompoundButton.types'; - -export const defaultCompoundButtonTokens: TokenSettings = (theme: Theme): CompoundButtonTokens => ({ +export const defaultCompoundButtonTokens = (theme: Theme) => ({ medium: { padding: globalTokens.size120 - globalTokens.stroke.width10, focused: { diff --git a/packages/components/Button/src/ToggleButton/ToggleButtonColorTokens.win32.ts b/packages/theming/win32-theme/src/components/Button/ToggleButtonColorTokens.ts similarity index 74% rename from packages/components/Button/src/ToggleButton/ToggleButtonColorTokens.win32.ts rename to packages/theming/win32-theme/src/components/Button/ToggleButtonColorTokens.ts index e2bee988a4..e4ff217377 100644 --- a/packages/components/Button/src/ToggleButton/ToggleButtonColorTokens.win32.ts +++ b/packages/theming/win32-theme/src/components/Button/ToggleButtonColorTokens.ts @@ -1,12 +1,9 @@ import { PlatformColor } from 'react-native'; -import type { Theme } from '@fluentui-react-native/framework'; +import type { Theme } from '@fluentui-react-native/theme-types'; import { isHighContrast } from '@fluentui-react-native/theming-utils'; -import type { TokenSettings } from '@fluentui-react-native/use-styling'; -import type { ToggleButtonTokens } from './ToggleButton.types'; - -export const defaultToggleButtonColorTokens: TokenSettings = (t: Theme): ToggleButtonTokens => { +export const defaultToggleButtonColorTokens = (t: Theme) => { if (isHighContrast(t)) { return highContrastColors; } diff --git a/packages/theming/win32-theme/src/createOfficeTheme.ts b/packages/theming/win32-theme/src/createOfficeTheme.ts index 46c903acae..52e8489d73 100644 --- a/packages/theming/win32-theme/src/createOfficeTheme.ts +++ b/packages/theming/win32-theme/src/createOfficeTheme.ts @@ -2,6 +2,7 @@ import { createDefaultTheme } from '@fluentui-react-native/default-theme'; import { ThemeReference } from '@fluentui-react-native/theme'; import type { OfficePalette, Theme, ThemeOptions } from '@fluentui-react-native/theme-types'; +import { defaultButtonTheme } from './components/Button/ButtonTheme'; import { createAliasesFromPalette } from './createAliasesFromPalette'; import { createBrandedThemeWithAlias } from './createBrandedThemeWithAlias'; import { createOfficeColorAliasTokens, createOfficeShadowAliasTokens } from './createOfficeAliasTokens'; @@ -70,6 +71,7 @@ export function createOfficeTheme(options: ThemeOptions = {}): ThemeReference { typography: win32Typography(), }; }, + defaultButtonTheme, ); // set up the callback for theme changes on the native side diff --git a/yarn.lock b/yarn.lock index 35b2a48cc3..760d4ff69f 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3063,7 +3063,9 @@ __metadata: version: 0.0.0-use.local resolution: "@fluentui-react-native/android-theme@workspace:packages/theming/android-theme" dependencies: + "@fluentui-react-native/button": "workspace:*" "@fluentui-react-native/eslint-config-rules": "workspace:*" + "@fluentui-react-native/immutable-merge": "workspace:*" "@fluentui-react-native/memo-cache": "workspace:*" "@fluentui-react-native/scripts": "workspace:*" "@fluentui-react-native/theme": "workspace:*" @@ -3094,11 +3096,13 @@ __metadata: version: 0.0.0-use.local resolution: "@fluentui-react-native/apple-theme@workspace:packages/theming/apple-theme" dependencies: + "@fluentui-react-native/button": "workspace:*" "@fluentui-react-native/default-theme": "workspace:*" "@fluentui-react-native/design-tokens-ios": "npm:^0.53.0" "@fluentui-react-native/design-tokens-macos": "npm:^0.53.0" "@fluentui-react-native/eslint-config-rules": "workspace:*" "@fluentui-react-native/experimental-appearance-additions": "workspace:*" + "@fluentui-react-native/immutable-merge": "workspace:*" "@fluentui-react-native/memo-cache": "workspace:*" "@fluentui-react-native/scripts": "workspace:*" "@fluentui-react-native/theme": "workspace:*" @@ -3449,6 +3453,7 @@ __metadata: resolution: "@fluentui-react-native/default-theme@workspace:packages/theming/default-theme" dependencies: "@fluentui-react-native/eslint-config-rules": "workspace:*" + "@fluentui-react-native/immutable-merge": "workspace:*" "@fluentui-react-native/memo-cache": "workspace:*" "@fluentui-react-native/scripts": "workspace:*" "@fluentui-react-native/theme": "workspace:*" @@ -5375,15 +5380,18 @@ __metadata: version: 0.0.0-use.local resolution: "@fluentui-react-native/win32-theme@workspace:packages/theming/win32-theme" dependencies: + "@fluentui-react-native/button": "workspace:*" "@fluentui-react-native/default-theme": "workspace:*" "@fluentui-react-native/design-tokens-win32": "npm:^0.53.0" "@fluentui-react-native/eslint-config-rules": "workspace:*" + "@fluentui-react-native/immutable-merge": "workspace:*" "@fluentui-react-native/memo-cache": "workspace:*" "@fluentui-react-native/scripts": "workspace:*" "@fluentui-react-native/theme": "workspace:*" "@fluentui-react-native/theme-tokens": "workspace:*" "@fluentui-react-native/theme-types": "workspace:*" "@fluentui-react-native/theming-utils": "workspace:*" + "@fluentui-react-native/use-styling": "workspace:*" "@office-iss/react-native-win32": "npm:^0.73.0" "@react-native/babel-preset": "npm:^0.73.0" "@react-native/metro-config": "npm:^0.73.0"