├── .gitignore ├── .DS_Store ├── .babelrc ├── lib ├── index.css └── index.js ├── src ├── index.less └── index.js ├── LICENSE ├── package.json ├── rollup.config.js └── README.md /.gitignore: -------------------------------------------------------------------------------- 1 | **/node_modules 2 | **/build 3 | **/yarn.lock 4 | 5 | .DS_Store -------------------------------------------------------------------------------- /.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yrobot-npm/react-mobile-table/HEAD/.DS_Store -------------------------------------------------------------------------------- /.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": ["@babel/preset-env", "@babel/preset-react"] 3 | } 4 | -------------------------------------------------------------------------------- /lib/index.css: -------------------------------------------------------------------------------- 1 | .react-mobile-table{position:relative;width:100%;overflow-x:scroll;line-height:1.5;font-size:14px}.react-mobile-table .react-mobile-table-column-holder{position:relative;white-space:nowrap}.react-mobile-table .react-mobile-table-column-holder .react-mobile-table-table-column{display:inline-block;font-weight:400;color:rgba(0,0,0,.65)}.react-mobile-table .react-mobile-table-column-holder .react-mobile-table-table-column .react-mobile-table-item{box-sizing:border-box;padding:10px 16px;text-align:left;overflow:hidden;white-space:nowrap;text-overflow:ellipsis;border-bottom:1px solid #f9f9f9}.react-mobile-table .react-mobile-table-column-holder .react-mobile-table-table-column .react-mobile-table-item:first-child{background:#fafafa;font-weight:500;color:rgba(0,0,0,.85);overflow:visible} -------------------------------------------------------------------------------- /src/index.less: -------------------------------------------------------------------------------- 1 | .react-mobile-table { 2 | position: relative; 3 | width: 100%; 4 | overflow-x: scroll; 5 | line-height: 1.5; 6 | font-size: 14px; 7 | .react-mobile-table-column-holder { 8 | position: relative; 9 | white-space: nowrap; 10 | .react-mobile-table-table-column { 11 | display: inline-block; 12 | font-weight: 400; 13 | color: rgba(0, 0, 0, 0.65); 14 | .react-mobile-table-item { 15 | &:first-child { 16 | background: rgba(250, 250, 250, 1); 17 | font-weight: 500; 18 | color: rgba(0, 0, 0, 0.85); 19 | overflow: visible; 20 | } 21 | box-sizing: border-box; 22 | padding: 10px 16px; 23 | text-align: left; 24 | overflow: hidden; 25 | white-space: nowrap; 26 | text-overflow: ellipsis; 27 | border-bottom: 1px solid rgb(249, 249, 249); 28 | } 29 | } 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2022 npm 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. 22 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@yrobot/react-mobile-table", 3 | "version": "1.2.0", 4 | "description": "@yrobot 基于react实现的移动端table", 5 | "main": "lib/index.js", 6 | "files": [ 7 | "lib" 8 | ], 9 | "keywords": [ 10 | "react", 11 | "table", 12 | "mobile", 13 | "yrobot" 14 | ], 15 | "author": "Yrobot", 16 | "license": "MIT", 17 | "dependencies": { 18 | "memoize-one": "^5.1.1" 19 | }, 20 | "scripts": { 21 | "start": "rollup -c", 22 | "build": "NODE_ENV=production && rm -rf lib && rollup -c" 23 | }, 24 | "repository": { 25 | "type": "git", 26 | "url": "https://github.com/Yrobot/react-mobile-table.git" 27 | }, 28 | "peerDependencies": { 29 | "react": ">=15.6.0", 30 | "react-dom": ">=15.6.0" 31 | }, 32 | "devDependencies": { 33 | "@babel/core": "^7.4.0", 34 | "@babel/preset-env": "^7.10.2", 35 | "@babel/preset-react": "^7.0.0", 36 | "react": "^15.6.2", 37 | "react-dom": "^15.6.2", 38 | "less": "^3.11.3", 39 | "less-loader": "^4.1.0", 40 | "rollup": "^1.7.4", 41 | "rollup-plugin-babel": "^4.3.2", 42 | "rollup-plugin-filesize": "^6.0.1", 43 | "rollup-plugin-node-resolve": "^4.0.1", 44 | "rollup-plugin-peer-deps-external": "^2.2.0", 45 | "rollup-plugin-postcss": "^2.0.3", 46 | "rollup-plugin-progress": "^1.1.1", 47 | "rollup-plugin-terser": "^7.0.2" 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /rollup.config.js: -------------------------------------------------------------------------------- 1 | import external from 'rollup-plugin-peer-deps-external'; 2 | import resolve from 'rollup-plugin-node-resolve'; 3 | import filesize from 'rollup-plugin-filesize'; 4 | import progress from 'rollup-plugin-progress'; 5 | import postcss from 'rollup-plugin-postcss'; 6 | import babel from 'rollup-plugin-babel'; 7 | import { terser } from "rollup-plugin-terser"; 8 | 9 | export default { 10 | input: 'src/index.js', 11 | output: [ 12 | { 13 | file: 'lib/index.js', 14 | format: 'es', 15 | sourcemap: false, 16 | name: '@yrobot/react-mobile-table', 17 | }, 18 | ], 19 | plugins: [ 20 | resolve({ 21 | extensions: ['.js', '.jsx', '.ts', '.tsx'], 22 | jsnext: true, 23 | }), 24 | 25 | babel({ 26 | extensions: ['.js', '.jsx', '.ts', '.tsx'], 27 | include: ['**/*'], 28 | exclude: 'node_modules/**', 29 | }), 30 | terser(), 31 | 32 | // Automatically externalize peerDependencies in a rollup bundle. 33 | external(), 34 | 35 | postcss({ 36 | minimize: true, // uses cssnano behind scene 37 | modules: false, // enable css modules 38 | extract: true, 39 | use: ['less'], 40 | extensions: ['.css', '.scss', '.sass', '.less'], // uses node-sass 41 | }), 42 | 43 | filesize(), 44 | 45 | // Progress while building 46 | progress({ clearLine: false }), 47 | ], 48 | external: ['react', 'react-dom'], 49 | }; 50 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # @yrobot/react-mobile-table 2 | 3 | ## 特性 4 | 5 | 1. 适配 kbone,可在小程序端使用 6 | 2. table 宽度基于 MobileTable 外层元素宽度 100% 7 | 3. 自动计算每一列合适的默认宽度(a 保证完全展示 title) 8 | 4. 点击存在压缩的列会展开对应列的所有数据(保证此列所有数据都完整展开) 9 | 5. 展开列后可以左右滑动 table 查看 10 | 6. 点击展开列可以恢复收缩状态 11 | 12 | ## 利用 codesandbox 查看 13 | 14 | [view in codesandbox](https://codesandbox.io/s/react-mobile-table-jrled?file=/src/App.js) 15 | 16 | ## 查看 demo 17 | 18 | ![](https://tva1.sinaimg.cn/large/007S8ZIlly1gfn15t5e25g30cg0qodly.gif) 19 | 20 | ## 使用 21 | 22 | 1. 安装`@yrobot/react-mobile-table` 23 | 24 | ``` 25 | yarn add @yrobot/react-mobile-table 26 | ``` 27 | 28 | 2. 引入组件并使用 29 | 30 | ```js 31 | import MobileTable from '@yrobot/react-mobile-table'; 32 | import '@yrobot/react-mobile-table/lib/index.css'; 33 | 34 | const data = [ 35 | ['ID', 'Name', 'Age', 'Address'], // title line 36 | ['1', 'John Brown', 32, 'New York No. 1 Lake Park, New York No. 1 Lake Park'], 37 | ['2', 'Jim Green', 42, 'London No. 2 Lake Park, London No. 2 Lake Park'], 38 | ['3', 'Joe Black', 32, 'Sidney No. 1 Lake Park, Sidney No. 1 Lake Park'], 39 | ['4', 'Joe Green jsdkk sdkksdqww', 36, 'Sidney NOOOOOO'], 40 | ]; 41 | 42 | { 47 | const { 48 | column, //第几列,从0开始 49 | line, // 第几行,从0开始,包含header行 50 | collapse, // 当前列是否折叠,boolean 51 | event, 52 | } = param; 53 | console.log(param); 54 | }} 55 | />; 56 | ``` 57 | 58 | ## 版本更新 log 59 | 60 | #### v1.2.0 [feature] 61 | 62 | 1. 支持 onClick 事件,获取点击行列和展开状态 63 | 64 | #### v1.1.4 [fix bug] 65 | 66 | 1. 修复使用 CRA 安装 mobile-table 时会安装两个版本 react 67 | 68 | #### v1.1.3 [fix bug] 69 | 70 | 1. 适配仓库兼容 react 版本 15(使用 memoize-one 缓存计算结果) 71 | 2. 组件库添加 terser 压缩 js 代码 72 | 3. demo 添加分页效果 73 | 74 | #### v1.1.2 [fix bug] 75 | 76 | 1. 添加 line-height,修复特殊字体中英文行高不一致的情况 77 | -------------------------------------------------------------------------------- /lib/index.js: -------------------------------------------------------------------------------- 1 | import t from"react";function n(t,n){for(var e=0;et.length)&&(n=t.length);for(var e=0,r=new Array(n);e0&&void 0!==arguments[0]?arguments[0]:[],n=arguments.length>1?arguments[1]:void 0,e=n/375*100,r=t.length;if(e>100/r)return t;var o=0,i=0;if(t.map((function(t){t>e?i+=t-e:o+=e-t})),0===o)return t;var c=(i-o)/i;return t.map((function(t){return t>e?e+(t-e)*c:e}))})),p=l((function(t){var n=arguments.length>1&&void 0!==arguments[1]?arguments[1]:10,e=arguments.length>2&&void 0!==arguments[2]?arguments[2]:1,r=[],o=0;return t.map((function(){var t=arguments.length>0&&void 0!==arguments[0]?arguments[0]:[],o=arguments.length>1?arguments[1]:void 0,i=(o>0?e:n)/1;t.map((function(t,n){r[n]=(r[n]||0)+(t+"").getCharSize()*i}))})),r.map((function(t){o+=t})),r.map((function(t){return 100*t/o}))})),h=function(e){!function(t,n){if("function"!=typeof n&&null!==n)throw new TypeError("Super expression must either be null or a function");t.prototype=Object.create(n&&n.prototype,{constructor:{value:t,writable:!0,configurable:!0}}),n&&r(t,n)}(l,t.Component);var o,c,u,a=i(l);function l(t){var n;return function(t,n){if(!(t instanceof n))throw new TypeError("Cannot call a class as a function")}(this,l),(n=a.call(this,t)).state={},n}return o=l,(c=[{key:"switchFold",value:function(t){var n=this.props.data,e=void 0===n?[[]]:n,r=this.state.columnFolds,o=(void 0===r?e[0].map((function(){return!0})):r).slice();o[t]=!o[t],this.setState({columnFolds:o})}},{key:"render",value:function(){var n=this,e=this.props,r=e.data,o=void 0===r?[[]]:r,i=e.columnMinWidth,c=void 0===i?69:i,u=e.onClick,a=void 0===u?function(){}:u,l=s(p(o),c),h=this.state.columnFolds,m=void 0===h?o[0].map((function(){return!0})):h,v=o.length||0,d=o[0].length||0;return t.createElement("div",{className:"react-mobile-table"},t.createElement("div",{className:"react-mobile-table-column-holder"},f(d)((function(e){return t.createElement("div",{key:"cloumn".concat(e),className:"react-mobile-table-table-column",style:{minWidth:l[e]+"%",width:m[e]?l[e]+"%":"auto"},onClick:function(){n.switchFold(e)}},f(v)((function(n){return t.createElement("div",{key:"cloumn".concat(e,"line").concat(n),className:"react-mobile-table-item",onClick:function(t){a({column:e,line:n,collapse:!m[e],event:t})}},o[n][e])})))}))))}}])&&n(o.prototype,c),u&&n(o,u),l}();export default h; 2 | -------------------------------------------------------------------------------- /src/index.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @author Yrobot 3 | * @time 2020.6.9 4 | */ 5 | import React from 'react'; 6 | import memoize from 'memoize-one'; 7 | // import React, { useState, useMemo } from 'react'; 8 | import './index.less'; 9 | 10 | // 生成一个长度为len的数组,并返回以index为参数的闭包 11 | const getArray = (len) => (fn) => [...Array(len)].map((v, i) => fn(i)); 12 | 13 | // 获取string的字节宽度:中文字符=》2字节宽 14 | String.prototype.getCharSize = function () { 15 | return this.replace(/[^\u0000-\u00ff]/g, ' ').length; 16 | }; 17 | 18 | // 根据传入的宽度百分比,自动优化,防止过小的列 19 | const autoSize = memoize((arr = [], MIN_SIZE) => { 20 | // const MIN_SIZE = (Math.max(...titles.map((v) => (v + '').getCharSize())) / 2) * fontSize + 16 // 根据(最长title字长+padding=计算MIN_SIZE)计算MIN_SIZE 21 | const MIN_WIDTH_P = (MIN_SIZE / 375) * 100; 22 | const LEN = arr.length; 23 | 24 | // 如果平均宽度都到不了MIN_WIDTH_P,直接返回arr 25 | if (MIN_WIDTH_P > 100 / LEN) return arr; 26 | 27 | let subCount = 0; // 记录所有小于MIN_WIDTH_P的缺值和 28 | let overCount = 0; // 记录所有大于MIN_WIDTH_P的超值和 29 | arr.map((v) => { 30 | if (v > MIN_WIDTH_P) { 31 | overCount += v - MIN_WIDTH_P; 32 | } else { 33 | subCount += MIN_WIDTH_P - v; 34 | } 35 | }); 36 | 37 | if (subCount === 0) return arr; // 所有数据都大于MIN_WIDTH_P,直接返回arr 38 | 39 | const overRate = (overCount - subCount) / overCount; // 计算超出值的缩放比例 40 | 41 | return arr.map((v) => { 42 | if (v > MIN_WIDTH_P) { 43 | return MIN_WIDTH_P + (v - MIN_WIDTH_P) * overRate; 44 | } else { 45 | return MIN_WIDTH_P; 46 | } 47 | }); 48 | }); 49 | 50 | // 根据传入的data配置每一列的宽度百分比 51 | const getWidthPercent = memoize((data, titleWeigth = 10, contentWeight = 1) => { 52 | const widthLen = []; 53 | let count = 0; 54 | data.map((arr = [], index) => { 55 | const base = (index > 0 ? contentWeight : titleWeigth) / 1; 56 | arr.map((item, i) => { 57 | widthLen[i] = (widthLen[i] || 0) + (item + '').getCharSize() * base; 58 | }); 59 | }); 60 | widthLen.map((len) => { 61 | count += len; 62 | }); 63 | return widthLen.map((v) => (v * 100) / count); 64 | }); 65 | 66 | class MobileTable extends React.Component { 67 | constructor(props) { 68 | super(props); 69 | this.state = {}; 70 | } 71 | 72 | switchFold(i) { 73 | const { data = [[]] } = this.props; 74 | const { columnFolds = data[0].map(() => true) } = this.state; 75 | let newColumnFolds = columnFolds.slice(); 76 | newColumnFolds[i] = !newColumnFolds[i]; 77 | this.setState({ 78 | columnFolds: newColumnFolds, 79 | }); 80 | } 81 | 82 | render() { 83 | const { data = [[]], columnMinWidth = 69, onClick = () => {} } = this.props; 84 | const widthPercent = autoSize(getWidthPercent(data), columnMinWidth); 85 | const { columnFolds = data[0].map(() => true) } = this.state; 86 | const lineSize = data.length || 0; 87 | const columnSize = data[0].length || 0; 88 | return ( 89 |
90 |
91 | {getArray(columnSize)((columnIndex) => ( 92 |
{ 100 | this.switchFold(columnIndex); 101 | }} 102 | > 103 | {getArray(lineSize)((lineIndex) => ( 104 |
{ 108 | onClick({ 109 | column: columnIndex, 110 | line: lineIndex, 111 | collapse: !columnFolds[columnIndex], 112 | event, 113 | }); 114 | }} 115 | > 116 | {data[lineIndex][columnIndex]} 117 |
118 | ))} 119 |
120 | ))} 121 |
122 |
123 | ); 124 | } 125 | } 126 | 127 | export default MobileTable; 128 | --------------------------------------------------------------------------------