---
name: Component
route: /component/
---

import { Playground } from 'docz';
import { Vertical, Horizontal, Responsive } from '../..'; 
import * as demos from '../../demos'; 

<demos.Star/>

# Component 

GLS can easily work with components without any special requirements, so you can bring your own or add explicit support for `ComponentProps`. We cover these below.

## Bring your own

For example, here is a simple `<input/>` without any gls use: 

<Playground>
  <input placeholder='Example'/>
</Playground>

You can easily place these (raw `input`s) in a [vertical][vertical] and you can see that they get nicely vertically-spaced, and `stretch` nicely (thanks to `Vertical`'s default `horizontalAlign`):

<Playground>
  <Vertical>
    <input placeholder='Example'/>
    <input placeholder='Example'/>
    <input placeholder='Example'/>
  </Vertical>
</Playground>

You can even have a *single* input in a `Vertical` and it essentially stretches (horizontally) to match the parent: 

<Playground>
  <Vertical>
    <input placeholder='Example'/>
  </Vertical>
</Playground>

This allows you to create a reusable (`Vertical>input`) pair that you can provide various [BaseProps][common-types] for (using the `Vertical` in the `Vertical>input` pair).
 
And you can control various props using either of the `Vertical`/`input` e.g. you can `stretch` on the Vertical (as explained in the [sizing principle][principle-sizing]) allowing two streching `Vertical>input`s in a Horziontal: 

<Playground>
  <Horizontal>
    <Vertical sizing="stretch">
      <input placeholder='Example'/>
    </Vertical>
    <Vertical sizing="stretch">
      <input placeholder='Example'/>
    </Vertical>
  </Horizontal>
</Playground>

You can props to the `input` as well e.g. an explicit height can be set: 

<Playground>
  <Vertical sizing="stretch">
    <input placeholder='Example' style={{height: '30px'}}/>
  </Vertical>
</Playground>

Of course you can create a component out of the `Vertical>input` pair. However if you want to compose a component that supports various gls props in a single tag, you can use `gls.ComponentProps` / `gls.component` which we cover next. 

## ComponentProps

`ComponentProps` is a grouping of a set of common props that allow you to hook into the layout principles provided by gls:

* `ClassNameProp`: [Allows you to pass in CSS class names][common-types].
* `ScrollProp`: [Specify scrolling behaviours][common-types].
* `PaddingProp`: [Specifies padding][common-types].
* `SizingProp`: [Specify implicit size control][common-types].
* `SizeProps`: [Specify explicit size control][common-types].
* `StylesProp`: [Allows you to use TypeStyle CSS mixins][common-types]. 


## component
There is `gls.component(props) => props` function which is a simple map:

```
OtherProps + ComponentProps (subset or fullset) + { className? }
  => OtherProps + { className (including existing className if present) }
``` 

The `gls.component` function that takes any set (subset or fullset) of `ComponentProps` and returns props with any `ComponentProps` omitted + processsed and placed into the `className` prop (taking into account any existing `className` as well). You then use the `className` in your component.

We look at examples below.

## Creating a component
You can use any partial set of `ComponentProps` in your component e.g here we just use the `SizeProps`: 

```ts
export interface OnlySomeProps extends
  React.ButtonHTMLAttributes<HTMLButtonElement>,
  gls.SizeProps {
}

export const OnlySomePropsButton: React.FC<OnlySomeProps> = (props) => {
  const processedProps = gls.component(props);

  return <button {...processedProps} />;
}
```

or you can use all the `ComponentProps`: 

```ts
export interface AllTheProps extends
  React.ButtonHTMLAttributes<HTMLButtonElement>,
  gls.ComponentProps {
}
export const AllThePropsButton: React.FC<AllTheProps> = (props) => {
  const processedProps = gls.component(props);

  return <button {...processedProps} />;
}
```

Any props that are not a part of `ComponentProps` are yours to handle and returned as is from `gls.component` e.g. below you can see the `error` prop passing-through the `component` function without any processing: 

```ts
export interface LimitedButtonProps extends
  gls.ComponentProps {
  /** Turns red if in error state */
  error?: boolean
}
export const LimitedButton: React.FC<LimitedButtonProps> = (props) => {
  /** 
   * Generates a className from component props 
   * + returns the rest
   **/
  const {className, error} = gls.component(props);
  
  /** Handle the error */
  const errorStyle = error ? { backgroundColor: 'red' } : {};
  
  return <button className={className} style={errorStyle} />;
}
```

## defaults

The `component` function takes a second `defaults: ComponentProps` argument.

You can use it to provide defaults for any `ComponentProps` e.g. 
it is good practice to have an `input` with default `sizing:'stretch'`([later we also cover best layout practices for components][guidance-components]): 

```ts
export interface DefaultInputProps extends
  React.InputHTMLAttributes<HTMLInputElement>,
  gls.ComponentProps {
}

export const DefaultInput: React.FC<DefaultInputProps> = (props) => {
  const { className, ...otherProps } =
    gls.component(props, { sizing: 'stretch' });

  return <input className={className} {...otherProps} />
};
```

Such an input behaves nicely in common vertical, horizontal and responsive layouts: 

<Playground>
  <Vertical>
    <demos.DefaultInput placeholder="Example"/>
    <demos.DefaultInput placeholder="Example"/>
  </Vertical>
</Playground>

<Playground>
  <Horizontal>
    <demos.DefaultInput placeholder="Example"/>
    <demos.DefaultInput placeholder="Example"/>
  </Horizontal>
</Playground>

<Playground>
  <Responsive breakpoint={650}>
    <demos.DefaultInput placeholder="Example"/>
    <demos.DefaultInput placeholder="Example"/>
  </Responsive>
</Playground>

## Using third party libraries

Since the `component` function just returns a `className` you can easily use its features with any third party components that support the className prop e.g. material-ui

```ts
// Use `gls.component`
const className = gls.component({width: '100%'});

// With any library
import Button from '@material-ui/core/Button';

<Button className={className} variant="contained" color="primary">
  Hello World
</Button>
```

[vertical]:../vertical/
[common-types]:../common-types/
[principle-sizing]:../princile-sizing/
[guidance-components]:../guidance-components/
