# ray-page-container

* 简单使用react进行演示demo开发，主要用于组件功能展示。

* 包含一级导航（顶部）、二级导航（上下或者左右）等。

## install

npm i --save ray-page-container

## usage

### basic use

-- entry.js

```js
import React, { Component } from 'react';
import { render } from 'react-dom';
import Container from 'ray-page-container';

import Home from './Home';
import About from './About';
import WithSub from './WithSub';
import WithSub2 from './WithSub2';

const navs = [
  { key: 'home', text: 'home', component: <Home /> },
  { key: 'about', text: 'about', component: <About /> },
  { key: 'withsub_h', text: 'withsub_h', component: <WithSub /> },
  { key: 'withsub_v', text: 'withsub_v', component: <WithSub2 /> }
];

class Entry extends Component {

  render() {
    // onNavChange: (navKey, contentHtml) => {}
    // add isFullContent
    return (
      <Container
        navs={navs}
        defaultNav="home"
        logo="ray-page-container"
      />
    );
  }
}

render(<Entry />, document.getElementById('__page__'));

if (module.hot){
  module.hot.accept();
}
```

-- demos

```js
// Home.js
class Home extends Component {
  render() {
    return (
      <div style={{ background: '#c8c8ff' }}>
        home
      </div>
    );
  }
}

// About.js
class About extends Component {
  render() {
    return (
      <div style={{ background: '#b1b173' }}>
        about
      </div>
    );
  }
}

// Actions.js
import { ActionItem } from 'ray-page-container';

class Actions extends Component {
  constructor(props) {
    super(props);
    this.state = {
      name: props.defaultValue || '',
      range: 1
    };
  }

  onChange = (e) => {
    const value = e.target.value;
    this.setState({
      name: value
    });
    this.props.onPropsChange({
      type: 'name',
      value
    });
  }

  onChangeNumber = (e) => {
    const value = e.target.value;
    this.setState({
      range: value
    });
    this.props.onPropsChange({
      type: 'range',
      value
    });
  }

  render() {
    const { name, range } = this.state;
    return (
      <div>
        <ActionItem label="修改名称">
          <input value={name} onChange={this.onChange} />
        </ActionItem>
        <ActionItem label="显示值" extra={range}>
          <input type="range" value={range} onChange={this.onChangeNumber} />
        </ActionItem>
      </div>
    );
  }
}

// Demo.js
import { DemoContent } from 'ray-page-container';
import Actions from './Actions';
import DemoInner from './DemoInner';

class Demo extends Component {
  static propTypes = {
    name: PropTypes.string
  };

  render(){
    const { name } = this.props;
    return (
      <DemoContent
        title={`${name}: props设置`}
        descr="demo 描述信息"
        actions={<Actions />}
      >
        <DemoInner name={name} />
      </DemoContent>
    );
  }
}

export default Demo;

// DemoInner.js
const DemoInner = ({ name }) => {
  return (
    <div>
      {name}
    </div>
  );
};

DemoInner.propTypes = {
  name: PropTypes.string
};

export default DemoInner;

// WithSub.js
import { SubContainer } from 'ray-page-container';

import Demo from './Demo';

const navs = [
  { key: 'demo1', text: 'demo1', component: <Demo name="demo1" /> },
  { key: 'demo2', text: 'demo2', component: <Demo name="demo2" /> },
  { key: 'demo3', text: 'demo3', component: <Demo name="demo3" /> }
];

class WithSub extends Component {
  render() {
    // onNavChange: (navKey, contentHtml) => {}
    return (
      <SubContainer
        defaultNav="demo1"
        navs={navs}
        derection="h"
      />
    );
  }
}

// WithSub2
import { SubContainer } from 'ray-page-container';

import Demo from './Demo';

const VDemo1 = (props) => {
  return <Demo name="vdemo1" {...props} />
}
const VDemo2 = (props) => {
  return <Demo name="vdemo2" {...props} />
}
const VDemo3 = (props) => {
  return <Demo name="vdemo3" {...props} />
}

const navs = [
  { key: 'vdemo1', text: 'vdemo1', component: <VDemo1 /> },
  { key: 'vdemo2', text: 'vdemo2', component: <VDemo2 /> },
  { key: 'vdemo3', text: 'vdemo3', component: <VDemo3 /> }
];

class WithSub2 extends Component {
  render() {
    return (
      <SubContainer
        defaultNav="vdemo1"
        navs={navs}
        derection="v"
      />
    );
  }
}

// WithSub3
import { SubContainer, SeeSrcCode } from 'ray-page-container';

import Demo from './Demo';

const VDemo1 = (props) => {
  return <Demo name="vdemo1" {...props} />
}
const VDemo2 = (props) => {
  return <Demo name="vdemo2" {...props} />
}
const VDemo3 = (props) => {
  return <Demo name="vdemo3" {...props} />
}

const navs = [
  { key: 'vdemo1', text: 'vdemo1', component: <VDemo1 /> },
  {
    key: 'group', text: '菜单组', children: [
      { key: 'vdemo2', text: 'vdemo2', component: <VDemo2 /> },
      { key: 'vdemo3', text: 'vdemo3', component: <VDemo3 /> }
    ]
  }
];

class WithSub3 extends Component {
  render() {
    return (
      <SubContainer
        defaultNav="vdemo1"
        navs={navs}
        derection="v"
        useNav
      >
        <SeeSrcCode onClick={this.onClick} />
      </SubContainer>
    );
  }
}
```

-- use demoitem 主要是用于有多个并列的demo时使用，同时默认设置外层 **div** `style={{ width: '30rem', height: '30rem' }}`

```js
import { connectDemoItem } from 'ray-page-container';
import Demo from './Demo';

// 方式一
const WrappedDemo = connectDemoItem(Demo);

// 方式二

@connectDemoItem
class Demo2 extends Component {
  render() {
    return (
      <SomComponent
        {...this.props}
      />
    );
  }
}

```

> 注意： 其中 `DemoContent` 组件的`children` 必须是一个`Component`才能将具体的props进行注入。
> 说明： `DemoContent` 将会给传入的 `actions` 注入 `onPropsChange`,用于统一处理 demo 组件的props属性变化。`onPropsChange({ type: string, value: any }): void`

### 单独使用 SingleContainer

> 菜单悬浮在主内容之上，内容区占满整个屏幕。支持设置菜单显示在左侧或者右侧。
> 支持自定义的navContent，系统提供 `SingleNav 和 Nav` 两种菜单方式，其中 `Nav` 支持子菜单

#### 使用默认的 `SingleNav`

```js
import React, { Component } from 'react';
import { render } from 'react-dom';
import { SingleContainer } from 'ray-page-container';

import Home from './Home';
import About from './About';

const navs = [
  { key: 'home', text: 'home', component: <Home /> },
  { key: 'about', text: 'about', component: <About /> }
];

class Entry extends Component {

  render() {
    // position 默认 `left`，可选值为 `left/right`
    // logo 设置为 null 则禁用 logo
    // onNavChange: (navKey, contentHtml) => {}
    return (
      <SingleContainer
        navs={navs}
        defaultNav="home"
        logo="ray-page-container"
        position="left"
      />
    );
  }
}

render(<Entry />, document.getElementById('__page__'));

if (module.hot){
  module.hot.accept();
}

```

#### 使用具有子菜单的 `Nav`

```js
import React, { Component } from 'react';
import { render } from 'react-dom';
import { SingleContainer, Nav } from 'ray-page-container';

import Demo from './Demo';
import DemoCode from './DemoCode';

const navs = [
  { key: 'demo1', text: 'demo1', component: <Demo name="demo1" /> },
  { key: 'demo-group', text: 'demo组', children: [
    { key: 'demo2', text: 'demo2', component: <Demo name="demo2" /> },
    { key: 'DemoCode', text: 'DemoCode', component: <DemoCode name="demoCode" /> }
  ] }
];

class Entry extends Component {

  render() {
    // position 默认 `left`，可选值为 `left/right`
    // logo 设置为 null 则禁用 logo
    // onNavChange: (navKey, contentHtml) => {}
    // navKey: 用于控制当前选中的 nav
    return (
      <SingleContainer
        navs={navs}
        defaultNav="home"
        logo="ray-page-container"
        position="left"
        navContent={Nav}
      />
    );
  }
}

render(<Entry />, document.getElementById('__page__'));

if (module.hot){
  module.hot.accept();
}

```

#### SingleNav / Nav / Nav2 或者自定义的菜单说明

在 `SingleContainer` 中使用自定义菜单时，默认会给菜单组件注入 `navs、currentKey、onNavChange` 三个属性。

* `navs` 是数组，用于渲染菜单；
* `currentKey` 表示当前选中的菜单，可以用来修改选中菜单的颜色；
* `onNavChange` 菜单切换回调，参数为 `(currentKey, contentRef)`。

Nav 组件，除了默认prop之外，还支持 `dark、defaultOpenKeys` 配置

`import { SingleContainer, Nav, Nav2, SingleNav } from 'ray-page-container';`

`SingleContainer` 中，`navContent` 默认采用的 是 `SingleNav`

```js
<SingleContainer
  navs={navs}
  defaultNav="home"
  logo="ray-page-container"
  position="left"
  navContent={Nav}
  navProps={{ dark: true, defaultOpenKeys: ['1', '2'] }}
/>
```

### 使用 Prismjs 进行 code 格式化

采用 `prismjs` 进行代码美化，默认已经导入具体的模块包

[配置信息](http://prismjs.com/)，使用时，需自行引入 themes

```js
import PrismCode from 'ray-page-container/lib/PrismCode';
import 'prismjs/themes/prism.css';

<PrismCode lang="js">
{`
  <button>xxx</button>
`}
</PrismCode>
```

直接使用 `FmtCode`: 仅支持传入 `code、lang`

```js
import FmtCode from 'ray-page-container/lib/FmtCode';

const code = `<button>xxx</button>`;

<FmtCode lang="js" code={} />
```

### 使用 `FmtCode2` 对代码进行美化

采用 `ray-doc-render` 进行codeBeauty，可以避免 prismjs 进行全局污染

```js
import FmtCode2 from 'ray-page-container/lib/FmtCode2';

const code = `<button>xxx</button>`;

<FmtCode2 lang="js" code={} />
```

### 使用 DemoCodeContent 进行案例显示

默认采用 `左中右` 结构展示，左边为 actions 中间为 案例，右侧为 文档

支持 `children` 为 一个 `function`, 如： `(options) => <MyDemo {...options} />`, 如果设置 `useOption=true` 时，则是如下示例： `(args) => <MyDemo options={args.options} />`

```js

import { DemoCodeContent } from 'ray-page-container';

// useOption = true, 则 DemoInner 注入的props 为 `options={options}`。
// useOption = false, 则 DemoInner 注入的props 为 `{ ...options }`。

<DemoCodeContent
  title={`${name}: props设置`}
  descr="demo 描述信息"
  actions={<Actions />}
  doc={`//something`}
>
  <DemoInner name={name} />
</DemoCodeContent>
```

### 使用 DemoActuator 进行案例显示

默认采用 `左右` 结构展示，左边为 actions，右侧为 案例

```js

import { DemoActuator } from 'ray-page-container';

// useOption = true, 则 DemoInner 注入的props 为 `options={options}`。
// useOption = false, 则 DemoInner 注入的props 为 `{ ...options }`。

<DemoActuator
  title={`${name}: props设置`}
  descr="demo 描述信息"
  actions={<Actions />}
>
  <DemoInner name={name} />
</DemoActuator>
```

### 使用 【codeTools/openCode】 查看源码

如果需要显示的源码，进行美化，默认采用 prismjs 进行code fmt。

需要自行设置 `window.__pluginPrefix__`。如：
将 prismjs 的插件，放在 `/plugins` 目录下，则需要设置为 `window.__pluginPrefix__ = '/plugins'`。具体目录结构：

```conf
...
--libs
--index.html
...
--plugins
  |
  --prismjs
    |
    -- prismjs.js
    -- /themes/prism.css
```

```js
import { openCode } from 'ray-page-container/lib/codeTools';

const code = `
// 源码
`;

openCode('标题', code);
```

### 使用 【rayCodeTools/openCode】 查看源码

采用 `ray-doc-render` 进行 codeBeauty，无需引入其它插件

```js
import { openCode } from 'ray-page-container/lib/rayCodeTools';

const code = `
// 源码
`;

// 第三个参数，表示采用什么语言，可选值:
// markup、properties、conf、xml、html、css、js、sh、bash、java
// sass|scss|less 默认采用 css，javascript|jsx 默认采用 js，xhtml -> html，yaml -> conf
openCode('标题', code, 'js');
```

### copy to clipboard

`copy(value, showResult, cb);`

```js
import copy from 'ray-page-container/lib/copyTool';

copy('hello');

// or
window._clipboardValue = 'hello man!';
copy();
```

## changelog

### 2024-6-23 v1.3.0

1. 去除低版本 react 中的 func，支持 `react16.x` 所有版本

### 2021-6-17 v1.2.5

1. modify `sub container`

### 2021-6-15 v1.2.4

1. add `DemoActuator`

### 2021-3-16 v1.2.1

1. modify maincolor `white`

### 2020-11-19 v1.2.0

1. add `wrappedSubContainer`

### 2020-7-28 v1.1.9

1. modify reset css

### 2020-7-6 v1.1.8

1. modify `pre` css。
2. modify `DemoContent、DemoCodeContent` actions bugs

### 2020-5-27 v1.1.7

1. modify `SubContainer` bugs `onChange`。

### 2020-5-26 v1.1.6

1. modify `SubContainer` bugs `children nav genContent is null`。

### 2020-5-26 v1.1.5

1. modify `SubContainer`, add `children、useNav、navProps` props。

### 2020-5-13 v1.1.4

1. modify `code` pre styles

### 2020-4-14~5-13 v1.1.3

1. 去除强制依赖 `prismjs`，如果使用 `import { openCode } from 'ray-page-container/lib/codeTools';`
   进行源码展示，需自行安装 `prismjs` : `npm i --save-dev prismjs@1.15.0`
2. `FmtCode、FmtCode2` add default `lang='js'`

### 2020-3-31 v1.1.2

1. add `Nav2`

### 2020-1-3 v1.1.0

1. 将 `PrismCode` 和 `FmtCode` 从默认模块中移除，如果需要使用该组件，则采用直接导入的方式使用
2. 将默认的 `DemoCodeContent 和 DemoContent` 中采用的 代码格式化替换为 `FmtCode2`
3. 添加 `rayCodeTools` 用于替换原有 `prismjs` 源码格式化

## Lecense

MIT
