npm package: @mightifier/mighty-client
Description: Client logic, react components and utility methods.
Latest version: 0.4.4

Client logic, react components and utility methods.

## Installation

```bash
npm install --save @mightifier/mighty-client
```

Import react components:

```javascript static
import { MightyProvider } from "@mightifier/mighty-client";
```

Import utils:

```javascript static
import { createAsyncAction } from "@mightifier/mighty-client/utils/redux";
import { post, get, put } from "@mightifier/mighty-client/utils/http";
```

Import higher-order react components:

```javascript static
import { withBreakpoints } from "@mightifier/mighty-client/hoc";
```

Import react hooks:

```javascript static
import { usePromise } from "@mightifier/mighty-client/hooks";
```

## Components

**MightyProvider**

Required at the top of any ui-kit component.

```jsx static
import ReactDOM from "react-dom";
import { MightyProvider } from "@mightifier/mighty-client";
import App from "./App";

ReactDOM.render(
  <MightyProvider
    propsFromMediaQueries={{
      isMobile: "(max-width: 767px)",
      isTablet: "(min-width: 768px) and (max-width: 991px)",
      isDesktop: "(min-width: 992px)"
    }}
    styles={{
      DirectedGraph: {
        backgroundColor: "blue"
      }
    }}
  >
    <App />
  </MightyProvider>
);
```

See [other components](https://mightifier.gitlab.io/mightify-kit/packages/client/public).

## High Order Components

**withBreakpoints**

High order component which will inject RWD properties base on screen resolution and media queries provided by `MightyProvider`

```jsx static
import { withBreakpoints } from "@mightifier/mighty-client/hoc";

const ExampleComponent = ({ isMobile, isTablet, isDesktop }) => (
    <div>Hi from {isMobile ? "Mobile" : isTablet ? "Tablet" : "Desktop"}!</div>
));

export default withBreakpoints(ExampleComponent)
```

**withGoogleAnalyticsTracker**

High order component which will track movement between pages and sync it to google analytics.

Important:

1. This method is not initializing google analytics, you have to do it mannually.
2. Component wrapped by this hoc have to be either a child of `<Route>` component or wrapped by `withRouter` from `react-router` as `withGoogleAnalyticsTracker` requires `location` property to be passed.

```jsx static
import { withGoogleAnalyticsTracker } from "@mightifier/mighty-client/hoc";
import { withRouter } from "react-router-dom";

const Page = () => (
    <div>Root page navigated by !react-router!</div>
));

export default withRouter(withGoogleAnalyticsTracker(Page))
```

## Hooks

**usePromise**

Utility hook for remote data fetching.

Parameters:

- `promiseFactory` **[Function][5]** Factory that will return a promise
- `initResult` Result available before promise will resolve

Returns an array with items accordingly:

- Result of a promise mapped by `resultMapper`
- Bolean value indicating that promise is still resolving
- Function handler to reinvoke the promise on demand

```javascript static
import { useCallback } from "react";
import { usePromise } from "@mightifier/mighty-client/hooks";

/*
  Response shape: 
  {
    status: number,
    payload: {
      id: string,
      name: string
    }
  }
*/
const fetchItem = id => fetch(`/item/${id}`).then(res => res.json());

const initialItem = {};

const Item = ({ id }) => {
  const fetchItemFactory = useCallback(() => fetchItem(id), [id]);

  const [{ id, name }, isFetching, reInvoke] = usePromise(
    fetchItemFactory,
    initialItem
  );

  if (isFetching) return "Loading...";

  return (
    <div>
      <div>{id}</div>
      <div>{name}</div>
      <button onClick={reInvoke}>Load once again</button>
    </div>
  );
};
```

## Utility methods

### Redux

**createAsyncAction**

Creates async redux action

Parameters:

- `baseType` **[String][1]** Action base type that will be used as a prefix for other actions
- `apiCall` **[Function][5]** Function that will return promise. First argument passed to this method is a value passed to action while invoking, and second one is a `getState` method from the store.

Returns **[Promise][2]** promise which will dispatch redux actions. Special `TYPES` enum with action types will be available in returned function prototype.

```javascript static
import { createAsyncAction } from "@mightifier/mighty-client/utils/redux";

const action = createAsyncAction("STUDENTS", (id, getState) =>
  fetch(`/students/${id}`)
);
//usage
await action(1);

//TYPES

// returns 'STUDENTS_REQUEST'
action.TYPES.REQUEST;

// returns 'STUDENTS_FAILURE'
action.TYPES.FAILURE;

// returns 'STUDENTS_SUCCESS'
action.TYPES.SUCCESS;
```

**pendingActionsReducer**

Reducer storing pending actions

**reduceActions**

Reduce multiple redux actions to single result

Parameters:

- `actions` **[Array][6]** Array of redux actions objects
- `reducer` **[Function][5]** Redux reducer
- `initialState` Initial state for reducer

```javascript static
import { reduceActions } from "@mightifier/mighty-client/utils/redux";

const reducer = (state = 0, { type }) => {
  switch (type) {
    case "inc":
      return state + 1;
    case "dec":
      return state - 1;
    default:
      return state;
  }
};

// returns 3
reduceActions([{ type: "inc" }, { type: "inc" }, { type: "dec" }], reducer, 2);
```

### Http

Tiny abstraction on top of fetch API. Each helper method is curried and returns a **[Promise][2]** that resolves to the `requestResult` object, whether it is successful or not.

**get**

Perform HTTP GET action.

Parameters:

- `url` **[String][1]**
- `options` **[Object][3]** An options object containing any custom settings that you want to apply to the request.
- `query` **[Object][3]** Object that will be transformed to query params.

**post**

Perform HTTP POST action.

Parameters:

- `url` **[String][1]**
- `options` **[Object][3]** An options object containing any custom settings that you want to apply to the request.
- `body` **[Object][3]** Object that will be transformed to request body.

**put**

Perform HTTP PUT action.

Parameters:

- `url` **[String][1]**
- `options` **[Object][3]** An options object containing any custom settings that you want to apply to the request.
- `body` **[Object][3]** Object that will be transformed to request body.

**patch**

Perform HTTP PATCH action.

Parameters:

- `url` **[String][1]**
- `options` **[Object][3]** An options object containing any custom settings that you want to apply to the request.
- `body` **[Object][3]** Object that will be transformed to request body.

**remove**

Perform HTTP DELETE action.

Parameters:

- `url` **[String][1]**
- `options` **[Object][3]** An options object containing any custom settings that you want to apply to the request.
- `body` **[Object][3]** Object that will be transformed to request body.

**createResource**

Returns object with all HTTP methods applying parameters to all of them at ones.

Parameters:

- `url` **[String][1]**
- `optionsFactory` **[Function][5]** A function which will return options object containing any custom settings that you want to apply to the request.

```javascript static
import { createResource } from "@mightifier/mighty-client/utils/http";

const roundResource = createResource("/round", () => ({
  headers: {
    authorization: "token"
  }
}));

const getResult = await roundResource.get({});
const postResult = await roundResource.post({});
```

### Localization

**allowedRegions**

Dictionary with supported user regions.

**getRegion**

Returns promise which resolves with user region based on his geo-localization using [ipstack](https://ipstack.com/).
Both Americas returns `us` region and everything else `eu`.

Parameters:

- `ipStackAccessKey` **[String][1]** Ipstack access key.

```javascript static
const userRegion = await getRegion("042f325d-cb0b-46e");
```

### StyledComponents

**applyStyles**

Inverted styled components api for better composition. First argument is style object, second is a component to style.

```javascript static
import { applyStyles } from "@mightifier/mighty-client/styledComponents";
import { compose } from "ramda";

const Render = ({ className }) => <div className={className}></div>;

const styles = {
  display: "flex",
  flex: 1
};

export const MyComponent = compose(applyStyles(styles))(Render);
```

## Mixins

A lightweight toolset of mixins for using with `styled-components`

**withCursor**

Returns a CSS formula with `cursor: pointer` if component has `onClick` property

```javascript static
import { withCursor } from "@mightifier/mighty-client/mixins";

const Component = styled.div`
  ${withCursor};
`;
```

**withDisabled**

Returns a CSS formula that represent disabled element if component has `disabled: true` property.

```javascript static
import { withDisabled } from "@mightifier/mighty-client/mixins";

const Component = styled.div`
  ${withDisabled};
`;

/* Css styles:
  opacity: 0.5;
  pointer-events: none;
  cursor: not-allowed;
*/
```

**withEllipsis**

Returns a CSS formula that will ensure ellipsis text overflow when applied to element if component has `ellipsis: true` property.

```javascript static
import { withEllipsis } from "@mightifier/mighty-client/mixins";

const Component = styled.div`
  ${withEllipsis};
`;

/* Css styles:
  white-space: nowrap; 
  overflow: hidden;
  text-overflow: ellipsis;
*/
```

[1]: https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String
[2]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise
[3]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object
[4]: https://mightifier.gitlab.io/mightify-kit/#request
[5]: https://developer.mozilla.org/docs/Web/JavaScript/Reference/Statements/function
[6]: https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Array
[7]: https://developer.mozilla.org/en-US/docs/Web/API/Element
[8]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Boolean
[9]: https://www.styled-components.com/docs/api#styled