├── .eslintignore ├── example ├── alert.doc ├── card.doc ├── menu.doc ├── navbar.doc ├── tabs.doc ├── pagination.doc ├── date-time-picker.doc ├── modal.doc ├── button.doc ├── drop-down.doc ├── date-time-picker │ ├── test.vue │ ├── align.vue │ ├── date-time-picker.md │ ├── range.vue │ └── base.vue ├── alert │ ├── basic.vue │ ├── helper.vue │ └── with-close.vue ├── button │ ├── button.md │ ├── base.vue │ ├── disable.vue │ ├── outline.vue │ ├── size.vue │ └── icon-button.vue ├── menu │ └── basic.vue ├── tabs │ ├── router-tabs.vue │ └── base.vue ├── pagination │ └── basic.vue ├── drop-down │ ├── drop-down.md │ ├── drop-down-menu.vue │ ├── slot.vue │ ├── no-auto-close.vue │ ├── base.vue │ └── width.vue ├── card │ ├── base.vue │ └── with-header-footer.vue ├── navbar │ └── basic.vue └── modal │ └── basic.vue ├── src ├── component │ ├── mixin │ │ ├── tabs-mixin.js │ │ ├── drop-down-mixin.js │ │ └── prop-fill.js │ ├── menu │ │ ├── sub-menu.vue │ │ ├── index.js │ │ ├── index.vue │ │ └── menu-item.vue │ ├── pagination │ │ ├── index.js │ │ └── index.vue │ ├── alert │ │ ├── alert-link.vue │ │ ├── alert-heading.vue │ │ └── alert.vue │ ├── navbar │ │ ├── navbar-nav.vue │ │ ├── navbar-brand.vue │ │ ├── index.js │ │ ├── index.vue │ │ ├── nav-group.vue │ │ └── nav-item.vue │ ├── svg-icon │ │ ├── props-mixin.js │ │ ├── chevron-down.vue │ │ ├── chevron-left.vue │ │ ├── chevron-right.vue │ │ ├── chevron-up.vue │ │ ├── arrow-back.vue │ │ ├── arrow-forward.vue │ │ ├── event-note.vue │ │ ├── av-time.vue │ │ └── svg-icon.vue │ ├── drop-down │ │ ├── drop-down-menu-divider.vue │ │ ├── drop-down-menu-item.vue │ │ ├── drop-down-menu.vue │ │ └── drop-down.vue │ ├── card │ │ ├── card-text.vue │ │ ├── card-block.vue │ │ ├── card-link.vue │ │ ├── card-title.vue │ │ ├── card-image.vue │ │ └── card.vue │ ├── button │ │ ├── icon-button.vue │ │ └── button.vue │ ├── tabs │ │ ├── tabs-mixin.js │ │ ├── router-tabs.vue │ │ ├── tab.vue │ │ └── tabs.vue │ ├── modal │ │ ├── backdrop.vue │ │ ├── modal.vue │ │ └── notification.vue │ └── date-picker │ │ ├── month-picker.vue │ │ ├── year-picker.vue │ │ ├── date-picker.vue │ │ ├── util.js │ │ ├── time-picker.vue │ │ ├── date-time-picker.js │ │ └── date-time-picker.vue ├── util │ ├── index.js │ ├── dom.js │ └── EventListener.js ├── style │ ├── base.styl │ ├── variable.styl │ ├── component │ │ ├── dropdown.styl │ │ └── date-time-picker.styl │ ├── index.styl │ ├── transition.styl │ ├── button.styl │ └── normalize.css └── index.js ├── test ├── build │ ├── dev.sh │ ├── webpack.build.min.js │ ├── makeWebpackConfig.js │ ├── webpack.config.test.js │ ├── webpack.build.js │ ├── webpack.build.doc.js │ ├── karma.conf.js │ └── webpack.config.dev.js ├── test.js ├── .eslintrc └── unit │ ├── demo_spec │ └── drop-down-base.js │ └── demo_test.js ├── demo-loader ├── Readme.md ├── index.js ├── selector.js ├── loader.js └── .eslintrc ├── .babelrc ├── doc ├── index.vue ├── index.html ├── app.vue ├── menu.vue ├── doc.js ├── code-panel.vue ├── router.js └── doc.styl ├── .gitignore ├── .editorconfig ├── .travis.yml ├── Readme.md ├── server.js ├── CHANGELOG.md ├── .eslintrc.js ├── package.json └── gulpfile.js /.eslintignore: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /example/alert.doc: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /example/card.doc: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /example/menu.doc: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /example/navbar.doc: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /example/tabs.doc: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /example/pagination.doc: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /example/date-time-picker.doc: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /example/modal.doc: -------------------------------------------------------------------------------- 1 | placeholder -------------------------------------------------------------------------------- /example/button.doc: -------------------------------------------------------------------------------- 1 | placeholder -------------------------------------------------------------------------------- /example/drop-down.doc: -------------------------------------------------------------------------------- 1 | placeholder -------------------------------------------------------------------------------- /src/component/mixin/tabs-mixin.js: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /test/build/dev.sh: -------------------------------------------------------------------------------- 1 | node server.js & gulp test-watch 2 | -------------------------------------------------------------------------------- /example/date-time-picker/test.vue: -------------------------------------------------------------------------------- 1 | 4 | -------------------------------------------------------------------------------- /src/util/index.js: -------------------------------------------------------------------------------- 1 | import * as dom from './dom' 2 | 3 | export default { 4 | dom 5 | } 6 | -------------------------------------------------------------------------------- /demo-loader/Readme.md: -------------------------------------------------------------------------------- 1 | # issue 2 | 1. 首次启动编译两次 3 | 2. 文件删除和新增在重新编译的时候不会被编译进去(可能是因为cacheable) 4 | -------------------------------------------------------------------------------- /src/style/base.styl: -------------------------------------------------------------------------------- 1 | //@import "~normalize.css"; 2 | 3 | * { 4 | outline: none !important; 5 | } -------------------------------------------------------------------------------- /.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": ["es2015", "stage-0"], 3 | "plugins": ["transform-runtime"] 4 | } 5 | -------------------------------------------------------------------------------- /src/component/menu/sub-menu.vue: -------------------------------------------------------------------------------- 1 | 6 | -------------------------------------------------------------------------------- /src/component/pagination/index.js: -------------------------------------------------------------------------------- 1 | import pagination from './index.vue' 2 | 3 | export default pagination 4 | -------------------------------------------------------------------------------- /test/test.js: -------------------------------------------------------------------------------- 1 | const testsContext = require.context('.', true, /_test$/) 2 | testsContext.keys().forEach(testsContext) 3 | -------------------------------------------------------------------------------- /src/component/alert/alert-link.vue: -------------------------------------------------------------------------------- 1 | 6 | -------------------------------------------------------------------------------- /src/component/alert/alert-heading.vue: -------------------------------------------------------------------------------- 1 | 6 | -------------------------------------------------------------------------------- /src/component/navbar/navbar-nav.vue: -------------------------------------------------------------------------------- 1 | 6 | -------------------------------------------------------------------------------- /doc/index.vue: -------------------------------------------------------------------------------- 1 | 5 | 6 | 9 | -------------------------------------------------------------------------------- /src/component/navbar/navbar-brand.vue: -------------------------------------------------------------------------------- 1 | 4 | 5 | 10 | -------------------------------------------------------------------------------- /src/component/svg-icon/props-mixin.js: -------------------------------------------------------------------------------- 1 | export default { 2 | props: { 3 | size: { 4 | // type: [Number, String], 5 | default: 24 6 | } 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /src/component/drop-down/drop-down-menu-divider.vue: -------------------------------------------------------------------------------- 1 | 4 | 5 | 8 | -------------------------------------------------------------------------------- /src/component/menu/index.js: -------------------------------------------------------------------------------- 1 | import Menu from './index.vue' 2 | 3 | export MenuItem from './menu-item.vue' 4 | 5 | export SubMenu from './sub-menu.vue' 6 | 7 | export default Menu 8 | -------------------------------------------------------------------------------- /src/component/menu/index.vue: -------------------------------------------------------------------------------- 1 | 6 | 7 | 12 | -------------------------------------------------------------------------------- /src/component/menu/menu-item.vue: -------------------------------------------------------------------------------- 1 | 6 | 7 | 12 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | node_modules 3 | npm-debug.log 4 | dist 5 | .idea 6 | coverage 7 | bower_components 8 | *.swp 9 | *.swo 10 | ngrok.config.json 11 | *.cache 12 | doc-build 13 | source 14 | -------------------------------------------------------------------------------- /src/component/card/card-text.vue: -------------------------------------------------------------------------------- 1 | 6 | 7 | 10 | -------------------------------------------------------------------------------- /src/component/card/card-block.vue: -------------------------------------------------------------------------------- 1 | 6 | 7 | 10 | -------------------------------------------------------------------------------- /doc/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | x-vue doc 6 | 7 | 8 | 9 |
10 | 11 |
12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [src/**/*.{js, vue}] 4 | indent_style = space 5 | indent_size = 2 6 | 7 | end_of_line = lf 8 | insert_final_newline = true 9 | 10 | charset = utf-8 11 | 12 | trim_trailing_whitespace = true -------------------------------------------------------------------------------- /demo-loader/index.js: -------------------------------------------------------------------------------- 1 | var selectorPath = require.resolve('./selector.js') 2 | 3 | module.exports = function (source) { 4 | 5 | source = source.replace(/![^!]*selector\.js/g, '!' + selectorPath) 6 | 7 | return source 8 | 9 | } 10 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | node_js: 3 | - "4.1" 4 | - "4.0" 5 | 6 | before_script: 7 | - npm install -g gulp 8 | script: gulp 9 | after_script: 10 | - cat ./coverage/lcov.info | ./node_modules/coveralls/bin/coveralls.js 11 | -------------------------------------------------------------------------------- /src/style/variable.styl: -------------------------------------------------------------------------------- 1 | //// font weight 2 | //$font-weight-base: 100; 3 | // 4 | //// line height 5 | //$line-height-base: 48px; 6 | // 7 | //// color 8 | //$color-default: #ffffff; 9 | //$color-font-default: #333333; 10 | 11 | $z-index-drop-down = 1000; 12 | -------------------------------------------------------------------------------- /src/style/component/dropdown.styl: -------------------------------------------------------------------------------- 1 | @import "../variable"; 2 | 3 | .drop-down-content{ 4 | position : absolute; 5 | //left: 0; 6 | top: 100%; 7 | margin-top: 5px; 8 | z-index: $z-index-drop-down; 9 | min-width: 160px; 10 | white-space: nowrap; 11 | } 12 | -------------------------------------------------------------------------------- /example/alert/basic.vue: -------------------------------------------------------------------------------- 1 | 7 | -------------------------------------------------------------------------------- /src/component/button/icon-button.vue: -------------------------------------------------------------------------------- 1 | 4 | 5 | 13 | -------------------------------------------------------------------------------- /test/.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "globals": { 3 | "$": true, 4 | "jQuery": true, 5 | "before": true, 6 | "after": true, 7 | "beforeEach": true, 8 | "afterEach": true, 9 | "it": true, 10 | "app": true, 11 | "Vue": true 12 | }, 13 | "rules": { 14 | 15 | } 16 | } -------------------------------------------------------------------------------- /src/component/navbar/index.js: -------------------------------------------------------------------------------- 1 | import Navbar from './index.vue' 2 | 3 | export NavbarBrand from './navbar-brand.vue' 4 | 5 | export NavItem from './nav-item.vue' 6 | 7 | export NavbarNav from './navbar-nav.vue' 8 | 9 | export NavGroup from './nav-group.vue' 10 | 11 | export default Navbar 12 | -------------------------------------------------------------------------------- /src/style/index.styl: -------------------------------------------------------------------------------- 1 | @import "base"; 2 | //@import "button"; 3 | //@import "normalize.css"; 4 | 5 | // components style 6 | //@import "component/dropdown"; 7 | 8 | @import "transition"; 9 | 10 | 11 | // components 12 | @import "component/dropdown"; 13 | @import "component/date-time-picker"; 14 | 15 | // add test 16 | -------------------------------------------------------------------------------- /test/build/webpack.build.min.js: -------------------------------------------------------------------------------- 1 | const webpack = require('webpack') 2 | const buildConfig = require('./webpack.build') 3 | 4 | buildConfig.output.filename = 'x-vue.min.js' 5 | 6 | buildConfig.plugins.push(new webpack.optimize.UglifyJsPlugin({ 7 | compress: { 8 | warnings: false 9 | } 10 | })) 11 | 12 | module.exports = buildConfig 13 | -------------------------------------------------------------------------------- /src/component/mixin/drop-down-mixin.js: -------------------------------------------------------------------------------- 1 | export default { 2 | props: { 3 | label: { 4 | type: String, 5 | default: '' 6 | }, 7 | // 显示按钮的形态 8 | // OK 9 | btnType: { 10 | type: String, 11 | default: '' 12 | }, 13 | disabled: { 14 | type: Boolean, 15 | default: false 16 | }, 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /example/alert/helper.vue: -------------------------------------------------------------------------------- 1 | 12 | -------------------------------------------------------------------------------- /example/button/button.md: -------------------------------------------------------------------------------- 1 | ### 说明 2 | 这里是说明 3 | 4 | ### Props 5 | name | type | default | explain 6 | --- | --- | --- | --- 7 | type | String | 'secondary' | primary success info secondary danger warning link 8 | outline | Boolean | false | 是否只有边框上色 9 | size | String | '' | 'lg', 'sm' 10 | block | Boolean | false | is block 11 | disabled | Boolean | false | is disable 12 | -------------------------------------------------------------------------------- /src/component/tabs/tabs-mixin.js: -------------------------------------------------------------------------------- 1 | export default { 2 | props: { 3 | type: { 4 | type: String, 5 | default: 'tabs' 6 | } 7 | }, 8 | computed: { 9 | classes() { 10 | let classes = { 11 | 'nav': true, 12 | 'nav-tabs': this.type === 'tabs', 13 | 'nav-pills': this.type === 'pills' 14 | } 15 | return classes 16 | } 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /src/component/svg-icon/chevron-down.vue: -------------------------------------------------------------------------------- 1 | 4 | 5 | 16 | -------------------------------------------------------------------------------- /src/component/svg-icon/chevron-left.vue: -------------------------------------------------------------------------------- 1 | 4 | 5 | 16 | -------------------------------------------------------------------------------- /src/component/svg-icon/chevron-right.vue: -------------------------------------------------------------------------------- 1 | 4 | 5 | 16 | -------------------------------------------------------------------------------- /src/component/svg-icon/chevron-up.vue: -------------------------------------------------------------------------------- 1 | 4 | 5 | 16 | -------------------------------------------------------------------------------- /example/date-time-picker/align.vue: -------------------------------------------------------------------------------- 1 | 15 | -------------------------------------------------------------------------------- /example/menu/basic.vue: -------------------------------------------------------------------------------- 1 | 9 | 10 | 20 | -------------------------------------------------------------------------------- /src/component/svg-icon/arrow-back.vue: -------------------------------------------------------------------------------- 1 | 4 | 5 | 16 | -------------------------------------------------------------------------------- /src/component/svg-icon/arrow-forward.vue: -------------------------------------------------------------------------------- 1 | 4 | 5 | 16 | -------------------------------------------------------------------------------- /src/component/card/card-link.vue: -------------------------------------------------------------------------------- 1 | 6 | 7 | 21 | -------------------------------------------------------------------------------- /src/component/drop-down/drop-down-menu-item.vue: -------------------------------------------------------------------------------- 1 | 6 | 7 | 24 | -------------------------------------------------------------------------------- /doc/app.vue: -------------------------------------------------------------------------------- 1 | 9 | 10 | 25 | -------------------------------------------------------------------------------- /src/component/navbar/index.vue: -------------------------------------------------------------------------------- 1 | 6 | 7 | 25 | -------------------------------------------------------------------------------- /src/util/dom.js: -------------------------------------------------------------------------------- 1 | export function getBodyScrollTop() { 2 | let scrollTop 3 | if (typeof window.pageYOffset !== 'undefined') { // pageYOffset指的是滚动条顶部到网页顶部的距离 4 | scrollTop = window.pageYOffset 5 | } else if (typeof document.compatMode !== 'undefined' && document.compatMode !== 'BackCompat') { 6 | scrollTop = document.documentElement.scrollTop 7 | } else if (typeof document.body !== 'undefined') { 8 | scrollTop = document.body.scrollTop 9 | } 10 | return scrollTop 11 | } 12 | -------------------------------------------------------------------------------- /src/component/svg-icon/event-note.vue: -------------------------------------------------------------------------------- 1 | 4 | 5 | 16 | -------------------------------------------------------------------------------- /src/component/mixin/prop-fill.js: -------------------------------------------------------------------------------- 1 | /* 2 | learn from vue-mdl https://github.com/posva/vue-mdl/blob/develop/src/mixins/prop-fill.js 3 | */ 4 | 5 | // When declaring a prop with no value it should be evaluated to true 6 | // but '' is falsy. As a solution add the 7 | export default { 8 | beforeCompile () { 9 | for (let prop of Object.keys(this._props)) { 10 | let data = this._props[prop] 11 | if (data.options.fill && data.raw === '') { 12 | this[prop] = prop 13 | } 14 | } 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /src/component/navbar/nav-group.vue: -------------------------------------------------------------------------------- 1 | 6 | 7 | 28 | -------------------------------------------------------------------------------- /Readme.md: -------------------------------------------------------------------------------- 1 | # Intro 2 | Vue + Bootstrap4 components 3 | 4 | For detail, [view doc](https://x-front-team.github.io/x-vue/) 5 | 6 | # Install 7 | ``` 8 | npm i x-vue 9 | ``` 10 | 11 | # Usage 12 | ``` 13 | import vue from 'vue' 14 | import XVue from 'x-vue' 15 | vue.use(XVue) 16 | ``` 17 | 18 | # Development 19 | clone the project 20 | ``` 21 | npm install 22 | npm start 23 | ``` 24 | 25 | # 文档如何生成 26 | 在example根目录下加入某个组件为名字的`.doc`文件,如`button.doc`,然后创建一个button文件夹, 27 | 在该文件夹中随意创建多个demo,以`.vue`为后缀,然后在`router.js`中引用这个`.doc`文件 28 | -------------------------------------------------------------------------------- /src/component/navbar/nav-item.vue: -------------------------------------------------------------------------------- 1 | 8 | 9 | 27 | -------------------------------------------------------------------------------- /example/tabs/router-tabs.vue: -------------------------------------------------------------------------------- 1 | 4 | 5 | 27 | -------------------------------------------------------------------------------- /src/style/transition.styl: -------------------------------------------------------------------------------- 1 | .drop 2 | &-transition 3 | transition: transform .45s cubic-bezier(0.23, 1, 0.32, 1) 0s, opacity .45s cubic-bezier(0.23, 1, 0.32, 1) 0s; 4 | transform: scaleY(1); 5 | transform-origin: left top 0; 6 | &-enter, &-leave 7 | transform-origin: left top 0; 8 | transform: scaleY(0); 9 | opacity: 0; 10 | 11 | 12 | .fade 13 | &-transition 14 | transition: opacity .3s; 15 | &-enter, &-leave 16 | opacity: 0; 17 | 18 | .slide-down 19 | &-transition 20 | transition: .3s; 21 | &-enter, &-leave 22 | opacity: 0; 23 | transform: translateY(-30px) 24 | 25 | -------------------------------------------------------------------------------- /example/pagination/basic.vue: -------------------------------------------------------------------------------- 1 | 6 | 7 | 26 | -------------------------------------------------------------------------------- /example/drop-down/drop-down.md: -------------------------------------------------------------------------------- 1 | # Props 2 | name | type | default | explain 3 | --- | --- | --- | --- 4 | label | String | '' | 默认按钮显示的文字 5 | disabled | Boolean | false | 是否禁用 6 | closeOnLoseFocus | Boolean | true | 是否在失去焦点时关闭下拉框 7 | btnType | String | '' | bootstrap button style 8 | position | String | right | 'left' or 'right' 对齐方式 9 | toggle | Boolean | true | 是否点击按钮进行切换,如果时否,点击只打开 10 | showDropDown | Boolean | false | 双向属性,强制通过外部控制是否显示下拉框 11 | classNames | String, Object, Array | '' | 用classnames进行序列化,将传入的class加载drop-content上 12 | 13 | 14 | # slot 15 | ### default 16 | dropdown里面显示的内容 17 | 18 | ### btn 19 | dropdown触发的按钮,默认是一个button 20 | -------------------------------------------------------------------------------- /example/alert/with-close.vue: -------------------------------------------------------------------------------- 1 | 6 | 7 | 21 | 22 | 31 | -------------------------------------------------------------------------------- /src/component/card/card-title.vue: -------------------------------------------------------------------------------- 1 | 9 | 10 | 31 | -------------------------------------------------------------------------------- /src/component/svg-icon/av-time.vue: -------------------------------------------------------------------------------- 1 | 4 | 5 | 16 | -------------------------------------------------------------------------------- /example/button/base.vue: -------------------------------------------------------------------------------- 1 | 14 | 15 | 20 | 21 | 24 | -------------------------------------------------------------------------------- /example/card/base.vue: -------------------------------------------------------------------------------- 1 | 16 | 17 | 26 | -------------------------------------------------------------------------------- /example/button/disable.vue: -------------------------------------------------------------------------------- 1 | 9 | 10 | 15 | 16 | 30 | -------------------------------------------------------------------------------- /example/drop-down/drop-down-menu.vue: -------------------------------------------------------------------------------- 1 | 8 | 9 | 34 | -------------------------------------------------------------------------------- /src/component/svg-icon/svg-icon.vue: -------------------------------------------------------------------------------- 1 | 9 | 10 | 33 | -------------------------------------------------------------------------------- /example/button/outline.vue: -------------------------------------------------------------------------------- 1 | 14 | 15 | 20 | 21 | 24 | -------------------------------------------------------------------------------- /example/button/size.vue: -------------------------------------------------------------------------------- 1 | 13 | 14 | 19 | 20 | 23 | -------------------------------------------------------------------------------- /example/card/with-header-footer.vue: -------------------------------------------------------------------------------- 1 | 16 | 17 | 26 | -------------------------------------------------------------------------------- /test/build/makeWebpackConfig.js: -------------------------------------------------------------------------------- 1 | 2 | function Config(base) { 3 | this.base = base 4 | this.entry = [] 5 | } 6 | 7 | Config.prototype.build = function () { 8 | // return this.config 9 | } 10 | 11 | Config.prototype.entry = function (entry) { 12 | this.entry = entry instanceof Array ? entry : [entry] 13 | return this 14 | } 15 | 16 | Config.prototype.addEntry = function (entry) { 17 | if (this.entry instanceof Array) { 18 | this.entry.push(entry) 19 | } else { 20 | throw new TypeError('only use addEntry when your entry is an array') 21 | } 22 | return this 23 | } 24 | 25 | Config.prototype.style = function (options) { 26 | this.style = options 27 | return this 28 | } 29 | 30 | function make() { 31 | return new Config() 32 | } 33 | 34 | module.exports = make 35 | -------------------------------------------------------------------------------- /src/component/modal/backdrop.vue: -------------------------------------------------------------------------------- 1 | 6 | 7 | 18 | 19 | 36 | -------------------------------------------------------------------------------- /server.js: -------------------------------------------------------------------------------- 1 | const express = require('express') 2 | const webpack = require('webpack') 3 | 4 | const config = require('./build/webpack.config.dev') 5 | 6 | const app = express() 7 | const compiler = webpack(config) 8 | 9 | const serverOpts = { 10 | // noInfo: true, 11 | publicPath: config.output.publicPath, 12 | stats: { 13 | colors: true, 14 | chunks: false 15 | } 16 | } 17 | 18 | app.use(require('webpack-dev-middleware')(compiler, serverOpts)) 19 | app.use(require('webpack-hot-middleware')(compiler)) 20 | 21 | // app.get('*', function (req, res) { 22 | // res.sendFile(path.join(__dirname, './dist/index.html')) 23 | // }) 24 | 25 | app.listen(3333, '0.0.0.0', (err) => { 26 | if (err) { 27 | console.error(err) 28 | return 29 | } 30 | 31 | console.log('Listening at http://localhost:3333') 32 | }) 33 | 34 | -------------------------------------------------------------------------------- /src/component/tabs/router-tabs.vue: -------------------------------------------------------------------------------- 1 | 12 | 13 | 35 | -------------------------------------------------------------------------------- /doc/menu.vue: -------------------------------------------------------------------------------- 1 | 15 | 20 | 37 | -------------------------------------------------------------------------------- /example/navbar/basic.vue: -------------------------------------------------------------------------------- 1 | 19 | 20 | 32 | -------------------------------------------------------------------------------- /example/modal/basic.vue: -------------------------------------------------------------------------------- 1 | 14 | 15 | 38 | -------------------------------------------------------------------------------- /test/unit/demo_spec/drop-down-base.js: -------------------------------------------------------------------------------- 1 | 2 | describe('drop down base demo', () => { 3 | let 4 | container = $('#x-vue-demo-drop-down-base'), 5 | linkDown = $('#x-vue-demo-drop-down-base-link'), 6 | primaryDown = $('#x-vue-demo-drop-down-base-primary'), 7 | successDown = $('#x-vue-demo-drop-down-base-success'), 8 | infoDown = $('#x-vue-demo-drop-down-base-info'), 9 | dangerDown = $('#x-vue-demo-drop-down-base-danger') 10 | 11 | 12 | it('exists', () => { 13 | container.should.exist 14 | linkDown.should.exist 15 | primaryDown.should.exist 16 | successDown.should.exist 17 | infoDown.should.exist 18 | dangerDown.should.exist 19 | }) 20 | 21 | it('will show drop down after btn click', (done) => { 22 | linkDown.find('.dropdown-toggle').click() 23 | Vue.nextTick(() => { 24 | $('#x-vue-demo-drop-down-base-card-link').should.exist 25 | done() 26 | }) 27 | }) 28 | }) 29 | -------------------------------------------------------------------------------- /example/date-time-picker/date-time-picker.md: -------------------------------------------------------------------------------- 1 | # USAGE 2 | ```html 3 | 4 | ``` 5 | 6 | # params 7 | name | type | default | explain 8 | --- | --- | --- | --- 9 | format | String | 'yyyy-MM-dd' | 格式化时间,同时根据是否有`hh:mm:ss`来判断是否显示时间选择器 10 | highlightToday | Boolean | true | 是否高亮今天 11 | closeOnSelected | Boolean | true | 选择确定之后是否关闭选择器(未实现) 12 | maxDate | anything | '' | 可以被转成毫秒数的值,控制最大选择日期 13 | minDate | anything | '' | 可以被转成毫秒数的值,控制最小选择日期 14 | 15 | # range 16 | ```html 17 | 22 | 23 | 28 | ``` 29 | 通过:range.start[end]来控制区间,同一个name的start和end处于一个区间 30 | -------------------------------------------------------------------------------- /example/button/icon-button.vue: -------------------------------------------------------------------------------- 1 | 14 | 15 | 20 | 21 | 31 | -------------------------------------------------------------------------------- /example/drop-down/slot.vue: -------------------------------------------------------------------------------- 1 | 21 | 33 | 36 | -------------------------------------------------------------------------------- /doc/doc.js: -------------------------------------------------------------------------------- 1 | import Vue from 'vue' 2 | import XVue from '../src' 3 | 4 | Vue.config.debug = true 5 | 6 | import 'bootstrap/dist/css/bootstrap.css' 7 | // import 'font-awesome/css/font-awesome.css' 8 | import '../src/style/index.styl' 9 | import './doc.styl' 10 | import 'highlight.js/styles/default.css' 11 | import 'highlight.js/styles/monokai-sublime.css' 12 | 13 | // import card from '../component/card/card.vue' 14 | // import cardTitle from '../component/card/card-title.vue' 15 | // import cardText from '../component/card/card-text.vue' 16 | // import cardBlock from '../component/card/card-block.vue' 17 | import codePanel from './code-panel.vue' 18 | // 19 | // Vue.component('card', card) 20 | // Vue.component('card-title', cardTitle) 21 | // Vue.component('card-text', cardText) 22 | // Vue.component('card-block', cardBlock) 23 | Vue.component('code-panel', codePanel) 24 | 25 | Vue.use(XVue) 26 | 27 | 28 | import router from './router' 29 | import App from './app.vue' 30 | 31 | router.start(App, '#root') 32 | 33 | -------------------------------------------------------------------------------- /src/util/EventListener.js: -------------------------------------------------------------------------------- 1 | const EventListener = { 2 | /** 3 | * Listen to DOM events during the bubble phase. 4 | * 5 | * @param {DOMEventTarget} target DOM element to register listener on. 6 | * @param {string} eventType Event type, e.g. 'click' or 'mouseover'. 7 | * @param {function} callback Callback function. 8 | * @return {object} Object with a `remove` method. 9 | */ 10 | listen(target, eventType, callback) { 11 | if (target.addEventListener) { 12 | target.addEventListener(eventType, callback, false) 13 | return { 14 | remove() { 15 | target.removeEventListener(eventType, callback, false) 16 | } 17 | } 18 | } else if (target.attachEvent) { 19 | target.attachEvent('on' + eventType, callback) 20 | return { 21 | remove() { 22 | target.detachEvent('on' + eventType, callback) 23 | } 24 | } 25 | } 26 | throw new TypeError('can\'t add listener') 27 | } 28 | } 29 | 30 | export default EventListener 31 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # v-0.1.9 2 | 1. 修改src中源文件的路径引用都为相对路径,保证编译的source文件能用 3 | 4 | # v-0.1.7 5 | 1. 修改目录结构,将源文件都放入src目录下 6 | 2. 发布到npm加入source文件夹,是编译成es5的js,并将其中的index.js作为入口文件 7 | 8 | # v-0.1.5 9 | 1. 分页组件增加显示第一页和最后一页的选型 10 | 11 | # v-0.1.3 12 | 1. 修改date-time-picker的样式文件,将所有样式包在x-date-time-picker-container里面 13 | 2. 增加drop-down的classNames props,以便于对drop-down进行一些定制 14 | 15 | # v-0.1.2 16 | 1. 增加外部改变日期控件value的时候校验生成的日期是否合法 17 | 2. 修改日期控件顶部切换日期和时间的控制器的dom结构,使用row必须在外部包一层container 18 | 3. 日期控件增加左右对其 19 | 20 | # v-0.1.0 21 | 1. 发布到github开源 22 | 2. 完善webpack配置,不再重复配置 23 | 3. 增加deploy_doc脚本,自动部署文档到github/gh-pages 24 | 4. 去掉无用的组件和文档页面 25 | 26 | # v-0.0.61 27 | 1. 增加drop-down的onLoseFocus方法props 28 | 29 | # v-0.0.60 30 | 1. 去掉时间控件中的repeat方法(导致ie不兼容) 31 | 2. 修改生成日期列表的最大最小时间对比方法,旧方法设置时间会出现临界值问题 32 | 33 | # v-0.0.59 34 | 1. 完善文档机制,提供一个页面同名.md文件,自动编译显示在页面底部 35 | 2. 修改drop向右靠齐的机制,转为document.body.clientWidth - element.rect.right 36 | 37 | # v-0.0.56 (2016-7-12) 38 | 1. 本地打包发布在package-min的时候会出现模版字符串style里面的变量消失,结果是依赖包太老的问题 39 | 40 | # v-0.0.10 (2016-5-11) 41 | 1. fix dropdown btn slot not toggle dropdown 42 | 2. change btn slot to `span` not `a` 43 | -------------------------------------------------------------------------------- /example/date-time-picker/range.vue: -------------------------------------------------------------------------------- 1 | 18 | 19 | 34 | -------------------------------------------------------------------------------- /doc/code-panel.vue: -------------------------------------------------------------------------------- 1 | 17 | 18 | 27 | 28 | 48 | -------------------------------------------------------------------------------- /example/tabs/base.vue: -------------------------------------------------------------------------------- 1 | 25 | 30 | 41 | -------------------------------------------------------------------------------- /src/component/card/card-image.vue: -------------------------------------------------------------------------------- 1 | 5 | 6 | 11 | 12 | 57 | -------------------------------------------------------------------------------- /test/build/webpack.config.test.js: -------------------------------------------------------------------------------- 1 | const path = require('path') 2 | const webpack = require('webpack') 3 | 4 | module.exports = { 5 | entry: path.join(__dirname, '../test/test.js'), 6 | output: { 7 | path: path.resolve(__dirname, '../test'), 8 | filename: 'test.js' 9 | }, 10 | resolve: { 11 | alias: { 12 | component: path.resolve(__dirname, '../component') 13 | } 14 | }, 15 | module: { 16 | loaders: [ 17 | { 18 | test: /\.js$/, 19 | loader: 'babel', 20 | exclude: /node_modules/ 21 | }, 22 | { 23 | test: /\.vue$/, 24 | loader: 'vue' 25 | } 26 | ], 27 | postLoaders: [ 28 | { 29 | test: /\.*/, 30 | include: /component|example/, 31 | loader: 'istanbul-instrumenter' 32 | } 33 | ] 34 | }, 35 | plugins: [ 36 | new webpack.DefinePlugin({ 37 | 'process.env': { 38 | NODE_ENV: '"production"' 39 | } 40 | }), 41 | new webpack.ProvidePlugin({ 42 | $: 'jquery', 43 | jQuery: 'jquery', 44 | 'window.jQuery': 'jquery' 45 | }) 46 | ], 47 | devServer: { 48 | contentBase: './test', 49 | noInfo: true 50 | }, 51 | devtool: '#source-map' 52 | } 53 | -------------------------------------------------------------------------------- /example/drop-down/no-auto-close.vue: -------------------------------------------------------------------------------- 1 | 19 | 28 | 43 | -------------------------------------------------------------------------------- /src/component/alert/alert.vue: -------------------------------------------------------------------------------- 1 | 11 | 12 | 52 | -------------------------------------------------------------------------------- /test/build/webpack.build.js: -------------------------------------------------------------------------------- 1 | const path = require('path') 2 | const webpack = require('webpack') 3 | const autoprefixer = require('autoprefixer') 4 | 5 | function postcss() { 6 | return [ 7 | autoprefixer({ 8 | browsers: '> 1%' 9 | }) 10 | ] 11 | } 12 | 13 | module.exports = { 14 | devtool: 'cheap-source-map', 15 | entry: { 16 | app: path.join(__dirname, '../component/index.js') 17 | }, 18 | externals: { 19 | 'vue': 'vue' 20 | }, 21 | debug: true, 22 | output: { 23 | path: path.join(__dirname, '../dist'), 24 | filename: 'x-vue.js', 25 | library: 'XVue', 26 | libraryTarget: 'umd', 27 | umdNamedDefine: true 28 | }, 29 | resolve: {}, 30 | module: { 31 | preLoaders: [ 32 | { test: /\.(js|vue)$/, loader: 'eslint-loader', exclude: /node_modules/ } 33 | ], 34 | loaders: [ 35 | { test: /\.js$/, loader: 'babel', exclude: /node_modules/ }, 36 | { test: /\.vue/, loader: 'vue' }, 37 | { test: /\.scss/, loader: 'style-loader!css-loader!postcss-loader!sass-loader' } 38 | ] 39 | }, 40 | vue: { 41 | postcss 42 | }, 43 | postcss, 44 | plugins: [ 45 | new webpack.DefinePlugin({ 46 | 'process.env': { 47 | NODE_ENV: '"production"' 48 | } 49 | }), 50 | new webpack.optimize.OccurenceOrderPlugin() 51 | ] 52 | } 53 | -------------------------------------------------------------------------------- /demo-loader/selector.js: -------------------------------------------------------------------------------- 1 | var path = require('path') 2 | var parse = require('vue-loader/lib/parser') 3 | var loaderUtils = require('loader-utils') 4 | var marked = require('marked') 5 | 6 | marked.setOptions({ 7 | gfm: true, 8 | tables: true, 9 | breaks: false, 10 | pedantic: false, 11 | sanitize: false, 12 | smartLists: true, 13 | smartypants: false, 14 | highlight: function(code, lang) { 15 | return require('highlight.js').highlight(lang, code).value; 16 | }, 17 | }); 18 | 19 | module.exports = function (content) { 20 | this.cacheable() 21 | var query = loaderUtils.parseQuery(this.query) 22 | var filename = path.basename(this.resourcePath) 23 | var parts = parse(content, filename, this.sourceMap) 24 | var part = parts[query.type][query.index] 25 | if (query.type === 'template') { 26 | 27 | var source = part.content 28 | 29 | 30 | var template_header = '' + 31 | '\n' + 32 | '
\n' + 33 | marked(`\`\`\`html\n ${content} \n\`\`\``) + 34 | '
\n' + 35 | '
\n' 36 | 37 | var template_footer = '' + 38 | '
\n' + 39 | '
\n' 40 | 41 | source = template_header + source + template_footer 42 | 43 | console.log(this._compiler) 44 | 45 | part.content = source 46 | 47 | } 48 | this.callback(null, part.content, part.map) 49 | } 50 | -------------------------------------------------------------------------------- /test/build/webpack.build.doc.js: -------------------------------------------------------------------------------- 1 | const path = require('path') 2 | const webpack = require('webpack') 3 | const autoprefixer = require('autoprefixer') 4 | 5 | function postcss() { 6 | return [ 7 | autoprefixer({ 8 | browsers: '> 1%' 9 | }) 10 | ] 11 | } 12 | 13 | module.exports = { 14 | devtool: 'cheap-source-map', 15 | entry: [path.join(__dirname, '../doc/doc.js')], 16 | debug: true, 17 | output: { 18 | path: path.join(__dirname, '../doc-built'), 19 | filename: 'index.js', 20 | publicPath: 'doc-built/' 21 | }, 22 | resolve: { 23 | alias: { 24 | component: path.resolve(__dirname, '../component'), 25 | util: path.resolve(__dirname, '../util') 26 | } 27 | }, 28 | module: { 29 | preLoaders: [ 30 | { test: /\.(js|vue)$/, loader: 'eslint-loader', exclude: /node_modules/ } 31 | ], 32 | loaders: [ 33 | { test: /\.js$/, loader: 'babel', exclude: /node_modules/ }, 34 | { test: /\.vue/, loader: 'vue' }, 35 | { test: /\.scss/, loader: 'style-loader!css-loader!postcss-loader!sass-loader' }, 36 | { test: /\.css/, loader: 'style-loader!css-loader' }, 37 | { test: /\.doc/, loader: '../demo-loader/loader' } 38 | ] 39 | }, 40 | vue: { 41 | postcss 42 | }, 43 | postcss, 44 | plugins: [ 45 | new webpack.DefinePlugin({ 46 | 'process.env': { 47 | NODE_ENV: '"production"' 48 | } 49 | }) 50 | ] 51 | } 52 | -------------------------------------------------------------------------------- /test/build/karma.conf.js: -------------------------------------------------------------------------------- 1 | 2 | module.exports = function(config) { 3 | config.set({ 4 | 5 | logLevel: config.LOG_DISABLE, 6 | 7 | files: [ 8 | // all files ending in "test" 9 | { pattern: 'component/**/*.js', included: false, served: true }, 10 | '../test/test.js' 11 | // each file acts as entry point for the webpack configuration 12 | ], 13 | 14 | // frameworks to use 15 | frameworks: ['mocha'], 16 | 17 | preprocessors: { 18 | // only specify one entry point 19 | // and require all tests in there 20 | '../test/test.js': ['webpack'], 21 | 'component/**/*.js': [ 22 | 'coverage' 23 | ] 24 | }, 25 | 26 | reporters: ['spec', 'coverage'], 27 | 28 | coverageReporter: { 29 | reporters: [ 30 | { type: 'lcov', dir: '../coverage/', subdir: '.' } 31 | // { type: 'text-summary', dir: 'coverage/', subdir: '.' }, 32 | // { type: 'html', dir: 'coverage/' } 33 | ] 34 | }, 35 | 36 | webpack: require('./webpack.config.test.js'), 37 | 38 | // webpack-dev-middleware configuration 39 | webpackMiddleware: { 40 | noInfo: true 41 | }, 42 | 43 | plugins: [ 44 | require('karma-webpack'), 45 | require('karma-mocha'), 46 | require('karma-coverage'), 47 | require('karma-phantomjs-launcher'), 48 | require('karma-spec-reporter') 49 | ], 50 | 51 | browsers: ['PhantomJS'] 52 | }) 53 | } 54 | -------------------------------------------------------------------------------- /src/component/tabs/tab.vue: -------------------------------------------------------------------------------- 1 | 10 | 11 | 64 | -------------------------------------------------------------------------------- /example/date-time-picker/base.vue: -------------------------------------------------------------------------------- 1 | 35 | 38 | 61 | -------------------------------------------------------------------------------- /src/component/card/card.vue: -------------------------------------------------------------------------------- 1 | 16 | 17 | 59 | -------------------------------------------------------------------------------- /.eslintrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | root: true, 3 | extends: "airbnb-base", 4 | globals: { 5 | "AMap": true, 6 | "window": true, 7 | "document": true 8 | }, 9 | parserOptions: { 10 | // so that 'use strict' is enable 11 | "ecmaFeatures": { 12 | "experimentalObjectRestSpread": true 13 | } 14 | }, 15 | // required to lint *.vue files 16 | plugins: [ 17 | "html" 18 | ], 19 | 20 | // add your custom rules here 21 | "rules": { 22 | // allow debugger during development 23 | "no-debugger": process.env.NODE_ENV === "production" ? 2 : 0, 24 | "import/no-unresolved": [0], 25 | "import/extensions": [0], 26 | "semi": [2, "never"], 27 | "no-plusplus": [0], 28 | "no-template-curly-in-string": ["off"], 29 | "one-var-declaration-per-line": [0], 30 | "space-infix-ops": [0], 31 | "arrow-parens": [0], 32 | "strict:": [0], 33 | "import/no-extraneous-dependencies": [0], 34 | "no-console": [0], 35 | "no-underscore-dangle": [0], 36 | "prefer-const": [0], 37 | "eol-last": ["off"], 38 | "no-self-assign": [0], 39 | "no-param-reassign": [0], 40 | "no-mixed-operators": [0], 41 | "no-shadow": [1], 42 | "arrow-body-style": [0], 43 | "comma-dangle": [0], 44 | "one-var": [0], 45 | "space-before-function-paren": [0], 46 | "prefer-template": [0], 47 | "no-new": [0], 48 | "consistent-return": [0], 49 | "quote-props": [0], 50 | "array-bracket-spacing": [0], 51 | "no-unused-vars": [1], 52 | "computed-property-spacing": [0], 53 | "no-use-before-define": [0] 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /doc/router.js: -------------------------------------------------------------------------------- 1 | import VueRouter from 'vue-router' 2 | import Vue from 'vue' 3 | 4 | Vue.use(VueRouter) 5 | 6 | import Index from './index.vue' 7 | import DropDown from '../example/drop-down.doc' 8 | import Modal from '../example/modal.doc' 9 | import pagination from '../example/pagination.doc' 10 | import Button from '../example/button.doc' 11 | import Card from '../example/card.doc' 12 | import Navbar from '../example/navbar.doc' 13 | import Menu from '../example/menu.doc' 14 | import Tabs from '../example/tabs.doc' 15 | import Alert from '../example/alert.doc' 16 | 17 | import DateTimePicker from '../example/date-time-picker.doc' 18 | 19 | const router = new VueRouter() 20 | 21 | export const routes = { 22 | '/': { 23 | component: Index, 24 | title: '首页' 25 | }, 26 | 'drop-down': { 27 | component: DropDown, 28 | title: '下拉框' 29 | }, 30 | 31 | 'pagination': { 32 | component: pagination, 33 | title: '分页' 34 | }, 35 | 36 | 'button': { 37 | component: Button, 38 | title: '按钮' 39 | }, 40 | 41 | 'card': { 42 | component: Card, 43 | title: '卡片' 44 | }, 45 | 46 | 'navbar': { 47 | component: Navbar, 48 | title: 'Navbar' 49 | }, 50 | 51 | 'menu': { 52 | component: Menu, 53 | title: 'Menu' 54 | }, 55 | 56 | 'tabs': { 57 | component: Tabs, 58 | title: 'Tab' 59 | }, 60 | 61 | 'date-time-picker': { 62 | component: DateTimePicker, 63 | title: '日期时间' 64 | }, 65 | 66 | 'modal': { 67 | component: Modal, 68 | title: '模态框', 69 | }, 70 | 71 | 'alert': { 72 | component: Alert, 73 | title: 'alert' 74 | } 75 | } 76 | 77 | router.map(routes) 78 | 79 | export default router 80 | -------------------------------------------------------------------------------- /test/build/webpack.config.dev.js: -------------------------------------------------------------------------------- 1 | const path = require('path') 2 | const webpack = require('webpack') 3 | const HtmlWebpackPlugin = require('html-webpack-plugin') 4 | const autoprefixer = require('autoprefixer') 5 | 6 | function postcss() { 7 | return [ 8 | autoprefixer({ 9 | browsers: '> 1%' 10 | }) 11 | ] 12 | } 13 | 14 | module.exports = { 15 | devtool: '#cheap-eval-source-map', 16 | entry: [path.join(__dirname, '../doc/doc.js'), 'webpack-hot-middleware/client'], 17 | debug: true, 18 | output: { 19 | path: path.join(__dirname, '../dist'), 20 | filename: 'index.js', 21 | publicPath: '/' 22 | }, 23 | resolve: { 24 | alias: { 25 | component: path.resolve(__dirname, '../component'), 26 | util: path.resolve(__dirname, '../util') 27 | } 28 | }, 29 | module: { 30 | preLoaders: [ 31 | { test: /\.(js|vue)$/, loader: 'eslint-loader', exclude: /node_modules/ } 32 | ], 33 | loaders: [ 34 | { test: /\.js$/, loader: 'babel', exclude: /node_modules/ }, 35 | { test: /\.vue/, loader: 'vue' }, 36 | { test: /\.scss/, loader: 'style-loader!css-loader!postcss-loader!sass-loader' }, 37 | { test: /\.css/, loader: 'style-loader!css-loader' }, 38 | { test: /\.doc/, loader: '../demo-loader/loader' } 39 | ] 40 | }, 41 | vue: { 42 | postcss 43 | }, 44 | postcss, 45 | plugins: [ 46 | new webpack.DefinePlugin({ 47 | 'process.env': { 48 | NODE_ENV: '"development"' 49 | } 50 | }), 51 | new webpack.HotModuleReplacementPlugin(), 52 | new HtmlWebpackPlugin({ 53 | template: path.join(__dirname, '../doc/index.html'), 54 | filename: 'index.html' 55 | }) 56 | ] 57 | } 58 | -------------------------------------------------------------------------------- /src/component/tabs/tabs.vue: -------------------------------------------------------------------------------- 1 | 15 | 16 | 64 | -------------------------------------------------------------------------------- /test/unit/demo_test.js: -------------------------------------------------------------------------------- 1 | import Vue from 'vue' 2 | import chai from 'chai' 3 | import jQuery from 'jquery' 4 | import chaiJquery from 'chai-jquery' 5 | import path from 'path' 6 | import _ from 'lodash' 7 | 8 | chai.use((chai, utils) => { 9 | return chaiJquery(chai, utils, jQuery) 10 | }) 11 | 12 | chai.should() 13 | chai.expect 14 | 15 | const demos = require.context('../../example', true, /\.vue$/) 16 | 17 | function getBaseName(dpath) { 18 | let baseName = path.basename(dpath) 19 | return _.lowerCase(_.camelCase(baseName.substr(0, baseName.lastIndexOf('.')))) 20 | } 21 | 22 | function getLasePath(dpath) { 23 | let fullPath = path.dirname(dpath) 24 | return _.lowerCase(_.camelCase(fullPath.substr(fullPath.lastIndexOf('/'), fullPath.length - 1))) 25 | } 26 | 27 | function getTemplates(paths) { 28 | return paths.reduce((result, p) => { 29 | return result + '<' + _.kebabCase(getLasePath(p) + ' ' + getBaseName((p))) + '/>' 30 | }, '') 31 | } 32 | 33 | function getComponents(paths, context) { 34 | return paths.reduce((result, p) => { 35 | result[_.camelCase(getLasePath(p) + ' ' + getBaseName(p))] = context(p) 36 | return result 37 | }, {}) 38 | } 39 | 40 | const componentsTemplate = getTemplates(demos.keys()) 41 | 42 | let el = $( 43 | `
44 | ${componentsTemplate} 45 |
` 46 | ) 47 | 48 | el.appendTo('body') 49 | 50 | const app = new Vue({ 51 | el: '#root', 52 | components: getComponents(demos.keys(), demos) 53 | }) 54 | 55 | window.app = app 56 | window.Vue = Vue 57 | window.expect = chai.expect 58 | 59 | describe('x-vue', () => { 60 | const testsContext = require.context('./demo_spec', false, /\.js$/) 61 | testsContext.keys().forEach(testsContext) 62 | }) 63 | -------------------------------------------------------------------------------- /src/component/button/button.vue: -------------------------------------------------------------------------------- 1 | 6 | 7 | 72 | -------------------------------------------------------------------------------- /src/style/button.styl: -------------------------------------------------------------------------------- 1 | 2 | // 3 | // Buttons 4 | // -------------------------------------------------- 5 | 6 | // Base styles 7 | // -------------------------------------------------- 8 | 9 | @import "variable"; 10 | 11 | .button { 12 | display: inline-block; 13 | margin-bottom: 0; // For input.btn 14 | font-weight: $font-weight-base; 15 | //line-height: $line-height-base; 16 | text-align: center; 17 | vertical-align: middle; 18 | touch-action: manipulation; 19 | cursor: pointer; 20 | background-image: none; // Reset unusual Firefox-on-Android default style; see https://github.com/necolas/normalize.css/issues/214 21 | border: 1px solid transparent; 22 | white-space: nowrap; 23 | user-select: none; 24 | text-decoration: none; 25 | color: $color-font-default; 26 | background-color: $color-default; 27 | border: 1px solid #eeeeee; 28 | padding: 6px 12px; 29 | 30 | &::-moz-focus-inner { 31 | padding: 0; 32 | border: 0; 33 | } 34 | 35 | &, 36 | &:active, 37 | &.active { 38 | &:focus, 39 | &.focus { 40 | // Default 41 | outline: thin dotted; 42 | // WebKit 43 | outline: 5px auto -webkit-focus-ring-color; 44 | outline-offset: -2px; 45 | } 46 | } 47 | 48 | &:hover, 49 | &:focus, 50 | &.focus { 51 | color: $color-font-default; 52 | text-decoration: none; 53 | } 54 | 55 | &:active, 56 | &.active { 57 | outline: 0; 58 | background-image: none; 59 | box-shadow: inset 0 3px 5px rgba(0,0,0,.125); 60 | } 61 | 62 | &.disabled, 63 | &[disabled], 64 | fieldset[disabled] & { 65 | cursor: default; 66 | opacity: .65; 67 | box-shadow: none; 68 | } 69 | 70 | a& { 71 | &.disabled, 72 | fieldset[disabled] & { 73 | pointer-events: none; // Future-proof disabling of clicks on `` elements 74 | } 75 | } 76 | } -------------------------------------------------------------------------------- /src/component/drop-down/drop-down-menu.vue: -------------------------------------------------------------------------------- 1 | 18 | 19 | 71 | -------------------------------------------------------------------------------- /example/drop-down/base.vue: -------------------------------------------------------------------------------- 1 | 23 | 32 | 57 | -------------------------------------------------------------------------------- /example/drop-down/width.vue: -------------------------------------------------------------------------------- 1 | 23 | 32 | 63 | -------------------------------------------------------------------------------- /src/component/date-picker/month-picker.vue: -------------------------------------------------------------------------------- 1 | 20 | 21 | 95 | -------------------------------------------------------------------------------- /src/component/date-picker/year-picker.vue: -------------------------------------------------------------------------------- 1 | 31 | 32 | 105 | -------------------------------------------------------------------------------- /demo-loader/loader.js: -------------------------------------------------------------------------------- 1 | var path = require('path') 2 | var fs = require('fs') 3 | var marked = require('marked') 4 | var loaderUtils = require('loader-utils') 5 | 6 | marked.setOptions({ 7 | gfm: true, 8 | tables: true, 9 | breaks: false, 10 | pedantic: false, 11 | sanitize: false, 12 | smartLists: true, 13 | smartypants: false, 14 | highlight: function (code, lang) { 15 | return require('highlight.js').highlight(lang, code).value; 16 | } 17 | }) 18 | 19 | module.exports = function (source) { 20 | 21 | if (this.cacheable) this.cacheable() 22 | 23 | var loaderContext = this 24 | var query = loaderUtils.parseQuery(this.query) 25 | var filePath = this.resourcePath 26 | var fileName = path.basename(filePath) 27 | var name = fileName.substr(0, fileName.lastIndexOf('.')) 28 | var docPath = path.join(filePath, '..', name) 29 | var explainPath = path.join(filePath, '..', name, name + '.md') 30 | var docFiles 31 | 32 | if (query.post) { 33 | if (fs.existsSync(filePath + '.cache')) { 34 | // fs.unlinkSync(filePath + '.cache') 35 | } 36 | return source 37 | } 38 | 39 | try { 40 | docFiles = fs.readdirSync(docPath) 41 | } catch(e) { 42 | throw new Error(docPath + ' does not exist') 43 | } 44 | 45 | var html = '', components = '', code, demoFilePath 46 | 47 | // var self = this 48 | this.addContextDependency(docPath) 49 | 50 | docFiles.forEach(function (f, index) { 51 | if (path.extname(f) === '.vue') { 52 | demoFilePath = path.join(docPath, f) 53 | code = fs.readFileSync(demoFilePath, 'utf8') 54 | // self.addDependency(demoFilePath) 55 | html += ( 56 | '\n' + 57 | '
\n' + 58 | '\n' + 59 | '
\n' + 60 | '
\n' + 61 | marked(`\`\`\`html\n ${code} \n\`\`\``) + '\n' + 62 | '
\n' + 63 | '
\n' 64 | ) 65 | components += 'demo' + index + ':' + ' require(\'./' + name + '/' + f + '\'),' 66 | } 67 | }) 68 | 69 | // console.log(explainPath) 70 | if (fs.existsSync(explainPath)) { 71 | // this.addDependency(explainPath) 72 | var explain = fs.readFileSync(explainPath, 'utf8') 73 | html += ( 74 | '
' + 75 | marked(explain) + 76 | '
' 77 | ) 78 | } 79 | 80 | var scripts = "" 85 | 86 | var templates = "" 89 | 90 | var finalPath = path.join(filePath, '..', '.' + name + '.doc.cache') 91 | 92 | fs.writeFileSync(finalPath, templates + '\n\n' + scripts, 'utf8') 93 | 94 | function getRequest() { 95 | return loaderUtils.stringifyRequest(loaderContext, 96 | '!!vue!' + finalPath) 97 | } 98 | 99 | return 'module.exports = require(' + getRequest() + ')' 100 | 101 | } 102 | -------------------------------------------------------------------------------- /src/index.js: -------------------------------------------------------------------------------- 1 | // button 2 | import XButton from './component/button/button.vue' 3 | 4 | // modal 5 | import XModal from './component/modal/modal.vue' 6 | 7 | // card 8 | import XCard from './component/card/card.vue' 9 | import XCardTitle from './component/card/card-title.vue' 10 | import XCardText from './component/card/card-text.vue' 11 | import XCardLink from './component/card/card-link.vue' 12 | import XCardBlock from './component/card/card-block.vue' 13 | import XCardImage from './component/card/card-image.vue' 14 | 15 | // drop down 16 | import XDropDown from './component/drop-down/drop-down.vue' 17 | import XDropDownMenu from './component/drop-down/drop-down-menu.vue' 18 | import XDropDownMenuDivider from './component/drop-down/drop-down-menu-divider.vue' 19 | import XDropDownItem from './component/drop-down/drop-down-menu-item.vue' 20 | 21 | // tabs 22 | import XTabs from './component/tabs/tabs.vue' 23 | import XTab from './component/tabs/tab.vue' 24 | import XRouterTabs from './component/tabs/router-tabs.vue' 25 | 26 | // date picker 27 | // import XDatePicker from './component/date-picker/date-picker.vue' 28 | // import XDateTimePicker from './component/date-picker/date-time-picker.vue' 29 | 30 | // nav 31 | import XNavBar from './component/navbar/index.vue' 32 | import XNavBarBrand from './component/navbar/navbar-brand.vue' 33 | import XNavItem from './component/navbar/nav-item.vue' 34 | import XNavBarNav from './component/navbar/navbar-nav.vue' 35 | import XNavGroup from './component/navbar/nav-group.vue' 36 | 37 | // pagination 38 | import XPagination from './component/pagination/index.vue' 39 | 40 | // alert 41 | import XAlert from './component/alert/alert.vue' 42 | import XAlertLink from './component/alert/alert-link.vue' 43 | import XAlertHeader from './component/alert/alert-heading.vue' 44 | 45 | // directives 46 | // import DateTimePicker from './component/date-picker/date-time-picker' 47 | 48 | // ueditor 49 | // import Ueditor from './component/ueditor/ueditor.vue' 50 | 51 | export const components = { 52 | XButton, 53 | 54 | XModal, 55 | 56 | XCard, 57 | XCardTitle, 58 | XCardText, 59 | XCardLink, 60 | XCardBlock, 61 | XCardImage, 62 | 63 | XDropDown, 64 | XDropDownMenu, 65 | XDropDownMenuDivider, 66 | XDropDownItem, 67 | 68 | XTabs, 69 | XTab, 70 | XRouterTabs, 71 | 72 | // XDatePicker, 73 | // XDateTimePicker, 74 | 75 | XNavBar, 76 | XNavBarBrand, 77 | XNavItem, 78 | XNavBarNav, 79 | XNavGroup, 80 | 81 | XPagination, 82 | 83 | XAlert, 84 | XAlertLink, 85 | XAlertHeader 86 | 87 | // Ueditor, 88 | } 89 | 90 | // export const directives = { 91 | // DateTimePicker 92 | // } 93 | 94 | export default { 95 | 96 | install(Vue) { 97 | Vue.X_VUE_VERSION = process.env.X_VUE_VERSION 98 | Object.keys(components).forEach((name) => { 99 | Vue.component(name, components[name]) 100 | }) 101 | // Object.keys(directives).forEach((name) => { 102 | // Vue.directive(name, directives[name]) 103 | // }) 104 | } 105 | 106 | } 107 | 108 | require('./style/index.styl') 109 | // import '../style/index.styl' 110 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "x-vue", 3 | "version": "0.1.10", 4 | "description": "vue components based on bootstrap", 5 | "main": "source/index.js", 6 | "scripts": { 7 | "dev": "./build/dev.sh", 8 | "start": "node server.js", 9 | "test": "gulp test", 10 | "deploy-doc": "./build/deploy_doc.sh" 11 | }, 12 | "keywords": [ 13 | "vue", 14 | "x", 15 | "bootstrap" 16 | ], 17 | "files": [ 18 | "dist", 19 | "source", 20 | "CHANGELOG.md", 21 | "Readme.md" 22 | ], 23 | "author": "Jokcy ", 24 | "license": "ISC", 25 | "devDependencies": { 26 | "autoprefixer": "^6.3.5", 27 | "babel-eslint": "^6.0.2", 28 | "babel-loader": "^6.2.1", 29 | "babel-plugin-transform-runtime": "^6.4.3", 30 | "babel-preset-es2015": "^6.3.13", 31 | "babel-preset-es2015-rollup": "^1.1.1", 32 | "babel-preset-stage-0": "^6.3.13", 33 | "bootstrap": "^4.0.0-alpha.2", 34 | "bundle-loader": "^0.5.4", 35 | "chai": "^3.5.0", 36 | "chai-jquery": "^2.0.0", 37 | "coveralls": "^2.11.6", 38 | "css-loader": "^0.23.1", 39 | "eslint": "^2.5.3", 40 | "eslint-config-airbnb-base": "^10.0.1", 41 | "eslint-loader": "^1.3.0", 42 | "eslint-plugin-html": "^1.4.0", 43 | "express": "^4.13.4", 44 | "extract-text-webpack-plugin": "^1.0.1", 45 | "file-loader": "^0.8.5", 46 | "gulp": "^3.9.0", 47 | "gulp-autoprefixer": "^3.1.0", 48 | "gulp-babel": "^6.1.2", 49 | "gulp-bump": "^2.1.0", 50 | "gulp-cssmin": "^0.1.7", 51 | "gulp-header": "^1.8.7", 52 | "gulp-load-plugins": "^1.2.4", 53 | "gulp-rename": "^1.2.2", 54 | "gulp-rollup": "^1.8.0", 55 | "gulp-stylus": "^2.5.0", 56 | "gulp-webpack": "^1.5.0", 57 | "highlight.js": "^9.3.0", 58 | "html-webpack-plugin": "^2.22.0", 59 | "istanbul-instrumenter-loader": "^0.2.0", 60 | "jquery": "^2.2.2", 61 | "karma": "^0.13.19", 62 | "karma-coverage": "^0.5.3", 63 | "karma-mocha": "^0.2.1", 64 | "karma-phantomjs-launcher": "^1.0.0", 65 | "karma-spec-reporter": "0.0.23", 66 | "karma-webpack": "^1.7.0", 67 | "lodash": "^4.11.1", 68 | "marked": "^0.3.5", 69 | "mocha": "^2.4.5", 70 | "mocha-lcov-reporter": "^1.0.0", 71 | "mocha-loader": "^0.7.1", 72 | "phantomjs-polyfill": "0.0.1", 73 | "phantomjs-prebuilt": "^2.1.3", 74 | "postcss-loader": "^0.8.2", 75 | "rimraf": "^2.5.2", 76 | "rollup-plugin-vue": "^2.0.0", 77 | "run-sequence": "^1.1.5", 78 | "scriptjs": "^2.5.8", 79 | "scss-loader": "0.0.1", 80 | "style-loader": "^0.13.1", 81 | "stylus": "^0.54.5", 82 | "stylus-loader": "^2.1.1", 83 | "url-loader": "^0.5.7", 84 | "vue": "^1.0.16", 85 | "vue-demo-loader": "^0.1.0", 86 | "vue-hot-reload-api": "^1.3.2", 87 | "vue-html-loader": "^1.1.0", 88 | "vue-loader": "^8.2.3", 89 | "vue-router": "^0.7.13", 90 | "vue-style-loader": "^1.0.0", 91 | "webpack": "^1.13.1", 92 | "webpack-dev-middleware": "^1.5.1", 93 | "webpack-dev-server": "^1.14.1", 94 | "webpack-hot-middleware": "^2.6.4" 95 | }, 96 | "dependencies": { 97 | "classnames": "^2.2.5", 98 | "font-awesome": "^4.6.3" 99 | } 100 | } 101 | -------------------------------------------------------------------------------- /src/component/modal/modal.vue: -------------------------------------------------------------------------------- 1 | 33 | 98 | 99 | 140 | -------------------------------------------------------------------------------- /gulpfile.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const gulp = require('gulp') 4 | const Server = require('karma').Server 5 | const spawn = require('child_process').spawn 6 | const rimraf = require('rimraf') 7 | const cssmin = require('gulp-cssmin') 8 | const rename = require('gulp-rename') 9 | const stylus = require('gulp-stylus') 10 | const autoprefixer = require('gulp-autoprefixer') 11 | const bump = require('gulp-bump') 12 | const header = require('gulp-header') 13 | const runSequence = require('run-sequence') 14 | const babel = require('gulp-babel') 15 | 16 | const webpack = require('gulp-webpack') 17 | 18 | gulp.task('default', ['test']) 19 | 20 | /* TEST SETUP */ 21 | gulp.task('test-watch', ['test'], () => { 22 | gulp.watch(['component/**/*.js', 'test/**/*.js'], ['test']) 23 | }) 24 | 25 | gulp.task('test', (done) => { 26 | const server = new Server({ 27 | configFile: __dirname + '/build/karma.conf.js', 28 | singleRun: true 29 | }, () => { 30 | done() 31 | }) 32 | 33 | server.start() 34 | }) 35 | 36 | 37 | gulp.task('copy-stylus', () => { 38 | return gulp.src('src/**/*.stylus') 39 | .pipe(gulp.dest('source')) 40 | }) 41 | 42 | // copy vue files 43 | gulp.task('copy-vue', () => { 44 | return gulp.src('src/**/*.vue') 45 | .pipe(gulp.dest('source')) 46 | }) 47 | 48 | gulp.task('compile-js', () => { 49 | return gulp.src('src/**/*.js') 50 | .pipe(babel()) 51 | .pipe(gulp.dest('source')) 52 | }) 53 | 54 | // babel parse 55 | gulp.task('source', (done) => { 56 | rimraf('./source', () => { 57 | runSequence('copy-vue', 'copy-stylus', 'compile-js', () => { 58 | done() 59 | }) 60 | }) 61 | }) 62 | 63 | // publish to npm 64 | gulp.task('npm-publish', (done) => { 65 | spawn('npm', ['publish'], { stdio: 'inherit' }).on('close', done) 66 | }) 67 | 68 | // package 69 | gulp.task('package', () => { 70 | return gulp.src('component/index.js') 71 | .pipe(webpack(require('./build/webpack.build'))) 72 | .pipe(gulp.dest('dist')) 73 | }) 74 | 75 | // package min 76 | gulp.task('package-min', () => { 77 | return gulp.src('component/index.js') 78 | .pipe(webpack(require('./build/webpack.build.min'))) 79 | .pipe(gulp.dest('dist')) 80 | }) 81 | 82 | // package the css 83 | gulp.task('package-css', () => { 84 | return gulp.src('style/index.styl') 85 | .pipe(stylus()) 86 | .pipe(autoprefixer({ browsers: '> 1%' })) 87 | .pipe(rename('x-vue.css')) 88 | .pipe(gulp.dest('dist')) 89 | .pipe(cssmin()) 90 | .pipe(rename({ suffix: '.min' })) 91 | .pipe(gulp.dest('dist')) 92 | }) 93 | 94 | // clear dist 95 | gulp.task('clear-dist', (done) => { 96 | rimraf('./dist', done) 97 | }) 98 | 99 | // bump 100 | gulp.task('bump', () => { 101 | return gulp.src('package.json') 102 | .pipe(bump({ type: 'patch' })) 103 | .pipe(gulp.dest('./')) 104 | }) 105 | 106 | gulp.task('add-header', () => { 107 | let pkg = require('./package.json') 108 | let banner = ['/**', 109 | ' * <%= pkg.name %> - <%= pkg.description %>', 110 | ' * @version v<%= pkg.version %>', 111 | ' * @link <%= pkg.homepage %>', 112 | ' * @license <%= pkg.license %>', 113 | ' */', 114 | ''].join('\n') 115 | 116 | return gulp.src(['dist/*.js', 'dist/*.css']) 117 | .pipe(header(banner, { pkg })) 118 | .pipe(gulp.dest('dist/')) 119 | }) 120 | 121 | gulp.task('pre-publish', (done) => { 122 | runSequence('clear-dist', 'source', 'bump', 'package', 'package-min', 'add-header', () => { 123 | done() 124 | }) 125 | }) 126 | 127 | gulp.task('publish', (done) => { 128 | runSequence('npm-publish', () => { 129 | done() 130 | }) 131 | }) 132 | 133 | 134 | // make doc 135 | gulp.task('build-doc', () => { 136 | return gulp.src('doc/doc.js') 137 | .pipe(webpack(require('./build/webpack.build.doc'))) 138 | .pipe(gulp.dest('doc-built')) 139 | }) 140 | -------------------------------------------------------------------------------- /src/component/modal/notification.vue: -------------------------------------------------------------------------------- 1 | 15 | 16 | 63 | 64 | -------------------------------------------------------------------------------- /src/component/pagination/index.vue: -------------------------------------------------------------------------------- 1 | 41 | 42 | 50 | 51 | 162 | -------------------------------------------------------------------------------- /src/component/date-picker/date-picker.vue: -------------------------------------------------------------------------------- 1 | 34 | 35 | 171 | -------------------------------------------------------------------------------- /src/component/drop-down/drop-down.vue: -------------------------------------------------------------------------------- 1 | 22 | 23 | 32 | 33 | 166 | -------------------------------------------------------------------------------- /demo-loader/.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "root": true, 3 | "env": { 4 | "browser": true, 5 | "node": true 6 | }, 7 | 8 | "rules": { 9 | "accessor-pairs": 2, 10 | "array-bracket-spacing": 0, 11 | "block-scoped-var": 0, 12 | "brace-style": [2, "1tbs", { "allowSingleLine": true }], 13 | "camelcase": 0, 14 | "comma-dangle": [2, "never"], 15 | "comma-spacing": [2, { "before": false, "after": true }], 16 | "comma-style": [2, "last"], 17 | "complexity": 0, 18 | "computed-property-spacing": 0, 19 | "consistent-return": 0, 20 | "consistent-this": 0, 21 | "constructor-super": 2, 22 | "curly": [2, "multi-line"], 23 | "default-case": 0, 24 | "dot-location": [2, "property"], 25 | "dot-notation": 0, 26 | "eol-last": 2, 27 | "eqeqeq": [2, "allow-null"], 28 | "func-names": 0, 29 | "func-style": 0, 30 | "generator-star-spacing": [2, { "before": true, "after": true }], 31 | "guard-for-in": 0, 32 | "handle-callback-err": [2, "^(err|error)$" ], 33 | "indent": [2, 2, { "SwitchCase": 1 }], 34 | "key-spacing": [2, { "beforeColon": false, "afterColon": true }], 35 | "linebreak-style": 0, 36 | "lines-around-comment": 0, 37 | "max-nested-callbacks": 0, 38 | "new-cap": [2, { "newIsCap": true, "capIsNew": false }], 39 | "new-parens": 2, 40 | "newline-after-var": 0, 41 | "no-alert": 0, 42 | "no-array-constructor": 2, 43 | "no-caller": 2, 44 | "no-catch-shadow": 0, 45 | "no-cond-assign": 2, 46 | "no-console": 0, 47 | "no-constant-condition": 0, 48 | "no-continue": 0, 49 | "no-control-regex": 2, 50 | "no-debugger": 2, 51 | "no-delete-var": 2, 52 | "no-div-regex": 0, 53 | "no-dupe-args": 2, 54 | "no-dupe-keys": 2, 55 | "no-duplicate-case": 2, 56 | "no-else-return": 0, 57 | "no-empty": 0, 58 | "no-empty-character-class": 2, 59 | "no-empty-label": 2, 60 | "no-eq-null": 0, 61 | "no-eval": 2, 62 | "no-ex-assign": 2, 63 | "no-extend-native": 2, 64 | "no-extra-bind": 2, 65 | "no-extra-boolean-cast": 2, 66 | "no-extra-parens": 0, 67 | "no-extra-semi": 0, 68 | "no-fallthrough": 2, 69 | "no-floating-decimal": 2, 70 | "no-func-assign": 2, 71 | "no-implied-eval": 2, 72 | "no-inline-comments": 0, 73 | "no-inner-declarations": [2, "functions"], 74 | "no-invalid-regexp": 2, 75 | "no-irregular-whitespace": 2, 76 | "no-iterator": 2, 77 | "no-label-var": 2, 78 | "no-labels": 2, 79 | "no-lone-blocks": 2, 80 | "no-lonely-if": 0, 81 | "no-loop-func": 0, 82 | "no-mixed-requires": 0, 83 | "no-mixed-spaces-and-tabs": 2, 84 | "no-multi-spaces": 2, 85 | "no-multi-str": 2, 86 | "no-multiple-empty-lines": [2, { "max": 1 }], 87 | "no-native-reassign": 2, 88 | "no-negated-in-lhs": 2, 89 | "no-nested-ternary": 0, 90 | "no-new": 2, 91 | "no-new-func": 0, 92 | "no-new-object": 2, 93 | "no-new-require": 2, 94 | "no-new-wrappers": 2, 95 | "no-obj-calls": 2, 96 | "no-octal": 2, 97 | "no-octal-escape": 2, 98 | "no-param-reassign": 0, 99 | "no-path-concat": 0, 100 | "no-process-env": 0, 101 | "no-process-exit": 0, 102 | "no-proto": 0, 103 | "no-redeclare": 2, 104 | "no-regex-spaces": 2, 105 | "no-restricted-modules": 0, 106 | "no-return-assign": 2, 107 | "no-script-url": 0, 108 | "no-self-compare": 2, 109 | "no-sequences": 2, 110 | "no-shadow": 0, 111 | "no-shadow-restricted-names": 2, 112 | "no-spaced-func": 2, 113 | "no-sparse-arrays": 2, 114 | "no-sync": 0, 115 | "no-ternary": 0, 116 | "no-this-before-super": 2, 117 | "no-throw-literal": 2, 118 | "no-trailing-spaces": 2, 119 | "no-undef": 2, 120 | "no-undef-init": 2, 121 | "no-undefined": 0, 122 | "no-underscore-dangle": 0, 123 | "no-unexpected-multiline": 2, 124 | "no-unneeded-ternary": 2, 125 | "no-unreachable": 2, 126 | "no-unused-expressions": 0, 127 | "no-unused-vars": [2, { "vars": "all", "args": "none" }], 128 | "no-use-before-define": 0, 129 | "no-var": 0, 130 | "no-void": 0, 131 | "no-warning-comments": 0, 132 | "no-with": 2, 133 | "object-curly-spacing": 0, 134 | "object-shorthand": 0, 135 | "one-var": [2, { "initialized": "never" }], 136 | "operator-assignment": 0, 137 | "operator-linebreak": [2, "after", { "overrides": { "?": "before", ":": "before" } }], 138 | "padded-blocks": 0, 139 | "prefer-const": 0, 140 | "quote-props": 0, 141 | "quotes": [2, "single", "avoid-escape"], 142 | "radix": 2, 143 | "semi": [2, "never"], 144 | "semi-spacing": 0, 145 | "sort-vars": 0, 146 | "space-after-keywords": [2, "always"], 147 | "space-before-blocks": [2, "always"], 148 | "space-before-function-paren": [2, "always"], 149 | "space-in-parens": [2, "never"], 150 | "space-infix-ops": 2, 151 | "space-return-throw-case": 2, 152 | "space-unary-ops": [2, { "words": true, "nonwords": false }], 153 | "spaced-comment": [2, "always", { "markers": ["global", "globals", "eslint", "eslint-disable", "*package", "!"] }], 154 | "strict": 0, 155 | "use-isnan": 2, 156 | "valid-jsdoc": 0, 157 | "valid-typeof": 2, 158 | "vars-on-top": 0, 159 | "wrap-iife": [2, "any"], 160 | "wrap-regex": 0, 161 | "yoda": [2, "never"] 162 | } 163 | } 164 | -------------------------------------------------------------------------------- /src/component/date-picker/util.js: -------------------------------------------------------------------------------- 1 | const MONTH_DAYS = [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31] 2 | 3 | export function isLeapYear(year) { 4 | return typeof year === 'number' && (year % 100 === 0 ? year % 400 === 0 : year % 4 === 0) 5 | } 6 | 7 | let testFullDate 8 | let testFullSec = 0 9 | const isLessThen = function (year, month, date, minDate) { 10 | if (minDate === -1) return false 11 | // if (date === 30) debugger 12 | // testFullDate.setFullYear(year) 13 | // testFullDate.setMonth(month) 14 | // testFullDate.setDate(date) 15 | // testFullDate.setHours(23) 16 | // testFullDate.setMinutes(59) 17 | // testFullDate.setSeconds(59) 18 | testFullDate = new Date(year, month, date, 23, 59, 59) 19 | 20 | testFullSec = testFullDate.getTime() 21 | 22 | return testFullSec <= minDate 23 | } 24 | 25 | const isLargerThen = function (year, month, date, maxDate) { 26 | if (maxDate === -1) return false 27 | // if (date === 30) debugger 28 | // testFullDate.setFullYear(year) 29 | // testFullDate.setMonth(month) 30 | // testFullDate.setDate(date) 31 | // testFullDate.setHours(0) 32 | // testFullDate.setMinutes(0) 33 | // testFullDate.setSeconds(0) 34 | testFullDate = new Date(year, month, date, 0, 0, 0) 35 | 36 | testFullSec = testFullDate.getTime() 37 | 38 | return testFullSec >= maxDate 39 | } 40 | 41 | // 获取正常的每个月的日期的数组 42 | export function getNormalDateArray(year, month, today, { maxDate, minDate, valueDate }) { 43 | let todayYear = today.getFullYear() 44 | let todayMonth = today.getMonth() 45 | let todayDate = today.getDate() 46 | let isThisYearMonth = todayYear === year && todayMonth === month 47 | 48 | let length = MONTH_DAYS[month] 49 | 50 | if (length === 28) { 51 | length = isLeapYear(year) ? 29 : 28 52 | } 53 | 54 | let arr = [] 55 | for (let i=0; itable{ 137 | width : 100%; 138 | text-align: center; 139 | 140 | td{ 141 | height: 52px; 142 | } 143 | 144 | .month-item{ 145 | display: inline-block; 146 | border-radius: 2px; 147 | padding : 1px 8px; 148 | cursor: pointer; 149 | transition: all .3s; 150 | color: #000000; 151 | font-size: 12px; 152 | &:hover{ 153 | background-color: #eee; 154 | text-decoration: none; 155 | } 156 | 157 | &.selected{ 158 | background-color: #009ce5; 159 | color: #ffffff; 160 | &.this-month{ 161 | color: #ffffff; 162 | border: 1px solid #009ce5; 163 | } 164 | } 165 | 166 | &.disabled{ 167 | color: #aeaeae; 168 | &:hover{ 169 | cursor: not-allowed; 170 | background-color: transparent; 171 | } 172 | } 173 | 174 | &.this-month{ 175 | color: #009ce5; 176 | border: 1px solid #009ce5; 177 | } 178 | 179 | } 180 | } 181 | } 182 | 183 | 184 | // year picker 185 | .x-year-picker{ 186 | ul { 187 | margin : 0; 188 | padding : 0; 189 | } 190 | table{ 191 | width : 100%; 192 | text-align: center; 193 | } 194 | } 195 | .year-item{ 196 | display: inline-block; 197 | float : left; 198 | width : 68px; 199 | height : 52px; 200 | text-align: center; 201 | } 202 | .control-item{ 203 | display: block; 204 | float : left; 205 | width : 68px; 206 | height: 52px; 207 | text-align: center; 208 | 209 | .year-picker-item{ 210 | color : #999999; 211 | margin-top : 12px; 212 | &:hover{ 213 | color : #009ce5; 214 | background-color: transparent; 215 | } 216 | } 217 | } 218 | .year-picker-item{ 219 | display: inline-block; 220 | padding : 2px 8px; 221 | margin-top : 15px; 222 | font-size: 12px; 223 | color : #000000; 224 | border-radius: 2px; 225 | &.this-year{ 226 | color: #009ce5; 227 | border: 1px solid #009ce5; 228 | } 229 | &.selected{ 230 | text-decoration: none; 231 | color : #ffffff; 232 | background-color: #009ce5; 233 | } 234 | &:hover{ 235 | text-decoration: none; 236 | color : #ffffff; 237 | background-color: #009ce5; 238 | } 239 | &.disabled{ 240 | color: #aeaeae; 241 | &:hover{ 242 | cursor: not-allowed; 243 | background-color: transparent; 244 | } 245 | } 246 | } 247 | 248 | .x-time-picker { 249 | //padding: 10px; 250 | } 251 | 252 | .done { 253 | width: 93%; 254 | padding: .1rem .75rem; 255 | margin: 0 auto; 256 | background-color: #009ce5; 257 | border-radius: 3px; 258 | } 259 | } 260 | -------------------------------------------------------------------------------- /src/component/date-picker/time-picker.vue: -------------------------------------------------------------------------------- 1 | 41 | 42 | 178 | 179 | 224 | -------------------------------------------------------------------------------- /src/style/normalize.css: -------------------------------------------------------------------------------- 1 | /*! normalize.css v4.0.0 | MIT License | github.com/necolas/normalize.css */ 2 | 3 | /** 4 | * 1. Change the default font family in all browsers (opinionated). 5 | * 2. Prevent adjustments of font size after orientation changes in IE and iOS. 6 | */ 7 | 8 | html { 9 | font-family: sans-serif; /* 1 */ 10 | -ms-text-size-adjust: 100%; /* 2 */ 11 | -webkit-text-size-adjust: 100%; /* 2 */ 12 | } 13 | 14 | /** 15 | * Remove the margin in all browsers (opinionated). 16 | */ 17 | 18 | body { 19 | margin: 0; 20 | } 21 | 22 | /* HTML5 display definitions 23 | ========================================================================== */ 24 | 25 | /** 26 | * Add the correct display in IE 9-. 27 | * 1. Add the correct display in Edge, IE, and Firefox. 28 | * 2. Add the correct display in IE. 29 | */ 30 | 31 | article, 32 | aside, 33 | details, /* 1 */ 34 | figcaption, 35 | figure, 36 | footer, 37 | header, 38 | main, /* 2 */ 39 | menu, 40 | nav, 41 | section, 42 | summary { /* 1 */ 43 | display: block; 44 | } 45 | 46 | /** 47 | * Add the correct display in IE 9-. 48 | */ 49 | 50 | audio, 51 | canvas, 52 | progress, 53 | video { 54 | display: inline-block; 55 | } 56 | 57 | /** 58 | * Add the correct display in iOS 4-7. 59 | */ 60 | 61 | audio:not([controls]) { 62 | display: none; 63 | height: 0; 64 | } 65 | 66 | /** 67 | * Add the correct vertical alignment in Chrome, Firefox, and Opera. 68 | */ 69 | 70 | progress { 71 | vertical-align: baseline; 72 | } 73 | 74 | /** 75 | * Add the correct display in IE 10-. 76 | * 1. Add the correct display in IE. 77 | */ 78 | 79 | template, /* 1 */ 80 | [hidden] { 81 | display: none; 82 | } 83 | 84 | /* Links 85 | ========================================================================== */ 86 | 87 | /** 88 | * Remove the gray background on active links in IE 10. 89 | */ 90 | 91 | a { 92 | background-color: transparent; 93 | } 94 | 95 | /** 96 | * Remove the outline on focused links when they are also active or hovered 97 | * in all browsers (opinionated). 98 | */ 99 | 100 | a:active, 101 | a:hover { 102 | outline-width: 0; 103 | } 104 | 105 | /* Text-level semantics 106 | ========================================================================== */ 107 | 108 | /** 109 | * 1. Remove the bottom border in Firefox 39-. 110 | * 2. Add the correct text decoration in Chrome, Edge, IE, Opera, and Safari. 111 | */ 112 | 113 | abbr[title] { 114 | border-bottom: none; /* 1 */ 115 | text-decoration: underline; /* 2 */ 116 | text-decoration: underline dotted; /* 2 */ 117 | } 118 | 119 | /** 120 | * Prevent the duplicate application of `bolder` by the next rule in Safari 6. 121 | */ 122 | 123 | b, 124 | strong { 125 | font-weight: inherit; 126 | } 127 | 128 | /** 129 | * Add the correct font weight in Chrome, Edge, and Safari. 130 | */ 131 | 132 | b, 133 | strong { 134 | font-weight: bolder; 135 | } 136 | 137 | /** 138 | * Add the correct font style in Android 4.3-. 139 | */ 140 | 141 | dfn { 142 | font-style: italic; 143 | } 144 | 145 | /** 146 | * Correct the font size and margin on `h1` elements within `section` and 147 | * `article` contexts in Chrome, Firefox, and Safari. 148 | */ 149 | 150 | h1 { 151 | font-size: 2em; 152 | margin: 0.67em 0; 153 | } 154 | 155 | /** 156 | * Add the correct background and color in IE 9-. 157 | */ 158 | 159 | mark { 160 | background-color: #ff0; 161 | color: #000; 162 | } 163 | 164 | /** 165 | * Add the correct font size in all browsers. 166 | */ 167 | 168 | small { 169 | font-size: 80%; 170 | } 171 | 172 | /** 173 | * Prevent `sub` and `sup` elements from affecting the line height in 174 | * all browsers. 175 | */ 176 | 177 | sub, 178 | sup { 179 | font-size: 75%; 180 | line-height: 0; 181 | position: relative; 182 | vertical-align: baseline; 183 | } 184 | 185 | sub { 186 | bottom: -0.25em; 187 | } 188 | 189 | sup { 190 | top: -0.5em; 191 | } 192 | 193 | /* Embedded content 194 | ========================================================================== */ 195 | 196 | /** 197 | * Remove the border on images inside links in IE 10-. 198 | */ 199 | 200 | img { 201 | border-style: none; 202 | } 203 | 204 | /** 205 | * Hide the overflow in IE. 206 | */ 207 | 208 | svg:not(:root) { 209 | overflow: hidden; 210 | } 211 | 212 | /* Grouping content 213 | ========================================================================== */ 214 | 215 | /** 216 | * 1. Correct the inheritance and scaling of font size in all browsers. 217 | * 2. Correct the odd `em` font sizing in all browsers. 218 | */ 219 | 220 | code, 221 | kbd, 222 | pre, 223 | samp { 224 | font-family: monospace, monospace; /* 1 */ 225 | font-size: 1em; /* 2 */ 226 | } 227 | 228 | /** 229 | * Add the correct margin in IE 8. 230 | */ 231 | 232 | figure { 233 | margin: 1em 40px; 234 | } 235 | 236 | /** 237 | * 1. Add the correct box sizing in Firefox. 238 | * 2. Show the overflow in Edge and IE. 239 | */ 240 | 241 | hr { 242 | box-sizing: content-box; /* 1 */ 243 | height: 0; /* 1 */ 244 | overflow: visible; /* 2 */ 245 | } 246 | 247 | /* Forms 248 | ========================================================================== */ 249 | 250 | /** 251 | * Change font properties to `inherit` in all browsers (opinionated). 252 | */ 253 | 254 | button, 255 | input, 256 | select, 257 | textarea { 258 | font: inherit; 259 | } 260 | 261 | /** 262 | * Restore the font weight unset by the previous rule. 263 | */ 264 | 265 | optgroup { 266 | font-weight: bold; 267 | } 268 | 269 | /** 270 | * Show the overflow in IE. 271 | * 1. Show the overflow in Edge. 272 | * 2. Show the overflow in Edge, Firefox, and IE. 273 | */ 274 | 275 | button, 276 | input, /* 1 */ 277 | select { /* 2 */ 278 | overflow: visible; 279 | } 280 | 281 | /** 282 | * Remove the margin in Safari. 283 | * 1. Remove the margin in Firefox and Safari. 284 | */ 285 | 286 | button, 287 | input, 288 | select, 289 | textarea { /* 1 */ 290 | margin: 0; 291 | } 292 | 293 | /** 294 | * Remove the inheritence of text transform in Edge, Firefox, and IE. 295 | * 1. Remove the inheritence of text transform in Firefox. 296 | */ 297 | 298 | button, 299 | select { /* 1 */ 300 | text-transform: none; 301 | } 302 | 303 | /** 304 | * Change the cursor in all browsers (opinionated). 305 | */ 306 | 307 | button, 308 | [type="button"], 309 | [type="reset"], 310 | [type="submit"] { 311 | cursor: pointer; 312 | } 313 | 314 | /** 315 | * Restore the default cursor to disabled elements unset by the previous rule. 316 | */ 317 | 318 | [disabled] { 319 | cursor: default; 320 | } 321 | 322 | /** 323 | * 1. Prevent a WebKit bug where (2) destroys native `audio` and `video` 324 | * controls in Android 4. 325 | * 2. Correct the inability to style clickable types in iOS. 326 | */ 327 | 328 | button, 329 | html [type="button"], /* 1 */ 330 | [type="reset"], 331 | [type="submit"] { 332 | -webkit-appearance: button; /* 2 */ 333 | } 334 | 335 | /** 336 | * Remove the inner border and padding in Firefox. 337 | */ 338 | 339 | button::-moz-focus-inner, 340 | input::-moz-focus-inner { 341 | border: 0; 342 | padding: 0; 343 | } 344 | 345 | /** 346 | * Restore the focus styles unset by the previous rule. 347 | */ 348 | 349 | button:-moz-focusring, 350 | input:-moz-focusring { 351 | outline: 1px dotted ButtonText; 352 | } 353 | 354 | /** 355 | * Change the border, margin, and padding in all browsers (opinionated). 356 | */ 357 | 358 | fieldset { 359 | border: 1px solid #c0c0c0; 360 | margin: 0 2px; 361 | padding: 0.35em 0.625em 0.75em; 362 | } 363 | 364 | /** 365 | * 1. Correct the text wrapping in Edge and IE. 366 | * 2. Correct the color inheritance from `fieldset` elements in IE. 367 | * 3. Remove the padding so developers are not caught out when they zero out 368 | * `fieldset` elements in all browsers. 369 | */ 370 | 371 | legend { 372 | box-sizing: border-box; /* 1 */ 373 | color: inherit; /* 2 */ 374 | display: table; /* 1 */ 375 | max-width: 100%; /* 1 */ 376 | padding: 0; /* 3 */ 377 | white-space: normal; /* 1 */ 378 | } 379 | 380 | /** 381 | * Remove the default vertical scrollbar in IE. 382 | */ 383 | 384 | textarea { 385 | overflow: auto; 386 | } 387 | 388 | /** 389 | * 1. Add the correct box sizing in IE 10-. 390 | * 2. Remove the padding in IE 10-. 391 | */ 392 | 393 | [type="checkbox"], 394 | [type="radio"] { 395 | box-sizing: border-box; /* 1 */ 396 | padding: 0; /* 2 */ 397 | } 398 | 399 | /** 400 | * Correct the cursor style of increment and decrement buttons in Chrome. 401 | */ 402 | 403 | [type="number"]::-webkit-inner-spin-button, 404 | [type="number"]::-webkit-outer-spin-button { 405 | height: auto; 406 | } 407 | 408 | /** 409 | * Correct the odd appearance of search inputs in Chrome and Safari. 410 | */ 411 | 412 | [type="search"] { 413 | -webkit-appearance: textfield; 414 | } 415 | 416 | /** 417 | * Remove the inner padding and cancel buttons in Chrome on OS X and 418 | * Safari on OS X. 419 | */ 420 | 421 | [type="search"]::-webkit-search-cancel-button, 422 | [type="search"]::-webkit-search-decoration { 423 | -webkit-appearance: none; 424 | } 425 | -------------------------------------------------------------------------------- /src/component/date-picker/date-time-picker.js: -------------------------------------------------------------------------------- 1 | /** 2 | * // 核心功能 3 | * format: YYYY-MM-DD HH:mm:ss 4 | * 如果有DD, 则整个Date picker都显示 5 | * 如果没有根据最近的一个现实picker 6 | * 时间类似 7 | * 8 | * v-date-time-picker:range.start="name" 9 | * argument: range // 这是一个range时间选择 10 | * modifiers: {start: true, end: false} // 代表这是一个range的开始选择 11 | * expression: name // 代表这个range的名字,每个名字的range只能有一个start和end,不然报错 12 | * value: name代表的值 13 | * 14 | * closeOnSelected: Boolean default true 15 | * 是否在选择完了之后关闭选择器 16 | * 17 | * highlightToday: Boolean default true 18 | * 当前日期是否高亮 19 | * 20 | * weekStart: 0-6 代表 周日到周六 21 | * 22 | * method: focus or click 23 | * 24 | * // 非核心功能 25 | * default: 默认时间,在bind中给vm设置这个值 26 | * 27 | * lang: 国际化 28 | */ 29 | 30 | import Vue from 'vue' 31 | 32 | import dateTimePicker from './date-time-picker.vue' 33 | import { formatDate, parseDateTime } from './util' 34 | 35 | import EventListener from '../../util/EventListener' 36 | import { getBodyScrollTop } from '../../util/dom' 37 | 38 | const parseDirective = Vue.parsers.directive.parseDirective 39 | const { createAnchor, after, on, off } = Vue.util 40 | 41 | const template = '' 55 | 56 | const body = document.querySelector('body') 57 | const REGEX_FILTER = /[^|]\|[^|]/ 58 | 59 | const Component = Vue.extend({ 60 | template, 61 | components: { 62 | dateTimePicker 63 | } 64 | }) 65 | 66 | const RANGE_CONTROL = new Vue() 67 | 68 | // real directive 69 | export default { 70 | 71 | params: [ 72 | 'value', 73 | 'format', 74 | 'default', 75 | 'highlightToday', 76 | 'closeOnSelected', 77 | 'maxDate', 78 | 'minDate', 79 | 'align' // 对齐,left or right 80 | ], 81 | 82 | paramWatchers: { 83 | highlightToday (val) { 84 | this.__vm.highlightToday = !!val 85 | }, 86 | minDate (val) { 87 | this.__vm.minDate = parseDateTime(val) 88 | }, 89 | maxDate (val) { 90 | this.__vm.maxDate = parseDateTime(val) 91 | } 92 | }, 93 | 94 | bind () { 95 | // console.log(this.params) 96 | const el = this.el 97 | let _range = this.modifiers 98 | let _rangeName = this.expression 99 | 100 | // get v-model attr 101 | let raw = el.getAttribute('v-model') 102 | let { model } = this.parseModelRaw(raw) 103 | this.model = model 104 | this.params.format = this.params.format || 'yyyy-MM-dd' 105 | this.isDateEnabled = /yyyy|MM|dd/.test(this.params.format) 106 | 107 | // create date picker Vue instance 108 | this.__vm = this.createVm() 109 | this.setVmValue() 110 | 111 | // create start and end anchor to help insert picker into body end 112 | let startAnchor = createAnchor('date time picker start') 113 | let endAnchor = createAnchor('date time picker end') 114 | 115 | // // insert to body end 116 | after(startAnchor, body.lastChild) 117 | after(endAnchor, startAnchor) 118 | 119 | // 设置range属性 120 | if (_range.start) { 121 | this.isStart = true 122 | } else if (_range.end) { 123 | this.isEnd = true 124 | } 125 | 126 | if ((this.isStart || this.isEnd) && !_rangeName) { 127 | return 128 | } 129 | 130 | if (this.isStart || this.isEnd) { 131 | this.doWithRange() 132 | } 133 | 134 | // watch model, model变化重新设置组件的value 135 | this.watchModel() 136 | 137 | // do mount 138 | this.__vm.$mount() 139 | this.__vm.$before(endAnchor) 140 | this.__vm.$el = this.__vm.$el.childNodes[0] 141 | 142 | // 根据目标节点绑定事件控制控件显示 143 | this.bindEvent() 144 | }, 145 | 146 | unbind () { 147 | if (this._removeEventBind) { 148 | this._removeEventBind() 149 | } 150 | 151 | if (this._removeRangeControl) this._removeRangeControl() 152 | }, 153 | 154 | /* 155 | * watch this.vm[model], 如果变化则改变this.__vm.value 156 | */ 157 | watchModel() { 158 | if (!this.model) return 159 | this.vm.$watch(this.model, () => { 160 | this.setVmValue() 161 | }) 162 | }, 163 | 164 | /* 165 | * 根据父vm的model的值设置value 166 | */ 167 | setVmValue() { 168 | let value = this.vm.$get(this.model || '') 169 | if (!value) { 170 | return 171 | } 172 | let seconds = parseDateTime(value) 173 | if (!isNaN(seconds)) { 174 | this.__vm.value = seconds 175 | } 176 | }, 177 | 178 | // bind events to toggle date time picker 179 | bindEvent() { 180 | const focusCb = () => { 181 | // TODO:这样的定位方式存在一些缺陷 182 | // 如果存在局部滚动,在局部滚动的时候就会出现错位 183 | let rect = this.el.getBoundingClientRect() 184 | let bodyScrollTop = getBodyScrollTop() 185 | this.__vm.$set('rect', { 186 | left: rect.left + document.body.scrollLeft, 187 | right: rect.right, 188 | top: rect.top + bodyScrollTop, 189 | bottom: rect.bottom, 190 | width: rect.width || this.el.clientWidth, 191 | height: rect.height || this.el.clientHeight 192 | }) 193 | this.__vm.show = true 194 | } 195 | 196 | let self = this 197 | 198 | // important: 使用vm.$mount生成的节点,给vm设置的$el是一个注释节点 199 | // 只有使用nextElementSibling来获取模板生成的节点 200 | const _closeListener = EventListener.listen(window, 'click', (e) => { 201 | // if (self.el && 202 | // !self.el.contains(e.target) && 203 | // self.__vm.$el && 204 | // !self.__vm.$el.contains(e.target)) self.__vm.show = false 205 | if (e.target !== this.el) self.__vm.show = false 206 | }) 207 | 208 | const _closeEleListener = EventListener.listen(this.__vm.$el, 'click', (e) => { 209 | e.stopPropagation() 210 | }) 211 | 212 | // const _closeTargetListener = EventListener.listen(this.el, 'click', (e) => { 213 | // e.stopPropagation() 214 | // }) 215 | 216 | on(this.el, 'focus', focusCb) 217 | 218 | // on(this.el, 'blur', () => { 219 | // this.__vm.show = false 220 | // }) 221 | 222 | this._removeEventBind = function () { 223 | off(this.el, 'focus', focusCb) 224 | _closeListener.remove() 225 | _closeEleListener.remove() 226 | // _closeTargetListener.remove() 227 | } 228 | }, 229 | 230 | // create date-time-picker Vue instance with init data and components 231 | createVm() { 232 | const _this = this 233 | let vm = new Component({ 234 | replace: false, 235 | data: { 236 | show: false, 237 | rect: {}, 238 | value: null, 239 | highlightToday: _this.params.highlightToday, 240 | align: _this.params.align, 241 | minDate: parseDateTime(this.params.minDate) || -1, 242 | maxDate: parseDateTime(this.params.maxDate) || -1, 243 | format: _this.params.format, 244 | }, 245 | methods: { 246 | onChange (value) { 247 | this.value = parseDateTime(value) 248 | }, 249 | onComplete (value) { 250 | value = value || new Date() 251 | // 如果设置了在选择时关闭则设置show为false 252 | if (_this.params.closeOnSelected !== false) { 253 | this.show = false 254 | } 255 | // set value to modle 256 | if (_this.model) { 257 | _this.vm.$set(_this.model, formatDate(value, _this.params.format)) 258 | } else { 259 | _this.el.value = formatDate(value, _this.params.format) 260 | } 261 | // 如果有设置range时间的方法,则执行 262 | // 只通过判断是否有该方法判断是否是range 263 | if (_this.setRangeTime) _this.setRangeTime(value) 264 | } 265 | } 266 | }) 267 | 268 | return vm 269 | }, 270 | 271 | parseModelRaw (raw) { 272 | if (REGEX_FILTER.test(raw)) { 273 | let parsed = parseDirective(raw) 274 | return { model: parsed.expression, filters: parsed.filters } 275 | } else { 276 | return { model: raw } 277 | } 278 | }, 279 | 280 | /** 281 | * put this into range control 282 | * listen to range change to control picker to control range 283 | */ 284 | doWithRange() { 285 | let rc = RANGE_CONTROL 286 | let rangeName = this.expression 287 | let rangeType = this.isStart ? 'start' : 'end' 288 | let rangeOtherType = this.isStart ? 'end' : 'start' 289 | // debugger 290 | let initModel = this.vm.$get(this.model || '') 291 | let initDate = parseDateTime(initModel) 292 | // 293 | Vue.set(this.__vm, rangeOtherType + 'Date', -1) 294 | // 初始化 295 | if (!rc[rangeName]) { 296 | Vue.set(rc, rangeName, { 297 | start: -1, 298 | end: -1 299 | }) 300 | } 301 | // expression start startValue 302 | // 给range中间vue对象中的自己设置初始值 303 | // 等待range所有组件初始化完毕 304 | Vue.nextTick(() => { 305 | Vue.set(rc[rangeName], rangeType, initDate || -1) 306 | }) 307 | let unWatchRange = rc.$watch(rangeName + '.' + rangeOtherType, (newVal) => { 308 | // if (newVal[rangeOtherType] !== oldVal[rangeOtherType]) { 309 | // 设置 this.__vm相关的数据 310 | this.__vm[rangeOtherType + 'Date'] = newVal 311 | // } 312 | }, { deep: true }) 313 | this.setRangeTime = function (time) { 314 | rc[rangeName][rangeType] = time 315 | } 316 | this._removeRangeControl = function () { 317 | unWatchRange() 318 | if (rc[rangeName]) { 319 | // Vue.delete(rc, rangeName) 320 | rc[rangeName] = null 321 | } 322 | } 323 | } 324 | 325 | } 326 | -------------------------------------------------------------------------------- /src/component/date-picker/date-time-picker.vue: -------------------------------------------------------------------------------- 1 | 72 | 73 | 338 | -------------------------------------------------------------------------------- /doc/doc.styl: -------------------------------------------------------------------------------- 1 | body{ 2 | font-size: 14px; 3 | } 4 | #x-vue-menu{ 5 | position : fixed; 6 | width : 260px; 7 | top : 0; 8 | bottom : 0; 9 | //border-right: 1px solid #ddd; 10 | background-color: #efefef; 11 | box-shadow: 2px 0 3px #dadada; 12 | 13 | ul{ 14 | list-style: none; 15 | padding : 0; 16 | } 17 | 18 | .menu-item{ 19 | height : 40px; 20 | line-height: 40px; 21 | padding : 0 20px; 22 | text-decoration: none; 23 | color: #333; 24 | text-align: right; 25 | display: block; 26 | transition: all .5s; 27 | 28 | &:hover{ 29 | background-color: #0b97c4; 30 | color: #ffffff; 31 | } 32 | 33 | } 34 | 35 | } 36 | #x-vue-title{ 37 | height : 50px; 38 | background-color: #0b97c4; 39 | color: #ffffff; 40 | padding-right : 20px; 41 | text-align: right; 42 | font-size: 28px; 43 | line-height: 50px; 44 | font-weight: 900; 45 | } 46 | #container{ 47 | margin-left: 260px; 48 | padding : 20px; 49 | } 50 | 51 | .x-vue-example-panel{ 52 | .card{ 53 | max-width: 300px; 54 | } 55 | } 56 | 57 | 58 | // markdown 59 | //@font-face { 60 | // font-family: octicons-link; 61 | // src: url(data:font/woff;charset=utf-8;base64,d09GRgABAAAAAAZwABAAAAAACFQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABEU0lHAAAGaAAAAAgAAAAIAAAAAUdTVUIAAAZcAAAACgAAAAoAAQAAT1MvMgAAAyQAAABJAAAAYFYEU3RjbWFwAAADcAAAAEUAAACAAJThvmN2dCAAAATkAAAABAAAAAQAAAAAZnBnbQAAA7gAAACyAAABCUM+8IhnYXNwAAAGTAAAABAAAAAQABoAI2dseWYAAAFsAAABPAAAAZwcEq9taGVhZAAAAsgAAAA0AAAANgh4a91oaGVhAAADCAAAABoAAAAkCA8DRGhtdHgAAAL8AAAADAAAAAwGAACfbG9jYQAAAsAAAAAIAAAACABiATBtYXhwAAACqAAAABgAAAAgAA8ASm5hbWUAAAToAAABQgAAAlXu73sOcG9zdAAABiwAAAAeAAAAME3QpOBwcmVwAAAEbAAAAHYAAAB/aFGpk3jaTY6xa8JAGMW/O62BDi0tJLYQincXEypYIiGJjSgHniQ6umTsUEyLm5BV6NDBP8Tpts6F0v+k/0an2i+itHDw3v2+9+DBKTzsJNnWJNTgHEy4BgG3EMI9DCEDOGEXzDADU5hBKMIgNPZqoD3SilVaXZCER3/I7AtxEJLtzzuZfI+VVkprxTlXShWKb3TBecG11rwoNlmmn1P2WYcJczl32etSpKnziC7lQyWe1smVPy/Lt7Kc+0vWY/gAgIIEqAN9we0pwKXreiMasxvabDQMM4riO+qxM2ogwDGOZTXxwxDiycQIcoYFBLj5K3EIaSctAq2kTYiw+ymhce7vwM9jSqO8JyVd5RH9gyTt2+J/yUmYlIR0s04n6+7Vm1ozezUeLEaUjhaDSuXHwVRgvLJn1tQ7xiuVv/ocTRF42mNgZGBgYGbwZOBiAAFGJBIMAAizAFoAAABiAGIAznjaY2BkYGAA4in8zwXi+W2+MjCzMIDApSwvXzC97Z4Ig8N/BxYGZgcgl52BCSQKAA3jCV8CAABfAAAAAAQAAEB42mNgZGBg4f3vACQZQABIMjKgAmYAKEgBXgAAeNpjYGY6wTiBgZWBg2kmUxoDA4MPhGZMYzBi1AHygVLYQUCaawqDA4PChxhmh/8ODDEsvAwHgMKMIDnGL0x7gJQCAwMAJd4MFwAAAHjaY2BgYGaA4DAGRgYQkAHyGMF8NgYrIM3JIAGVYYDT+AEjAwuDFpBmA9KMDEwMCh9i/v8H8sH0/4dQc1iAmAkALaUKLgAAAHjaTY9LDsIgEIbtgqHUPpDi3gPoBVyRTmTddOmqTXThEXqrob2gQ1FjwpDvfwCBdmdXC5AVKFu3e5MfNFJ29KTQT48Ob9/lqYwOGZxeUelN2U2R6+cArgtCJpauW7UQBqnFkUsjAY/kOU1cP+DAgvxwn1chZDwUbd6CFimGXwzwF6tPbFIcjEl+vvmM/byA48e6tWrKArm4ZJlCbdsrxksL1AwWn/yBSJKpYbq8AXaaTb8AAHja28jAwOC00ZrBeQNDQOWO//sdBBgYGRiYWYAEELEwMTE4uzo5Zzo5b2BxdnFOcALxNjA6b2ByTswC8jYwg0VlNuoCTWAMqNzMzsoK1rEhNqByEyerg5PMJlYuVueETKcd/89uBpnpvIEVomeHLoMsAAe1Id4AAAAAAAB42oWQT07CQBTGv0JBhagk7HQzKxca2sJCE1hDt4QF+9JOS0nbaaYDCQfwCJ7Au3AHj+LO13FMmm6cl7785vven0kBjHCBhfpYuNa5Ph1c0e2Xu3jEvWG7UdPDLZ4N92nOm+EBXuAbHmIMSRMs+4aUEd4Nd3CHD8NdvOLTsA2GL8M9PODbcL+hD7C1xoaHeLJSEao0FEW14ckxC+TU8TxvsY6X0eLPmRhry2WVioLpkrbp84LLQPGI7c6sOiUzpWIWS5GzlSgUzzLBSikOPFTOXqly7rqx0Z1Q5BAIoZBSFihQYQOOBEdkCOgXTOHA07HAGjGWiIjaPZNW13/+lm6S9FT7rLHFJ6fQbkATOG1j2OFMucKJJsxIVfQORl+9Jyda6Sl1dUYhSCm1dyClfoeDve4qMYdLEbfqHf3O/AdDumsjAAB42mNgYoAAZQYjBmyAGYQZmdhL8zLdDEydARfoAqIAAAABAAMABwAKABMAB///AA8AAQAAAAAAAAAAAAAAAAABAAAAAA==) format('woff'); 62 | //} 63 | 64 | .markdown-body { 65 | -ms-text-size-adjust: 100%; 66 | -webkit-text-size-adjust: 100%; 67 | color: #333; 68 | font-family: "Helvetica Neue", Helvetica, "Segoe UI", Arial, freesans, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol"; 69 | font-size: 16px; 70 | line-height: 1.6; 71 | word-wrap: break-word; 72 | } 73 | 74 | .markdown-body a { 75 | background-color: transparent; 76 | -webkit-text-decoration-skip: objects; 77 | } 78 | 79 | .markdown-body a:active, 80 | .markdown-body a:hover { 81 | outline-width: 0; 82 | } 83 | 84 | .markdown-body strong { 85 | font-weight: inherit; 86 | } 87 | 88 | .markdown-body strong { 89 | font-weight: bolder; 90 | } 91 | 92 | .markdown-body h1 { 93 | font-size: 2em; 94 | margin: 0.67em 0; 95 | } 96 | 97 | .markdown-body img { 98 | border-style: none; 99 | } 100 | 101 | .markdown-body svg:not(:root) { 102 | overflow: hidden; 103 | } 104 | 105 | .markdown-body code, 106 | .markdown-body kbd, 107 | .markdown-body pre { 108 | font-family: monospace, monospace; 109 | font-size: 1em; 110 | } 111 | 112 | .markdown-body hr { 113 | box-sizing: content-box; 114 | height: 0; 115 | overflow: visible; 116 | } 117 | 118 | .markdown-body input { 119 | font: inherit; 120 | margin: 0; 121 | } 122 | 123 | .markdown-body input { 124 | overflow: visible; 125 | } 126 | 127 | .markdown-body button:-moz-focusring, 128 | .markdown-body [type="button"]:-moz-focusring, 129 | .markdown-body [type="reset"]:-moz-focusring, 130 | .markdown-body [type="submit"]:-moz-focusring { 131 | outline: 1px dotted ButtonText; 132 | } 133 | 134 | .markdown-body [type="checkbox"] { 135 | box-sizing: border-box; 136 | padding: 0; 137 | } 138 | 139 | .markdown-body table { 140 | border-spacing: 0; 141 | border-collapse: collapse; 142 | } 143 | 144 | .markdown-body td, 145 | .markdown-body th { 146 | padding: 0; 147 | } 148 | 149 | .markdown-body * { 150 | box-sizing: border-box; 151 | } 152 | 153 | .markdown-body input { 154 | font: 13px/1.4 Helvetica, arial, nimbussansl, liberationsans, freesans, clean, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol"; 155 | } 156 | 157 | .markdown-body a { 158 | color: #4078c0; 159 | text-decoration: none; 160 | } 161 | 162 | .markdown-body a:hover, 163 | .markdown-body a:active { 164 | text-decoration: underline; 165 | } 166 | 167 | .markdown-body hr { 168 | height: 0; 169 | margin: 15px 0; 170 | overflow: hidden; 171 | background: transparent; 172 | border: 0; 173 | border-bottom: 1px solid #ddd; 174 | } 175 | 176 | .markdown-body hr::before { 177 | display: table; 178 | content: ""; 179 | } 180 | 181 | .markdown-body hr::after { 182 | display: table; 183 | clear: both; 184 | content: ""; 185 | } 186 | 187 | .markdown-body h1, 188 | .markdown-body h2, 189 | .markdown-body h3, 190 | .markdown-body h4, 191 | .markdown-body h5, 192 | .markdown-body h6 { 193 | margin-top: 0; 194 | margin-bottom: 0; 195 | line-height: 1.5; 196 | } 197 | 198 | .markdown-body h1 { 199 | font-size: 30px; 200 | } 201 | 202 | .markdown-body h2 { 203 | font-size: 21px; 204 | } 205 | 206 | .markdown-body h3 { 207 | font-size: 16px; 208 | } 209 | 210 | .markdown-body h4 { 211 | font-size: 14px; 212 | } 213 | 214 | .markdown-body h5 { 215 | font-size: 12px; 216 | } 217 | 218 | .markdown-body h6 { 219 | font-size: 11px; 220 | } 221 | 222 | .markdown-body p { 223 | margin-top: 0; 224 | margin-bottom: 10px; 225 | } 226 | 227 | .markdown-body blockquote { 228 | margin: 0; 229 | } 230 | 231 | .markdown-body ul, 232 | .markdown-body ol { 233 | padding-left: 0; 234 | margin-top: 0; 235 | margin-bottom: 0; 236 | } 237 | 238 | .markdown-body ol ol, 239 | .markdown-body ul ol { 240 | list-style-type: lower-roman; 241 | } 242 | 243 | .markdown-body ul ul ol, 244 | .markdown-body ul ol ol, 245 | .markdown-body ol ul ol, 246 | .markdown-body ol ol ol { 247 | list-style-type: lower-alpha; 248 | } 249 | 250 | .markdown-body dd { 251 | margin-left: 0; 252 | } 253 | 254 | .markdown-body code { 255 | font-family: Consolas, "Liberation Mono", Menlo, Courier, monospace; 256 | font-size: 12px; 257 | } 258 | 259 | .markdown-body pre { 260 | margin-top: 0; 261 | margin-bottom: 0; 262 | font: 12px Consolas, "Liberation Mono", Menlo, Courier, monospace; 263 | } 264 | 265 | .markdown-body .pl-0 { 266 | padding-left: 0 !important; 267 | } 268 | 269 | .markdown-body .pl-1 { 270 | padding-left: 3px !important; 271 | } 272 | 273 | .markdown-body .pl-2 { 274 | padding-left: 6px !important; 275 | } 276 | 277 | .markdown-body .pl-3 { 278 | padding-left: 12px !important; 279 | } 280 | 281 | .markdown-body .pl-4 { 282 | padding-left: 24px !important; 283 | } 284 | 285 | .markdown-body .pl-5 { 286 | padding-left: 36px !important; 287 | } 288 | 289 | .markdown-body .pl-6 { 290 | padding-left: 48px !important; 291 | } 292 | 293 | .markdown-body .form-select::-ms-expand { 294 | opacity: 0; 295 | } 296 | 297 | .markdown-body:before { 298 | display: table; 299 | content: ""; 300 | } 301 | 302 | .markdown-body:after { 303 | display: table; 304 | clear: both; 305 | content: ""; 306 | } 307 | 308 | .markdown-body>*:first-child { 309 | margin-top: 0 !important; 310 | } 311 | 312 | .markdown-body>*:last-child { 313 | margin-bottom: 0 !important; 314 | } 315 | 316 | .markdown-body a:not([href]) { 317 | color: inherit; 318 | text-decoration: none; 319 | } 320 | 321 | .markdown-body .anchor { 322 | display: inline-block; 323 | padding-right: 2px; 324 | margin-left: -18px; 325 | } 326 | 327 | .markdown-body .anchor:focus { 328 | outline: none; 329 | } 330 | 331 | .markdown-body h1, 332 | .markdown-body h2, 333 | .markdown-body h3, 334 | .markdown-body h4, 335 | .markdown-body h5, 336 | .markdown-body h6 { 337 | margin-top: 1em; 338 | margin-bottom: 16px; 339 | font-weight: bold; 340 | line-height: 1.4; 341 | } 342 | 343 | .markdown-body h1 .octicon-link, 344 | .markdown-body h2 .octicon-link, 345 | .markdown-body h3 .octicon-link, 346 | .markdown-body h4 .octicon-link, 347 | .markdown-body h5 .octicon-link, 348 | .markdown-body h6 .octicon-link { 349 | color: #000; 350 | vertical-align: middle; 351 | visibility: hidden; 352 | } 353 | 354 | .markdown-body h1:hover .anchor, 355 | .markdown-body h2:hover .anchor, 356 | .markdown-body h3:hover .anchor, 357 | .markdown-body h4:hover .anchor, 358 | .markdown-body h5:hover .anchor, 359 | .markdown-body h6:hover .anchor { 360 | text-decoration: none; 361 | } 362 | 363 | .markdown-body h1:hover .anchor .octicon-link, 364 | .markdown-body h2:hover .anchor .octicon-link, 365 | .markdown-body h3:hover .anchor .octicon-link, 366 | .markdown-body h4:hover .anchor .octicon-link, 367 | .markdown-body h5:hover .anchor .octicon-link, 368 | .markdown-body h6:hover .anchor .octicon-link { 369 | visibility: visible; 370 | } 371 | 372 | .markdown-body h1 { 373 | padding-bottom: 0.3em; 374 | font-size: 2.25em; 375 | line-height: 1.2; 376 | border-bottom: 1px solid #eee; 377 | } 378 | 379 | .markdown-body h1 .anchor { 380 | line-height: 1; 381 | } 382 | 383 | .markdown-body h2 { 384 | padding-bottom: 0.3em; 385 | font-size: 1.75em; 386 | line-height: 1.225; 387 | border-bottom: 1px solid #eee; 388 | } 389 | 390 | .markdown-body h2 .anchor { 391 | line-height: 1; 392 | } 393 | 394 | .markdown-body h3 { 395 | font-size: 1.5em; 396 | line-height: 1.43; 397 | } 398 | 399 | .markdown-body h3 .anchor { 400 | line-height: 1.2; 401 | } 402 | 403 | .markdown-body h4 { 404 | font-size: 1.25em; 405 | } 406 | 407 | .markdown-body h4 .anchor { 408 | line-height: 1.2; 409 | } 410 | 411 | .markdown-body h5 { 412 | font-size: 1em; 413 | } 414 | 415 | .markdown-body h5 .anchor { 416 | line-height: 1.1; 417 | } 418 | 419 | .markdown-body h6 { 420 | font-size: 1em; 421 | color: #777; 422 | } 423 | 424 | .markdown-body h6 .anchor { 425 | line-height: 1.1; 426 | } 427 | 428 | .markdown-body p, 429 | .markdown-body blockquote, 430 | .markdown-body ul, 431 | .markdown-body ol, 432 | .markdown-body dl, 433 | .markdown-body table, 434 | .markdown-body pre { 435 | margin-top: 0; 436 | margin-bottom: 16px; 437 | } 438 | 439 | .markdown-body hr { 440 | height: 4px; 441 | padding: 0; 442 | margin: 16px 0; 443 | background-color: #e7e7e7; 444 | border: 0 none; 445 | } 446 | 447 | .markdown-body ul, 448 | .markdown-body ol { 449 | padding-left: 2em; 450 | } 451 | 452 | .markdown-body ul ul, 453 | .markdown-body ul ol, 454 | .markdown-body ol ol, 455 | .markdown-body ol ul { 456 | margin-top: 0; 457 | margin-bottom: 0; 458 | } 459 | 460 | .markdown-body li>p { 461 | margin-top: 16px; 462 | } 463 | 464 | .markdown-body dl { 465 | padding: 0; 466 | } 467 | 468 | .markdown-body dl dt { 469 | padding: 0; 470 | margin-top: 16px; 471 | font-size: 1em; 472 | font-style: italic; 473 | font-weight: bold; 474 | } 475 | 476 | .markdown-body dl dd { 477 | padding: 0 16px; 478 | margin-bottom: 16px; 479 | } 480 | 481 | .markdown-body blockquote { 482 | padding: 0 15px; 483 | color: #777; 484 | border-left: 4px solid #ddd; 485 | } 486 | 487 | .markdown-body blockquote>:first-child { 488 | margin-top: 0; 489 | } 490 | 491 | .markdown-body blockquote>:last-child { 492 | margin-bottom: 0; 493 | } 494 | 495 | .markdown-body table { 496 | display: block; 497 | width: 100%; 498 | overflow: auto; 499 | word-break: normal; 500 | word-break: keep-all; 501 | } 502 | 503 | .markdown-body table th { 504 | font-weight: bold; 505 | } 506 | 507 | .markdown-body table th, 508 | .markdown-body table td { 509 | padding: 6px 13px; 510 | border: 1px solid #ddd; 511 | } 512 | 513 | .markdown-body table tr { 514 | background-color: #fff; 515 | border-top: 1px solid #ccc; 516 | } 517 | 518 | .markdown-body table tr:nth-child(2n) { 519 | background-color: #f8f8f8; 520 | } 521 | 522 | .markdown-body img { 523 | max-width: 100%; 524 | box-sizing: content-box; 525 | background-color: #fff; 526 | } 527 | 528 | .markdown-body code { 529 | padding: 0; 530 | padding-top: 0.2em; 531 | padding-bottom: 0.2em; 532 | margin: 0; 533 | font-size: 85%; 534 | background-color: rgba(0,0,0,0.04); 535 | border-radius: 3px; 536 | } 537 | 538 | .markdown-body code:before, 539 | .markdown-body code:after { 540 | letter-spacing: -0.2em; 541 | content: "\00a0"; 542 | } 543 | 544 | .markdown-body pre>code { 545 | padding: 0; 546 | margin: 0; 547 | font-size: 100%; 548 | word-break: normal; 549 | white-space: pre; 550 | background: transparent; 551 | border: 0; 552 | } 553 | 554 | .markdown-body .highlight { 555 | margin-bottom: 16px; 556 | } 557 | 558 | .markdown-body .highlight pre, 559 | .markdown-body pre { 560 | padding: 16px; 561 | overflow: auto; 562 | font-size: 85%; 563 | line-height: 1.45; 564 | background-color: #f7f7f7; 565 | border-radius: 3px; 566 | } 567 | 568 | .markdown-body .highlight pre { 569 | margin-bottom: 0; 570 | word-break: normal; 571 | } 572 | 573 | .markdown-body pre { 574 | word-wrap: normal; 575 | } 576 | 577 | .markdown-body pre code { 578 | display: inline; 579 | max-width: initial; 580 | padding: 0; 581 | margin: 0; 582 | overflow: initial; 583 | line-height: inherit; 584 | word-wrap: normal; 585 | background-color: transparent; 586 | border: 0; 587 | } 588 | 589 | .markdown-body pre code:before, 590 | .markdown-body pre code:after { 591 | content: normal; 592 | } 593 | 594 | .markdown-body kbd { 595 | display: inline-block; 596 | padding: 3px 5px; 597 | font-size: 11px; 598 | line-height: 10px; 599 | color: #555; 600 | vertical-align: middle; 601 | background-color: #fcfcfc; 602 | border: solid 1px #ccc; 603 | border-bottom-color: #bbb; 604 | border-radius: 3px; 605 | box-shadow: inset 0 -1px 0 #bbb; 606 | } 607 | 608 | .markdown-body .pl-c { 609 | color: #969896; 610 | } 611 | 612 | .markdown-body .pl-c1, 613 | .markdown-body .pl-s .pl-v { 614 | color: #0086b3; 615 | } 616 | 617 | .markdown-body .pl-e, 618 | .markdown-body .pl-en { 619 | color: #795da3; 620 | } 621 | 622 | .markdown-body .pl-s .pl-s1, 623 | .markdown-body .pl-smi { 624 | color: #333; 625 | } 626 | 627 | .markdown-body .pl-ent { 628 | color: #63a35c; 629 | } 630 | 631 | .markdown-body .pl-k { 632 | color: #a71d5d; 633 | } 634 | 635 | .markdown-body .pl-pds, 636 | .markdown-body .pl-s, 637 | .markdown-body .pl-s .pl-pse .pl-s1, 638 | .markdown-body .pl-sr, 639 | .markdown-body .pl-sr .pl-cce, 640 | .markdown-body .pl-sr .pl-sra, 641 | .markdown-body .pl-sr .pl-sre { 642 | color: #183691; 643 | } 644 | 645 | .markdown-body .pl-v { 646 | color: #ed6a43; 647 | } 648 | 649 | .markdown-body .pl-id { 650 | color: #b52a1d; 651 | } 652 | 653 | .markdown-body .pl-ii { 654 | background-color: #b52a1d; 655 | color: #f8f8f8; 656 | } 657 | 658 | .markdown-body .pl-sr .pl-cce { 659 | color: #63a35c; 660 | font-weight: bold; 661 | } 662 | 663 | .markdown-body .pl-ml { 664 | color: #693a17; 665 | } 666 | 667 | .markdown-body .pl-mh, 668 | .markdown-body .pl-mh .pl-en, 669 | .markdown-body .pl-ms { 670 | color: #1d3e81; 671 | font-weight: bold; 672 | } 673 | 674 | .markdown-body .pl-mq { 675 | color: #008080; 676 | } 677 | 678 | .markdown-body .pl-mi { 679 | color: #333; 680 | font-style: italic; 681 | } 682 | 683 | .markdown-body .pl-mb { 684 | color: #333; 685 | font-weight: bold; 686 | } 687 | 688 | .markdown-body .pl-md { 689 | background-color: #ffecec; 690 | color: #bd2c00; 691 | } 692 | 693 | .markdown-body .pl-mi1 { 694 | background-color: #eaffea; 695 | color: #55a532; 696 | } 697 | 698 | .markdown-body .pl-mdr { 699 | color: #795da3; 700 | font-weight: bold; 701 | } 702 | 703 | .markdown-body .pl-mo { 704 | color: #1d3e81; 705 | } 706 | 707 | .markdown-body kbd { 708 | display: inline-block; 709 | padding: 3px 5px; 710 | font: 11px Consolas, "Liberation Mono", Menlo, Courier, monospace; 711 | line-height: 10px; 712 | color: #555; 713 | vertical-align: middle; 714 | background-color: #fcfcfc; 715 | border: solid 1px #ccc; 716 | border-bottom-color: #bbb; 717 | border-radius: 3px; 718 | box-shadow: inset 0 -1px 0 #bbb; 719 | } 720 | 721 | .markdown-body .full-commit .btn-outline:not(:disabled):hover { 722 | color: #4078c0; 723 | border: 1px solid #4078c0; 724 | } 725 | 726 | .markdown-body :checked+.radio-label { 727 | position: relative; 728 | z-index: 1; 729 | border-color: #4078c0; 730 | } 731 | 732 | .markdown-body .octicon { 733 | display: inline-block; 734 | vertical-align: text-top; 735 | fill: currentColor; 736 | } 737 | 738 | .markdown-body .task-list-item { 739 | list-style-type: none; 740 | } 741 | 742 | .markdown-body .task-list-item+.task-list-item { 743 | margin-top: 3px; 744 | } 745 | 746 | .markdown-body .task-list-item input { 747 | margin: 0 0.2em 0.25em -1.6em; 748 | vertical-align: middle; 749 | } 750 | 751 | .markdown-body hr { 752 | border-bottom-color: #eee; 753 | } 754 | 755 | --------------------------------------------------------------------------------