├── .babelrc ├── .gitattributes ├── .gitignore ├── LICENSE ├── README.md ├── build ├── gh-pages.sh ├── webpack.config.js ├── webpack.example.base.conf.js ├── webpack.example.dev.conf.js └── webpack.example.prod.conf.js ├── example ├── index.html └── src │ ├── App.vue │ ├── best-rating.vue │ ├── main.js │ └── template.js ├── lib └── collapse.js ├── package.json ├── screenshot └── collapse.gif ├── src ├── components │ ├── collapse-item.vue │ └── collapse.vue ├── index.js ├── styles │ └── less │ │ ├── base.less │ │ ├── collapse.less │ │ └── index.less ├── transitions │ └── collapse-transition.js └── utils │ └── dom.js └── yarn.lock /.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": [ 3 | ["env", { "modules": false }] 4 | ] 5 | } 6 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | **/*.js linguist-language=Vue 2 | **/*.less linguist-language=Vue 3 | **/*.css linguist-language=Vue 4 | **/*.html linguist-language=Vue -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | node_modules 3 | .idea 4 | example/dist 5 | gh-pages 6 | images -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # collapse 2 | 3 | > [Element](https://github.com/ElemeFE/element) Collapse clone, extract the core function and did a little change. If you have a better idea of this component improvement, please share it and I will update it immediately. 4 | 5 | ## Screenshots 6 | 7 | ![vue-multiple-collapse](screenshot/collapse.gif) 8 | 9 | ## Install 10 | 11 | ```bash 12 | npm install vue-multiple-collapse -S 13 | ``` 14 | 15 | ## Quick Start 16 | 17 | ```bash 18 | import Vue from 'vue' 19 | import {VmCollapse, VmCollapseItem } from 'vue-multiple-collapse' 20 | Vue.component('vm-collapse', VmCollapse) 21 | Vue.component('vm-collapse-item', VmCollapseItem) 22 | ``` 23 | 24 | OR 25 | 26 | ```bash 27 | import Vue from 'vue' 28 | import VmCollapse from 'vue-multiple-collapse' 29 | Vue.use(VmCollapse) 30 | ``` 31 | 32 | For more information, please refer to [Collapse](https://vue-multiple.github.io/collapse) in our documentation. 33 | 34 | ## Build Setup 35 | 36 | ``` bash 37 | # install dependencies 38 | npm install 39 | 40 | # serve with hot reload at localhost:8080 41 | npm run demo:dev 42 | 43 | # build for demo with minification 44 | npm run demo:build 45 | 46 | # build for gh-pages with minification 47 | npm run demo:prepublish 48 | 49 | # build for production with minification 50 | npm run build 51 | 52 | # generate gh-pages 53 | npm run deploy 54 | ``` 55 | 56 | ## LICENSE 57 | 58 | [MIT](http://opensource.org/licenses/MIT) 59 | -------------------------------------------------------------------------------- /build/gh-pages.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | npm run demo:prepublish 3 | cd gh-pages 4 | git init 5 | git add -A 6 | git commit -m 'update gh-pages' 7 | git push -f git@github.com:vue-multiple/collapse.git master:gh-pages -------------------------------------------------------------------------------- /build/webpack.config.js: -------------------------------------------------------------------------------- 1 | var path = require('path') 2 | var webpack = require('webpack') 3 | var ExtractTextPlugin = require('extract-text-webpack-plugin') 4 | 5 | module.exports = { 6 | entry: './src/index.js', 7 | output: { 8 | path: path.resolve(__dirname, '..', './lib'), 9 | filename: 'collapse.js', 10 | library: 'collapse', 11 | libraryTarget: 'umd' 12 | }, 13 | externals: { 14 | vue: { 15 | root: 'Vue', 16 | commonjs: 'vue', 17 | commonjs2: 'vue', 18 | amd: 'vue' 19 | } 20 | }, 21 | module: { 22 | rules: [ 23 | { 24 | test: /\.vue$/, 25 | loader: 'vue-loader', 26 | options: { 27 | loaders: { 28 | css: 'vue-style-loader!css-loader', 29 | less: 'vue-style-loader!css-loader!less-loader', 30 | // css: ExtractTextPlugin.extract({ 31 | // use: 'css-loader', 32 | // fallback: 'vue-style-loader' 33 | // }), 34 | // less: ExtractTextPlugin.extract({ 35 | // fallback: 'vue-style-loader', 36 | // use: ['css-loader', 'less-loader'] 37 | // }) 38 | } 39 | // other vue-loader options go here 40 | } 41 | }, 42 | { 43 | test: /\.css$/, 44 | use: [ 45 | { loader: 'style-loader' }, 46 | { loader: 'css-loader' } 47 | ] 48 | // loader: ExtractTextPlugin.extract({ 49 | // use: "css-loader", 50 | // fallback: "style-loader" 51 | // }) 52 | }, 53 | { 54 | test: /\.less$/, 55 | use: [ 56 | { loader: 'style-loader' }, 57 | { loader: 'css-loader' }, 58 | { loader: 'less-loader' } 59 | ] 60 | // loader: ExtractTextPlugin.extract({ 61 | // fallback: 'style-loader', 62 | // use: ['css-loader', 'less-loader'] 63 | // }) 64 | }, 65 | { 66 | test: /\.js$/, 67 | loader: 'babel-loader', 68 | exclude: /node_modules/ 69 | }, 70 | { 71 | test: /\.(png|jpg|gif|svg)$/, 72 | loader: 'file-loader', 73 | options: { 74 | name: '[name].[ext]?[hash]' 75 | } 76 | }, 77 | { 78 | test: /\.(woff2?|eot|ttf|otf)(\?.*)?$/, 79 | loader: 'url-loader', 80 | query: { 81 | limit: 10000, 82 | name: path.posix.join('static', 'fonts/[name].[hash:7].[ext]') 83 | } 84 | } 85 | ] 86 | }, 87 | resolve: { 88 | extensions: ['.js', '.vue', '.json'] 89 | }, 90 | plugins: [ 91 | new webpack.optimize.UglifyJsPlugin({ 92 | sourceMap: true, 93 | compress: { 94 | warnings: false 95 | } 96 | }) 97 | // , 98 | // new ExtractTextPlugin({ 99 | // filename: '../lib/collapse.css', 100 | // allChunks: true 101 | // }) 102 | ] 103 | } 104 | -------------------------------------------------------------------------------- /build/webpack.example.base.conf.js: -------------------------------------------------------------------------------- 1 | var path = require('path') 2 | var ExtractTextPlugin = require('extract-text-webpack-plugin') 3 | var isProduction = process.env.NODE_ENV === 'production' 4 | 5 | module.exports = { 6 | entry: { 7 | app: './example/src/main.js' 8 | }, 9 | output: { 10 | path: '/example', 11 | filename: '[name].js', 12 | publicPath: '/' 13 | }, 14 | module: { 15 | rules: [ 16 | { 17 | test: /\.vue$/, 18 | loader: 'vue-loader', 19 | options: { 20 | // other vue-loader options go here 21 | loaders: { 22 | css: isProduction ? ExtractTextPlugin.extract({ 23 | fallback: 'vue-style-loader', 24 | use: 'css-loader' 25 | }) : 'vue-style-loader!css-loader', 26 | less: isProduction ? ExtractTextPlugin.extract({ 27 | fallback: 'vue-style-loader', 28 | use: ['css-loader', 'less-loader'] 29 | }) : 'vue-style-loader!css-loader!less-loader' 30 | } 31 | } 32 | }, 33 | { 34 | test: /\.js$/, 35 | loader: 'babel-loader', 36 | exclude: /node_modules/ 37 | }, 38 | { 39 | test: /\.(png|jpg|gif|svg)$/, 40 | loader: 'file-loader', 41 | options: { 42 | name: '[name].[ext]?[hash]', 43 | publicPath: '../example/src/images/', 44 | outputPath: '../gh-pages/images/' 45 | } 46 | }, 47 | { 48 | test: /\.(woff2?|eot|ttf|otf)(\?.*)?$/, 49 | loader: 'url-loader', 50 | query: { 51 | limit: 10000, 52 | name: path.posix.join('static', 'fonts/[name].[hash:7].[ext]') 53 | } 54 | } 55 | ] 56 | } 57 | } -------------------------------------------------------------------------------- /build/webpack.example.dev.conf.js: -------------------------------------------------------------------------------- 1 | var webpack = require('webpack') 2 | var merge = require('webpack-merge') 3 | var baseWebpackConfig = require('./webpack.example.base.conf') 4 | var HtmlWebpackPlugin = require('html-webpack-plugin') 5 | var FriendlyErrorsPlugin = require('friendly-errors-webpack-plugin') 6 | 7 | module.exports = merge(baseWebpackConfig, { 8 | module: { 9 | rules: [ 10 | { 11 | test: /\.css$/, 12 | loader: 'vue-style-loader!css-loader' 13 | }, 14 | { 15 | test: /\.less$/, 16 | loader: 'vue-style-loader!css-loader!less-loader' 17 | } 18 | ] 19 | }, 20 | // cheap-module-eval-source-map is faster for development 21 | devtool: '#cheap-module-eval-source-map', 22 | plugins: [ 23 | new webpack.DefinePlugin({ 24 | 'process.env': { 25 | NODE_ENV: '"development"' 26 | } 27 | }), 28 | new webpack.NoEmitOnErrorsPlugin(), 29 | new HtmlWebpackPlugin({ 30 | filename: 'index.html', 31 | template: 'example/index.html', 32 | inject: true 33 | }), 34 | new FriendlyErrorsPlugin() 35 | ] 36 | }) -------------------------------------------------------------------------------- /build/webpack.example.prod.conf.js: -------------------------------------------------------------------------------- 1 | var path = require('path') 2 | var webpack = require('webpack') 3 | var merge = require('webpack-merge') 4 | var baseWebpackConfig = require('./webpack.example.base.conf') 5 | var HtmlWebpackPlugin = require('html-webpack-plugin') 6 | var ExtractTextPlugin = require('extract-text-webpack-plugin') 7 | var isProduction = process.env.NODE_ENV === 'production' 8 | 9 | module.exports = merge(baseWebpackConfig, { 10 | module: { 11 | rules: [ 12 | { 13 | test: /\.css$/, 14 | loader: ExtractTextPlugin.extract({ 15 | fallback: "vue-style-loader", 16 | use: "css-loader" 17 | }) 18 | }, 19 | { 20 | test: /\.less/, 21 | loader: ExtractTextPlugin.extract({ 22 | fallback: "vue-style-loader", 23 | use: ["css-loader", "less-loader"] 24 | }) 25 | } 26 | ] 27 | }, 28 | devtool: '#source-map', 29 | output: { 30 | path: path.resolve(__dirname, '..', `${isProduction ? './example/dist' : 'gh-pages'}`), 31 | publicPath: isProduction ? '/' : '/collapse', 32 | filename: 'js/[name].[chunkhash].js' 33 | }, 34 | plugins: [ 35 | new webpack.DefinePlugin({ 36 | 'process.env': { 37 | NODE_ENV: '"production"' 38 | } 39 | }), 40 | new webpack.optimize.UglifyJsPlugin({ 41 | sourceMap: true, 42 | compress: { 43 | warnings: false 44 | } 45 | }), 46 | new ExtractTextPlugin({ 47 | filename: 'css/[name].[contenthash].css', 48 | allChunks: true 49 | }), 50 | new HtmlWebpackPlugin({ 51 | filename: 'index.html', 52 | template: 'example/index.html', 53 | inject: true, 54 | minify: { 55 | removeComments: true, 56 | collapseWhitespace: true, 57 | removeAttributeQuotes: true 58 | // more options: 59 | // https://github.com/kangax/html-minifier#options-quick-reference 60 | }, 61 | // necessary to consistently work with multiple chunks via CommonsChunkPlugin 62 | chunksSortMode: 'dependency' 63 | }) 64 | ] 65 | }) 66 | -------------------------------------------------------------------------------- /example/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 7 | 8 | collapse 9 | 10 | 11 |
12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /example/src/App.vue: -------------------------------------------------------------------------------- 1 | 452 | 453 | 607 | 608 | 616 | -------------------------------------------------------------------------------- /example/src/best-rating.vue: -------------------------------------------------------------------------------- 1 | 34 | 54 | -------------------------------------------------------------------------------- /example/src/main.js: -------------------------------------------------------------------------------- 1 | import Vue from 'vue' 2 | import App from './App.vue' 3 | 4 | import Collapse from '../../src/index.js' 5 | Vue.use(Collapse) 6 | 7 | import VmRate from 'vue-multiple-rate' 8 | import 'vue-multiple-rate/lib/rate.css' 9 | Vue.component(VmRate.name, VmRate) 10 | 11 | import VmTabs from 'vue-multiple-tabs' 12 | Vue.use(VmTabs) 13 | 14 | import VueDemonstration from 'vue-demonstration' 15 | Vue.component('demonstration', VueDemonstration) 16 | 17 | new Vue({ 18 | el: '#app', 19 | render: h => h(App) 20 | }) 21 | -------------------------------------------------------------------------------- /example/src/template.js: -------------------------------------------------------------------------------- 1 | export const sourcecodeA = `
2 | 默认 3 | 4 | 5 |
与现实生活一致:与现实生活的流程、逻辑保持一致,遵循用户习惯的语言和概念;
6 |
在界面中一致:所有的元素和结构需保持一致,比如:设计样式、图标和文本、元素的位置等。
7 |
8 | 9 |
控制反馈:通过界面样式和交互动效让用户可以清晰的感知自己的操作;
10 |
页面反馈:操作后,通过页面元素的变化清晰地展现当前状态。
11 |
12 | 13 |
简化流程:设计简洁直观的操作流程;
14 |
清晰明确:语言表达清晰且表意明确,让用户快速理解进而作出决策;
15 |
帮助用户识别:界面简单直白,让用户快速识别而非回忆,减少用户记忆负担。
16 |
17 | 18 |
用户决策:根据场景可给予用户操作建议或安全提示,但不能代替用户进行决策;
19 |
结果可控:用户可以自由的进行操作,包括撤销、回退和终止当前操作等。
20 |
21 |
22 |
23 |
24 | 带边框 25 | 26 | ... 27 | 28 |
29 | 30 | ` 40 | 41 | export const sourcecodeB = ` 42 | ... 43 | 44 | 45 | ` 54 | 55 | export const sourcecodeC = `
56 | 默认模式下 57 | 58 | ... 59 | 60 |
61 |
62 | 手风琴模式下 63 | 64 | ... 65 | 66 |
67 | 68 | ` 78 | 79 | export const sourcecodeD = `
80 | 默认模式下 81 | 82 | ... 83 | 84 |
85 |
86 | 默认模式下(可关闭) 87 | 88 | ... 89 | 90 |
91 |
92 | 手风琴模式下(无效 93 | 94 | ... 95 | 96 |
97 |
98 | 变换模式下 99 | 100 | ... 101 | 102 |
103 | 104 | ` 116 | 117 | export const sourcecodeE = `
118 | 自定义标题 119 | 120 | 121 | 124 |
与现实生活一致:与现实生活的流程、逻辑保持一致,遵循用户习惯的语言和概念;
125 |
在界面中一致:所有的元素和结构需保持一致,比如:设计样式、图标和文本、元素的位置等。
126 |
127 | ... 128 |
129 |
130 |
131 | 自定义头部 132 | 133 | 134 | 137 |
与现实生活一致:与现实生活的流程、逻辑保持一致,遵循用户习惯的语言和概念;
138 |
在界面中一致:所有的元素和结构需保持一致,比如:设计样式、图标和文本、元素的位置等。
139 |
140 | ... 141 |
142 |
` -------------------------------------------------------------------------------- /lib/collapse.js: -------------------------------------------------------------------------------- 1 | !function(e,t){"object"==typeof exports&&"object"==typeof module?module.exports=t():"function"==typeof define&&define.amd?define([],t):"object"==typeof exports?exports.collapse=t():e.collapse=t()}(this,function(){return function(e){function t(o){if(n[o])return n[o].exports;var r=n[o]={i:o,l:!1,exports:{}};return e[o].call(r.exports,r,r.exports,t),r.l=!0,r.exports}var n={};return t.m=e,t.c=n,t.i=function(e){return e},t.d=function(e,n,o){t.o(e,n)||Object.defineProperty(e,n,{configurable:!1,enumerable:!0,get:o})},t.n=function(e){var n=e&&e.__esModule?function(){return e.default}:function(){return e};return t.d(n,"a",n),n},t.o=function(e,t){return Object.prototype.hasOwnProperty.call(e,t)},t.p="",t(t.s=6)}([function(e,t){e.exports=function(e,t,n,o,r){var i,a=e=e||{},s=typeof e.default;"object"!==s&&"function"!==s||(i=e,a=e.default);var l="function"==typeof a?a.options:a;t&&(l.render=t.render,l.staticRenderFns=t.staticRenderFns),o&&(l._scopeId=o);var c;if(r?(c=function(e){e=e||this.$vnode&&this.$vnode.ssrContext||this.parent&&this.parent.$vnode&&this.parent.$vnode.ssrContext,e||"undefined"==typeof __VUE_SSR_CONTEXT__||(e=__VUE_SSR_CONTEXT__),n&&n.call(this,e),e&&e._registeredComponents&&e._registeredComponents.add(r)},l._ssrRegister=c):n&&(c=n),c){var u=l.functional,d=u?l.render:l.beforeCreate;u?l.render=function(e,t){return c.call(t),d(e,t)}:l.beforeCreate=d?[].concat(d,c):[c]}return{esModule:i,exports:a,options:l}}},function(e,t,n){var o=n(9);"string"==typeof o&&(o=[[e.i,o,""]]);var r={};r.transform=void 0;n(11)(o,r);o.locals&&(e.exports=o.locals)},function(e,t,n){var o=n(0)(n(4),n(14),null,null,null);o.options.__file="F:\\DailyLearning\\collapse\\src\\components\\collapse-item.vue",o.esModule&&Object.keys(o.esModule).some(function(e){return"default"!==e&&"__"!==e.substr(0,2)})&&console.error("named exports are not supported in *.vue files."),o.options.functional&&console.error("[vue-loader] collapse-item.vue: functional components are not supported with templates, they should use render functions."),e.exports=o.exports},function(e,t,n){var o=n(0)(n(5),n(13),null,null,null);o.options.__file="F:\\DailyLearning\\collapse\\src\\components\\collapse.vue",o.esModule&&Object.keys(o.esModule).some(function(e){return"default"!==e&&"__"!==e.substr(0,2)})&&console.error("named exports are not supported in *.vue files."),o.options.functional&&console.error("[vue-loader] collapse.vue: functional components are not supported with templates, they should use render functions."),e.exports=o.exports},function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0});var o=n(7);t.default={name:"VmCollapseItem",componentName:"VmCollapseItem",components:{"vm-collapse-transition":o.a},props:{title:String,name:{type:[String,Number],default:function(){return this._uid}}},computed:{isActive:function(){return this.$parent.activeNames.indexOf(this.name)>-1},transform:function(){var e=this.findParentComponentByName("VmCollapse");return e&&e.transform},remain:function(){var e=this.findParentComponentByName("VmCollapse");return e&&e.remain},closable:function(){var e=this.findParentComponentByName("VmCollapse");return e&&e.closable}},methods:{handleClick:function(e){var t=this.$parent.activeNames.length;this.remain&&t>=this.remain&&this.isActive&&!this.closable||this.dispatch("VmCollapse","item-click",this)},dispatch:function(e,t,n){var o=this.findParentComponentByName(e);o&&o.$emit.apply(o,[t].concat(n))},findParentComponentByName:function(e){for(var t=this.$parent||this.$root,n=t.$options.componentName;t&&(!n||n!==e);)(t=t.$parent)&&(n=t.$options.componentName);return t}}}},function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.default={name:"VmCollapse",componentName:"VmCollapse",props:{accordion:Boolean,transform:Boolean,closable:Boolean,remain:{type:Number,default:0},value:{type:[Array,String,Number],default:function(){return[]}}},data:function(){return{activeNames:[].concat(this.value)}},watch:{value:function(e){this.activeNames=[].concat(e)}},methods:{setActiveNames:function(e){e=[].concat(e);var t=this.accordion?e[0]:e;this.activeNames=e,this.$emit("input",t),this.$emit("change",t)},handleItemClick:function(e){if(this.accordion)this.setActiveNames(!this.activeNames[0]&&0!==this.activeNames[0]||this.activeNames[0]!==e.name?e.name:"");else{var t=this.activeNames.slice(0),n=t.indexOf(e.name);n>-1?t.splice(n,1):(t.push(e.name),this.remain&&t.length>this.remain&&t.shift()),this.setActiveNames(t)}}},created:function(){this.$on("item-click",this.handleItemClick)}}},function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0});var o=n(3),r=n.n(o),i=n(2),a=n.n(i),s=n(1);n.n(s);n.d(t,"VmCollapse",function(){return r.a}),n.d(t,"VmCollapseItem",function(){return a.a});var l=function e(t){arguments.length>1&&void 0!==arguments[1]&&arguments[1];e.installed||(t.component(r.a.name,r.a),t.component(a.a.name,a.a))};"undefined"!=typeof window&&window.Vue&&l(window.Vue),t.default={install:l,VmCollapse:r.a,VmCollapseItem:a.a}},function(e,t,n){"use strict";function o(e,t){if(!(e instanceof t))throw new TypeError("Cannot call a class as a function")}var r=n(8),i=function(){function e(e,t){for(var n=0;n-1}function i(e,t){if(e){for(var n=e.className,o=(t||"").split(" "),i=0,a=o.length;i .vm-collapse-item__header .vm-collapse-icon--arrow-right {\n top: 6px;\n transform: rotate(90deg);\n}\n.vm-collapse-item:last-child {\n margin-bottom: -1px;\n}\n.vm-collapse-icon--arrow-right {\n position: relative;\n top: 2px;\n display: inline-block;\n width: 14px;\n height: 14px;\n transform: rotate(0deg);\n}\n.vm-collapse-icon--arrow-right:after {\n content: "";\n display: block;\n width: 0;\n height: 0;\n border: 7px solid transparent;\n border-left-color: #48576a;\n}\n.vm-collapse .collapse-transition {\n transition: 0.3s height ease-in-out, 0.3s padding-top ease-in-out, 0.3s padding-bottom ease-in-out;\n}\n',""])},function(e,t){function n(e,t){var n=e[1]||"",r=e[3];if(!r)return n;if(t&&"function"==typeof btoa){var i=o(r);return[n].concat(r.sources.map(function(e){return"/*# sourceURL="+r.sourceRoot+e+" */"})).concat([i]).join("\n")}return[n].join("\n")}function o(e){return"/*# sourceMappingURL=data:application/json;charset=utf-8;base64,"+btoa(unescape(encodeURIComponent(JSON.stringify(e))))+" */"}e.exports=function(e){var t=[];return t.toString=function(){return this.map(function(t){var o=n(t,e);return t[2]?"@media "+t[2]+"{"+o+"}":o}).join("")},t.i=function(e,n){"string"==typeof e&&(e=[[null,e,""]]);for(var o={},r=0;r=0&&b.splice(t,1)}function s(e){var t=document.createElement("style");return e.attrs.type="text/css",c(t,e.attrs),i(e,t),t}function l(e){var t=document.createElement("link");return e.attrs.type="text/css",e.attrs.rel="stylesheet",c(t,e.attrs),i(e,t),t}function c(e,t){Object.keys(t).forEach(function(n){e.setAttribute(n,t[n])})}function u(e,t){var n,o,r,i;if(t.transform&&e.css){if(!(i=t.transform(e.css)))return function(){};e.css=i}if(t.singleton){var c=y++;n=g||(g=s(t)),o=d.bind(null,n,c,!1),r=d.bind(null,n,c,!0)}else e.sourceMap&&"function"==typeof URL&&"function"==typeof URL.createObjectURL&&"function"==typeof URL.revokeObjectURL&&"function"==typeof Blob&&"function"==typeof btoa?(n=l(t),o=p.bind(null,n,t),r=function(){a(n),n.href&&URL.revokeObjectURL(n.href)}):(n=s(t),o=f.bind(null,n),r=function(){a(n)});return o(e),function(t){if(t){if(t.css===e.css&&t.media===e.media&&t.sourceMap===e.sourceMap)return;o(e=t)}else r()}}function d(e,t,n,o){var r=n?"":o.css;if(e.styleSheet)e.styleSheet.cssText=x(t,r);else{var i=document.createTextNode(r),a=e.childNodes;a[t]&&e.removeChild(a[t]),a.length?e.insertBefore(i,a[t]):e.appendChild(i)}}function f(e,t){var n=t.css,o=t.media;if(o&&e.setAttribute("media",o),e.styleSheet)e.styleSheet.cssText=n;else{for(;e.firstChild;)e.removeChild(e.firstChild);e.appendChild(document.createTextNode(n))}}function p(e,t,n){var o=n.css,r=n.sourceMap,i=void 0===t.convertToAbsoluteUrls&&r;(t.convertToAbsoluteUrls||i)&&(o=_(o)),r&&(o+="\n/*# sourceMappingURL=data:application/json;base64,"+btoa(unescape(encodeURIComponent(JSON.stringify(r))))+" */");var a=new Blob([o],{type:"text/css"}),s=e.href;e.href=URL.createObjectURL(a),s&&URL.revokeObjectURL(s)}var m={},v=function(e){var t;return function(){return void 0===t&&(t=e.apply(this,arguments)),t}}(function(){return window&&document&&document.all&&!window.atob}),h=function(e){var t={};return function(n){return void 0===t[n]&&(t[n]=e.call(this,n)),t[n]}}(function(e){return document.querySelector(e)}),g=null,y=0,b=[],_=n(12);e.exports=function(e,t){if("undefined"!=typeof DEBUG&&DEBUG&&"object"!=typeof document)throw new Error("The style-loader cannot be used in a non-browser environment");t=t||{},t.attrs="object"==typeof t.attrs?t.attrs:{},t.singleton||(t.singleton=v()),t.insertInto||(t.insertInto="head"),t.insertAt||(t.insertAt="bottom");var n=r(e,t);return o(n,t),function(e){for(var i=[],a=0;a 2 |
3 |
4 | 5 | 6 | {{title}} 7 | 8 |
9 | 10 |
11 |
12 | 13 |
14 |
15 |
16 |
17 | 18 | 92 | -------------------------------------------------------------------------------- /src/components/collapse.vue: -------------------------------------------------------------------------------- 1 | 6 | 80 | -------------------------------------------------------------------------------- /src/index.js: -------------------------------------------------------------------------------- 1 | import VmCollapse from './components/collapse.vue' 2 | import VmCollapseItem from './components/collapse-item.vue' 3 | import './styles/less/index.less' 4 | 5 | const install = function (Vue, opts = {}) { 6 | if (install.installed) return 7 | Vue.component(VmCollapse.name, VmCollapse) 8 | Vue.component(VmCollapseItem.name, VmCollapseItem) 9 | } 10 | 11 | if (typeof window !== 'undefined' && window.Vue) { 12 | install(window.Vue) 13 | } 14 | 15 | export { 16 | VmCollapse, 17 | VmCollapseItem 18 | } 19 | 20 | export default { 21 | install, 22 | VmCollapse, 23 | VmCollapseItem 24 | } -------------------------------------------------------------------------------- /src/styles/less/base.less: -------------------------------------------------------------------------------- 1 | @css-prefix: ~"vm-"; 2 | 3 | @collaspe-border-color: #dfe6ec; 4 | @collapse-border-radius: 0; 5 | 6 | @collapse-header-height: 43px; 7 | @collapse-header-fill: #fff; 8 | @collapse-header-color: #48576a; 9 | @collapse-header-size: 13px; 10 | @collapse-border-color: #dfe6ec; 11 | 12 | @collapse-content-fill: #fbfdff; 13 | @collapse-content-size: 13px; 14 | @collapse-content-color: #1f2d3d; -------------------------------------------------------------------------------- /src/styles/less/collapse.less: -------------------------------------------------------------------------------- 1 | @collapse-css-prefix-cls: ~"@{css-prefix}collapse"; 2 | 3 | .@{collapse-css-prefix-cls} { 4 | border-radius: @collapse-border-radius; 5 | &--bordered { 6 | border: 1px solid @collaspe-border-color; 7 | } 8 | &-item { 9 | &__header { 10 | height: @collapse-header-height; 11 | padding-left: 15px; 12 | font-size: @collapse-header-size; 13 | line-height: @collapse-header-height; 14 | color: @collapse-header-color; 15 | background-color: @collapse-header-fill; 16 | border-bottom: 1px solid @collapse-border-color; 17 | cursor: pointer; 18 | 19 | &__arrow { 20 | transition: transform .3s, top .3s; 21 | } 22 | } 23 | &__wrap { 24 | will-change: height; 25 | background-color: @collapse-content-fill; 26 | border-bottom: 1px solid @collapse-border-color; 27 | overflow: hidden; 28 | box-sizing: border-box; 29 | } 30 | &__content { 31 | padding: 10px 15px; 32 | font-size: @collapse-content-size; 33 | color: @collapse-content-color; 34 | line-height: 1.769230769230769; 35 | } 36 | &.is-active { 37 | > .vm-collapse-item__header { 38 | .vm-collapse-icon--arrow-right { 39 | top: 6px; 40 | transform: rotate(90deg); 41 | } 42 | } 43 | } 44 | &:last-child { 45 | margin-bottom: -1px; 46 | } 47 | } 48 | &-icon--arrow-right { 49 | position: relative; 50 | top: 2px; 51 | display: inline-block; 52 | width: 14px; 53 | height: 14px; 54 | transform: rotate(0deg); 55 | &:after { 56 | content: ""; 57 | display: block; 58 | width: 0; 59 | height: 0; 60 | border: 7px solid transparent; 61 | border-left-color: #48576a; 62 | } 63 | } 64 | .collapse-transition { 65 | transition: 0.3s height ease-in-out, 0.3s padding-top ease-in-out, 0.3s padding-bottom ease-in-out; 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /src/styles/less/index.less: -------------------------------------------------------------------------------- 1 | @import "base"; 2 | @import "collapse"; -------------------------------------------------------------------------------- /src/transitions/collapse-transition.js: -------------------------------------------------------------------------------- 1 | import { addClass, removeClass } from '../utils/dom' 2 | 3 | class Transition { 4 | beforeEnter (el) { 5 | addClass(el, 'collapse-transition') 6 | if (!el.dataset) el.dataset = {} 7 | 8 | el.dataset.oldPaddingTop = el.style.paddingTop 9 | el.dataset.oldPaddingBottom = el.style.paddingBottom 10 | 11 | el.style.height = 0 12 | el.style.paddingTop = 0 13 | el.style.paddingBottom = 0 14 | } 15 | 16 | enter (el) { 17 | el.dataset.oldOverflow = el.style.overflow 18 | if (el.scrollHeight !== 0) { 19 | el.style.height = el.scrollHeight + 'px' 20 | el.style.paddingTop = el.dataset.oldPaddingTop 21 | el.style.paddingBottom = el.dataset.oldPaddingBottom 22 | } else { 23 | el.style.height = '' 24 | el.style.paddingTop = el.dataset.oldPaddingTop 25 | el.style.paddingBottom = el.dataset.oldPaddingBottom 26 | } 27 | el.style.overflow = 'hidden' 28 | } 29 | 30 | afterEnter (el) { 31 | removeClass(el, 'collapse-transition') 32 | el.style.height = '' 33 | el.style.overflow = el.dataset.oldOverflow 34 | } 35 | 36 | beforeLeave (el) { 37 | if (!el.dataset) el.dataset = {} 38 | el.dataset.oldPaddingTop = el.style.paddingTop 39 | el.dataset.oldPaddingBottom = el.style.paddingBottom 40 | el.dataset.oldOverflow = el.style.overflow 41 | 42 | el.style.height = el.scrollHeight + 'px' 43 | el.style.overflow = 'hidden' 44 | } 45 | 46 | leave (el) { 47 | if (el.scrollHeight !== 0) { 48 | addClass(el, 'collapse-transition') 49 | el.style.height = 0 50 | el.style.paddingTop = 0 51 | el.style.paddingBottom = 0 52 | } 53 | } 54 | 55 | afterLeave (el) { 56 | removeClass(el, 'collapse-transition') 57 | el.style.height = '' 58 | el.style.overflow = el.dataset.oldOverflow 59 | el.style.paddingTop = el.dataset.oldPaddingTop 60 | el.style.paddingBottom = el.dataset.oldPaddingBottom 61 | } 62 | } 63 | 64 | export default { 65 | name: 'VmCollapseTransition', 66 | functional: true, 67 | render (h, context) { 68 | const transition = new Transition() 69 | const data = { 70 | on: { 71 | 'before-enter': transition.beforeEnter, 72 | 'enter': transition.enter, 73 | 'after-enter': transition.afterEnter, 74 | 'before-leave': transition.beforeLeave, 75 | 'leave': transition.leave, 76 | 'after-leave': transition.afterLeave 77 | } 78 | } 79 | 80 | return h('transition', data, context.children) 81 | } 82 | } 83 | -------------------------------------------------------------------------------- /src/utils/dom.js: -------------------------------------------------------------------------------- 1 | export function trim (string) { // BOM 2 | return (string || '').replace(/^[\s\uFEFF]+|[\s\uFEFF]+/g, '') 3 | } 4 | 5 | export function hasClass (el, cls) { 6 | if (!el || !cls) return false 7 | if (cls.indexOf(' ') !== -1) throw new Error('className should not contain space.') 8 | if (el.classList) { 9 | return el.classList.contains(cls) 10 | } else { 11 | return (' ' + el.className + ' ').indexOf(' ' + cls + ' ') > -1 12 | } 13 | } 14 | 15 | export function addClass (el, cls) { 16 | if (!el) return 17 | let curClass = el.className 18 | let classes = (cls || '').split(' ') 19 | 20 | for (let i = 0, len = classes.length; i < len; i++) { 21 | let clsName = classes[i] 22 | if (!clsName) continue 23 | 24 | if (el.classList) { 25 | el.classList.add(clsName) 26 | } else { 27 | if (!hasClass(el, clsName)) { 28 | curClass += ' ' + clsName 29 | } 30 | } 31 | } 32 | if (!el.classList) { 33 | el.className = curClass 34 | } 35 | } 36 | 37 | export function removeClass (el, cls) { 38 | if (!el || !cls) return 39 | let curClass = ' ' + el.className + ' ' 40 | let classes = cls.split(' ') 41 | 42 | for (let i = 0, len = classes.length; i < len; i++) { 43 | let clsName = classes[i] 44 | if (!clsName) continue 45 | 46 | if (el.classList) { 47 | el.classList.remove(clsName) 48 | } else { 49 | if (hasClass(el, clsName)) { 50 | curClass = curClass.replace(' ' + clsName + ' ', ' ') 51 | } 52 | } 53 | } 54 | if (!el.classList) { 55 | el.className = trim(curClass) 56 | } 57 | } 58 | --------------------------------------------------------------------------------