├── .yo-rc.json ├── tests ├── index.js └── Formatter.spec.js ├── demo ├── FormatterDemo.less ├── index.js └── FormatterDemo.js ├── src ├── index.js └── Formatter.js ├── .gitignore ├── .npmignore ├── HISTORY.md ├── index.html ├── .eslintrc.json ├── .travis.yml ├── package.json └── README.md /.yo-rc.json: -------------------------------------------------------------------------------- 1 | { 2 | "generator-uxcore": {} 3 | } -------------------------------------------------------------------------------- /tests/index.js: -------------------------------------------------------------------------------- 1 | /** 2 | * only require other specs here 3 | */ 4 | 5 | const req = require.context('.', false, /\.spec\.js(x)?$/); 6 | req.keys().forEach(req); 7 | -------------------------------------------------------------------------------- /demo/FormatterDemo.less: -------------------------------------------------------------------------------- 1 | /** 2 | * Formatter Component Demo Style for Uxcore 3 | * @author guanghong.wsj 4 | * 5 | * Copyright 2014-2015, Uxcore Team, Alinw. 6 | * All rights reserved. 7 | */ -------------------------------------------------------------------------------- /src/index.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Formatter Component for uxcore 3 | * @author guanghong.wsj 4 | * 5 | * Copyright 2014-2015, Uxcore Team, Alinw. 6 | * All rights reserved. 7 | */ 8 | 9 | module.exports = require('./Formatter'); 10 | -------------------------------------------------------------------------------- /demo/index.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Formatter Component Demo for uxcore 3 | * @author guanghong.wsj 4 | * 5 | * Copyright 2014-2015, Uxcore Team, Alinw. 6 | * All rights reserved. 7 | */ 8 | 9 | const Demo = require('./FormatterDemo'); 10 | window.Formatter = Demo; 11 | 12 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.iml 2 | *.log 3 | .idea/ 4 | .ipr 5 | .iws 6 | *~ 7 | ~* 8 | *.diff 9 | *.patch 10 | *.bak 11 | .DS_Store 12 | Thumbs.db 13 | .project 14 | .*proj 15 | .svn/ 16 | *.swp 17 | *.swo 18 | *.pyc 19 | *.pyo 20 | .build 21 | node_modules 22 | _site 23 | sea-modules 24 | spm_modules 25 | .cache 26 | .happypack 27 | dist 28 | build 29 | assets/**/*.css 30 | -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | bower_components/ 2 | *.cfg 3 | node_modules/ 4 | nohup.out 5 | *.iml 6 | .idea/ 7 | .ipr 8 | .iws 9 | *~ 10 | ~* 11 | *.diff 12 | *.log 13 | *.patch 14 | *.bak 15 | .DS_Store 16 | Thumbs.db 17 | .project 18 | .*proj 19 | .svn/ 20 | *.swp 21 | out/ 22 | .build 23 | .happypack 24 | node_modules 25 | _site 26 | sea-modules 27 | spm_modules 28 | .cache 29 | dist -------------------------------------------------------------------------------- /HISTORY.md: -------------------------------------------------------------------------------- 1 | # 0.1.8 2 | * `CHANGED` undefined/null/'' return '' 3 | * because they are all invalid value for date. 4 | 5 | # 0.1.7 6 | * `CHANGED` null check 7 | 8 | # 0.1.6 9 | * `CHANGED` fit React@15 10 | * `CHANGED` delete 'package-lock.json' file 11 | # 0.1.3 12 | 13 | * `CHANGED` eslint 14 | * `CHANGED` add tests 15 | * `CHANGED` use default delimeter only delimeter is undefined 16 | 17 | # 0.1.2 18 | 19 | `CHANGED` remove es6 syntax 20 | 21 | # 0.1.1 22 | 23 | `CHANGED` more rational money formatter -------------------------------------------------------------------------------- /index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | uxcore-formatter 5 | 6 | 7 | 8 |
9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "airbnb", 3 | "parser": "babel-eslint", 4 | "plugins": [ 5 | "react" 6 | ], 7 | "env": { 8 | "browser": true, 9 | "mocha": true 10 | }, 11 | "rules": { 12 | "import/no-extraneous-dependencies": "off", 13 | "react/jsx-no-bind": "off", 14 | "no-underscore-dangle": "off", 15 | "jsx-a11y/label-has-for": "off", 16 | "no-plusplus": [ 17 | "error", 18 | { 19 | "allowForLoopAfterthoughts": true 20 | } 21 | ], 22 | "react/no-unused-prop-types": "off", 23 | "react/forbid-prop-types": "off", 24 | "jsx-a11y/no-static-element-interactions": "off", 25 | "arrow-body-style": "off" 26 | } 27 | } -------------------------------------------------------------------------------- /demo/FormatterDemo.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Formatter Component Demo for uxcore 3 | * @author guanghong.wsj 4 | * 5 | * Copyright 2014-2015, Uxcore Team, Alinw. 6 | * All rights reserved. 7 | */ 8 | 9 | const Formatter = require('../src'); 10 | const log = window.console.log; 11 | 12 | log(Formatter.date(new Date(), 'YYYY-MM-DD HH:mm:ss')); // 2015-10-12 14:22:16 13 | log(Formatter.money('6566456.65466545')); // "6 566 456.65466545" 14 | log(Formatter.money('6566456.65466545', ',')); // "6,566,456.65466545" 15 | log(Formatter.money('6566456', ',')); // "6,566,456" 16 | log(Formatter.money('6566456', ',', 4)); // "6,566,456.0000" 17 | log(Formatter.money('6566456', ',', 2)); // "6,566,456.00" 18 | log(Formatter.money('6566456.65466545', ',', 4)); // "6,566,456.6547" 19 | log(Formatter.money('6566456.65466545', ',', 2)); // "6,566,456.65" 20 | module.exports = Formatter; 21 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | 3 | sudo: false 4 | 5 | addons: 6 | apt: 7 | packages: 8 | - xvfb 9 | 10 | notification: 11 | email: 12 | - wsj7552715@hotmail.com 13 | 14 | node_js: 15 | - 6.9.0 16 | 17 | before_install: 18 | - | 19 | if ! git diff --name-only $TRAVIS_COMMIT_RANGE | grep -qve '(\.md$)|(\.html$)' 20 | then 21 | echo "Only docs were updated, stopping build process." 22 | exit 23 | fi 24 | phantomjs --version 25 | install: 26 | - export DISPLAY=':99.0' 27 | - Xvfb :99 -screen 0 1024x768x24 > /dev/null 2>&1 & 28 | - npm install 29 | 30 | 31 | script: 32 | - | 33 | if [ "$TEST_TYPE" = test ]; then 34 | npm test 35 | else 36 | npm run $TEST_TYPE 37 | fi 38 | env: 39 | matrix: 40 | - TEST_TYPE=test 41 | - TEST_TYPE=coverage 42 | - TEST_TYPE=saucelabs 43 | 44 | matrix: 45 | allow_failures: 46 | - env: "TEST_TYPE=saucelabs" -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "uxcore-formatter", 3 | "version": "0.1.8", 4 | "description": "a formatter library for uxcore", 5 | "repository": "https://github.com/uxcore/uxcore-formatter.git", 6 | "author": "eternalsky", 7 | "main": "build/index.js", 8 | "scripts": { 9 | "start": "uxcore-tools run start", 10 | "server": "uxcore-tools run server", 11 | "lint": "uxcore-tools run lint", 12 | "build": "uxcore-tools run build", 13 | "test": "uxcore-tools run test", 14 | "coverage": "uxcore-tools run coverage", 15 | "pub": "uxcore-tools run pub", 16 | "dep": "uxcore-tools run dep", 17 | "tnpm-dep": "uxcore-tools run tnpm-dep", 18 | "chrome": "uxcore-tools run chrome", 19 | "browsers": "uxcore-tools run browsers", 20 | "saucelabs": "uxcore-tools run saucelabs", 21 | "update": "uxcore-tools run update", 22 | "tnpm-update": "uxcore-tools run tnpm-update" 23 | }, 24 | "bugs": { 25 | "url": "http://github.com/uxcore/uxcore-formatter/issues" 26 | }, 27 | "keywords": [ 28 | "react", 29 | "react-component", 30 | "uxcore-formatter", 31 | "Formatter", 32 | "" 33 | ], 34 | "devDependencies": { 35 | "console-polyfill": "^0.2.2", 36 | "enzyme": "^3.0.0", 37 | "enzyme-adapter-react-15": "^1.0.0", 38 | "es5-shim": "^4.5.8", 39 | "expect.js": "~0.3.1", 40 | "kuma-base": "1.x", 41 | "react": "15.x", 42 | "react-dom": "15.x", 43 | "react-test-renderer": "15.x", 44 | "uxcore-kuma": "*", 45 | "uxcore-tools": "0.2.x" 46 | }, 47 | "dependencies": { 48 | "object-assign": "^2.0.0", 49 | "prop-types": "15.x" 50 | }, 51 | "contributors": [], 52 | "license": "MIT" 53 | } -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | --- 2 | 3 | ## uxcore-formatter [![Dependency Status](http://img.shields.io/david/uxcore/uxcore-formatter.svg?style=flat-square)](https://david-dm.org/uxcore/uxcore-formatter) [![devDependency Status](http://img.shields.io/david/dev/uxcore/uxcore-formatter.svg?style=flat-square)](https://david-dm.org/uxcore/uxcore-formatter#info=devDependencies) 4 | 5 | 6 | Formatter 是一些处理字符串的工具函数的集合,他的设计原则是第一个参数为一个 string,返回值为一个 string,后面的参数皆为可选,即有默认值。 7 | 8 | #### setup develop environment 9 | 10 | ```sh 11 | $ git clone https://github.com/uxcore/uxcore-formatter 12 | $ cd uxcore-formatter 13 | $ npm install 14 | $ npm start 15 | ``` 16 | 17 | ## Usage 18 | ``` 19 | var Formatter = require("uxcore-formatter"); 20 | console.log(Formatter.date(new Date(), 'YYYY-MM-DD'); 21 | ``` 22 | 23 | ## API 24 | > 目前提供的函数包括: 25 | 26 | ### Date 27 | ```javascript 28 | Formatter.date(new Date(), 'YYYY-MM-DD HH:mm:ss'); // 2015-10-12 14:22:16 29 | ``` 30 | 31 | ### money 32 | 33 | ```javascript 34 | Formatter.money("6566456.65466545") // "6 566 456.65466545" 35 | Formatter.money("6566456.65466545", ",") // "6,566,456.65466545" 36 | Formatter.money("6566456", ",") // "6,566,456" 37 | Formatter.money("6566456", ",", 4) // "6,566,456.0000" 38 | Formatter.money("6566456", ",", 2) // "6,566,456.00" 39 | Formatter.money("6566456.65466545", ",", 4) // "6,566,456.6547" 40 | Formatter.money("6566456.65466545", ",", 2) // "6,566,456.65" 41 | ``` 42 | 43 | ### cnmobile 44 | ```javascript 45 | Formatter.cnmobile('+8615652988282', ' '); // +86 1565 2988 282 46 | ``` 47 | 48 | ### card 49 | ```javascript 50 | Formatter.card('1565298828212233', ' '); // 1565 2988 2821 2233 51 | ``` 52 | 53 | 54 | -------------------------------------------------------------------------------- /src/Formatter.js: -------------------------------------------------------------------------------- 1 | /* eslint no-console: "off" */ 2 | /** 3 | * Formatter Component for uxcore 4 | * @author eternalsky 5 | * 6 | * Copyright 2014-2015, Uxcore Team, Alinw. 7 | * All rights reserved. 8 | */ 9 | 10 | const Formatter = {}; 11 | 12 | Formatter.date = (str, pattern) => { 13 | // new Date(null,false) 会返回最初日期。 14 | if (str === null || str === false || str === undefined || str === '') { 15 | console.warn('Formatter: invalid date'); 16 | return ''; 17 | } 18 | const date = new Date(str); 19 | if (Object.prototype.toString.call(date) === '[object Date]') { 20 | if (isNaN(date.getTime())) { 21 | // invalid 22 | console.warn('Formatter: invalid date'); 23 | return ''; 24 | } 25 | let actualPattern = pattern || 'YYYY-MM-DD'; 26 | const o = { 27 | 'M+': date.getMonth() + 1, // 月份 28 | 'D+': date.getDate(), // 日 29 | 'd+': date.getDate(), // 日 30 | 'H+': date.getHours(), // 小时 31 | 'h+': date.getHours(), // 小时 32 | 'm+': date.getMinutes(), // 分 33 | 's+': date.getSeconds(), // 秒 34 | 'Q+': Math.floor((date.getMonth() + 3) / 3), // 季度 35 | 'q+': Math.floor((date.getMonth() + 3) / 3), // 季度 36 | S: date.getMilliseconds(), // 毫秒 37 | }; 38 | if (/(y+)/i.test(actualPattern)) { 39 | actualPattern = actualPattern.replace( 40 | RegExp.$1, 41 | (`${date.getFullYear()}`).substr(4 - RegExp.$1.length) 42 | ); 43 | } 44 | // for (const k in o) { 45 | // if (new RegExp(`(${k})`).test(actualPattern)) { 46 | // actualPattern = actualPattern.replace(RegExp.$1, 47 | // (RegExp.$1.length === 1) ? (o[k]) : ((`00${o[k]}`).substr((`${o[k]}`).length)) 48 | // ); 49 | // } 50 | // } 51 | Object.keys(o).forEach((k) => { 52 | if (new RegExp(`(${k})`).test(actualPattern)) { 53 | actualPattern = actualPattern.replace(RegExp.$1, 54 | (RegExp.$1.length === 1) ? (o[k]) : ((`00${o[k]}`).substr((`${o[k]}`).length)) 55 | ); 56 | } 57 | }); 58 | return actualPattern; 59 | } 60 | return ''; 61 | }; 62 | 63 | Formatter.money = (str, delimiter = ' ', fixedNum) => { 64 | const actualStr = fixedNum ? parseFloat(str).toFixed(fixedNum).toString() : str; 65 | if (actualStr.indexOf('.') !== -1) { 66 | return actualStr.replace(/(\d)(?=(?:\d{3})+(\.))/g, (match, $1) => $1 + delimiter) 67 | .replace(/(\d{3})(?![$|\.|\(|\s])/g, (match, $1) => $1); 68 | } 69 | return actualStr.replace(/(\d)(?=(?:\d{3})+$)/g, (match, $1) => $1 + delimiter); 70 | }; 71 | 72 | Formatter.cnmobile = (str, delimiter = ' ') => { 73 | return str.replace(/^(\+?0?86)(?!$)/, 74 | `$1${delimiter}` 75 | ).replace(/(\d{4})(?!$)/g, `$1${delimiter}`); 76 | }; 77 | 78 | Formatter.card = (str, delimiter = ' ') => { 79 | return str.replace(/(\d{4})(?!$)/g, `$1${delimiter}`); 80 | }; 81 | 82 | export default Formatter; 83 | -------------------------------------------------------------------------------- /tests/Formatter.spec.js: -------------------------------------------------------------------------------- 1 | import expect from 'expect.js'; 2 | import Formatter from '../src'; 3 | 4 | String.prototype.padStart = function (targetLength, padString) { 5 | padString = padString || ' '; 6 | if (this.length >= targetLength) { 7 | return this; 8 | } 9 | const gap = targetLength - this.length; 10 | const times = Math.floor(gap / padString.length); 11 | let result = ''; 12 | for (let i = 0; i < times; i++) { 13 | result += padString; 14 | } 15 | return result + padString.slice(0, gap % padString.length) + this; 16 | }; 17 | 18 | describe('Formatter.date', () => { 19 | const date = new Date(); 20 | it('returns empty string when date is invalid', () => { 21 | expect(Formatter.date('test!@#$%^', 'YYYY-MM-DD')).to.be(''); 22 | }); 23 | 24 | it('returns empty string when date is null', () => { 25 | expect(Formatter.date(null, 'YYYY-MM-DD')).to.be(''); 26 | expect(Formatter.date(undefined, 'YYYY-MM-DD')).to.be(''); 27 | expect(Formatter.date(false, 'YYYY-MM-DD')).to.be(''); 28 | expect(Formatter.date('', 'YYYY-MM-DD')).to.be(''); 29 | }); 30 | 31 | it('works fine', () => { 32 | expect(Formatter.date(date, 'YYYY-MM-DD')).to.be(`${date.getFullYear()}-${`${date.getMonth() + 1}`.padStart(2, '0')}-${`${date.getDate()}`.padStart(2, '0')}`); 33 | }); 34 | 35 | it('works without pattern', () => { 36 | expect(Formatter.date(date)).to.be(`${date.getFullYear()}-${`${date.getMonth() + 1}`.padStart(2, '0')}-${`${date.getDate()}`.padStart(2, '0')}`); 37 | }); 38 | 39 | it('works with custom pattern', () => { 40 | expect(Formatter.date(date, 'MM-DD')).to.be(`${date.getMonth() < 9 ? `0${date.getMonth() + 1}` : date.getMonth() + 1}-${date.getDate() < 10 ? `0${date.getDate()}` : date.getDate()}`); 41 | expect(Formatter.date(date, 'DD')).to.be(`${date.getDate() < 10 ? `0${date.getDate()}` : date.getDate()}`); 42 | expect(Formatter.date(date, 'MM-dd')).to.be(`${date.getMonth() < 9 ? `0${date.getMonth() + 1}` : date.getMonth() + 1}-${date.getDate() < 10 ? `0${date.getDate()}` : date.getDate()}`); 43 | expect(Formatter.date(date, 'QQ')).to.be(`${Math.floor(date.getMonth() / 3) + 1}`.padStart(2, '0')); 44 | expect(Formatter.date(date, 'qq')).to.be(`${Math.floor(date.getMonth() / 3) + 1}`.padStart(2, '0')); 45 | expect(Formatter.date(date, 'hh:mm:ss S')).to.be(`${`${date.getHours()}`.padStart(2, '0')}:${`${date.getMinutes()}`.padStart(2, '0')}:${`${date.getSeconds()}`.padStart(2, '0')} ${`${date.getMilliseconds()}`.padStart(3, '0')}`); 46 | }); 47 | }); 48 | 49 | describe('Formatter.money', () => { 50 | it('works fine', () => { 51 | expect(Formatter.money('100', ' ')).to.be('100'); 52 | expect(Formatter.money('1000000000', ' ')).to.be('1 000 000 000'); 53 | expect(Formatter.money('1000000000.999999', ' ')).to.be('1 000 000 000.999999'); 54 | }); 55 | 56 | it('works without delimiter', () => { 57 | expect(Formatter.money('100')).to.be('100'); 58 | expect(Formatter.money('1000000000')).to.be('1 000 000 000'); 59 | expect(Formatter.money('1000000000.999999')).to.be('1 000 000 000.999999'); 60 | }); 61 | 62 | it('works with custom delimiter', () => { 63 | expect(Formatter.money('100', ',')).to.be('100'); 64 | expect(Formatter.money('1000000000', ',')).to.be('1,000,000,000'); 65 | expect(Formatter.money('1000000000.999999', ',')).to.be('1,000,000,000.999999'); 66 | expect(Formatter.money('1000000000', '')).to.be('1000000000'); 67 | }); 68 | 69 | it('works with fixed number', () => { 70 | expect(Formatter.money('100', ',', 0)).to.be('100'); 71 | expect(Formatter.money('100', ',', 2)).to.be('100.00'); 72 | expect(Formatter.money('1000000000.8888', ',', 2)).to.be('1,000,000,000.89'); 73 | expect(Formatter.money('1000000000.99', ',', 5)).to.be('1,000,000,000.99000'); 74 | }); 75 | }); 76 | 77 | describe('Formatter.cnmobile', () => { 78 | it('works fine', () => { 79 | expect(Formatter.cnmobile('13888888888', ' ')).to.be('1388 8888 888'); 80 | expect(Formatter.cnmobile('+8613888888888', ' ')).to.be('+86 1388 8888 888'); 81 | expect(Formatter.cnmobile('08613888888888', ' ')).to.be('086 1388 8888 888'); 82 | }); 83 | 84 | it('works without delimiter', () => { 85 | expect(Formatter.cnmobile('13888888888')).to.be('1388 8888 888'); 86 | expect(Formatter.cnmobile('+8613888888888')).to.be('+86 1388 8888 888'); 87 | expect(Formatter.cnmobile('08613888888888')).to.be('086 1388 8888 888'); 88 | }); 89 | 90 | it('works with custom delimiter', () => { 91 | expect(Formatter.cnmobile('13888888888', ',')).to.be('1388,8888,888'); 92 | expect(Formatter.cnmobile('+8613888888888', ',')).to.be('+86,1388,8888,888'); 93 | expect(Formatter.cnmobile('08613888888888', ',')).to.be('086,1388,8888,888'); 94 | }); 95 | }); 96 | 97 | describe('Formatter.card', () => { 98 | it('works fine', () => { 99 | expect(Formatter.card('1234', ' ')).to.be('1234'); 100 | expect(Formatter.card('12345678', ' ')).to.be('1234 5678'); 101 | expect(Formatter.card('123456789012', ' ')).to.be('1234 5678 9012'); 102 | expect(Formatter.card('1234567890123456', ' ')).to.be('1234 5678 9012 3456'); 103 | expect(Formatter.card('1234567890123456789', ' ')).to.be('1234 5678 9012 3456 789'); 104 | }); 105 | 106 | it('works without delimiter', () => { 107 | expect(Formatter.card('1234')).to.be('1234'); 108 | expect(Formatter.card('12345678')).to.be('1234 5678'); 109 | expect(Formatter.card('123456789012')).to.be('1234 5678 9012'); 110 | expect(Formatter.card('1234567890123456')).to.be('1234 5678 9012 3456'); 111 | expect(Formatter.card('1234567890123456789')).to.be('1234 5678 9012 3456 789'); 112 | }); 113 | 114 | it('works with custom delimiter', () => { 115 | expect(Formatter.card('12345678', ',')).to.be('1234,5678'); 116 | expect(Formatter.card('123456789012', ',')).to.be('1234,5678,9012'); 117 | expect(Formatter.card('1234567890123456', ',')).to.be('1234,5678,9012,3456'); 118 | expect(Formatter.card('1234567890123456789', ',')).to.be('1234,5678,9012,3456,789'); 119 | }); 120 | }); 121 | --------------------------------------------------------------------------------