<Spinner win32> - Add TrackSvg and implement platform specific design (#2747)

* add tracker svg for win32

* Change files

* define useSpinner hook

* add sizes as tokens
This commit is contained in:
lenahong 2023-04-10 11:24:41 -07:00 коммит произвёл GitHub
Родитель eaa485222e
Коммит 05bd73ded2
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: 4AEE18F83AFDEB23
18 изменённых файлов: 556 добавлений и 167 удалений

Просмотреть файл

@ -0,0 +1,92 @@
import * as React from 'react';
import { View /*Switch */ } from 'react-native';
//import type { SpinnerStatus } from '@fluentui-react-native/spinner';
import { Spinner } from '@fluentui-react-native/spinner';
import { Stack } from '@fluentui-react-native/stack';
import { TextV1 as Text } from '@fluentui-react-native/text';
import { E2ETestingSpinner } from './SpinnerE2ETest';
import { SPINNER_TESTPAGE } from '../../../../E2E/src/Spinner/consts';
import { stackStyle, commonTestStyles as commonStyles } from '../Common/styles';
import type { TestSection, PlatformStatus } from '../Test';
import { Test } from '../Test';
const BasicSpinnerTest: React.FunctionComponent = () => {
return (
<Stack style={stackStyle}>
<View style={commonStyles.root}>
<View style={commonStyles.settings}></View>
<Spinner />
</View>
</Stack>
);
};
const SpinnerSizeTest: React.FunctionComponent = () => {
return (
<Stack style={stackStyle}>
<View>
<View>
<Text>tiny</Text>
<Spinner size="tiny" />
</View>
<View>
<Text>x-small</Text>
<Spinner size="x-small" />
</View>
<View>
<Text>small</Text>
<Spinner size="small" />
</View>
<View>
<Text>medium</Text>
<Spinner size="medium" />
</View>
<View>
<Text>large</Text>
<Spinner size="large" />
</View>
<View>
<Text>x-large</Text>
<Spinner size="x-large" />
</View>
<View>
<Text>huge</Text>
<Spinner size="huge" />
</View>
</View>
</Stack>
);
};
const spinnerSections: TestSection[] = [
{
name: 'Basic Spinner Test',
testID: SPINNER_TESTPAGE,
component: BasicSpinnerTest,
},
{
name: 'Spinner Size Test',
component: SpinnerSizeTest,
},
{
name: 'Spinner for E2E Testing',
component: () => <E2ETestingSpinner />,
},
];
export const SpinnerTest: React.FunctionComponent = () => {
const status: PlatformStatus = {
win32Status: 'Experimental',
uwpStatus: 'Backlog',
iosStatus: 'Beta',
macosStatus: 'Backlog',
androidStatus: 'Backlog',
};
const description =
'Spinner is a visual representation that data is being loaded. It is implemented with a View wrapping an Animated SVG. The View is to ensure that AccessibilityRole works. AccessibilityRole currently does not work on SVGs.';
return <Test name="Spinner Test" description={description} sections={spinnerSections} status={status}></Test>;
};

Просмотреть файл

@ -253,7 +253,7 @@ export const tests: TestDescription[] = [
name: 'Spinner V1',
component: SpinnerTest,
testPageButton: Constants.HOMEPAGE_SPINNER_BUTTON,
platforms: ['android'],
platforms: ['android', 'win32'],
},
{
name: 'Stroke Width Tokens',

Просмотреть файл

@ -0,0 +1,7 @@
{
"type": "minor",
"comment": "add tracker svg for win32",
"packageName": "@fluentui-react-native/spinner",
"email": "email not defined",
"dependentChangeType": "patch"
}

Просмотреть файл

@ -0,0 +1,7 @@
{
"type": "minor",
"comment": "add tracker svg for win32",
"packageName": "@fluentui-react-native/tester",
"email": "email not defined",
"dependentChangeType": "patch"
}

Просмотреть файл

@ -42,13 +42,6 @@ The `Spinner` is an outline of a circle which animates around itself, to visuall
## Variants
Label postion decides where will the label place with respect to Spinner :
- If `labelPosition` is equal to "above", then the Label appears be vertically above with respect to Spinner.
- If `labelPosition` is equal to "below", then the Label appears be vertically below with respect to Spinner.
- If `labelPosition` is equal to "before", then the Label appears be horizontally before with respect to Spinner.
- If `labelPosition` is equal to "after", then the Label appears be horizontally after with respect to Spinner.
#### Status
Users can control whether `Spinner` is animating or not :
@ -60,6 +53,8 @@ Users can control whether `Spinner` is animating or not :
Users can control the `Spinner` size from the configuration below :
##### Mobile
| Size | Diameter (height/width) | Line Thickness |
| -------- | ----------------------- | ---------------- |
| xx-small | 12 (iconSize120) | 1 (stokeWidth10) |
@ -68,6 +63,18 @@ Users can control the `Spinner` size from the configuration below :
| large | 32 | 3 (stokeWidth30) |
| x-large | 40 (iconSize360) | 4 (stokeWidth40) |
##### Win32
| Size | Diameter (height/width) | Line Thickness |
| ------- | ----------------------- | ---------------- |
| tiny | 20 (iconSize200) | 2 (stokeWidth20) |
| x-small | 24 (iconSize240) | 2 (stokeWidth20) |
| small | 28 (iconSize280) | 2 (stokeWidth20) |
| medium | 32 (iconSize320) | 3 (stokeWidth30) |
| large | 36 (iconSize360) | 3 (stokeWidth30) |
| x-large | 40 (iconSize400) | 3 (stokeWidth30) |
| huge | 44 (iconSize440) | 4 (stokeWidth40) |
#### Label Postion
Label postion decides where will the label place with respect to Spinner :
@ -86,10 +93,10 @@ The `Spinner` uses 5 slots on win32 and 2 slots on mobile platforms:
Win32 Slots
- `root` [View] The outer container of the component
- `track` [Svg]The container for the spinner svg .
- `tail` [Svg] The svg of the tail that will act as animated spinner
- `track` [trackSvg]The container for the spinner svg .
- `tail` [tailSvg] The svg of the tail that will act as animated spinner
- `tailContainer` [RCTNativeAnimatedSpinner] The Container for the tail of the spinner
- `label` [TextV1] If specified, renders the name of the passed value as text.
- `label` [Text] If specified, renders the name of the passed value as text.
Mobile Slots
@ -101,7 +108,7 @@ Mobile Slots
Below is the set of props `Spinner` supports.
```tsx
export interface SpinnerProps extends ViewProps, SpinnerTokens {
export interface SpinnerProps extends ViewProps {
/**
* Spinner appearnace
* @defaultValue 'primary'
@ -114,6 +121,11 @@ export interface SpinnerProps extends ViewProps, SpinnerTokens {
* Note: This is not supported on mobile platforms
*/
labelPosition?: SpinnerLabelPosition;
/**
* Spinner label
* Note: This is not supported on mobile platforms
*/
label?: string;
/**
* Spinner size
* @defaultValue 'medium'
@ -124,11 +136,6 @@ export interface SpinnerProps extends ViewProps, SpinnerTokens {
* @defaultValue 'active'
*/
status?: SpinnerStatus;
/**
* Spinner label
* Note: This is not supported on mobile platforms
*/
label?: string;
/**
* Spinner hidden when not animating or not hidden
* @defaultValue 'true'
@ -136,12 +143,23 @@ export interface SpinnerProps extends ViewProps, SpinnerTokens {
*/
hidesWhenStopped?: boolean;
}
export interface SpinnerSvgProps extends SpinnerTokens {
/**
* The height and width of the viewBox are internal props used by the SVG to size themselves and
* set up their viewBox to establish coordinate space for DPI scaling purposes.
*/
viewBoxHeight: number;
viewBoxWidth: number;
}
```
### Styling Tokens
Tokens can be used to customize the styling of the control by using the customize function on the `Spinner`. For more information on using the customize API, please see [this page](https://github.com/microsoft/fluentui-react-native/blob/main/packages/framework/composition/README.md). The `Spinner` has the following tokens:
#### Shared Tokens
```tsx
export interface SpinnerTokens {
/**
@ -158,5 +176,10 @@ export interface SpinnerTokens {
* @defaultValue 'medium'
*/
size?: SpinnerSize;
/**
* Spinner appearnace
* @defaultValue 'true'
*/
inverted?: SpinnerTokens;
}
```

Просмотреть файл

@ -5,11 +5,13 @@ import { Animated, Easing, View } from 'react-native';
import { compose, mergeProps, withSlots } from '@fluentui-react-native/framework';
import type { UseSlots } from '@fluentui-react-native/framework';
import { Svg, Path } from 'react-native-svg';
import { Path, Svg } from 'react-native-svg';
import { diameterSizeMap, lineThicknessSizeMap, stylingSettings } from './Spinner.styling';
import { stylingSettings } from './Spinner.styling';
import type { SpinnerProps, SpinnerType } from './Spinner.types';
import { spinnerName } from './Spinner.types';
import { diameterSizeMap, lineThicknessSizeMap } from './SpinnerTokens.win32';
import { useSpinner } from './useSpinner';
const getSpinnerPath = (diameter: number, width: number, color: ColorValue) => {
const start = {
@ -31,7 +33,8 @@ export const Spinner = compose<SpinnerType>({
svg: AnimatedSvg,
},
useRender: (props: SpinnerProps, useSlots: UseSlots<SpinnerType>) => {
const Slots = useSlots(props);
const spinnerProps = useSpinner(props);
const Slots = useSlots(spinnerProps);
const status = props.status !== undefined ? props.status : 'active';
const hidesWhenStopped = props.hidesWhenStopped != undefined ? props.hidesWhenStopped : true;
const hideOpacity = status === 'inactive' && hidesWhenStopped == true ? 0 : 1;
@ -74,11 +77,7 @@ export const Spinner = compose<SpinnerType>({
outputRange: ['0deg', '359deg'],
});
const path = getSpinnerPath(
diameterSizeMap[Slots.root({}).props.size],
lineThicknessSizeMap[Slots.root({}).props.size],
Slots.root({}).props.trackColor,
);
const path = getSpinnerPath(diameterSizeMap[spinnerProps.size], lineThicknessSizeMap[spinnerProps.size], spinnerProps.trackColor);
// perspective is needed for animations to work on Android. See https://reactnative.dev/docs/animations#bear-in-mind
const animatedSvgProps = {

Просмотреть файл

@ -5,21 +5,6 @@ import type { SpinnerProps, SpinnerSlotProps, SpinnerTokens } from './Spinner.ty
import { spinnerName } from './Spinner.types';
import { defaultSpinnerTokens } from './SpinnerTokens';
export const diameterSizeMap: { [key: string]: number } = {
'xx-small': 12,
'x-small': 16,
medium: 24,
large: 32,
'x-large': 40,
};
export const lineThicknessSizeMap: { [key: string]: number } = {
'xx-small': 1,
'x-small': 1,
medium: 2,
large: 3,
'x-large': 4,
};
export const stylingSettings: UseStylingOptions<SpinnerProps, SpinnerSlotProps, SpinnerTokens> = {
tokens: [defaultSpinnerTokens, spinnerName],
tokensThatAreAlsoProps: 'all',
@ -29,21 +14,19 @@ export const stylingSettings: UseStylingOptions<SpinnerProps, SpinnerSlotProps,
trackColor: tokens.trackColor,
size: tokens.size,
lineThickness: tokens.size != 'medium' ? tokens.size : tokens.size,
accessibilityRole: 'progressbar',
accessible: true,
style: {
width: diameterSizeMap[tokens.size],
height: diameterSizeMap[tokens.size],
width: tokens.width,
height: tokens.height,
},
}),
['trackColor', 'size'],
),
svg: buildProps(
(tokens: SpinnerTokens) => ({
width: diameterSizeMap[tokens.size],
height: diameterSizeMap[tokens.size],
width: tokens.width,
height: tokens.height,
}),
['size'],
['width', 'height'],
),
},
};

Просмотреть файл

@ -0,0 +1,31 @@
import type { UseStylingOptions } from '@fluentui-react-native/framework';
import { buildProps } from '@fluentui-react-native/framework';
import type { SpinnerProps, SpinnerSlotProps, SpinnerTokens } from './Spinner.types.win32';
import { spinnerName } from './Spinner.types.win32';
import { defaultSpinnerTokens } from './SpinnerTokens';
export const stylingSettings: UseStylingOptions<SpinnerProps, SpinnerSlotProps, SpinnerTokens> = {
tokens: [defaultSpinnerTokens, spinnerName],
tokensThatAreAlsoProps: 'all',
slotProps: {
root: buildProps(
(tokens: SpinnerTokens) => ({
style: {
width: tokens.width,
height: tokens.height,
},
}),
['width', 'height'],
),
track: buildProps(
(tokens: SpinnerTokens) => ({
size: tokens.size,
trackColor: tokens.trackColor,
viewBoxWidth: tokens.width,
viewBoxHeight: tokens.height,
}),
['size', 'trackColor', 'width', 'height'],
),
},
};

Просмотреть файл

@ -1,35 +1,28 @@
/** @jsx withSlots */
import { View } from 'react-native';
import { Animated, View } from 'react-native';
import type { UseSlots } from '@fluentui-react-native/framework';
import { compose, mergeProps, withSlots } from '@fluentui-react-native/framework';
import { TextV1 as Text } from '@fluentui-react-native/text';
import { Svg } from 'react-native-svg';
import { RCTNativeAnimatedSpinner } from './consts.win32';
import type { SpinnerProps, SpinnerType } from './Spinner.types';
import { spinnerName } from './Spinner.types';
import { useSpinner } from './useSpinner';
/* TODO: Implement Spinner with following slots */
export const AnimatedSvg = Animated.createAnimatedComponent(Svg);
export const Spinner = compose<SpinnerType>({
displayName: spinnerName,
slots: {
root: View,
track: Svg,
tail: Svg,
tailContainer: RCTNativeAnimatedSpinner,
label: Text,
svg: AnimatedSvg,
},
useRender: (props: SpinnerProps, useSlots: UseSlots<SpinnerType>) => {
const Slots = useSlots(props);
const spinnerProps = useSpinner(props);
const Slots = useSlots(spinnerProps);
return (rest: SpinnerProps) => {
const { ...mergedProps } = mergeProps(props, rest);
return (
<Slots.root {...mergedProps}>
<Slots.label />
</Slots.root>
);
const { ...mergedProps } = mergeProps(spinnerProps, rest);
return <Slots.root {...mergedProps}></Slots.root>;
};
},
});

Просмотреть файл

@ -0,0 +1,106 @@
import type { Animated, ViewProps } from 'react-native';
import type { SvgProps } from 'react-native-svg';
export const spinnerName = 'Spinner';
/**
* Specifies the possible appearance of the Spinner.
*/
export type SpinnerAppearance = 'primary' | 'inverted';
/**
* Specifies the possible label position of the Spinner.
*/
export type SpinnerLabelPosition = 'above' | 'below' | 'before' | 'after';
/**
* Specifies the possible sizes of the Spinner.
*/
export type SpinnerSize = 'tiny' | 'xx-small' | 'x-small' | 'small' | 'medium' | 'large' | 'x-large' | 'huge';
/**
* Specifies the possible status of the Spinner.
*/
export type SpinnerStatus = 'active' | 'inactive';
export interface SpinnerTokens {
/**
* Spinner element color
*/
trackColor?: string;
/**
* Spinner element color
* Note: This is not supported on mobile platforms
*/
tailColor?: string;
/**
* Size of the Spinner view
* @defaultValue 'medium'
*/
size?: SpinnerSize;
/**
* Spinner appearnace
* @defaultValue 'false'
*/
inverted?: SpinnerTokens;
width?: number;
height?: number;
/**
* Sizes of the Spinner
*/
'x-small'?: SpinnerTokens;
small?: SpinnerTokens;
medium?: SpinnerTokens;
large?: SpinnerTokens;
'x-large'?: SpinnerTokens;
/* win32 specific */
tiny?: SpinnerTokens;
huge?: SpinnerTokens;
/* mobile specific */
'xx-small'?: SpinnerTokens;
}
export interface SpinnerProps extends ViewProps, SpinnerTokens {
/**
* Spinner appearnace
* @defaultValue 'primary'
* Note: This is not supported on mobile platforms
*/
appearance?: SpinnerAppearance;
/**
* Spinner label position
* @defaultValue 'after'
* Note: This is not supported on mobile platforms
*/
labelPosition?: SpinnerLabelPosition;
/**
* Spinner label
* Note: This is not supported on mobile platforms
*/
label?: string;
/**
* Spinner size
* @defaultValue 'medium'
*/
size?: SpinnerSize;
/**
* Spinner animating or not
* @defaultValue 'active'
*/
status?: SpinnerStatus;
/**
* Spinner hidden when not animating or not hidden
* @defaultValue 'true'
* @platform android
*/
hidesWhenStopped?: boolean;
}
export type SpinnerState = SpinnerProps;
export interface SpinnerSlotProps {
root: SpinnerProps;
svg?: Animated.AnimatedProps<SvgProps>;
}
export interface SpinnerType {
props: SpinnerProps;
slotProps: SpinnerSlotProps;
tokens: SpinnerTokens;
}

Просмотреть файл

@ -1,97 +1,12 @@
import type { Animated, TextProps, ViewProps } from 'react-native';
import type { SvgProps } from 'react-native-svg';
export const spinnerName = 'Spinner';
/**
* Specifies the possible appearance of the Spinner.
*/
export type SpinnerAppearance = 'primary' | 'inverted';
/**
* Specifies the possible label position of the Spinner.
*/
export type SpinnerLabelPosition = 'above' | 'below' | 'before' | 'after';
/**
* Specifies the possible sizes of the Spinner.
*/
export type SpinnerSize = 'tiny' | 'xx-small' | 'x-small' | 'small' | 'medium' | 'large' | 'x-large' | 'huge';
/**
* Specifies the possible status of the Spinner.
*/
export type SpinnerStatus = 'active' | 'inactive';
export interface SpinnerTokens {
/**
* Spinner element color
*/
trackColor?: string;
/**
* Spinner element color
* Note: This is not supported on mobile platforms
*/
tailColor?: string;
/**
* Size of the Spinner view
* @defaultValue 'medium'
*/
size?: SpinnerSize;
}
export interface SpinnerProps extends ViewProps, SpinnerTokens {
/**
* Spinner appearnace
* @defaultValue 'primary'
* Note: This is not supported on mobile platforms
*/
appearance?: SpinnerAppearance;
/**
* Spinner label position
* @defaultValue 'after'
* Note: This is not supported on mobile platforms
*/
labelPosition?: SpinnerLabelPosition;
/**
* Spinner size
* @defaultValue 'medium'
*/
size?: SpinnerSize;
/**
* Spinner animating or not
* @defaultValue 'active'
*/
status?: SpinnerStatus;
/**
* Spinner label
* Note: This is not supported on mobile platforms
*/
label?: string;
/**
* Spinner hidden when not animating or not hidden
* @defaultValue 'true'
* @platform android
*/
hidesWhenStopped?: boolean;
}
export interface SpinnerSvgProps extends SpinnerTokens {
/**
* The height and width of the viewBox are internal props used by the SVG to size themselves and
* set up their viewBox to establish coordinate space for DPI scaling purposes.
*/
viewBoxHeight: number;
viewBoxWidth: number;
}
export interface SpinnerSlotProps {
root: SpinnerProps; //SpinnerProps extends ViewProps which is required for win32 native module.
track?: SpinnerSvgProps;
tail?: SpinnerSvgProps;
tailContainer?: SpinnerSvgProps;
label?: TextProps;
svg?: Animated.AnimatedProps<SvgProps>;
}
export interface SpinnerType {
props: SpinnerProps;
slotProps: SpinnerSlotProps;
tokens: SpinnerTokens;
}
export {
spinnerName,
SpinnerAppearance,
SpinnerLabelPosition,
SpinnerSize,
SpinnerStatus,
SpinnerState,
SpinnerTokens,
SpinnerProps,
SpinnerSlotProps,
SpinnerType,
} from './Spinner.types.shared';

Просмотреть файл

@ -0,0 +1,28 @@
import type { TextProps } from 'react-native';
import type { SpinnerProps, SpinnerTokens } from './Spinner.types.shared';
export { SpinnerProps, SpinnerTokens };
export { spinnerName, SpinnerAppearance, SpinnerLabelPosition, SpinnerSize, SpinnerStatus } from './Spinner.types.shared';
export interface SpinnerSvgProps extends SpinnerTokens {
/**
* The height and width of the viewBox are internal props used by the SVG to size themselves and
* set up their viewBox to establish coordinate space for DPI scaling purposes.
*/
viewBoxHeight?: number;
viewBoxWidth?: number;
}
export interface SpinnerSlotProps {
root: SpinnerProps; //SpinnerProps extends ViewProps which is required for win32 native module.
track?: SpinnerSvgProps;
tail?: SpinnerSvgProps;
tailContainer?: SpinnerSvgProps;
label?: TextProps;
}
export interface SpinnerType {
props: SpinnerProps;
slotProps: SpinnerSlotProps;
tokens: SpinnerTokens;
}

Просмотреть файл

@ -0,0 +1,77 @@
/** @jsx withSlots */
import type { ColorValue } from 'react-native';
import { View } from 'react-native';
import type { UseSlots } from '@fluentui-react-native/framework';
import { compose, mergeProps, withSlots } from '@fluentui-react-native/framework';
import { TextV1 as Text } from '@fluentui-react-native/text';
import { Path, Svg } from 'react-native-svg';
import type { SvgProps } from 'react-native-svg';
import { RCTNativeAnimatedSpinner } from './consts.win32';
import { stylingSettings } from './Spinner.styling.win32';
import { spinnerName } from './Spinner.types';
import type { SpinnerProps, SpinnerType, SpinnerSvgProps } from './Spinner.types.win32';
import { diameterSizeMap, lineThicknessSizeMap, getDefaultSize } from './SpinnerTokens.win32';
import { useSpinner } from './useSpinner';
// TODO: getTailPath, tailSvg
const getTrackPath = (diameter: number, width: number, color: ColorValue) => {
const start = {
x: width / 2,
y: diameter / 2 + width / 2,
};
const path = `M${start.x} ${start.y} a${diameter / 2} ${diameter / 2} 0 1 0 ${diameter} 0 a${diameter / 2} ${
diameter / 2
} 0 1 0 -${diameter} 0}`;
return <Path d={path} stroke={color} strokeWidth={width} strokeLinecap="round" fillOpacity={0} />;
};
/* Track is a full circle with a transparent fill */
const trackSvg: React.FunctionComponent<SpinnerSvgProps> = (props: SpinnerSvgProps) => {
const { size, trackColor } = props;
const svgProps: SvgProps = {
style: {
height: diameterSizeMap[size],
width: diameterSizeMap[size],
},
};
const path = getTrackPath(diameterSizeMap[size] - lineThicknessSizeMap[size], lineThicknessSizeMap[size], trackColor);
return <Svg {...svgProps}>{path}</Svg>;
};
export const spinnerLookup = (layer: string, userProps: SpinnerProps): boolean => {
return (
userProps[layer] ||
layer === userProps['appearance'] ||
layer === userProps['size'] ||
(!userProps['size'] && layer === getDefaultSize())
);
};
export const Spinner = compose<SpinnerType>({
displayName: spinnerName,
...stylingSettings,
slots: {
root: View,
track: trackSvg,
tail: Svg,
tailContainer: RCTNativeAnimatedSpinner,
label: Text,
},
useRender: (props: SpinnerProps, useSlots: UseSlots<SpinnerType>) => {
const spinnerProps = useSpinner(props);
const Slots = useSlots(spinnerProps, (layer) => spinnerLookup(layer, spinnerProps));
return (final: SpinnerProps) => {
const { ...mergedProps } = mergeProps(spinnerProps, final);
return (
<Slots.root {...mergedProps}>
<Slots.track />
</Slots.root>
);
};
},
});

Просмотреть файл

@ -8,6 +8,4 @@ import type { SpinnerTokens } from './Spinner.types';
export const defaultSpinnerTokens: TokenSettings<SpinnerTokens, Theme> = () =>
({
trackColor: Appearance.getColorScheme() === 'light' ? globalTokens.color.grey56 : globalTokens.color.grey72,
lineThickness: 'medium',
size: 'medium',
} as SpinnerTokens);

Просмотреть файл

@ -1,13 +1,50 @@
import { Appearance } from 'react-native';
import type { Theme } from '@fluentui-react-native/framework';
import type { TokenSettings } from '@fluentui-react-native/use-styling';
import type { SpinnerTokens } from './Spinner.types';
export const defaultSpinnerTokens: TokenSettings<SpinnerTokens, Theme> = () =>
/* Mobile sizes */
export const diameterSizeMap: { [key: string]: number } = {
'xx-small': 12,
'x-small': 16,
medium: 24,
large: 32,
'x-large': 40,
};
export const lineThicknessSizeMap: { [key: string]: number } = {
'xx-small': 1,
'x-small': 1,
medium: 2,
large: 3,
'x-large': 4,
};
export const defaultSpinnerTokens: TokenSettings<SpinnerTokens, Theme> = (t: Theme) =>
({
trackColor: Appearance.getColorScheme() === 'light' ? '#BDBDBD' : '#666666',
lineThickness: 'medium',
size: 'medium',
xxSmall: {
size: 'xx-small',
width: diameterSizeMap['xx-small'],
height: diameterSizeMap['xx-small'],
},
small: {
size: 'x-small',
width: diameterSizeMap['x-small'],
height: diameterSizeMap['x-small'],
},
medium: {
size: 'medium',
width: diameterSizeMap['medium'],
height: diameterSizeMap['medium'],
},
large: {
size: 'large',
width: diameterSizeMap['large'],
height: diameterSizeMap['large'],
},
xlarge: {
size: 'x-large',
width: diameterSizeMap['x-large'],
height: diameterSizeMap['x-large'],
},
trackColor: t.colors.brandStroke2,
} as SpinnerTokens);

Просмотреть файл

@ -0,0 +1,82 @@
import type { Theme, Variant } from '@fluentui-react-native/framework';
import type { TokenSettings } from '@fluentui-react-native/use-styling';
import type { SpinnerSize, SpinnerTokens } from './Spinner.types.win32';
export const diameterSizeMap: { [key: string]: number } = {
tiny: 20,
'x-small': 24,
small: 28,
medium: 32,
large: 36,
'x-large': 40,
huge: 44,
};
export const lineThicknessSizeMap: { [key: string]: number } = {
tiny: 2,
'x-small': 2,
small: 2,
medium: 3,
large: 3,
'x-large': 3,
huge: 4,
};
export const textStyleMap: { [key: string]: Variant } = {
tiny: 'body1',
'x-small': 'body1',
small: 'body1',
medium: 'subtitle2',
large: 'subtitle2',
'x-large': 'subtitle2',
huge: 'subtitle1',
};
export const getDefaultSize = (): SpinnerSize => {
return 'medium';
};
export const defaultSpinnerTokens: TokenSettings<SpinnerTokens, Theme> = (t: Theme) =>
({
tiny: {
size: 'tiny',
width: diameterSizeMap['tiny'],
height: diameterSizeMap['tiny'],
},
'x-small': {
size: 'x-small',
width: diameterSizeMap['x-small'],
height: diameterSizeMap['x-small'],
},
small: {
size: 'small',
width: diameterSizeMap['small'],
height: diameterSizeMap['small'],
},
medium: {
size: 'medium',
width: diameterSizeMap['medium'],
height: diameterSizeMap['medium'],
},
large: {
size: 'large',
width: diameterSizeMap['large'],
height: diameterSizeMap['large'],
},
'x-large': {
size: 'x-large',
width: diameterSizeMap['x-large'],
height: diameterSizeMap['x-large'],
},
huge: {
size: 'huge',
width: diameterSizeMap['huge'],
height: diameterSizeMap['huge'],
},
tailColor: t.colors.brandStroke1,
trackColor: t.colors.brandStroke2,
inverted: {
tailColor: t.colors.neutralStroke2,
trackColor: t.colors.neutralBackgroundInverted,
},
} as SpinnerTokens);

Просмотреть файл

@ -1,4 +1,5 @@
export { Spinner } from './Spinner';
export { spinnerName } from './Spinner.types';
export {
SpinnerTokens,
SpinnerProps,

Просмотреть файл

@ -0,0 +1,10 @@
import type { SpinnerProps, SpinnerState } from './Spinner.types';
export const useSpinner = (props: SpinnerProps): SpinnerState => {
return {
accessible: true,
accessibilityRole: 'progressbar',
size: 'medium',
...props,
};
};