diff --git a/types/styled-components/index.d.ts b/types/styled-components/index.d.ts index 25d006f6f1..a842c94c63 100644 --- a/types/styled-components/index.d.ts +++ b/types/styled-components/index.d.ts @@ -9,17 +9,28 @@ /// -import * as React from 'react'; -import * as CSS from 'csstype'; +import * as CSS from "csstype"; +import * as React from "react"; -export type CSSObject = CSS.Properties; +export type CSSObject = CSS.Properties & + // Index type to allow selector nesting + // This is "[key in string]" and not "[key: string]" to allow CSSObject to be self-referential + { + // we need the CSS.Properties in here too to ensure the index signature doesn't create impossible values + [key in string]: + | CSS.Properties[keyof CSS.Properties< + string | number + >] + | CSSObject + }; +export type CSSKeyframes = object & { [key: string]: CSSObject }; export interface ThemeProps { theme: T; } export type ThemedStyledProps = P & ThemeProps; -export type StyledProps

= ThemedStyledProps; +export type StyledProps

= ThemedStyledProps>; export type StyledComponentProps< // The Component from whose props are derived @@ -45,27 +56,25 @@ type StyledComponentPropsWithAs< export type FalseyValue = undefined | null | false; export type Interpolation

= - | FlattenInterpolation

- | ReadonlyArray< - FlattenInterpolation

| ReadonlyArray> - >; -export type FlattenInterpolation

= | InterpolationValue + | FlattenInterpolation

| InterpolationFunction

; +// must be an interface to be self-referential +export interface FlattenInterpolation

+ extends ReadonlyArray> {} export type InterpolationValue = | string | number - | Styles | FalseyValue | Keyframes | StyledComponentInterpolation | CSSObject; export type SimpleInterpolation = | InterpolationValue - | ReadonlyArray>; -export interface Styles { - [ruleOrSelector: string]: string | number | Styles; -} + | FlattenSimpleInterpolation; +// must be an interface to be self-referential +interface FlattenSimpleInterpolation + extends ReadonlyArray {} export type InterpolationFunction

= (props: P) => Interpolation

; @@ -76,19 +85,23 @@ type DeprecatedAttrs, T> = { [K in keyof A]: ((props: ThemedStyledProps) => A[K]) | A[K] }; -export type ThemedGlobalStyledClassProps = P & { - suppressMultiMountWarning?: boolean - theme?: T +export type ThemedGlobalStyledClassProps = WithOptionalTheme & { + suppressMultiMountWarning?: boolean; }; export interface GlobalStyleComponent extends React.ComponentClass> {} // remove the call signature from StyledComponent so Interpolation can still infer InterpolationFunction -type StyledComponentInterpolation = Pick< - StyledComponent, - keyof StyledComponent ->; +type StyledComponentInterpolation = + | Pick< + StyledComponentBase, + keyof StyledComponentBase + > + | Pick< + StyledComponentBase, + keyof StyledComponentBase + >; // abuse Pick to strip the call signature from ForwardRefExoticComponent type ForwardRefExoticBase

= Pick< @@ -101,7 +114,16 @@ export type AnyStyledComponent = | StyledComponent | StyledComponent; -export interface StyledComponent< +export type StyledComponent< + C extends keyof JSX.IntrinsicElements | React.ComponentType, + T extends object, + O extends object = {}, + A extends keyof any = never +> = // the "string" allows this to be used as an object key + // I really want to avoid this if possible but it's the only way to use nesting with object styles... + string & StyledComponentBase; + +export interface StyledComponentBase< C extends keyof JSX.IntrinsicElements | React.ComponentType, T extends object, O extends object = {}, @@ -127,7 +149,7 @@ export interface StyledComponent< * * String types need to be cast to themselves to become literal types (as={'a' as 'a'}). */ - as?: keyof JSX.IntrinsicElements | React.ComponentType + as?: keyof JSX.IntrinsicElements | React.ComponentType; } ): React.ReactElement>; withComponent( @@ -145,49 +167,73 @@ export interface StyledComponent< ): StyledComponent; } -export interface ThemedStyledFunction< +export interface ThemedStyledFunctionBase< C extends keyof JSX.IntrinsicElements | React.ComponentType, T extends object, O extends object = {}, A extends keyof any = never > { ( - strings: TemplateStringsArray, - ...interpolations: Array< + first: + | TemplateStringsArray + | NonNullable< + Interpolation< + ThemedStyledProps & O, T> + > + >, + ...rest: Array< Interpolation< ThemedStyledProps & O, T> > > ): StyledComponent; + // at least the first argument is required, whatever it is ( - strings: TemplateStringsArray, - ...interpolations: Array< + first: + | TemplateStringsArray + | NonNullable< + Interpolation< + ThemedStyledProps< + StyledComponentPropsWithRef & O & U, + T + > + > + >, + ...rest: Array< Interpolation< ThemedStyledProps & O & U, T> > > ): StyledComponent; +} + +export interface ThemedStyledFunction< + C extends keyof JSX.IntrinsicElements | React.ComponentType, + T extends object, + O extends object = {}, + A extends keyof any = never +> extends ThemedStyledFunctionBase { // Fun thing: 'attrs' can also provide a polymorphic 'as' prop // My head already hurts enough so maybe later... attrs< U, - A extends Partial & U> & { - [others: string]: any + NewA extends Partial & U> & { + [others: string]: any; } = {} >( - attrs: Attrs & U, A, T> - ): ThemedStyledFunction; + attrs: Attrs & U, NewA, T> + ): ThemedStyledFunction; // Only this overload is deprecated // tslint:disable:unified-signatures /** @deprecated Prefer using the new single function style, to be removed in v5 */ attrs< U, - A extends Partial & U> & { - [others: string]: any + NewA extends Partial & U> & { + [others: string]: any; } = {} >( - attrs: DeprecatedAttrs & U, A, T> - ): ThemedStyledFunction; + attrs: DeprecatedAttrs & U, NewA, T> + ): ThemedStyledFunction; // tslint:enable:unified-signatures } @@ -222,17 +268,13 @@ type StyledComponentInnerAttrs< export interface ThemedBaseStyledInterface extends ThemedStyledComponentFactories { - (tag: TTag): ThemedStyledFunction< - TTag, - T - >; (component: C): ThemedStyledFunction< StyledComponentInnerComponent, T, StyledComponentInnerOtherProps, StyledComponentInnerAttrs >; - >( + >( // unfortunately using a conditional type to validate that it can receive a `theme?: Theme` // causes tests to fail in TS 3.1 component: C @@ -240,60 +282,67 @@ export interface ThemedBaseStyledInterface } export type ThemedStyledInterface = ThemedBaseStyledInterface< - Extract extends never ? any : T + AnyIfEmpty >; export type StyledInterface = ThemedStyledInterface; export interface BaseThemedCssFunction { - (cssObject: CSSObject): InterpolationValue[]; ( - strings: TemplateStringsArray, - ...interpolations: SimpleInterpolation[] - ): InterpolationValue[]; -

( - strings: TemplateStringsArray, + first: + | TemplateStringsArray + | NonNullable>>, + ...interpolations: Array>> + ): FlattenInterpolation>; +

( + first: + | TemplateStringsArray + | NonNullable>>, ...interpolations: Array>> - ): Array>>; -

(func: InterpolationFunction>): Array< - FlattenInterpolation> - >; + ): FlattenInterpolation>; } + export type ThemedCssFunction = BaseThemedCssFunction< - Extract extends never ? any : T + AnyIfEmpty >; // Helper type operators type Omit = Pick>; -type DiffBetween = Pick> & - Pick>; -type WithOptionalTheme

= Omit & { - theme?: T +type WithOptionalTheme

= Omit & { + theme?: T; }; +type AnyIfEmpty = keyof T extends never ? any : T; -export interface ThemedStyledComponentsModule { +export interface ThemedStyledComponentsModule< + T extends object, + U extends object = T +> { default: ThemedStyledInterface; css: ThemedCssFunction; - keyframes(cssObject: CSSObject): Keyframes; + // unfortunately keyframes can't interpolate props from the theme keyframes( - strings: TemplateStringsArray, + strings: TemplateStringsArray | CSSKeyframes, ...interpolations: SimpleInterpolation[] ): Keyframes; - createGlobalStyle(cssObject: CSSObject): GlobalStyleComponent<{}, T>; - createGlobalStyle

( - strings: TemplateStringsArray, + createGlobalStyle

( + first: + | TemplateStringsArray + | NonNullable>>, ...interpolations: Array>> ): GlobalStyleComponent; - createGlobalStyle

( - func: InterpolationFunction> - ): GlobalStyleComponent; withTheme: WithThemeFnInterface; - ThemeProvider: ThemeProviderComponent; + ThemeProvider: ThemeProviderComponent; ThemeConsumer: React.Consumer; ThemeContext: React.Context; + + // This could be made to assert `target is StyledComponent` instead, but that feels not type safe + isStyledComponent: typeof isStyledComponent; + + ServerStyleSheet: typeof ServerStyleSheet; + StyleSheetManager: typeof StyleSheetManager; } declare const styled: StyledInterface; @@ -310,49 +359,51 @@ export type BaseWithThemeFnInterface = < WithOptionalTheme, T> >; export type WithThemeFnInterface = BaseWithThemeFnInterface< - Extract extends never ? any : T + AnyIfEmpty >; export const withTheme: WithThemeFnInterface; +/** + * This interface can be augmented by users to add types to `styled-components`' default theme + * without needing to reexport `ThemedStyledComponentsModule`. + */ +// Unfortunately, there is no way to write tests for this +// as any augmentation will break the tests for the default case (not augmented). // tslint:disable-next-line:no-empty-interface export interface DefaultTheme {} -export interface ThemeProviderProps { +export interface ThemeProviderProps { children?: React.ReactChild; // only one child is allowed, goes through React.Children.only - theme: T | ((theme: T) => T); + theme: T | ((theme: U) => T); } -export type BaseThemeProviderComponent = React.ComponentClass< - ThemeProviderProps ->; +export type BaseThemeProviderComponent< + T extends object, + U extends object = T +> = React.ComponentClass>; export type ThemeProviderComponent< - T extends object -> = BaseThemeProviderComponent extends never ? any : T>; -export const ThemeProvider: ThemeProviderComponent; -// NOTE: this technically starts as undefined -// Also, this cannot be DefaultTheme as it breaks TypedComponents' assignability -export const ThemeContext: React.Context; -export const ThemeConsumer: typeof ThemeContext['Consumer']; + T extends object, + U extends object = T +> = BaseThemeProviderComponent, AnyIfEmpty>; +export const ThemeProvider: ThemeProviderComponent>; +// NOTE: this technically starts as undefined, but allowing undefined is unhelpful when used correctly +export const ThemeContext: React.Context>; +export const ThemeConsumer: typeof ThemeContext["Consumer"]; export interface Keyframes { getName(): string; } -export function keyframes(cssObject: CSSObject): Keyframes; export function keyframes( - strings: TemplateStringsArray, + strings: TemplateStringsArray | CSSKeyframes, ...interpolations: SimpleInterpolation[] ): Keyframes; -export function createGlobalStyle( - cssObject: CSSObject -): GlobalStyleComponent<{}, DefaultTheme>; -export function createGlobalStyle

( - strings: TemplateStringsArray, +export function createGlobalStyle

( + first: + | TemplateStringsArray + | NonNullable>>, ...interpolations: Array>> ): GlobalStyleComponent; -export function createGlobalStyle

( - func: InterpolationFunction> -): GlobalStyleComponent; export function isStyledComponent( target: any @@ -373,12 +424,12 @@ export class ServerStyleSheet { type StyleSheetManagerProps = | { - sheet: ServerStyleSheet - target?: never + sheet: ServerStyleSheet; + target?: never; } | { - sheet?: never - target: HTMLElement + sheet?: never; + target: HTMLElement; }; export class StyleSheetManager extends React.Component< diff --git a/types/styled-components/test/index.tsx b/types/styled-components/test/index.tsx index 64e0f447ba..e435d90011 100644 --- a/types/styled-components/test/index.tsx +++ b/types/styled-components/test/index.tsx @@ -1,6 +1,6 @@ -import * as React from 'react'; -import * as ReactDOM from 'react-dom'; -import * as ReactDOMServer from 'react-dom/server'; +import * as React from "react"; +import * as ReactDOM from "react-dom"; +import * as ReactDOMServer from "react-dom/server"; import styled, { css, @@ -14,7 +14,8 @@ import styled, { withTheme, ThemeConsumer, StyledComponent, -} from 'styled-components'; + ThemedStyledComponentsModule +} from "styled-components"; /** * general usage @@ -72,8 +73,8 @@ const TomatoButton = styled(MyButton)` const CustomizableButton = styled(MyButton)` /* Adapt the colors based on primary prop */ - background: ${props => (props.primary ? 'palevioletred' : 'white')}; - color: ${props => (props.primary ? 'white' : 'palevioletred')}; + background: ${props => (props.primary ? "palevioletred" : "white")}; + color: ${props => (props.primary ? "white" : "palevioletred")}; font-size: 1em; margin: 1em; @@ -86,7 +87,7 @@ const example = css` font-size: 1.5em; text-align: center; color: ${props => props.theme.primary}; - border-color: ${'red'}; + border-color: ${"red"}; `; const fadeIn = keyframes` @@ -99,15 +100,15 @@ const fadeIn = keyframes` `; const animationRule = css` - ${fadeIn} 1s infinite alternate; + ${fadeIn} 1s infinite alternate; `; const ComponentWithKeyframe = styled.div` - animation: ${animationRule}; + animation: ${animationRule}; `; const theme = { - main: 'mediumseagreen', + main: "mediumseagreen" }; const ExampleGlobalStyle = createGlobalStyle` @@ -130,7 +131,7 @@ class Example extends React.Component { Hello World, this is my first styled component! - + @@ -143,13 +144,13 @@ class Example extends React.Component { // css which only uses simple interpolations without functions const cssWithValues1 = css` - font-size: ${14} ${'pt'}; + font-size: ${14} ${"pt"}; `; // css which uses other simple interpolations without functions const cssWithValues2 = css` ${cssWithValues1} ${[cssWithValues1, cssWithValues1]} - font-weight: ${'bold'}; + font-weight: ${"bold"}; `; // css which uses function interpolations with common props @@ -168,7 +169,7 @@ const styledButton = styled.button` ${() => [cssWithFunc1, cssWithFunc2]} `; -const name = 'hey'; +const name = "hey"; const ThemedMyButton = withTheme(MyButton); @@ -216,7 +217,7 @@ const ComposedLink = () => ( // Create a react component that renders an which is // centered, palevioletred and sized at 1.5em -const LinkFromString = styled('a')` +const LinkFromString = styled("a")` font-size: 1.5em; text-align: center; color: palevioletred; @@ -235,10 +236,10 @@ interface LinkProps { canClick: boolean; } -const LinkFromStringWithProps = styled('a')` +const LinkFromStringWithProps = styled("a")` font-size: 1.5em; text-align: center; - color: ${(a: LinkProps) => (a.canClick ? 'palevioletred' : 'gray')}; + color: ${(a: LinkProps) => (a.canClick ? "palevioletred" : "gray")}; `; // A LinkFromStringWithProps instance should be backed by an HTMLAnchorElement @@ -251,10 +252,10 @@ const MyOtherComponentWithProps = () => ( // Create a react component that renders an // which takes extra props passed as a generic type argument -const LinkFromStringWithPropsAndGenerics = styled('a')` +const LinkFromStringWithPropsAndGenerics = styled("a")` font-size: 1.5em; text-align: center; - color: ${a => (a.canClick ? 'palevioletred' : 'gray')}; + color: ${a => (a.canClick ? "palevioletred" : "gray")}; `; // A LinkFromStringWithPropsAndGenerics instance should be backed by an HTMLAnchorElement @@ -274,19 +275,19 @@ interface ObjectStyleProps { } const functionReturningStyleObject = (props: ObjectStyleProps) => ({ - padding: props.size === 'big' ? '10px' : 2, + padding: props.size === "big" ? "10px" : 2 }); const ObjectStylesBox = styled.div` ${functionReturningStyleObject} ${{ - backgroundColor: 'red', + backgroundColor: "red", // Supports nested objects (pseudo selectors, media queries, etc) - '@media screen and (min-width: 800px)': { - backgroundColor: 'blue', + "@media screen and (min-width: 800px)": { + backgroundColor: "blue" }, - fontSize: 2, + fontSize: 2 }}; `; @@ -298,11 +299,11 @@ const ObjectStylesBox = styled.div` const AttrsInput = styled.input.attrs({ // we can define static props - type: 'password', + type: "password", // or we can define dynamic ones - margin: (props: any) => (props.size as string) || '1em', - padding: (props: any) => (props.size as string) || '1em', + margin: (props: any) => (props.size as string) || "1em", + padding: (props: any) => (props.size as string) || "1em" })` color: palevioletred; font-size: 1em; @@ -315,11 +316,15 @@ const AttrsInput = styled.input.attrs({ `; // https://github.com/DefinitelyTyped/DefinitelyTyped/issues/30042 -const AttrsWithOnlyNewProps = styled.h2.attrs({ as: 'h1' })` - color: ${props => props.as === 'h1' ? 'red' : 'blue'}; - font-size: ${props => props.as === 'h1' ? 2 : 1}; +const AttrsWithOnlyNewProps = styled.h2.attrs({ as: "h1" })` + color: ${props => (props.as === "h1" ? "red" : "blue")}; + font-size: ${props => (props.as === "h1" ? 2 : 1)}; `; +const AttrsInputExtra = styled(AttrsInput).attrs({ autoComplete: "off" })``; + +; + /** * component type */ @@ -356,14 +361,14 @@ const ThemedButton = styled.button` // Define our `fg` and `bg` on the theme const theme2 = { - fg: 'palevioletred', - bg: 'white', + fg: "palevioletred", + bg: "white" }; // This theme swaps `fg` and `bg` const invertTheme = ({ fg, bg }: { fg: string; bg: string }) => ({ fg: bg, - bg: fg, + bg: fg }); const MyApp = ( @@ -386,7 +391,7 @@ class MyComponent extends React.Component> { render() { const { theme } = this.props; - console.log('Current theme: ', theme); + console.log("Current theme: ", theme); return

Hello

; } @@ -400,7 +405,7 @@ const ThemedMyComponent = withTheme(MyComponent); // ref; // }}/>; const themedRef = React.createRef(); -; +; interface WithThemeProps { theme: { @@ -415,12 +420,10 @@ const Component = (props: WithThemeProps) => ( const ComponentWithTheme = withTheme(Component); -; // ok -; // ok +; // ok +; // ok - - {(theme) => } -; +{theme => }; /** * isStyledComponent utility @@ -439,7 +442,7 @@ class ClassComponent extends React.Component { isStyledComponent(StyledComponent); isStyledComponent(StatelessComponent); isStyledComponent(ClassComponent); -isStyledComponent('div'); +isStyledComponent("div"); /** * server side rendering @@ -470,7 +473,7 @@ const css2 = sheet2.getStyleElement(); const sheet3 = new ServerStyleSheet(); const appStream = ReactDOMServer.renderToNodeStream(Hello world); const wrappedCssStream: NodeJS.ReadableStream = sheet3.interleaveWithNodeStream( - appStream, + appStream ); /** @@ -511,10 +514,10 @@ class Random extends React.Component { } } -const WithComponentH2 = WithComponentH1.withComponent('h2'); -const WithComponentAbbr = WithComponentH1.withComponent('abbr'); +const WithComponentH2 = WithComponentH1.withComponent("h2"); +const WithComponentAbbr = WithComponentH1.withComponent("abbr"); -const WithComponentAnchor = WithComponentH1.withComponent('a'); +const WithComponentAnchor = WithComponentH1.withComponent("a"); const AnchorContainer = () => ( withComponent Anchor @@ -524,37 +527,41 @@ const AnchorContainer = () => ( const WithComponentRandomHeading = WithComponentH1.withComponent(Random); const WithComponentCompA: React.SFC<{ a: number; className?: string }> = ({ - className, + className }) =>
; const WithComponentCompB: React.SFC<{ b: number; className?: string }> = ({ - className, + className }) =>
; const WithComponentStyledA = styled(WithComponentCompA)` color: ${(props: { color: string }) => props.color}; `; const WithComponentFirstStyledA = styled(WithComponentStyledA).attrs({ - a: 1, + a: 1 })``; const WithComponentFirstStyledB = WithComponentFirstStyledA.withComponent( - WithComponentCompB, + WithComponentCompB ); -const WithComponentFirstStyledANew = styled(WithComponentStyledA).attrs(props => ({ a: 1 }))``; +const WithComponentFirstStyledANew = styled(WithComponentStyledA).attrs( + props => ({ a: 1 }) +)``; const test = () => [ - , - , - , + , + , + ]; -const WithComponentRequired = styled((props: { to: string }) => )``; +const WithComponentRequired = styled((props: { to: string }) => ( + +))``; // These tests pass in tsservice, but they fail in dtslint. I do not know why. // ; // $ExpectError // ; -const WithComponentRequired2 = WithComponentRequired.withComponent('a'); +const WithComponentRequired2 = WithComponentRequired.withComponent("a"); // These tests pass in tsservice, but they fail in dtslint. I do not know why. // ; // ; // $ExpectError @@ -569,7 +576,7 @@ const asTest = ( ); interface TestContainerProps { - size: 'big' | 'small'; + size: "big" | "small"; test?: boolean; } const TestContainer = ({ size, test }: TestContainerProps) => { @@ -581,7 +588,7 @@ const StyledTestContainer = styled(TestContainer)` `; interface Test2ContainerProps { - type: 'foo' | 'bar'; + type: "foo" | "bar"; } class Test2Container extends React.Component { render() { @@ -592,28 +599,175 @@ class Test2Container extends React.Component { const containerTest = ( // TODO (TypeScript 3.2): once the polymorphic overload is un-commented-out this should be the correct test // - + ); // 4.0 refs -const divFnRef = (ref: HTMLDivElement|null) => { /* empty */ }; +const divFnRef = (ref: HTMLDivElement | null) => { + /* empty */ +}; const divRef = React.createRef(); const StyledDiv = styled.div``; -; -; -; // $ExpectError +; +; +; // $ExpectError const StyledStyledDiv = styled(StyledDiv)``; -; -; -; // $ExpectError +; +; +; // $ExpectError -const StyledA = StyledDiv.withComponent('a'); -; // $ExpectError - { - // $ExpectType HTMLAnchorElement | null - ref; -}}/>; +const StyledA = StyledDiv.withComponent("a"); +; // $ExpectError + { + // $ExpectType HTMLAnchorElement | null + ref; + }} +/>; + +async function typedThemes() { + const theme = { + color: "green" + }; + + // abuse "await import(...)" to be able to reference the styled-components namespace + // without actually doing a top level namespace import + const { + default: styled, + css, + createGlobalStyle, + ThemeProvider, + ThemeConsumer + } = (await import("styled-components")) as ThemedStyledComponentsModule< + typeof theme + >; + + const ThemedDiv = styled.div` + background: ${props => { + // $ExpectType string + props.theme.color; + // $ExpectType number | undefined + props.tabIndex; + return props.theme.color; + }}; + `; + const ThemedDiv2 = styled.div(props => { + // $ExpectType string + props.theme.color; + // $ExpectType number | undefined + props.tabIndex; + + return { + background: props.theme.color + }; + }); + const ThemedDiv3 = styled.div(props => { + // $ExpectType string + props.theme.color; + // $ExpectType number | undefined + props.tabIndex; + + return css` + background: ${props.theme.color}; + `; + }); + const themedCss = css` + background: ${props => { + // $ExpectType string + props.theme.color; + // $ExpectType "theme" + type Keys = keyof typeof props; + return props.theme.color; + }}; + `; + const ThemedDiv4 = styled.div(themedCss); + + const themedCssWithNesting = css(props => ({ + color: props.theme.color, + [ThemedDiv3]: { + color: "green" + } + })); + + const Global = createGlobalStyle` + ${themedCssWithNesting} + `; + + return ( + + <> + + + + + + + {theme => { + // $ExpectType string + theme.color; + return theme.color; + }} + + + + ); +} + +async function reexportCompatibility() { + const sc = await import("styled-components"); + const themed = sc as ThemedStyledComponentsModule; + + let { ...scExports } = sc; + let { ...themedExports } = themed; + // both branches must be assignable to each other + if (Math.random()) { + scExports = themedExports; + } else { + themedExports = scExports; + } +} + +async function themeAugmentation() { + interface BaseTheme { + background: string; + } + interface ExtraTheme extends BaseTheme { + accent: string; + } + + const base = (await import("styled-components")) as ThemedStyledComponentsModule< + BaseTheme + >; + const extra = (await import("styled-components")) as ThemedStyledComponentsModule< + ExtraTheme, + BaseTheme + >; + + return ( + + <> + base} // $ExpectError + > + {() => null} + + ({ + ...base, + accent: "blue" + })} + > + {() => null} + + + + ); +}