# 工具类

工具列表

* [MailToBtn](#MailToBtn)
* [PhoneCallBtn](#PhoneCallBtn)
* [InputFile](#InputFile)
* [Base64Image](#Base64Image)
* [TimeNow](#TimeNow)
* [Marquee](#Marquee)
* [RMediaQuery](#RMediaQuery)
* [Fragment](#Fragment)
* [Version](#Version)
* [DomTitle](#DomTitle)
* [EagleEye](#EagleEye)

## MailToBtn

展示一个 可以发送 email 的链接或者按钮

### MailToBtn 基本使用

直接进行发送邮件

---demo
```js
import { MailToBtn } from 'amos-framework';

ReactDOM.render(<MailToBtn address="admin@admin.com" text="给admin发送邮件" />, _react_runner_);
```
---demoend

| params | type | default | description |
|--------- |-------- |--------- |-------- |
| address | String | - | email收件人地址 |
| text | string | - | 按钮内容 |

## PhoneCallBtn

展示一个 可以 拨打电话 的链接或者按钮

### PhoneCallBtn 基本使用

直接进行拨打电话

---demo
```js
import { PhoneCallBtn } from 'amos-framework';

ReactDOM.render(<PhoneCallBtn phoneNumber="15522223333" text="给小张拨打电话" />, _react_runner_);
```
---demoend

| params | type | default | description |
|--------- |-------- |--------- |-------- |
| phoneNumber | String or number | - | 电话号码 |
| text | string | - | 按钮内容 |

## InputFile

自定义上传图片输入框，不上传至服务器，如果需要上传至服务器，可在 `onChange` 事件中自行扩展

### InputFile 基本使用

选择图片，不进行上传，打开控制台，查看选择的文件

---demo
```js
import { InputFile, Button } from 'amos-framework';

const onInputFileChange = (file) => {
  console.log(file);
}

ReactDOM.render(<InputFile onChange={onInputFileChange}><Button>选择文件</Button></InputFile>, _react_runner_);
```
---demoend

| params | type | default | description |
|--------- |-------- |--------- |-------- |
| prefixCls | string | `amos-input-file` | 自定义样式前缀 |
| className | string | - | 自定义样式名 |
| style | object | - | 根节点标签的样式 |
| component | string | `span` | 外层标签名，如`span、div、p` 等 |
| disabled | boolean | - | 是否禁用 |
| accept | string | - | 接收的文件类型 |
| children | ReactNode | - | 组件内容 |
| onChange | `function: (file) => {}` | - | 选择文件时的回调，可在这里进行文件校验、上传至服务器 |
| error | string or ReactNode | - | 文件校验错误提示 |

## Base64Image

选择图片，同时转化为base64格式，填充至输入框

### Base64Image 基本使用

选择图片，并转为base64格式

---demo
```js
import { Base64Image } from 'amos-framework';

const onBase64ImageChange = (dataUrl, files) => {
  // 当直接改变输入框数据是，参数为 (value)
  console.log(dataUrl);
}

ReactDOM.render(<Base64Image onChange={onBase64ImageChange} />, _react_runner_);
```
---demoend

| params | type | default | description |
|--------- |-------- |--------- |-------- |
| prefixCls | string | `amos-base64-image` | 自定义样式前缀 |
| className | string | - | 自定义样式名 |
| beforeReadFile | `function: (file) => {}` | - | 选择文件读取为base64之前的回调，此处可进行图片校验，返回 `true` 或者 `error 信息`，注意，默认只处理了这两种返回格式，其它返回类型，将会显示为 `未知错误！` |
| onChange | `function: (dataUrl, file) => {}` | - | 文件读取完毕回调，参数为 `base64 dataUrl` 和 `file`文件，如果需要上传文件至服务器，可在此处进行处理 |
| value | string | - | base64文件内容，或者其它自定义内容 |
| accept | string | `image/gif,image/jpeg,image/jpg,image/png,image/svg` | 接受的文件类型 |
| placeholder | string | - | 输入框提示内容 |
| inputProps | object | - | 输入框支持的其它 props |
| btnProps | object | - | 按钮支持的其它 props |

> 该输入框，也可作为直接输入图片地址使用，然后进行持久化存储到数据库等。也可作为将上传的图片转化为 `base64` 格式进行持久化存储到数据库。
> 当直接收入是，回调 onChange 为 `(value) => {}`，当上传图片时，回调 onChange 为 `(dataUrl, file) => {}`。

**注意**，设置 `accept="image/*"`时，Webkit浏览器下对话框显示滞慢或卡顿时，请设置为具体的值，如:`accept="image/gif,image/jpeg,image/jpg,image/png,image/svg"`。

更多MediaTypes，请移步 [IANA Media Types](https://www.iana.org/assignments/media-types/media-types.xhtml)

## TimeNow

### TimeNow 基本使用

动态变化时间

---demo
```js
import { Button, TimeNow } from 'amos-framework';

class Demo extends Component {
  constructor(props) {
    super(props);
    this.state = {
      fmt: 'yyyy年MM月dd日 www HH:mm:ss'
    };
  }

  toggleFmt = () => {
    this.setState(({ fmt }) => ({
      fmt: fmt === 'HH:mm' ? 'yyyy年MM月dd日 www HH:mm:ss' : 'HH:mm'
    }));

  }

  render() {
    return (
      <div style={{ width: '20em', display: 'inline-block' }}>
        <Button onClick={this.toggleFmt}>改变fmt</Button>
        <TimeNow />
        <TimeNow fmt="yyyy-MM-dd HH:mm:ss" />
        <TimeNow fmt="yyyy年MM月dd日 HH:mm:ss" />
        <TimeNow fmt="yyyy-MM-dd 第q季度 www HH:mm:ss" />
        <TimeNow fmt="yyyy-MM-dd q季度 w HH:mm:ss" />
        <TimeNow fmt={this.state.fmt} />
      </div>
    );
  }
}

ReactDOM.render(<Demo />, _react_runner_);
```
---demoend

## TakeupTime

### TakeupTime 基本使用

已运行耗时时间, 格式化仅支持 `HH:mm:ss、HH:mm、mm:ss` 等

---demo
```js
import { Button, TimeNow } from 'amos-framework';

const TakeupTime = TimeNow.TakeupTime;

const btnLabels = {
  start: '停止',
  stop: '重新运行'
};

class Demo extends Component {
  constructor(props) {
    super(props);
    this.state = {
      fmt: 'HH:mm',
      runmode: 'start'
    };

    this.ttRef = React.createRef();
  }

  toggleFmt = () => {
    this.setState(({ fmt }) => ({
      fmt: fmt === 'HH:mm' ? 'HH:mm:ss' : 'HH:mm'
    }));
  }

  ctl = () => {
    this.setState((state) => {
      return {
        runmode: state.runmode === 'start' ? 'stop' : 'start'
      };
    }, () => {
      // start reStart stop
      const { runmode } = this.state;
      // this.ttRef.current[runmode]?.();
      this.ttRef.current[runmode] && this.ttRef.current[runmode]();
    });
  }

  render() {
    const { runmode } = this.state;
    return (
      <div style={{ width: '20em', display: 'inline-block' }}>
        <Button onClick={this.toggleFmt}>改变fmt</Button>
        <Button onClick={this.ctl}>{btnLabels[runmode]}</Button>
        <TakeupTime />
        <div>
          可通过外部控制运行：<TakeupTime ref={this.ttRef} />
        </div>
        <TakeupTime baseElapsedTime={300} />
        <TakeupTime baseElapsedTime={300} fmt="HH:mm:ss" />
        <TakeupTime baseElapsedTime={300} fmt="mm:ss" />
        <TakeupTime fmt={this.state.fmt} />
      </div>
    );
  }
}

ReactDOM.render(<Demo />, _react_runner_);
```
---demoend

## Marquee

### Marquee 基本使用

---demo
```js
import { Marquee } from 'amos-framework';

ReactDOM.render(<Marquee>跑马灯内容...</Marquee>, _react_runner_);
```
---demoend

| params | type | default | description |
|--------- |-------- |--------- |-------- |
| useNative | boolean | - | 是否采用 html5 内置的 `marquee` 标签 |
| speed | String | normal | 滚动速度，当 `useNative=true` 时，不起效。可选值为：normal(18s)， slow(26s)，fast(6s)  |

## RMediaQuery

### RMediaQuery 基本使用

不同的分辨率下，展示不同的结果

---demo
```js
import { RMediaQuery } from 'amos-framework';

class Demo extends Component {
  state = {
    min: false
  };

  componentDidMount() {
    RMediaQuery.register('only screen and (min-width: 0) and (max-width: 992px)', {
      match: () => {
        this.setState({
          min: true
        });
      },
      unmatch: () => {
        this.setState({
          min: false
        });
      }
    });
  }

  render(){
    const { min } = this.state;
    return (
      <div>
        RMediaQuery min: {min ? '小分辨率' : '宽度超过992px'}
      </div>
    );
  }
}

ReactDOM.render(<Demo />, _react_runner_);
```
---demoend

支持的方法

- `register(q, options: { match, unmatch, setup, deferSetup = false }, shouldDegrade)`
- `unregister(q, handler)`

## Fragment

### Fragment 基本使用

仅返回 `children` 的html节点，不需要包裹 `children` 节点， 类似于 `React.Fragment`。

当 `children` 为数组时，又不需要生成新的包裹节点时使用。

---demo
```js
import { Fragment } from 'amos-framework';

ReactDOM.render((
  <Fragment>
    <p>段落1</p>
    <p>段落2</p>
    <p>段落3</p>
    <p>打开 devtools 查看 Elements 元素节点</p>
  </Fragment>
), _react_runner_);
```
---demoend

## Version

### Version 基本使用

---demo
```js
import { Version } from 'amos-framework';

ReactDOM.render((
  <div>
    <Version />
    <div style={{ color: 'red' }}>amos-framework:{Version._version}</div>
    <div style={{ color: 'red' }}>react:{Version.__ReactVersion__}</div>
  </div>
), _react_runner_);
```
---demoend

## DomTitle

props `title`, 默认值为 `document.title`。

### DomTitle 基本使用

输入标题时，查看标签变化。使用中，还可以使用 `defaultTitle` 用于设置当 title 不存在时，默认显示的 title 值。用于页面切换时，下一个页面没使用 DomTitle 的场景。

---demo
```js
import { Input, DomTitle, Button } from 'amos-framework';

class Demo extends Component {
  constructor(props) {
    super(props);
    this.state = {
      title: '默认的标题'
    };
  }

  render() {
    return (
      <DomTitle title={this.state.title} defaultTitle="我是缺省标题">
        <div style={{ width: '22em', display: 'inline-block' }}>
          输入一个新的标题:
          <Input
            value={this.state.title}
            onChange={(e) => this.setState({ title: e.target.value })}
          />
          <Button onClick={() => this.setState({ title: undefined })}>置为默认</Button>
        </div>
      </DomTitle>
    );
  }
}

ReactDOM.render(<Demo />, _react_runner_);
```
---demoend

> 升级： 采用 `DomHtml` 可以更加灵活的使用内嵌 html 相关功能

```js
import { DomHtml } from 'amos-framework';

class App extends Component {

  render() {
    return (
      <div className="application">
        <DomHtml>
          <meta charSet="utf-8" />
          <title>My Title</title>
          <link rel="canonical" href="http://mysite.com/example" />
        </DomHtml>
        ...
      </div>
    );
  }
}
```

[more info](https://www.npmjs.com/package/dom-casque)

## EagleEye

鹰眼的使用

### EagleEye 基本使用

---demo
```js
import { EagleEye } from 'amos-framework';

const { EagleEyeChild } = EagleEye;

const DARK = 'dark';
const RED = 'red';
const YELLOW = 'yellow';

function _wrapBox(type){
  return ({ className, style }) => (
    <div className={`widget-item ${className ? className : ''}`} style={style}>
      <div className={`box ${type}`}>
        <div>{type}</div>
      </div>
    </div>
  );
}

const Dark = _wrapBox(DARK);
const Red = _wrapBox(RED);
const Yellow = _wrapBox(YELLOW);

function random(){
  return {
    left: Math.round(Math.random() * 3000),
    top: Math.round(Math.random() * 100)
  };
}

class Demo extends Component {
  state = {
    cnt: 1
  };

  componentDidMount() {
    // 简易触发位置变化
    setInterval(() => {
      this.setState((prevState) => ({ cnt: 1 + prevState.cnt }));
    }, 2000)
  }

  // rest 中包含 left/top/width/height
  renderChild = ({ node, ...rest }) => {

    const classNames = [YELLOW, RED, DARK];
    let classNameFound = null;

    for (let i = 0, len = node.classList.length; i < len; i++) {
      const className = node.classList[i];
      if (classNames.includes(className)){
        classNameFound = className;
        break;
      }
    }

    if (classNameFound === null) {
      return <EagleEyeChild {...rest} />
    }

    return (
      <div
        style={{
          ...rest,
          position: 'absolute',
          backgroundColor: classNameFound,
        }}
      />
    )
  }

  render() {
    return (
      <EagleEye selector=".box" keepAspectRatio childComponent={this.renderChild}>
        <Dark />
        <Yellow className="pos-rlt" style={random()}/>
        <Red className="pos-rlt" style={{width: 200, left: 4000, top: 100}}/>
        <Yellow className="pos-rlt" style={random()} />
        <Dark className="pos-rlt" style={random()} />
        <Yellow className="pos-rlt" style={{width: 200, left: 2000}} />
        <Dark className="pos-rlt" style={{width: 200, left: 2000, marginTop: 30 }}/>
        <Yellow />
        <Dark />
        <Yellow />
      </EagleEye>
    );
  }
}

ReactDOM.render(<Demo />, _react_runner_);
```
---demoend
