├── .eslintrc.json
├── .gitignore
├── .npmignore
├── .travis.yml
├── LICENSE
├── README-ZH.md
├── README.md
├── __sites_
├── .gitignore
├── .travis.yml
├── README.md
├── build
│ └── bundle.js
├── index.html
├── package.json
├── server.js
├── src
│ ├── app.js
│ ├── components
│ │ ├── doc
│ │ │ ├── cn
│ │ │ │ ├── compress-image.js
│ │ │ │ ├── crop-image.js
│ │ │ │ ├── custom-component.js
│ │ │ │ ├── events.js
│ │ │ │ ├── get-started.js
│ │ │ │ ├── home.js
│ │ │ │ ├── multiple-file.js
│ │ │ │ ├── others.js
│ │ │ │ ├── post-data.js
│ │ │ │ ├── props.js
│ │ │ │ └── resize-image.js
│ │ │ └── en
│ │ │ │ ├── compress-image.js
│ │ │ │ ├── crop-image.js
│ │ │ │ ├── custom-component.js
│ │ │ │ ├── events.js
│ │ │ │ ├── get-started.js
│ │ │ │ ├── home.js
│ │ │ │ ├── multiple-file.js
│ │ │ │ ├── others.js
│ │ │ │ ├── post-data.js
│ │ │ │ ├── props.js
│ │ │ │ └── resize-image.js
│ │ ├── ft.js
│ │ ├── hd.js
│ │ └── nav-list.js
│ ├── index.js
│ ├── less
│ │ ├── config.less
│ │ ├── modules
│ │ │ ├── m-button.less
│ │ │ ├── m-icon.less
│ │ │ ├── m-layout.less
│ │ │ ├── m-pages.less
│ │ │ ├── m-pagination.less
│ │ │ ├── m-table.less
│ │ │ ├── m-type.less
│ │ │ └── reset.less
│ │ ├── plugins
│ │ │ ├── angular.progress.less
│ │ │ └── hljs.theme.less
│ │ └── vtui.less
│ └── lib
│ │ ├── constants.js
│ │ └── vendor.js
├── webpack.config.js
└── yarn.lock
├── karma.conf.js
├── package.json
├── react-core-image-upload.js
├── server.js
├── shots
├── react-core-image-upload.jpg
└── vuedba0ed377b88fc84d51026310efcb255b.png
├── src
├── components
│ ├── crop.js
│ └── resize-bar.js
├── index.js
├── lib
│ ├── canvas-helper.js
│ ├── drag.js
│ ├── error-code.js
│ ├── helper.js
│ ├── loading-gif.js
│ ├── resize.js
│ └── xhr.js
├── propTypes.js
├── props.js
├── react-core-image-upload.js
└── style.css
├── tests
└── react-core-image-upload.test.js
├── webpack.config.build.js
└── webpack.config.js
/.eslintrc.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "airbnb",
3 | "plugins": [
4 | "import",
5 | "react"
6 | ],
7 | "env": {
8 | "browser": 1
9 | },
10 | "rules": {
11 | "prefer-template": 0,
12 | "no-console": 0
13 | },
14 | "parserOptions": {
15 | "ecmaVersion": 6,
16 | "sourceType": "module",
17 | "ecmaFeatures": {
18 | "jsx": true
19 | }
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | .DS_Store
2 | .AppleDouble
3 | .LSOverride
4 | Icon
5 | ._*
6 | .Spotlight-V100
7 | .Trashes
8 | Thumbs.db
9 | ehthumbs.db
10 | Desktop.ini
11 | $RECYCLE.BIN/
12 | node_modules/
13 | npm-debug.log
14 | .idea/
15 | test/temp-test
16 | coverage/
--------------------------------------------------------------------------------
/.npmignore:
--------------------------------------------------------------------------------
1 | __site__
2 | shots
3 | tests
4 |
--------------------------------------------------------------------------------
/.travis.yml:
--------------------------------------------------------------------------------
1 | language: node_js
2 | node_js:
3 | - 5.0
4 | - 6.0
5 | - 7.0
6 | script: node_modules/karma/bin/karma start ./karma.conf.js --singleRun
7 | before_install:
8 | - export CHROME_BIN=chromium-browser
9 | - export DISPLAY=:99.0
10 | - sh -e /etc/init.d/xvfb start
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Copyright (c) 2016 Vanthink-UED
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
--------------------------------------------------------------------------------
/README-ZH.md:
--------------------------------------------------------------------------------
1 | # react-core-image-upload
2 |
3 | []()
4 | 
5 | [](https://travis-ci.org/Vanthink-UED/react-core-image-upload)
6 |
7 |
8 |
9 | 一款轻量级的图片上传裁剪组件
10 |
11 | [English Doc]('./README.md')
12 |
13 |
14 | ### 快速开始
15 | 使用 npm
16 | ```bash
17 | npm install react-core-image-upload --save
18 | ```
19 |
20 | 使用 yarn
21 | ``` bash
22 | yarn add react-core-image-upload
23 | ```
24 |
25 | ### 使用ES6 进行开发
26 | ``` js
27 | import React from 'react';
28 | import ReactCoreImageUpload from 'react-core-image-upload';
29 | let App = React.createClass({
30 | //...
31 |
32 | render() {
33 | return(
34 |
",
15 | "license": "MIT",
16 | "dependencies": {
17 | "core-image-xhr": "^1.0.1",
18 | "less-loader": "^2.2.3",
19 | "prop-types": "^15.5.10",
20 | "react": "^15.5.4",
21 | "react-core-image-upload": "^2.2.1",
22 | "react-dom": "^15.5.4",
23 | "react-highlight": "^0.10.0",
24 | "react-highlight.js": "^1.0.5",
25 | "react-router": "^2.8.1"
26 | },
27 | "devDependencies": {
28 | "babel": "^6.5.2",
29 | "babel-core": "^6.17.0",
30 | "babel-loader": "^6.2.5",
31 | "babel-preset-es2015": "^6.16.0",
32 | "babel-preset-react": "^6.16.0",
33 | "history": "^4.3.0",
34 | "less": "^3.8.1",
35 | "react-hot-loader": "^3.0.0-beta.6",
36 | "webpack": "^1.13.2",
37 | "webpack-dev-server": "^1.16.2"
38 | },
39 | "engines": {
40 | "node": ">=4.0.0",
41 | "npm": ">=2.5.1"
42 | }
43 | }
44 |
--------------------------------------------------------------------------------
/__sites_/server.js:
--------------------------------------------------------------------------------
1 | var webpack = require('webpack');
2 | var WebpackDevServer = require('webpack-dev-server');
3 | var config = require('./webpack.config');
4 |
5 | new WebpackDevServer(webpack(config), {
6 | publicPath: config.output.publicPath,
7 | hot: true,
8 | historyApiFallback: true
9 | }).listen(9000, 'localhost', function (err, result) {
10 | if (err) {
11 | return console.log(err);
12 | }
13 |
14 | console.log('Listening at http://localhost:9000/');
15 | });
--------------------------------------------------------------------------------
/__sites_/src/app.js:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react';
2 | import { render } from 'react-dom';
3 | import { Router, Route, hashHistory } from 'react-router';
4 | import Ft from './components/ft';
5 | import Hd from './components/hd';
6 | import Navlist from './components/nav-list';
7 | //pages
8 | import EnHome from './components/doc/en/home';
9 | import CnHome from './components/doc/cn/home';
10 | import CngetStarted from './components/doc/cn/get-started';
11 | import EngetStarted from './components/doc/en/get-started';
12 | import CnProps from './components/doc/cn/props';
13 | import EnProps from './components/doc/en/props';
14 | import CnEvents from './components/doc/cn/events';
15 | import EnEvents from './components/doc/en/events';
16 | import CnCustomComponent from './components/doc/cn/custom-component';
17 | import EnCustomComponent from './components/doc/en/custom-component';
18 | import CnCropImage from './components/doc/cn/crop-image';
19 | import EnCropImage from './components/doc/en/crop-image';
20 | import CnResizeImage from './components/doc/cn/resize-image';
21 | import EnResizeImage from './components/doc/en/resize-image';
22 | import CnCompressImage from './components/doc/cn/compress-image';
23 | import EnCompressImage from './components/doc/en/compress-image';
24 | import CnMultipleFile from './components/doc/cn/multiple-file';
25 | import EnMultipleFile from './components/doc/en/multiple-file';
26 | import CnPostData from './components/doc/cn/post-data';
27 | import EnPostData from './components/doc/en/post-data';
28 | import CnOthers from './components/doc/cn/others';
29 | import EnOthers from './components/doc/en/others';
30 |
31 |
32 | class App extends Component {
33 | render() {
34 | return (
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 | );
70 | }
71 | }
72 |
73 | export default App;
74 |
--------------------------------------------------------------------------------
/__sites_/src/components/doc/cn/compress-image.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import vendor from '../../../lib/vendor';
3 | import ReactCoreImageUpload from '../../../../../src/index';
4 | import Highlight from 'react-highlight.js';
5 |
6 | export default class CnMultipleFile extends React.Component {
7 | constructor(props) {
8 | super(props);
9 | this.state = {
10 | src: 'http://img1.vued.vanthink.cn/vued7553a09a5d5209ebd00a48264394b7f3.png',
11 | };
12 | this.imageUploded = this.imageUploded.bind(this);
13 | }
14 | render() {
15 | return (
16 |
17 |
压缩图片
18 |
设置compress
的数值,你可以在上传之前进行图片的本地压缩。其中 compress 为 0 表示不压缩,数据越大,图片的质量越差,且最大值不能大于100。
19 |
20 |
21 |
29 |
30 |
31 |
代码示例
32 |
{`
40 | `}
41 |
42 |
43 | );
44 | }
45 |
46 | imageUploded(res) {
47 | if (res.errcode === 0) {
48 | this.setState({
49 | src: res.data.src,
50 | });
51 | }
52 | }
53 |
54 | };
55 |
--------------------------------------------------------------------------------
/__sites_/src/components/doc/cn/crop-image.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import vendor from '../../../lib/vendor';
3 | import ReactCoreImageUpload from '../../../../../src/index';
4 | import Highlight from 'react-highlight.js';
5 |
6 | export default class CnCropImage extends React.Component {
7 | constructor(props) {
8 | super(props);
9 | this.state = {
10 | src: 'http://img1.vued.vanthink.cn/vued7553a09a5d5209ebd00a48264394b7f3.png',
11 | cropSrc: 'http://img1.vued.vanthink.cn/vued7553a09a5d5209ebd00a48264394b7f3.png',
12 | cropArgs: {
13 | toCropImgH: '?',
14 | toCropImgW: '?',
15 | toCropImgX: '?',
16 | toCropImgY: '?',
17 | },
18 | };
19 | this.cropLocalImageUploaded = this.cropLocalImageUploaded.bind(this);
20 | this.crpoServerImageUploaded = this.crpoServerImageUploaded.bind(this);
21 | }
22 |
23 | render() {
24 | return (
25 |
26 |
裁剪图片
27 |
你可以通过设置 crop
,来实现图片的裁剪。你可以指定图片裁剪的宽高,以及它的最大宽度和高度这些参数。
28 |
设置 cropRatio
来限制裁剪图片的形状,需要字符串的格式(1:1 或者2:3这种比例形式),当然你可以设置为 auto 则不限制裁剪框的形状。
29 |
设置图片裁剪后,批量上传将不再生效。
30 |
图片裁剪完有两种选择,选择本地裁剪local
或者服务端裁剪 server
。
31 |
本地裁剪
32 |
你可以将 crop 设置为 local 来实现本地裁剪。本地裁剪完成后发送给服务端接口的图片便是已经裁剪好的图片。
33 |
34 |
35 |
36 |
37 |
44 |
45 |
46 |
服务端裁剪
47 |
服务端裁剪是指将原图片和裁剪的参数一起发给后端,方便服务端保存原图,以及对原图的其他操作,而服务端能够接收到post的参数如下:
48 |
49 |
每个字段具体说明如下:
50 |
51 | toCropImgX
: 裁剪的区域距离图片的左边缘的距离
52 | toCropImgY
: 裁剪的区域距离图片的上边缘的距离
53 | toCropImgW
: 裁剪的区域的宽度
54 | toCropImgH
: 裁剪的区域的高度
55 | maxWidth
: 你期望裁剪的图片的最大宽度
56 | maxHeight
: 你期望裁剪的图片的最大高度
57 |
58 |
裁剪区域的样式,你可以自行复写样式进行覆盖
59 |
服务端裁剪DEMO
60 |
上传图片后可以看到裁剪的参数
61 |
62 |
63 |
64 |
65 |
71 |
72 |
73 |
74 |
75 |
76 | H
77 | W
78 | X
79 | Y
80 |
81 |
82 |
83 |
84 | {this.state.cropArgs.toCropImgH}
85 | {this.state.cropArgs.toCropImgW}
86 | {this.state.cropArgs.toCropImgX}
87 | {this.state.cropArgs.toCropImgY}
88 |
89 |
90 |
91 |
Code Example
92 |
93 |
设置resize="server"
同理,会上传原图片,只是会在服务端的参数自动添加裁剪的比例 imgChangeRatio
。
94 |
View Code Source
95 |
96 | );
97 | }
98 |
99 | cropLocalImageUploaded(res) {
100 | this.src = res.data.src;
101 | }
102 |
103 | crpoServerImageUploaded(res) {
104 | if (res.errcode === 0) {
105 | this.setState({
106 | cropArgs: {
107 | toCropImgH: parseInt(res.data.post.toCropImgH),
108 | toCropImgW: parseInt(res.data.post.toCropImgW),
109 | toCropImgX: parseInt(res.data.post.toCropImgX),
110 | toCropImgY: parseInt(res.data.post.toCropImgY)
111 | },
112 | cropSrc: 'http://img1.vued.vanthink.cn/vued41b900045d6d44f3b32e06049621b415.png',
113 | });
114 |
115 | }
116 | }
117 |
118 | };
119 |
--------------------------------------------------------------------------------
/__sites_/src/components/doc/cn/custom-component.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import vendor from '../../../lib/vendor';
3 | import ReactCoreImageUpload from '../../../../../src/index';
4 | import Highlight from 'react-highlight.js';
5 |
6 | export default class CustomComponent extends React.Component {
7 | constructor(props) {
8 | super(props);
9 | this.state = {
10 | src: 'http://img1.vued.vanthink.cn/vued0a233185b6027244f9d43e653227439a.png',
11 | };
12 | this.imageUploded = this.imageUploded.bind(this);
13 | }
14 |
15 | render() {
16 | return (
17 |
18 |
自定义组件样式
19 |
你可以设置组件的class
以及自己编写子组件的形式来控制组件的显示的样子。
20 |
Demo
21 |
下面是一个图片按钮。
22 |
23 |
24 |
25 |
26 |
33 |
34 |
35 |
36 |
Code Example
37 |
38 | {`
45 |
46 | `}
47 |
48 |
49 | );
50 | }
51 |
52 | imageUploded(res) {
53 | if (res.errcode === 0) {
54 | this.setState({
55 | fileList: res.data,
56 | });
57 | }
58 | }
59 |
60 | };
61 |
--------------------------------------------------------------------------------
/__sites_/src/components/doc/cn/events.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import ReactCoreImageUpload from '../../../../../src/index';
3 | import Highlight from 'react-highlight';
4 |
5 | export default class Events extends React.Component {
6 | constructor(props) {
7 | super(props);
8 | this.state = {
9 | src: 'http://img1.vued.vanthink.cn/vued0a233185b6027244f9d43e653227439a.png',
10 | step: 0,
11 | };
12 | this.imagechanged = this.imagechanged.bind(this);
13 | this.imageuploading = this.imageuploading.bind(this);
14 | this.imageuploaded = this.imageuploaded.bind(this);
15 | }
16 |
17 | render() {
18 | return (
19 |
20 |
响应事件
21 |
我们在上传的不同阶段指定了不同的派发事件,你可以绑定每个事件的响应方法,实现对于流程的控制。
22 |
imageuploaded
23 |
当图片上传完,会调用该事件绑定的函数,并且用户可以获取到服务端返回的数据。
24 |
imagechanged
25 |
当input框改变选择图片时候触发,会返回input的获取的图片数据
26 |
imageuploading
27 |
当图片上传过程中触发,你可以自定义你需要处理的内容比如显示加载动画等。
28 |
errorhandle
29 |
当图片上传发生错误的时候触发,会返回错误状态信息
30 |
Code Example
31 |
32 |
33 |
34 |
35 |
44 |
45 |
46 |
47 |
48 |
49 |
50 | 图片选中
51 | 图片上传
52 | 图片完成
53 |
54 |
55 |
56 |
57 |
58 | 0 ? 'circle-bar active': 'circle-bar'}>
59 |
60 | 1 ? 'circle-bar active': 'circle-bar'}>
61 | 2 ? 'circle-bar active': 'circle-bar'}>
62 |
63 |
64 |
65 |
66 |
上面的演示,表示了上传自定义事件的执行状况,参考代码如下:
67 |
68 | {` `}
76 |
77 |
完整代码
78 |
79 | );
80 | }
81 |
82 | imagechanged() {
83 | this.plus();
84 | }
85 |
86 | imageuploading() {
87 | this.plus();
88 | }
89 |
90 | imageuploaded() {
91 | this.plus();
92 | }
93 |
94 | plus() {
95 | this.setState({
96 | step: this.state.step += 1,
97 | });
98 | }
99 |
100 | }
101 |
--------------------------------------------------------------------------------
/__sites_/src/components/doc/cn/get-started.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import ReactCoreImageUpload from '../../../../../src/index';
3 | import Highlight from 'react-highlight';
4 |
5 | export default class GetStarted extends React.Component {
6 | constructor(props) {
7 | super(props);
8 | this.state = {
9 | src: 'http://img1.vued.vanthink.cn/vued0a233185b6027244f9d43e653227439a.png',
10 | };
11 | this.imageuploaded = this.imageuploaded.bind(this);
12 | }
13 |
14 | render() {
15 | return (
16 |
17 |
快速开始
18 |
使用 npm 安装依赖
19 |
npm install react-core-image-upload --save
20 |
安装完成后,编辑源码
21 |
22 | {`import React from 'react';
23 | import ReactCoreImageUpload from 'react-core-image-upload';
24 |
25 | export default class GetStarted extends React.Component {
26 | constructor(props) {
27 | super(props);
28 | this.state = {
29 | src: 'http://img1.vued.vanthink.cn/vued0a233185b6027244f9d43e653227439a.png',
30 | };
31 | this.imageuploaded = this.imageuploaded.bind(this);
32 | }
33 |
34 | redner() {
35 | return(
36 |
37 |
38 |
39 |
40 |
41 |
47 |
48 |
49 |
50 | );
51 | }
52 |
53 | imageuploaded(res) {
54 | if (res.errcode == 0) {
55 | this.setState({
56 | src: res.data.src,
57 | });
58 | }
59 | }
60 | }`
61 | }
62 |
63 |
Code Example
64 |
65 |
66 |
67 |
68 |
74 |
75 |
76 |
如果我们要使用上传插件,我们首先需要引入我们的组件然后并在components
中声明。
77 | 实现上传,我们需要定义我们上传的服务器地址url
,然后我们需要指定上传完成后触发的方法,也就是imageUploaded
,这样我们才能获取上传完后的数据,从而进行下一步的操作。
78 |
79 |
查看详细文档
80 |
81 | );
82 | }
83 |
84 | imageuploaded(res) {
85 | if (res.errcode == 0) {
86 | this.setState({
87 | src: res.data.src,
88 | });
89 | }
90 | }
91 |
92 | };
93 |
--------------------------------------------------------------------------------
/__sites_/src/components/doc/cn/home.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import vendor from '../../../lib/vendor';
3 |
4 | export default class CnHome extends React.Component {
5 | constructor(props) {
6 | super(props);
7 | }
8 |
9 | render() {
10 | return (
11 |
12 |
13 |
14 |
15 |
16 | 快速开始
17 |
18 |
English Document
19 |
react-core-image-upload 是一款轻量级的 react.js 上传插件,它可以支持的图片的上传,裁剪,压缩。它同样也支持在移动端的图片处理,它定义了诸多上传周期,你可以自由的进行流程控制。
20 |
21 | );
22 | }
23 |
24 | goToEnglishDoc() {
25 | vendor.setLocalData('lan', 'en');
26 | window.lan = 'en';
27 | location.href = './index.html#/en/home';
28 | location.reload();
29 | }
30 |
31 | };
32 |
--------------------------------------------------------------------------------
/__sites_/src/components/doc/cn/multiple-file.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import vendor from '../../../lib/vendor';
3 | import ReactCoreImageUpload from '../../../../../src/index';
4 | import Highlight from 'react-highlight.js';
5 |
6 | export default class CnMultipleFile extends React.Component {
7 | constructor(props) {
8 | super(props);
9 | this.state = {
10 | fileList: [],
11 | };
12 | this.imageUploded = this.imageUploded.bind(this);
13 | }
14 |
15 | render() {
16 | const trs = [];
17 | console.log(this.state.fileList);
18 | for(let i = 0; i < this.state.fileList.length; i++) {
19 | const item = this.state.fileList[i];
20 |
21 | trs.push(
22 |
23 | {item.name}
24 | {item.size}
25 |
26 | );
27 | }
28 |
29 | return (
30 |
31 |
上传多个文件
32 |
multiple
33 |
你可以使用 multiple
属性设置为true来实现多文件的上传。需要注意的是,你设置了该属性后,服务端收到文件上传的字段数据会是一个数组。
34 |
multiple-size
35 |
你可以使用multiple-size
来限制图片的数量,你可以限制上传文件的数量。
36 |
演示
37 |
38 |
45 |
46 |
47 |
48 |
49 |
50 | 文件名称
51 | 文件大小
52 |
53 |
54 |
55 | {trs}
56 |
57 |
58 |
点击上传按钮,可以选择多张图片,然后在表格中可以看到上传的图片名称和图片大小。
59 |
Code Example
60 |
{`
68 | `}
69 |
70 |
查看完整源码
71 |
72 | );
73 | }
74 |
75 | imageUploded(res) {
76 | if (res.errcode === 0) {
77 | this.setState({
78 | fileList: res.data,
79 | });
80 | }
81 | }
82 |
83 | };
84 |
--------------------------------------------------------------------------------
/__sites_/src/components/doc/cn/others.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 |
3 | export default class Others extends React.Component {
4 | constructor(props) {
5 | super(props);
6 | }
7 |
8 | render() {
9 | return (
10 |
11 |
其他与反馈
12 |
你可以在 Github issue 中搜索你遇见的问题,如果没有对应的情况,请直接提交你遇到的问题。
13 |
如果你觉得问题比较复杂,难以描述,你可以直接联系我,我的邮箱是: kakashjack#gmail.com (# 替换成 @符号)
14 |
微信:
15 |
16 |
Facebook Messager:
17 |
18 |
19 | );
20 | }
21 |
22 | }
23 |
--------------------------------------------------------------------------------
/__sites_/src/components/doc/cn/post-data.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import ReactCoreImageUpload from '../../../../../src/index';
3 | import Highlight from 'react-highlight';
4 |
5 | export default class PostData extends React.Component {
6 | constructor(props) {
7 | super(props);
8 | this.state = {
9 | data: {
10 | text: ''
11 | },
12 | };
13 | this.change = this.change.bind(this);
14 | }
15 |
16 | render() {
17 | return (
18 |
19 |
向服务端发送数据
20 |
你可以设置data
来将一些附带的数据发送给服务端。
21 | 当然你也可以将一些数据包含在 header 中传递过去,你只需要绑定到 header
即可。
22 |
Exmaple:
23 |
下面会将输入框的内容一并发送过去
24 |
25 |
26 |
27 |
34 |
35 |
36 |
上传的过程中我们可以打开devtool查看请求,可以看到发送数据中带上了一个新的 text 字段,也就是文本框内容。
37 |
38 |
Code Example
39 |
你可以设置 isXhr
来取消向服务端上传。
40 |
查看源码
41 |
42 | );
43 | }
44 |
45 | imageuploaded(res) {
46 | if (res.errcode == 0) {
47 | this.setState({
48 | src: res.data.src,
49 | });
50 | }
51 | }
52 |
53 | change(e) {
54 | var val = e.target.value;
55 | if (val) {
56 | this.setState({
57 | data: {
58 | text: val,
59 | }
60 | })
61 | }
62 | }
63 |
64 | };
65 |
--------------------------------------------------------------------------------
/__sites_/src/components/doc/cn/props.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import vendor from '../../../lib/vendor';
3 | import ReactCoreImageUpload from '../../../../../src/index';
4 | import Highlight from 'react-highlight.js';
5 |
6 | export default class Props extends React.Component {
7 | constructor(props) {
8 | super(props);
9 | }
10 | render() {
11 | return (
12 |
13 |
基本属性
14 |
Vue-core-image-upload 提供了很多可配置的选项,从而希望尽可能的满足开发者的需求。
15 |
Code Example
16 |
{
17 | `
23 | `
24 | }
25 |
其中 className-file-size均是插件支持的属性,你可以具体查看这张表格:
26 |
27 |
28 |
29 |
30 | 属性
31 | 数据类型
32 | 示例
33 | 描述
34 |
35 |
36 |
37 |
38 | url
39 | String
40 | '/crop.php'
41 | 服务端上传的地址
42 |
43 |
44 | text
45 | String
46 | 'Upload Image'
47 | 你需要显示按钮的文本
48 |
49 |
50 | inputOfFile
51 | String
52 | 'file'
53 | 上传服务端对应表单 name
54 |
55 |
56 | extensions
57 | String
58 | 'png,jpg,gif'
59 | 限制的图片类型
60 |
61 |
62 | crop
63 | Boolean
64 | true
65 | 是否需要裁剪
66 |
67 |
68 | cropRatio
69 | String
70 | '1:1'
71 | 限制裁剪的形状(设置为auto表示不限制裁剪框形状)
72 |
73 |
74 | cropBtn
75 | Object
76 | {"{ok:'Save','cancel':'Give Up'}"}
77 | 按钮文本
78 |
79 |
80 | maxFileSize
81 | Number
82 | 10485760(10M)
83 | 文件大小限制
84 |
85 |
86 | maxWidth
87 | Number
88 | 150
89 | 限制图片的最大宽度
90 |
91 |
92 | maxheight
93 | Number
94 | 150
95 | 限制图片的最大高度
96 |
97 |
98 | inputAccept
99 | string
100 | 'image/*' / 'image/jpg,image/jpeg,image/png'
101 | 赋予上传file的接受类型
102 |
103 |
104 | compress
105 | Number
106 | 50
107 | 设置本地图片压缩的质量
108 |
109 |
110 | isXhr
111 | Boolean
112 | true
113 | 是否需要调用系统内自己的上传功能
114 |
115 |
116 | headers
117 | Object
118 | {"{auth: xxxxx}"}
119 | 设置xhr上传 的header
120 |
121 |
122 | data
123 | Object
124 | {"{name: xxxxx}"}
125 | 设置附带发送给服务端的数据
126 |
127 |
128 |
129 |
130 |
后文会对一些重要的属性进行详细的描述,以及一些其他 Demos 你可以参考左边的导航。
131 |
url
132 |
url 表示我们指定的服务端配置接口地址,插件会将文件使用 Ajax 的形式上传到到你指定的地址服务,然后你可以通过imageuploaded
的回调取得服务器的响应的数据。
133 |
inputOfFile
134 |
如果你的服务端端需要指定上传表单file name 的字段,你可以通过inputOfFile
设置,只能是字符串如果没有设置,默认发给服务端的字段是files 。如果你是使用多图片上传的话,字段会变成files[] 。
135 |
extensions
136 |
限制图片的上传类型,你可以传递一组CSV数据进去比如 'png,jpg,gif',从而只允许png,jpg,gif的类型图片上传到服务器。
137 |
maxFileSize
138 |
虽然很多时候服务端限制了图片的大小,但是本地同样可以限制上传图片的大小,你可以设置 maxFileSize
的值来实现,它的基本单位为字节比如(10485760B = 10M)。
139 |
inputAccept
140 |
赋予上传file的接受类型,它可以帮助你再上传图片选择时,禁用掉不需要的文件,默认值为 image/jpg,image/jpeg,image/png
141 |
isXhr
142 |
我们同样接受取消掉默认的上传,你可以设置isXhr
为 false ,自行处理我们获取到文件对象。
143 |
关于没有涉及到的属性,左边的导航会给出详细说明
144 |
145 | );
146 | }
147 |
148 | imageUploded(res) {
149 | if (res.errcode === 0) {
150 | this.setState({
151 | src: res.data.src,
152 | });
153 | }
154 | }
155 |
156 | };
157 |
--------------------------------------------------------------------------------
/__sites_/src/components/doc/cn/resize-image.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import vendor from '../../../lib/vendor';
3 | import ReactCoreImageUpload from '../../../../../src/index';
4 | import Highlight from 'react-highlight.js';
5 |
6 | export default class CnResizeImage extends React.Component {
7 | constructor(props) {
8 | super(props);
9 | this.state = {
10 | src: 'http://img1.vued.vanthink.cn/vued0a233185b6027244f9d43e653227439a.png',
11 | };
12 | this.imageUploded = this.imageUploded.bind(this);
13 | }
14 |
15 | render() {
16 | return (
17 |
18 |
调整图片
19 |
你可以设置 resize
来进行图片的缩放。
20 |
设置resize="local"
意味着图片的缩放将在本地进行。发给服务端的将会是大小调整完毕的后的图片。
21 |
22 |
23 |
24 |
25 |
31 |
32 |
33 |
Code Example
34 |
35 |
设置resize="server"
同理,会上传原图片,只是会在服务端的参数自动添加裁剪的比例 imgChangeRatio
。
36 |
View Code Source
37 |
38 | );
39 | }
40 |
41 | imageUploded(res) {
42 | if (res.errcode === 0) {
43 | this.setState({
44 | src: res.data.src,
45 | });
46 | }
47 | }
48 |
49 | };
50 |
--------------------------------------------------------------------------------
/__sites_/src/components/doc/en/compress-image.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import vendor from '../../../lib/vendor';
3 | import ReactCoreImageUpload from '../../../../../src/index';
4 | import Highlight from 'react-highlight.js';
5 |
6 | export default class CnMultipleFile extends React.Component {
7 | constructor(props) {
8 | super(props);
9 | this.state = {
10 | src: 'http://img1.vued.vanthink.cn/vued7553a09a5d5209ebd00a48264394b7f3.png',
11 | };
12 | this.imageUploded = this.imageUploded.bind(this);
13 | }
14 | render() {
15 | return (
16 |
17 |
Compress Image
18 |
Props compress
means the quality of the image you want to compress via browser
19 | and then send the compressed image to the server.
20 |
21 |
22 |
30 |
31 |
32 |
代码示例
33 |
{`
41 | `}
42 |
43 |
44 | );
45 | }
46 |
47 | imageUploded(res) {
48 | if (res.errcode === 0) {
49 | this.setState({
50 | src: res.data.src,
51 | });
52 | }
53 | }
54 |
55 | };
56 |
--------------------------------------------------------------------------------
/__sites_/src/components/doc/en/crop-image.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import vendor from '../../../lib/vendor';
3 | import ReactCoreImageUpload from '../../../../../src/index';
4 | import Highlight from 'react-highlight.js';
5 |
6 | export default class CnCropImage extends React.Component {
7 | constructor(props) {
8 | super(props);
9 | this.state = {
10 | src: 'http://img1.vued.vanthink.cn/vued7553a09a5d5209ebd00a48264394b7f3.png',
11 | cropSrc: 'http://img1.vued.vanthink.cn/vued7553a09a5d5209ebd00a48264394b7f3.png',
12 | cropArgs: {
13 | toCropImgH: '?',
14 | toCropImgW: '?',
15 | toCropImgX: '?',
16 | toCropImgY: '?',
17 | },
18 | };
19 | this.cropLocalImageUploaded = this.cropLocalImageUploaded.bind(this);
20 | this.crpoServerImageUploaded = this.crpoServerImageUploaded.bind(this);
21 | }
22 |
23 | render() {
24 | return (
25 |
26 |
Crop Image
27 |
Set crop
value to help you crop the image.
28 |
if you setted the crop props, you can not upload multiple files.
29 |
cropRatio
can be setted for diffrent crop shape.But it must be a string like '2:3' or '1:1'. If you set it to 'auto', users can crop any shape images.
30 |
You have two values to select,local crop:local
orserver-side crop: server
.
31 |
Local Crop
32 |
crop="local"
The Browser will crop the image via canvas API and send the cropped image to the server.
33 |
34 |
35 |
36 |
37 |
44 |
45 |
46 |
Server-side crop
47 |
crop="server"
means the bwowser will send the original image to the server and post the cropped data below to the server:
48 |
49 |
Each filed introduce:
50 |
51 | toCropImgX
: The x-axis distance between the crop area and the image
52 | toCropImgY
: The y-axis distance between the crop area and the image
53 | toCropImgW
: The width of crop area
54 | toCropImgH
: The height of crop area
55 | maxWidth
: The maximum width of the crop image
56 | maxHeight
: The maximum height of the crop image
57 |
58 |
Code example
59 |
Click button to upload and you can view some post params.
60 |
61 |
62 |
63 |
64 |
70 |
71 |
72 |
73 |
74 |
75 | H
76 | W
77 | X
78 | Y
79 |
80 |
81 |
82 |
83 | {this.state.cropArgs.toCropImgH}
84 | {this.state.cropArgs.toCropImgW}
85 | {this.state.cropArgs.toCropImgX}
86 | {this.state.cropArgs.toCropImgY}
87 |
88 |
89 |
90 |
Code Example
91 |
{`
97 | `}
98 |
View Code Source
99 |
100 | );
101 | }
102 |
103 | cropLocalImageUploaded(res) {
104 | this.src = res.data.src;
105 | }
106 |
107 | crpoServerImageUploaded(res) {
108 | if (res.errcode === 0) {
109 | this.setState({
110 | cropArgs: {
111 | toCropImgH: parseInt(res.data.post.toCropImgH),
112 | toCropImgW: parseInt(res.data.post.toCropImgW),
113 | toCropImgX: parseInt(res.data.post.toCropImgX),
114 | toCropImgY: parseInt(res.data.post.toCropImgY)
115 | },
116 | cropSrc: 'http://img1.vued.vanthink.cn/vued41b900045d6d44f3b32e06049621b415.png',
117 | });
118 |
119 | }
120 | }
121 |
122 | };
123 |
--------------------------------------------------------------------------------
/__sites_/src/components/doc/en/custom-component.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import vendor from '../../../lib/vendor';
3 | import ReactCoreImageUpload from '../../../../../src/index';
4 | import Highlight from 'react-highlight.js';
5 |
6 | export default class CustomComponent extends React.Component {
7 | constructor(props) {
8 | super(props);
9 | this.state = {
10 | src: 'http://img1.vued.vanthink.cn/vued0a233185b6027244f9d43e653227439a.png',
11 | };
12 | this.imageUploded = this.imageUploded.bind(this);
13 | }
14 |
15 | render() {
16 | return (
17 |
18 |
Custom Component
19 |
class
will be bind to the component and you can include any child component.
20 |
Demo
21 |
This is a image upload button.
22 |
23 |
24 |
25 |
26 |
33 |
34 |
35 |
36 |
Code Example
37 |
38 | {`
45 |
46 | `}
47 |
48 |
49 | );
50 | }
51 |
52 | imageUploded(res) {
53 | if (res.errcode === 0) {
54 | this.setState({
55 | fileList: res.data,
56 | });
57 | }
58 | }
59 |
60 | };
61 |
--------------------------------------------------------------------------------
/__sites_/src/components/doc/en/events.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import ReactCoreImageUpload from '../../../../../src/index';
3 | import Highlight from 'react-highlight';
4 |
5 | export default class Events extends React.Component {
6 | constructor(props) {
7 | super(props);
8 | this.state = {
9 | src: 'http://img1.vued.vanthink.cn/vued0a233185b6027244f9d43e653227439a.png',
10 | step: 0,
11 | };
12 | this.imagechanged = this.imagechanged.bind(this);
13 | this.imageuploading = this.imageuploading.bind(this);
14 | this.imageuploaded = this.imageuploaded.bind(this);
15 | }
16 |
17 | render() {
18 | return (
19 |
20 |
Custom Events
21 |
It defines different custom event in different upload progress. You can control the files flow via binding some functions.
22 |
imageuploaded
23 |
When image has been uploaded, it call the function you bind. You could recive response from server as an param
24 |
imagechanged
25 |
When input[file] velue has been changed, it call the function you bind. You could recive an file source param
26 |
imageuploading
27 |
When the image is uploading.
28 |
errorhandle
29 |
Whne you meet some error like network error or file size error.
30 |
Code Example
31 |
32 |
33 |
34 |
35 |
44 |
45 |
46 |
47 |
48 |
49 |
50 | Selected
51 | Uplaoding
52 | Finished
53 |
54 |
55 |
56 |
57 |
58 | 0 ? 'circle-bar active': 'circle-bar'}>
59 |
60 | 1 ? 'circle-bar active': 'circle-bar'}>
61 | 2 ? 'circle-bar active': 'circle-bar'}>
62 |
63 |
64 |
65 |
66 |
Code:
67 |
68 | {` `}
76 |
77 |
View Code
78 |
79 | );
80 | }
81 |
82 | imagechanged() {
83 | this.plus();
84 | }
85 |
86 | imageuploading() {
87 | this.plus();
88 | }
89 |
90 | imageuploaded() {
91 | this.plus();
92 | }
93 |
94 | plus() {
95 | this.setState({
96 | step: this.state.step += 1,
97 | });
98 | }
99 |
100 | }
101 |
--------------------------------------------------------------------------------
/__sites_/src/components/doc/en/get-started.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import ReactCoreImageUpload from '../../../../../src/index';
3 | import Highlight from 'react-highlight';
4 |
5 | export default class GetStarted extends React.Component {
6 | constructor(props) {
7 | super(props);
8 | this.state = {
9 | src: 'http://img1.vued.vanthink.cn/vued0a233185b6027244f9d43e653227439a.png',
10 | };
11 | this.imageuploaded = this.imageuploaded.bind(this);
12 | }
13 |
14 | render() {
15 | return (
16 |
17 |
快速开始
18 |
使用 npm 安装依赖
19 |
npm install vue-core-image-upload --save
20 |
安装完成后,编辑源码
21 |
22 | {`
23 |
24 |
31 |
32 |
33 |
34 |
35 | `
56 | }
57 |
58 |
Code Example
59 |
60 |
61 |
62 |
63 |
69 |
70 |
71 |
如果我们要使用上传插件,我们首先需要引入我们的组件然后并在components
中声明。
72 | 实现上传,我们需要定义我们上传的服务器地址url
,然后我们需要指定上传完成后触发的方法,也就是@imageuploaded
,这样我们才能获取上传完后的数据,从而进行下一步的操作。
73 |
74 |
查看详细文档
75 |
76 | );
77 | }
78 |
79 | imageuploaded(res) {
80 | if (res.errcode == 0) {
81 | this.src = res.data.src;
82 | }
83 | }
84 |
85 | };
86 |
--------------------------------------------------------------------------------
/__sites_/src/components/doc/en/home.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import vendor from '../../../lib/vendor';
3 |
4 | export default class EnHome extends React.Component {
5 | constructor(props) {
6 | super(props);
7 | }
8 |
9 | render() {
10 | return (
11 |
12 |
13 |
14 |
15 |
16 | Get Started
17 |
18 |
Chinese Document
19 |
react-core-image-upload is a lightweight plugin for developers to upload and crop images. There is also a good experience on mobile devices. We has define different type events for developers and they can control the file flow and do more thing they want.
20 |
21 |
22 | );
23 | }
24 |
25 | goToChineseDoc() {
26 | vendor.setLocalData('lan', 'cn');
27 | window.lan = 'cn';
28 | location.href = './index.html#/cn/home';
29 | location.reload();
30 | }
31 |
32 |
33 | };
34 |
--------------------------------------------------------------------------------
/__sites_/src/components/doc/en/multiple-file.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import vendor from '../../../lib/vendor';
3 | import ReactCoreImageUpload from '../../../../../src/index';
4 | import Highlight from 'react-highlight.js';
5 |
6 | export default class CnMultipleFile extends React.Component {
7 | constructor(props) {
8 | super(props);
9 | this.state = {
10 | fileList: [],
11 | };
12 | this.imageUploded = this.imageUploded.bind(this);
13 | }
14 |
15 | render() {
16 | const trs = [];
17 | console.log(this.state.fileList);
18 | for(let i = 0; i < this.state.fileList.length; i++) {
19 | const item = this.state.fileList[i];
20 |
21 | trs.push(
22 |
23 | {item.name}
24 | {item.size}
25 |
26 | );
27 | }
28 |
29 | return (
30 |
31 |
上传多个文件
32 |
multiple
33 |
你可以使用 multiple
属性设置为true来实现多文件的上传。需要注意的是,你设置了该属性后,服务端收到文件上传的字段数据会是一个数组。
34 |
multiple-size
35 |
你可以使用multiple-size
来限制图片的数量,你可以限制上传文件的数量。
36 |
演示
37 |
38 |
45 |
46 |
47 |
48 |
49 |
50 | 文件名称
51 | 文件大小
52 |
53 |
54 |
55 | {trs}
56 |
57 |
58 |
点击上传按钮,可以选择多张图片,然后在表格中可以看到上传的图片名称和图片大小。
59 |
Code Example
60 |
{`
68 | `}
69 |
70 |
查看完整源码
71 |
72 | );
73 | }
74 |
75 | imageUploded(res) {
76 | if (res.errcode === 0) {
77 | this.setState({
78 | fileList: res.data,
79 | });
80 | }
81 | }
82 |
83 | };
84 |
--------------------------------------------------------------------------------
/__sites_/src/components/doc/en/others.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 |
3 | export default class Others extends React.Component {
4 | constructor(props) {
5 | super(props);
6 | }
7 |
8 | render() {
9 | return (
10 |
11 |
Feedback
12 |
You could find some solution from Github issue .
13 |
If you need help when you meet some trouble, it is free to contac with me via email:kakashjack@gmail.com
14 |
WeChat:
15 |
16 |
Facebook Messager:
17 |
18 |
19 | );
20 | }
21 |
22 | }
23 |
--------------------------------------------------------------------------------
/__sites_/src/components/doc/en/post-data.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import ReactCoreImageUpload from '../../../../../src/index';
3 | import Highlight from 'react-highlight';
4 |
5 | export default class PostData extends React.Component {
6 | constructor(props) {
7 | super(props);
8 | this.state = {
9 | data: {
10 | text: ''
11 | },
12 | };
13 | this.change = this.change.bind(this);
14 | }
15 |
16 | render() {
17 | return (
18 |
19 |
Send Data to Server
20 |
Setting data
attribute will send some data you bind to the server via ajax.
21 | Of course you could pass data to server via header, just pass data to header
.
22 |
Exmaple:
23 |
You could type some text here and its text will be sended to the server when you upload your image.
24 |
25 |
26 |
27 |
34 |
35 |
36 |
We could open chrome devtool to view http status when uploading image. Below is the result after uploading image successfully.
37 |
38 |
Code Example
39 |
{
40 | `
47 | `
48 | }
49 |
Set isXhr
equal false to cancel default ajax uploading.
50 |
View Code
51 |
52 | );
53 | }
54 |
55 | imageuploaded(res) {
56 | if (res.errcode == 0) {
57 | this.setState({
58 | src: res.data.src,
59 | });
60 | }
61 | }
62 |
63 | change(e) {
64 | var val = e.target.value;
65 | if (val) {
66 | this.setState({
67 | data: {
68 | text: val,
69 | }
70 | })
71 | }
72 | }
73 |
74 | };
75 |
--------------------------------------------------------------------------------
/__sites_/src/components/doc/en/props.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import vendor from '../../../lib/vendor';
3 | import ReactCoreImageUpload from '../../../../../src/index';
4 | import Highlight from 'react-highlight.js';
5 |
6 | export default class Props extends React.Component {
7 | constructor(props) {
8 | super(props);
9 | }
10 | render() {
11 | return (
12 |
13 |
Props
14 |
Vue-core-image-upload supports many props to meet developers needs.
15 |
Code Example
16 |
{
17 | `
23 | `
24 | }
25 |
class
, url
and max-file-size
are different props. And the table below simply introduces how it work.
26 |
27 |
28 |
29 |
30 | Props
31 | Data Type
32 | Example
33 | Details
34 |
35 |
36 |
37 |
38 | url
39 | String
40 | '/crop.php'
41 | Your server api
42 |
43 |
44 | text
45 | String
46 | 'Upload Image'
47 | The text of your uploading button
48 |
49 |
50 | inputOfFile
51 | String
52 | 'file'
53 | Yout input[file] name
54 |
55 |
56 | extensions
57 | String
58 | 'png,jpg,gif'
59 | Limit the image type
60 |
61 |
62 | crop
63 | Boolean
64 | 'server'
65 | Crop image option
66 |
67 |
68 | cropRatio
69 | String
70 | '1:1'
71 | The cropped image shape(set 'auto' not limit the crop shape)
72 |
73 |
74 | cropBtn
75 | Object
76 | {"{ok:'Save','cancel':'Give Up'}"}
77 | The Text of cropping button text
78 |
79 |
80 | maxFileSize
81 | Number
82 | 10485760(10M)
83 | Limit the size of the file
84 |
85 |
86 | maxWidth
87 | Number
88 | 150
89 | The maximum width of cropped image
90 |
91 |
92 | maxheight
93 | Number
94 | 150
95 | 限制图片的最大高度
96 |
97 |
98 | inputAccept
99 | string
100 | 'image/*' / 'image/jpg,image/jpeg,image/png'
101 | the input[file] accept
102 |
103 |
104 | compress
105 | Number
106 | 50
107 | Set the quality of compressed image
108 |
109 |
110 | isXhr
111 | Boolean
112 | true
113 | IF cancel ajax uploading
114 |
115 |
116 | headers
117 | Object
118 | {"{auth: xxxxx}"}
119 | Set customed header when ajax uploading
120 |
121 |
122 | data
123 | Object
124 | {"{name: xxxxx}"}
125 | Set customed data when ajax posting server
126 |
127 |
128 |
129 |
130 |
url
131 |
url
means a http url that ajax will send,and you need bind a function on @imageuploaded
to handle the server response
132 |
inputOfFile
133 |
You can set input[file] name which the server must use via inputOfFile
. The default value is files 。
134 | If you upload multiple images the name will be files[] 。
135 |
extensions
136 |
If you want only one or two image types to upload to server, extensions
is the right way when you pass an CSV string to it.
137 |
maxFileSize
138 |
Even though the server would check the file size , you cloud also limit the file size locally for thrifting the traffic of network.
139 |
isXhr
140 |
Set isXhr
equal false
, the uploading will be canceld and you can do something by yourself.
141 |
The naviagtion will show other props documents and demos
142 |
143 | );
144 | }
145 |
146 | imageUploded(res) {
147 | if (res.errcode === 0) {
148 | this.setState({
149 | src: res.data.src,
150 | });
151 | }
152 | }
153 |
154 | };
155 |
--------------------------------------------------------------------------------
/__sites_/src/components/doc/en/resize-image.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import vendor from '../../../lib/vendor';
3 | import ReactCoreImageUpload from '../../../../../src/index';
4 | import Highlight from 'react-highlight.js';
5 |
6 | export default class CnResizeImage extends React.Component {
7 | constructor(props) {
8 | super(props);
9 | this.state = {
10 | src: 'http://img1.vued.vanthink.cn/vued0a233185b6027244f9d43e653227439a.png',
11 | };
12 | this.imageUploded = this.imageUploded.bind(this);
13 | }
14 |
15 | render() {
16 | return (
17 |
18 |
Resize
19 |
Set resize
props to help you to resize the imagey you want to upload.
20 |
resize="local"
means you can resize image in local browser via canvas and it will send server the resized image.
21 |
22 |
23 |
24 |
25 |
31 |
32 |
33 |
Code Example
34 |
35 |
resize="server"
means it will send the original image you upload,and it will send the server with data params imgChangeRatio
.
36 |
View Code Source
37 |
38 | );
39 | }
40 |
41 | imageUploded(res) {
42 | if (res.errcode === 0) {
43 | this.setState({
44 | src: res.data.src,
45 | });
46 | }
47 | }
48 |
49 | };
50 |
--------------------------------------------------------------------------------
/__sites_/src/components/ft.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import vendor from '../lib/vendor'
3 |
4 | export default class Ft extends React.Component {
5 | constructor(props) {
6 | super(props);
7 | const lan = vendor.getLocalData('lan') || 'cn';
8 | this.state = {
9 | lan,
10 | };
11 | this.changeCn = this.changeCn.bind(this);
12 | this.changeEn = this.changeEn.bind(this);
13 | }
14 |
15 | render() {
16 | return (
17 |
38 | );
39 | }
40 |
41 | changeCn() {
42 | vendor.setLocalData('lan', 'cn');
43 | location.href="./index.html#/cn/home";
44 | location.reload();
45 | }
46 |
47 | changeEn() {
48 | vendor.setLocalData('lan', 'en');
49 | location.href="./index.html#/en/home";
50 | location.reload();
51 | }
52 |
53 |
54 |
55 | };
56 |
--------------------------------------------------------------------------------
/__sites_/src/components/hd.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import vendor from '../lib/vendor';
3 |
4 | let language = vendor.getLocalData('data');
5 | if (!language) {
6 | language = {
7 | label: '🇨🇳 Chinese',
8 | value: 'cn'
9 | }
10 | } else {
11 | language = language['lan']
12 | }
13 |
14 | export default class Hd extends React.Component {
15 | constructor(props) {
16 | super(props);
17 | this.state = {
18 | language,
19 | options: [
20 | {
21 | label: '🇨🇳 Chinese',
22 | value: 'cn',
23 | },
24 | {
25 | label: '🇺🇸 English',
26 | value: 'en',
27 | },
28 | ],
29 | };
30 | }
31 |
32 | render() {
33 | return (
34 |
52 | );
53 |
54 | }
55 |
56 | toggleMenu() {
57 | const $aside = document.querySelector('aside');
58 | if ($aside.className.indexOf('active') > -1) {
59 | $aside.className = '';
60 | } else {
61 | $aside.className = 'active';
62 | }
63 | }
64 |
65 | };
66 |
--------------------------------------------------------------------------------
/__sites_/src/components/nav-list.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { routers } from '../lib/constants';
3 | import vendor from '../lib/vendor';
4 |
5 | export default class NavList extends React.Component {
6 | constructor(props) {
7 | super(props);
8 | const lan = vendor.getLocalData('lan') || 'cn';
9 | for (var item of routers) {
10 | item.url = '#/' + lan + '/' + item.url;
11 | if (lan !== 'en') {
12 | item.name = item.cn_name;
13 | }
14 | }
15 | this.state = {
16 | url: location.hash.replace(/\?.*/, ''),
17 | list: routers,
18 | };
19 | this.setUrl = this.setUrl.bind(this);
20 | }
21 |
22 | setUrl(e) {
23 | const url = /\#(.*)/.exec(e.target.href)[0];
24 | this.setState({
25 | url,
26 | });
27 | const $aside = document.querySelector('aside');
28 | if ($aside) {
29 | $aside.classList.remove('active');
30 | }
31 |
32 | }
33 |
34 | render() {
35 | const lis = [];
36 | for (let i = 0; i < this.state.list.length; i++) {
37 | const classname = this.state.list[i].url === this.state.url ? 'active' : '';
38 | const item = this.state.list[i];
39 | lis.push(
40 |
41 | {item.name}
42 |
43 | );
44 | }
45 | return (
46 |
52 | );
53 | }
54 |
55 | }
56 |
--------------------------------------------------------------------------------
/__sites_/src/index.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import ReactDOM from 'react-dom';
3 | import App from './app';
4 |
5 | require('./less/vtui.less');
6 |
7 | ReactDOM.render( , document.getElementById('app'));
8 |
--------------------------------------------------------------------------------
/__sites_/src/less/config.less:
--------------------------------------------------------------------------------
1 | /**config **/
2 |
3 | @bgcolor: #f1f1f1;
4 |
5 |
6 |
7 | /***flat ui color**/
8 |
9 | @turquoise: #1abc9c;
10 | @green-sea: #16a085;
11 |
12 | @emerald: #2ecc71;
13 | @nephritis: #27ae60;
14 |
15 | @peter-river: #3498db;
16 | @belize-hole: #2980b9;
17 |
18 | @amethyst: #9b59b6;
19 | @wisteria: #8e44ad;
20 |
21 | @wet-asphalt: #34495e;
22 | @midnight-blue: #2c3e50;
23 |
24 | @sun-flower: #f1c40f;
25 | @orange: #f39c12;
26 |
27 | @carrot: #e67e22;
28 | @pumpkin: #d35400;
29 |
30 | @alizarin: #e74c3c;
31 | @pomegranate: #c0392b;
32 |
33 | @clouds: #ecf0f1;
34 | @silver: #bdc3c7;
35 |
36 | @concrete: #95a5a6;
37 | @asbestos: #7f8c8d;
38 |
39 | /****/
40 |
41 | @gray: @concrete;
42 | @gray-light: @silver;
43 | @inverse: white;
44 |
45 | // Brand colors
46 | @brand-primary: @wet-asphalt;
47 | @brand-secondary: @turquoise;
48 | @brand-success: @emerald;
49 | @brand-warning: @sun-flower;
50 | @brand-danger: @alizarin;
51 | @brand-info: @peter-river;
52 |
53 | @body-bg: #fff;
54 | @text-color: @brand-primary;
55 |
56 | //** Global textual link color.
57 | @link-color: @green-sea;
58 | @link-hover-color: @turquoise;
59 | //** Link hover decoration.
60 | @link-hover-decoration: none;
61 |
62 |
63 | //== Typography
64 | //
65 | //## Font, line-height for body text, headings, and more.
66 |
67 | @font-family-base: '\5FAE\8F6F\96C5\9ED1','\9ED1\4F53',arial,sans-serif;
68 | @font-family-demo: "Helvetica Neue", Helvetica, Arial, sans-serif;
69 | @font-family-monospace: Monaco, Menlo, Consolas, "Courier New", monospace;
70 | @font-size-base: 14px;
71 |
72 | @local-font-path: "../fonts/lato/";
73 | @local-font-name: "lato-regular";
74 | @local-font-svg-id: "latoregular";
75 | @local-font-name-light: "lato-light";
76 | @local-font-svg-id-light: "latolight";
77 | @local-font-name-black: "lato-black";
78 | @local-font-svg-id-black: "latoblack";
79 | @local-font-name-bold: "lato-bold";
80 | @local-font-svg-id-bold: "latobold";
81 | @local-font-name-italic: "lato-italic";
82 | @local-font-svg-id-italic: "latoitalic";
83 | @local-font-name-bold-italic: "lato-bolditalic";
84 | @local-font-svg-id-bold-italic: "latobold-italic";
85 |
86 | @font-size-h1: floor((@font-size-base * 3.444)); // ~62px
87 | @font-size-h2: ceil((@font-size-base * 2.889)); // ~52px
88 | @font-size-h3: ceil((@font-size-base * 2.222)); // ~40px
89 | @font-size-h4: ceil((@font-size-base * 1.611)); // ~29px
90 | @font-size-h5: floor((@font-size-base * 1.556)); // ~28px
91 | @font-size-h6: ceil((@font-size-base * 1.333)); // ~24px
92 |
93 | @line-height-base: 1.72222; // 31/18
94 | @line-height-computed: floor((@font-size-base * @line-height-base)); // ~31px
95 |
96 | @headings-font-family: inherit;
97 | @headings-font-weight: 700;
98 | @headings-line-height: 1.1;
99 | @headings-color: inherit;
100 |
101 |
102 | //== Iconography
103 | //
104 | //## Specify custom locations of the include Glyphicons icon font.
105 |
106 | @icon-font-path: "../fonts/glyphicons/";
107 | @icon-font-name: "flat-ui-icons-regular";
108 | @icon-font-svg-id: "flat-ui-icons-regular";
109 |
110 | //** Icon sizes for use in components
111 | @icon-normal: 16px;
112 | @icon-medium: 18px;
113 | @icon-large: 32px;
114 |
115 |
116 | //== Components
117 | //
118 | //## Define common padding and border radius sizes and more.
119 |
120 | //** Default font-size in components
121 | @component-font-size-base: ceil((@font-size-base * 0.833)); // ~15px
122 |
123 | // Border-radius
124 | @border-radius-base: 4px;
125 | @border-radius-large: 6px;
126 | @border-radius-small: 3px;
127 |
128 | //** Width of the `border` for generating carets that indicator dropdowns.
129 | @caret-width-base: 6px;
130 | @caret-width-base-vertical: (@caret-width-base + 2);
131 |
132 | @caret-width-xs: 4px;
133 | @caret-width-xs-vertical: (@caret-width-xs + 2);
134 |
135 | //== Buttons
136 | //
137 | //## For each of Flat UI's buttons, define text, background, font size and height.
138 |
139 | @btn-font-size-base: @component-font-size-base;
140 | @btn-font-size-xs: ceil((@component-font-size-base * 0.8)); // ~12px
141 | @btn-font-size-sm: floor((@component-font-size-base * 0.867)); // ~13px
142 | @btn-font-size-lg: ceil((@component-font-size-base * 1.133)); // ~17px
143 | @btn-font-size-hg: floor((@component-font-size-base * 1.467)); // ~22px
144 |
145 | @btn-line-height-base: 1.4; // ~21px
146 | @btn-line-height-hg: 1.227; // ~27px
147 | @btn-line-height-lg: 1.471; // ~25px
148 | @btn-line-height-sm: 1.385; // ~16px
149 | @btn-line-height-xs: 1.083; // ~13px
150 |
151 | @btn-social-font-size-base: floor((@component-font-size-base * 0.867)); // ~13px
152 | @btn-social-line-height-base: 1.077; // ~14px
153 |
154 | @btn-font-weight: normal;
155 |
156 | @btn-default-color: @inverse;
157 | @btn-default-bg: @gray-light;
158 | @btn-hover-bg: mix(@gray-light, white, 80%);
159 | @btn-active-bg: mix(@gray-light, black, 85%);
160 |
161 | @btn-primary-hover-bg: mix(@brand-secondary, white, 80%);
162 | @btn-primary-active-bg: mix(@brand-secondary, black, 85%);
163 |
164 | @btn-info-hover-bg: mix(@brand-info, white, 80%);
165 | @btn-info-active-bg: mix(@brand-info, black, 85%);
166 |
167 | @btn-success-hover-bg: mix(@brand-success, white, 80%);
168 | @btn-success-active-bg: mix(@brand-success, black, 85%);
169 |
170 | @btn-danger-hover-bg: mix(@brand-danger, white, 80%);
171 | @btn-danger-active-bg: mix(@brand-danger, black, 85%);
172 |
173 | @btn-warning-hover-bg: overlay(@brand-warning, darken(white, 37.5%));
174 | @btn-warning-active-bg: mix(@brand-warning, black, 85%);
175 |
176 | @btn-inverse-hover-bg: overlay(@brand-primary, darken(white, 37.5%));
177 | @btn-inverse-active-bg: mix(@brand-primary, black, 85%);
178 |
179 | @btn-link-disabled-color: @gray-light;
180 |
181 |
182 | //== Forms
183 | //
184 | //##
185 |
186 | @input-font-size-base: @component-font-size-base;
187 | @input-font-size-sm: floor((@component-font-size-base * 0.867)); // ~13px
188 | @input-font-size-lg: ceil((@component-font-size-base * 1.133)); // ~17px
189 | @input-font-size-hg: floor((@component-font-size-base * 1.467)); // ~22px
190 |
191 | @input-line-height-base: 1.467; // ~22px
192 | @input-line-height-sm: 1.462; // ~19px
193 | @input-line-height-lg: 1.235; // ~21px
194 | @input-line-height-hg: 1.318; // ~29px
195 |
196 | @input-icon-font-size: ceil((@component-font-size-base * 1.067)); // ~16px
197 | @input-icon-font-size-lg: ceil((@component-font-size-base * 1.2)); // ~18px
198 | @input-icon-font-size-hg: ceil((@component-font-size-base * 1.333)); // ~20px
199 |
200 | @input-bg: @inverse;
201 | @input-bg-disabled: mix(@gray, white, 10%);
202 |
203 | @input-height-sm: 35px;
204 | @input-height-base: 41px;
205 | @input-height-lg: 45px;
206 | @input-height-hg: 53px;
207 |
208 | @input-border-radius: @border-radius-large;
209 |
210 | //** Disabled cursor for form controls and buttons.
211 | @cursor-disabled: not-allowed;
212 |
213 | @legend-color: inherit;
214 |
215 |
216 |
217 |
218 |
219 |
220 | //== Tags Input
221 | //
222 | //##
223 |
224 | @tagsinput-container-bg: @inverse;
225 | @tagsinput-container-border-color: mix(@inverse, @brand-primary, 90%);
226 | @tagsinput-container-border-radius: @border-radius-large;
227 |
228 | @tagsinput-input-color: @brand-primary;
229 |
230 | @tagsinput-tag-bg: mix(@inverse, @brand-primary, 90%);
231 | @tagsinput-tag-color: mix(@brand-primary, @inverse, 65%);
232 | @tagsinput-tag-hover-bg: mix(@brand-secondary, black, 85%);
233 | @tagsinput-tag-hover-color: @inverse;
234 | @tagsinput-tag-icon-color: @inverse;
235 | @tagsinput-tag-border-radius: @border-radius-base;
236 |
237 | @tagsinput-primary-container-border-color: @brand-secondary;
238 | @tagsinput-primary-tag-bg: @brand-secondary;
239 | @tagsinput-primary-tag-color: @inverse;
240 | @tagsinput-primary-tag-hover-bg: mix(@brand-secondary, black, 85%);
241 | @tagsinput-primary-tag-hover-color: @inverse;
242 |
243 |
244 | //== Selects
245 | //
246 | //## For each of Flat UI's selects, define text, background, font size and height.
247 |
248 | @select-font-size-base: @btn-font-size-base;
249 | @select-font-size-sm: @btn-font-size-sm;
250 | @select-font-size-lg: @btn-font-size-lg;
251 | @select-font-size-hg: @btn-font-size-hg;
252 |
253 | @select-line-height-base: @btn-line-height-base;
254 | @select-line-height-hg: @btn-line-height-hg;
255 | @select-line-height-lg: @btn-line-height-lg;
256 | @select-line-height-sm: @btn-line-height-sm;
257 |
258 | @select-font-weight: @btn-font-weight;
259 |
260 | @select-disabled-opacity: 0.7;
261 |
262 | @select-default-color: @btn-default-color;
263 | @select-default-bg: @btn-default-bg;
264 | @select-default-hover-bg: @btn-hover-bg;
265 | @select-default-active-bg: @btn-active-bg;
266 |
267 | @select-primary-hover-bg: @btn-primary-hover-bg;
268 | @select-primary-active-bg: @btn-primary-active-bg;
269 |
270 | @select-info-hover-bg: @btn-info-hover-bg;
271 | @select-info-active-bg: @btn-info-active-bg;
272 |
273 | @select-success-hover-bg: @btn-success-hover-bg;
274 | @select-success-active-bg: @btn-success-active-bg;
275 |
276 | @select-danger-hover-bg: @btn-danger-hover-bg;
277 | @select-danger-active-bg: @btn-danger-active-bg;
278 |
279 | @select-warning-hover-bg: @btn-warning-hover-bg;
280 | @select-warning-active-bg: @btn-warning-active-bg;
281 |
282 | @select-inverse-hover-bg: @btn-inverse-hover-bg;
283 | @select-inverse-active-bg: @btn-inverse-active-bg;
284 |
285 | @select-link-disabled-color: @btn-link-disabled-color;
286 | @select-arrow-color: @brand-primary;
287 |
288 | // Select dropdowns
289 | @select-dropdown-border-radius: @border-radius-base;
290 |
291 | @select-dropdown-item-color: fade(@brand-primary, 85%);
292 | @select-dropdown-item-hover-color: inherit;
293 | @select-dropdown-item-hover-bg: mix(@inverse, @brand-primary, 85%);
294 |
295 | @select-dropdown-disabled-item-color: fade(@brand-primary, 95%);
296 | @select-dropdown-disabled-item-opacity: 0.4;
297 |
298 | @select-dropdown-highlighted-item-bg: @brand-secondary;
299 | @select-dropdown-highlighted-item-color: @inverse;
300 |
301 | @select-dropdown-optgroup-color: fade(@brand-primary, 60%);
302 |
303 | // Multiselect
304 | @multiselect-container-bg: @tagsinput-container-bg;
305 | @multiselect-container-border-color: @tagsinput-container-border-color;
306 | @multiselect-container-border-radius: @tagsinput-container-border-radius;
307 |
308 | @multiselect-tag-border-radius: @tagsinput-tag-border-radius;
309 | @multiselect-tag-color: @inverse;
310 | @multiselect-tag-hover-color: @tagsinput-tag-hover-color;
311 | @multiselect-tag-icon-color: @tagsinput-tag-icon-color;
312 |
313 | @multiselect-dropdown-border-radius: @border-radius-large;
314 | @multiselect-dropdown-item-border-radius: @border-radius-base;
315 |
316 | @multiselect-input-color: @tagsinput-input-color;
317 |
318 |
319 | //== Pagination
320 | //
321 | //##
322 |
323 | @pagination-bg: mix(@brand-primary, white, 20%);
324 | @pagination-hover-bg: @brand-secondary;
325 | @pagination-color: @inverse;
326 | @pagination-border-radius: @border-radius-large;
327 |
328 |
329 | //== Pager
330 | //
331 | //##
332 |
333 | @pager-padding: 9px 15px 10px;
334 | @pager-bg: @brand-primary;
335 | @pager-hover-bg: mix(@brand-primary, black, 85%);
336 | @pager-active-bg: @pager-hover-bg;
337 | @pager-border-radius: @border-radius-large;
338 | @pager-color: @inverse;
339 |
340 |
341 |
342 |
343 | //== Navbar
344 | //
345 | //##
346 |
347 | // Basics of a navbar
348 | @zindex-navbar: 1000;
349 | @zindex-navbar-fixed: 1030;
350 | @navbar-height-base: 53px;
351 | @navbar-height-large: 76px;
352 | @navbar-input-line-height: 1.4; // ~21px
353 | @navbar-margin-bottom: @line-height-computed;
354 | @navbar-border-radius: @border-radius-large;
355 |
356 | @navbar-default-bg: saturate(spin(tint(@brand-primary, 91%), -18), 2%);
357 |
358 | // Navbar links
359 | @navbar-default-link-color: @brand-primary;
360 | @navbar-default-link-hover-color: @brand-secondary;
361 | @navbar-default-link-hover-bg: transparent;
362 | @navbar-default-link-active-color: @brand-secondary;
363 | @navbar-default-link-active-bg: transparent;
364 | @navbar-default-link-disabled-color: #ccc;
365 | @navbar-default-link-disabled-bg: transparent;
366 |
367 | // Navbar nav carets
368 | @navbar-default-caret-color: @navbar-default-link-color;
369 | @navbar-default-caret-hover-color: @navbar-default-link-hover-color;
370 | @navbar-default-caret-active-color: @navbar-default-link-active-color;
371 |
372 | // Navbar brand label
373 | @navbar-default-brand-color: @navbar-default-link-color;
374 | @navbar-default-brand-hover-color: @navbar-default-link-hover-color;
375 | @navbar-default-brand-hover-bg: transparent;
376 |
377 | // Navbar toggle
378 | @navbar-default-toggle-color: @navbar-default-link-color;
379 | @navbar-default-toggle-hover-color: @navbar-default-link-hover-color;
380 |
381 | // Navbar form
382 | @navbar-default-form-placeholder: spin(tint(@brand-primary, 60%), 2);
383 | @navbar-default-form-icon: desaturate(tint(@brand-primary, 45%), 2%);
384 | @navbar-default-form-border: shade(@navbar-default-bg, 3%);
385 |
386 |
387 | // Inverted navbar
388 | // Reset inverted navbar basics
389 | @navbar-inverse-divider: darken(@brand-primary, 3%);
390 |
391 | // Reset inverted navbar basics
392 | @navbar-inverse-color: @inverse;
393 | @navbar-inverse-bg: @brand-primary;
394 | @navbar-inverse-border: darken(@navbar-inverse-bg, 10%);
395 |
396 | // Inverted navbar links
397 | @navbar-inverse-link-color: @inverse;
398 | @navbar-inverse-link-hover-color: @brand-secondary;
399 | @navbar-inverse-link-hover-bg: transparent;
400 | @navbar-inverse-link-active-color: @navbar-inverse-link-color;
401 | @navbar-inverse-link-active-bg: @brand-secondary;
402 | @navbar-inverse-link-disabled-color: #444;
403 | @navbar-inverse-link-disabled-bg: transparent;
404 |
405 | // Navbar nav carets
406 | @navbar-inverse-caret-color: lighten(desaturate(@brand-primary, 7%), 9%);
407 | @navbar-inverse-caret-hover-color: @navbar-inverse-link-hover-color;
408 | @navbar-inverse-caret-active-color: @navbar-inverse-link-active-color;
409 |
410 | // Inverted navbar brand label
411 | @navbar-inverse-brand-color: @navbar-inverse-link-color;
412 | @navbar-inverse-brand-hover-color: @navbar-inverse-link-hover-color;
413 | @navbar-inverse-brand-hover-bg: transparent;
414 |
415 | // Inverted navbar toggle
416 | @navbar-inverse-toggle-color: @navbar-inverse-link-color;
417 | @navbar-inverse-toggle-hover-color: @navbar-inverse-link-hover-color;
418 |
419 | // Navbar form
420 | @navbar-inverse-form-bg: darken(@brand-primary, 6%);
421 | @navbar-inverse-form-placeholder: desaturate(lighten(@brand-primary, 13%), 7%);
422 | @navbar-inverse-form-icon: desaturate(lighten(@brand-primary, 13%), 6%);
423 | @navbar-inverse-form-border: @navbar-inverse-divider;
424 |
425 | // Navbar dropdowns
426 | @navbar-inverse-dropdown-bg: @navbar-inverse-bg;
427 | @navbar-inverse-dropdown-link-color: mix(@navbar-inverse-bg, @navbar-inverse-color, 15%);
428 | @navbar-inverse-dropdown-link-hover-color: @inverse;
429 | @navbar-inverse-dropdown-link-hover-bg: @brand-secondary;
430 |
431 | //== Dropdowns
432 | //
433 | //## Dropdown menu container and contents.
434 |
435 |
436 | @zindex-dropdown: 1000;
437 | @dropdown-border-radius: @border-radius-base;
438 |
439 | //** Background for the dropdown menu.
440 | @dropdown-bg: desaturate(lighten(@brand-primary, 67%), 20%);
441 |
442 | //** Dropdown link text color.
443 | @dropdown-link-color: mix(darken(@brand-primary, 5%), @inverse, 75%);
444 | //** Hover color for dropdown links.
445 | @dropdown-link-hover-color: darken(@dropdown-link-color, 5%);
446 | //** Hover background for dropdown links.
447 | @dropdown-link-hover-bg: fade(desaturate(lighten(@brand-primary, 52%), 21%), 50%);
448 |
449 | //** Active dropdown menu item text color.
450 | @dropdown-link-active-color: @inverse;
451 | //** Active dropdown menu item background color.
452 | @dropdown-link-active-bg: @brand-secondary;
453 |
454 | //** Disabled dropdown menu item background color.
455 | @dropdown-link-disabled-color: @gray-light;
456 |
457 | //** Divider color for between dropdown items.
458 | @dropdown-divider-bg: fade(@dropdown-link-hover-bg, 50%);
459 |
460 | //** Text color for headers within dropdown menus.
461 | @dropdown-header-color: fade(@brand-primary, 60%);
462 |
463 |
464 | // Inverted dropdown
465 | //
466 |
467 | @dropdown-inverse-bg: @brand-primary;
468 |
469 |
470 | //** Dropdown link text color.
471 | @dropdown-inverse-link-color: fade(@inverse, 85%);
472 | //** Hover color for dropdown links.
473 | @dropdown-inverse-link-hover-color: fade(@inverse, 85%);
474 | //** Hover background for dropdown links.
475 | @dropdown-inverse-link-hover-bg: fade(darken(@brand-primary, 5%), 50%);
476 |
477 | //** Active dropdown menu item text color.
478 | @dropdown-inverse-link-active-color: fade(@inverse, 85%);
479 | //** Active dropdown menu item background color.
480 | @dropdown-inverse-link-active-bg: @brand-secondary;
481 |
482 | //** Disabled dropdown menu item background color.
483 | @dropdown-inverse-link-disabled-color: fade(@dropdown-inverse-link-color, 50%);
484 |
485 | //** Divider color for between dropdown items.
486 | @dropdown-inverse-divider-bg: @dropdown-inverse-link-hover-bg;
487 |
488 | //** Text color for headers within dropdown menus.
489 | @dropdown-inverse-header-color: fade(@inverse, 40%);
490 |
491 |
492 | //== Progress bars
493 | //
494 | //##
495 |
496 | @progress-height: 12px;
497 |
498 |
499 | //== Slider
500 | //
501 | //##
502 |
503 | @slider-height: 12px;
504 | @slider-value-font-size: floor((@component-font-size-base * 0.867)); // ~13px;
505 |
506 | @slider-handle-bg: mix(@brand-secondary, black, 85%);
507 | @slider-handle-hover-bg: mix(@brand-secondary, white, 80%);
508 | @slider-handle-active-bg: mix(@brand-secondary, black, 85%);
509 |
510 | @slider-range-bg: @brand-secondary;
511 |
512 | @slider-segment-bg: mix(desaturate(@brand-primary, 15%), white, 20%);
513 |
514 |
515 | //== Switch
516 | //
517 | //##
518 |
519 | @switch-name: bootstrap-switch;
520 | @switch-border-radius: 30px;
521 | @switch-width: 80px;
522 | @switch-height: 29px;
523 |
524 |
525 |
526 | //== Video player
527 | //
528 | //##
529 |
530 | @vplayer-border-radius: @border-radius-large;
531 | @vplayer-fullscreen-bg: #000;
532 | @vplayer-fullscreen-zindex: 10000;
533 |
534 | @vplayer-control-bar-color: @inverse;
535 | @vplayer-control-bar-bg: @midnight-blue;
536 |
537 | @vplayer-preloader-primary-bg: #e74c3c;
538 | @vplayer-preloader-secondary-bg: #ebedee;
539 |
540 | @vplayer-text-track-bg: rgba(0,0,0,.5);
541 |
542 | @vplaver-play-control-color: @brand-secondary;
543 | @vplaver-play-control-hover-color: mix(@brand-secondary, black, 85%);
544 |
545 | @vplaver-second-controls-color: desaturate(lighten(@midnight-blue, 12%), 6%);
546 | @vplaver-second-controls-hover-color: desaturate(lighten(@midnight-blue, 20%), 6%);
547 |
548 | @vplaver-progress-bg: mix(@brand-primary, @inverse, 93%);
549 | @vplaver-play-progress-bg: @brand-secondary;
550 | @vplaver-load-progress-bg: mix(@brand-primary, @inverse, 20%);
551 |
552 | @vplayer-seek-handle-bg: mix(@brand-secondary, black, 85%);
553 | @vplayer-seek-handle-hover-bg: mix(@brand-secondary, black, 75%);
554 | @vplayer-seek-handle-active-bg: mix(@brand-secondary, black, 65%);
555 |
556 | @vplayer-time-divider-color: mix(@brand-primary, @inverse, 80%);
557 | @vplayer-duration-color: mix(@brand-primary, @inverse, 80%);
558 |
559 |
560 |
561 |
562 | //== Todo list
563 | //
564 | //##
565 |
566 | @todo-bg: @brand-primary;
567 | @todo-bg-active: mix(@brand-primary, black, 85%);
568 | @todo-search-bg: @brand-secondary;
569 | @todo-search-color: @brand-primary;
570 | @todo-color: mix(@brand-primary, @inverse, 66%);
571 | @todo-name-color: @inverse;
572 | @todo-color-active: @brand-secondary;
573 | @todo-border-radius: @border-radius-large;
574 |
575 |
576 | //== Thumbnails
577 | //
578 | //##
579 |
580 | //** Padding around the thumbnail image
581 | @thumbnail-padding: 4px;
582 | //** Thumbnail background color
583 | @thumbnail-bg: @body-bg;
584 | //** Thumbnail border color
585 | @thumbnail-border: @gray-light;
586 | //** Thumbnail border radius
587 | @thumbnail-border-radius: @border-radius-large;
588 |
589 | //** Custom text color for thumbnail captions
590 | @thumbnail-caption-color: @text-color;
591 | //** Padding around the thumbnail caption
592 | @thumbnail-caption-padding: 9px;
593 |
594 |
595 | //== Tiles
596 | //
597 | //##
598 |
599 | @tiles-bg: mix(@brand-primary, @inverse, 8%);
600 | @tiles-border-radius: @border-radius-large;
601 |
602 |
603 |
604 | //== Media queries breakpoints
605 | //
606 | //## Define the breakpoints at which your layout will change, adapting to different screen sizes.
607 |
608 | // Extra small screen / phone
609 | @screen-xs-min: 480px;
610 |
611 | // Small screen / tablet
612 | @screen-sm-min: 768px;
613 |
614 | // Medium screen / desktop
615 | @screen-md-min: 992px;
616 |
617 | // Large screen / wide desktop
618 | @screen-lg-min: 1200px;
619 |
620 | // So media queries don't overlap when required, provide a maximum
621 | @screen-xs-max: (@screen-sm-min - 1);
622 | @screen-sm-max: (@screen-md-min - 1);
623 | @screen-md-max: (@screen-lg-min - 1);
624 |
625 |
626 | //== Grid system
627 | //
628 | //## Define your custom responsive grid.
629 |
630 | //** Number of columns in the grid.
631 | @grid-columns: 12;
632 | //** Padding between columns. Gets divided in half for the left and right.
633 | @grid-gutter-width: 30px;
634 | // Navbar collapse
635 | //** Point at which the navbar becomes uncollapsed.
636 | @grid-float-breakpoint: @screen-sm-min;
637 | //** Point at which the navbar begins collapsing.
638 | @grid-float-breakpoint-max: (@grid-float-breakpoint - 1);
639 |
640 |
641 | //== Form states and alerts
642 | //
643 | //## Define colors for form feedback states and, by default, alerts.
644 |
645 | @state-success-text: @brand-success;
646 | @state-success-bg: #dff0d8;
647 | @state-success-border: darken(spin(@state-success-bg, -10), 5%);
648 |
649 | @state-info-text: @brand-info;
650 | @state-info-bg: #d9edf7;
651 | @state-info-border: darken(spin(@state-info-bg, -10), 7%);
652 |
653 | @state-warning-text: @brand-warning;
654 | @state-warning-bg: #fcf8e3;
655 | @state-warning-border: darken(spin(@state-warning-bg, -10), 5%);
656 |
657 | @state-danger-text: @brand-danger;
658 | @state-danger-bg: #f2dede;
659 | @state-danger-border: darken(spin(@state-danger-bg, -10), 5%);
660 |
661 |
662 | //== Tooltips
663 | //
664 | //##
665 |
666 | //** Tooltip max width
667 | @tooltip-max-width: 183px;
668 | //** Tooltip text color
669 | @tooltip-color: @inverse;
670 | //** Tooltip background color
671 | @tooltip-bg: @brand-primary;
672 | @tooltip-opacity: 1;
673 | //** Tooltip zIndex
674 | @zindex-tooltip: 1070;
675 |
676 | //** Tooltip inverse text color
677 | @tooltip-inverse-color: @brand-primary;
678 | //** Tooltip inverse background color
679 | @tooltip-inverse-bg: mix(@brand-primary, white, 9%);
680 |
681 | //** Tooltip arrow width
682 | @tooltip-arrow-width: 9px;
683 | //** Tooltip arrow color
684 | @tooltip-arrow-color: @tooltip-bg;
685 | //** Tooltip inverse arrow color
686 | @tooltip-inverse-arrow-color: @tooltip-inverse-bg;
687 |
688 |
689 |
690 |
691 | //== Code
692 | //
693 | //##
694 |
695 | @code-color: #c7254e;
696 | @code-bg: #f9f2f4;
697 |
698 | @kbd-color: @inverse;
699 | @kbd-bg: @brand-primary;
700 |
701 | @pre-bg: @inverse;
702 | @pre-color: inherit;
703 | @pre-border-color: mix(@brand-primary, @inverse, 12%);
704 | @pre-scrollable-max-height: 340px;
705 | @pre-border-radius: @border-radius-large;
706 |
707 |
708 | //== Form states and alerts
709 | //
710 | //##
711 |
712 | //** Text muted color
713 | @text-muted: @gray-light;
714 | //** Abbreviations and acronyms border color
715 | @abbr-border-color: @gray-light;
716 | //** Headings small color
717 | @headings-small-color: mix(@brand-primary, @inverse, 12%);
718 | //** Blockquote small color
719 | @blockquote-small-color: inherit;
720 | //** Blockquote border color
721 | @blockquote-border-color: mix(@brand-primary, @inverse, 12%);
722 | //** Page header border color
723 | @page-header-border-color: mix(@brand-primary, @inverse, 12%);
724 | //** Width of horizontal description list titles
725 | @dl-horizontal-offset: @component-offset-horizontal;
726 |
727 |
728 | //== Miscellaneous
729 | //
730 | //##
731 |
732 | //** Hr border color
733 | @hr-border: mix(@brand-primary, @inverse, 63%);
734 |
735 | //** Horizontal forms & lists
736 | @component-offset-horizontal: 180px;
737 |
--------------------------------------------------------------------------------
/__sites_/src/less/modules/m-button.less:
--------------------------------------------------------------------------------
1 | /** just for button **/
2 | .btn {
3 | display: inline-block;
4 | line-height: 30px;
5 | padding: 0 15px;
6 | border-radius: 2px;
7 | background: #fff;
8 | border: 1px solid #e7eaec;
9 | min-width: 46px;
10 | color:#323c48;
11 | text-align: center;
12 | transition: all .15s ease;
13 | font-size: 13px;
14 | cursor: pointer;
15 | outline: none !important;
16 | box-shadow: 0 1px 2px -1px rgba(255,255,255,0.1);
17 | transition: all .25s ease;
18 | }
19 | .btn-sl{
20 | padding: 0 8px;
21 | line-height: 22px;
22 | font-size: 12px;
23 | }
24 | .btn:hover {
25 | border-color: #d2d2d2;
26 | box-shadow: 0 2px 4px -2px rgba(255,255,255,0.2);
27 | }
28 | .btn:disabled,
29 | .btn:disabled:hover{
30 | background: #b4b4b4 !important;
31 | color:#fff;
32 | border-color: #ccc;
33 | box-shadow: none;
34 | cursor: not-allowed;
35 | }
36 |
37 | .submit-btn {
38 | margin-right: 6px;
39 | }
40 | .btn-primary,
41 | .submit-btn,
42 | .btn.active{
43 | color:#fff;
44 | background: @peter-river;
45 | border-color: @peter-river;
46 | }
47 | .btn-primary:focus,
48 | .btn-primary:active,
49 | .submit-btn:focus,
50 | .submit-btn:active,
51 | .btn.active:focuss,
52 | .btn.active:active{
53 | border-color:@belize-hole;
54 | }
55 | .btn-primary:hover,
56 | .submit-btn:hover,
57 | .btn.active:hover{
58 | background: @belize-hole;
59 | border-color: @belize-hole;
60 | color: #fff;
61 | }
62 | .btn-info{
63 | color:@peter-river;
64 | background: #fff;
65 | border-color:@peter-river;
66 | }
67 | .btn-info:focus,
68 | .btn-info:active{
69 | background: @belize-hole;
70 | border-color: @belize-hole;
71 | color:#fff;
72 | }
73 | .btn-info:hover{
74 | background: @belize-hole;
75 | border-color:@belize-hole;
76 | color:#fff;
77 | }
78 | a.btn-info:hover {
79 | color: #fff;
80 | }
81 |
82 |
83 | .btn-warning{
84 | color:#fff;
85 | background: #faa732;
86 | border-color:#faa732;
87 | }
88 | .btn-warning:focus,
89 | .btn-warning:active{
90 | border-color:#dd942e;
91 | }
92 | .btn-warning:hover{
93 | background: #dd942e;
94 | border-color: #dd942e;
95 | }
96 |
97 | .btn-danger{
98 | color:#fff;
99 | background: #ea6153;
100 | border-color:#d14233;
101 | }
102 | .btn-danger:focus,
103 | .btn-danger:active{
104 | border-color:#d14233;
105 | }
106 | .btn-danger:hover{
107 | background: #dc5b4e;
108 | border-color: #bd2b41;
109 | }
110 | .btn-link{
111 | line-height: 28px;
112 | padding: 0 15px;
113 | background-color: transparent;
114 | border:none;
115 | color:#00a8e6;
116 | }
117 | .btn-link:focus,
118 | .btn-link:active{
119 | color:#005598;
120 | }
121 | .btn-link:hover{
122 | color:#0077dd;
123 | border:0px;
124 | }
125 | .btn-lg{
126 | line-height: 36px;
127 | padding: 0 30px;
128 | }
129 | .btn-sm{
130 | line-height: 28px;
131 | padding: 0 15px;
132 | }
133 |
134 | .btn-icon{
135 | position: relative;
136 | width: 32px;
137 | min-width: 32px;
138 | height: 32px;
139 | padding: 0;
140 | margin-right: 15px;
141 | text-align: center;
142 | }
143 | .btn.btn-icon .icon{
144 | margin: 0;
145 | font-size: 0.9rem;
146 | }
147 | .btn-icon:hover .icon-tips-box{
148 | display: inline-block;
149 | }
150 | .icon-tips-box{
151 | z-index: 5;
152 | position: absolute;
153 | display: none;
154 | left:100%;
155 | top:50%;
156 | margin-top: -15px;
157 | min-width: 56px;
158 | height: 30px;
159 | line-height: 30px;
160 | margin-left: 10px;
161 | padding: 0 15px;
162 | border-radius: 3px;
163 | color:#f1f1f1;
164 | background: rgba(0,0,0,.8);
165 | background-color: #333;
166 | font-size: 12px;
167 | }
168 | .icon-tips-box:before{
169 | content: '';
170 | position: absolute;
171 | display: inline-block;
172 | top:50%;
173 | margin-top: -6px;
174 | left:-12px;
175 | border-width:6px;
176 | border-style: solid;
177 | border-color: transparent rgba(0,0,0,.8) transparent transparent ;
178 | }
179 | .btn-icon.left .icon-tips-box{
180 | left:inherit;
181 | right: 100%;
182 | margin-right: 5px;
183 | }
184 | .btn-icon.left .icon-tips-box:before{
185 | left:inherit;
186 | right:-12px;
187 | border-color: transparent transparent transparent rgba(0,0,0,.8) ;
188 |
189 | }
190 | .btn-group .btn,
191 | .btn-group .text{
192 | float: left;
193 | margin-left: 0p;
194 | margin-right: -1px;
195 | }
196 | .btn-group .btn.active{
197 | background: #00a8e6;
198 | border-color: #00a8e6;
199 | color:#fff;
200 | }
201 | .btn-group .btn.active:hover{
202 | background: #009cd6;
203 | }
204 | .btn-group.hover-group{
205 | padding-right: 10px;
206 | }
207 | .btn-group.hover-group .btn-primary{
208 | opacity: 0;
209 | }
210 | .btn-group.hover-group:hover .btn-primary{
211 | opacity: 1;
212 | }
213 |
214 |
215 | .btn .icon{
216 | font-size: 16px;
217 | margin-right: 6px;
218 | }
219 | .btn-noborder {
220 | background: none;
221 | border: none 0;
222 | padding: 0 15px;
223 | }
224 | .btn-noborder:hover{
225 | color:#333;
226 | }
227 |
228 |
229 | /*.btn.btn-default {
230 | background: #fff;
231 | color: #636365;
232 | border: 1px solid #D8DCE1;
233 | border-radius: 2px;
234 | line-height: 30px;
235 | padding: 0 10px;
236 | }
237 |
238 |
239 | .btn-noborder i {
240 | font-size: 0.85714285714286rem;
241 | border: 1px solid #707070;
242 | color: #707070;
243 | border-radius: 2px;
244 | padding: 1px 2px;
245 | background: #fff;
246 | }
247 | .btn-noborder:hover i {
248 | border: 1px solid #61bbf4;
249 | color: #61bbf4;
250 | }
251 | .btn-default:hover,
252 | .btn-default:focus,
253 | .btn-default:active,
254 | .btn-default.active,
255 | .open .dropdown-toggle.btn-default {
256 | color: #333333;
257 | background-color: #ebebeb;
258 | border-color: #adadad;
259 | }
260 |
261 | .btn-default:active,
262 | .btn-default.active,
263 | .open .dropdown-toggle.btn-default {
264 | background-image: none;
265 | }
266 |
267 | .btn-default.disabled,
268 | .btn-default[disabled],
269 | fieldset[disabled] .btn-default,
270 | .btn-default.disabled:hover,
271 | .btn-default[disabled]:hover,
272 | fieldset[disabled] .btn-default:hover,
273 | .btn-default.disabled:focus,
274 | .btn-default[disabled]:focus,
275 | fieldset[disabled] .btn-default:focus,
276 | .btn-default.disabled:active,
277 | .btn-default[disabled]:active,
278 | fieldset[disabled] .btn-default:active,
279 | .btn-default.disabled.active,
280 | .btn-default[disabled].active,
281 | fieldset[disabled] .btn-default.active {
282 | background-color: #ffffff;
283 | border-color: #cccccc;
284 | }*/
285 | .btn-group,
286 | .btn-group-vertical {
287 | position: relative;
288 | display: inline-block;
289 | vertical-align: middle;
290 | }
291 | .btn-group > .btn,
292 | .btn-group-vertical > .btn {
293 | position: relative;
294 | float: left;
295 | }
296 | .btn-group > .btn:focus,
297 | .btn-group-vertical > .btn:focus {
298 | outline: none;
299 | }
300 | .btn-group .btn + .btn,
301 | .btn-group .btn + .btn-group,
302 | .btn-group .btn-group + .btn,
303 | .btn-group .btn-group + .btn-group {
304 | margin-left: -1px;
305 | }
306 | .btn-group > .btn:not(:first-child):not(:last-child):not(.dropdown-toggle) {
307 | border-radius: 0;
308 | }
309 | .btn-group > .btn:first-child {
310 | margin-left: 0;
311 | }
312 | .btn-group > .btn:first-child:not(:last-child):not(.dropdown-toggle) {
313 | border-top-right-radius: 0;
314 | border-bottom-right-radius: 0;
315 | }
316 | .btn-group > .btn:last-child:not(:first-child),
317 | .btn-group > .dropdown-toggle:not(:first-child) {
318 | border-top-left-radius: 0;
319 | border-bottom-left-radius: 0;
320 | }
321 | .btn-group > .btn-group {
322 | float: left;
323 | }
324 | .btn-group > .btn-group:not(:first-child):not(:last-child) > .btn {
325 | border-radius: 0;
326 | }
327 | .btn-group > .btn-group:first-child > .btn:last-child,
328 | .btn-group > .btn-group:first-child > .dropdown-toggle {
329 | border-top-right-radius: 0;
330 | border-bottom-right-radius: 0;
331 | }
332 | .btn-group > .btn-group:last-child > .btn:first-child {
333 | border-top-left-radius: 0;
334 | border-bottom-left-radius: 0;
335 | }
336 | .btn-group .dropdown-toggle:active,
337 | .btn-group.open .dropdown-toggle {
338 | outline: 0;
339 | }
340 | .btn-group > .btn + .dropdown-toggle {
341 | padding-right: 8px;
342 | padding-left: 8px;
343 | }
344 | .btn-group > .btn-lg + .dropdown-toggle {
345 | padding-right: 12px;
346 | padding-left: 12px;
347 | }
348 | .btn-group.open .dropdown-toggle {
349 | -webkit-box-shadow: inset 0 3px 5px rgba(0, 0, 0, .125);
350 | box-shadow: inset 0 3px 5px rgba(0, 0, 0, .125);
351 | }
352 | .btn-group.open .dropdown-toggle.btn-link {
353 | -webkit-box-shadow: none;
354 | box-shadow: none;
355 | }
356 |
357 | .btn-circle{
358 | position: relative;
359 | width: 60px;
360 | height: 60px;
361 | background: #fff;
362 | border-radius: 30px;
363 | box-shadow: 0px 2px 5px rgba(0,0,0,.05);
364 | line-height: 60px;
365 | font-size: 18px;
366 | color:#999;
367 | }
368 | .btn-circle:hover{
369 | box-shadow: 0px 5px 12px rgba(0,0,0,.1);
370 | }
371 | .btn-circle:hover:after{
372 | content: attr(data-tooltip);
373 | position: absolute;
374 | top:70px;
375 | width: 80px;
376 | left:-10px;
377 | background: #333;
378 | background: rgba(0,0,0,.8);
379 | border-radius: 2px;
380 | color: #fff;
381 | padding: 0 10px;
382 | font-size: 12px;
383 | line-height: 25px;
384 | }
385 |
--------------------------------------------------------------------------------
/__sites_/src/less/modules/m-icon.less:
--------------------------------------------------------------------------------
1 | .svg-icon{
2 | width: 1.8em;
3 | height: 1.8em;
4 | fill: #222;
5 | }
6 |
--------------------------------------------------------------------------------
/__sites_/src/less/modules/m-layout.less:
--------------------------------------------------------------------------------
1 | /** layout **/
2 | .container {
3 | position: relative;
4 | max-width: 1140px;
5 | margin: 0 auto;
6 | }
7 |
8 | .nav {
9 | position: fixed;
10 | top: 0;
11 | left: 0;
12 | right: 0;
13 | bottom: 0;
14 | z-index: 5;
15 | height: 70px;
16 | background: #fff;
17 | margin-bottom: 0;
18 | font-size: 1.5rem;
19 | line-height: 70px;
20 | box-shadow: 0px 1px 3px -1px rgba(0, 0, 0, 0.1);
21 | opacity: .95;
22 | }
23 |
24 | .nav-logo {
25 | display: inline-block;
26 | width: 45px;
27 | height: 45px;
28 | background-image: url(http://img1.vued.vanthink.cn/vued7cecc028b8b640e58157bf4f2dd17184.png);
29 | background-size: cover;
30 | vertical-align: middle;
31 | }
32 |
33 | .nav-title {
34 | position: absolute;
35 | top: 45px;
36 | left: 30px;
37 | font-size: 16px;
38 | font-weight: 100;
39 | text-transform: uppercase;
40 | color: #fff;
41 | }
42 |
43 | .menu{
44 | display: none;
45 | }
46 | .nav-list {
47 | float: right;
48 | z-index: 100;
49 | height: 40px;
50 | line-height: 40px;
51 | margin-top: 15px;
52 | margin-left: 2rem;
53 | transition: all .25s ease;
54 | text-align: right;
55 | }
56 |
57 |
58 |
59 | .nav-list a {
60 | text-decoration: none;
61 | font-size: 14px;
62 | display: inline-block;
63 | padding: 0 1rem;
64 | overflow: hidden;
65 | white-space: nowrap;
66 | text-overflow: ellipsis;
67 | transition: all .25s ease;
68 | color: #444;
69 | }
70 |
71 | .nav-list:hover a {
72 | opacity: 1;
73 | color:#111;
74 | }
75 | .nav-list a.active{
76 | color:#000;
77 | }
78 | .nav-list .icon-github{
79 | margin-top:2px;
80 | transform:translateY(4px)
81 | }
82 | aside{
83 | float: left;
84 | position: relative;
85 | width: 220px;
86 | padding: 100px 15px 40px 20px;
87 | h4{
88 | margin: 0;
89 | background-color: #bbb;
90 | color: #fff;
91 | padding: 10px;
92 | font-size: 15px;
93 | }
94 | ul{
95 | background-color: #e1e1e1;
96 | padding-bottom: 20px;
97 | }
98 | li{
99 | margin-bottom: 5px;
100 | margin-top: 0;
101 | }
102 | a{
103 | display: inline-block;
104 | line-height: 30px;
105 | color: #777;
106 | padding-left: 15px;
107 | &.active{
108 | color: @peter-river;
109 | }
110 | }
111 |
112 | }
113 |
114 |
115 |
116 | .main{
117 | margin-left: 240px;
118 | max-width: 900px;
119 | min-height: 400px;
120 | padding: 100px 20px;
121 |
122 | .components {
123 | text-align: left;
124 |
125 | .center{
126 | text-align: center;
127 | padding-bottom: 20px;
128 | }
129 | }
130 | }
131 |
132 | .ft{
133 | background: #fff;
134 | padding-top: 120px;
135 | padding-bottom: 40px;
136 | margin-top: 40px;
137 | text-align: center;
138 | }
139 | .ft .icon-list a{
140 | position: relative;
141 | display: inline-block;
142 | width: 75px;
143 | height: 75px;
144 | margin: 20px;
145 | }
146 |
147 | .ft .other-info {
148 | font-size: 13px;
149 | color:#888;
150 | }
151 |
152 | .ft .lang-list a {
153 | margin: 0 10px;
154 | color: #999;
155 |
156 | &.active {
157 | color: @emerald;
158 | }
159 | }
160 |
161 | @media all and (max-width: 764px) {
162 | .nav{
163 | height: 40px;
164 | line-height: 40px;
165 |
166 | }
167 | .menu {
168 | margin-left: 10px;
169 | margin-top: 4px;
170 | display: inline-block;
171 | .svg-icon{
172 | width: 24px;
173 | height: 24px;
174 | }
175 | }
176 |
177 | .nav-logo {
178 | display: none;
179 | }
180 | .nav-list{
181 | margin-top: 0;
182 | }
183 |
184 |
185 | aside{
186 | z-index: 9;
187 | position: fixed;
188 | left: -250px;
189 | top: 40px;
190 | bottom: 0;
191 | padding: 10px;
192 | background-color: #fff;
193 | box-shadow: 3px 4px 5px rgba(0,0,0,.25);
194 | transition: left ease .25s;
195 | &.active{
196 | left: 0;
197 | }
198 | h4, ul {
199 | background-color: #fff;
200 | }
201 |
202 | }
203 | .main {
204 | margin-left: 10px;
205 | padding-top: 60px;
206 | }
207 |
208 | .ft .icon-list a{
209 | width: 40px;
210 | margin: 15px;
211 | img{
212 | width: 100%;
213 | }
214 | }
215 | }
216 |
--------------------------------------------------------------------------------
/__sites_/src/less/modules/m-pages.less:
--------------------------------------------------------------------------------
1 | .c-home {
2 | text-align: center;
3 | }
4 | .c-home img {
5 | width: 360px;
6 | }
7 | .c-home .btn-go-started{
8 | border-radius: 25px;
9 | padding: 5px 40px;
10 | }
11 | .circle-bar{
12 | display: inline-block;
13 | width: 20px;
14 | height: 20px;
15 | border-radius: 10px;
16 | background-color: #999;
17 | }
18 | .circle-bar.active{
19 | background-color: #2ecc71;
20 | }
21 |
22 | .avatar {
23 | width: 150px;
24 | height: 150px;
25 | margin-bottom: 20px;
26 | border-radius: 50%;
27 | border: 2px solid rgba(0,0,0,.05);
28 | }
29 |
--------------------------------------------------------------------------------
/__sites_/src/less/modules/m-pagination.less:
--------------------------------------------------------------------------------
1 | .m-pagination{
2 | position: relative;
3 | text-align: center;
4 | margin-top: 20px;
5 | }
6 |
7 | .m-pagination li,.m-pagination li a{
8 | display: inline-block;
9 | width:34px;
10 | height: 34px;
11 | line-height: 32px;
12 | background: #fff;
13 | color:#313c48;
14 |
15 | }
16 | .m-pagination li{
17 | margin-left: 5px;
18 | }
19 | .m-pagination li a{
20 | border:1px solid #e6eaed;
21 | border-radius: 2px;
22 | }
23 | .m-pagination li a:hover{
24 | border-color: #00a8e6;
25 | color:#00a8e6;
26 | }
27 | .m-pagination li a.active,
28 | .m-pagination li.active a{
29 | background:#00a8e6;
30 | border-color: #00a8e6;
31 | color:#fff;
32 | }
33 |
34 |
35 | .m-pagination li a.disabled,.m-pagination li.disabled a{
36 | background: #b4b4b4;
37 | border-color: #b4b4b4;
38 | color: #fff;
39 | cursor: not-allowed;
40 | }
41 |
42 |
43 |
44 |
45 |
46 |
--------------------------------------------------------------------------------
/__sites_/src/less/modules/m-table.less:
--------------------------------------------------------------------------------
1 | .m-table-label {
2 | margin-bottom: 24px;
3 | border-bottom:1px solid #ddd;
4 | line-height: 49px;
5 | color:#313c48;
6 | }
7 | .m-table thead th,.m-table tbody td {
8 | padding:10px 14px;
9 | border-bottom: 1px solid #d9dde3;
10 | border-collapse: collapse;
11 | }
12 | .m-table {
13 | width:100%;
14 | text-align: left;
15 | line-height: 100%;
16 | thead {
17 | background: #fff;
18 | border-bottom: 2px solid #d9dde3;
19 | th {
20 | border-width: 0px;
21 | text-align: left;
22 | }
23 | }
24 | tbody {
25 | tr.disabled td{
26 | background: #e3e3e3 !important;
27 | cursor: not-allowed;
28 | }
29 | td.rowspan-td {
30 | border-right: 1px solid #d9dde3;
31 | border-left: 1px solid #d9dde3;
32 | padding-left:48px;
33 | text-align: left;
34 | }
35 | td.rowspan-right {
36 | border-right: 1px solid #d9dde3;
37 | }
38 | tr:hover {
39 | td {
40 | background-color: #fff;
41 | }
42 | td.rowspan-td {
43 | background: none;
44 | }
45 | }
46 | tr.error {
47 | background: #faf2ca !important;
48 | }
49 | tr {
50 |
51 |
52 | td {
53 | .icon-btn {
54 | color: #707070;
55 | border: none;
56 | background: transparent;
57 | }
58 | .icon-btn:hover {
59 | color: #61bbf4;
60 | }
61 | .icon-btn[disabled] {
62 | color: #ccc;
63 | }
64 |
65 | }
66 |
67 | &.selected{
68 |
69 | border-left:3px solid @peter-river;
70 |
71 | }
72 | }
73 | td.inner:only-child {
74 | padding-right: 48px;
75 | padding-top: 10px;
76 | padding-bottom: 10px;
77 | }
78 | td.inner:only-child:hover {
79 | background-color: transparent;
80 | }
81 | td.inner {
82 | th {
83 | text-align: center;
84 | }
85 | th:first-child {
86 | text-align: left;
87 | }
88 | td {
89 | background-color: transparent;
90 | }
91 | tr:hover {
92 | td {
93 | background-color: #f8f8f8;
94 | }
95 | }
96 | }
97 | tr.end-noborder td{
98 | border-bottom-width: 0px;
99 | }
100 |
101 | }
102 | input[type="checkbox"] {
103 | margin-right:10px;
104 | }
105 |
106 | .text-link {
107 | color: #61bbf4;
108 | }
109 | .text-link:hover {
110 | color: #707070;
111 | }
112 | td.inner {
113 | & > table {
114 | width: 100%;
115 | thead {
116 | background: #d9dde3;
117 | border-color: #d9dde3;
118 | color: #707070;
119 | }
120 | }
121 | }
122 | }
123 |
124 | .disable {
125 | tbody {
126 | tr:hover {
127 | td {
128 | background: #f8f8f8;
129 | }
130 | }
131 | }
132 | }
133 | .no-bottom-border{
134 | tbody tr td {
135 | border-bottom-width: 0px;
136 | }
137 | }
138 | .bordered {
139 | thead {
140 | tr th{
141 | border:1px solid #d9dde3;
142 | }
143 | }
144 | tbody {
145 | td:first-child {
146 | border-left: 1px solid #d9dde3;
147 | }
148 | td:last-child {
149 | border-right: 1px solid #d9dde3;
150 | }
151 | tr:hover {
152 | td {
153 | background: #fff;
154 | }
155 | }
156 | }
157 | thead {
158 | background: #f5f5f7;
159 | }
160 | }
161 | .bordered thead,.bordered tbody tr td {
162 | border:1px solid #d9dde3;
163 | }
164 | .centered {
165 | text-align: center;
166 | }
167 | .m-table.dark {
168 | thead {
169 | border-color: #e5eef5;
170 | border:1px solid #d9dde3;
171 | border-bottom: none;
172 | tr {
173 | background: #e5eef5;
174 | height: 30px;
175 | th {
176 | color:#687178;
177 | }
178 | }
179 | }
180 | }
181 | .dark {
182 | tbody {
183 | tr:nth-child(even) {
184 | background: #fff;
185 | }
186 | tr {
187 | td:first-child {
188 | border-left:1px solid #d9dde3;
189 | }
190 | td:last-child {
191 | border-right:1px solid #d9dde3;
192 | }
193 | }
194 | }
195 | }
196 | .tbl-form {
197 | tbody {
198 | tr {
199 | td {
200 | padding-top: 10px;
201 | border-bottom: 0px;
202 | }
203 | }
204 | }
205 | }
206 |
207 | .hover{
208 | tbody tr:hover td{
209 | background: #f3f3f3;
210 | cursor: pointer;
211 | }
212 | }
213 |
214 | .no-bordered tbody tr td,
215 | .no-bordered thead,
216 | .no-bordered thead tr th
217 | {
218 | border-width: 0px !important;
219 | }
220 |
--------------------------------------------------------------------------------
/__sites_/src/less/modules/m-type.less:
--------------------------------------------------------------------------------
1 | //
2 | // Typography
3 | // --------------------------------------------------
4 | // Headings
5 | // -------------------------
6 | a {
7 | text-decoration: none;
8 | color: @peter-river;
9 | }
10 | a:hover,
11 | a:active{
12 | color: @belize-hole;
13 | }
14 | h1,
15 | h2,
16 | h3,
17 | h4,
18 | h5,
19 | h6,
20 | .h1,
21 | .h2,
22 | .h3,
23 | .h4,
24 | .h5,
25 | .h6 {
26 | font-family: @headings-font-family;
27 | font-weight: @headings-font-weight;
28 | line-height: @headings-line-height;
29 | color: @headings-color;
30 | small {
31 | color: @headings-small-color;
32 | }
33 | }
34 |
35 | h1,
36 | h2,
37 | h3 {
38 | margin-top: @line-height-computed;
39 | margin-bottom: (@line-height-computed / 2);
40 | padding-bottom: (@line-height-computed / 2);
41 | }
42 |
43 | h3 {
44 | border-bottom: 1px solid #ddd;
45 | }
46 |
47 | h4,
48 | h5,
49 | h6 {
50 | margin-top: (@line-height-computed / 2);
51 | margin-bottom: (@line-height-computed / 2);
52 | }
53 |
54 | h6 {
55 | font-weight: normal;
56 | }
57 |
58 | h1,
59 | .h1 {
60 | font-size: @font-size-h1;
61 | } // ~62px
62 | h2,
63 | .h2 {
64 | font-size: @font-size-h2;
65 | } // ~52px
66 | h3,
67 | .h3 {
68 | font-size: @font-size-h3;
69 | } // ~40px
70 | h4,
71 | .h4 {
72 | font-size: @font-size-h4;
73 | } // ~29px
74 | h5,
75 | .h5 {
76 | font-size: @font-size-h5;
77 | } // ~28px
78 | h6,
79 | .h6 {
80 | font-size: @font-size-h6;
81 | } // ~24px
82 | // Body text
83 | // -------------------------
84 | p {
85 | font-size: @font-size-base;
86 | line-height: @line-height-base;
87 | margin: 0 0 (@line-height-computed / 2);
88 | }
89 |
90 | .lead {
91 | margin-bottom: @line-height-computed;
92 | font-size: floor((@font-size-base * 1.556)); // ~28px
93 | line-height: 1.46428571; // ~41px
94 | font-weight: 300;
95 | @media (min-width: @screen-sm-min) {
96 | font-size: (@font-size-base * 1.667); // ~30px
97 | }
98 | }
99 |
100 | // Emphasis & misc
101 | // -------------------------
102 | // Ex: 18px base font * 83% = about 15px
103 | small,
104 | .small {
105 | font-size: 83%; // ~15px
106 | line-height: 2.067; // ~31px
107 | }
108 |
109 | // Contextual emphasis
110 | .text-muted {
111 | color: @text-muted;
112 | }
113 |
114 | .text-inverse {
115 | color: @inverse;
116 | }
117 |
118 | // Page header
119 | // -------------------------
120 | .page-header {
121 | padding-bottom: ((@line-height-computed / 2) - 1);
122 | margin: (@line-height-computed * 2) 0 @line-height-computed;
123 | border-bottom: 2px solid @page-header-border-color;
124 | }
125 |
126 | // Lists
127 | // --------------------------------------------------
128 | // Unordered and Ordered lists
129 | ul,
130 | ol {
131 | margin-bottom: (@line-height-computed / 2);
132 | }
133 | li{
134 | margin-top: (@line-height-computed / 2);
135 | }
136 | // Description Lists
137 | dl {
138 | margin-bottom: @line-height-computed;
139 | }
140 |
141 | dt,
142 | dd {
143 | line-height: @line-height-base;
144 | }
145 |
146 | // Horizontal description lists
147 | //
148 | // Defaults to being stacked without any of the below styles applied, until the
149 | // grid breakpoint is reached (default of ~768px).
150 | @media (min-width: @grid-float-breakpoint) {
151 | .dl-horizontal {
152 | dt {
153 | width: (@dl-horizontal-offset - 20);
154 | }
155 | dd {
156 | margin-left: @dl-horizontal-offset;
157 | }
158 | }
159 | }
160 |
161 | // MISC
162 | // ----
163 | // Abbreviations and acronyms
164 | abbr[title],
165 | abbr[data-original-title] {
166 | border-bottom: 1px dotted @abbr-border-color;
167 | }
168 |
169 | // Blockquotes
170 | blockquote {
171 | border-left: 3px solid @blockquote-border-color;
172 | padding: 0 0 0 16px;
173 | margin: 0 0 @line-height-computed;
174 | p {
175 | font-size: ceil((@font-size-base * 1.111)); // ~20px
176 | line-height: 1.55; // ~31px
177 | font-weight: normal;
178 | margin-bottom: .4em;
179 | }
180 | small,
181 | .small {
182 | font-size: @font-size-base;
183 | line-height: @line-height-base;
184 | font-style: italic;
185 | color: @blockquote-small-color;
186 | &:before {
187 | content: "";
188 | }
189 | }
190 | // Float right with text-align: right
191 | &.pull-right {
192 | padding-right: 16px;
193 | padding-left: 0;
194 | border-right: 3px solid @blockquote-border-color;
195 | border-left: 0;
196 | small:after {
197 | content: "";
198 | }
199 | }
200 | }
201 |
202 | // Addresses
203 | address {
204 | margin-bottom: @line-height-computed;
205 | line-height: @line-height-base;
206 | }
207 |
208 | // Sup and Sub
209 | sub,
210 | sup {
211 | font-size: 70%;
212 | }
213 |
214 | hr {
215 | display: block;
216 | height: 1px;
217 | border: 0;
218 | border-top: #EFEFEF 1px solid;
219 | margin: 3.2em 0;
220 | padding: 0;
221 | }
222 |
223 | blockquote {
224 | -moz-box-sizing: border-box;
225 | box-sizing: border-box;
226 | margin: 1.75em 0 1.75em -2.2em;
227 | padding: 0 0 0 1.75em;
228 | border-left: #3498db 0.4em solid;
229 | }
230 |
231 | blockquote p {
232 | margin: 0.8em 0;
233 | font-style: italic;
234 | }
235 |
236 | blockquote small {
237 | display: inline-block;
238 | margin: 0.8em 0 0.8em 1.5em;
239 | font-size: 0.9em;
240 | color: #CCC;
241 | }
242 |
243 | blockquote small:before {
244 | content: "\2014 \00A0";
245 | }
246 |
247 | blockquote cite {
248 | font-weight: 700;
249 | }
250 |
251 | blockquote cite a {
252 | font-weight: normal;
253 | }
254 |
255 | mark {
256 | background-color: #fdffb6;
257 | }
258 |
259 | code,
260 | tt {
261 | padding: 1px 3px;
262 | border: #f8f8f8 1px solid;
263 | background: #f8f8f8;
264 | border-radius: 2px;
265 | white-space: pre-wrap;
266 | color: #e67e22;
267 | }
268 |
269 | pre {
270 | -moz-box-sizing: border-box;
271 | box-sizing: border-box;
272 | margin: 0 0 1.75em 0;
273 | width: 100%;
274 | padding: 10px;
275 | font-size: 0.9em;
276 | white-space: pre;
277 | overflow: auto;
278 | background: #f7f7f7;
279 | border-radius: 3px;
280 | }
281 |
282 | pre code,
283 | pre tt {
284 | font-size: inherit;
285 | white-space: pre-wrap;
286 | background: transparent;
287 | border: none;
288 | padding: 0;
289 | }
290 |
291 | kbd {
292 | display: inline-block;
293 | margin-bottom: 0.4em;
294 | padding: 1px 8px;
295 | border: #CCC 1px solid;
296 | color: #666;
297 | text-shadow: #FFF 0 1px 0;
298 | font-size: 0.9em;
299 | font-weight: 700;
300 | background: #F4F4F4;
301 | border-radius: 4px;
302 | box-shadow: 0 1px 0 rgba(0, 0, 0, 0.2), 0 1px 0 0 #ffffff inset;
303 | }
304 |
305 | table {
306 | -moz-box-sizing: border-box;
307 | box-sizing: border-box;
308 | margin: 1.75em 0;
309 | width: 100%;
310 | max-width: 100%;
311 | background-color: transparent;
312 | }
313 |
314 | table th,
315 | table td {
316 | padding: 8px;
317 | line-height: 20px;
318 | text-align: left;
319 | vertical-align: top;
320 | border-top: #EFEFEF 1px solid;
321 | }
322 |
323 | table th {
324 | color: #000;
325 | }
326 |
327 | table caption + thead tr:first-child th,
328 | table caption + thead tr:first-child td,
329 | table colgroup + thead tr:first-child th,
330 | table colgroup + thead tr:first-child td,
331 | table thead:first-child tr:first-child th,
332 | table thead:first-child tr:first-child td {
333 | border-top: 0;
334 | }
335 |
336 | table tbody + tbody {
337 | border-top: #EFEFEF 2px solid;
338 | }
339 |
340 | table table table {
341 | background-color: #FFF;
342 | }
343 |
344 | table tbody > tr:nth-child(odd) > td,
345 | table tbody > tr:nth-child(odd) > th {
346 | background-color: #F6F6F6;
347 | }
348 |
349 | table.plain tbody > tr:nth-child(odd) > td,
350 | table.plain tbody > tr:nth-child(odd) > th {
351 | background: transparent;
352 | }
353 |
354 | iframe,
355 | .fluid-width-video-wrapper {
356 | display: block;
357 | margin: 1.75em 0;
358 | }
359 | /* When a video is inside the fitvids wrapper, drop the
360 | margin on the iframe, cause it breaks stuff. */
361 |
362 | .fluid-width-video-wrapper iframe {
363 | margin: 0;
364 | }
365 | .warnning{
366 | border-left: 3px solid #f39c12;
367 | padding-left: 15px;
368 | padding-top: 10px;
369 | padding-bottom: 10px;
370 | background-color: #ddd;
371 | }
372 |
--------------------------------------------------------------------------------
/__sites_/src/less/modules/reset.less:
--------------------------------------------------------------------------------
1 | /**reset css**/
2 | html,body,div,span,
3 | applet,object,iframe,
4 | h1,h2,h3,h4,h5,h6,p,blockquote,pre,
5 | a,abbr,acronym,address,big,cite,code,
6 | del,dfn,em,font,img,ins,kbd,q,s,samp,
7 | small,strike,strong,sub,sup,tt,var,
8 | dd,dl,dt,li,ol,ul,
9 | fieldset,form,label,legend,
10 | table,caption,tbody,tfoot,thead,tr,th,td {
11 | margin: 0;
12 | padding: 0;
13 | border: 0;
14 | }
15 | table {
16 | border-collapse: collapse;
17 | border-spacing: 0;
18 | }
19 | ol,ul {
20 | list-style: none;
21 | }
22 |
23 | q:before,q:after,
24 | blockquote:before,blockquote:after {
25 | content: "";
26 | }
27 | /**base style**/
28 | body{
29 | font-family: '\5FAE\8F6F\96C5\9ED1','\9ED1\4F53',arial,sans-serif;
30 | font-size: @font-size-base;
31 | background-color: @bgcolor;
32 | }
33 |
--------------------------------------------------------------------------------
/__sites_/src/less/plugins/angular.progress.less:
--------------------------------------------------------------------------------
1 | /* Styling for the ngProgress itself */
2 | #ngProgress {
3 | margin: 0;
4 | padding: 0;
5 | z-index: 99998;
6 | background-color: @nephritis !important;
7 | color: @nephritis;
8 | box-shadow:none; /* Inherits the font color */
9 | height: 2px;
10 | opacity: 0;
11 |
12 | /* Add CSS3 styles for transition smoothing */
13 | -webkit-transition: all 0.5s ease-in-out;
14 | -moz-transition: all 0.5s ease-in-out;
15 | -o-transition: all 0.5s ease-in-out;
16 | transition: all 0.5s ease-in-out;
17 | }
18 |
19 | /* Styling for the ngProgress-container */
20 | #ngProgress-container {
21 | position: fixed;
22 | margin: 0;
23 | padding: 0;
24 | top: 0;
25 | left: 0;
26 | right: 0;
27 | z-index: 99999;
28 | }
--------------------------------------------------------------------------------
/__sites_/src/less/plugins/hljs.theme.less:
--------------------------------------------------------------------------------
1 | /** view doc https://highlightjs.org **/
2 | .hljs {
3 | display: block;
4 | overflow-x: auto;
5 | padding: 0.5em;
6 | background: #f7f7f7;
7 | line-height: 1.65;
8 | }
9 | .hljs,
10 | .hljs-subst {
11 | color: #2c3e50;
12 | }
13 | .hljs-comment {
14 | color: #999;
15 | }
16 | .hljs-keyword,
17 | .hljs-attribute,
18 | .hljs-selector-tag,
19 | .hljs-meta-keyword,
20 | .hljs-doctag,
21 | .hljs-name {
22 | font-weight: bold;
23 | }
24 | .hljs-type,
25 | .hljs-string,
26 | .hljs-number,
27 | .hljs-selector-id,
28 | .hljs-selector-class,
29 | .hljs-quote,
30 | .hljs-template-tag,
31 | .hljs-deletion {
32 | color: #e74c3c;
33 | }
34 | .hljs-title,
35 | .hljs-section {
36 | color: #e74c3c;
37 | font-weight: bold;
38 | }
39 | .hljs-regexp,
40 | .hljs-symbol,
41 | .hljs-variable,
42 | .hljs-template-variable,
43 | .hljs-link,
44 | .hljs-selector-attr,
45 | .hljs-selector-pseudo {
46 | color: #bc6060;
47 | }
48 | .hljs-literal {
49 | color: #78a960;
50 | }
51 | .hljs-built_in,
52 | .hljs-bullet,
53 | .hljs-code,
54 | .hljs-addition {
55 | color: #16a085;
56 | }
57 | .hljs-meta {
58 | color: #2980b9;
59 | }
60 | .hljs-meta-string {
61 | color: #2980b9;
62 | }
63 | .hljs-emphasis {
64 | font-style: italic;
65 | }
66 | .hljs-strong {
67 | font-weight: bold;
68 | }
69 |
70 |
--------------------------------------------------------------------------------
/__sites_/src/less/vtui.less:
--------------------------------------------------------------------------------
1 | /** vtui
2 | ** https://github.com/Vanthink-UED/VTUI @Vanthink-UED
3 | **/
4 |
5 | @import "./config.less";
6 | @import "modules/reset.less";
7 | @import "modules/m-icon.less";
8 | @import "modules/m-layout.less";
9 | @import "modules/m-type.less";
10 | @import "modules/m-button.less";
11 | @import "modules/m-table.less";
12 | @import "modules/m-pagination.less";
13 | @import "modules/m-pages.less";
14 |
15 |
16 | /**the plugins**/
17 |
18 | @import "plugins/angular.progress.less";
19 | @import "plugins/hljs.theme.less";
20 |
--------------------------------------------------------------------------------
/__sites_/src/lib/constants.js:
--------------------------------------------------------------------------------
1 | // all routers
2 | let routers = [
3 | {
4 | name: 'Home',
5 | cn_name: '首页',
6 | },
7 | {
8 | name: 'Get Started',
9 | cn_name: '快速开始',
10 | },
11 | {
12 | name: 'Props',
13 | cn_name: '基本属性',
14 | },
15 | {
16 | name: 'Events',
17 | cn_name: '响应事件',
18 | },
19 | {
20 | name: 'Custom Component',
21 | cn_name: '自定义组件'
22 | },
23 | {
24 | name: 'Crop Image',
25 | cn_name: '裁剪图片'
26 | },
27 | {
28 | name: 'Resize Image',
29 | cn_name: '调整图片'
30 | },
31 | {
32 | name: 'Multiple File',
33 | cn_name: '上传多文件'
34 | },
35 | {
36 | name: 'Compress Image',
37 | cn_name: '压缩图片',
38 | },
39 | {
40 | name: 'Post Data',
41 | cn_name: '向服务端发送数据',
42 | },
43 | {
44 | name: 'Others',
45 | cn_name: '其他 & 问题反馈',
46 | }
47 | ]
48 |
49 | for (const item of routers) {
50 | item.url = item['name'].toLowerCase().replace(/\s+/g, '-');
51 | }
52 | export {
53 | routers
54 | }
55 |
--------------------------------------------------------------------------------
/__sites_/src/lib/vendor.js:
--------------------------------------------------------------------------------
1 | export default {
2 | getLocalData(key) {
3 | let data = '';
4 | try {
5 | data = localStorage.getItem(key);
6 | } catch (ew) {
7 | console.log('Warnning: cannot get data')
8 | }
9 | return data;
10 | },
11 |
12 | setLocalData(key, data) {
13 | localStorage.setItem(key, data);
14 | }
15 |
16 |
17 |
18 | }
19 |
--------------------------------------------------------------------------------
/__sites_/webpack.config.js:
--------------------------------------------------------------------------------
1 | var webpack = require('webpack');
2 |
3 | module.exports = {
4 | entry: [
5 | "webpack-dev-server/client?http://localhost:9000",
6 | 'webpack/hot/only-dev-server',
7 | "./src/index"
8 | ],
9 | output: {
10 | path: __dirname + '/build',
11 | filename: "bundle.js",
12 | publicPath: '/build/',
13 | },
14 | devtool: 'inline-source-map',
15 | module: {
16 | loaders: [
17 | {
18 | test: /\.js?$/,
19 | loaders: ["react-hot-loader/webpack",'babel-loader?presets[]=react,presets[]=es2015'],
20 | exclude: /node_modules/
21 | },
22 | {
23 | test: /\.less$/,
24 | loader: "style!css!less"
25 | }
26 | ]
27 | },
28 | plugins: [
29 | new webpack.NoErrorsPlugin(),
30 | new webpack.HotModuleReplacementPlugin()
31 | ]
32 | };
33 |
--------------------------------------------------------------------------------
/karma.conf.js:
--------------------------------------------------------------------------------
1 | var path = require('path');
2 |
3 | var browsers = ['Chrome'];
4 | // trvis env
5 |
6 | if (process.env.TRAVIS) {
7 | browsers = ['Chrome_travis_ci'];
8 | }
9 |
10 |
11 | module.exports = function(config) {
12 | config.set({
13 | basePath: '',
14 | frameworks: ['jasmine'],
15 | files: [
16 | './tests/**/*.js'
17 | ],
18 |
19 | preprocessors: {
20 | // add webpack as preprocessor
21 | 'src/**/*.js': ['webpack', 'sourcemap'],
22 | 'tests/**/*.test.js': ['webpack', 'sourcemap']
23 | },
24 | // webpack file
25 | webpack: {
26 | devtool: 'inline-source-map', //just do inline source maps instead of the default
27 | module: {
28 | loaders: [
29 | {
30 | test: /\.js$/,
31 | loader: 'babel',
32 | exclude: path.resolve(__dirname, 'node_modules'),
33 | query: {
34 | presets: ['airbnb']
35 | }
36 | },
37 | {
38 | test: /\.json$/,
39 | loader: 'json',
40 | },
41 | ]
42 | },
43 | externals: {
44 | 'react/addons': true,
45 | 'react/lib/ExecutionEnvironment': true,
46 | 'react/lib/ReactContext': true
47 | }
48 | },
49 |
50 | webpackServer: {
51 | noInfo: true //please don't spam the console when running in karma!
52 | },
53 |
54 | plugins: [
55 | 'karma-webpack',
56 | 'karma-jasmine',
57 | 'karma-sourcemap-loader',
58 | 'karma-chrome-launcher',
59 | ],
60 |
61 | babelPreprocessor: {
62 | options: {
63 | presets: ['airbnb']
64 | }
65 | },
66 | reporters: ['progress'],
67 | // custom launchers
68 | customLaunchers: {
69 | Chrome_travis_ci: {
70 | base: 'Chrome',
71 | flags: ['--no-sandbox']
72 | }
73 | },
74 | // port: 9002,
75 | logLevel: config.LOG_INFO,
76 | browsers: browsers,
77 | singleRun: false
78 | })
79 |
80 | };
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "react-core-image-upload",
3 | "version": "2.2.2",
4 | "description": "a component for image to upload and crop",
5 | "keywords": [
6 | "react",
7 | "iamge",
8 | "upload",
9 | "crop",
10 | "component",
11 | "core"
12 | ],
13 | "main": "./react-core-image-upload.js",
14 | "repository": {
15 | "url": "https://github.com/Vanthink-UED/react-core-image-upload.git",
16 | "type": "git"
17 | },
18 | "scripts": {
19 | "start": "webpack-dev-server --port 9000",
20 | "build": "webpack --config ./webpack.config.build.js --progress --colors",
21 | "test": "karma start karma.conf.js"
22 | },
23 | "author": "JackPu ",
24 | "license": "MIT",
25 | "dependencies": {
26 | "core-image-xhr": "^1.0.3",
27 | "prop-types": "^15.5.10"
28 | },
29 | "peerDependencies": {
30 | "react": "^15.3.2"
31 | },
32 | "devDependencies": {
33 | "babel": "^6.5.2",
34 | "babel-core": "^6.17.0",
35 | "babel-loader": "^6.2.5",
36 | "babel-preset-airbnb": "^2.1.1",
37 | "babel-preset-es2015": "^6.16.0",
38 | "babel-preset-react": "^6.16.0",
39 | "browserify": "^13.1.0",
40 | "css-loader": "^0.26.2",
41 | "enzyme": "^2.5.1",
42 | "eslint": "^3.17.0",
43 | "eslint-config-airbnb": "^14.1.0",
44 | "eslint-plugin-import": "^2.2.0",
45 | "eslint-plugin-jsx-a11y": "^4.0.0",
46 | "eslint-plugin-react": "^6.10.0",
47 | "expect": "^1.20.2",
48 | "history": "^4.3.0",
49 | "jasmine-core": "^2.5.2",
50 | "json-loader": "^0.5.4",
51 | "karma": "^1.3.0",
52 | "karma-browserify": "^5.1.0",
53 | "karma-chrome-launcher": "^2.0.0",
54 | "karma-jasmine": "^1.0.2",
55 | "karma-mocha": "^1.2.0",
56 | "karma-sourcemap-loader": "^0.3.7",
57 | "karma-webpack": "^1.8.0",
58 | "mocha": "^3.1.2",
59 | "react-addons-test-utils": "^15.3.2",
60 | "react": "^15.3.2",
61 | "react-dom": "^15.3.2",
62 | "react-hot-loader": "^3.0.0-beta.6",
63 | "style-loader": "*",
64 | "watchify": "^3.7.0",
65 | "webpack": "^1.13.2",
66 | "webpack-dev-server": "^1.16.2"
67 | },
68 | "engines": {
69 | "node": ">=4.0.0",
70 | "npm": "~3.3.6"
71 | }
72 | }
--------------------------------------------------------------------------------
/server.js:
--------------------------------------------------------------------------------
1 | var webpack = require('webpack');
2 | var WebpackDevServer = require('webpack-dev-server');
3 | var config = require('./webpack.config');
4 |
5 | new WebpackDevServer(webpack(config), {
6 | publicPath: config.output.publicPath,
7 | hot: true,
8 | historyApiFallback: true
9 | }).listen(9000, 'localhost', function (err, result) {
10 | if (err) {
11 | return console.log(err);
12 | }
13 |
14 | console.log('Listening at http://localhost:9000/');
15 | });
--------------------------------------------------------------------------------
/shots/react-core-image-upload.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Vanthink-UED/react-core-image-upload/8423d7e609009c93864fc4838898f73371639e1c/shots/react-core-image-upload.jpg
--------------------------------------------------------------------------------
/shots/vuedba0ed377b88fc84d51026310efcb255b.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Vanthink-UED/react-core-image-upload/8423d7e609009c93864fc4838898f73371639e1c/shots/vuedba0ed377b88fc84d51026310efcb255b.png
--------------------------------------------------------------------------------
/src/components/crop.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import PropTypes from 'prop-types';
3 | import ResizeBar from './resize-bar';
4 | import helper from '../lib/helper';
5 | import resize from '../lib/resize';
6 | import drag from '../lib/drag';
7 | import GIF_LOADING_SRC from '../lib/loading-gif';
8 |
9 | const CROPBOX_PERCENT = 75;
10 | const isMobile = helper.isMobile;
11 | const areaWidth = window.innerWidth - 60;
12 | const areaHeight = window.innerHeight - 110;
13 |
14 | export default class Crop extends React.Component {
15 | constructor(props) {
16 | super(props);
17 | this.state = {
18 | left: 0,
19 | top: 0,
20 | width: 0,
21 | height: 0,
22 | cropCSS: {
23 | left: 0,
24 | top: 0,
25 | width: 0,
26 | height: 0,
27 | },
28 | src: GIF_LOADING_SRC,
29 | };
30 | this.drag = this.drag.bind(this);
31 | this.resize = this.resize.bind(this);
32 | this.resizeImage = this.resizeImage.bind(this);
33 | }
34 |
35 | render() {
36 | return (
37 |
38 |
39 |
43 |
53 |
57 |
58 | {!this.props.isResize ?
59 |
60 |
68 |
69 |
77 |
78 |
86 |
87 |
95 |
96 |
97 | : null}
98 | { !this.props.isResize ?
99 |
108 |
109 |
110 |
114 |
115 |
116 | : ''}
117 |
118 |
119 |
120 |
121 | );
122 | }
123 |
124 | setImage(src, w, h) {
125 | this.setState({
126 | src,
127 | })
128 | if (this.props.ratio.indexOf(':') > 0) {
129 | this.ratioW = this.props.ratio.split(':')[0];
130 | this.ratioH = this.props.ratio.split(':')[1];
131 | this.ratioVal = this.ratioW / this.ratioH;
132 | } else {
133 | this.ratioW = 1;
134 | this.ratioH = 1;
135 | this.ratioVal = this.ratio;
136 | }
137 | this.natrualWidth = w;
138 | this.natrualHeight = h;
139 | this.setLayout(w, h);
140 | const resizeBar = this.refs.resizeBar;
141 | if (this.props.isResize) {
142 | resizeBar.setProgress(100);
143 | } else {
144 | resizeBar.setProgress(0);
145 | }
146 | return this.imgChangeRatio;
147 | }
148 |
149 | resizeImage(progress) {
150 | let w,
151 | h;
152 | if (this.props.isResize) {
153 | w = this.natrualWidth * this.imgChangeRatio * progress;
154 | h = this.natrualHeight * this.imgChangeRatio * progress;
155 | } else {
156 | w = this.initWidth + (progress * (this.natrualWidth - this.initWidth));
157 | h = this.initHeight + (progress * (this.natrualHeight - this.initHeight));
158 | }
159 | if (w <= this.props.minWidth || h < this.props.minHeight) {
160 | return;
161 | }
162 | this.setState({
163 | left: this.state.left + ((this.state.width - w) / 2),
164 | top: this.state.top + ((this.state.height - h) / 2),
165 | width: w,
166 | height: h,
167 | });
168 | this.imgChangeRatio = this.width / this.natrualWidth;
169 | }
170 |
171 | setLayout(w, h) {
172 | let H = areaHeight,
173 | W = areaWidth,
174 | width = w,
175 | height = h,
176 | marginLeft = 0,
177 | marginTop = 0;
178 | // caculate the image ratio
179 | let R = width / height;
180 | let Rs = W / H;
181 | if (R > Rs) {
182 | height = H;
183 | width = H * R;
184 | marginLeft = (W - (H * R)) / 2;
185 | } else {
186 | width = H * R,
187 | height = H;
188 | marginLeft = (W - (H * R)) / 2;
189 | }
190 | this._setStyle(width, height, marginLeft, marginTop, R, true);
191 | }
192 |
193 | _setStyle(w, h, ml, mt, r, isInit) {
194 | const $container = this.__find('.g-crop-image-principal');
195 | if(!isInit) {
196 | this.marginLeft = this.marginLeft + (this.width - w) / 2;
197 | this.marginTop = this.marginTop + (this.height - h) / 2;
198 | }
199 | $container.style.cssText = 'width:' + w + 'px;height:' + h + 'px;margin-left:'
200 | + ml + 'px;' + 'margin-top:' + mt + 'px';
201 | const cropCSS = this.setCropBox(w, h);
202 | let width;
203 | let height;
204 | if (this.props.isResize) {
205 | this.setState({
206 | width: w,
207 | height: h,
208 | });
209 | } else {
210 | if (r >= 1) {
211 | height = cropCSS.height;
212 | width = height * r;
213 | } else {
214 | width = cropCSS.width;
215 | height = width / r;
216 | }
217 | this.initWidth = width;
218 | this.initHeight = height;
219 | const left = (w - width) / 2;
220 | const top = (h - height) / 2;
221 | this.setState({
222 | width,
223 | height,
224 | left,
225 | top,
226 | });
227 | }
228 | this.imgChangeRatio = width / this.natrualWidth;
229 | }
230 |
231 | setCropBox(w, h, r) {
232 | if (this.props.isResize) {
233 | return;
234 | }
235 | let $selectCropBox = this.refs['cropbox'];
236 | let $wrap = this.refs['container'];
237 | let imageWidth = w,
238 | imageHeight = h,
239 | ratioW = this.ratioW,
240 | ratioH = this.ratioH;
241 | let cropWidth = imageWidth;
242 | if (areaWidth < w) {
243 | cropWidth = areaWidth;
244 | }
245 | const baseCropWidth = (cropWidth / 100) * CROPBOX_PERCENT;
246 | const CSSObj = {
247 | width: baseCropWidth,
248 | height: (baseCropWidth / ratioW) * ratioH,
249 | }
250 | CSSObj.left = (imageWidth - baseCropWidth) / 2;
251 | CSSObj.top = (imageHeight - CSSObj.height) / 2;
252 | $selectCropBox.style.cssText = helper.setCssText(CSSObj);
253 | if (CSSObj.height > imageHeight) {
254 | const baseCropHeight = (imageHeight / 100) * CROPBOX_PERCENT
255 | CSSObj.height = baseCropHeight;
256 | CSSObj.width = (CSSObj.height * ratioW) / ratioH;
257 | CSSObj.left = (imageWidth - CSSObj.width) / 2;
258 | CSSObj.top = (imageHeight - CSSObj.height) / 2;
259 | $selectCropBox.style.cssText = helper.setCssText(CSSObj);
260 | }
261 | this.setState({
262 | cropCSS: CSSObj,
263 | });
264 | return CSSObj;
265 | }
266 |
267 | getCropData() {
268 | // keep compatible with old api
269 | if (this.props.isResize) {
270 | return {
271 | imgChangeRatio: this.imgChangeRatio,
272 | toCropImgX: 0,
273 | toCropImgY: 0,
274 | toCropImgW: this.natrualWidth,
275 | toCropImgH: this.natrualHeight,
276 | };
277 | }
278 | return {
279 | toCropImgX: (this.state.cropCSS.left - this.state.left) / this.imgChangeRatio,
280 | toCropImgY: (this.state.cropCSS.top - this.state.top) / this.imgChangeRatio,
281 | toCropImgW: this.state.cropCSS.width / this.imgChangeRatio,
282 | toCropImgH: this.state.cropCSS.height / this.imgChangeRatio,
283 | };
284 | }
285 |
286 | getCropImage() {
287 | return this.refs['crop-image'];
288 | }
289 |
290 | __find(str) {
291 | let dq = this.refs['container'];
292 | return dq.querySelector(str);
293 | }
294 |
295 | resize(e) {
296 | e.stopPropagation();
297 | if (!this.props.ratio.indexOf(':')) {
298 | return;
299 | }
300 | let $el = e.target.parentElement;
301 | let $container = this.__find('.g-crop-image-principal');
302 | if (this._$container) {
303 | this._$container = container;
304 | }
305 | const self = this;
306 | const coor = {
307 | x: helper.isMobile ? e.touches[0].clientX : e.clientX,
308 | y: helper.isMobile ? e.touches[0].clientY : e.clientY,
309 | w: $el.offsetWidth,
310 | h: $el.offsetHeight,
311 | };
312 | this.el = $el;
313 | this.container = $container;
314 | const move = function (ev) {
315 | const newCropStyle = resize(ev, self.el, $container, coor, self.ratioVal);
316 | if (newCropStyle) {
317 | self.setState({
318 | cropCSS:{
319 | width: newCropStyle.width,
320 | height: newCropStyle.height,
321 | }
322 | });
323 | }
324 | };
325 | const end = function (ev) {
326 | this.el = null;
327 | if (helper.isMobile) {
328 | document.removeEventListener('touchmove', move, false);
329 | document.removeEventListener('touchend', end, false);
330 | }
331 | document.removeEventListener('mousemove', move, false);
332 | document.removeEventListener('mouseup', end, false);
333 | };
334 | if (helper.isMobile) {
335 | document.addEventListener('touchmove', move, false);
336 | document.addEventListener('touchend', end, false);
337 | }
338 | document.addEventListener('mousemove', move, false);
339 | document.addEventListener('mouseup', end, false);
340 | }
341 |
342 | drag(e) {
343 | e.preventDefault();
344 | const $el = this.__find('.image-wrap');
345 | this.el = $el;
346 | const $cropBox = this.__find('.crop-box');
347 | const $container = e.currentTarget;
348 | const self = this;
349 | const isMobile = helper.isMobile;
350 | const coor = {
351 | x: (isMobile ? e.touches[0]['clientX'] : e.clientX) - $el.offsetLeft,
352 | y: (isMobile ? e.touches[0]['clientY'] : e.clientY) - $el.offsetTop,
353 | maxLeft: $cropBox.offsetLeft,
354 | maxTop: $cropBox.offsetTop,
355 | minLeft: ($cropBox.offsetWidth + $cropBox.offsetLeft) - $el.offsetWidth,
356 | minTop: ($cropBox.offsetHeight + $cropBox.offsetTop) - $el.offsetHeight,
357 | };
358 | const move = function (ev) {
359 | const newCropStyle = drag(ev, self.el, coor);
360 | if (newCropStyle) {
361 | self.setState({
362 | left: newCropStyle.left,
363 | top: newCropStyle.top,
364 | })
365 | }
366 | };
367 | const stopMove = function (ev) {
368 | self.el = null;
369 | if (isMobile) {
370 | document.removeEventListener('touchmove', move, false);
371 | document.removeEventListener('touchend', stopMove, false);
372 | return;
373 | }
374 | document.removeEventListener('mousemove', move, false);
375 | document.removeEventListener('mouseup', stopMove, false);
376 | };
377 | if (isMobile) {
378 | document.addEventListener('touchmove', move, false);
379 | document.addEventListener('touchend', stopMove, false);
380 | return;
381 | }
382 | document.addEventListener('mousemove', move, false);
383 | document.addEventListener('mouseup', stopMove, false);
384 | }
385 |
386 | }
387 |
388 | Crop.propTypes = {
389 | isResize: PropTypes.bool,
390 | minWidth: PropTypes.number,
391 | minHeight: PropTypes.number,
392 | ratio: PropTypes.string,
393 | };
394 |
395 | Crop.defaultProps = {
396 | isResize: false,
397 | minWidth: 50,
398 | minHeight: 50,
399 | ratio: '1:1',
400 | };
401 |
--------------------------------------------------------------------------------
/src/components/resize-bar.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import PropTypes from 'prop-types';
3 | import helper from '../lib/helper';
4 | import drag from '../lib/drag';
5 |
6 | export default class ResizeBar extends React.Component {
7 | constructor(props) {
8 | super(props);
9 | this.state = {
10 | left: 0,
11 | };
12 | this.drag = this.drag.bind(this);
13 | }
14 |
15 | render() {
16 | return (
17 |
31 | );
32 | }
33 |
34 | setProgress(num) {
35 | this.setState({
36 | left: num,
37 | })
38 | }
39 |
40 | drag(e) {
41 | const $el = e.target;
42 | this.el = $el;
43 | const $container = this.refs.container.parentElement;
44 | const self = this;
45 | const isMobile = helper.isMobile;
46 | const width = 200;
47 | const coor = {
48 | x: (isMobile ? e.touches[0]['clientX'] : e.clientX) - $el.offsetLeft,
49 | y: (isMobile ? e.touches[0]['clientY'] : e.clientY) - $el.offsetTop,
50 | maxLeft: width,
51 | maxTop: parseInt($container.style.height) - parseInt($el.style.height),
52 | minLeft: 0,
53 | minTop: 0,
54 | };
55 | const move = function (ev) {
56 | const newCoor = drag(ev, self.el, coor);
57 | if (newCoor) {
58 | if((newCoor.left / width) < self.minProgress) {
59 | return;
60 | }
61 | self.progress = newCoor.left / width;
62 | self.setState({
63 | left: newCoor.left / width * 100,
64 | });
65 | self.props.resize(self.progress);
66 | }
67 | };
68 | const stopMove = function (ev) {
69 | self.el = null;
70 | if (isMobile) {
71 | document.removeEventListener('touchmove', move, false);
72 | document.removeEventListener('touchend', stopMove, false);
73 | return;
74 | }
75 | document.removeEventListener('mousemove', move, false);
76 | document.removeEventListener('mouseup', stopMove, false);
77 | };
78 | if (isMobile) {
79 | document.addEventListener('touchmove', move, false);
80 | document.addEventListener('touchend', stopMove, false);
81 | return;
82 | }
83 | document.addEventListener('mousemove', move, false);
84 | document.addEventListener('mouseup', stopMove, false);
85 | }
86 | };
87 | ResizeBar.PropTypes = {
88 | minProgress: PropTypes.minProgress,
89 | resize: PropTypes.function,
90 | };
91 | ResizeBar.defaultProps = {
92 | minProgress: 0,
93 | };
94 |
--------------------------------------------------------------------------------
/src/index.js:
--------------------------------------------------------------------------------
1 | import ReactCoreImageUpload from './react-core-image-upload';
2 |
3 | module.exports = ReactCoreImageUpload;
4 |
--------------------------------------------------------------------------------
/src/lib/canvas-helper.js:
--------------------------------------------------------------------------------
1 | /**
2 | * compress image
3 | * reference https://github.com/brunobar79/J-I-C
4 | **/
5 |
6 | export default {
7 | _getImageType(str) {
8 | let mimeType = 'image/jpeg';
9 | const outputType = str.match(/(image\/[\w]+)\.*/)[0];
10 | if (typeof outputType !== 'undefined'){
11 | mimeType = outputType;
12 | }
13 | return mimeType;
14 | },
15 |
16 | compress (src, quality, callback) {
17 | const reader = new FileReader();
18 | const self = this;
19 | reader.onload = function(event) {
20 | let image = new Image();
21 | image.src = event.target.result;
22 | image.onload = function() {
23 | const mimeType = self._getImageType(src.type);
24 | const cvs = self._getCanvas(image.naturalWidth, image.naturalHeight);
25 | const ctx = cvs.getContext("2d").drawImage(image, 0, 0);
26 | const newImageData = cvs.toDataURL(mimeType, quality/100);
27 | callback(newImageData);
28 | }
29 | };
30 | reader.readAsDataURL(src);
31 | },
32 | /**
33 | * crop image via canvas and generate data
34 | **/
35 | crop(image, options, callback) {
36 | const checkNumber = function(num) {
37 | return (typeof num === 'number');
38 | };
39 | // check crop options
40 | if(checkNumber(options.toCropImgX) && checkNumber(options.toCropImgY) && options.toCropImgW > 0 && options.toCropImgH > 0) {
41 | let w = options.toCropImgW;
42 | let h = options.toCropImgH;
43 | if(options.maxWidth && options.maxWidth < w) {
44 | w = options.maxWidth;
45 | h = options.toCropImgH * w / options.toCropImgW;
46 | }
47 | if (options.maxHeight && options.maxHeight < h) {
48 | h = options.maxHeight
49 | }
50 | const cvs = this._getCanvas(w, h);
51 | const ctx = cvs.getContext('2d').drawImage(image, options.toCropImgX, options.toCropImgY, options.toCropImgW, options.toCropImgH, 0 , 0, w, h);
52 | const mimeType = this._getImageType(image.src);
53 | const data = cvs.toDataURL(mimeType, options.compress/100);
54 | callback(data);
55 | }
56 | },
57 |
58 | resize(image, options, callback) {
59 | const checkNumber = function(num) {
60 | return (typeof num === 'number');
61 | };
62 | if(checkNumber(options.toCropImgX) && checkNumber(options.toCropImgY) && options.toCropImgW > 0 && options.toCropImgH > 0) {
63 | let w = options.toCropImgW * options.imgChangeRatio;
64 | let h = options.toCropImgH * options.imgChangeRatio;
65 | const cvs = this._getCanvas(w, h);
66 | const ctx = cvs.getContext('2d').drawImage(image, 0, 0, options.toCropImgW, options.toCropImgH, 0 , 0, w , h);
67 | const mimeType = this._getImageType(image.src);
68 | const data = cvs.toDataURL(mimeType, options.compress/100);
69 | callback(data);
70 | }
71 | },
72 |
73 | _loadImage(data, callback) {
74 | const image = new Image();
75 | image.src = data;
76 | image.onload = function () {
77 | callback(image);
78 | }
79 | image.onerror = function() {
80 | console.log('Error: image error!');
81 | }
82 | },
83 |
84 | _getCanvas(width, height) {
85 | const canvas = document.createElement('canvas');
86 | canvas.width = width;
87 | canvas.height = height;
88 | return canvas;
89 | },
90 |
91 | };
92 |
--------------------------------------------------------------------------------
/src/lib/drag.js:
--------------------------------------------------------------------------------
1 | import helper from './helper';
2 |
3 | const isMobile = helper.isMobile;
4 | export default function drag(e, el, coor) {
5 | if (!el) {
6 | return;
7 | }
8 | const currentX = isMobile ? e.changedTouches[0]['clientX'] : e.clientX;
9 | const currentY = isMobile ? e.changedTouches[0]['clientY'] : e.clientY;
10 |
11 | let left = currentX - coor.x;
12 | let top = currentY - coor.y;
13 | if (left <= coor.minLeft) {
14 | left = coor.minLeft;
15 | }
16 | if (left >= coor.maxLeft) {
17 | left = coor.maxLeft;
18 | }
19 | if (top <= coor.minTop) {
20 | top = coor.minTop;
21 | }
22 | if (top >= coor.maxTop) {
23 | top = coor.maxTop;
24 | }
25 | return {
26 | left,
27 | top
28 | };
29 | };
30 |
--------------------------------------------------------------------------------
/src/lib/error-code.js:
--------------------------------------------------------------------------------
1 | const SERVER_ERROR = 2500;
2 | const FILE_SIZE_ERROR = 2501;
3 | const FILE_FORMAT = 2502;
4 |
5 | export default {
6 | SERVER_ERROR,
7 | FILE_SIZE_ERROR,
8 | FILE_FORMAT,
9 | };
10 |
--------------------------------------------------------------------------------
/src/lib/helper.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | isMobile: /Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(navigator.userAgent),
3 |
4 | setCssText: function(obj) {
5 | var cssArr = [];
6 | for(var key in obj) {
7 | var val = obj[key];
8 | if (typeof val === 'number') {
9 | val = '' + val + 'px';
10 | }
11 | cssArr.push(key + ': ' + val + ';');
12 | }
13 | return cssArr.join('');
14 | }
15 | };
16 |
--------------------------------------------------------------------------------
/src/lib/loading-gif.js:
--------------------------------------------------------------------------------
1 | const GIF_LOADING_SRC = 'data:image/gif;base64,R0lGODlhGAAYAPQAAP///3FxcePj4/v7++3t7dLS0vHx8b+/v+Dg4MfHx+jo6M7Oztvb2/f397Kysru7u9fX16qqqgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACH/C05FVFNDQVBFMi4wAwEAAAAh/hpDcmVhdGVkIHdpdGggYWpheGxvYWQuaW5mbwAh+QQJBwAAACwAAAAAGAAYAAAFriAgjiQAQWVaDgr5POSgkoTDjFE0NoQ8iw8HQZQTDQjDn4jhSABhAAOhoTqSDg7qSUQwxEaEwwFhXHhHgzOA1xshxAnfTzotGRaHglJqkJcaVEqCgyoCBQkJBQKDDXQGDYaIioyOgYSXA36XIgYMBWRzXZoKBQUMmil0lgalLSIClgBpO0g+s26nUWddXyoEDIsACq5SsTMMDIECwUdJPw0Mzsu0qHYkw72bBmozIQAh+QQJBwAAACwAAAAAGAAYAAAFsCAgjiTAMGVaDgR5HKQwqKNxIKPjjFCk0KNXC6ATKSI7oAhxWIhezwhENTCQEoeGCdWIPEgzESGxEIgGBWstEW4QCGGAIJEoxGmGt5ZkgCRQQHkGd2CESoeIIwoMBQUMP4cNeQQGDYuNj4iSb5WJnmeGng0CDGaBlIQEJziHk3sABidDAHBgagButSKvAAoyuHuUYHgCkAZqebw0AgLBQyyzNKO3byNuoSS8x8OfwIchACH5BAkHAAAALAAAAAAYABgAAAW4ICCOJIAgZVoOBJkkpDKoo5EI43GMjNPSokXCINKJCI4HcCRIQEQvqIOhGhBHhUTDhGo4diOZyFAoKEQDxra2mAEgjghOpCgz3LTBIxJ5kgwMBShACREHZ1V4Kg1rS44pBAgMDAg/Sw0GBAQGDZGTlY+YmpyPpSQDiqYiDQoCliqZBqkGAgKIS5kEjQ21VwCyp76dBHiNvz+MR74AqSOdVwbQuo+abppo10ssjdkAnc0rf8vgl8YqIQAh+QQJBwAAACwAAAAAGAAYAAAFrCAgjiQgCGVaDgZZFCQxqKNRKGOSjMjR0qLXTyciHA7AkaLACMIAiwOC1iAxCrMToHHYjWQiA4NBEA0Q1RpWxHg4cMXxNDk4OBxNUkPAQAEXDgllKgMzQA1pSYopBgonCj9JEA8REQ8QjY+RQJOVl4ugoYssBJuMpYYjDQSliwasiQOwNakALKqsqbWvIohFm7V6rQAGP6+JQLlFg7KDQLKJrLjBKbvAor3IKiEAIfkECQcAAAAsAAAAABgAGAAABbUgII4koChlmhokw5DEoI4NQ4xFMQoJO4uuhignMiQWvxGBIQC+AJBEUyUcIRiyE6CR0CllW4HABxBURTUw4nC4FcWo5CDBRpQaCoF7VjgsyCUDYDMNZ0mHdwYEBAaGMwwHDg4HDA2KjI4qkJKUiJ6faJkiA4qAKQkRB3E0i6YpAw8RERAjA4tnBoMApCMQDhFTuySKoSKMJAq6rD4GzASiJYtgi6PUcs9Kew0xh7rNJMqIhYchACH5BAkHAAAALAAAAAAYABgAAAW0ICCOJEAQZZo2JIKQxqCOjWCMDDMqxT2LAgELkBMZCoXfyCBQiFwiRsGpku0EshNgUNAtrYPT0GQVNRBWwSKBMp98P24iISgNDAS4ipGA6JUpA2WAhDR4eWM/CAkHBwkIDYcGiTOLjY+FmZkNlCN3eUoLDmwlDW+AAwcODl5bYl8wCVYMDw5UWzBtnAANEQ8kBIM0oAAGPgcREIQnVloAChEOqARjzgAQEbczg8YkWJq8nSUhACH5BAkHAAAALAAAAAAYABgAAAWtICCOJGAYZZoOpKKQqDoORDMKwkgwtiwSBBYAJ2owGL5RgxBziQQMgkwoMkhNqAEDARPSaiMDFdDIiRSFQowMXE8Z6RdpYHWnEAWGPVkajPmARVZMPUkCBQkJBQINgwaFPoeJi4GVlQ2Qc3VJBQcLV0ptfAMJBwdcIl+FYjALQgimoGNWIhAQZA4HXSpLMQ8PIgkOSHxAQhERPw7ASTSFyCMMDqBTJL8tf3y2fCEAIfkECQcAAAAsAAAAABgAGAAABa8gII4k0DRlmg6kYZCoOg5EDBDEaAi2jLO3nEkgkMEIL4BLpBAkVy3hCTAQKGAznM0AFNFGBAbj2cA9jQixcGZAGgECBu/9HnTp+FGjjezJFAwFBQwKe2Z+KoCChHmNjVMqA21nKQwJEJRlbnUFCQlFXlpeCWcGBUACCwlrdw8RKGImBwktdyMQEQciB7oACwcIeA4RVwAODiIGvHQKERAjxyMIB5QlVSTLYLZ0sW8hACH5BAkHAAAALAAAAAAYABgAAAW0ICCOJNA0ZZoOpGGQrDoOBCoSxNgQsQzgMZyIlvOJdi+AS2SoyXrK4umWPM5wNiV0UDUIBNkdoepTfMkA7thIECiyRtUAGq8fm2O4jIBgMBA1eAZ6Knx+gHaJR4QwdCMKBxEJRggFDGgQEREPjjAMBQUKIwIRDhBDC2QNDDEKoEkDoiMHDigICGkJBS2dDA6TAAnAEAkCdQ8ORQcHTAkLcQQODLPMIgIJaCWxJMIkPIoAt3EhACH5BAkHAAAALAAAAAAYABgAAAWtICCOJNA0ZZoOpGGQrDoOBCoSxNgQsQzgMZyIlvOJdi+AS2SoyXrK4umWHM5wNiV0UN3xdLiqr+mENcWpM9TIbrsBkEck8oC0DQqBQGGIz+t3eXtob0ZTPgNrIwQJDgtGAgwCWSIMDg4HiiUIDAxFAAoODwxDBWINCEGdSTQkCQcoegADBaQ6MggHjwAFBZUFCm0HB0kJCUy9bAYHCCPGIwqmRq0jySMGmj6yRiEAIfkECQcAAAAsAAAAABgAGAAABbIgII4k0DRlmg6kYZCsOg4EKhLE2BCxDOAxnIiW84l2L4BLZKipBopW8XRLDkeCiAMyMvQAA+uON4JEIo+vqukkKQ6RhLHplVGN+LyKcXA4Dgx5DWwGDXx+gIKENnqNdzIDaiMECwcFRgQCCowiCAcHCZIlCgICVgSfCEMMnA0CXaU2YSQFoQAKUQMMqjoyAglcAAyBAAIMRUYLCUkFlybDeAYJryLNk6xGNCTQXY0juHghACH5BAkHAAAALAAAAAAYABgAAAWzICCOJNA0ZVoOAmkY5KCSSgSNBDE2hDyLjohClBMNij8RJHIQvZwEVOpIekRQJyJs5AMoHA+GMbE1lnm9EcPhOHRnhpwUl3AsknHDm5RN+v8qCAkHBwkIfw1xBAYNgoSGiIqMgJQifZUjBhAJYj95ewIJCQV7KYpzBAkLLQADCHOtOpY5PgNlAAykAEUsQ1wzCgWdCIdeArczBQVbDJ0NAqyeBb64nQAGArBTt8R8mLuyPyEAOwAAAAAAAAAAAA==';
2 |
3 | export default GIF_LOADING_SRC;
4 |
--------------------------------------------------------------------------------
/src/lib/resize.js:
--------------------------------------------------------------------------------
1 | /** Reszie
2 | * @el dom
3 | * @container dom
4 | * @ratio string '1:1' like this
5 | * e events
6 | **/
7 | import helper from './helper';
8 |
9 | const isMobile = helper.isMobile;
10 | const W = document.body.offsetWidth;
11 | export default function resize(e, el, container, coor, ratio) {
12 | if (!el) {
13 | return ;
14 | }
15 | const H = document.body.offsetHeight;
16 | const ratioRemainder = 1 / ratio;
17 | const dotBoxW = parseInt(window.getComputedStyle(container).width);
18 | const dotBoxH = parseInt(window.getComputedStyle(container).height);
19 | const $topH = document.querySelector('.info-aside');
20 | const halfX = (W - dotBoxW) / 2;
21 | const topH = parseInt(window.getComputedStyle($topH).height);
22 | const halfY = (H - dotBoxH - topH)/2;
23 | const resetX = isMobile ? e.changedTouches[0]['clientX'] : e.clientX;
24 | const resetY = isMobile ? e.changedTouches[0]['clientY'] : e.clientY;
25 | const elOffsetWidth = parseInt(el.offsetWidth);
26 | const elOffsetHeight = parseInt(el.offsetHeight);
27 | const CSSObj = {};
28 | if (ratio >= 1 && resetX <= halfX + dotBoxW) {
29 | if (elOffsetWidth >= dotBoxW) {
30 | CSSObj.width = dotBoxW;
31 | }
32 | CSSObj.width = (coor.w + resetX - coor.x);
33 | CSSObj.height = elOffsetWidth * ratioRemainder;
34 | if (dotBoxW > dotBoxH) {
35 | if (elOffsetWidth > dotBoxH) {
36 | CSSObj.height = dotBoxH;
37 | CSSObj.width = dotBoxH * ratio;
38 | }
39 | } else if (dotBoxW < dotBoxH) {
40 | if (elOffsetWidth > dotBoxW) {
41 | CSSObj.width = dotBoxW;
42 | CSSObj.height = dotBoxW * ratioRemainder;
43 | }
44 | } else if (elOffsetWidth >= dotBoxW) {
45 | CSSObj.width = dotBoxW ;
46 | CSSObj.height = dotBoxW * ratioRemainder;
47 | }
48 | } else if (ratio < 1 && resetY < (halfY + dotBoxH + topH)) {
49 | CSSObj.height = (coor.h + resetY - coor.y);
50 | CSSObj.width = parseInt(el.style.height) * ratio;
51 | // 限制拖拉的范围在图片内
52 | if (dotBoxW > dotBoxH) {
53 | if (elOffsetHeight > dotBoxH) {
54 | CSSObj.height = dotBoxH;
55 | CSSObj.width = dotBoxH * ratio;
56 | }
57 | } else if (dotBoxW < dotBoxH) {
58 | if (elOffsetWidth > dotBoxW) {
59 | CSSObj.width = dotBoxW;
60 | CSSObj.height = dotBoxW * ratioRemainder;
61 | }
62 | } else if (elOffsetWidth > dotBoxW) {
63 | CSSObj.width = dotBoxW;
64 | CSSObj.height = dotBoxW * ratioRemainder;
65 | }
66 | } else if(ratio == 'auto' && resetY <= (halfY + dotBoxH + topH) && resetX <= halfY + dotBoxW) {
67 | CSSObj.height = (coor.h + resetY - coor.y);
68 | CSSObj.width = (coor.w + resetX - coor.x);
69 | } else if (resetX <= halfX + dotBoxW) {
70 | CSSObj.width = (coor.w + resetX - coor.x);
71 | CSSObj.height = el.style.width;
72 | // limit the crop box area
73 | if (dotBoxW > dotBoxH) {
74 | if (elOffsetHeight > dotBoxH) {
75 | CSSObj.height = dotBoxH;
76 | CSSObj.width = dotBoxH;
77 | }
78 | } else if (dotBoxW < dotBoxH) {
79 | if (elOffsetWidth > dotBoxW) {
80 | CSSObj.width = dotBoxW;
81 | CSSObj.height = dotBoxW;
82 | }
83 | } else if (elOffsetWidth > dotBoxW) {
84 | CSSObj.width = el.style.height = dotBoxW;
85 | }
86 | }
87 | return CSSObj;
88 | };
89 |
--------------------------------------------------------------------------------
/src/lib/xhr.js:
--------------------------------------------------------------------------------
1 | /**
2 | * simple ajax handler
3 | **/
4 |
5 | //ADD sendAsBinary compatibilty to older browsers
6 | if (XMLHttpRequest.prototype.sendAsBinary === undefined) {
7 | XMLHttpRequest.prototype.sendAsBinary = function(string) {
8 | var bytes = Array.prototype.map.call(string, function(c) {
9 | return c.charCodeAt(0) & 0xff;
10 | });
11 | this.send(new Uint8Array(bytes).buffer);
12 | };
13 | }
14 |
15 | module.exports = function (method, url, headers, data, callback, err, isBinary) {
16 |
17 | var r = new XMLHttpRequest();
18 | var error = err || function () {
19 | console.error('AJAX ERROR!');
20 | };
21 | const boundary = 'webcodeimageupload';
22 | // Binary?
23 | let binary = false;
24 | if (method === 'blob') {
25 | binary = method;
26 | method = 'GET';
27 | }
28 | method = method.toUpperCase();
29 | // Xhr.responseType 'json' is not supported in any of the vendors yet.
30 | r.onload = function () {
31 | let json = r.response;
32 | try {
33 | json = JSON.parse(r.responseText);
34 | } catch (_e) {
35 | if (r.status === 401) {
36 | json = error('access_denied', r.statusText);
37 | }
38 | }
39 | let headers = headersToJSON(r.getAllResponseHeaders());
40 | headers.statusCode = r.status;
41 | callback(json || (method === 'GET' ? error('empty_response', 'Could not get resource') : {}), headers);
42 | };
43 | r.onerror = function () {
44 | let json = r.responseText;
45 | try {
46 | json = JSON.parse(r.responseText);
47 | } catch (_e) {
48 | console.error(_e);
49 | }
50 | callback(json || error('access_denied', 'Could not get resource'));
51 | };
52 | let x;
53 | // Should we add the query to the URL?
54 | if (method === 'GET' || method === 'DELETE') {
55 | data = null;
56 | } else if (isBinary) {
57 | const keyData = data;
58 | const code = data.base64Code.replace('data:' + data.type + ';base64,', '');
59 | data = ['--' + boundary, 'Content-Disposition: form-data; name="' + data.filed + '"; filename="' + data.filename + '"', 'Content-Type: ' + data.type, '', window.atob(code), ''].join('\r\n');
60 | const keyArr = Object.keys(keyData);
61 | if (keyArr.length > 4) {
62 | for (const k of keyArr) {
63 | if (['filed', 'filename', 'type', 'base64Code'].indexOf(k) == -1) {
64 | data += ['--' + boundary, 'Content-Disposition: form-data; name="' + k + '";', '', ''].join('\r\n');
65 | data += [typeof keyData[k] === 'object' ? JSON.stringify(keyData[k]) : encodeURI(keyData[k]), ''].join('\r\n');
66 | }
67 | }
68 | }
69 | data += '--' + boundary + '--';
70 | }
71 | // Open the path, async
72 | r.open(method, url, true);
73 | if (binary) {
74 | if ('responseType' in r) {
75 | r.responseType = binary;
76 | }
77 | else {
78 | r.overrideMimeType('text/plain; charset=x-user-defined');
79 | }
80 | }
81 | // Set any bespoke headers
82 | if (headers) {
83 | for (x in headers) {
84 | r.setRequestHeader(x, headers[x]);
85 | }
86 | }
87 | if (isBinary) {
88 | r.setRequestHeader('Content-Type', 'multipart/form-data; boundary=' + boundary);
89 | return r.sendAsBinary(data);
90 | }
91 | r.withCredentials = true;
92 | r.send(data);
93 | return r;
94 | // Headers are returned as a string
95 | function headersToJSON(s) {
96 | var o = {};
97 | var reg = /([a-z\-]+):\s?(.*);?/gi;
98 | let m;
99 | while (m = reg.exec(s)) {
100 | o[m[1]] = m[2];
101 | }
102 | return o;
103 | }
104 | };
105 |
--------------------------------------------------------------------------------
/src/propTypes.js:
--------------------------------------------------------------------------------
1 | import PropTypes from 'prop-types';
2 |
3 | export default {
4 | url: PropTypes.string,
5 | text: PropTypes.string,
6 | inputAccept: PropTypes.string,
7 | inputOfFile: PropTypes.string,
8 | cropBtn: PropTypes.object,
9 | cropRatio: PropTypes.string,
10 | resizeBtn: PropTypes.object,
11 | maxFileSize: PropTypes.number,
12 | maxWidth: PropTypes.number,
13 | maxHeight: PropTypes.number,
14 | minWidth: PropTypes.number,
15 | minHeight: PropTypes.number,
16 | data: PropTypes.object,
17 | header: PropTypes.object,
18 | multipleSize: PropTypes.number,
19 | compress: PropTypes.number,
20 | };
21 |
--------------------------------------------------------------------------------
/src/props.js:
--------------------------------------------------------------------------------
1 | // default props
2 | export default {
3 | url: '',
4 | text: 'upload',
5 | inputOfFile: 'files',
6 | crop: false,
7 | cropBtn: {
8 | ok: 'Save',
9 | cancel: 'Cancel',
10 | },
11 | extensions: ['png', 'jpg', 'jpeg', 'gif', 'svg', 'webp'],
12 | cropRatio: '1:1',
13 | resize: false,
14 | resizeBtn: {
15 | ok: 'Save',
16 | cancel: 'Cancel',
17 | },
18 | inputAccept: 'image/jpg,image/jpeg,image/png',
19 | data: {},
20 | header: {},
21 | isXhr: true,
22 | multiple: false,
23 | compress: 0,
24 | imageUploaded: function(res) {
25 |
26 | },
27 | imageUploading: function(res) {
28 | console.info('uploading');
29 | },
30 | imageChanged: function() {
31 |
32 | },
33 | errorHandle: function(err) {
34 | console.error(err);
35 | },
36 | };
37 |
--------------------------------------------------------------------------------
/src/react-core-image-upload.js:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react';
2 | import xhr from 'core-image-xhr';
3 | import defaultProps from './props';
4 | import propTypes from './propTypes';
5 | import Crop from './components/crop';
6 | import errorCode from './lib/error-code';
7 | import canvasHelper from './lib/canvas-helper';
8 | require('style!css!./style.css');
9 |
10 | let overflowVal = '';
11 | class ReactCoreImageUpload extends React.Component {
12 | constructor(props) {
13 | super(props);
14 | this.state = {
15 | formID: 'g-core-upload-input-' + Math.floor(Math.random() * 10000),
16 | uploading: false,
17 | hasImage: false,
18 | image: {
19 | src: 'http://img1.vued.vanthink.cn/vuedcb0efb21e5f2ca013ca1480198bbf4b3.png',
20 | width: 0,
21 | height: 0,
22 | }
23 | };
24 | if (this.props.multiple) {
25 | this.name = this.props.inputOfFile + '[]';
26 | } else {
27 | this.name = this.props.inputOfFile;
28 | }
29 | this.change = this.change.bind(this);
30 | this.doCrop = this.doCrop.bind(this);
31 | this.doResize = this.doResize.bind(this);
32 | this.cancel = this.cancel.bind(this);
33 | }
34 |
35 | render() {
36 | return (
37 |
94 | );
95 | }
96 |
97 | __dispatch(name, res) {
98 | if (this.props[name] && typeof this.props[name] === 'function') {
99 | this.props[name].apply(this, Array.from(arguments).slice(1));
100 | }
101 | }
102 |
103 | __find(ele) {
104 | let dq = document.getElementById(this.state.formID);
105 | return dq.querySelector(ele);
106 | }
107 |
108 | change(e) {
109 | let fileVal = this.__find('input').value.replace(/C:\\fakepath\\/i, "");
110 | let fileExt = fileVal.substring(fileVal.lastIndexOf(".") + 1);
111 | const extensionsArr = this.props.extensions;
112 | if (extensionsArr.length > 1 ) {
113 | var reg = new RegExp('^[' + extensionsArr.join('|') + ']+$','i');
114 | if (!reg.test(fileExt)) {
115 | return this.__dispatch('errorHandle', errorCode['FILE_FORMAT']);
116 | }
117 | }
118 | if (e.target.files[0].size > this.props.maxFileSize) {
119 | var formatSize;
120 | if (parseInt(this.maxFileSize / 1024 / 1024) > 0) {
121 | formatSize = (this.maxFileSize / 1024 / 1024).toFixed(2) + 'MB';
122 | } else if (parseInt(this.maxFileSize / 1024) > 0) {
123 | formatSize = (this.maxFileSize / 1024).toFixed(2) + 'kB';
124 | } else {
125 | formatSize = options.maxFileSize.toFixed(2) + 'Byte';
126 | }
127 | this.__dispatch('errorHandle', errorCode['FILE_MAXSIZE'], 'FILE IS TOO LARGER THAN THE MAX VALUE ' + formatSize);
128 | return;
129 | }
130 |
131 | this.files = e.target.files;
132 | if(this.props.crop || this.props.resize) {
133 | this.__showImage();
134 | return;
135 | }
136 | this.__dispatch('imageChanged', this.files.length > 1 ? this.files : this.files[0]);
137 | if (this.props.compress && this.files[0]['type'] !== 'image/png' && this.files[0]['type'] !== 'image/gif') {
138 | canvasHelper.compress(this.files[0], 100 - this.props.compress, (code) => {
139 | this.tryAjaxUpload('', true, code);
140 | });
141 | } else {
142 | this.tryAjaxUpload();
143 | }
144 | }
145 | __showImage() {
146 | this.setState({
147 | hasImage: true
148 | })
149 | this.__readFiles();
150 | }
151 | __readFiles() {
152 | let reader = new FileReader();
153 | let self = this;
154 | reader.onload = function(e) {
155 | let src = e.target.result;
156 | overflowVal = document.body.style.overflow;
157 | document.body.style.overflow = 'hidden';
158 | self.__initImage(src);
159 |
160 | }
161 | reader.readAsDataURL(this.files[0]);
162 | }
163 | __initImage(src) {
164 | let pic = new Image();
165 | let self = this;
166 | pic.src = src;
167 | const cropBox = this.cropbox;
168 | pic.onload= function() {
169 | self.setState({
170 | image:{
171 | src: src,
172 | width: pic.naturalWidth,
173 | height: pic.naturalHeight,
174 | }
175 | });
176 | self.imgChangeRatio = cropBox.setImage(src, pic.naturalWidth, pic.naturalHeight);
177 | }
178 | }
179 |
180 | resizeImage(progress) {
181 | const cropBox = this.$refs.cropBox;
182 | cropBox.resizeImage(progress);
183 | }
184 |
185 | doCrop(e) {
186 | this.__setData('crop');
187 | const cropBox = this.cropbox;
188 | const upload = this.__setUpload(e.target);
189 | if (this.props.crop === 'local') {
190 | const targetImage = cropBox.getCropImage();
191 | this.props.data.comprese = 100 - this.props.compress;
192 | return canvasHelper.crop(targetImage, this.props.data, (code) => {
193 | console.log(code);
194 | upload(code);
195 | })
196 | }
197 | upload();
198 | }
199 |
200 | doResize(e) {
201 | this.__setData('reszie');
202 | const cropBox = this.cropbox;
203 | const upload = this.__setUpload(e.target);
204 | if (this.props.resize === 'local') {
205 | const targetImage = cropBox.getCropImage();
206 | this.data.comprose = 100 - this.compress;
207 | return canvasHelper.resize(targetImage, this.data, (code) => {
208 | upload(code);
209 | })
210 | }
211 | upload();
212 | }
213 |
214 | __setData(type) {
215 | this.props.data["request"] = type;
216 | const cropBox = this.cropbox;
217 | const newCSSObj = cropBox.getCropData();
218 | for (const k of Object.keys(newCSSObj)) {
219 | this.props.data[k] = newCSSObj[k];
220 | }
221 | if (this.props.maxWidth) {
222 | this.props.data['maxWidth'] = this.maxWidth;
223 | }
224 | if (this.maxHeight) {
225 | this.props.data['maxHeight'] = this.maxHeight;
226 | }
227 | if (this.minWidth) {
228 | this.props.data['minWidth'] = this.minWidth;
229 | }
230 | }
231 | __setUpload(btn) {
232 | btn.value = btn.value + '...';
233 | btn.disabled = true;
234 | const upload = (code) => {
235 | this.tryAjaxUpload(() => {
236 | btn.value = btn.value.replace('...','');
237 | btn.disabled = false;
238 | }, code ? true: false, code);
239 | };
240 | return upload;
241 | }
242 |
243 | cancel() {
244 | this.setState({
245 | hasImage: false,
246 | })
247 | document.body.style.overflow = overflowVal;
248 | this.files = '';
249 | this.__find('input').value = '';
250 | }
251 | // use ajax upload IE10+
252 | tryAjaxUpload(callback, isBinary, base64Code) {
253 | const self = this;
254 | this. __dispatch('imageuploading',this.files[0]);
255 | const done = function(res) {
256 | if(typeof callback === 'function') {
257 | callback();
258 | }
259 | self.uploading = false;
260 | self.cancel();
261 | self.__dispatch('imageUploaded',res);
262 | };
263 | const errorUpload = function(err) {
264 | self.__dispatch('errorHandle', errorCode['SERVER_ERROR']);
265 | };
266 | if (!this.props.isXhr) {
267 | if(this.props.crop) {
268 | this.setState(
269 | hasImage: false,
270 | );
271 | }
272 | return typeof callback === 'function' && callback();
273 | }
274 | let data;
275 | if (isBinary) {
276 | data = {
277 | type: this.files[0]['type'],
278 | filename: this.files[0]['name'],
279 | filed: this.props.inputOfFile,
280 | base64Code: base64Code
281 | };
282 | if (typeof this.props.data === 'object') {
283 | data = Object.assign(this.props.data, data);
284 | }
285 | } else {
286 | data = new FormData();
287 | for (let i = 0;i < this.files.length; i++) {
288 | data.append(this.name, this.files[i]);
289 | }
290 | if (typeof this.props.data === 'object') {
291 | for(let k in this.props.data) {
292 | if(this.props.data[k] !== undefined) {
293 | data.append(k,this.props.data[k]);
294 | }
295 | }
296 | }
297 | }
298 | xhr('POST', this.props.url, this.props.headers, data, done, errorUpload, isBinary);
299 | }
300 | }
301 | ReactCoreImageUpload.propTypes = propTypes;
302 | ReactCoreImageUpload.defaultProps = defaultProps;
303 |
304 | export default ReactCoreImageUpload;
305 |
--------------------------------------------------------------------------------
/src/style.css:
--------------------------------------------------------------------------------
1 | .g-core-image-upload-btn {
2 | position: relative;
3 | overflow: hidden;
4 | cursor: pointer;
5 | }
6 |
7 | .g-core-image-upload-form {
8 | position: absolute;
9 | left: 0;
10 | right: 0;
11 | top: 0;
12 | bottom: 0;
13 | opacity: 0;
14 | }
15 |
16 | .g-core-image-upload-container {
17 | position: absolute;
18 | background: #111;
19 | z-index: 900;
20 | }
21 |
22 | .g-core-image-upload-modal {
23 | position: absolute;
24 | left: 0;
25 | right: 0;
26 | width: 100%;
27 | height: 100%;
28 | border: 1px solid #ccc;
29 | z-index: 899;
30 | }
31 |
32 | .dropped {
33 | border: 4px solid #ea6153;
34 | }
35 |
36 | .g-core-image-corp-container {
37 | z-index: 1900;
38 | position: fixed;
39 | left: 0;
40 | right: 0;
41 | top: 0;
42 | bottom: 0;
43 | background: rgba(0, 0, 0, .9);
44 | color: #f1f1f1;
45 | }
46 |
47 | .g-core-image-corp-container .image-aside {
48 | overflow: hidden;
49 | position: absolute;
50 | right: 30px;
51 | left: 30px;
52 | top: 60px;
53 | bottom: 20px;
54 | text-align: center;
55 | }
56 |
57 | .g-core-image-corp-container .image-aside img {
58 | max-width: 100%;
59 | -webkit-touch-callout: none;
60 | -webkit-user-select: none;
61 | -khtml-user-select: none;
62 | -moz-user-select: none;
63 | -ms-user-select: none;
64 | user-select: none;
65 | }
66 |
67 | .g-core-image-corp-container .info-aside {
68 | position: absolute;
69 | left: 0;
70 | right: 0;
71 | top: 0;
72 | height: 40px;
73 | padding-left: 10px;
74 | padding-right: 10px;
75 | background: #fefefe;
76 | color: #777;
77 | }
78 |
79 | .g-core-image-corp-container .info-aside .image-corp-preview {
80 | position: relative;
81 | overflow: hidden;
82 | text-align: center;
83 | border: 2px solid #ccc;
84 | }
85 |
86 | .g-core-image-corp-container .info-aside .image-corp-preview.circled {
87 | border-radius: 160px;
88 | }
89 |
90 | .g-core-image-corp-container .info-aside .image-corp-preview img {
91 | width: 100%;
92 | }
93 |
94 |
95 | .g-core-image-corp-container .btn-groups {
96 | text-align: right;
97 | margin: 5px 0 0;
98 | }
99 |
100 | .g-core-image-corp-container .btn {
101 | display: inline-block;
102 | padding: 0 15px;
103 | height: 32px;
104 | margin-left: 15px;
105 | background: #fff;
106 | border: 1px solid #ccc;
107 | border-radius: 2px;
108 | font-size: 13px;
109 | color: #222;
110 | line-height: 32px;
111 | transition: all .1s ease-in;
112 | }
113 |
114 | .g-core-image-corp-container .btn:hover {
115 | border: 1px solid #777;
116 | box-shadow: 0 1px 3px rgba(0, 0, 0, .05);
117 | }
118 |
119 | .g-core-image-corp-container .btn:active,
120 | {
121 | background: #ddd;
122 | }
123 |
124 | .g-core-image-corp-container .btn:disabled {
125 | background: #eee !important;
126 | border-color: #ccc;
127 | cursor: not-allowed;
128 | }
129 |
130 | .g-core-image-corp-container .btn-upload {
131 | background: #2d2d2d;
132 | border-color: #3d3d3d;
133 | color: #fff;
134 | }
135 |
136 | .g-core-image-corp-container .btn-upload:hover {
137 | background: #222;
138 | border-color: #222;
139 | box-shadow: 0 1px 3px rgba(0, 0, 0, .05);
140 | }
141 |
142 | .g-core-image-corp-container .g-crop-image-box,
143 | .g-core-image-corp-container .g-crop-image-box .g-crop-image-principal {
144 | position: relative;
145 | }
146 |
147 | .g-crop-image-principal{
148 | overflow: hidden;
149 | background-color: #fff;
150 | background-image: -webkit-linear-gradient(bottom left, #efefef 25%, transparent 25%, transparent 75%, #efefef 75%, #efefef),-webkit-linear-gradient(bottom left, #efefef 25%, transparent 25%, transparent 75%, #efefef 75%, #efefef);
151 | background-image: -moz-linear-gradient(bottom left, #efefef 25%, transparent 25%, transparent 75%, #efefef 75%, #efefef),-moz-linear-gradient(bottom left, #efefef 25%, transparent 25%, transparent 75%, #efefef 75%, #efefef);
152 | background-image: -o-linear-gradient(bottom left, #efefef 25%, transparent 25%, transparent 75%, #efefef 75%, #efefef),-o-linear-gradient(bottom left, #efefef 25%, transparent 25%, transparent 75%, #efefef 75%, #efefef);
153 | background-image: linear-gradient(to top right, #efefef 25%, transparent 25%, transparent 75%, #efefef 75%, #efefef),linear-gradient(to top right, #efefef 25%, transparent 25%, transparent 75%, #efefef 75%, #efefef);
154 | background-position: 0 0,10px 10px;
155 | -webkit-background-size: 21px 21px;
156 | background-size: 21px 21px;
157 | }
158 | .image-aside{
159 | overflow: hidden;
160 | position: absolute;
161 | right: 30px;
162 | left:30px;
163 | top:70px;
164 | bottom:40px;
165 | text-align: center;
166 | }
167 | .image-aside .image-wrap{
168 | position: absolute;
169 | left: 0;
170 | top: 0;
171 | -webkit-touch-callout: none;
172 | -webkit-user-select: none;
173 | -khtml-user-select: none;
174 | -moz-user-select: none;
175 | -ms-user-select: none;
176 | user-select: none;
177 | box-shadow: 0 3px 5px -2px rgba(0,0,0,.25);
178 | background-size: cover;
179 | }
180 | .image-mask{
181 | position: absolute;
182 | left: 0;
183 | top: 0;
184 | width:100%;
185 | height: 100%;
186 | }
187 | .image-mask .mask {
188 | position: absolute;
189 | background-color: rgba(255,255,255,.6);
190 | }
191 | .crop-box{
192 | z-index: 2000;
193 | box-sizing: border-box;
194 | position: absolute;
195 | background: none;
196 | cursor: move;
197 | width:100px;
198 | height: 100px;
199 | border:1px solid rgba(255,255,255, .95);
200 | }
201 | .crop-box:after,
202 | .crop-box:before{
203 | content: '';
204 | display: block;
205 | opacity: 0;
206 | position: absolute;
207 | left: 33.3333%;
208 | top: 0;
209 | width: 33.334%;
210 | height: 100%;
211 | background-color: transparent;
212 | border-color: rgba(255,255,255,.7);
213 | border-style: solid;
214 | border-width: 0;
215 | }
216 | .crop-box:active::before,
217 | .crop-box:active::after{
218 | opacity: 1;
219 | }
220 | .crop-box:before{
221 | border-left-width: 1px;
222 | border-right-width: 1px;
223 | }
224 | .crop-box:after{
225 | top: 33.3333%;
226 | left: 0;
227 | height: 33.3334%;
228 | width: 100%;
229 | border-top-width: 1px;
230 | border-bottom-width: 1px;
231 | }
232 | .crop-box .g-resize{
233 | display: inline-block;
234 | z-index: 1910;
235 | position: absolute;
236 | bottom: -8px;
237 | right: -8px;
238 | width: 16px;
239 | height: 16px;
240 | cursor: se-resize;
241 | border-radius: 10px;
242 | background-color: #fff;
243 | box-shadow: 0 2px 4px -2px rgba(0,0,0,.25);
244 | }
245 |
246 | .g-resize-bar{
247 | position: absolute;
248 | bottom: 0px;
249 | margin: 17px auto;
250 | height: 6px;
251 | border-radius: 3px;
252 | width:200px;
253 | margin-left: -100px;
254 | left: 50%;
255 | background-color: #9cd6fd;
256 | box-shadow: 0 2px 3px -1px rgba(0,0,0,.3);
257 | }
258 | .g-resize-bar .g-resize-highlight{
259 | position: absolute;
260 | left: 0;
261 | top:0;
262 | height: 6px;
263 | background-color: #3498db;
264 | border-radius: 3px;
265 | }
266 | .g-resize-bar .circle-btn{
267 | display: block;
268 | position: absolute;
269 | left:0;
270 | top: -5px;
271 | width: 14px;
272 | height: 14px;
273 | margin-left: -7px;
274 | background-color: #fff;
275 | border-radius: 7px;
276 | box-shadow: 0 2px 3px -2px rgba(0,0,0,.6), 0 -2px 3px -2px rgba(0,0,0,.55);
277 | border-width: 0;
278 | }
279 |
--------------------------------------------------------------------------------
/tests/react-core-image-upload.test.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { shallow, mount, render } from 'enzyme';
3 | import ReactCoreImageUpload from '../src/react-core-image-upload';
4 |
5 | describe('ReactCoreImageUpload Component Tests', function () {
6 |
7 | it('has a form',function() {
8 | expect(shallow( ).find('form').length).toBe(1);
9 | });
10 |
11 | it('has a file input',function() {
12 | expect(shallow( ).find('input[type="file"]').length).toBe(1);
13 | });
14 |
15 | it('allows us to set props', () => {
16 | let isSuccess = false;
17 | const successCallback = function() {
18 | isSuccess = true;
19 | };
20 | const wrapper = mount(
21 | );
22 | expect(wrapper.props().text).toEqual("Upload Your Image");
23 | wrapper.setProps({ class: ['btn']});
24 | expect(wrapper.props().class).toEqual(['btn']);
25 | });
26 | });
--------------------------------------------------------------------------------
/webpack.config.build.js:
--------------------------------------------------------------------------------
1 | var webpack = require('webpack');
2 |
3 | module.exports = {
4 | entry: "./src/react-core-image-upload",
5 | output: {
6 | path: __dirname + '/',
7 | filename: "react-core-image-upload.js",
8 | libraryTarget: 'umd',
9 | },
10 | externals: {
11 | "react":"react",
12 | "react-dom":'react-dom',
13 | 'prop-types': 'prop-types'
14 | },
15 |
16 | module: {
17 | loaders: [
18 | {
19 | test: /\.js?$/,
20 | loaders: ['babel-loader?presets[]=react,presets[]=es2015'],
21 | exclude: /node_modules/
22 | },
23 |
24 | {
25 | test: /\.ope/,
26 | loader: "style!css"
27 | }
28 | ]
29 | },
30 | plugins: [
31 | new webpack.NoErrorsPlugin(),
32 | new webpack.optimize.UglifyJsPlugin( {
33 | minimize : true,
34 | sourceMap : false,
35 | mangle: true,
36 | compress: {
37 | warnings: false
38 | }
39 | } )
40 | ]
41 | };
42 |
--------------------------------------------------------------------------------
/webpack.config.js:
--------------------------------------------------------------------------------
1 | var webpack = require('webpack');
2 |
3 | module.exports = {
4 | entry: [
5 | "webpack-dev-server/client?http://localhost:9000",
6 | 'webpack/hot/only-dev-server',
7 | "./src/index"
8 | ],
9 | output: {
10 | path: __dirname + '/build',
11 | filename: "bundle.js",
12 | publicPath: '/build/',
13 | },
14 |
15 | module: {
16 | loaders: [
17 | {
18 | test: /\.js?$/,
19 | loaders: ["react-hot-loader/webpack",'babel-loader?presets[]=react,presets[]=es2015'],
20 | exclude: /node_modules/
21 | },
22 |
23 | {
24 | test: /\.ope/,
25 | loader: "style!css"
26 | }
27 | ]
28 | },
29 | plugins: [
30 | new webpack.NoErrorsPlugin(),
31 | new webpack.HotModuleReplacementPlugin()
32 | ]
33 | };
--------------------------------------------------------------------------------