Full Code of NewDadaFE/vue-impression for AI

master 6ff2b38f2a07 cached
204 files
327.8 KB
81.5k tokens
28 symbols
1 requests
Download .txt
Showing preview only (375K chars total). Download the full file or copy to clipboard to get everything.
Repository: NewDadaFE/vue-impression
Branch: master
Commit: 6ff2b38f2a07
Files: 204
Total size: 327.8 KB

Directory structure:
gitextract_vku1hzea/

├── .babelrc
├── .editorconfig
├── .eslintignore
├── .eslintrc.js
├── .gitignore
├── .npmignore
├── .scss-lint.yml
├── README.md
├── _config.yml
├── gulpfile.js
├── index.html
├── package.json
├── src/
│   ├── scripts/
│   │   ├── components/
│   │   │   ├── Alert.vue
│   │   │   ├── BackToTop.vue
│   │   │   ├── Badge.vue
│   │   │   ├── Button.vue
│   │   │   ├── Card.vue
│   │   │   ├── CardBody.vue
│   │   │   ├── Cell.vue
│   │   │   ├── Checkbox.vue
│   │   │   ├── CheckboxGroup.vue
│   │   │   ├── DatePicker/
│   │   │   │   ├── DateRange.vue
│   │   │   │   ├── DateTable.vue
│   │   │   │   ├── Picker.vue
│   │   │   │   └── index.js
│   │   │   ├── Drawer.vue
│   │   │   ├── DrawerItem.vue
│   │   │   ├── Flex.vue
│   │   │   ├── FlexItem.vue
│   │   │   ├── Group.vue
│   │   │   ├── GroupTitle.vue
│   │   │   ├── HRule.vue
│   │   │   ├── Icon.vue
│   │   │   ├── InlineSelector.vue
│   │   │   ├── InlineSelectorOption.vue
│   │   │   ├── InputNumber.vue
│   │   │   ├── InputText.vue
│   │   │   ├── InputTextarea.vue
│   │   │   ├── Loading.vue
│   │   │   ├── Loadmore.vue
│   │   │   ├── Mask.vue
│   │   │   ├── Media.vue
│   │   │   ├── MediaBody.vue
│   │   │   ├── MediaObject.vue
│   │   │   ├── Navbar.vue
│   │   │   ├── Navigation.vue
│   │   │   ├── NavigationItem.vue
│   │   │   ├── Picker.vue
│   │   │   ├── PickerOption.vue
│   │   │   ├── Progressbar.vue
│   │   │   ├── Progressbars.vue
│   │   │   ├── Radio.vue
│   │   │   ├── RadioGroup.vue
│   │   │   ├── Searchbar.vue
│   │   │   ├── SearchbarBtn.vue
│   │   │   ├── SearchbarPlaceholder.vue
│   │   │   ├── SegmentedControl.vue
│   │   │   ├── SegmentedControlItem.vue
│   │   │   ├── Selector.vue
│   │   │   ├── SelectorOption.vue
│   │   │   ├── Sidelip.vue
│   │   │   ├── SlideUp.vue
│   │   │   ├── SlideUpBody.vue
│   │   │   ├── SlideUpHeader.vue
│   │   │   ├── Stepbar.vue
│   │   │   ├── StepbarItem.vue
│   │   │   ├── Sticky.vue
│   │   │   ├── Swipe.vue
│   │   │   ├── SwipeItem.vue
│   │   │   ├── Tabbar.vue
│   │   │   ├── TabbarItem.vue
│   │   │   ├── Tag.vue
│   │   │   ├── Timeline.vue
│   │   │   ├── TimelineItem.vue
│   │   │   ├── Tip.vue
│   │   │   ├── Toast.vue
│   │   │   ├── Toggle.vue
│   │   │   └── index.js
│   │   ├── containers/
│   │   │   ├── layout.vue
│   │   │   └── menubar.vue
│   │   ├── directives/
│   │   │   └── disfavor.js
│   │   ├── index.js
│   │   ├── mixins/
│   │   │   ├── emitter.js
│   │   │   ├── select.js
│   │   │   ├── selectOption.js
│   │   │   ├── sync.js
│   │   │   ├── tab.js
│   │   │   └── tabItem.js
│   │   ├── router.js
│   │   ├── routes.json
│   │   ├── utils/
│   │   │   ├── alert.js
│   │   │   ├── cssPrefix.js
│   │   │   ├── date.js
│   │   │   ├── dateUtil.js
│   │   │   ├── dom.js
│   │   │   ├── draggable.js
│   │   │   ├── easing.js
│   │   │   ├── loading.js
│   │   │   ├── toast.js
│   │   │   ├── translate.js
│   │   │   └── type.js
│   │   └── views/
│   │       ├── alert.vue
│   │       ├── back-to-top.vue
│   │       ├── badge.vue
│   │       ├── button.vue
│   │       ├── card.vue
│   │       ├── cell.vue
│   │       ├── checkbox.vue
│   │       ├── confirm.vue
│   │       ├── datepicker.vue
│   │       ├── drawer.vue
│   │       ├── flex.vue
│   │       ├── hrule.vue
│   │       ├── icon.vue
│   │       ├── index.vue
│   │       ├── inline-selecor.vue
│   │       ├── input-number.vue
│   │       ├── input-text.vue
│   │       ├── input-textarea.vue
│   │       ├── loading.vue
│   │       ├── media.vue
│   │       ├── navbar.vue
│   │       ├── navigation.vue
│   │       ├── picker.vue
│   │       ├── progressbar.vue
│   │       ├── pull-down.vue
│   │       ├── pull-up.vue
│   │       ├── radio.vue
│   │       ├── searchbar.vue
│   │       ├── segmented-control.vue
│   │       ├── selector.vue
│   │       ├── sideslip.vue
│   │       ├── slideup.vue
│   │       ├── stepbar.vue
│   │       ├── sticky.vue
│   │       ├── swipe.vue
│   │       ├── tabbar.vue
│   │       ├── tag.vue
│   │       ├── timeline.vue
│   │       ├── tip.vue
│   │       ├── toast.vue
│   │       └── toggle.vue
│   └── styles/
│       ├── animate.scss
│       ├── index.scss
│       ├── mixins/
│       │   ├── border.scss
│       │   ├── button.scss
│       │   ├── media.scss
│       │   ├── navbar.scss
│       │   ├── progressbar.scss
│       │   ├── tag.scss
│       │   └── text.scss
│       ├── mixins.scss
│       ├── modules/
│       │   ├── alert.scss
│       │   ├── back-to-top.scss
│       │   ├── badge.scss
│       │   ├── button.scss
│       │   ├── card.scss
│       │   ├── cell.scss
│       │   ├── checkbox.scss
│       │   ├── date-picker.scss
│       │   ├── drawer.scss
│       │   ├── group.scss
│       │   ├── hrule.scss
│       │   ├── inline-selector.scss
│       │   ├── input-number.scss
│       │   ├── input-text.scss
│       │   ├── input-textarea.scss
│       │   ├── loading.scss
│       │   ├── loadmore.scss
│       │   ├── mask.scss
│       │   ├── media.scss
│       │   ├── navbar.scss
│       │   ├── navigation.scss
│       │   ├── picker.scss
│       │   ├── progressbar.scss
│       │   ├── searchbar.scss
│       │   ├── segmented-control.scss
│       │   ├── selector.scss
│       │   ├── sidelip.scss
│       │   ├── slideup.scss
│       │   ├── stepbar.scss
│       │   ├── sticky.scss
│       │   ├── swipe.scss
│       │   ├── tabbar.scss
│       │   ├── tag.scss
│       │   ├── timeline.scss
│       │   ├── tip.scss
│       │   ├── toast.scss
│       │   └── toggle.scss
│       ├── modules.scss
│       ├── utils/
│       │   ├── background.scss
│       │   ├── border.scss
│       │   ├── clearfix.scss
│       │   ├── display.scss
│       │   ├── flex.scss
│       │   ├── img.scss
│       │   ├── reboot.scss
│       │   ├── spacing.scss
│       │   └── text.scss
│       ├── utils.scss
│       └── variables.scss
├── webpack.dev.config.js
├── webpack.prebuilt.config.js
└── webpack.prod.config.js

================================================
FILE CONTENTS
================================================

================================================
FILE: .babelrc
================================================
{
    "presets": ["vue", ["es2015", { "modules": false }], "stage-1"],
    "comments": false,
    "plugins": [
        "transform-runtime",
        "transform-object-assign"
    ]
}


================================================
FILE: .editorconfig
================================================
root = true

[*]
charset = utf-8
indent_style = space
indent_size = 4
end_of_line = lf
insert_final_newline = true
trim_trailing_whitespace = true

[*.{json,yml}]
trim_trailing_whitespace = false



================================================
FILE: .eslintignore
================================================
build.js
webpack.*.js


================================================
FILE: .eslintrc.js
================================================
module.exports = {
    extends: 'vue-impression',
    rules: {
        'import/no-extraneous-dependencies': 0,
    }
};


================================================
FILE: .gitignore
================================================
/node_modules
/site/build
/site/node_modules
/site/.sass-cache
/site/src/scripts/components/impression
/site/src/styles/impression
/build
/cache
*.log
/.idea
/.sass-cache
scss-lint-report.xml
/lib
.DS_Store
/dist


================================================
FILE: .npmignore
================================================
/build
/site
/.sass-cache
.DS_Store
Makefile
.babelrc
.editorconfig
.eslintignore
.eslintrc.js
.scss-lint.yml
index.todo
/src
webpack.*.js
*.log
_config.yml
build.js
gulpfile.js
index.html


================================================
FILE: .scss-lint.yml
================================================
scss_files: "src/styles/**/*.scss"
exclude: ['src/styles/font-awesome/**', 'src/styles/modules/_normalize.scss', 'src/styles/modules/_reboot.scss']
plugin_directories: ['.scss-linters']

# List of gem names to load custom linters from (make sure they are already
# installed)
plugin_gems: []

# Default severity of all linters.
severity: warning

linters:
  BangFormat:
    enabled: true
    space_before_bang: true
    space_after_bang: false

  BemDepth:
    enabled: false
    max_elements: 1

  BorderZero:
    enabled: true
    convention: zero # or `none`

  ChainedClasses:
    enabled: false

  ColorKeyword:
    enabled: true

  ColorVariable:
    enabled: true

  Comment:
    enabled: true
    style: silent

  DebugStatement:
    enabled: true

  DeclarationOrder:
    enabled: false

  DisableLinterReason:
    enabled: false

  DuplicateProperty:
    enabled: true

  ElsePlacement:
    enabled: true
    style: same_line # or 'new_line'

  EmptyLineBetweenBlocks:
    enabled: true
    ignore_single_line_blocks: true

  EmptyRule:
    enabled: true

  ExtendDirective:
    enabled: false

  FinalNewline:
    enabled: true
    present: true

  HexLength:
    enabled: true
    style: short # or 'long'

  HexNotation:
    enabled: true
    style: lowercase # or 'uppercase'

  HexValidation:
    enabled: true

  IdSelector:
    enabled: true

  ImportantRule:
    enabled: false

  ImportPath:
    enabled: true
    leading_underscore: false
    filename_extension: false

  Indentation:
    enabled: true
    allow_non_nested_indentation: false
    character: space # or 'tab'
    width: 4

  LeadingZero:
    enabled: true
    style: exclude_zero # or 'include_zero'

  MergeableSelector:
    enabled: true
    force_nesting: true

  NameFormat:
    enabled: true
    allow_leading_underscore: true
    convention: hyphenated_lowercase # or 'camel_case', or 'snake_case', or a regex pattern

  NestingDepth:
    enabled: true
    max_depth: 6
    ignore_parent_selectors: false

  PlaceholderInExtend:
    enabled: false

  PrivateNamingConvention:
    enabled: false
    prefix: _

  PropertyCount:
    enabled: false
    include_nested: false
    max_properties: 10

  PropertySortOrder:
    enabled: false
    ignore_unspecified: false
    min_properties: 2
    separate_groups: false

  PropertySpelling:
    enabled: true
    extra_properties: []
    disabled_properties: []

  PropertyUnits:
    enabled: true
    global: [
      'ch', 'em', 'ex', 'rem',                 # Font-relative lengths
      'cm', 'in', 'mm', 'pc', 'pt', 'px', 'q', # Absolute lengths
      'vh', 'vw', 'vmin', 'vmax',              # Viewport-percentage lengths
      'deg', 'grad', 'rad', 'turn',            # Angle
      'ms', 's',                               # Duration
      'Hz', 'kHz',                             # Frequency
      'dpi', 'dpcm', 'dppx',                   # Resolution
      '%']                                     # Other
    properties: {}

  PseudoElement:
    enabled: true

  QualifyingElement:
    enabled: false
    allow_element_with_attribute: false
    allow_element_with_class: false
    allow_element_with_id: false

  SelectorDepth:
    enabled: true
    max_depth: 4

  SelectorFormat:
    enabled: true
    convention: hyphenated_lowercase # or 'strict_BEM', or 'hyphenated_BEM', or 'snake_case', or 'camel_case', or a regex pattern

  Shorthand:
    enabled: true
    allowed_shorthands: [1, 2, 3, 4]

  SingleLinePerProperty:
    enabled: true
    allow_single_line_rule_sets: true

  SingleLinePerSelector:
    enabled: true

  SpaceAfterComma:
    enabled: true
    style: one_space # or 'no_space', or 'at_least_one_space'

  SpaceAfterPropertyColon:
    enabled: true
    style: one_space # or 'no_space', or 'at_least_one_space', or 'aligned'

  SpaceAfterPropertyName:
    enabled: true

  SpaceAfterVariableColon:
    enabled: false
    style: one_space # or 'no_space', 'at_least_one_space' or 'one_space_or_newline'

  SpaceAfterVariableName:
    enabled: true

  SpaceAroundOperator:
    enabled: true
    style: one_space # or 'at_least_one_space', or 'no_space'

  SpaceBeforeBrace:
    enabled: true
    style: space # or 'new_line'
    allow_single_line_padding: false

  SpaceBetweenParens:
    enabled: true
    spaces: 0

  StringQuotes:
    enabled: true
    style: single_quotes # or double_quotes

  TrailingSemicolon:
    enabled: true

  TrailingWhitespace:
    enabled: true

  TrailingZero:
    enabled: false

  TransitionAll:
    enabled: false

  UnnecessaryMantissa:
    enabled: true

  UnnecessaryParentReference:
    enabled: true

  UrlFormat:
    enabled: true

  UrlQuotes:
    enabled: true

  VariableForProperty:
    enabled: false
    properties: []

  VendorPrefix:
    enabled: true
    identifier_list: base
    additional_identifiers: []
    excluded_identifiers: []

  ZeroUnit:
    enabled: true

  Compass::*:
    enabled: false


================================================
FILE: README.md
================================================
# vue-impression

A Vue.js 2.0 UI elements for mobile.

## Demo

https://newdadafe.github.io/impression_vue/#/button

## Installation

```sh
yarn add vue-impression
```

## Usage

styles:

```scss
@import '~vue-impression/dist/styles/index.scss';
```

scripts:

```js
import Vue from 'vue';
import VueImpression from 'vue-impression';

Vue.use(VueImpression);
```

tree-shaking:

```sh
yarn add babel-plugin-transform-imports -D
```

```json
{
    "plugins": [
        [
            "transform-imports",
            {
                "vue-impression": {
                    "transform": "vue-impression/dist/scripts/components/${member}",
                    "preventFullImport": true
                }
            }
        ]
    ]
}
```

## Example

```html
<btn theme="primary" size="sm" @click="doSomething">按钮</btn>
```

## Components

-   [x] Button
-   [x] Group
-   [x] GroupTitle
-   [x] Cell
-   [x] Flex
-   [x] FlexItem
-   [x] Icon
-   [x] Navbar
-   [x] Navigation
-   [x] Tabbar
-   [x] Drawer
-   [x] Loading
-   [x] Alert
-   [x] Toast
-   [x] Radio
-   [x] RadioGroup
-   [x] Checkbox
-   [x] CheckboxGroup
-   [x] Toggle(Switch)
-   [x] InputNumber
-   [x] InputText
-   [x] InputArea
-   [x] Selector
-   [x] Tag
-   [x] Badge
-   [x] Tip
-   [x] HRule(Hr)
-   [x] InlineSelector
-   [x] Swipe
-   [x] SlideUp
-   [x] SegmentedControl
-   [x] Media
-   [x] Card
-   [x] Picker
-   [x] DatePicker
-   [x] Search
-   [x] BackToTop
-   [x] Pull down
-   [x] Pull up
-   [x] Sideslip
-   [x] Progressbar
-   [x] Stepbar
-   [x] Timeline
-   [x] Sticky

## Quick start

[generator-vue-impression](https://github.com/NewDadaFE/generator/tree/master/packages/generator-vue-impression)


================================================
FILE: _config.yml
================================================
theme: jekyll-theme-cayman

================================================
FILE: gulpfile.js
================================================
const fs = require('fs-extra');
const gulp = require('gulp');
const minimist = require('minimist');
const plugin = require('gulp-load-plugins')();
const replaceExt = require('replace-ext');
const through = require('through2');
const { createDefaultCompiler, assemble } = require('@vue/component-compiler');

const options = minimist(process.argv.slice(2));

process.env.NODE_ENV = options.env || 'production';

const clean = () => Promise.all([fs.emptyDir('dist'), fs.emptyDir('build')]);

const copyHTML = () => fs.copy('index.html', 'build/index.html');

const copyImage = () => fs.copy('src/images', 'build/images');

const style = () => fs.copy('src/styles', 'dist/styles');

const sass = () => {
    return gulp
        .src('src/styles/index.scss')
        .pipe(plugin.sass().on('error', plugin.sass.logError))
        .pipe(plugin.autoprefixer({ browsers: ['last 30 version', '> 90%'] }))
        .pipe(plugin.cssmin())
        .pipe(gulp.dest('build/styles'));
};

const script = () => {
    const directory = ['components', 'directives', 'mixins', 'utils'];
    const compiler = createDefaultCompiler();

    return gulp
        .src(`src/scripts/?(${directory.join('|')})/**/*.{js,vue}`)
        .pipe(
            through.obj((file, encoding, callback) => {
                if(file.extname === '.vue') {
                    const result = compiler.compileToDescriptor(
                        file.basename,
                        file.contents.toString()
                    );
                    const output = assemble(compiler, file.basename, result);

                    file.contents = new Buffer(output.code);
                    file.path = replaceExt(file.path, '.js');
                }
                callback(null, file);
            })
        )
        .pipe(plugin.babel())
        .pipe(gulp.dest('dist/scripts'));
};

const watch = () => gulp.watch('src/styles/**/*.scss', sass);

const build = gulp.series(clean, gulp.parallel(copyHTML, copyImage, sass));

const release = gulp.series(clean, gulp.parallel(style, script));

module.exports = { watch, build, release };


================================================
FILE: index.html
================================================
<!DOCTYPE html>
<html>
<head>
    <meta charset="UTF-8">
    <title>vue-impression</title>
    <meta name="viewport" content="width=device-width,initial-scale=1.0,maximum-scale=1.0,minimum-scale=1.0,user-scalable=no">
    <meta name="format-detection" content="telephone=no">
    <link rel="shortcut icon" type="image/x-icon" href="images/favicon.ico">
    <link rel="stylesheet" href="styles/index.css">
    <link rel="stylesheet" href="https://fe.imdada.cn/font-awesome/4.7.0/index.css">
</head>
<body>
    <div id="app"></div>
    <script src="scripts/app.js"></script>
</body>
</html>


================================================
FILE: package.json
================================================
{
  "name": "vue-impression",
  "version": "0.21.13",
  "description": "A Vue.js 2.0 UI elements for mobile.",
  "sass": "dist/styles/index.scss",
  "main": "dist/scripts/components/index.js",
  "module": "dist/scripts/components/index.js",
  "scripts": {
    "start": "gulp build && webpack -p --config webpack.prebuilt.config.js",
    "poststart": "webpack-dev-server --config webpack.dev.config.js --colors --port 9008",
    "build:site": "gulp build && webpack -p --config webpack.prod.config.js",
    "scsslint": "scss-lint src/styles/**/*.scss",
    "eslint": "eslint ./src/scripts/",
    "build": "npm run eslint && gulp release"
  },
  "repository": {
    "type": "git",
    "url": "git+https://github.com/NewDadaFE/vue-impression.git"
  },
  "keywords": [
    "Vue 2.0",
    "UI"
  ],
  "author": "NewDadaFE",
  "license": "MIT",
  "bugs": {
    "url": "https://github.com/NewDadaFE/vue-impression/issues"
  },
  "homepage": "https://github.com/NewDadaFE/vue-impression#readme",
  "devDependencies": {
    "@vue/component-compiler": "^3.6.0",
    "ajv": "5.5.2",
    "autoprefixer": "^6.5.3",
    "babel-cli": "^6.24.1",
    "babel-core": "^6.18.2",
    "babel-eslint": "^7.1.1",
    "babel-helper-vue-jsx-merge-props": "^2.0.2",
    "babel-loader": "^6.2.8",
    "babel-plugin-syntax-jsx": "^6.18.0",
    "babel-plugin-transform-object-assign": "^6.22.0",
    "babel-plugin-transform-runtime": "^6.23.0",
    "babel-plugin-transform-vue-jsx": "^3.2.0",
    "babel-preset-es2015": "^6.18.0",
    "babel-preset-stage-1": "^6.24.1",
    "babel-preset-vue": "^0.1.0",
    "babel-register": "^6.18.0",
    "chalk": "^1.1.3",
    "connect-history-api-fallback": "^1.3.0",
    "css-loader": "^0.26.0",
    "eslint": "^3.10.2",
    "eslint-config-vue-impression": "2.1.6",
    "eslint-friendly-formatter": "^2.0.6",
    "eslint-import-resolver-webpack": "^0.7.0",
    "eslint-loader": "^1.6.1",
    "eslint-plugin-import": "^2.2.0",
    "extract-text-webpack-plugin": "2.1.2",
    "file-loader": "^0.9.0",
    "fs-extra": "^7.0.0",
    "gulp": "^4.0.0",
    "gulp-autoprefixer": "^3.1.1",
    "gulp-babel": "7.0.1",
    "gulp-cssmin": "^0.1.7",
    "gulp-imagemin": "^3.1.1",
    "gulp-load-plugins": "^1.5.0",
    "gulp-minify-css": "^1.2.4",
    "gulp-rename": "^1.2.2",
    "gulp-sass": "4.0.1",
    "html-webpack-plugin": "^2.24.1",
    "json-loader": "^0.5.4",
    "minimist": "^1.2.0",
    "postcss": "6.0.23",
    "replace-ext": "^1.0.0",
    "through2": "^2.0.3",
    "url-loader": "^0.5.7",
    "vue-html-loader": "^1.2.3",
    "vue-loader": "^10.0.0",
    "vue-router": "^2.1.1",
    "vue-style-loader": "^1.0.0",
    "vue-template-compiler": "^2.1.10",
    "webpack": "2.7.0",
    "webpack-dashboard": "^0.2.0",
    "webpack-dev-middleware": "^1.8.4",
    "webpack-dev-server": "^1.16.2",
    "webpack-hot-middleware": "^2.13.2",
    "webpack-merge": "^0.17.0"
  },
  "dependencies": {
    "babel-runtime": "^6.26.0",
    "vue": "^2.1.10"
  }
}


================================================
FILE: src/scripts/components/Alert.vue
================================================
<template>
    <div>
        <transition name="zoom"
            @after-leave="afterLeave">
            <div
                class="alert"
                v-show="currentValue"
                @click.self="maskClosable && (currentValue = false)"
                @touchmove.prevent.stop>
                <div class="alert-modal">
                    <div class="alert-title"  v-html="title">
                    </div>
                    <div class="alert-message" v-html="message"></div>
                    <div class="alert-footer" :class="{'alert-footer-vertical': vertical}">
                        <div
                            v-for="btn in btns"
                            class="alert-btn"
                            @click="clickHandle(btn.click)"
                            v-html="btn.text">
                        </div>
                    </div>
                </div>
            </div>
        </transition>
        <mask-layer :clickable="false" />
    </div>
</template>
<script>
    export default {
        name: 'alert',
        props: {
            // 标题
            title: {
                type: String,
                default: '确认',
            },
            // 内容
            message: String,
            // 按钮垂直摆放
            vertical: {
                type: Boolean,
                default: false,
            },
            // 自动关闭
            autoClose: {
                type: Boolean,
                default: true,
            },
            // 点击mask是否可以关闭
            maskClosable: {
                type: Boolean,
                default: false,
            },
            // 按钮组
            btns: {
                type: Array,
                default() {
                    return [{
                        text: '确定',
                    }];
                },
            },
        },
        data() {
            return {
                currentValue: false,
            };
        },
        methods: {
            // 显示
            show() {
                this.currentValue = true;
            },
            // 按钮点击
            clickHandle(callback) {
                callback && callback();
                this.autoClose && (this.currentValue = false);
            },
            hide() {
                this.autoClose && (this.currentValue = false);
            },
            // 移除dom
            /* global document:true */
            afterLeave() {
                document.body.removeChild(this.$el);
            },
        },
    };
</script>


================================================
FILE: src/scripts/components/BackToTop.vue
================================================
<template>
    <div
        class="back-to-top"
        :class="{'active': active}"
        @click.prevent.stop="scrollToTopHandle">
        <icon class="back-to-top-icon" name="arrow-up" />
    </div>
</template>

<script>
    import { easeInOutCubic } from '../utils/easing';
    import { getScrollContainer } from '../utils/dom';

    export default {
        name: 'back-to-top',
        data() {
            return {
                active: false,
            };
        },
        methods: {
            /* global window:true document:true requestAnimationFrame:true */
            scrollToTopHandle() {
                let scrollTop = this.getScrollTop(),
                    startTime = Date.now();

                let frameFunc = () => {
                    let timestamp = Date.now(),
                        time = timestamp - startTime;

                    this.setScrollTop(easeInOutCubic(time, scrollTop, 0, 450));
                    if(time < 450) {
                        requestAnimationFrame(frameFunc);
                    }
                };

                requestAnimationFrame(frameFunc);
                this.active = false;
            },
            getScrollTop() {
                return this.scrollTargetEl === document
                ? window.pageYOffset : this.scrollTargetEl.scrollTop;
            },
            setScrollTop(val) {
                if(this.scrollTargetEl === document) {
                    document.body.scrollTop = val;
                    document.documentElement.scrollTop = val;

                    return;
                }

                this.scrollTargetEl.scrollTop = val;
            },
            touchStartHandle() {
                this._offsetY = this.scrollTargetEl === document
                ? window.pageYOffset : this.scrollTargetEl.scrollTop;
            },
            touchEndHandle() {
                let scrollTop = this.scrollTargetEl === document
                ? window.pageYOffset : this.scrollTargetEl.scrollTop;

                // 显示
                if(scrollTop < this._offsetY && (this._offsetY - scrollTop > 60)) {
                    this.active = true;
                } else if(Math.abs(this._offsetY - scrollTop) > 60) {
                    // 隐藏
                    this.active = false;
                }

                this._offsetY = scrollTop;
            },
        },
        mounted() {
            this.scrollTargetEl = getScrollContainer(this.$el);

            this.scrollTargetEl.addEventListener('touchstart', this.touchStartHandle);
            this.scrollTargetEl.addEventListener('touchend', this.touchEndHandle);
        },
    };
</script>


================================================
FILE: src/scripts/components/Badge.vue
================================================
<template>
    <div
        @click="$emit('click')"
        class="badge"
        :class="{'badge-gap': $slots.default}">
        <div class="badge-addon" :class="`bg-${theme}`">{{content}}</div>
        <slot></slot>
    </div>
</template>

<script>
    export default {
        name: 'badge',
        props: {
            // 内容
            content: {},
            // 主题
            theme: {
                type: String,
                default: 'primary',
                validator(value) {
                    return ['primary', 'secondary', 'success', 'warning', 'danger'].indexOf(value) > -1;
                },
            },
        },
    };
</script>


================================================
FILE: src/scripts/components/Button.vue
================================================
<template>
    <button
        :type="type"
        @click="clickHandle"
        class="btn"
        :class="{
            [`btn-${theme}`]: theme,
            [`btn-${size}`]: size,
            [`btn-${theme}-outline`]: hollow,
            ['btn-block']: block,
            ['btn-loading']: loading,
            [`border-${shape}`]: shape,
        }"
        :disabled="disabled || loading">
        <slot></slot>
        <loading
            size="sm"
            v-if="loading"
            :theme="theme === 'default' ? 'primary' : null" />
    </button>
</template>

<script>
    // Button
    export default {
        name: 'btn',
        props: {
            block: Boolean,
            hollow: Boolean,
            disabled: Boolean,
            // 加载中
            loading: Boolean,
            // 类型
            type: {
                type: String,
                default: 'button',
                validator(value) {
                    return ['button', 'submit', 'reset'].indexOf(value) > -1;
                },
            },
            // 主题
            theme: {
                type: String,
                default: 'primary',
                validator(value) {
                    return ['primary', 'secondary', 'default'].indexOf(value) > -1;
                },
            },
            // 尺寸
            size: {
                type: String,
                validator(value) {
                    return ['sm', 'lg'].indexOf(value) > -1;
                },
            },
            // 形状
            shape: {
                type: String,
                validator(value) {
                    return ['pill'].indexOf(value) > -1;
                },
            },
        },
        methods: {
            clickHandle(event) {
                this.$emit('click', event);
            },
        },
    };
</script>


================================================
FILE: src/scripts/components/Card.vue
================================================
<template>
    <div class="card" @click="$emit('click')">
        <slot></slot>
    </div>
</template>

<script>
    export default {
        name: 'card',
    };
</script>


================================================
FILE: src/scripts/components/CardBody.vue
================================================
<template>
    <div class="card-body" @click="$emit('click')">
        <slot></slot>
    </div>
</template>

<script>
    export default {
        name: 'card-body',
    };
</script>


================================================
FILE: src/scripts/components/Cell.vue
================================================
<template>
    <router-link
        v-if="!disabled && to"
        :to="to"
        class="cell cell-link"
        :class="{
            'cell-disabled': disabled,
            'cell-no-gap': !hasGap,
        }">
        <div class="cell-header" v-if="$slots.header">
            <slot name="header"></slot>
        </div>
        <span class="cell-body">
            <slot></slot>
        </span>
        <div class="cell-footer" v-if="$slots.footer">
            <slot name="footer"></slot>
        </div>
        <i v-if="hasArrow" class="fa fa-angle-right cell-arrow" />
    </router-link>

    <a
        v-else
        :href="!disabled && href"
        @click="!disabled && $emit('click')"
        class="cell"
        :class="{
            'cell-link': clickable,
            'cell-disabled': disabled,
            'cell-no-gap': !hasGap,
        }" >
        <div class="cell-header" v-if="$slots.header">
            <slot name="header"></slot>
        </div>
        <span class="cell-body">
            <slot></slot>
        </span>
        <div class="cell-footer" v-if="$slots.footer">
            <slot name="footer"></slot>
        </div>
        <i v-if="hasArrow" class="fa fa-angle-right cell-arrow" />
    </a>
</template>

<script>
    export default {
        name: 'cell',
        props: {
            to: [String, Object],
            href: String,
            disabled: Boolean,
            hasGap: {
                type: Boolean,
                default: true,
            },
            hasArrow: {
                type: Boolean,
                default() {
                    // 有href和to时默认为true
                    return !!(this.href || this.to);
                },
            },
        },
        computed: {
            clickable() {
                return !this.disabled && (this.href || this._events.click);
            },
        },
    };
</script>


================================================
FILE: src/scripts/components/Checkbox.vue
================================================
<template>
    <label
        class="checkbox"
        :class="'checkbox-' + type">
        <input
            type="checkbox"
            class="checkbox-input"
            :disabled="disabled || $parent.disabled"
            v-model="currentValue">
        <span class="checkbox-addon">
            <i class="fa fa-check" />
        </span>
        <span class="checkbox-label">
            <slot></slot>
        </span>
    </label>
</template>

<script>
    import Sync from '../mixins/sync';
    
    export default {
        name: 'checkbox',
        mixins: [Sync],
        props: {
            type: {
                type: String,
                default: 'square',
                validator(value) {
                    return ['square', 'circle'].indexOf(value) > -1;
                },
            },
        },
        data() {
            let currentValue;

            if(this.isGroupChildComponent) {
                currentValue = this.$parent.currentValue &&
                    this.$parent.currentValue.indexOf(this.value) > -1;
            } else {
                currentValue = this.value;
            }

            return { currentValue };
        },
        watch: {
            currentValue(val) {
                if(this.isGroupChildComponent) {
                    this.$parent.$emit('optionChecked', this.value);
                }
                this.$emit('input', val);
            },
        },
        beforeCreate() {
            // 是否CheckboxGroup下的子组件
            this.isGroupChildComponent = this.$parent.$options._componentTag === 'checkbox-group';
        },
    };
</script>

================================================
FILE: src/scripts/components/CheckboxGroup.vue
================================================
<template>
    <div class="checkbox-group">
        <slot></slot>
    </div>
</template>

<script>
    import Sync from '../mixins/sync';
    
    export default {
        name: 'checkbox-group',
        mixins: [Sync],
        data() {
            return {
                currentValue: this.value || [],
            };
        },
        methods: {
            optionCheckedHandle(option) {
                this.currentValue = this.currentValue || [];
                const index = this.currentValue.indexOf(option);

                if(index === -1) {
                    this.currentValue.push(option);
                } else {
                    this.currentValue.splice(index, 1);
                }
            },
        },
        created() {
            this.$on('optionChecked', this.optionCheckedHandle);
        },
    };
</script>

================================================
FILE: src/scripts/components/DatePicker/DateRange.vue
================================================
<template>
	<div
	   v-show="visible"
	   class="date-range-picker">
    
        <div class="header">
            <div class="header-cnt">
                <btn
                    @click="prevMonth"
                    class="btn btn-left">
                    <icon name="angle-left" size="lg"/>
                </btn>
                <btn
                    @click="nextMonth"
                    class="btn btn-right">
                    <icon name="angle-right" size="lg"/>
                </btn>
                <div>{{ dateLabel }}</div>
            </div>
        </div>
        <date-table
            selection-mode="range"
            :date="date"
            :min-date="minDate"
            :max-date="maxDate"
            :range-state="rangeState"
            :disabled-date="disabledDate"
            @changerange="handleChangeRange"
            :first-day-of-week="firstDayOfWeek"
            @pick="handleRangePick">
        </date-table>
	</div>
</template>

<script type="text/babel">
    import DateTable from './DateTable';
    import Btn from '../Button';
    import Icon from '../Icon';
    import {
        formatDate,
        isDate,
        prevMonth,
        nextMonth,
    } from '../../utils/date';

    const DayMilliseconds = 24 * 60 * 60 * 1000;

    export default {

        computed: {
            dateLabel() {
                /*eslint-disable*/
                return this.date.getFullYear() + '年' + (this.date.getMonth() + 1) + '月';
                /*eslint-disable*/
            },
        },

        data() {
            return {
                popperClass: '',
                defaultTime: null,
                minDate: '',
                maxDate: '',
                date: new Date(),
                rangeState: {
                    endDate: null,
                    selecting: false,
                    row: null,
                    column: null,
                },
                visible: '',
                firstDayOfWeek: 1,
                dateDisable: false,
                prevPickedDisableDays: 0,
                nextPickedDisableDays: 0,
            };
        },

        methods: {
            disabledDate(time) {
                if(this.dateDisable) {
                    if(this.maxDate) {
                        return false;
                    }

                    if(this.minDate) {
                        let prePickedDisabled = time.getTime()
                                                <=
                                                (new Date(this.minDate).getTime() - this.prevPickedDisableDays * DayMilliseconds);
                        let nextPickedDisabled = time.getTime()
                                                >=
                                                (new Date(this.minDate).getTime() + this.nextPickedDisableDays * DayMilliseconds);

                        return prePickedDisabled || nextPickedDisabled;
                    }
                }

                return false;
            },

            handleChangeRange(val) {
                this.minDate = val.minDate;
                this.maxDate = val.maxDate;
                this.rangeState = val.rangeState;
            },

            handleRangePick(val, close = true) {
                const minDate = val.minDate;
                const maxDate = val.maxDate;

                if(this.maxDate === maxDate && this.minDate === minDate) {
                    return;
                }
                this.onPick && this.onPick(val, formatDate);

                this.maxDate = maxDate;
                this.minDate = minDate;

                // workaround for https://github.com/ElemeFE/element/issues/7539, should remove this block when we don't have to care about Chromium 55 - 57
                setTimeout(() => {
                    this.maxDate = maxDate;
                    this.minDate = minDate;
                }, 10);
                if(!close) {
                    return;
                }
                this.handleConfirm();
            },

            prevMonth() {
                this.date = prevMonth(this.date);
            },

            nextMonth() {
                this.date = nextMonth(this.date);
            },

            handleConfirm() {
                this.$emit('pick', [this.minDate, this.maxDate]);
            },

            isValidValue(value) {
                return Array.isArray(value) &&
                    value && value[0] && value[1] &&
                    isDate(value[0]) && isDate(value[1]) &&
                    value[0].getTime() <= value[1].getTime() && (
                    typeof this.disabledDate === 'function'
                        ? !this.disabledDate(value[0]) && !this.disabledDate(value[1])
                        : true
                );
            },
        },

        components: { DateTable, Btn, Icon },
    };
</script>


================================================
FILE: src/scripts/components/DatePicker/DateTable.vue
================================================
<template>
    <table
        cellspacing="0"
        cellpadding="0"
        class="date-table"
        @click="handleClick"
        @mousemove="handleMouseMove">
        <tbody>
            <tr>
                <th v-if="showWeekNumber"></th>
                <th v-for="week in WEEKS">{{week}}</th>
            </tr>
            <tr
                class="date-table__row"
                v-for="row in rows">
                <td
                    v-for="cell in row"
                    :class="getCellClasses(cell)">
                    <div>
                        <span>
                            {{ cell.type==='today' ? '今' : cell.text }}
                        </span>
                    </div>
                </td>
            </tr>
        </tbody>
    </table>
</template>

<script>
    import { getFirstDayOfMonth, getDayCountOfMonth, getWeekNumber, getStartDateOfMonth, nextDate } from '../../utils/date';
    import { hasClass } from '../../utils/dom';

    const WEEKS = ['日', '一', '二', '三', '四', '五', '六'];
    const clearHours = function(time) {
        const cloneDate = new Date(time);

        cloneDate.setHours(0, 0, 0, 0);

        return cloneDate.getTime();
    };

    export default {
        name: 'date-table',

        props: {
            // 一周的第一天
            firstDayOfWeek: {
                default: 7,
                type: Number,
                validator: val => val >= 1 && val <= 7,
            },

            value: {},

            date: {},

            selectionMode: {
                type: String,
                default: 'day',
            },
            // 是否展示周
            showWeekNumber: {
                type: Boolean,
                default: false,
            },

            // 禁选日期
            disabledDate: {},

            selectedDate: {
                type: Array,
            },

            minDate: {},

            maxDate: {},

            // 日期区间
            rangeState: {
                default() {
                    return {
                        endDate: null,
                        selecting: false,
                        row: null,
                        column: null,
                    };
                },
            },
        },

        computed: {
            offsetDay() {
                const week = this.firstDayOfWeek;

                // 周日为界限,左右偏移的天数,3217654 例如周一就是 -1,目的是调整前两行日期的位置

                return week > 3 ? 7 - week : -week;
            },

            WEEKS() {
                const week = this.firstDayOfWeek;

                return WEEKS.concat(WEEKS).slice(week, week + 7);
            },

            year() {
                return this.date.getFullYear();
            },

            month() {
                return this.date.getMonth();
            },

            startDate() {
                return getStartDateOfMonth(this.year, this.month);
            },

            rows() {
                // TODO: refactory rows / getCellClasses
                const date = new Date(this.year, this.month, 1);
                let day = getFirstDayOfMonth(date); // day of first day
                const dateCountOfMonth = getDayCountOfMonth(date.getFullYear(), date.getMonth());
                const dateCountOfLastMonth = getDayCountOfMonth(
                                                date.getFullYear(),
                                                (date.getMonth() === 0 ? 11 : date.getMonth() - 1));

                day = (day === 0 ? 7 : day);

                const offset = this.offsetDay;
                const rows = this.tableRows;
                let count = 1;
                let firstDayPosition;

                const startDate = this.startDate;
                const disabledDate = this.disabledDate;
                const selectedDate = this.selectedDate || this.value;
                const now = clearHours(new Date());

                for (let i = 0; i < 6; i++) {
                    const row = rows[i];

                    if(this.showWeekNumber) {
                        if(!row[0]) {
                            row[0] = { type: 'week', text: getWeekNumber(nextDate(startDate, i * 7 + 1)) };
                        }
                    }

                    for (let j = 0; j < 7; j++) {
                        let cell = row[this.showWeekNumber ? j + 1 : j];

                        if(!cell) {
                            cell = { row: i, column: j, type: 'normal', inRange: false, start: false, end: false };
                        }

                        cell.type = 'normal';

                        const index = i * 7 + j;
                        const time = nextDate(startDate, index - offset).getTime();

                        /**     仅选择一次时 选择状态不可保持     **/
                        if(!this.maxDate) {
                            // cell.inRange = time >= clearHours(this.minDate);
                            cell.inRange = cell.end = cell.start = this.minDate &&
                                                                    time === clearHours(this.minDate);
                        } else {
                            cell.inRange = time >= clearHours(this.minDate) &&
                                        time <= clearHours(this.maxDate);
                            cell.start = this.minDate && time === clearHours(this.minDate);
                            cell.end = this.maxDate && time === clearHours(this.maxDate);
                        }

                        // cell.inRange = time >= clearHours(this.minDate) &&
                        //                 time <= clearHours(this.maxDate);
                        // cell.start = this.minDate && time === clearHours(this.minDate);
                        // cell.end = this.maxDate && time === clearHours(this.maxDate);
                        /***********************************/

                        const isToday = time === now;

                        if(isToday) {
                            cell.type = 'today';
                        }

                        if(i >= 0 && i <= 1) {
                            if(j + i * 7 >= (day + offset)) {
                                cell.text = count++;
                                if(count === 2) {
                                    firstDayPosition = i * 7 + j;
                                }
                            } else {
                                cell.text = dateCountOfLastMonth - (day + offset - j % 7) + 1 + i * 7;
                                cell.type = 'prev-month';
                            }
                        } else if(count <= dateCountOfMonth) {
                            cell.text = count++;
                            if(count === 2) {
                                firstDayPosition = i * 7 + j;
                            }
                        } else {
                            cell.text = count++ - dateCountOfMonth;
                            cell.type = 'next-month';
                        }

                        let newDate = new Date(time);

                        cell.disabled = typeof disabledDate === 'function' && disabledDate(newDate);
                        /*eslint-disable*/
                        cell.selected = Array.isArray(selectedDate) &&
                          selectedDate.filter(date => date.toString() === newDate.toString())[0];
                        /*eslint-disable*/

                        this.$set(row, this.showWeekNumber ? j + 1 : j, cell);
                    }
                }

                rows.firstDayPosition = firstDayPosition;

                return rows;
            },
        },

        watch: {
            'rangeState.endDate'(newVal) {
                this.markRange(newVal);
            },

            minDate(newVal, oldVal) {
                if(newVal && newVal !== oldVal) {
                    this.rangeState.selecting = true;
                    this.markRange(newVal);
                } else if(!newVal) {
                    this.rangeState.selecting = false;
                    this.markRange(newVal);
                } else {
                    this.markRange();
                }
            },

            maxDate(newVal, oldVal) {
                if(newVal && newVal !== oldVal) {
                    this.rangeState.selecting = false;
                    this.markRange(newVal);
                    this.$emit('pick', {
                        minDate: this.minDate,
                        maxDate: this.maxDate,
                    });
                }
            },
        },

        data() {
            return {
                tableRows: [[], [], [], [], [], []],
            };
        },

        methods: {
            getCellClasses(cell) {
                const selectionMode = this.selectionMode;
                let classes = [];

                if((cell.type === 'normal' || cell.type === 'today') && !cell.disabled) {
                    classes.push('available');
                    if(cell.type === 'today') {
                        classes.push('today');
                    }
                } else {
                    classes.push(cell.type);
                }

                if(cell.disabled) {
                    classes.push('disabled');
                } else if(cell.inRange) {
                    classes.push('in-range');

                    if(cell.start) {
                        classes.push('start-date');
                    }

                    if(cell.end) {
                        classes.push('end-date');
                    }
                }

                if(cell.selected) {
                    classes.push('selected');
                }

                return classes.join(' ');
            },

            getDateOfCell(row, column) {
                const offsetFromStart = row * 7
                                        + (column - (this.showWeekNumber ? 1 : 0))
                                        - this.offsetDay;

                return nextDate(this.startDate, offsetFromStart);
            },

            markRange(maxDate) {
                const startDate = this.startDate;

                if(!maxDate) {
                    maxDate = this.maxDate;
                }

                const rows = this.rows;
                const minDate = this.minDate;

                for (let i = 0, k = rows.length; i < k; i++) {
                    const row = rows[i];

                    for (let j = 0, l = row.length; j < l; j++) {
                        /*eslint-disable*/
                        if(this.showWeekNumber && j === 0) continue;
                        /*eslint-disable*/

                        const cell = row[j];
                        const index = i * 7 + j + (this.showWeekNumber ? -1 : 0);
                        const time = nextDate(startDate, index - this.offsetDay).getTime();

                        if(maxDate && maxDate < minDate) {
                            cell.inRange = minDate &&
                                            time >= clearHours(maxDate) &&
                                            time <= clearHours(minDate);
                            cell.start = maxDate && time === clearHours(maxDate.getTime());
                            cell.end = minDate && time === clearHours(minDate.getTime());
                        } else {
                            cell.inRange = minDate &&
                                            time >= clearHours(minDate) &&
                                            time <= clearHours(maxDate);
                            cell.start = minDate && time === clearHours(minDate.getTime());
                            cell.end = maxDate && time === clearHours(maxDate.getTime());
                        }
                    }
                }
            },

            handleMouseMove(event) {
                if(!this.rangeState.selecting) {
                    return;
                }

                this.$emit('changerange', {
                    minDate: this.minDate,
                    maxDate: this.maxDate,
                    rangeState: this.rangeState,
                });

                let target = event.target;

                if(target.tagName === 'SPAN') {
                    target = target.parentNode.parentNode;
                }
                if(target.tagName === 'DIV') {
                    target = target.parentNode;
                }
                if(target.tagName !== 'TD') return;
                else if(hasClass(target, 'disabled')) return;

                const column = target.cellIndex;
                const row = target.parentNode.rowIndex - 1;
                const { row: oldRow, column: oldColumn } = this.rangeState;

                if(oldRow !== row || oldColumn !== column) {
                    this.rangeState.row = row;
                    this.rangeState.column = column;

                    this.rangeState.endDate = this.getDateOfCell(row, column);
                }
            },

            handleClick(event) {
                let target = event.target;

                if(target.tagName === 'SPAN') {
                    target = target.parentNode.parentNode;
                }
                if(target.tagName === 'DIV') {
                    target = target.parentNode;
                }

                if(target.tagName !== 'TD') return;
                if(hasClass(target, 'disabled')) {
                    return;
                }

                const selectionMode = this.selectionMode;

                let year = Number(this.year);
                let month = Number(this.month);

                const cellIndex = target.cellIndex;
                const rowIndex = target.parentNode.rowIndex;
                const cell = this.rows[rowIndex - 1][cellIndex];
                const text = cell.text;
                const className = target.className;

                const newDate = new Date(year, month, 1);

                if(className.indexOf('prev') !== -1) {
                    if(month === 0) {
                        year -= 1;
                        month = 11;
                    } else {
                        month -= 1;
                    }
                    newDate.setFullYear(year);
                    newDate.setMonth(month);
                } else if(className.indexOf('next') !== -1) {
                    if(month === 11) {
                        year += 1;
                        month = 0;
                    } else {
                        month += 1;
                    }
                    newDate.setFullYear(year);
                    newDate.setMonth(month);
                }

                newDate.setDate(parseInt(text, 10));

                if(this.selectionMode === 'range') {
                    if(this.minDate && this.maxDate) {
                        // 最小,最大日期独有

                        const minDate = new Date(newDate.getTime());
                        const maxDate = null;

                        this.$emit('pick', { minDate, maxDate }, false);
                        this.rangeState.selecting = true;
                        this.markRange(this.minDate);
                        this.$nextTick(() => {
                            this.handleMouseMove(event);
                        });
                    } else if(this.minDate && !this.maxDate) {
                        // 有最小日期 无最大日期

                        if(newDate >= this.minDate) {
                            const maxDate = new Date(newDate.getTime());

                            this.rangeState.selecting = false;
                            this.$emit('pick', {
                                minDate: this.minDate,
                                maxDate,
                            });
                        } else {
                            const minDate = new Date(newDate.getTime());

                            this.rangeState.selecting = false;
                            this.$emit('pick', { minDate, maxDate: this.minDate });
                        }
                    } else if(!this.minDate) {
                        // 无最小日期

                        const minDate = new Date(newDate.getTime());

                        this.$emit('pick', { minDate, maxDate: this.maxDate }, false);
                        this.rangeState.selecting = true;
                        this.markRange(this.minDate);
                    }
                } else if(selectionMode === 'day') {
                    this.$emit('pick', newDate);
                }
            },
        },
    };
</script>



================================================
FILE: src/scripts/components/DatePicker/Picker.vue
================================================
<template>
    <div
        class="date-picker"
        :class="{
            [`date-picker-${size}`]: size,
            [`date-picker-${theme}`]: theme,
        }">
    </div>
</template>

<script>
    import Vue from 'vue';
    // import { formatDate } from '../../utils/date';
    import Emitter from '../../mixins/emitter';

    export default {
        mixins: [Emitter],

        props: {
            // 主题
            theme: {
                type: String,
                default: 'primary',
                validator(value) {
                    return ['primary', 'default'].indexOf(value) > -1;
                },
            },
            // 尺寸
            size: {
                type: String,
                default: 'base',
                validator(value) {
                    return ['base', 'sm', 'lg'].indexOf(value) > -1;
                },
            },
            // 是否开启日期不可选
            dateDisable: {
                type: Boolean,
                default: false,
            },
            // 相较于当前时间,往前不可选择的天数
            prevPickedDisableDays: {
                type: Number,
                default: 0,
            },
            // 相较于当前时间,往后不可选择的天数
            nextPickedDisableDays: {
                type: Number,
                default: 0,
            },

            value: {},
            // 是否总是展示日期组件
            pickerAlwaysShow: {
                type: Boolean,
                default: true,
            },
            onPick: {},
        },

        data() {
            return {
                pickerVisible: false,
            };
        },

        watch: {
            pickerVisible(val) {
                if(val) {
                    this.showPicker();
                } else {
                    this.hidePicker();
                }
            },
            value: {
                immediate: true,
                handler(val) {
                    if(this.picker) {
                        this.picker.value = val;
                        this.picker.selectedDate = Array.isArray(val) ? val : [];
                    }
                },
            },
        },

        computed: {
            selectionMode() {
                // todo
                // if(this.type === 'week') {
                //     return 'week';
                // } else if(this.type === 'month') {
                //     return 'month';
                // } else if(this.type === 'year') {
                //     return 'year';
                // } else if(this.type === 'dates') {
                //     return 'dates';
                // }
                //
                // return 'day';
                return this.type;
            },

            // todo
            pickerSize() {
                return this.size;
            },
        },

        created() {
            // 默认展示日期选择框
            this.pickerVisible = true;
        },

        methods: {
            hidePicker() {
                if(this.picker) {
                    this.picker.resetView && this.picker.resetView();
                    this.pickerVisible = this.picker.visible = false;
                }
            },

            showPicker() {
                if(this.$isServer) return;
                if(!this.picker) {
                    this.mountPicker();
                }
                this.pickerVisible = this.picker.visible = true;

                this.picker.value = this.value;
                this.picker.resetView && this.picker.resetView();

                this.$nextTick(() => {
                    this.picker.adjustSpinners && this.picker.adjustSpinners();
                });
            },

            mountPicker() {
                this.picker = new Vue(this.view).$mount();

                this.picker.selectedDate = Array.isArray(this.value) && this.value || [];
                this.$watch('format', format => {
                    this.picker.format = format;
                });

                this.picker.dateDisable = this.dateDisable;
                this.picker.prevPickedDisableDays = this.prevPickedDisableDays;
                this.picker.nextPickedDisableDays = this.nextPickedDisableDays;

                this.$el.appendChild(this.picker.$el);
                this.picker.resetView && this.picker.resetView();

                this.picker.onPick = this.onPick;

                this.picker.$on('dodestroy', this.doDestroy);
                this.picker.$on('pick', (date = '', visible = this.pickerAlwaysShow) => {
                    this.pickerVisible = this.picker.visible = visible;
                    this.picker.resetView && this.picker.resetView();
                });

                this.picker.$on('select-range', (start, end, pos) => {
                    if(this.refInput.length === 0) return;
                    if(!pos || pos === 'min') {
                        this.refInput[0].setSelectionRange(start, end);
                        this.refInput[0].focus();
                    } else if(pos === 'max') {
                        this.refInput[1].setSelectionRange(start, end);
                        this.refInput[1].focus();
                    }
                });
            },

            unmountPicker() {
                if(this.picker) {
                    this.picker.$destroy();
                    this.picker.$off();
                    this.picker.$el.parentNode.removeChild(this.picker.$el);
                }
            },
        },
    };
</script>


================================================
FILE: src/scripts/components/DatePicker/index.js
================================================
import Picker from './Picker';
import DateRangeView from './DateRange';

const getView = function(type) {
    if(type === 'daterange') {
        return DateRangeView;
    }
    // other type view

    return null;
};

const DatePicker = {
    mixins: [Picker],

    name: 'date-picker',

    props: {
        type: {
            type: String,
            default: 'daterange',
        },
    },

    watch: {
        type(type) {
            if(this.picker) {
                this.unmountPicker();
                this.view = getView(type);
                this.mountPicker();
            } else {
                this.view = getView(type);
            }
        },
    },

    created() {
        this.view = getView(this.type);
    },
};

export default DatePicker;


================================================
FILE: src/scripts/components/Drawer.vue
================================================
<template>
    <div class="drawer">
        <slot></slot>
    </div>
</template>

<script>
    import Tab from '../mixins/tab';

    export default {
        name: 'drawer',
        mixins: [Tab],
        methods: {
            reset() {
                this.currentActiveKey = undefined;
            },
        },
    };
</script>


================================================
FILE: src/scripts/components/DrawerItem.vue
================================================
<template>
    <div
        class="drawer-item"
        :class="{
            'active': $parent.currentActiveKey !== undefined && $parent.currentActiveKey === currentEventKey,
            disabled: disabled || $parent.disabled,
        }"
        @click="clickHandle">
        <slot></slot>
        <icon name="caret-down" class="drawer-item-icon"></icon>
    </div>
</template>

<script>
    import TabItem from '../mixins/tabItem';

    export default {
        name: 'drawer-item',
        mixins: [TabItem],
    };
</script>


================================================
FILE: src/scripts/components/Flex.vue
================================================
<template>
    <div
        class="flex"
        :class="[
            alignClass,
            {[`flex-justify-${justify}`]: justify},
            {'flex-vertical': direction === 'column'}
            ]">
        <slot></slot>
    </div>
</template>

<script>
    export default {
        name: 'flex',
        props: {
            // 方向
            direction: {
                type: String,
                default: 'row',
                validator(value) {
                    return ['row', 'column'].indexOf(value) > -1;
                },
            },
            // 排版
            justify: {
                type: String,
                validator(value) {
                    return ['start', 'end', 'center', 'between', 'around'].indexOf(value) > -1;
                },
            },
            // 对齐
            align: {
                type: String,
                validator(value) {
                    return ['top', 'bottom', 'middle'].indexOf(value) > -1;
                },
            },
        },
        computed: {
            alignClass() {
                let align = this.align;

                if(this.direction === 'row' && !align) {
                    align = 'middle';
                }

                return `flex-align-${align}`;
            },
        },
    };
</script>


================================================
FILE: src/scripts/components/FlexItem.vue
================================================
<template>
    <div
        @click="$emit('click')"
        class="flex-item"
        :style="{ flex }">
        <slot></slot>
    </div>
</template>

<script>
    export default {
        name: 'flex-item',
        props: {
            flex: {
                type: [Number, String],
                default: 1,
            },
        },
    };
</script>


================================================
FILE: src/scripts/components/Group.vue
================================================
<template>
    <div class="group">
        <slot></slot>
    </div>
</template>

<script>
    export default {
        name: 'group',
    };
</script>


================================================
FILE: src/scripts/components/GroupTitle.vue
================================================
<template>
    <div class="group-title">
        <slot></slot>
    </div>
</template>

<script>
    export default {
        name: 'group-title',
    };
</script>


================================================
FILE: src/scripts/components/HRule.vue
================================================
<template>
    <hr class="hr" :class="{
        [`border-${type}`]: type,
        [`border-${theme}`]: theme,
    }">
</template>

<script>
    export default {
        name: 'hrule',
        props: {
            type: {
                type: String,
                validator(value) {
                    return ['dashed', 'dotted'].indexOf(value) > -1;
                },
            },
            theme: {
                type: String,
                validator(value) {
                    return ['primary', 'secondary', 'warning', 'success', 'danger'].indexOf(value) > -1;
                },
            },
        },
    };
</script>


================================================
FILE: src/scripts/components/Icon.vue
================================================
<template>
    <i
        @click="$emit('click')"
        class="fa"
        :class="[
            `fa-${name}`,
            {
                [`fa-${size}`]: size,
                'gap-left': left,
                'gap-right': right,
            }
        ]" />
</template>

<script>
    export default {
        name: 'icon',
        props: {
            left: Boolean,
            right: Boolean,
            name: {
                type: String,
                required: true,
            },
            size: {
                type: String,
                validator(value) {
                    return ['lg', '2x', '3x', '4x', '5x'].indexOf(value) > -1;
                },
            },
        },
    };
</script>


================================================
FILE: src/scripts/components/InlineSelector.vue
================================================
<template>
    <div class="inline-selector">
        <slot></slot>
    </div>
</template>

<script>
    import Sync from '../mixins/sync';
    import Select from '../mixins/select';

    export default {
        name: 'inline-selector',
        mixins: [Sync, Select],
        props: {
            theme: {
                type: String,
                default: 'primary',
                validator(value) {
                    return ['primary', 'success', 'warning', 'danger'].indexOf(value) > -1;
                },
            },
        },
    };
</script>


================================================
FILE: src/scripts/components/InlineSelectorOption.vue
================================================
<template>
    <tag
        class="inline-selector-option"
        :class="{
            disabled: disabled || $parent.disabled,
        }"
        hollow
        size="sm"
        @click="clickHandle"
        :theme="isActive() ? $parent.theme : 'default'">
        <slot></slot>
    </tag>
</template>

<script>
    import SelectOption from '../mixins/selectOption';

    export default {
        name: 'inline-selector-option',
        mixins: [SelectOption],
    };
</script>


================================================
FILE: src/scripts/components/InputNumber.vue
================================================
<template>
    <div class="input-number" :class="{'input-number-disabled': disabled}">
        <a
            class="input-number-minus"
            :class="{'disabled': currentValue <= min}"
            @click="minusHandle">-</a>
        <input
            type="number"
            v-model="currentValue"
            :disabled="disabled"
            class="input-number-input">
        <a
            class="input-number-plus"
            :class="{'disabled': currentValue >= max}"
            @click="plusHandle">
            +
        </a>
    </div>
</template>

<script>
    import Sync from '../mixins/sync';

    export default {
        name: 'input-number',
        mixins: [Sync],
        props: {
            value: {
                type: Number,
                default: 0,
            },
            max: {
                type: Number,
                default: Infinity,
            },
            min: {
                type: Number,
                default: -Infinity,
            },
            step: {
                type: Number,
                default: 1,
            },
        },
        methods: {
            // 减
            minusHandle() {
                if(this.disabled || this.currentValue <= this.min) return;

                this.currentValue -= this.step;
            },
            // 加
            plusHandle() {
                if(this.disabled || this.currentValue >= this.max) return;

                this.currentValue += this.step;
            },
        },
    };
</script>


================================================
FILE: src/scripts/components/InputText.vue
================================================
<template>
    <div class="input-text" v-disfavor="blur">
        <input
            type="number"
            v-if="type === 'number'"
            ref="input"
            v-model="currentValue"
            :disabled="currentDisabled"
            :placeholder="placeholder"
            @focus="focus = true"
            class="input-text-input">
        <input
            type="tel"
            v-else-if="type === 'tel'"
            ref="input"
            v-model="currentValue"
            :disabled="currentDisabled"
            :placeholder="placeholder"
            @focus="focus = true"
            class="input-text-input">
        <input
            type="date"
            v-else-if="type === 'date'"
            ref="input"
            v-model="currentValue"
            :disabled="currentDisabled"
            :placeholder="placeholder"
            @focus="focus = true"
            class="input-text-input">
        <input
            type="datetime"
            v-else-if="type === 'datetime'"
            ref="input"
            v-model="currentValue"
            :disabled="currentDisabled"
            :placeholder="placeholder"
            @focus="focus = true"
            class="input-text-input">
        <input
            type="time"
            v-else-if="type === 'time'"
            ref="input"
            v-model="currentValue"
            :disabled="currentDisabled"
            :placeholder="placeholder"
            @focus="focus = true"
            class="input-text-input">
        <input
            type="password"
            v-else-if="type === 'password'"
            ref="input"
            v-model="currentValue"
            :disabled="currentDisabled"
            :placeholder="placeholder"
            @focus="focus = true"
            class="input-text-input">
        <input
            type="email"
            v-else-if="type === 'email'"
            ref="input"
            v-model="currentValue"
            :disabled="currentDisabled"
            :placeholder="placeholder"
            @focus="focus = true"
            class="input-text-input">
        <input
            type="text"
            v-else
            ref="input"
            v-model="currentValue"
            :disabled="currentDisabled"
            :placeholder="placeholder"
            @focus="focus = true"
            class="input-text-input">
        <icon
            v-show="clearable && focus && !!currentValue"
            class="input-text-clear"
            name="times-circle"
            @click="clearHandle"
            size="lg" />
        <icon
            v-if="state"
            :name="getStateIcon()"
            :class="getStateClass()"
            size="lg" />
    </div>
</template>

<script>
    import Sync from '../mixins/sync';

    export default {
        name: 'input-text',
        mixins: [Sync],
        props: {
            clearable: Boolean,
            placeholder: String,
            type: {
                type: String,
                default: 'text',
                validator(value) {
                    return ['text', 'number', 'tel', 'date', 'datetime', 'time', 'password', 'email'].indexOf(value) > -1;
                },
            },
            state: {
                type: String,
                validator(value) {
                    return ['success', 'warning', 'error'].indexOf(value) > -1;
                },
            },
        },
        data() {
            return {
                focus: false,
                currentDisabled: this.disabled || this.$parent.disabled,
            };
        },
        methods: {
            blur() {
                this.focus = false;
            },
            clearHandle() {
                if(!this.currentDisabled) {
                    this.currentValue = '';
                    this.$refs.input.focus();
                }
            },
            getStateClass() {
                return {
                    success: 'text-success',
                    warning: 'text-warning',
                    error: 'text-danger',
                }[this.state];
            },
            getStateIcon() {
                return {
                    success: 'check',
                    warning: 'exclamation-triangle',
                    error: 'exclamation-circle',
                }[this.state];
            },
        },
    };
</script>


================================================
FILE: src/scripts/components/InputTextarea.vue
================================================
<template>
    <div class="textarea">
        <textarea
            v-model="currentValue"
            :disabled="disabled"
            :placeholder="placeholder"
            class="textarea-input"
            :rows="rows"></textarea>
        <div class="textarea-counter" v-if="countable">{{currentValue.length}}/{{maxLength}}</div>
    </div>
</template>

<script>
    import Sync from '../mixins/sync';

    export default {
        name: 'input-textarea',
        mixins: [Sync],
        props: {
            placeholder: String,
            countable: Boolean,
            maxLength: {
                type: Number,
                default: 200,
            },
            rows: {
                type: Number,
                default: 4,
            },
        },
        data() {
            return {
                stage: 0,
            };
        },
        watch: {
            currentValue(val) {
                if(val.length > this.maxLength) {
                    this.currentValue = val.substring(0, this.maxLength);
                }
            },
        },
    };
</script>


================================================
FILE: src/scripts/components/Loading.vue
================================================
<template>
    <svg
        class="loading"
        :class="{
            [`loading-${size}`]: size,
            [`loading-${theme}`]: theme,
        }"
        viewBox="25 25 50 50">
        <circle class="loading-path" cx="50" cy="50" r="23" fill="none"/>
    </svg>
</template>

<script>
    export default {
        name: 'loading',
        props: {
            size: {
                type: String,
                validator(value) {
                    return ['sm', 'lg'].indexOf(value) > -1;
                },
            },
            theme: {
                type: String,
                validator(value) {
                    return ['primary', 'secondary'].indexOf(value) > -1;
                },
            },
        },
    };
</script>


================================================
FILE: src/scripts/components/Loadmore.vue
================================================
<template>
    <div class="loadmore">
        <div
            class="loadmore-content"
            :class="{ dropped: topDropped || bottomDropped }"
            :style="{ transform: `translate3d(0, ${translate}px, 0)` }">
            <slot name="top">
                <div class="loadmore-hint loadmore-hint-top" v-if="topMethod">
                    <loading v-if="showLoading && topStatus === 'loading'"></loading>
                    <span class="loadmore-text">{{ topText }}</span>
                </div>
            </slot>
            <slot></slot>
            <slot name="bottom">
                <div class="loadmore-hint loadmore-hint-bottom" v-if="bottomMethod">
                    <loading v-if="showLoading && bottomStatus === 'loading'" ></loading>
                    <span class="loadmore-text">{{ bottomText }}</span>
                </div>
            </slot>
        </div>
    </div>
</template>

<script>
    export default {
        name: 'loadmore',
        props: {
            topPullText: {
                type: String,
                default: 'pull down to refresh',
            },
            topDropText: {
                type: String,
                default: 'release to load more',
            },
            topLoadingText: {
                type: String,
                default: 'loading...',
            },
            topDistance: {
                type: Number,
                default: 70,
            },
            topMethod: {
                type: Function,
            },
            topCancelledMethod: {
                type: Function,
            },
            topPulledMethod: {
                type: Function,
            },
            topAllLoaded: {
                type: Boolean,
                default: false,
            },
            bottomPullText: {
                type: String,
                default: 'pull up to refresh',
            },
            bottomDropText: {
                type: String,
                default: 'release to load more',
            },
            bottomLoadingText: {
                type: String,
                default: 'loading...',
            },
            bottomDistance: {
                type: Number,
                default: 70,
            },
            bottomMethod: {
                type: Function,
            },
            bottomAllLoaded: {
                type: Boolean,
                default: false,
            },
            showLoading: {
                type: Boolean,
                default: false,
            },
        },
        data() {
            return {
                topStatus: '',
                topText: '',
                topDropped: false,
                topPulled: false,
                bottomStatus: '',
                bottomText: '',
                bottomDropped: false,
                bottomReached: false,
                scrollElement: null,
                startY: 0,
                startScrollTop: 0,
                currentY: 0,
                direction: '',
                translate: 0,
            };
        },
        watch: {
            topStatus(status) {
                switch(status) {
                    case 'pull':
                        this.topText = this.topPullText;
                        break;
                    case 'drop':
                        this.topText = this.topDropText;
                        break;
                    case 'loading':
                        this.topText = this.topLoadingText;
                        break;
                    default:
                        break;
                }
                this.$emit('topStatusChanged', status);
            },
            bottomStatus(status) {
                switch(status) {
                    case 'pull':
                        this.bottomText = this.bottomPullText;
                        break;
                    case 'drop':
                        this.bottomText = this.bottomDropText;
                        break;
                    case 'loading':
                        this.bottomText = this.bottomLoadingText;
                        break;
                    default:
                        break;
                }
                this.$emit('bottomStatusChanged', status);
            },
        },
        methods: {
            getScrollElement(element) {
                let currentNode = element,
                    overflowY = '';

                while(
                    currentNode &&
                    currentNode.tagName !== 'HTML' &&
                    currentNode.tagName !== 'BODY' &&
                    currentNode.nodeType === 1
                ) {
                    overflowY = document.defaultView.getComputedStyle(currentNode).overflowY;
                    if(overflowY === 'scroll' || overflowY === 'auto') return currentNode;
                    currentNode = currentNode.parentNode;
                }

                return window;
            },
            getScrollTop(element) {
                if(element === window) {
                    return Math.max(window.pageYOffset || 0, document.documentElement.scrollTop);
                }

                return element.scrollTop;
            },
            handleTouchStart(event) {
                this.startY = event.touches[0].clientY;
                this.startScrollTop = this.getScrollTop(this.scrollElement);
                this.bottomReached = false;

                if(this.topStatus !== 'loading') {
                    this.topStatus = 'pull';
                    this.topDropped = false;
                    this.topPulled = false;
                }

                if(this.bottomStatus !== 'loading') {
                    this.bottomStatus = 'pull';
                    this.bottomDropped = false;
                }
            },
            handleTouchMove(event) {
                // outside element
                const rect = this.$el.getBoundingClientRect();

                if(this.startY < rect.top && this.startY > rect.bottom) return;

                // hand moved
                let distance = 0;

                this.currentY = event.touches[0].clientY;
                // avoid accident
                distance = (this.currentY - this.startY) / 2;
                // detect direction
                this.direction = distance > 0 ? 'down' : 'up';

                // pull down
                if(
                    typeof this.topMethod === 'function' &&
                    this.topStatus !== 'loading' &&
                    this.direction === 'down' &&
                    this.getScrollTop(this.scrollElement) === 0 &&
                    !this.topAllLoaded
                ) {
                    event.preventDefault();
                    event.stopPropagation();
                    this.translate = distance - this.startScrollTop;
                    if(this.translate < 0) this.translate = 0;
                    this.topStatus = this.translate >= this.topDistance ? 'drop' : 'pull';
                    if(!this.topPulled) {
                        this.topPulled = true;
                        this.topPulledMethod && this.topPulledMethod();
                    }
                }

                // pull up
                if(this.direction === 'up') {
                    this.bottomReached = this.bottomReached || this.isBottomReached();
                }

                if(
                    typeof this.bottomMethod === 'function' &&
                    this.bottomStatus !== 'loading' &&
                    this.direction === 'up' &&
                    this.bottomReached &&
                    !this.bottomAllLoaded
                ) {
                    event.preventDefault();
                    event.stopPropagation();
                    this.translate = this.getScrollTop(this.scrollElement)
                        - this.startScrollTop
                        + distance;
                    if(this.translate > 0) this.translate = 0;
                    this.bottomStatus = -this.translate >= this.bottomDistance ? 'drop' : 'pull';
                }
            },
            handleTouchEnd() {
                // pull down
                if(this.direction === 'down' &&
                    this.getScrollTop(this.scrollElement) === 0 &&
                    this.translate > 0
                ) {
                    this.topDropped = true;

                    if(this.topStatus === 'drop') {
                        this.translate = 50;
                        this.topStatus = 'loading';
                        this.topMethod();
                    } else {
                        this.translate = 0;
                        this.topStatus = 'pull';
                        this.topCancelledMethod && this.topCancelledMethod();
                    }
                }

                // pull up
                if(
                    this.direction === 'up' &&
                    this.bottomReached &&
                    this.translate < 0
                ) {
                    this.bottomDropped = true;
                    // reset after pull up takes effect
                    this.bottomReached = false;

                    if(this.bottomStatus === 'drop') {
                        this.translate = -50;
                        this.bottomStatus = 'loading';
                        this.bottomMethod();
                    } else {
                        this.translate = 0;
                        this.bottomStatus = 'pull';
                    }
                }

                // reset direction
                this.direction = '';
            },
            isBottomReached() {
                if(this.scrollElement === window) {
                    return (
                        document.documentElement.clientHeight + (document.body.scrollTop || window.pageYOffset)
                        === document.body.scrollHeight
                    );
                }

                return (
                    this.scrollElement.getBoundingClientRect().bottom
                    >= this.$el.getBoundingClientRect().bottom
                );
            },
            onTopLoaded() {
                this.translate = 0;
                setTimeout(() => {
                    this.topStatus = 'pull';
                }, 200);
            },
            onBottomLoaded() {
                this.bottomStatus = 'pull';
                this.bottomDropped = false;
                this.$nextTick(() => {
                    if(this.scrollElement === window) {
                        document.body.scrollTop += 50;
                    } else {
                        this.scrollElement.scrollTop += 50;
                    }
                    this.translate = 0;
                });
            },
        },
        mounted() {
            this.topText = this.topPullText;
            this.topStatus = 'pull';
            this.bottomStatus = 'pull';
            this.scrollElement = this.getScrollElement(this.$el);

            if(
                typeof this.topMethod === 'function' ||
                typeof this.bottomMethod === 'function'
            ) {
                this.$el.addEventListener('touchstart', this.handleTouchStart);
                this.$el.addEventListener('touchmove', this.handleTouchMove);
                this.$el.addEventListener('touchend', this.handleTouchEnd);
            }
        },
        beforeDestroy() {
            if(
                typeof this.topMethod === 'function' ||
                typeof this.bottomMethod === 'function'
            ) {
                this.$el.removeEventListener('touchstart', this.handleTouchStart);
                this.$el.removeEventListener('touchmove', this.handleTouchMove);
                this.$el.removeEventListener('touchend', this.handleTouchEnd);
            }
        },
    };
</script>


================================================
FILE: src/scripts/components/Mask.vue
================================================
<template>
    <transition :name="transition">
        <div
            class="mask"
            v-show="$parent.currentValue"
            @click="clickable && ($parent.currentValue = false)">
        </div>
    </transition>
</template>

<script>
    export default {
        name: 'mask-layer',
        props: {
            transition: {
                type: String,
                default: 'fade',
            },
            clickable: {
                type: Boolean,
                default: true,
            },
        },
    };
</script>


================================================
FILE: src/scripts/components/Media.vue
================================================
<template>
    <div
        @click="$emit('click')"
        class="media"
        :class="`flex-align-${align}`">
        <slot></slot>
    </div>
</template>

<script>
    export default {
        name: 'media',
        props: {
            // 对齐
            align: {
                type: String,
                default: 'top',
                validator(value) {
                    return ['top', 'bottom', 'middle'].indexOf(value) > -1;
                },
            },
        },
    };
</script>


================================================
FILE: src/scripts/components/MediaBody.vue
================================================
<template>
    <div class="media-body">
        <slot></slot>
    </div>
</template>

<script>
    export default {
        name: 'media-body',
    };
</script>


================================================
FILE: src/scripts/components/MediaObject.vue
================================================
<template>
    <div class="media-object">
        <slot></slot>
    </div>
</template>

<script>
    export default {
        name: 'media-object',
    };
</script>


================================================
FILE: src/scripts/components/Navbar.vue
================================================
<template>
    <div class="navbar" :class="'navbar-' + theme">
        <div class="navbar-header">
            <slot></slot>
        </div>
        <div class="navbar-body" v-if="$slots.body">
            <slot name="body"></slot>
        </div>
        <div class="navbar-footer">
            <slot name="footer"></slot>
        </div>
    </div>
</template>

<script>
    export default {
        name: 'navbar',
        props: {
            // 主题
            theme: {
                type: String,
                default: 'primary',
                validator(value) {
                    return ['primary', 'default'].indexOf(value) > -1;
                },
            },
        },
    };
</script>


================================================
FILE: src/scripts/components/Navigation.vue
================================================
<template>
    <div class="navigation">
        <slot></slot>
    </div>
</template>

<script>
    import Tab from '../mixins/tab';

    export default {
        name: 'navigation',
        mixins: [Tab],
        props: {
            activeKey: {
                default: 0,
            },
        },
    };
</script>


================================================
FILE: src/scripts/components/NavigationItem.vue
================================================
<template>
    <div
        class="navigation-item"
        :class="{'active': $parent.currentActiveKey === currentEventKey}"
        @click="$parent.$emit('itemClick', currentEventKey)">
        <slot></slot>
        <div class="navigation-item-label">
            {{label}}
        </div>
    </div>
</template>

<script>
    import TabItem from '../mixins/tabItem';

    export default {
        name: 'navigation-item',
        mixins: [TabItem],
        props: {
            label: String,
        },
    };
</script>


================================================
FILE: src/scripts/components/Picker.vue
================================================
<template>
    <div
        class="picker"
        :class="{
            [`picker-${size}`]: size,
        }"
        ref="picker">
        <div class="picker-mask"></div>
        <div class="picker-indicator"></div>
        <div class="picker-list" :class="{active: dragging}" ref="pickerList">
            <slot></slot>
        </div>
    </div>
</template>

<script>
    import Sync from '../mixins/sync';
    import draggable from '../utils/draggable';
    import { getTranslate, setTranslate } from '../utils/translate';

    export default {
        name: 'picker',
        mixins: [Sync],
        props: {
            size: {
                type: String,
                validator(value) {
                    return ['sm', 'lg'].indexOf(value) > -1;
                },
            },
        },
        data() {
            return {
                dragging: false,
                itemHeight: 0,
                pickedIndex: -1,
            };
        },
        methods: {
            // 计算拖拽区间
            getDragRange() {
                let { pickerList } = this.$refs;

                return {
                    min: -1 * this.getOptionHeight() * (pickerList.children.length - 1),
                    max: 0,
                };
            },
            // 获取每个Option高度
            getOptionHeight() {
                if(this.itemHeight) return this.itemHeight;

                let style = document.body.currentStyle ||
                document.defaultView.getComputedStyle(document.body, '');

                this.itemHeight = 3 * parseInt(style.fontSize, 10);

                return this.itemHeight;
            },
            // 选择Option
            pickOption() {
                let pickIndex = 0;

                this.$children.forEach((option, index) => {
                    if(option.value === this.currentValue) {
                        pickIndex = index;
                    }
                });

                let itemHeight = this.getOptionHeight(),
                    translate = itemHeight * pickIndex * -1;

                setTranslate(this.$refs.pickerList, null, translate);
                this.pickedIndex = Math.abs(pickIndex);
            },
            // 事件初始化
            initDrag() {
                let { picker, pickerList } = this.$refs,
                    prevTranslateY;

                draggable(picker, {
                    effectEl: pickerList,
                    onDragStart: ({ translateY }, event) => {
                        event.preventDefault();
                        prevTranslateY = translateY;
                    },
                    onDrag: ({ dragging, effectEl, translateY }, event) => {
                        event.preventDefault();
                        this.dragging = dragging;

                        let maxTranslateY = this.getOptionHeight() * 3;
                        let currentTranslateY = translateY;
                        let pickerHeight = this.$children.length * this.getOptionHeight();

                        // 拉过了
                        if(translateY > 0) {
                            let rate = (maxTranslateY - translateY) / maxTranslateY;

                            rate = rate >= 0 ? rate : 0.1;

                            currentTranslateY = prevTranslateY + rate * (translateY - prevTranslateY);
                        } else if(translateY < -pickerHeight) {
                            let rate = ((translateY - pickerHeight) - maxTranslateY) / maxTranslateY;

                            rate = rate >= 0 ? rate : 0.1;

                            currentTranslateY = prevTranslateY + rate * (translateY - prevTranslateY);
                        }

                        prevTranslateY = currentTranslateY;
                        setTranslate(effectEl, null, currentTranslateY);
                    },
                    onDragEnd: ({ dragging, startTimestamp, velocityTranslateY }, event) => {
                        event.preventDefault();

                        this.dragging = dragging;
                        let momentumRatio = 10,
                            itemHeight = this.getOptionHeight(),
                            translateY = getTranslate(pickerList).y,
                            duration = new Date() - startTimestamp;

                        let momentumTranslate;

                        if(duration < 300) {
                            momentumTranslate = translateY + (velocityTranslateY * momentumRatio);
                        }

                        // this.$nextTick(() => {
                        let translate;
                        let dragRange = this.getDragRange();

                        if(momentumTranslate) {
                            translate = Math.round(momentumTranslate / itemHeight) * itemHeight;
                        } else {
                            translate = Math.round(translateY / itemHeight) * itemHeight;
                        }

                        translate = Math.max(Math.min(translate, dragRange.max), dragRange.min);
                        setTranslate(pickerList, null, translate);

                        this.pickedIndex = Math.abs(translate / itemHeight);
                        // });
                    },
                });
            },
        },
        watch: {
            pickedIndex(val) {
                this.currentValue = this.$children[val].value;
            },
            value() {
                this.pickOption();
            },
        },
        mounted() {
            this.pickOption();
            this.initDrag();
        },
    };
</script>


================================================
FILE: src/scripts/components/PickerOption.vue
================================================
<template>
    <div
        class="picker-list-item"
        :class="{active: $parent.currentValue === value}">
        <slot></slot>
    </div>
</template>

<script>
    export default {
        name: 'picker-option',
        props: {
            value: {},
        },
    };
</script>


================================================
FILE: src/scripts/components/Progressbar.vue
================================================
<template>
    <div
        class="progressbar"
        :class="{
            [`progressbar-${size}`]: size,
            [`progressbar-${theme}`]: theme,
        }">
        <div class="progressbar-indicator" :style="{width: `${value * 100}%`}">
            <slot></slot>
        </div>
    </div>
</template>

<script>
    export default {
        name: 'progressbar',
        props: {
            value: {
                type: Number,
                default: 0,
                required: true,
                validator(value) {
                    return value >= 0 && value <= 1;
                },
            },
            // 主题
            theme: {
                type: String,
                default: 'primary',
                validator(value) {
                    return ['primary', 'success', 'warning', 'danger'].indexOf(value) > -1;
                },
            },
            // 尺寸
            size: {
                type: String,
                validator(value) {
                    return ['sm'].indexOf(value) > -1;
                },
            },
        },
    };
</script>


================================================
FILE: src/scripts/components/Progressbars.vue
================================================
<template>
    <div
        class="progressbars"
        :class="{
            [`progressbars-${size}`]: size,
            [`progressbars-${theme}`]: theme,
        }">
        <div class="line-bg" :style="`background-color: ${backgroundColor};`">
            <div class="line-progress" :style="`width: ${this.progressWidth}; background-color: ${this.foregroundColor};`"></div>
        </div>
        <div
            v-for="(nodeData, index) in nodeDataList"
            :key="index"
            :class="getNodeClass(index)"
            :style="getNodeStyle(index)"
            >
            <div class="info">
                <slot :name="`infoSlot${index}`" />
            </div>
        </div>
    </div>
</template>

<script>
    export default {
        name: 'progressbars',
        data() {
            return {
                circleWidth: 0.8,
            }
        },
        props: {
            // 所有节点数值
            nodeDataList: {
                type: Array,
                default: [0, 10, 50],
                required: true,
                validator(value) {
                    return value && value.length >= 2;
                }
            },
            // 当前数值
            currentData: {
                type: Number,
                default: 0,
                required: true,
            },
            // 所有节点展示内容
            nodeInfoList: {
                type: Array,
                default: null,
                // validator(value) {
                //     if (value) {
                //         return value.length === this.nodeDataList.length;
                //     }
                //
                //     return
                // }
            },
            // 主题
            theme: {
                type: String,
                default: 'primary',
                validator(value) {
                    return ['primary', 'success', 'warning', 'danger'].indexOf(value) > -1;
                },
            },
            // 尺寸
            size: {
                type: String,
                default: 'sm',
                validator(value) {
                    return ['xsm', 'sm', 'lg'].indexOf(value) > -1;
                },
            },
            // 节点形状
            nodeShape: {
                type: String,
                default: 'dot',
                validator(value) {
                    return ['circle', 'dot', 'checkCircle'].indexOf(value) > -1;
                },
            },
            // 进度条背景色
            backgroundColor: {
                type: String,
                default: '#DEE0E8',
            },
            // 进度条前景色
            foregroundColor: {
                type: String,
                default: '#1C89EA',
            },
        },
        computed: {
            nodesLength: function () {
                return this.nodeDataList.length
            },
            // 方案一
            // 进度条百分比 + N个节点宽度 = 精确的进度条长度
            //
            // 方案二:
            // (100% - 所有节点宽度)/(所有节点数 - 1)*(所处区间头节点index + 占当前区间百分比)+
            // (所处区间头节点index + 1)* 一个节点宽度 =
            // 精确区间百分比 + 包含所有节点宽度 =
            // 精确的进度条长度
            progressWidth: function () {
                /* eslint-disable */
                // debugger
                // 最高等级
                if (this.currentDotIndex === this.nodesLength - 1) {
                    return '100%'
                }
                // 方案一:总体进度
                // const widthPercent =
                // // 每个区间占比
                // 1 / (this.nodeDataList.length - 1) *
                // (
                //     this.currentDotIndex +
                //       // 当前区间分值
                //       ((this.currentData - this.nodeDataList[this.currentDotIndex]) /
                //         // 对应区间总分值
                //         (this.nodeDataList[this.currentDotIndex + 1] - this.nodeDataList[this.currentDotIndex]))
                //   )
                //
                // return `calc(${widthPercent * 100}% + ${(this.currentDotIndex + 1) * this.circleWidth}em)`

                // 方案二:以下是经过拆解后的计算过程
                // 当前区间占比
                const currentAreaPercent =
                    // 当前区间分值
                    ((this.currentData - this.nodeDataList[this.currentDotIndex]) /
                      // 对应区间总分值
                      (this.nodeDataList[this.currentDotIndex + 1] - this.nodeDataList[this.currentDotIndex]))
                const part1 = `(100% - ${this.nodesLength * this.circleWidth}em)`
                const part2 = `${this.nodesLength - 1}`
                const part3 = `${this.currentDotIndex + currentAreaPercent}`
                const part4 = `${(this.currentDotIndex + 1) * this.circleWidth}em`

                return `calc(${part1} / ${part2} * ${part3} + ${part4})`
            },
            currentDotIndex: function () {
                  let currentDotIndex = 0

                  for (let i = 0; i < this.nodeDataList.length; i++) {
                    if (i === (this.nodeDataList.length - 1)) {
                      if (this.currentData >= this.nodeDataList[i]) {
                        currentDotIndex = i
                      }
                    } else {
                      if (this.currentData >= this.nodeDataList[i] && this.currentData < this.nodeDataList[i + 1]) {
                        currentDotIndex = i
                        break
                      }
                    }
                  }

                  return currentDotIndex
            },
        },
        methods: {
            getNodeClass(index) {
              let nodeClass = 'node '

              if (this.nodeShape === 'dot') {
                  nodeClass += 'dot '
              } else if (this.nodeShape === 'circle') {
                  nodeClass += 'circle '
              } else if (this.nodeShape === 'checkCircle') {
                  nodeClass += 'check-circle '
              }

              if (index <= this.currentDotIndex) {
                nodeClass += 'active '
              }
              if (index === this.currentDotIndex) {
                nodeClass += 'current-dot '
              }

              return nodeClass
            },
            getNodeStyle(index) {
                let nodeStyle = ''

                if (this.nodeShape === 'dot') {
                    nodeStyle = 'border-color: '
                } else if (this.nodeShape === 'circle') {
                    nodeStyle = 'background-color: '
                } else if (this.nodeShape === 'checkCircle') {
                    // nodeStyle = 'color: '
                    nodeStyle = 'background-color: '
                }

                if (index <= this.currentDotIndex) {
                    nodeStyle += `${this.foregroundColor};`
                } else {
                    nodeStyle += `${this.backgroundColor};`
                }

                return `${this.getCirclePosition(index)} ${nodeStyle}`
            },
            getCirclePosition(index) {
                // (全长 - 一个节点宽度) / (节点数 - 1) * 节点索引 = 节点left定位
                // return `left: calc(100%/${this.nodesLength-1}*${index} - ${this.circleWidth / (this.nodesLength -1 ) * index}em);`
                return `left: calc((100% - ${this.circleWidth}em) / ${this.nodesLength - 1} * ${index});`
            },
        },
    };
</script>


================================================
FILE: src/scripts/components/Radio.vue
================================================
<template>
    <label
        class="radio"
        :class="'radio-' + shape">
        <input
            type="radio"
            class="radio-input"
            v-model="model"
            :value="isGroupChildComponent ? value : val"
            :disabled="disabled || $parent.disabled">
        <span class="radio-addon">
            <i v-if="shape==='default'" />
            <i v-else class="fa fa-check" />
        </span>
        <span class="radio-label">
            <slot></slot>
        </span>
    </label>
</template>

<script>
    export default {
        name: 'radio',
        props: {
            shape: {
                type: String,
                default: 'default',
                validator(value) {
                    return ['square', 'circle', 'default'].indexOf(value) > -1;
                },
            },
            value: {},
            val: {},
            disabled: {
                type: Boolean,
                default: false,
            },
        },
        computed: {
            model: {
                get() {
                    return this.isGroupChildComponent ? this.$parent.value : this.value;
                },
                set(val) {
                    if(this.isGroupChildComponent) {
                        this.$parent.$emit('input', val);
                    } else {
                        this.$emit('input', val);
                    }
                },
            },
        },
        beforeCreate() {
            this.isGroupChildComponent = this.$parent.$options._componentTag === 'radio-group';
        },
    };
</script>


================================================
FILE: src/scripts/components/RadioGroup.vue
================================================
<template>
    <div class="radio-group">
        <slot></slot>
    </div>
</template>

<script>
    import Sync from '../mixins/sync';

    export default {
        name: 'radio-group',
        mixins: [Sync],
    };
</script>


================================================
FILE: src/scripts/components/Searchbar.vue
================================================
<template>
    <div class="searchbar" :class="{active: focus}" v-disfavor="blur">
        <slot></slot>
    </div>
</template>

<script>
    import Sync from '../mixins/sync';

    export default {
        name: 'searchbar',
        mixins: [Sync],
        props: {
            clearable: {
                type: Boolean,
                default: true,
            },
            shape: {
                type: String,
                validator(value) {
                    return ['circle'].indexOf(value) > -1;
                },
            },
            autoBlur: {
                type: Boolean,
                default: true,
            },
        },
        data() {
            return {
                focus: false,
            };
        },
        methods: {
            blur() {
                this.autoBlur && (this.focus = false);
            },
        },
    };
</script>


================================================
FILE: src/scripts/components/SearchbarBtn.vue
================================================
<template>
    <div class="searchbar-btn">
        <slot></slot>
    </div>
</template>

<script>
    export default {
        name: 'searchbar-btn',
    };
</script>


================================================
FILE: src/scripts/components/SearchbarPlaceholder.vue
================================================
<template>
    <div
        @click="clickHandle"
        class="searchbar-input"
        :class="{
            'border-circle': $parent.shape === 'circle'
        }">
        <div
            class="searchbar-input-placeholder"
            :style="{overflow: !!$parent.currentValue ? 'hidden' : 'visible'}">
            <slot></slot>
        </div>
        <input
            type="text"
            class="searchbar-input-field"
            v-model="currentValue"
            ref="input">
        <icon
            v-show="$parent.clearable && !!$parent.currentValue"
            class="searchbar-input-clear"
            name="times-circle"
            @click="currentValue = ''"
            size="lg" />
    </div>
</template>

<script>
    export default {
        name: 'searchbar-placeholder',
        data() {
            return {
                currentValue: this.$parent.value,
            };
        },
        watch: {
            currentValue(val) {
                this.$parent.currentValue = val;
            },
        },
        methods: {
            clickHandle() {
                this.$parent.focus = true;
                setTimeout(() => {
                    this.$refs.input.focus();
                }, 300);
            },
        },
    };
</script>


================================================
FILE: src/scripts/components/SegmentedControl.vue
================================================
<template>
    <div class="segmented-control" :class="{disabled}">
        <slot></slot>
    </div>
</template>

<script>
    import Tab from '../mixins/tab';

    export default {
        name: 'segmented-control',
        mixins: [Tab],
        props: {
            activeKey: {
                default: 0,
            },
        },
    };
</script>


================================================
FILE: src/scripts/components/SegmentedControlItem.vue
================================================
<template>
    <div
        class="segmented-control-item"
        :class="{
            'active': $parent.currentActiveKey === currentEventKey,
            disabled: disabled || $parent.disabled,
        }"
        @click="clickHandle">
        <slot></slot>
    </div>
</template>

<script>
    import TabItem from '../mixins/tabItem';

    export default {
        name: 'segmented-control-item',
        mixins: [TabItem],
    };
</script>


================================================
FILE: src/scripts/components/Selector.vue
================================================
<template>
    <div class="selector">
        <slot></slot>
    </div>
</template>

<script>
    import Sync from '../mixins/sync';
    import Select from '../mixins/select';

    export default {
        name: 'selector',
        mixins: [Sync, Select],
    };
</script>


================================================
FILE: src/scripts/components/SelectorOption.vue
================================================
<template>
    <div
        class="cell selector-option"
        :class="{
            'active': isActive(),
            'cell-disabled': disabled || $parent.disabled }"
        @click="clickHandle">
        <span slot="header">
        </span>
        <span class="cell-body">
            <slot></slot>
        </span>
        <icon
            :name="checkedIcon"
            size="lg"
            class="selector-icon" />
    </div>
</template>

<script>
    import SelectOption from '../mixins/selectOption';

    export default {
        name: 'selector-option',
        mixins: [SelectOption],
    };
</script>


================================================
FILE: src/scripts/components/Sidelip.vue
================================================
<template>
    <div @click="$emit('click')">
        <transition :name="transition">
            <div v-show="currentValue" class="sidelip">
                <slot></slot>
            </div>
        </transition>
        <mask-layer :clickable="closeOnClickMask" />
    </div>
</template>

<script>
    import Sync from '../mixins/sync';

    export default {
        name: 'sidelip',
        mixins: [Sync],
        props: {
            transition: {
                type: String,
                default: 'slide-left',
            },
            closeOnClickMask: {
                type: Boolean,
                default: true,
            },
        },
        watch: {
            currentValue(val) {
                !val && (this.$emit('close'));
            },
        },
    };
</script>


================================================
FILE: src/scripts/components/SlideUp.vue
================================================
<template>
    <div>
        <transition :name="transition">
            <div v-show="currentValue" class="slideup">
                <slot></slot>
            </div>
        </transition>
        <mask-layer :clickable="closeOnClickMask" />
    </div>
</template>

<script>
    import Sync from '../mixins/sync';

    export default {
        name: 'slideup',
        mixins: [Sync],
        props: {
            transition: {
                type: String,
                default: 'slide-up',
            },
            closeOnClickMask: {
                type: Boolean,
                default: true,
            },
        },
        watch: {
            currentValue(val) {
                val ? this.$emit('show') : this.$emit('close');
            },
        },
    };
</script>


================================================
FILE: src/scripts/components/SlideUpBody.vue
================================================
<template>
    <div class="slideup-body" :class="{'no-padding': noPadding}">
        <slot></slot>
    </div>
</template>

<script>
    export default {
        name: 'slideup-body',
        props: {
            noPadding: Boolean,
        },
    };
</script>
=


================================================
FILE: src/scripts/components/SlideUpHeader.vue
================================================
<template>
    <div class="slideup-header">
        <slot></slot>
    </div>
</template>

<script>
    export default {
        name: 'slideup-header',
    };
</script>


================================================
FILE: src/scripts/components/Stepbar.vue
================================================
<template>
    <div class="stepbar">
        <slot></slot>
    </div>
</template>

<script>
    import Sync from '../mixins/sync';

    export default {
        name: 'stepbar',
        mixins: [Sync],
        props: {
            size: {
                type: String,
                validator(value) {
                    return ['sm', 'lg'].indexOf(value) > -1;
                },
            },
        },
    };
</script>


================================================
FILE: src/scripts/components/StepbarItem.vue
================================================
<template>
    <div class="stepbar-item">
        <div
            class="stepbar-line"
            :class="{active: state === 'active' || state === 'finished' }"
            v-if="!isFirstChild"></div>
        <div
            class="stepbar-item-addon"
            :class="{[state]: state !== 'default'}">
            <icon name="check" v-if="state === 'finished'"/>
            {{state === 'finished' ? '' : (index + 1)}}
        </div>
        <div
            class="stepbar-item-title"
            :class="{[state]: state !== 'default'}">
            <slot></slot>
        </div>
        <div
            class="stepbar-line"
            :class="{active: state === 'finished' }"
            v-if="!isLastChild"></div>
    </div>
</template>

<script>
    export default {
        name: 'stepbar-item',
        props: {
            description: {
                type: String,
            },
        },
        data() {
            return {
                isFirstChild: false,
                isLastChild: false,
                state: 'default',
                index: 0,
            };
        },
        mounted() {
            let length = this.$parent.$children.length;

            this.index = this.$parent.$children.indexOf(this);

            this.index === 0 && (this.isFirstChild = true);
            (this.index === length - 1) && (this.isLastChild = true);

            switch(true) {
                case this.index === this.$parent.currentValue:
                    this.state = 'active';
                    break;
                case this.index < this.$parent.currentValue:
                    this.state = 'finished';
                    break;
                default:
                    this.state = 'default';
            }
        },
    };
</script>


================================================
FILE: src/scripts/components/Sticky.vue
================================================
<template>
    <div>
        <div :class="classes" :style="styles">
            <slot></slot>
        </div>
    </div>
</template>

<script>
    const prefixCls = 'sticky-affix';

    function getScroll(target, top) {
        const prop = top ? 'pageYOffset' : 'pageXOffset';
        const method = top ? 'scrollTop' : 'scrollLeft';
        let ret = target[prop];

        if(typeof ret !== 'number') {
            ret = window.document.documentElement[method];
        }

        return ret;
    }

    function getOffset(element) {
        const rect = element.getBoundingClientRect();
        const scrollTop = getScroll(window, true);
        const scrollLeft = getScroll(window);
        const docEl = window.document.body;
        const clientTop = docEl.clientTop || 0;
        const clientLeft = docEl.clientLeft || 0;

        return {
            top: rect.top + scrollTop - clientTop,
            left: rect.left + scrollLeft - clientLeft,
        };
    }

    export default {
        name: 'sticky',
        props: {
            offsetTop: {
                type: Number,
                default: 0,
            },
            offsetBottom: {
                type: Number,
            },
        },
        data() {
            return {
                affix: false,
                styles: {},
            };
        },
        computed: {
            offsetType() {
                let type = 'top';

                if(this.offsetBottom >= 0) {
                    type = 'bottom';
                }

                return type;
            },
            classes() {
                return [
                    {
                        [`${prefixCls}`]: this.affix,
                    },
                ];
            },
        },
        mounted() {
            window.addEventListener('scroll', this.handleScroll, false);
            window.addEventListener('resize', this.handleScroll, false);
        },
        beforeDestroy() {
            window.removeEventListener('scroll', this.handleScroll, false);
            window.removeEventListener('resize', this.handleScroll, false);
        },
        methods: {
            handleScroll() {
                const affix = this.affix;
                const scrollTop = getScroll(window, true);
                const elOffset = getOffset(this.$el);
                const windowHeight = window.innerHeight;
                const elHeight = this.$el.getElementsByTagName('div')[0].offsetHeight;

                // Fixed Top
                if((elOffset.top - this.offsetTop) < scrollTop && this.offsetType === 'top' && !affix) {
                    this.affix = true;
                    this.styles = {
                        top: `${this.offsetTop}px`,
                        left: `${elOffset.left}px`,
                        width: `${this.$el.offsetWidth}px`,
                    };
                    this.$emit('on-change', true);
                } else if((elOffset.top - this.offsetTop) > scrollTop && this.offsetType === 'top' && affix) {
                    this.affix = false;
                    this.styles = null;
                    this.$emit('on-change', false);
                }
                // Fixed Bottom
                if((elOffset.top + this.offsetBottom + elHeight) > (scrollTop + windowHeight) && this.offsetType === 'bottom' && !affix) {
                    this.affix = true;
                    this.styles = {
                        bottom: `${this.offsetBottom}px`,
                        left: `${elOffset.left}px`,
                        width: `${this.$el.offsetWidth}px`,
                    };
                    this.$emit('on-change', true);
                } else if((elOffset.top + this.offsetBottom + elHeight) < (scrollTop + windowHeight) && this.offsetType === 'bottom' && affix) {
                    this.affix = false;
                    this.styles = null;
                    this.$emit('on-change', false);
                }
            },
        },
    };
</script>


================================================
FILE: src/scripts/components/Swipe.vue
================================================
<template>
    <div class="swipe" ref="swipe">
        <div class="swipe-items">
            <slot></slot>
        </div>
        <div class="swipe-indicators" v-show="length > 1 ? dots : (dots && onlyOneDot)">
            <div
                v-for="index in length"
                :key="index"
                class="swipe-indicator"
                :class="{active: index - 1 === activeIndex}"
                ></div>
        </div>
    </div>
</template>

<script>
    import draggable from '../utils/draggable';

    export default {
        name: 'swipe',
        props: {
            // 自动播放
            autoplay: {
                type: Boolean,
                default: true,
            },
            // 默认起始项
            defaultIndex: {
                type: Number,
                default: 0,
            },
            // 是否显示指示器
            dots: {
                type: Boolean,
                default: true,
            },
            // 只有一张图片时,是否显示指示器
            onlyOneDot: {
                type: Boolean,
                default: true,
            },
            // 轮播间隔时间
            interval: {
                type: Number,
                default: 3000,
            },
            // 轮播速度
            speed: {
                type: Number,
                default: 300,
            },
            dragThreshold: {
                type: Number,
                default: 1 / 2,
            },
            dragRate: {
                type: Number,
                default: 0.2,
            },
            onDragStart: Function,
            onDrag: Function,
            onDragEnd: Function,
            // 过渡效果
            easing: {
                type: String,
                default: 'ease-in-out',
            },
            // 循环播放
            cycle: {
                type: Boolean,
                default: true,
            },
        },
        data() {
            return {
                length: 0,
                dragging: false,
                transitioning: false,
                negative: false,
                activeIndex: this.defaultIndex,
                isCycleEnd: false,
            };
        },
        methods: {
            init() {
                this.length = this.$children.length;
            },
            // 获取前一个索引
            getPrevIndex() {
                let prevIndex = this.activeIndex - 1;

                if(!this.cycle) {
                    return prevIndex < 0 ? 0 : prevIndex;
                }

                return prevIndex < 0 ? this.length + prevIndex : prevIndex;
            },
            // 获取后一个索引
            getNextIndex() {
                const nextIndex = this.activeIndex + 1;
                const length = this.length - 1;

                if(!this.cycle) {
                    return nextIndex > length ? length : nextIndex;
                }

                return nextIndex > length ? nextIndex % this.length : nextIndex;
            },
            // 是禁止循环
            isDisableCycle(nextIndex) {
                if(!this.cycle && nextIndex === this.activeIndex) {
                    return true;
                }

                return false;
            },
            // 初始化拖拽
            initDrag() {
                if(this.length <= 1) {
                    return;
                }

                let { swipe } = this.$refs,
                    translateX,
                    newIndex,
                    dragStartTime;

                draggable(swipe, {
                    onDragStart: () => {
                        // event.preventDefault();
                        this.onDragStart && this.onDragStart(this.activeIndex);

                        if(this.transitioning) return;

                        dragStartTime = new Date();
                        this.dragging = true;
                        this.isCycleEnd = false;
                        clearInterval(this.swipeInterval);
                    },
                    onDrag: (option, event) => {
                        event.preventDefault();
                        if(this.transitioning) return;

                        translateX = option.translateX;
                        this.onDrag && this.onDrag(this.activeIndex, translateX);

                        if(translateX === 0 || !this.$children[this.activeIndex]) return;
                        // 往左
                        if(translateX < 0) {
                            let nextIndex = this.getNextIndex();

                            // 禁止循环播放
                            if(this.isDisableCycle(nextIndex)) {
                                this.isCycleEnd = true;

                                return;
                            }

                            this.$children[this.activeIndex].swipeToLeft(translateX);
                            this.$children[nextIndex].swipeToLeft(translateX);

                            this.negative = false;
                            newIndex = nextIndex;
                        } else {
                            // 往右
                            let prevIndex = this.getPrevIndex();

                            // 禁止循环播放
                            if(this.isDisableCycle(prevIndex)) {
                                this.isCycleEnd = true;

                                return;
                            }

                            this.$children[this.activeIndex].swipeToRight(translateX);
                            this.$children[prevIndex].swipeToRight(translateX);

                            this.negative = true;
                            newIndex = prevIndex;
                        }
                    },
                    onDragEnd: () => {
                        // event.preventDefault();
                        if(this.transitioning) return;

                        this.dragging = false;

                        if(!this.isCycleEnd && this.$children[this.activeIndex]) {
                            let threshold = this.$children[this.activeIndex].width * this.dragThreshold,
                                rate = Math.abs(translateX) / (new Date() - dragStartTime);

                            if(Math.abs(translateX) >= threshold || rate > this.dragRate) {
                                this.activeIndex = newIndex;
                            } else if(this.negative) {
                                let prevIndex = this.getPrevIndex();

                                this.$children[this.activeIndex].swipeToLeft(0);
                                this.$children[prevIndex].swipeToLeft(0);
                            } else {
                                // 往左
                                let nextIndex = this.getNextIndex();

                                this.$children[this.activeIndex].swipeToRight(0);
                                this.$children[nextIndex].swipeToRight(0);
                            }
                            this.onDragEnd && this.onDragEnd(this.activeIndex);
                        }

                        setTimeout(() => {
                            this.play();
                        }, this.interval);
                    },
                });
            },
            // 自动播放
            play() {
                if(!this.autoplay) return;

                clearInterval(this.swipeInterval);
                this.swipeInterval = setInterval(() => {
                    if(this.dragging) return;

                    this.activeIndex = this.getNextIndex();
                }, this.interval);
            },
        },
        watch: {
            activeIndex() {
                this.transitioning = true;

                let nextItem = this.$children[this.activeIndex],
                    currentIndex = this.negative ? this.getNextIndex() : this.getPrevIndex(),
                    currentItem = this.$children[currentIndex];

                // 重置()
                // this.$children.forEach((child, activeIndex) => {
                //     if(activeIndex !== currentIndex) {
                //         child.reset(this.negative);
                //     }
                // });

                if(!this.negative) {
                    currentItem.swipeToLeft();
                    nextItem.swipeToLeft();
                } else {
                    currentItem.swipeToRight();
                    nextItem.swipeToRight();
                    this.negative = false;
                }
            },
            // 异步swipeItem
            length() {
                if(this.length) {
                    this.initDrag();
                    this.play();
                }
            },
        },
        mounted() {
            this.init();
        },
        updated() {
            this.$nextTick(() => {
                this.init();
            });
            setTimeout(() => {
                this.transitioning = false;
            }, this.speed);
        },
        beforeDestroy() {
            clearInterval(this.swipeInterval);
        },
    };
</script>


================================================
FILE: src/scripts/components/SwipeItem.vue
================================================
<template>
    <div class="swipe-item"
        @webkitTransitionEnd="resetByTranslateX()">
        <slot></slot>
    </div>
</template>

<script>
    import { getTranslate, setTranslate } from '../utils/translate';
    import { getPrefixStyle } from '../utils/cssPrefix';

    export default {
        name: 'swipe-item',
        data() {
            return {
                width: 0,
                translate: 0,
            };
        },
        methods: {
            // 根据位移重置
            resetByTranslateX() {
                this.$el.style.transition = '';

                let translateX = getTranslate(this.$el).x;

                translateX < 0 && setTranslate(this.$el, this.width, 0);
            },
            // 根据是否active重置
            resetByNegative(negative) {
                this.$el.style.transition = '';
                if(this.index !== this.$parent.activeIndex) {
                    setTranslate(this.$el, negative ? -this.width : this.width, 0);
                }
            },
            // 初始化
            init() {
                this.width = this.$el.offsetWidth;
                this.index = this.$parent.$children.indexOf(this);
                let translate = this.width;

                if(this.$parent.defaultIndex === this.index) {
                    translate = 0;
                }

                setTranslate(this.$el, translate, 0);
            },
            // 是否在可编译范围内
            isInTheLimitRange(translate, negative = false) {
                let min,
                    max;

                // 正序
                if(!negative) {
                    min = 0;
                    max = this.width;

                    if(this.index === this.$parent.activeIndex) {
                        min = -this.width;
                        max = 0;
                    }
                } else {
                    min = -this.width;
                    max = 0;

                    if(this.index === this.$parent.activeIndex) {
                        min = 0;
                        max = this.width;
                    }
                }

                return translate >= min && translate <= max;
            },
            // 向左滑动
            swipeToLeft(translateX) {
                // 轮播
                if(!translateX) {
                    this.$el.style.transition = getPrefixStyle('transform', `${this.$parent.speed}ms ${this.$parent.easing}`);

                    let translate = -this.width;

                    if(this.index === this.$parent.activeIndex) {
                        translate = 0;
                    }

                    setTranslate(this.$el, translate, 0);

                    return;
                }

                // 滑动
                let initTranslate = this.width;

                if(this.index === this.$parent.activeIndex) {
                    initTranslate = 0;
                }

                let finalTranslateX = initTranslate + translateX;

                if(this.isInTheLimitRange(finalTranslateX)) {
                    setTranslate(this.$el, finalTranslateX, 0);
                }
            },
            // 向右滑动
            swipeToRight(translateX) {
                // 轮播
                if(!translateX) {
                    this.$el.style.transition = getPrefixStyle('transform', `${this.$parent.speed}ms ${this.$parent.easing}`);

                    let translate = this.width;

                    if(this.index === this.$parent.activeIndex) {
                        translate = 0;
                    }

                    setTranslate(this.$el, translate, 0);

                    return;
                }

                // 滑动
                let initTranslate = -this.width;

                if(this.index === this.$parent.activeIndex) {
                    initTranslate = 0;
                }

                let finalTranslateX = initTranslate + translateX;

                if(this.isInTheLimitRange(finalTranslateX, true)) {
                    setTranslate(this.$el, finalTranslateX, 0);
                }
            },
        },
        mounted() {
            this.init();
        },
    };
</script>


================================================
FILE: src/scripts/components/Tabbar.vue
================================================
<template>
    <div class="tabbar" :class="{disabled}">
        <slot></slot>
        <div class="tabbar-indicator" ref="indicator"></div>
    </div>
</template>

<script>
    import Tab from '../mixins/tab';
    import { setTranslate } from '../utils/translate';

    export default {
        name: 'tabbar',
        mixins: [Tab],
        props: {
            activeKey: {
                default: 0,
            },
        },
        methods: {
            // 设置指示器宽度
            setIndicatorWidth() {
                this.indicatorWidth = this.$el.offsetWidth / this.$children.length;
                this.$refs.indicator.style.width = `${this.indicatorWidth}px`;
            },
            // 初始化指示器位置
            initSelectedIndicator() {
                this.$children.forEach((child, index) => {
                    if(this.currentActiveKey === child.currentEventKey) {
                        let $indicator = this.$refs.indicator,
                            translateX = index * this.indicatorWidth;

                        setTranslate($indicator, translateX, 0);
                    }
                });
            },
        },
        watch: {
            currentActiveKey() {
                this.initSelectedIndicator();
            },
        },
        mounted() {
            this.setIndicatorWidth();
            this.initSelectedIndicator();
        },
        updated() {
            this.setIndicatorWidth();
            this.initSelectedIndicator();
        },
    };
</script>


================================================
FILE: src/scripts/components/TabbarItem.vue
================================================
<template>
    <div
        class="tabbar-item"
        :class="{
            'active': $parent.currentActiveKey === currentEventKey,
            disabled: disabled || $parent.disabled,
        }"
        @click="clickHandle">
        <slot></slot>
    </div>
</template>

<script>
    import TabItem from '../mixins/tabItem';

    export default {
        name: 'tabbar-item',
        mixins: [TabItem],
    };
</script>


================================================
FILE: src/scripts/components/Tag.vue
================================================
<template>
    <span
        @click="$emit('click')"
        class="tag"
        :class="{
            [`tag-${theme}`]: !hollow,
            [`tag-outline-${theme}`]: hollow,
            [`tag-${size}`]: size,
            [`border-${shape}`]: shape,
        }">
        <slot></slot>
    </span>
</template>

<script>
    export default {
        name: 'tag',
        props: {
            // 空心
            hollow: Boolean,
            // 尺寸
            size: {
                type: String,
                validator(value) {
                    return ['sm'].indexOf(value) > -1;
                },
            },
            // 主题
            theme: {
                type: String,
                default: 'primary',
                validator(value) {
                    return ['primary', 'default', 'success', 'warning', 'danger'].indexOf(value) > -1;
                },
            },
            // 形状
            shape: {
                type: String,
                validator(value) {
                    return ['pill'].indexOf(value) > -1;
                },
            },
        },
    };
</script>


================================================
FILE: src/scripts/components/Timeline.vue
================================================
<template>
    <div class="timeline">
        <slot></slot>
    </div>
</template>

<script>
    export default {
        name: 'timeline',
    };
</script>


================================================
FILE: src/scripts/components/TimelineItem.vue
================================================
<template>
    <div class="timeline-item" :class="{'active': index === 0}">
        <div class="timeline-item-addon"></div>
        <div class="timeline-item-line"></div>
        <div class="timeline-item-body">
            <slot></slot>
        </div>
    </div>
</template>

<script>
    export default {
        name: 'timeline-item',
        data() {
            return {
                index: -1,
            };
        },
        mounted() {
            this.index = this.$parent.$children.indexOf(this);
        },
    };
</script>


================================================
FILE: src/scripts/components/Tip.vue
================================================
<template>
    <div class="tip" @click="$emit('click')">
        <hrule :type="type" :theme="theme" />
        <span class="tip-label" :class="{[`text-${theme}`]: theme}">
            <slot></slot>
        </span>
        <hrule :type="type" :theme="theme" />
    </div>
</template>

<script>
    export default {
        name: 'tip',
        props: {
            type: {
                type: String,
                validator(value) {
                    return ['dashed', 'dotted'].indexOf(value) > -1;
                },
            },
            theme: {
                type: String,
                validator(value) {
                    return ['primary', 'secondary', 'warning', 'success', 'danger'].indexOf(value) > -1;
                },
            },
        },
    };
</script>


================================================
FILE: src/scripts/components/Toast.vue
================================================
<template>
    <div>
        <transition name="toast-fade">
            <div
                v-show="currentValue"
                class="toast"
                :class="[
                    `toast-${position}`,
                    { 'toast-lg': type }
                ]">
                <div class="toast-icon" v-if="icon">
                    <icon :name="icon" />
                </div>
                <loading v-else-if="type === 'loading'" />
                <div class="toast-message" v-html="message" />
            </div>
        </transition>
        <mask-layer
            :clickable="closeOnClickMask"
            class="bg-transparent"
            v-if="!!type"/>
    </div>
</template>

<script>
    export default {
        name: 'toast',
        props: {
            message: String,
            position: {
                type: String,
                default: 'bottom',
                validator(value) {
                    return ['bottom', 'top', 'center'].indexOf(value) > -1;
                },
            },
            type: {
                type: String,
                validator(value) {
                    return ['success', 'error', 'warning', 'loading'].indexOf(value) > -1;
                },
            },
            closeOnClickMask: {
                type: Boolean,
                default: false,
            },
        },
        data() {
            return {
                currentValue: false,
            };
        },
        computed: {
            icon() {
                switch(this.type) {
                    case 'error':
                        return 'exclamation-circle';
                    case 'success':
                        return 'check';
                    case 'warning':
                        return 'exclamation-triangle';
                    default:
                        return null;
                }
            },
        },
    };
</script>


================================================
FILE: src/scripts/components/Toggle.vue
================================================
<template>
    <label class="toggle">
        <input
            type="checkbox"
            class="toggle-input"
            :disabled="disabled"
            v-model="currentValue">
        <span class="toggle-addon">
            <i />
        </span>
    </label>
</template>

<script>
    import Sync from '../mixins/sync';

    export default {
        name: 'toggle',
        mixins: [Sync],
    };
</script>


================================================
FILE: src/scripts/components/index.js
================================================
import Button from './Button';
import Icon from './Icon';
import Group from './Group';
import GroupTitle from './GroupTitle';
import Cell from './Cell';
import Flex from './Flex';
import FlexItem from './FlexItem';
import Navbar from './Navbar';
import Tabbar from './Tabbar';
import TabbarItem from './TabbarItem';
import Mask from './Mask';
import Loading from './Loading';
import Checkbox from './Checkbox';
import CheckboxGroup from './CheckboxGroup';
import Radio from './Radio';
import RadioGroup from './RadioGroup';
import Toggle from './Toggle';
import InputNumber from './InputNumber';
import InputText from './InputText';
import InputTextarea from './InputTextarea';
import Selector from './Selector';
import SelectorOption from './SelectorOption';
import InlineSelector from './InlineSelector';
import InlineSelectorOption from './InlineSelectorOption';
import Navigation from './Navigation';
import NavigationItem from './NavigationItem';
import Tag from './Tag';
import Tip from './Tip';
import HRule from './HRule';
import BackToTop from './BackToTop';
import Badge from './Badge';
import Drawer from './Drawer';
import DrawerItem from './DrawerItem';
import SlideUp from './SlideUp';
import SlideUpHeader from './SlideUpHeader';
import SlideUpBody from './SlideUpBody';
import SegmentedControl from './SegmentedControl';
import SegmentedControlItem from './SegmentedControlItem';
import Sidelip from './Sidelip';
import Media from './Media';
import MediaObject from './MediaObject';
import MediaBody from './MediaBody';
import Card from './Card';
import CardBody from './CardBody';
import DatePicker from './DatePicker';
import Searchbar from './Searchbar';
import SearchbarBtn from './SearchbarBtn';
import SearchbarPlaceholder from './SearchbarPlaceholder';
import Picker from './Picker';
import PickerOption from './PickerOption';
import Loadmore from './Loadmore';
import Alert from './Alert';
import Swipe from './Swipe';
import SwipeItem from './SwipeItem';
import Progressbar from './Progressbar';
import Progressbars from './Progressbars';
import Stepbar from './Stepbar';
import StepbarItem from './StepbarItem';
import Timeline from './Timeline';
import TimelineItem from './TimelineItem';
import Sticky from './Sticky';
// global utils
import toast from '../utils/toast';
import { alert } from '../utils/alert';
import loading from '../utils/loading';
// directives
import disfavor from '../directives/disfavor';

const impression = {
    Button,
    Group,
    GroupTitle,
    Cell,
    Flex,
    FlexItem,
    Icon,
    Tag,
    Tip,
    HRule,
    Badge,
    Media,
    MediaObject,
    MediaBody,
    Card,
    CardBody,
    Swipe,
    SwipeItem,
    Navbar,
    Tabbar,
    TabbarItem,
    Navigation,
    NavigationItem,
    Drawer,
    DrawerItem,
    SegmentedControl,
    SegmentedControlItem,
    SlideUp,
    SlideUpHeader,
    SlideUpBody,
    Sidelip,
    Searchbar,
    SearchbarBtn,
    SearchbarPlaceholder,
    Picker,
    PickerOption,
    Loadmore,
    Mask,
    Alert,
    Loading,
    BackToTop,
    Checkbox,
    CheckboxGroup,
    Radio,
    RadioGroup,
    Toggle,
    InputNumber,
    InputText,
    InputTextarea,
    Selector,
    SelectorOption,
    InlineSelector,
    InlineSelectorOption,
    DatePicker,
    Progressbar,
    Progressbars,
    Stepbar,
    StepbarItem,
    Timeline,
    TimelineItem,
    Sticky,
};

const install = Vue => {
    if(install.installed) return;

    // components
    Object.keys(impression).forEach(key => {
        Vue.component(impression[key].name, impression[key]);
    });

    // global component utils
    Vue.$toast = Vue.prototype.$toast = toast;
    Vue.$alert = Vue.prototype.$alert = alert;
    Vue.$loading = Vue.prototype.$loading = loading;

    // directives
    Vue.directive('disfavor', disfavor);
};

if(typeof window !== 'undefined' && window.Vue) {
    install(window.Vue);
}

export {
    Button,
    Group,
    GroupTitle,
    Cell,
    Flex,
    FlexItem,
    Icon,
    Tag,
    Tip,
    HRule,
    Badge,
    Media,
    MediaObject,
    MediaBody,
    Card,
    CardBody,
    Swipe,
    SwipeItem,
    Navbar,
    Tabbar,
    TabbarItem,
    Navigation,
    NavigationItem,
    Drawer,
    DrawerItem,
    SegmentedControl,
    SegmentedControlItem,
    SlideUp,
    SlideUpHeader,
    SlideUpBody,
    Sidelip,
    Searchbar,
    SearchbarBtn,
    SearchbarPlaceholder,
    Picker,
    PickerOption,
    Loadmore,
    Mask,
    Alert,
    Loading,
    BackToTop,
    Checkbox,
    CheckboxGroup,
    Radio,
    RadioGroup,
    Toggle,
    InputNumber,
    InputText,
    InputTextarea,
    Selector,
    SelectorOption,
    InlineSelector,
    InlineSelectorOption,
    DatePicker,
    Progressbar,
    Stepbar,
    StepbarItem,
    Timeline,
    TimelineItem,
    Sticky,
};

export default {
    install,
    ...impression,
};


================================================
FILE: src/scripts/containers/layout.vue
================================================
<template>
    <flex direction="column">
        <navbar theme="default">
            <icon name="bars" size="lg" @click="showMenubar = true" />
            <h3 slot="body">{{$route.name || 'Impression'}}</h3>
        </navbar>
        <flex-item>
            <router-view></router-view>
        </flex-item>
        <menubar v-model="showMenubar" />
    </flex>
</template>

<script>
    import menubar from './menubar';

    export default {
        components: {
            menubar,
        },
        data() {
            return {
                showMenubar: false,
            };
        },
    };
</script>


================================================
FILE: src/scripts/containers/menubar.vue
================================================
<template>
    <sidelip v-model="currentValue">
        <flex direction="column" :style="{backgroundColor: '#f0eff5'}">
            <navbar theme="primary">
                <h3>Impression</h3>
            </navbar>
            <flex-item>
                <div v-for="group in groups">
                    <group-title v-if="group.title"><strong>{{group.title}}</strong></group-title>
                    <group>
                        <cell
                            v-for="cell in group.children"
                            :to="{path: cell.path}">
                            <icon size="lg" left :name="cell.icon"></icon>
                            {{cell.name}}
                        </cell>
                    </group>
                </div>
            </flex-item>
            <group>
                <cell>
                    NewDadaFE©️所有
                </cell>
            </group>
        </flex>
    </sidelip>
</template>

<script>
    import routesConfig from '../routes.json';

    export default {
        props: {
            value: {},
            disabled: Boolean,
        },
        data() {
            return {
                currentValue: this.value,
                groups: routesConfig,
            };
        },
        watch: {
            value(val) {
                this.currentValue = val;
            },
            currentValue(val) {
                this.$emit('input', val);
            },
            $route() {
                this.currentValue = false;
            },
        },
    };
</script>


================================================
FILE: src/scripts/directives/disfavor.js
================================================
import { contains } from '../utils/dom';

const disfavorContext = '@@disfavor';

// 失去焦点
export default {
    bind(el, binding, vnode) {
        const clickHandle = event => {
            if(vnode.context && !contains(el, event.target)) {
                el[disfavorContext].callback && vnode.context[el[disfavorContext].callback]();
            }
        };

        el[disfavorContext] = {
            clickHandle,
            callback: binding.expression,
        };

        document.addEventListener('click', clickHandle);
    },
    unbind(el) {
        let { clickHandle } = el[disfavorContext];

        document.removeEventListener('click', clickHandle);
    },
};



================================================
FILE: src/scripts/index.js
================================================
import Vue from 'vue';
import router from './router';
import Impression from './components';

Vue.use(Impression);

/* eslint-disable no-new */
new Vue({
    el: '#app',
    router,
    template: '<router-view></router-view>',
});


================================================
FILE: src/scripts/mixins/emitter.js
================================================
export default {
    methods: {
        dispatch(componentName, eventName, params) {
            let parent = this.$parent || this.$root,
                name = parent.$options._componentTag;

            while(parent && (!name || name !== componentName)) {
                parent = parent.$parent;

                if(parent) {
                    name = parent.$options._componentTag;
                }
            }

            if(parent) {
                parent.$emit.apply(parent, [eventName].concat(params));
            }
        },
    },
};


================================================
FILE: src/scripts/mixins/select.js
================================================
export default {
    props: {
        multiple: Boolean,
    },
    data() {
        return {
            currentText: {},
        };
    },
    methods: {
        optionClickHandle(option) {
            if(this.disabled || this.currentValue === option.value) return;

            if(this.multiple) {
                this.currentValue = this.currentValue || [];
                let index = this.currentValue.indexOf(option.value);

                if(index === -1) {
                    this.currentValue.push(option.value);
                } else {
                    this.currentValue.splice(index, 1);
                }

                return;
            }

            this.currentText = option.$el.innerText.trim();
            this.currentValue = option.value;
        },
    },
    created() {
        this.$on('optionClick', this.optionClickHandle);
    },
};


================================================
FILE: src/scripts/mixins/selectOption.js
================================================
export default {
    props: {
        value: {},
        checkedIcon: {
            type: String,
            default: 'check',
        },
        disabled: Boolean,
    },
    methods: {
        clickHandle() {
            if(this.disabled) return;

            this.$parent.$emit.apply(this.$parent, ['optionClick'].concat(this));
        },
        isActive() {
            if(this.$parent.multiple) {
                return this.$parent.currentValue &&
                this.$parent.currentValue.indexOf(this.value) !== -1;
            }

            return this.$parent.currentValue === this.value;
        },
    },
};


================================================
FILE: src/scripts/mixins/sync.js
================================================
import { isArray } from '../utils/type';

export default {
    props: {
        value: {},
        disabled: Boolean,
    },
    data() {
        return {
            currentValue: this.value,
        };
    },
    watch: {
        value(val) {
            this.currentValue = val;
        },
        currentValue(val) {
            if(this.disabled) return;

            if(isArray(val) && val.length === 0) {
                this.currentValue = undefined;

                return;
            }

            this.$emit('input', val);
            this.$emit('change', val, this.currentText);
        },
    },
};


================================================
FILE: src/scripts/mixins/tab.js
================================================
export default {
    props: {
        activeKey: {},
        disabled: Boolean,
    },
    data() {
        return {
            currentActiveKey: this.activeKey,
        };
    },
    methods: {
        itemClickHandle(val) {
            if(this.currentActiveKey === val) return;

            this.currentActiveKey = val;
            this.$emit('change', val);
        },
    },
    created() {
        this.$on('itemClick', this.itemClickHandle);
    },
};


================================================
FILE: src/scripts/mixins/tabItem.js
================================================
export default {
    props: {
        eventKey: {},
        disabled: Boolean,
    },
    data() {
        return {
            currentEventKey: this.eventKey,
            index: 0,
        };
    },
    mounted() {
        this.index = this.$parent.$children.indexOf(this);
        this.eventKey === undefined && (this.currentEventKey = this.index);
    },
    methods: {
        clickHandle() {
            if(this.disabled || this.$parent.disabled) return;

            this.$parent.$emit('itemClick', this.currentEventKey, this.index);
        },
    },
};


================================================
FILE: src/scripts/router.js
================================================
import Vue from 'vue';
import VueRouter from 'vue-router';
import routesConfig from './routes.json';

Vue.use(VueRouter);

// 提取路由
const extractRoutes = config => {
    const routes = [],
        children = [];

    routes.push({
        path: '/',
        component: require('./containers/layout'),
        children,
    });

    config.forEach(group => {
        group.children.forEach(item => {
            const { path, name } = item;

            children.push({
                path,
                name,
                component: require(`./views${path}`),
            });
        });
    });

    return routes;
};

const routes = extractRoutes(routesConfig);

// 创建router对象
const router = new VueRouter({
    base: __dirname,
    routes,
});

export default router;


================================================
FILE: src/scripts/routes.json
================================================
[
    {
        "title": "Base",
        "children": [
            {
                "path": "/button",
                "name": "Button",
                "icon": "hand-pointer-o"
            },
            {
                "path": "/icon",
                "name": "Icon",
                "icon": "flag"
            },
            {
                "path": "/cell",
                "name": "Group && Cell",
                "icon": "list"
            },
            {
                "path": "/flex",
                "name": "Flex && FlexItem",
                "icon": "th"
            },
            {
                "path": "/tag",
                "name": "Tag",
                "icon": "tag"
            },
            {
                "path": "/badge",
                "name": "Badge",
                "icon": "bell-o"
            },
            {
                "path": "/hrule",
                "name": "HRule(Hr)",
                "icon": "minus"
            },
            {
                "path": "/tip",
                "name": "Tip",
                "icon": "paperclip"
            },
            {
                "path": "/card",
                "name": "Card",
                "icon": "file-image-o"
            },
            {
                "path": "/media",
                "name": "Media",
                "icon": "id-card-o"
            },
            {
                "path": "/swipe",
                "name": "Swipe",
                "icon": "newspaper-o"
            },
            {
                "path": "/datepicker",
                "name": "DatePicker",
                "icon": "newspaper-o"
            }
            
        ]
    },
    {
        "title": "Layout",
        "children": [
            {
                "path": "/navbar",
                "name": "Navbar",
                "icon": "window-maximize"
            },
            {
                "path": "/tabbar",
                "name": "Tabbar",
                "icon": "clone"
            },
            {
                "path": "/navigation",
                "name": "Navigation",
                "icon": "tablet"
            },
            {
                "path": "/drawer",
                "name": "Drawer",
                "icon": "caret-square-o-down"
            },
            {
                "path": "/segmented-control",
                "name": "SegmentedControl",
                "icon": "columns"
            },
            {
                "path": "/slideup",
                "name": "SlideUp",
                "icon": "angle-double-up"
            },
            {
                "path": "/sideslip",
                "name": "Sideslip",
                "icon": "window-restore"
            },
            {
                "path": "/searchbar",
                "name": "Searchbar",
                "icon": "search"
            },
            {
                "path": "/stepbar",
                "name": "Stepbar",
                "icon": "flag-checkered"
            },
            {
                "path": "/timeline",
                "name": "Timeline",
                "icon": "flag-o"
            },
            {
                "path": "/picker",
                "name": "Picker",
                "icon": "building-o"
            },
            {
                "path": "/pull-down",
                "name": "Pull down",
                "icon": "long-arrow-down"
            },
            {
                "path": "/pull-up",
                "name": "Pull up",
                "icon": "long-arrow-up"
            },
            {
                "path": "/sticky",
                "name": "Sticky Affix",
                "icon": "thumb-tack"
            }
        ]
    },
    {
        "title": "prompt",
        "children": [
            {
                "path": "/toast",
                "name": "Toast",
                "icon": "commenting-o"
            },
            {
                "path": "/alert",
                "name": "Alert",
                "icon": "bullhorn"
            },
            {
                "path": "/loading",
                "name": "loading",
                "icon": "spinner"
            },
            {
                "path": "/progressbar",
                "name": "Progressbar",
                "icon": "battery-three-quarters"
            },
            {
                "path": "/back-to-top",
                "name": "BackToTop",
                "icon": "arrow-circle-up"
            }
        ]
    },
    {
        "title": "form",
        "children": [
            {
                "path": "/checkbox",
                "name": "Checkbox",
                "icon": "check-square"
            },
            {
                "path": "/radio",
                "name": "Radio",
                "icon": "dot-circle-o"
            },
            {
                "path": "/toggle",
                "name": "Toggle(Switch)",
                "icon": "toggle-on"
            },
            {
                "path": "/input-text",
                "name": "InputText",
                "icon": "font"
            },
            {
                "path": "/input-number",
                "name": "InputNumber",
                "icon": "plus-circle"
            },
            {
                "path": "/input-textarea",
                "name": "InputTextarea",
                "icon": "file-text-o"
            },
            {
                "path": "/selector",
                "name": "Selector",
                "icon": "check"
            },
            {
                "path": "/inline-selecor",
                "name": "InlineSelector",
                "icon": "th-large"
            }
        ]
    }
]


================================================
FILE: src/scripts/utils/alert.js
================================================
import Vue from 'vue';
import OriginAlert from '../components/Alert';

const Alert = Vue.extend(OriginAlert);
let alertInstance;

// Alert框
export const alert = option => {
    option.title = option.title || '提示';

    alertInstance = new Alert({
        el: document.createElement('div'),
    });

    document.body.appendChild(alertInstance.$el);

    Object.assign(alertInstance, option);

    Vue.nextTick(() => {
        alertInstance.show();
    });

    return alertInstance;
};

alert.hide = () => {
    alertInstance.hide();
};


================================================
FILE: src/scripts/utils/cssPrefix.js
================================================
let engine;
let docStyle = document.documentElement.style;

if('MozAppearance' in docStyle) {
    engine = 'gecko';
} else if('WebkitAppearance' in docStyle) {
    engine = 'webkit';
} else if(typeof navigator.cpuClass === 'string') {
    engine = 'trident';
}

export const vendorPrefix = { trident: 'ms', gecko: 'Moz', webkit: 'Webkit' }[engine];
export const cssPrefix = { trident: '-ms-', gecko: '-moz-', webkit: '-webkit-', presto: '-o-' }[engine];

export const getPrefixStyle = (name, value) => {
    let namePrefix = `${cssPrefix}${name}`;

    return `${namePrefix} ${value}`;
};


================================================
FILE: src/scripts/utils/date.js
================================================
/*eslint-disable*/

import dateUtil from './dateUtil';

const weeks = ['sun', 'mon', 'tue', 'wed', 'thu', 'fri', 'sat'];
const months = ['jan', 'feb', 'mar', 'apr', 'may', 'jun', 'jul', 'aug', 'sep', 'oct', 'nov', 'dec'];

const newArray = function(start, end) {
    let result = [];

    for (let i = start; i <= end; i++) {
      result.push(i);
    }
    return result;
};

export const toDate = function(date) {
    return isDate(date) ? new Date(date) : null;
};

export const isDate = function(date) {
    if(date === null || date === undefined) return false;
    if(isNaN(new Date(date).getTime())) return false;
    if(Array.isArray(date)) return false; // deal with `new Date([ new Date() ]) -> new Date()`
  
    return true;
};

export const isDateObject = function(val) {

    return val instanceof Date;
};

export const formatDate = function(date, format) {
    date = toDate(date);
    if(!date) return '';
    
    return dateUtil.format(date, format || 'yyyy-MM-dd');
};

export const parseDate = function(string, format) {
  
    return dateUtil.parse(string, format || 'yyyy-MM-dd');
};

export const getDayCountOfMonth = function(year, month) {
    if(month === 3 || month === 5 || month === 8 || month === 10) {
        return 30;
    }

    if(month === 1) {
        if(year % 4 === 0 && year % 100 !== 0 || year % 400 === 0) {
            
            return 29;
        } else {
            
            return 28;
        }
    }

    return 31;
};

export const getDayCountOfYear = function(year) {
    const isLeapYear = year % 400 === 0 || (year % 100 !== 0 && year % 4 === 0);
    return isLeapYear ? 366 : 365;
};

export const getFirstDayOfMonth = function(date) {
    const temp = new Date(date.getTime());
    temp.setDate(1);
    
    return temp.getDay();
};

// see: https://stackoverflow.com/questions/3674539/incrementing-a-date-in-javascript
// {prev, next} Date should work for Daylight Saving Time
// Adding 24 * 60 * 60 * 1000 does not work in the above scenario
export const prevDate = function(date, amount = 1) {
    
    return new Date(date.getFullYear(), date.getMonth(), date.getDate() - amount);
};

export const nextDate = function(date, amount = 1) {
    
    return new Date(date.getFullYear(), date.getMonth(), date.getDate() + amount);
};

export const getStartDateOfMonth = function(year, month) {
    const result = new Date(year, month, 1);
    const day = result.getDay();

    if(day === 0) {
        return prevDate(result, 7);
    } else {
        return prevDate(result, day);
    }
};

export const getWeekNumber = function(src) {
    if(!isDate(src)) return null;
    const date = new Date(src.getTime());
    date.setHours(0, 0, 0, 0);
    // Thursday in current week decides the year.
    date.setDate(date.getDate() + 3 - (date.getDay() + 6) % 7);
    // January 4 is always in week 1.
    const week1 = new Date(date.getFullYear(), 0, 4);
    // Adjust to Thursday in week 1 and count number of weeks from date to week 1.
    // Rounding should be fine for Daylight Saving Time. Its shift should never be more than 12 hours.
    return 1 + Math.round(((date.getTime() - week1.getTime()) / 86400000 - 3 + (week1.getDay() + 6) % 7) / 7);
};

export const getRangeHours = function(ranges) {
    const hours = [];
    let disabledHours = [];

    (ranges || []).forEach(range => {
        const value = range.map(date => date.getHours());

        disabledHours = disabledHours.concat(newArray(value[0], value[1]));
    });

    if (disabledHours.length) {
        for (let i = 0; i < 24; i++) {
            hours[i] = disabledHours.indexOf(i) === -1;
        }
    } else {
        for (let i = 0; i < 24; i++) {
            hours[i] = false;
        }
    }

    return hours;
};

export const range = function(n) {
    // see https://stackoverflow.com/questions/3746725/create-a-javascript-array-containing-1-n

    return Array.apply(null, {length: n}).map((_, n) => n);
};

export const modifyDate = function(date, y, m, d) {

    return new Date(y, m, d, date.getHours(), date.getMinutes(), date.getSeconds(), date.getMilliseconds());
};

export const modifyTime = function(date, h, m, s) {

    return new Date(date.getFullYear(), date.getMonth(), date.getDate(), h, m, s, date.getMilliseconds());
};

export const modifyWithTimeString = (date, time) => {
    if(date == null || !time) {
        return date;
    }
    time = parseDate(time, 'HH:mm:ss');

    return modifyTime(date, time.getHours(), time.getMinutes(), time.getSeconds());
};

export const clearTime = function(date) {
    return new Date(date.getFullYear(), date.getMonth(), date.getDate());
};

export const clearMilliseconds = function(date) {
    return new Date(date.getFullYear(), date.getMonth(), date.getDate(), date.getHours(), date.getMinutes(), date.getSeconds(), 0);
};

export const limitTimeRange = function(date, ranges, format = 'HH:mm:ss') {
    // TODO: refactory a more elegant solution
    if(ranges.length === 0) return date;
    const normalizeDate = date => dateUtil.parse(dateUtil.format(date, format), format);
    const ndate = normalizeDate(date);
    const nranges = ranges.map(range => range.map(normalizeDate));
    if(nranges.some(nrange => ndate >= nrange[0] && ndate <= nrange[1])) return date;

    let minDate = nranges[0][0];
    let maxDate = nranges[0][0];

    nranges.forEach(nrange => {
        minDate = new Date(Math.min(nrange[0], minDate));
        maxDate = new Date(Math.max(nrange[1], minDate));
    });

    const ret = ndate < minDate ? minDate : maxDate;
    // preserve Year/Month/Date
    return modifyDate(
        ret,
        date.getFullYear(),
        date.getMonth(),
        date.getDate()
    );
};

export const timeWithinRange = function(date, selectableRange, format) {
    const limitedDate = limitTimeRange(date, selectableRange, format);
    return limitedDate.getTime() === date.getTime();
};

export const changeYearMonthAndClampDate = function(date, year, month) {
    // clamp date to the number of days in `year`, `month`
    // eg: (2010-1-31, 2010, 2) => 2010-2-28
    const monthDate = Math.min(date.getDate(), getDayCountOfMonth(year, month));
    
    return modifyDate(date, year, month, monthDate);
};

export const prevMonth = function(date) {
    const year = date.getFullYear();
    const month = date.getMonth();

    return month === 0
        ? changeYearMonthAndClampDate(date, year - 1, 11)
        : changeYearMonthAndClampDate(date, year, month - 1);
};

export const nextMonth = function(date) {
    const year = date.getFullYear();
    const month = date.getMonth();

    return month === 11
        ? changeYearMonthAndClampDate(date, year + 1, 0)
        : changeYearMonthAndClampDate(date, year, month + 1);
};

export const prevYear = function(date, amount = 1) {
    const year = date.getFullYear();
    const month = date.getMonth();

    return changeYearMonthAndClampDate(date, year - amount, month);
};

export const nextYear = function(date, amount = 1) {
    const year = date.getFullYear();
    const month = date.getMonth();

    return changeYearMonthAndClampDate(date, year + amount, month);
};

export const extractDateFormat = function(format) {
    return format
        .replace(/\W?m{1,2}|\W?ZZ/g, '')
        .replace(/\W?h{1,2}|\W?s{1,3}|\W?a/gi, '')
        .trim();
};

export const extractTimeFormat = function(format) {
    return format
        .replace(/\W?D{1,2}|\W?Do|\W?d{1,4}|\W?M{1,4}|\W?y{2,4}/g, '')
        .trim();
};


================================================
FILE: src/scripts/utils/dateUtil.js
================================================
/* Modified from https://github.com/taylorhakes/fecha
 *
 * The MIT License (MIT)
 *
 * Copyright (c) 2015 Taylor Hakes
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 *     The above copyright notice and this permission notice shall be included in all
 * copies or substantial portions of the Software.
 *
 *     THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 *     FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 *     OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 * SOFTWARE.
 */

/*eslint-disable*/
// 把 YYYY-MM-DD 改成了 yyyy-MM-dd
(function (main) {
  'use strict';

  /**
   * Parse or format dates
   * @class fecha
   */
  var fecha = {};
  var token = /d{1,4}|M{1,4}|yy(?:yy)?|S{1,3}|Do|ZZ|([HhMsDm])\1?|[aA]|"[^"]*"|'[^']*'/g;
  var twoDigits = /\d\d?/;
  var threeDigits = /\d{3}/;
  var fourDigits = /\d{4}/;
  var word = /[0-9]*['a-z\u00A0-\u05FF\u0700-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF]+|[\u0600-\u06FF\/]+(\s*?[\u0600-\u06FF]+){1,2}/i;
  var noop = function () {
  };

  function shorten(arr, sLen) {
    var newArr = [];
    for (var i = 0, len = arr.length; i < len; i++) {
      newArr.push(arr[i].substr(0, sLen));
    }
    return newArr;
  }

  function monthUpdate(arrName) {
    return function (d, v, i18n) {
      var index = i18n[arrName].indexOf(v.charAt(0).toUpperCase() + v.substr(1).toLowerCase());
      if (~index) {
        d.month = index;
      }
    };
  }

  function pad(val, len) {
    val = String(val);
    len = len || 2;
    while (val.length < len) {
      val = '0' + val;
    }
    return val;
  }

  var dayNames = ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday'];
  var monthNames = ['January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December'];
  var monthNamesShort = shorten(monthNames, 3);
  var dayNamesShort = shorten(dayNames, 3);
  fecha.i18n = {
    dayNamesShort: dayNamesShort,
    dayNames: dayNames,
    monthNamesShort: monthNamesShort,
    monthNames: monthNames,
    amPm: ['am', 'pm'],
    DoFn: function DoFn(D) {
      return D + ['th', 'st', 'nd', 'rd'][D % 10 > 3 ? 0 : (D - D % 10 !== 10) * D % 10];
    }
  };

  var formatFlags = {
    D: function(dateObj) {
      return dateObj.getDay();
    },
    DD: function(dateObj) {
      return pad(dateObj.getDay());
    },
    Do: function(dateObj, i18n) {
      return i18n.DoFn(dateObj.getDate());
    },
    d: function(dateObj) {
      return dateObj.getDate();
    },
    dd: function(dateObj) {
      return pad(dateObj.getDate());
    },
    ddd: function(dateObj, i18n) {
      return i18n.dayNamesShort[dateObj.getDay()];
    },
    dddd: function(dateObj, i18n) {
      return i18n.dayNames[dateObj.getDay()];
    },
    M: function(dateObj) {
      return dateObj.getMonth() + 1;
    },
    MM: function(dateObj) {
      return pad(dateObj.getMonth() + 1);
    },
    MMM: function(dateObj, i18n) {
      return i18n.monthNamesShort[dateObj.getMonth()];
    },
    MMMM: function(dateObj, i18n) {
      return i18n.monthNames[dateObj.getMonth()];
    },
    yy: function(dateObj) {
      return String(dateObj.getFullYear()).substr(2);
    },
    yyyy: function(dateObj) {
      return dateObj.getFullYear();
    },
    h: function(dateObj) {
      return dateObj.getHours() % 12 || 12;
    },
    hh: function(dateObj) {
      return pad(dateObj.getHours() % 12 || 12);
    },
    H: function(dateObj) {
      return dateObj.getHours();
    },
    HH: function(dateObj) {
      return pad(dateObj.getHours());
    },
    m: function(dateObj) {
      return dateObj.getMinutes();
    },
    mm: function(dateObj) {
      return pad(dateObj.getMinutes());
    },
    s: function(dateObj) {
      return dateObj.getSeconds();
    },
    ss: function(dateObj) {
      return pad(dateObj.getSeconds());
    },
    S: function(dateObj) {
      return Math.round(dateObj.getMilliseconds() / 100);
    },
    SS: function(dateObj) {
      return pad(Math.round(dateObj.getMilliseconds() / 10), 2);
    },
    SSS: function(dateObj) {
      return pad(dateObj.getMilliseconds(), 3);
    },
    a: function(dateObj, i18n) {
      return dateObj.getHours() < 12 ? i18n.amPm[0] : i18n.amPm[1];
    },
    A: function(dateObj, i18n) {
      return dateObj.getHours() < 12 ? i18n.amPm[0].toUpperCase() : i18n.amPm[1].toUpperCase();
    },
    ZZ: function(dateObj) {
      var o = dateObj.getTimezoneOffset();
      return (o > 0 ? '-' : '+') + pad(Math.floor(Math.abs(o) / 60) * 100 + Math.abs(o) % 60, 4);
    }
  };

  var parseFlags = {
    d: [twoDigits, function (d, v) {
      d.day = v;
    }],
    M: [twoDigits, function (d, v) {
      d.month = v - 1;
    }],
    yy: [twoDigits, function (d, v) {
      var da = new Date(), cent = +('' + da.getFullYear()).substr(0, 2);
      d.year = '' + (v > 68 ? cent - 1 : cent) + v;
    }],
    h: [twoDigits, function (d, v) {
      d.hour = v;
    }],
    m: [twoDigits, function (d, v) {
      d.minute = v;
    }],
    s: [twoDigits, function (d, v) {
      d.second = v;
    }],
    yyyy: [fourDigits, function (d, v) {
      d.year = v;
    }],
    S: [/\d/, function (d, v) {
      d.millisecond = v * 100;
    }],
    SS: [/\d{2}/, function (d, v) {
      d.millisecond = v * 10;
    }],
    SSS: [threeDigits, function (d, v) {
      d.millisecond = v;
    }],
    D: [twoDigits, noop],
    ddd: [word, noop],
    MMM: [word, monthUpdate('monthNamesShort')],
    MMMM: [word, monthUpdate('monthNames')],
    a: [word, function (d, v, i18n) {
      var val = v.toLowerCase();
      if (val === i18n.amPm[0]) {
        d.isPm = false;
      } else if (val === i18n.amPm[1]) {
        d.isPm = true;
      }
    }],
    ZZ: [/[\+\-]\d\d:?\d\d/, function (d, v) {
      var parts = (v + '').match(/([\+\-]|\d\d)/gi), minutes;

      if (parts) {
        minutes = +(parts[1] * 60) + parseInt(parts[2], 10);
        d.timezoneOffset = parts[0] === '+' ? minutes : -minutes;
      }
    }]
  };
  parseFlags.DD = parseFlags.D;
  parseFlags.dddd = parseFlags.ddd;
  parseFlags.Do = parseFlags.dd = parseFlags.d;
  parseFlags.mm = parseFlags.m;
  parseFlags.hh = parseFlags.H = parseFlags.HH = parseFlags.h;
  parseFlags.MM = parseFlags.M;
  parseFlags.ss = parseFlags.s;
  parseFlags.A = parseFlags.a;


  // Some common format strings
  fecha.masks = {
    'default': 'ddd MMM dd yyyy HH:mm:ss',
    shortDate: 'M/D/yy',
    mediumDate: 'MMM d, yyyy',
    longDate: 'MMMM d, yyyy',
    fullDate: 'dddd, MMMM d, yyyy',
    shortTime: 'HH:mm',
    mediumTime: 'HH:mm:ss',
    longTime: 'HH:mm:ss.SSS'
  };

  /***
   * Format a date
   * @method format
   * @param {Date|number} dateObj
   * @param {string} mask Format of the date, i.e. 'mm-dd-yy' or 'shortDate'
   */
  fecha.format = function (dateObj, mask, i18nSettings) {
    var i18n = i18nSettings || fecha.i18n;

    if (typeof dateObj === 'number') {
      dateObj = new Date(dateObj);
    }

    if (Object.prototype.toString.call(dateObj) !== '[object Date]' || isNaN(dateObj.getTime())) {
      throw new Error('Invalid Date in fecha.format');
    }

    mask = fecha.masks[mask] || mask || fecha.masks['default'];

    return mask.replace(token, function ($0) {
      return $0 in formatFlags ? formatFlags[$0](dateObj, i18n) : $0.slice(1, $0.length - 1);
    });
  };

  /**
   * Parse a date string into an object, changes - into /
   * @method parse
   * @param {string} dateStr Date string
   * @param {string} format Date parse format
   * @returns {Date|boolean}
   */
  fecha.parse = function (dateStr, format, i18nSettings) {
    var i18n = i18nSettings || fecha.i18n;

    if (typeof format !== 'string') {
      throw new Error('Invalid format in fecha.parse');
    }

    format = fecha.masks[format] || format;

    // Avoid regular expression denial of service, fail early for really long strings
    // https://www.owasp.org/index.php/Regular_expression_Denial_of_Service_-_ReDoS
    if (dateStr.length > 1000) {
      return false;
    }

    var isValid = true;
    var dateInfo = {};
    format.replace(token, function ($0) {
      if (parseFlags[$0]) {
        var info = parseFlags[$0];
        var index = dateStr.search(info[0]);
        if (!~index) {
          isValid = false;
        } else {
          dateStr.replace(info[0], function (result) {
            info[1](dateInfo, result, i18n);
            dateStr = dateStr.substr(index + result.length);
            return result;
          });
        }
      }

      return parseFlags[$0] ? '' : $0.slice(1, $0.length - 1);
    });

    if (!isValid) {
      return false;
    }

    var today = new Date();
    if (dateInfo.isPm === true && dateInfo.hour != null && +dateInfo.hour !== 12) {
      dateInfo.hour = +dateInfo.hour + 12;
    } else if (dateInfo.isPm === false && +dateInfo.hour === 12) {
      dateInfo.hour = 0;
    }

    var date;
    if (dateInfo.timezoneOffset != null) {
      dateInfo.minute = +(dateInfo.minute || 0) - +dateInfo.timezoneOffset;
      date = new Date(Date.UTC(dateInfo.year || today.getFullYear(), dateInfo.month || 0, dateInfo.day || 1,
        dateInfo.hour || 0, dateInfo.minute || 0, dateInfo.second || 0, dateInfo.millisecond || 0));
    } else {
      date = new Date(dateInfo.year || today.getFullYear(), dateInfo.month || 0, dateInfo.day || 1,
        dateInfo.hour || 0, dateInfo.minute || 0, dateInfo.second || 0, dateInfo.millisecond || 0);
    }
    return date;
  };

  /* istanbul ignore next */
  if (typeof module !== 'undefined' && module.exports) {
    module.exports = fecha;
  } else if (typeof define === 'function' && define.amd) {
    define(function () {
      return fecha;
    });
  } else {
    main.fecha = fecha;
  }
})(this);


================================================
FILE: src/scripts/utils/dom.js
================================================
/**
 * 判断一个元素是否为另一个的后代元素.
 * @param  {[Element]} ancestor     [祖先元素]
 * @param  {[Element]} descendent   [后代元素]
 * @return {[Boolean]}              [是否]
 */
export const contains = (ancestor, descendent) => {
    if(ancestor.compareDocumentPosition) {
        return ancestor === descendent || !!(ancestor.compareDocumentPosition(descendent) & 16);
    }

    if(ancestor.contains && descendent.nodeType === 1) {
        return ancestor.contains(descendent) && ancestor !== descendent;
    }

    let tmpDescendent = descendent;

    // 递归
    while(tmpDescendent !== document) {
        tmpDescendent = tmpDescendent.parentNode;

        if(tmpDescendent === ancestor) return true;
    }

    return false;
};

/**
 * 判断元素是否有滚动条.
 * @param  {[Element]} el [元素]
 * @return {[Boolean]}    [是否有滚动条]
 */
export const hasScrollbar = el => {
    if(!el) return false;

    return el.scrollHeight > el.offsetHeight;
};

/**
 * 返回具有滚动条的祖先元素.
 * @param  {[Element]} el [Dom元素]
 * @return {[Element]}    [祖先元素]
 */
export const getScrollContainer = el => {
    let tmpEl = el;

    while(tmpEl !== document) {
        tmpEl = tmpEl.parentNode;

        if(hasScrollbar(tmpEl)) return tmpEl;
    }

    return document;
};

/**
 * 返回布尔值.
 * @param  {[Element]} el [Dom元素]
 * @param  {[class]} cls [样式]
 * @return {[Boolean]}    [是否有class]
 */
export const hasClass = (el, cls) => {
    if(!el || !cls) return false;
    if(cls.indexOf(' ') !== -1) throw new Error('className should not contain space.');
    if(el.classList) {
        return el.classList.contains(cls);
    }
    /*eslint-disable*/
    return (' ' + el.className + ' ').indexOf(' ' + cls + ' ') > -1;
    /*eslint-disable*/
};


================================================
FILE: src/scripts/utils/draggable.js
================================================
import { getTranslate } from './translate';

/* global document:true */
const draggable = (el, options) => {
    let prevTranslateX,
        prevTranslateY,
        dragState = {
            dragging: false,
            effectEl: options.effectEl || el,
        };

    // start
    const onTouchStartHandle = (event, sourceEvent) => {
        if(dragState.dragging) return;

        let translate = getTranslate(dragState.effectEl);

        Object.assign(dragState, {
            startTimestamp: new Date(),
            pageX: event.pageX,
            pageY: event.pageY,
            translateX: translate.x,
            translateY: translate.y,
        });

        document.onselectstart = () => false;
        document.ondragstart = () => false;

        if(options.onDragStart) options.onDragStart(dragState, sourceEvent);
    };

    // move
    const onTouchMoveHandle = (event, sourceEvent) => {
        let deltaX = event.pageX - dragState.pageX,
            deltaY = event.pageY - dragState.pageY,
            translateX = dragState.translateX + deltaX,
            translateY = dragState.translateY + deltaY,
            velocityTranslateX = translateX - prevTranslateX || translateX,
            velocityTranslateY = translateY - prevTranslateY || translateY;

        prevTranslateX = translateX;
        prevTranslateY = translateY;
        Object.assign(dragState, {
            dragging: true,
            velocityTranslateX,
            velocityTranslateY,
        });

        if(options.onDrag) {
            options.onDrag({
                ...dragState,
                deltaX,
                deltaY,
                translateX,
                translateY,
            }, sourceEvent);
        }
    };

    // end
    const onTouchEndHandle = (event, sourceEvent) => {
        Object.assign(dragState, {
            dragging: false,
        });

        document.onselectstart = null;
        document.ondragstart = null;

        if(options.onDragEnd) options.onDragEnd(dragState, sourceEvent);

        // reset
        dragState = {
            dragging: false,
            effectEl: options.effectEl || el,
        };
    };

    el.addEventListener('touchstart', event => onTouchStartHandle(event.changedTouches[0] || event.touches[0], event));
    el.addEventListener('touchmove', event => onTouchMoveHandle(event.changedTouches[0] || event.touches[0], event));
    el.addEventListener('touchend', event => onTouchEndHandle(event.changedTouches[0] || event.touches[0], event));
    el.addEventListener('touchcancel', event => onTouchEndHandle(event.changedTouches[0] || event.touches[0], event));
};

export default draggable;


================================================
FILE: src/scripts/utils/easing.js
================================================
// easeInOut
export const easeInOutCubic = (time, offset, end, duration) => {
    let cc = end - offset,
        tempTime = time / (duration / 2);

    if(tempTime < 1) {
        return (cc / 2 * tempTime * tempTime * tempTime) + offset;
    }

    return (cc / 2 * (((tempTime -= 2) * tempTime * tempTime) + 2)) + offset;
};


================================================
FILE: src/scripts/utils/loading.js
================================================
import Vue from 'vue';
import OriginToast from '../components/Toast';

const Toast = Vue.extend(OriginToast);

let instance,
    active = false;

export default {
    show(message = '加载中') {
        if(active) return;

        /* global document:true */
        if(!instance) {
            instance = new Toast({
                el: document.createElement('div'),
            });

            document.body.appendChild(instance.$el);
        }

        active = true;
        instance.message = message;
        instance.type = 'loading';
        instance.position = 'center';

        Vue.nextTick(() => {
            instance.show();
        });
    },
    hide() {
        instance.hide();
        active = false;
    },
    toggle(message) {
        return active ? this.hide() : this.show(message);
    },
};


================================================
FILE: src/scripts/utils/toast.js
================================================
import Vue from 'vue';
import OriginToast from '../components/Toast';

const Toast = Vue.extend(OriginToast);

// toast缓存池
const toastCache = {
    cache: [],
    active: false,
    pop() {
        if(this.cache.length) return this.cache.splice(0, 1)[0];

        return new Toast({
            el: document.createElement('div'),
        });
    },
    push(instance) {
        this.cache.push(instance);
    },
    toggle() {
        this.active = !this.active;
    },
};

Toast.prototype.show = function() {
    this.currentValue = true;
    toastCache.active = true;
};

Toast.prototype.hide = function() {
    this.currentValue = false;
    toastCache.active = false;
};

/* global document:true */
const toastUtil = (options = {}) => {
    if(toastCache.active) return;

    let duration = options.duration || 2000,
        instance = toastCache.pop();

    instance.message = typeof options === 'string' ? options : options.message;
    instance.type = options.type || '';
    instance.position = options.position || 'bottom';

    document.body.appendChild(instance.$el);

    instance.show();
    instance.timer = setTimeout(() => {
        instance.hide();

        toastCache.push(instance);
    }, duration);
};

export default toastUtil;


================================================
FILE: src/scripts/utils/translate.js
================================================
import { vendorPrefix } from './cssPrefix';

let transformProperty = `${vendorPrefix}Transform`;

// 获取位移
export const getTranslate = el => {
    let result = {
        x: 0,
        y: 0,
    };

    if(el === null || el.style === null) return result;

    let transform = el.style[transformProperty];
    let matches = /translate\(\s*(-?\d+(\.?\d+?)?)px,\s*(-?\d+(\.\d+)?)px\)\s*(translateZ\(0px\))?/g.exec(transform);

    if(matches) {
        result.x = +matches[1];
        result.y = +matches[3];
    }

    return result;
};

// 取消位移
export const cancelTranslate = el => {
    if(el === null || el.style === null) return;

    let transform = el.style[transformProperty];

    if(transform) {
        transform = transform.replace(/translate\(\s*(-?\d+(\.?\d+?)?)px,\s*(-?\d+(\.\d+)?)px\)\s*(translateZ\(0px\))?/g, '');
        el.style[transformProperty] = transform;
    }
};

// 设置位移
export const setTranslate = (el, x, y) => {
    if(!el) return;
    if(x === null && y === null) return;
    // if(!el.style.transform && !x && !y) return;

    let translate = getTranslate(el),
        currentX = x,
        currentY = y;

    if(x === null) currentX = translate.x;
    if(y === null) currentY = translate.y;

    cancelTranslate(el);

    el.style[transformProperty] += ` translate(${currentX ? `${currentX}px` : '0px'}, ${currentY ? `${currentY}px` : '0px'})`;
};



================================================
FILE: src/scripts/utils/type.js
================================================
// 是否数组
export const isArray = array => Object.prototype.toString.call(array) === '[object Array]';


================================================
FILE: src/scripts/views/alert.vue
================================================
<template>
    <div>
        <group-title>base</group-title>
        <group>
            <cell @click="alertClickHandle">
                <icon class="text-muted" name="exclamation-triangle" left size="lg" />
                alert
            </cell>
            <cell @click="confirmClickHandle">
                <icon class="text-muted" name="question-circle-o" left size="lg" />
                confirm
            </cell>
        </group>
        <group-title>vertical buttons</group-title>
        <group>
            <cell @click="confirmVerticalClickHandle">
                <icon class="text-muted" name="question-circle-o" left size="lg" />
                confirm
            </cell>
        </group>
    </div>
</template>

<script>
    export default {
        methods: {
            // alert
            alertClickHandle() {
                this.$alert({
                    maskClosable: true,
                    btns: [{
                        text: '关闭',
                        click() {
                            console.log('close!!!');
                        },
                    }],
                    title: '<span class="text-default">提示</span>',
                    message: '这是Vue 2.0组件库!',
                });
            },
            // confirm
            confirmClickHandle() {
                this.$alert({
                    btns: [{
                        text: '确定',
                        click() {
                            console.log('sure!!!');
                        },
                    }, {
                        text: '取消',
                        click() {
                            console.log('cancel!!!');
                        },
                    }],
                    title: '<span class="text-success">提示</span>',
                    message: '这是Vue 2.0组件库!',
                });
            },
            // confirm
            confirmVerticalClickHandle() {
                this.$alert({
                    vertical: true,
                    btns: [{
                        text: '验证身份',
                        click() {
                            console.log('check!!!');
                        },
                    }, {
                        text: '开始使用',
                        click() {
                            console.log('start!!!');
                        },
                    }],
                    title: '<span class="text-success">恭喜你</span>',
                    message: '通过考试,你已完成初级培训!',
                });
            },
        },
    };

</script>


================================================
FILE: src/scripts/views/back-to-top.vue
================================================
<template>
    <div>
        <div v-for="n in 10">
            <group-title><strong>group {{n}}</strong></group-title>
            <group>
                <cell v-for="m in 5">
                    {{n}}-{{m}}
                </cell>
            </group>
        </div>
        <back-to-top />
    </div>
</template>


================================================
FILE: src/scripts/views/badge.vue
================================================
<template>
    <div>
        <group-title>base</group-title>
        <group>
            <cell>
                <badge content="3" theme="primary" />
                <badge content="3" theme="secondary" />
                <badge content="3" theme="success" />
                <badge content="3" theme="warning" />
            </cell>
            <cell>
                <badge content="3" theme="primary">
                    <div class="bg-default" :style="{width: '50px', height: '50px'}"></div>
                </badge>
                <badge content="99+" theme="secondary">
                    <div class="bg-default" :style="{width: '50px', height: '50px'}"></div>
                </badge>
                <badge content="99+" theme="success">
                    <div class="bg-default" :style="{width: '50px', height: '50px'}"></div>
                </badge>
                <badge content="3" theme="warning">
                    <div class="bg-default" :style="{width: '50px', height: '50px'}"></div>
                </badge>
            </cell>
            <cell>
                <badge content="3" theme="primary">
                    <icon class="text-muted" size="3x" name="bell-o" />
                </badge>
                <badge content="3" theme="secondary">
                    <icon class="text-muted" size="3x" name="shopping-cart" />
                </badge>
                <badge content="3" theme="success">
                    <icon class="text-muted" size="3x" name="bell-o" />
                </badge>
                <badge content="3" theme="warning">
                    <icon class="text-muted" size="3x" name="shopping-cart" />
                </badge>
            </cell>
        </group>
        <group-title>dot</group-title>
        <group>
            <cell>
                <badge theme="primary">
                    <icon class="text-muted" size="2x" name="bell-o" />
                </badge>
                <badge theme="secondary">
                    <icon class="text-muted" size="2x" name="bell-o" />
                </badge>
                <badge theme="success">
                    <icon class="text-muted" size="2x" name="shopping-cart" />
                </badge>
                <badge theme="warning">
                    <icon class="text-muted" size="2x" name="shopping-cart" />
                </badge>
            </cell>
        </group>
    </div>
</template>


================================================
FILE: src/scripts/views/button.vue
================================================
<template>
    <div>
        <group-title>theme</group-title>
        <group>
            <cell>
                <btn theme="primary" @click="clickHandle('Vue 2.0')">primary</btn>
                <btn theme="secondary">secondary</btn>
                <btn theme="default">default</btn>
            </cell>
        </group>
        <group-title>hollow</group-title>
        <group>
            <cell class="bg-inverse">
                <btn hollow theme="primary">primary</btn>
                <btn hollow theme="secondary">secondary</btn>
                <btn hollow theme="default">default</btn>
            </cell>
        </group>
        <group-title>shape="pill"</group-title>
        <group>
            <cell>
                <btn shape="pill" theme="primary">primary</btn>
                <btn shape="pill" theme="secondary">secondary</btn>
                <btn shape="pill" theme="default">default</btn>
            </cell>
        </group>
        <group-title>loading</group-title>
        <group>
            <cell>
                <btn theme="primary" loading>loading</btn>
                <btn theme="default" loading>保存中</btn>
            </cell>
        </group>
        <group-title>disabled</group-title>
        <group>
            <cell>
                <btn disabled theme="primary">primary</btn>
                <btn disabled theme="secondary">secondary</btn>
                <btn disabled theme="default">default</btn>
            </cell>
        </group>
        <group-title>size</group-title>
        <group>
            <cell>
                <btn size="sm" hollow theme="primary">small</btn>
                <btn theme="primary">normal</btn>
                <btn size="lg" theme="primary">large</btn>
            </cell>
        </group>
        <group-title>block</group-title>
        <group>
            <cell>
                <btn block size="lg" theme="primary">primary</btn>
            </cell>
            <cell>
                <btn block size="lg" theme="secondary">secondary</btn>
            </cell>
            <cell>
                <btn block size="lg" theme="default">default</btn>
            </cell>
        </group>
    </div>
</template>

<script>
    export default {
        methods: {
            clickHandle(message) {
                this.$toast(`hello ${message}`);
            },
        },
    };
</script>


================================================
FILE: src/scripts/views/card.vue
================================================
<template>
    <div class="container">
        <tip><span class="text-pure">科比系列</span></tip>
        <div class="prod-container clearfix">
            <div class="prod-item">
                <card>
                    <img class="img-fluid" src="../../images/kobe8.jpg" alt="kobe 8">
                    <card-body>
                        <p class="prod-info">
                            Nike耐克 Kobe8 ZK8 科比八代低帮篮球鞋  全配色合集 745334-005科八蓝色 42
                        </p>
                        <div class="text-danger">¥ 998</div>
                    </card-body>
                </card>
            </div>
            <div class="prod-item">
                <card>
                    <img class="img-fluid" src="../../images/kobe8.jpg" alt="kobe 8">
                    <card-body>
                        <p class="prod-info">
                            Nike耐克 Kobe8 ZK8 科比八代低帮篮球鞋  全配色合集 745334-005科八蓝色 42
                        </p>
                        <div class="text-danger">¥ 998</div>
                    </card-body>
                </card>
            </div>
            <div class="prod-item">
                <card>
                    <img class="img-fluid" src="../../images/kobe9.jpg" alt="kobe 10">
                    <card-body>
                        <p class="prod-info">
                            Nike耐克 Kobe9 ZK9 科比九代低帮篮球鞋  全配色合集 745334-005科九蓝色 42
                        </p>
                        <div class="text-danger">¥ 1080</div>
                    </card-body>
                </card>
            </div>
            <div class="prod-item">
                <card>
                    <img class="img-fluid" src="../../images/kobe9.jpg" alt="kobe 10">
                    <card-body>
                        <p class="prod-info">
                            Nike耐克 Kobe9 ZK9 科比九代低帮篮球鞋  全配色合集 745334-005科九蓝色 42
                        </p>
                        <div class="text-danger">¥ 1080</div>
                    </card-body>
                </card>
            </div>
            <div class="prod-item">
                <card>
                    <img class="img-fluid" src="../../images/kobe10.jpg" alt="kobe 10">
                    <card-body>
                        <p class="prod-info">
                            Nike耐克 Kobe10 ZK10 科比十代低帮篮球鞋  全配色合集 745334-005科十紫色 42
                        </p>
                        <div class="text-danger">¥ 1380</div>
                    </card-body>
                </card>
            </div>
            <div class="prod-item">
                <card>
                    <img class="img-fluid" src="../../images/kobe10.jpg" alt="kobe 10">
                    <card-body>
                        <p class="prod-info">
                            Nike耐克 Kobe10 ZK10 科比十代低帮篮球鞋  全配色合集 745334-005科十紫色 42
                        </p>
                        <div class="text-danger">¥ 1380</div>
                    </card-body>
              
Download .txt
gitextract_vku1hzea/

├── .babelrc
├── .editorconfig
├── .eslintignore
├── .eslintrc.js
├── .gitignore
├── .npmignore
├── .scss-lint.yml
├── README.md
├── _config.yml
├── gulpfile.js
├── index.html
├── package.json
├── src/
│   ├── scripts/
│   │   ├── components/
│   │   │   ├── Alert.vue
│   │   │   ├── BackToTop.vue
│   │   │   ├── Badge.vue
│   │   │   ├── Button.vue
│   │   │   ├── Card.vue
│   │   │   ├── CardBody.vue
│   │   │   ├── Cell.vue
│   │   │   ├── Checkbox.vue
│   │   │   ├── CheckboxGroup.vue
│   │   │   ├── DatePicker/
│   │   │   │   ├── DateRange.vue
│   │   │   │   ├── DateTable.vue
│   │   │   │   ├── Picker.vue
│   │   │   │   └── index.js
│   │   │   ├── Drawer.vue
│   │   │   ├── DrawerItem.vue
│   │   │   ├── Flex.vue
│   │   │   ├── FlexItem.vue
│   │   │   ├── Group.vue
│   │   │   ├── GroupTitle.vue
│   │   │   ├── HRule.vue
│   │   │   ├── Icon.vue
│   │   │   ├── InlineSelector.vue
│   │   │   ├── InlineSelectorOption.vue
│   │   │   ├── InputNumber.vue
│   │   │   ├── InputText.vue
│   │   │   ├── InputTextarea.vue
│   │   │   ├── Loading.vue
│   │   │   ├── Loadmore.vue
│   │   │   ├── Mask.vue
│   │   │   ├── Media.vue
│   │   │   ├── MediaBody.vue
│   │   │   ├── MediaObject.vue
│   │   │   ├── Navbar.vue
│   │   │   ├── Navigation.vue
│   │   │   ├── NavigationItem.vue
│   │   │   ├── Picker.vue
│   │   │   ├── PickerOption.vue
│   │   │   ├── Progressbar.vue
│   │   │   ├── Progressbars.vue
│   │   │   ├── Radio.vue
│   │   │   ├── RadioGroup.vue
│   │   │   ├── Searchbar.vue
│   │   │   ├── SearchbarBtn.vue
│   │   │   ├── SearchbarPlaceholder.vue
│   │   │   ├── SegmentedControl.vue
│   │   │   ├── SegmentedControlItem.vue
│   │   │   ├── Selector.vue
│   │   │   ├── SelectorOption.vue
│   │   │   ├── Sidelip.vue
│   │   │   ├── SlideUp.vue
│   │   │   ├── SlideUpBody.vue
│   │   │   ├── SlideUpHeader.vue
│   │   │   ├── Stepbar.vue
│   │   │   ├── StepbarItem.vue
│   │   │   ├── Sticky.vue
│   │   │   ├── Swipe.vue
│   │   │   ├── SwipeItem.vue
│   │   │   ├── Tabbar.vue
│   │   │   ├── TabbarItem.vue
│   │   │   ├── Tag.vue
│   │   │   ├── Timeline.vue
│   │   │   ├── TimelineItem.vue
│   │   │   ├── Tip.vue
│   │   │   ├── Toast.vue
│   │   │   ├── Toggle.vue
│   │   │   └── index.js
│   │   ├── containers/
│   │   │   ├── layout.vue
│   │   │   └── menubar.vue
│   │   ├── directives/
│   │   │   └── disfavor.js
│   │   ├── index.js
│   │   ├── mixins/
│   │   │   ├── emitter.js
│   │   │   ├── select.js
│   │   │   ├── selectOption.js
│   │   │   ├── sync.js
│   │   │   ├── tab.js
│   │   │   └── tabItem.js
│   │   ├── router.js
│   │   ├── routes.json
│   │   ├── utils/
│   │   │   ├── alert.js
│   │   │   ├── cssPrefix.js
│   │   │   ├── date.js
│   │   │   ├── dateUtil.js
│   │   │   ├── dom.js
│   │   │   ├── draggable.js
│   │   │   ├── easing.js
│   │   │   ├── loading.js
│   │   │   ├── toast.js
│   │   │   ├── translate.js
│   │   │   └── type.js
│   │   └── views/
│   │       ├── alert.vue
│   │       ├── back-to-top.vue
│   │       ├── badge.vue
│   │       ├── button.vue
│   │       ├── card.vue
│   │       ├── cell.vue
│   │       ├── checkbox.vue
│   │       ├── confirm.vue
│   │       ├── datepicker.vue
│   │       ├── drawer.vue
│   │       ├── flex.vue
│   │       ├── hrule.vue
│   │       ├── icon.vue
│   │       ├── index.vue
│   │       ├── inline-selecor.vue
│   │       ├── input-number.vue
│   │       ├── input-text.vue
│   │       ├── input-textarea.vue
│   │       ├── loading.vue
│   │       ├── media.vue
│   │       ├── navbar.vue
│   │       ├── navigation.vue
│   │       ├── picker.vue
│   │       ├── progressbar.vue
│   │       ├── pull-down.vue
│   │       ├── pull-up.vue
│   │       ├── radio.vue
│   │       ├── searchbar.vue
│   │       ├── segmented-control.vue
│   │       ├── selector.vue
│   │       ├── sideslip.vue
│   │       ├── slideup.vue
│   │       ├── stepbar.vue
│   │       ├── sticky.vue
│   │       ├── swipe.vue
│   │       ├── tabbar.vue
│   │       ├── tag.vue
│   │       ├── timeline.vue
│   │       ├── tip.vue
│   │       ├── toast.vue
│   │       └── toggle.vue
│   └── styles/
│       ├── animate.scss
│       ├── index.scss
│       ├── mixins/
│       │   ├── border.scss
│       │   ├── button.scss
│       │   ├── media.scss
│       │   ├── navbar.scss
│       │   ├── progressbar.scss
│       │   ├── tag.scss
│       │   └── text.scss
│       ├── mixins.scss
│       ├── modules/
│       │   ├── alert.scss
│       │   ├── back-to-top.scss
│       │   ├── badge.scss
│       │   ├── button.scss
│       │   ├── card.scss
│       │   ├── cell.scss
│       │   ├── checkbox.scss
│       │   ├── date-picker.scss
│       │   ├── drawer.scss
│       │   ├── group.scss
│       │   ├── hrule.scss
│       │   ├── inline-selector.scss
│       │   ├── input-number.scss
│       │   ├── input-text.scss
│       │   ├── input-textarea.scss
│       │   ├── loading.scss
│       │   ├── loadmore.scss
│       │   ├── mask.scss
│       │   ├── media.scss
│       │   ├── navbar.scss
│       │   ├── navigation.scss
│       │   ├── picker.scss
│       │   ├── progressbar.scss
│       │   ├── searchbar.scss
│       │   ├── segmented-control.scss
│       │   ├── selector.scss
│       │   ├── sidelip.scss
│       │   ├── slideup.scss
│       │   ├── stepbar.scss
│       │   ├── sticky.scss
│       │   ├── swipe.scss
│       │   ├── tabbar.scss
│       │   ├── tag.scss
│       │   ├── timeline.scss
│       │   ├── tip.scss
│       │   ├── toast.scss
│       │   └── toggle.scss
│       ├── modules.scss
│       ├── utils/
│       │   ├── background.scss
│       │   ├── border.scss
│       │   ├── clearfix.scss
│       │   ├── display.scss
│       │   ├── flex.scss
│       │   ├── img.scss
│       │   ├── reboot.scss
│       │   ├── spacing.scss
│       │   └── text.scss
│       ├── utils.scss
│       └── variables.scss
├── webpack.dev.config.js
├── webpack.prebuilt.config.js
└── webpack.prod.config.js
Download .txt
SYMBOL INDEX (28 symbols across 11 files)

FILE: src/scripts/components/DatePicker/index.js
  method type (line 26) | type(type) {
  method created (line 37) | created() {

FILE: src/scripts/directives/disfavor.js
  method bind (line 7) | bind(el, binding, vnode) {
  method unbind (line 21) | unbind(el) {

FILE: src/scripts/mixins/emitter.js
  method dispatch (line 3) | dispatch(componentName, eventName, params) {

FILE: src/scripts/mixins/select.js
  method data (line 5) | data() {
  method optionClickHandle (line 11) | optionClickHandle(option) {
  method created (line 31) | created() {

FILE: src/scripts/mixins/selectOption.js
  method clickHandle (line 11) | clickHandle() {
  method isActive (line 16) | isActive() {

FILE: src/scripts/mixins/sync.js
  method data (line 8) | data() {
  method value (line 14) | value(val) {
  method currentValue (line 17) | currentValue(val) {

FILE: src/scripts/mixins/tab.js
  method data (line 6) | data() {
  method itemClickHandle (line 12) | itemClickHandle(val) {
  method created (line 19) | created() {

FILE: src/scripts/mixins/tabItem.js
  method data (line 6) | data() {
  method mounted (line 12) | mounted() {
  method clickHandle (line 17) | clickHandle() {

FILE: src/scripts/utils/dateUtil.js
  function shorten (line 44) | function shorten(arr, sLen) {
  function monthUpdate (line 52) | function monthUpdate(arrName) {
  function pad (line 61) | function pad(val, len) {

FILE: src/scripts/utils/loading.js
  method show (line 10) | show(message = '加载中') {
  method hide (line 31) | hide() {
  method toggle (line 35) | toggle(message) {

FILE: src/scripts/utils/toast.js
  method pop (line 10) | pop() {
  method push (line 17) | push(instance) {
  method toggle (line 20) | toggle() {
Condensed preview — 204 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (367K chars).
[
  {
    "path": ".babelrc",
    "chars": 182,
    "preview": "{\n    \"presets\": [\"vue\", [\"es2015\", { \"modules\": false }], \"stage-1\"],\n    \"comments\": false,\n    \"plugins\": [\n        \""
  },
  {
    "path": ".editorconfig",
    "chars": 197,
    "preview": "root = true\n\n[*]\ncharset = utf-8\nindent_style = space\nindent_size = 4\nend_of_line = lf\ninsert_final_newline = true\ntrim_"
  },
  {
    "path": ".eslintignore",
    "chars": 22,
    "preview": "build.js\nwebpack.*.js\n"
  },
  {
    "path": ".eslintrc.js",
    "chars": 120,
    "preview": "module.exports = {\n    extends: 'vue-impression',\n    rules: {\n        'import/no-extraneous-dependencies': 0,\n    }\n};\n"
  },
  {
    "path": ".gitignore",
    "chars": 213,
    "preview": "/node_modules\n/site/build\n/site/node_modules\n/site/.sass-cache\n/site/src/scripts/components/impression\n/site/src/styles/"
  },
  {
    "path": ".npmignore",
    "chars": 189,
    "preview": "/build\n/site\n/.sass-cache\n.DS_Store\nMakefile\n.babelrc\n.editorconfig\n.eslintignore\n.eslintrc.js\n.scss-lint.yml\nindex.todo"
  },
  {
    "path": ".scss-lint.yml",
    "chars": 4918,
    "preview": "scss_files: \"src/styles/**/*.scss\"\nexclude: ['src/styles/font-awesome/**', 'src/styles/modules/_normalize.scss', 'src/st"
  },
  {
    "path": "README.md",
    "chars": 1698,
    "preview": "# vue-impression\n\nA Vue.js 2.0 UI elements for mobile.\n\n## Demo\n\nhttps://newdadafe.github.io/impression_vue/#/button\n\n##"
  },
  {
    "path": "_config.yml",
    "chars": 26,
    "preview": "theme: jekyll-theme-cayman"
  },
  {
    "path": "gulpfile.js",
    "chars": 2102,
    "preview": "const fs = require('fs-extra');\nconst gulp = require('gulp');\nconst minimist = require('minimist');\nconst plugin = requi"
  },
  {
    "path": "index.html",
    "chars": 589,
    "preview": "<!DOCTYPE html>\n<html>\n<head>\n    <meta charset=\"UTF-8\">\n    <title>vue-impression</title>\n    <meta name=\"viewport\" con"
  },
  {
    "path": "package.json",
    "chars": 2958,
    "preview": "{\n  \"name\": \"vue-impression\",\n  \"version\": \"0.21.13\",\n  \"description\": \"A Vue.js 2.0 UI elements for mobile.\",\n  \"sass\":"
  },
  {
    "path": "src/scripts/components/Alert.vue",
    "chars": 2513,
    "preview": "<template>\n    <div>\n        <transition name=\"zoom\"\n            @after-leave=\"afterLeave\">\n            <div\n           "
  },
  {
    "path": "src/scripts/components/BackToTop.vue",
    "chars": 2660,
    "preview": "<template>\n    <div\n        class=\"back-to-top\"\n        :class=\"{'active': active}\"\n        @click.prevent.stop=\"scrollT"
  },
  {
    "path": "src/scripts/components/Badge.vue",
    "chars": 662,
    "preview": "<template>\n    <div\n        @click=\"$emit('click')\"\n        class=\"badge\"\n        :class=\"{'badge-gap': $slots.default}\""
  },
  {
    "path": "src/scripts/components/Button.vue",
    "chars": 1841,
    "preview": "<template>\n    <button\n        :type=\"type\"\n        @click=\"clickHandle\"\n        class=\"btn\"\n        :class=\"{\n         "
  },
  {
    "path": "src/scripts/components/Card.vue",
    "chars": 173,
    "preview": "<template>\n    <div class=\"card\" @click=\"$emit('click')\">\n        <slot></slot>\n    </div>\n</template>\n\n<script>\n    exp"
  },
  {
    "path": "src/scripts/components/CardBody.vue",
    "chars": 183,
    "preview": "<template>\n    <div class=\"card-body\" @click=\"$emit('click')\">\n        <slot></slot>\n    </div>\n</template>\n\n<script>\n  "
  },
  {
    "path": "src/scripts/components/Cell.vue",
    "chars": 1887,
    "preview": "<template>\n    <router-link\n        v-if=\"!disabled && to\"\n        :to=\"to\"\n        class=\"cell cell-link\"\n        :clas"
  },
  {
    "path": "src/scripts/components/Checkbox.vue",
    "chars": 1616,
    "preview": "<template>\n    <label\n        class=\"checkbox\"\n        :class=\"'checkbox-' + type\">\n        <input\n            type=\"che"
  },
  {
    "path": "src/scripts/components/CheckboxGroup.vue",
    "chars": 844,
    "preview": "<template>\n    <div class=\"checkbox-group\">\n        <slot></slot>\n    </div>\n</template>\n\n<script>\n    import Sync from "
  },
  {
    "path": "src/scripts/components/DatePicker/DateRange.vue",
    "chars": 4882,
    "preview": "<template>\n\t<div\n\t   v-show=\"visible\"\n\t   class=\"date-range-picker\">\n    \n        <div class=\"header\">\n            <div "
  },
  {
    "path": "src/scripts/components/DatePicker/DateTable.vue",
    "chars": 16584,
    "preview": "<template>\n    <table\n        cellspacing=\"0\"\n        cellpadding=\"0\"\n        class=\"date-table\"\n        @click=\"handleC"
  },
  {
    "path": "src/scripts/components/DatePicker/Picker.vue",
    "chars": 5462,
    "preview": "<template>\n    <div\n        class=\"date-picker\"\n        :class=\"{\n            [`date-picker-${size}`]: size,\n           "
  },
  {
    "path": "src/scripts/components/DatePicker/index.js",
    "chars": 768,
    "preview": "import Picker from './Picker';\nimport DateRangeView from './DateRange';\n\nconst getView = function(type) {\n    if(type =="
  },
  {
    "path": "src/scripts/components/Drawer.vue",
    "chars": 332,
    "preview": "<template>\n    <div class=\"drawer\">\n        <slot></slot>\n    </div>\n</template>\n\n<script>\n    import Tab from '../mixin"
  },
  {
    "path": "src/scripts/components/DrawerItem.vue",
    "chars": 529,
    "preview": "<template>\n    <div\n        class=\"drawer-item\"\n        :class=\"{\n            'active': $parent.currentActiveKey !== und"
  },
  {
    "path": "src/scripts/components/Flex.vue",
    "chars": 1312,
    "preview": "<template>\n    <div\n        class=\"flex\"\n        :class=\"[\n            alignClass,\n            {[`flex-justify-${justify"
  },
  {
    "path": "src/scripts/components/FlexItem.vue",
    "chars": 356,
    "preview": "<template>\n    <div\n        @click=\"$emit('click')\"\n        class=\"flex-item\"\n        :style=\"{ flex }\">\n        <slot><"
  },
  {
    "path": "src/scripts/components/Group.vue",
    "chars": 151,
    "preview": "<template>\n    <div class=\"group\">\n        <slot></slot>\n    </div>\n</template>\n\n<script>\n    export default {\n        n"
  },
  {
    "path": "src/scripts/components/GroupTitle.vue",
    "chars": 163,
    "preview": "<template>\n    <div class=\"group-title\">\n        <slot></slot>\n    </div>\n</template>\n\n<script>\n    export default {\n   "
  },
  {
    "path": "src/scripts/components/HRule.vue",
    "chars": 642,
    "preview": "<template>\n    <hr class=\"hr\" :class=\"{\n        [`border-${type}`]: type,\n        [`border-${theme}`]: theme,\n    }\">\n</"
  },
  {
    "path": "src/scripts/components/Icon.vue",
    "chars": 723,
    "preview": "<template>\n    <i\n        @click=\"$emit('click')\"\n        class=\"fa\"\n        :class=\"[\n            `fa-${name}`,\n       "
  },
  {
    "path": "src/scripts/components/InlineSelector.vue",
    "chars": 562,
    "preview": "<template>\n    <div class=\"inline-selector\">\n        <slot></slot>\n    </div>\n</template>\n\n<script>\n    import Sync from"
  },
  {
    "path": "src/scripts/components/InlineSelectorOption.vue",
    "chars": 480,
    "preview": "<template>\n    <tag\n        class=\"inline-selector-option\"\n        :class=\"{\n            disabled: disabled || $parent.d"
  },
  {
    "path": "src/scripts/components/InputNumber.vue",
    "chars": 1520,
    "preview": "<template>\n    <div class=\"input-number\" :class=\"{'input-number-disabled': disabled}\">\n        <a\n            class=\"inp"
  },
  {
    "path": "src/scripts/components/InputText.vue",
    "chars": 4367,
    "preview": "<template>\n    <div class=\"input-text\" v-disfavor=\"blur\">\n        <input\n            type=\"number\"\n            v-if=\"typ"
  },
  {
    "path": "src/scripts/components/InputTextarea.vue",
    "chars": 1094,
    "preview": "<template>\n    <div class=\"textarea\">\n        <textarea\n            v-model=\"currentValue\"\n            :disabled=\"disabl"
  },
  {
    "path": "src/scripts/components/Loading.vue",
    "chars": 755,
    "preview": "<template>\n    <svg\n        class=\"loading\"\n        :class=\"{\n            [`loading-${size}`]: size,\n            [`loadi"
  },
  {
    "path": "src/scripts/components/Loadmore.vue",
    "chars": 11865,
    "preview": "<template>\n    <div class=\"loadmore\">\n        <div\n            class=\"loadmore-content\"\n            :class=\"{ dropped: t"
  },
  {
    "path": "src/scripts/components/Mask.vue",
    "chars": 548,
    "preview": "<template>\n    <transition :name=\"transition\">\n        <div\n            class=\"mask\"\n            v-show=\"$parent.current"
  },
  {
    "path": "src/scripts/components/Media.vue",
    "chars": 504,
    "preview": "<template>\n    <div\n        @click=\"$emit('click')\"\n        class=\"media\"\n        :class=\"`flex-align-${align}`\">\n      "
  },
  {
    "path": "src/scripts/components/MediaBody.vue",
    "chars": 161,
    "preview": "<template>\n    <div class=\"media-body\">\n        <slot></slot>\n    </div>\n</template>\n\n<script>\n    export default {\n    "
  },
  {
    "path": "src/scripts/components/MediaObject.vue",
    "chars": 165,
    "preview": "<template>\n    <div class=\"media-object\">\n        <slot></slot>\n    </div>\n</template>\n\n<script>\n    export default {\n  "
  },
  {
    "path": "src/scripts/components/Navbar.vue",
    "chars": 705,
    "preview": "<template>\n    <div class=\"navbar\" :class=\"'navbar-' + theme\">\n        <div class=\"navbar-header\">\n            <slot></s"
  },
  {
    "path": "src/scripts/components/Navigation.vue",
    "chars": 318,
    "preview": "<template>\n    <div class=\"navigation\">\n        <slot></slot>\n    </div>\n</template>\n\n<script>\n    import Tab from '../m"
  },
  {
    "path": "src/scripts/components/NavigationItem.vue",
    "chars": 523,
    "preview": "<template>\n    <div\n        class=\"navigation-item\"\n        :class=\"{'active': $parent.currentActiveKey === currentEvent"
  },
  {
    "path": "src/scripts/components/Picker.vue",
    "chars": 5603,
    "preview": "<template>\n    <div\n        class=\"picker\"\n        :class=\"{\n            [`picker-${size}`]: size,\n        }\"\n        re"
  },
  {
    "path": "src/scripts/components/PickerOption.vue",
    "chars": 287,
    "preview": "<template>\n    <div\n        class=\"picker-list-item\"\n        :class=\"{active: $parent.currentValue === value}\">\n        "
  },
  {
    "path": "src/scripts/components/Progressbar.vue",
    "chars": 1106,
    "preview": "<template>\n    <div\n        class=\"progressbar\"\n        :class=\"{\n            [`progressbar-${size}`]: size,\n           "
  },
  {
    "path": "src/scripts/components/Progressbars.vue",
    "chars": 7298,
    "preview": "<template>\n    <div\n        class=\"progressbars\"\n        :class=\"{\n            [`progressbars-${size}`]: size,\n         "
  },
  {
    "path": "src/scripts/components/Radio.vue",
    "chars": 1601,
    "preview": "<template>\n    <label\n        class=\"radio\"\n        :class=\"'radio-' + shape\">\n        <input\n            type=\"radio\"\n "
  },
  {
    "path": "src/scripts/components/RadioGroup.vue",
    "chars": 227,
    "preview": "<template>\n    <div class=\"radio-group\">\n        <slot></slot>\n    </div>\n</template>\n\n<script>\n    import Sync from '.."
  },
  {
    "path": "src/scripts/components/Searchbar.vue",
    "chars": 891,
    "preview": "<template>\n    <div class=\"searchbar\" :class=\"{active: focus}\" v-disfavor=\"blur\">\n        <slot></slot>\n    </div>\n</tem"
  },
  {
    "path": "src/scripts/components/SearchbarBtn.vue",
    "chars": 167,
    "preview": "<template>\n    <div class=\"searchbar-btn\">\n        <slot></slot>\n    </div>\n</template>\n\n<script>\n    export default {\n "
  },
  {
    "path": "src/scripts/components/SearchbarPlaceholder.vue",
    "chars": 1277,
    "preview": "<template>\n    <div\n        @click=\"clickHandle\"\n        class=\"searchbar-input\"\n        :class=\"{\n            'border-c"
  },
  {
    "path": "src/scripts/components/SegmentedControl.vue",
    "chars": 352,
    "preview": "<template>\n    <div class=\"segmented-control\" :class=\"{disabled}\">\n        <slot></slot>\n    </div>\n</template>\n\n<script"
  },
  {
    "path": "src/scripts/components/SegmentedControlItem.vue",
    "chars": 444,
    "preview": "<template>\n    <div\n        class=\"segmented-control-item\"\n        :class=\"{\n            'active': $parent.currentActive"
  },
  {
    "path": "src/scripts/components/Selector.vue",
    "chars": 272,
    "preview": "<template>\n    <div class=\"selector\">\n        <slot></slot>\n    </div>\n</template>\n\n<script>\n    import Sync from '../mi"
  },
  {
    "path": "src/scripts/components/SelectorOption.vue",
    "chars": 617,
    "preview": "<template>\n    <div\n        class=\"cell selector-option\"\n        :class=\"{\n            'active': isActive(),\n           "
  },
  {
    "path": "src/scripts/components/Sidelip.vue",
    "chars": 794,
    "preview": "<template>\n    <div @click=\"$emit('click')\">\n        <transition :name=\"transition\">\n            <div v-show=\"currentVal"
  },
  {
    "path": "src/scripts/components/SlideUp.vue",
    "chars": 785,
    "preview": "<template>\n    <div>\n        <transition :name=\"transition\">\n            <div v-show=\"currentValue\" class=\"slideup\">\n   "
  },
  {
    "path": "src/scripts/components/SlideUpBody.vue",
    "chars": 262,
    "preview": "<template>\n    <div class=\"slideup-body\" :class=\"{'no-padding': noPadding}\">\n        <slot></slot>\n    </div>\n</template"
  },
  {
    "path": "src/scripts/components/SlideUpHeader.vue",
    "chars": 169,
    "preview": "<template>\n    <div class=\"slideup-header\">\n        <slot></slot>\n    </div>\n</template>\n\n<script>\n    export default {\n"
  },
  {
    "path": "src/scripts/components/Stepbar.vue",
    "chars": 427,
    "preview": "<template>\n    <div class=\"stepbar\">\n        <slot></slot>\n    </div>\n</template>\n\n<script>\n    import Sync from '../mix"
  },
  {
    "path": "src/scripts/components/StepbarItem.vue",
    "chars": 1781,
    "preview": "<template>\n    <div class=\"stepbar-item\">\n        <div\n            class=\"stepbar-line\"\n            :class=\"{active: sta"
  },
  {
    "path": "src/scripts/components/Sticky.vue",
    "chars": 4007,
    "preview": "<template>\n    <div>\n        <div :class=\"classes\" :style=\"styles\">\n            <slot></slot>\n        </div>\n    </div>\n"
  },
  {
    "path": "src/scripts/components/Swipe.vue",
    "chars": 8989,
    "preview": "<template>\n    <div class=\"swipe\" ref=\"swipe\">\n        <div class=\"swipe-items\">\n            <slot></slot>\n        </div"
  },
  {
    "path": "src/scripts/components/SwipeItem.vue",
    "chars": 4150,
    "preview": "<template>\n    <div class=\"swipe-item\"\n        @webkitTransitionEnd=\"resetByTranslateX()\">\n        <slot></slot>\n    </d"
  },
  {
    "path": "src/scripts/components/Tabbar.vue",
    "chars": 1506,
    "preview": "<template>\n    <div class=\"tabbar\" :class=\"{disabled}\">\n        <slot></slot>\n        <div class=\"tabbar-indicator\" ref="
  },
  {
    "path": "src/scripts/components/TabbarItem.vue",
    "chars": 422,
    "preview": "<template>\n    <div\n        class=\"tabbar-item\"\n        :class=\"{\n            'active': $parent.currentActiveKey === cur"
  },
  {
    "path": "src/scripts/components/Tag.vue",
    "chars": 1117,
    "preview": "<template>\n    <span\n        @click=\"$emit('click')\"\n        class=\"tag\"\n        :class=\"{\n            [`tag-${theme}`]:"
  },
  {
    "path": "src/scripts/components/Timeline.vue",
    "chars": 157,
    "preview": "<template>\n    <div class=\"timeline\">\n        <slot></slot>\n    </div>\n</template>\n\n<script>\n    export default {\n      "
  },
  {
    "path": "src/scripts/components/TimelineItem.vue",
    "chars": 540,
    "preview": "<template>\n    <div class=\"timeline-item\" :class=\"{'active': index === 0}\">\n        <div class=\"timeline-item-addon\"></d"
  },
  {
    "path": "src/scripts/components/Tip.vue",
    "chars": 793,
    "preview": "<template>\n    <div class=\"tip\" @click=\"$emit('click')\">\n        <hrule :type=\"type\" :theme=\"theme\" />\n        <span cla"
  },
  {
    "path": "src/scripts/components/Toast.vue",
    "chars": 1926,
    "preview": "<template>\n    <div>\n        <transition name=\"toast-fade\">\n            <div\n                v-show=\"currentValue\"\n     "
  },
  {
    "path": "src/scripts/components/Toggle.vue",
    "chars": 414,
    "preview": "<template>\n    <label class=\"toggle\">\n        <input\n            type=\"checkbox\"\n            class=\"toggle-input\"\n      "
  },
  {
    "path": "src/scripts/components/index.js",
    "chars": 4846,
    "preview": "import Button from './Button';\nimport Icon from './Icon';\nimport Group from './Group';\nimport GroupTitle from './GroupTi"
  },
  {
    "path": "src/scripts/containers/layout.vue",
    "chars": 615,
    "preview": "<template>\n    <flex direction=\"column\">\n        <navbar theme=\"default\">\n            <icon name=\"bars\" size=\"lg\" @click"
  },
  {
    "path": "src/scripts/containers/menubar.vue",
    "chars": 1546,
    "preview": "<template>\n    <sidelip v-model=\"currentValue\">\n        <flex direction=\"column\" :style=\"{backgroundColor: '#f0eff5'}\">\n"
  },
  {
    "path": "src/scripts/directives/disfavor.js",
    "chars": 675,
    "preview": "import { contains } from '../utils/dom';\n\nconst disfavorContext = '@@disfavor';\n\n// 失去焦点\nexport default {\n    bind(el, b"
  },
  {
    "path": "src/scripts/index.js",
    "chars": 231,
    "preview": "import Vue from 'vue';\nimport router from './router';\nimport Impression from './components';\n\nVue.use(Impression);\n\n/* e"
  },
  {
    "path": "src/scripts/mixins/emitter.js",
    "chars": 552,
    "preview": "export default {\n    methods: {\n        dispatch(componentName, eventName, params) {\n            let parent = this.$pare"
  },
  {
    "path": "src/scripts/mixins/select.js",
    "chars": 871,
    "preview": "export default {\n    props: {\n        multiple: Boolean,\n    },\n    data() {\n        return {\n            currentText: {"
  },
  {
    "path": "src/scripts/mixins/selectOption.js",
    "chars": 624,
    "preview": "export default {\n    props: {\n        value: {},\n        checkedIcon: {\n            type: String,\n            default: '"
  },
  {
    "path": "src/scripts/mixins/sync.js",
    "chars": 614,
    "preview": "import { isArray } from '../utils/type';\n\nexport default {\n    props: {\n        value: {},\n        disabled: Boolean,\n  "
  },
  {
    "path": "src/scripts/mixins/tab.js",
    "chars": 459,
    "preview": "export default {\n    props: {\n        activeKey: {},\n        disabled: Boolean,\n    },\n    data() {\n        return {\n   "
  },
  {
    "path": "src/scripts/mixins/tabItem.js",
    "chars": 561,
    "preview": "export default {\n    props: {\n        eventKey: {},\n        disabled: Boolean,\n    },\n    data() {\n        return {\n    "
  },
  {
    "path": "src/scripts/router.js",
    "chars": 777,
    "preview": "import Vue from 'vue';\nimport VueRouter from 'vue-router';\nimport routesConfig from './routes.json';\n\nVue.use(VueRouter)"
  },
  {
    "path": "src/scripts/routes.json",
    "chars": 5689,
    "preview": "[\n    {\n        \"title\": \"Base\",\n        \"children\": [\n            {\n                \"path\": \"/button\",\n                "
  },
  {
    "path": "src/scripts/utils/alert.js",
    "chars": 537,
    "preview": "import Vue from 'vue';\nimport OriginAlert from '../components/Alert';\n\nconst Alert = Vue.extend(OriginAlert);\nlet alertI"
  },
  {
    "path": "src/scripts/utils/cssPrefix.js",
    "chars": 589,
    "preview": "let engine;\nlet docStyle = document.documentElement.style;\n\nif('MozAppearance' in docStyle) {\n    engine = 'gecko';\n} el"
  },
  {
    "path": "src/scripts/utils/date.js",
    "chars": 7485,
    "preview": "/*eslint-disable*/\n\nimport dateUtil from './dateUtil';\n\nconst weeks = ['sun', 'mon', 'tue', 'wed', 'thu', 'fri', 'sat'];"
  },
  {
    "path": "src/scripts/utils/dateUtil.js",
    "chars": 10423,
    "preview": "/* Modified from https://github.com/taylorhakes/fecha\n *\n * The MIT License (MIT)\n *\n * Copyright (c) 2015 Taylor Hakes\n"
  },
  {
    "path": "src/scripts/utils/dom.js",
    "chars": 1684,
    "preview": "/**\n * 判断一个元素是否为另一个的后代元素.\n * @param  {[Element]} ancestor     [祖先元素]\n * @param  {[Element]} descendent   [后代元素]\n * @retu"
  },
  {
    "path": "src/scripts/utils/draggable.js",
    "chars": 2657,
    "preview": "import { getTranslate } from './translate';\n\n/* global document:true */\nconst draggable = (el, options) => {\n    let pre"
  },
  {
    "path": "src/scripts/utils/easing.js",
    "chars": 326,
    "preview": "// easeInOut\nexport const easeInOutCubic = (time, offset, end, duration) => {\n    let cc = end - offset,\n        tempTim"
  },
  {
    "path": "src/scripts/utils/loading.js",
    "chars": 814,
    "preview": "import Vue from 'vue';\nimport OriginToast from '../components/Toast';\n\nconst Toast = Vue.extend(OriginToast);\n\nlet insta"
  },
  {
    "path": "src/scripts/utils/toast.js",
    "chars": 1250,
    "preview": "import Vue from 'vue';\nimport OriginToast from '../components/Toast';\n\nconst Toast = Vue.extend(OriginToast);\n\n// toast缓"
  },
  {
    "path": "src/scripts/utils/translate.js",
    "chars": 1379,
    "preview": "import { vendorPrefix } from './cssPrefix';\n\nlet transformProperty = `${vendorPrefix}Transform`;\n\n// 获取位移\nexport const g"
  },
  {
    "path": "src/scripts/utils/type.js",
    "chars": 100,
    "preview": "// 是否数组\nexport const isArray = array => Object.prototype.toString.call(array) === '[object Array]';\n"
  },
  {
    "path": "src/scripts/views/alert.vue",
    "chars": 2566,
    "preview": "<template>\n    <div>\n        <group-title>base</group-title>\n        <group>\n            <cell @click=\"alertClickHandle\""
  },
  {
    "path": "src/scripts/views/back-to-top.vue",
    "chars": 316,
    "preview": "<template>\n    <div>\n        <div v-for=\"n in 10\">\n            <group-title><strong>group {{n}}</strong></group-title>\n "
  },
  {
    "path": "src/scripts/views/badge.vue",
    "chars": 2421,
    "preview": "<template>\n    <div>\n        <group-title>base</group-title>\n        <group>\n            <cell>\n                <badge c"
  },
  {
    "path": "src/scripts/views/button.vue",
    "chars": 2362,
    "preview": "<template>\n    <div>\n        <group-title>theme</group-title>\n        <group>\n            <cell>\n                <btn th"
  },
  {
    "path": "src/scripts/views/card.vue",
    "chars": 3727,
    "preview": "<template>\n    <div class=\"container\">\n        <tip><span class=\"text-pure\">科比系列</span></tip>\n        <div class=\"prod-c"
  },
  {
    "path": "src/scripts/views/cell.vue",
    "chars": 1867,
    "preview": "<template>\n    <div>\n        <group-title><strong>Base</strong></group-title>\n        <group>\n            <cell>Home</ce"
  },
  {
    "path": "src/scripts/views/checkbox.vue",
    "chars": 3013,
    "preview": "<template>\n    <div>\n        <group-title>Base</group-title>\n        <group>\n            <cell>\n                <checkbo"
  },
  {
    "path": "src/scripts/views/confirm.vue",
    "chars": 719,
    "preview": "<template>\n    <div>\n        <group-title>confirm</group-title>\n        <group>\n            <cell @click=\"showHandle(fal"
  },
  {
    "path": "src/scripts/views/datepicker.vue",
    "chars": 871,
    "preview": "<template>\n    <div class=\"date-wrapper\">\n        <date-picker\n            type=\"daterange\"\n            size=\"base\"\n    "
  },
  {
    "path": "src/scripts/views/drawer.vue",
    "chars": 988,
    "preview": "<template>\n    <div>\n        <group-title>base</group-title>\n        <drawer @change=\"changeHandle\">\n            <drawer"
  },
  {
    "path": "src/scripts/views/flex.vue",
    "chars": 2191,
    "preview": "<template>\n    <div>\n        <group-title>等比</group-title>\n        <flex class=\"row\">\n            <flex-item class=\"bg-p"
  },
  {
    "path": "src/scripts/views/hrule.vue",
    "chars": 760,
    "preview": "<template>\n    <div>\n        <group-title>base(hr)</group-title>\n        <group>\n            <cell>\n                <hru"
  },
  {
    "path": "src/scripts/views/icon.vue",
    "chars": 1085,
    "preview": "<template>\n    <div>\n        <group-title>fontawesome</group-title>\n        <group>\n            <cell href=\"http://fonta"
  },
  {
    "path": "src/scripts/views/index.vue",
    "chars": 779,
    "preview": "<template>\n    <div>\n        <div v-for=\"group in groups\">\n            <group-title v-if=\"group.title\"><strong>{{group.t"
  },
  {
    "path": "src/scripts/views/inline-selecor.vue",
    "chars": 4013,
    "preview": "<template>\n    <div>\n        <group-title>\n            base\n            <span class=\"pull-right\">size: {{size}}</span>\n "
  },
  {
    "path": "src/scripts/views/input-number.vue",
    "chars": 1490,
    "preview": "<template>\n    <div>\n        <group-title>Base</group-title>\n        <group>\n            <cell>\n                <span>de"
  },
  {
    "path": "src/scripts/views/input-text.vue",
    "chars": 1479,
    "preview": "<template>\n    <div>\n        <group-title>base</group-title>\n        <group>\n            <cell>\n                <span sl"
  },
  {
    "path": "src/scripts/views/input-textarea.vue",
    "chars": 456,
    "preview": "<template>\n    <div>\n        <group-title>base</group-title>\n        <group>\n            <cell class=\"no-padding\">\n     "
  },
  {
    "path": "src/scripts/views/loading.vue",
    "chars": 937,
    "preview": "<template>\n    <div>\n        <group-title>loading</group-title>\n        <group>\n            <cell @click=\"showLoadingHan"
  },
  {
    "path": "src/scripts/views/media.vue",
    "chars": 1012,
    "preview": "<template>\n    <div>\n        <group-title>Media</group-title>\n        <group>\n            <cell>\n                <media>"
  },
  {
    "path": "src/scripts/views/navbar.vue",
    "chars": 1053,
    "preview": "<template>\n    <div>\n        <navbar theme=\"default\">\n            <router-link :to=\"{path: '/'}\">\n                <icon "
  },
  {
    "path": "src/scripts/views/navigation.vue",
    "chars": 1305,
    "preview": "<template>\n    <div>\n        <group-title>base</group-title>\n        <navigation>\n            <navigation-item label=\"首页"
  },
  {
    "path": "src/scripts/views/picker.vue",
    "chars": 2397,
    "preview": "<template>\n    <div>\n        <group-title>base:{{defaultCity}}</group-title>\n        <picker v-model=\"defaultCity\">\n    "
  },
  {
    "path": "src/scripts/views/progressbar.vue",
    "chars": 8521,
    "preview": "<template>\n    <div>\n        <group-title>base</group-title>\n        <group>\n            <cell>\n                <progres"
  },
  {
    "path": "src/scripts/views/pull-down.vue",
    "chars": 2124,
    "preview": "<template>\n    <div>\n        <group-title>\n            Pull down\n            <span class=\"pull-right\">status: {{ topAllL"
  },
  {
    "path": "src/scripts/views/pull-up.vue",
    "chars": 2190,
    "preview": "<template>\n    <div>\n        <group-title>\n            Pull up\n            <span class=\"pull-right\">status: {{ bottomAll"
  },
  {
    "path": "src/scripts/views/radio.vue",
    "chars": 2003,
    "preview": "<template>\n    <div>\n        <group-title>\n            Radio\n            <span class=\"pull-right\">selected: {{ single }}"
  },
  {
    "path": "src/scripts/views/searchbar.vue",
    "chars": 1349,
    "preview": "<template>\n    <div>\n        <group-title>base:{{search}}</group-title>\n        <group>\n            <cell>\n             "
  },
  {
    "path": "src/scripts/views/segmented-control.vue",
    "chars": 1014,
    "preview": "<template>\n    <div>\n        <group-title>base</group-title>\n        <group>\n            <cell class=\"text-center\">\n    "
  },
  {
    "path": "src/scripts/views/selector.vue",
    "chars": 2158,
    "preview": "<template>\n    <div>\n        <group-title>\n            base\n            <span class=\"pull-right\">selected:{{single}}</sp"
  },
  {
    "path": "src/scripts/views/sideslip.vue",
    "chars": 2277,
    "preview": "<template>\n    <div>\n        <group-title>base</group-title>\n        <group>\n            <cell @click=\"toggleHandle\">sid"
  },
  {
    "path": "src/scripts/views/slideup.vue",
    "chars": 3395,
    "preview": "<template>\n    <div>\n        <group-title>base</group-title>\n        <group>\n            <cell @click=\"showBase = true\">"
  },
  {
    "path": "src/scripts/views/stepbar.vue",
    "chars": 1745,
    "preview": "<template>\n    <div>\n        <group-title>base</group-title>\n        <group>\n            <cell>\n                <stepbar"
  },
  {
    "path": "src/scripts/views/sticky.vue",
    "chars": 5324,
    "preview": "<template>\n    <div>\n        <group-title><strong>Base</strong></group-title>\n        <group>\n            <cell>Home</ce"
  },
  {
    "path": "src/scripts/views/swipe.vue",
    "chars": 3804,
    "preview": "<template>\n    <div>\n        <group-title>base</group-title>\n        <swipe\n            :onDragStart=\"onDragStart\"\n     "
  },
  {
    "path": "src/scripts/views/tabbar.vue",
    "chars": 1725,
    "preview": "<template>\n    <div>\n        <group-title>base</group-title>\n        <tabbar class=\"no-margin\" @change=\"changeHandle\">\n "
  },
  {
    "path": "src/scripts/views/tag.vue",
    "chars": 3797,
    "preview": "<template>\n    <div>\n        <group-title>base</group-title>\n        <group>\n            <cell>\n                primary\n"
  },
  {
    "path": "src/scripts/views/timeline.vue",
    "chars": 579,
    "preview": "<template>\n    <div>\n        <group-title>base</group-title>\n        <group>\n            <cell>\n                <timelin"
  },
  {
    "path": "src/scripts/views/tip.vue",
    "chars": 618,
    "preview": "<template>\n    <div>\n        <group-title>base</group-title>\n        <group>\n            <cell>\n                <tip>查看图"
  },
  {
    "path": "src/scripts/views/toast.vue",
    "chars": 1996,
    "preview": "<template>\n    <div>\n        <group-title>Base</group-title>\n        <group>\n            <cell @click=\"baseClickHandle('"
  },
  {
    "path": "src/scripts/views/toggle.vue",
    "chars": 868,
    "preview": "<template>\n    <div>\n        <group-title>Base</group-title>\n        <group>\n            <cell>\n                <toggle "
  },
  {
    "path": "src/styles/animate.scss",
    "chars": 1179,
    "preview": "// fade\n@keyframes fadeIn {\n    from {\n        opacity: 0;\n    }\n\n    to {\n        opacity: 1;\n    }\n}\n\n@keyframes fadeO"
  },
  {
    "path": "src/styles/index.scss",
    "chars": 96,
    "preview": "@import 'variables';\n@import 'mixins';\n@import 'modules';\n@import 'utils';\n@import 'animate';\n\n\n"
  },
  {
    "path": "src/styles/mixins/border.scss",
    "chars": 785,
    "preview": "// 上边框\n@mixin border-top {\n    background-image: $border-image-top;\n    background-size: 100% 1px;\n    background-repeat"
  },
  {
    "path": "src/styles/mixins/button.scss",
    "chars": 1133,
    "preview": "// Button sizes\n@mixin button-size($padding-y, $padding-x, $font-size, $border-radius) {\n    padding: $padding-y $paddin"
  },
  {
    "path": "src/styles/mixins/media.scss",
    "chars": 339,
    "preview": "@mixin respond-to($size) {\n    $query: map-get($all-media-query-device, $size);\n\n    @if not $query {\n        @error 'No"
  },
  {
    "path": "src/styles/mixins/navbar.scss",
    "chars": 306,
    "preview": "// Navbar variant\n@mixin navbar-variant($background-color, $color, $has-border) {\n    background-color: $background-colo"
  },
  {
    "path": "src/styles/mixins/progressbar.scss",
    "chars": 130,
    "preview": "@mixin progressbar-variant($background-color) {\n    .progressbar-indicator {\n        background-color: $background-color"
  },
  {
    "path": "src/styles/mixins/tag.scss",
    "chars": 204,
    "preview": "@mixin tag-variant($background-color) {\n    background-color: $background-color;\n}\n\n@mixin tag-outline-variant($border-c"
  },
  {
    "path": "src/styles/mixins/text.scss",
    "chars": 242,
    "preview": "// Typography\n@mixin text-emphasis-variant($parent, $color) {\n    #{$parent} {\n        color: $color !important;\n    }\n\n"
  },
  {
    "path": "src/styles/mixins.scss",
    "chars": 184,
    "preview": "// utils\n@import 'mixins/border';\n@import 'mixins/text';\n\n@import 'mixins/button';\n@import 'mixins/navbar';\n@import 'mix"
  },
  {
    "path": "src/styles/modules/alert.scss",
    "chars": 1598,
    "preview": ".alert {\n    position: fixed;\n    left: 0;\n    right: 0;\n    top: 0;\n    bottom: 0;\n    z-index: $z-index-alert;\n    dis"
  },
  {
    "path": "src/styles/modules/back-to-top.scss",
    "chars": 639,
    "preview": ".back-to-top {\n    position: fixed;\n    right: $top-right;\n    bottom: -1.5 * $top-height;\n    z-index: $z-index-top;\n  "
  },
  {
    "path": "src/styles/modules/badge.scss",
    "chars": 1412,
    "preview": ".badge {\n    position: relative;\n    display: inline-block;\n    vertical-align: middle;\n}\n\n.badge-gap {\n    margin-right"
  },
  {
    "path": "src/styles/modules/button.scss",
    "chars": 1687,
    "preview": ".btn {\n    display: inline-block;\n    appearance: none;\n    overflow: hidden;\n    outline: 0;\n    text-align: center;\n  "
  },
  {
    "path": "src/styles/modules/card.scss",
    "chars": 98,
    "preview": ".card {\n    background-color: $card-bg-color;\n}\n\n\n.card-body {\n    padding: $card-body-padding;\n}\n"
  },
  {
    "path": "src/styles/modules/cell.scss",
    "chars": 835,
    "preview": ".cell {\n    display: flex;\n    align-items: center;\n    overflow: hidden;\n    text-decoration: none;\n    color: $cell-co"
  },
  {
    "path": "src/styles/modules/checkbox.scss",
    "chars": 2721,
    "preview": ".checkbox,\n.radio {\n    user-select: none;\n    display: inline-block;\n}\n\n// shape\n.checkbox-square {\n    .checkbox-addon"
  },
  {
    "path": "src/styles/modules/date-picker.scss",
    "chars": 3974,
    "preview": "$ColorBlue: #1287FF;\n$ColorGrey: #BCC1CC;\n\n.date-picker {\n\twidth: 18.75em;\n    box-sizing: border-box;\n    margin: 0;\n  "
  },
  {
    "path": "src/styles/modules/drawer.scss",
    "chars": 729,
    "preview": ".drawer {\n    display: flex;\n    color: $drawer-color;\n    background-color: $drawer-bg-color;\n    padding: $drawer-padd"
  },
  {
    "path": "src/styles/modules/group.scss",
    "chars": 319,
    "preview": ".group {\n    padding-top: 1px;\n    padding-bottom: 1px;\n    margin: $group-margin;\n    @include border-vertical();\n    b"
  },
  {
    "path": "src/styles/modules/hrule.scss",
    "chars": 189,
    "preview": ".hr {\n    border-width: 0;\n    border-top-width: 1px;\n    border-style: solid;\n    border-color: $hr-color;\n    transfor"
  },
  {
    "path": "src/styles/modules/inline-selector.scss",
    "chars": 285,
    "preview": ".inline-selector {\n    display: block;\n}\n\n.inline-selector-option {\n    line-height: 1;\n    padding: $inline-selector-op"
  },
  {
    "path": "src/styles/modules/input-number.scss",
    "chars": 1829,
    "preview": ".input-number {\n    display: inline-block;\n}\n\n.input-number-disabled {\n    opacity: .6;\n\n    .input-number-input {\n     "
  },
  {
    "path": "src/styles/modules/input-text.scss",
    "chars": 388,
    "preview": ".input-text {\n    display: flex;\n    align-items: center;\n}\n\n.input-text-input {\n    flex: 1;\n    border: 0;\n    padding"
  },
  {
    "path": "src/styles/modules/input-textarea.scss",
    "chars": 416,
    "preview": ".textarea-input {\n    border: 0;\n    width: 100%;\n    resize: none;\n    display: block;\n    appearance: none;\n    user-s"
  },
  {
    "path": "src/styles/modules/loading.scss",
    "chars": 945,
    "preview": ".loading {\n    width: $loading-width;\n    animation: rotate $loading-animation-duration linear infinite;\n}\n\n.loading-pat"
  },
  {
    "path": "src/styles/modules/loadmore.scss",
    "chars": 484,
    "preview": ".loadmore {\n    overflow: hidden;\n}\n\n.loadmore-content {\n    &.dropped {\n        transition: $loadmore-dropped-transitio"
  },
  {
    "path": "src/styles/modules/mask.scss",
    "chars": 404,
    "preview": ".mask {\n    position: fixed;\n    left: 0;\n    top: 0;\n    right: 0;\n    bottom: 0;\n    background-color: $mask-bg-color;"
  },
  {
    "path": "src/styles/modules/media.scss",
    "chars": 295,
    "preview": ".media {\n    display: flex;\n    background-color: $media-bg-color;\n}\n\n\n.media-object {\n    display: block;\n\n    &:first-"
  },
  {
    "path": "src/styles/modules/navbar.scss",
    "chars": 1152,
    "preview": ".navbar {\n    display: flex;\n    user-select: none;\n    padding: $navbar-padding;\n    height: $navbar-height;\n    line-h"
  },
  {
    "path": "src/styles/modules/navigation.scss",
    "chars": 581,
    "preview": ".navigation {\n    display: flex;\n    align-items: center;\n    @include border-top();\n    height: $navigation-height;\n   "
  },
  {
    "path": "src/styles/modules/picker.scss",
    "chars": 2089,
    "preview": ".picker {\n    width: 100%;\n    display: block;\n    position: relative;\n    overflow: hidden;\n    background: $picker-bg-"
  },
  {
    "path": "src/styles/modules/progressbar.scss",
    "chars": 3193,
    "preview": ".progressbar {\n    height: $progressbar-height;\n    overflow: hidden;\n    background-color: $progressbar-bg-color;\n    b"
  },
  {
    "path": "src/styles/modules/searchbar.scss",
    "chars": 1468,
    "preview": ".searchbar {\n    display: flex;\n    align-items: center;\n\n    &.active {\n        .searchbar-input-field {\n            wi"
  },
  {
    "path": "src/styles/modules/segmented-control.scss",
    "chars": 674,
    "preview": ".segmented-control {\n    font-size: 0;\n    display: inline-block;\n    border: 1px solid $segmented-control-border-color;"
  },
  {
    "path": "src/styles/modules/selector.scss",
    "chars": 434,
    "preview": ".selector {\n    > .selector-option:last-child {\n        background-size: 0;\n    }\n}\n\n.selector-option {\n    transition: "
  },
  {
    "path": "src/styles/modules/sidelip.scss",
    "chars": 575,
    "preview": ".sidelip {\n    position: fixed;\n    left: 0;\n    top: 0;\n    display: block;\n    height: 100%;\n    overflow-y: auto;\n   "
  },
  {
    "path": "src/styles/modules/slideup.scss",
    "chars": 717,
    "preview": ".slideup {\n    position: fixed;\n    left: 0;\n    bottom: 0;\n    width: 100%;\n    background-color: $slide-bg-color;\n    "
  },
  {
    "path": "src/styles/modules/stepbar.scss",
    "chars": 1648,
    "preview": ".stepbar {\n    display: flex;\n    align-items: center;\n}\n\n.stepbar-item {\n    flex: 1;\n    display: flex;\n    align-item"
  },
  {
    "path": "src/styles/modules/sticky.scss",
    "chars": 203,
    "preview": ".sticky-affix {\n    position: fixed;\n    z-index: 999;\n}\n\n.demo-sticky {\n    display: inline-block;\n    color: #fff;\n   "
  },
  {
    "path": "src/styles/modules/swipe.scss",
    "chars": 767,
    "preview": ".swipe {\n    overflow: hidden;\n    position: relative;\n}\n\n.swipe-indicators {\n    position: absolute;\n    left: 50%;\n   "
  },
  {
    "path": "src/styles/modules/tabbar.scss",
    "chars": 754,
    "preview": ".tabbar {\n    position: relative;\n    display: flex;\n    height: $tabbar-height;\n    margin-bottom: $tabbar-margin-botto"
  },
  {
    "path": "src/styles/modules/tag.scss",
    "chars": 997,
    "preview": ".tag {\n    display: inline-block;\n    padding: $tag-padding;\n    color: $tag-color;\n    text-align: center;\n    white-sp"
  },
  {
    "path": "src/styles/modules/timeline.scss",
    "chars": 1891,
    "preview": ".timeline {\n    margin: 0;\n    padding: 0;\n    list-style: none;\n}\n\n.timeline-item {\n    position: relative;\n    padding"
  },
  {
    "path": "src/styles/modules/tip.scss",
    "chars": 192,
    "preview": ".tip {\n    display: flex;\n    align-items: center;\n    padding: $tip-padding;\n\n    .hr {\n        flex: 1;\n    }\n}\n\n.tip-"
  },
  {
    "path": "src/styles/modules/toast.scss",
    "chars": 1125,
    "preview": ".toast {\n    position: fixed;\n    text-align: center;\n    color: $toast-color;\n    z-index: $z-index-toast;\n    max-widt"
  },
  {
    "path": "src/styles/modules/toggle.scss",
    "chars": 872,
    "preview": ".toggle {\n    display: inline-block;\n    position: relative;\n}\n\n.toggle-input {\n    display: none;\n\n    &:checked + .tog"
  },
  {
    "path": "src/styles/modules.scss",
    "chars": 1038,
    "preview": "// css components\n@import 'modules/button';\n@import 'modules/group';\n@import 'modules/cell';\n@import 'modules/tag';\n@imp"
  },
  {
    "path": "src/styles/utils/background.scss",
    "chars": 681,
    "preview": "// backgrounds\n.bg-inverse {\n    color: $gray-lighter;\n    background-color: $gray-dark;\n}\n\n.bg-faded {\n    background-c"
  },
  {
    "path": "src/styles/utils/border.scss",
    "chars": 594,
    "preview": "// shape\n.border-circle {\n    border-radius: $border-radius-circle !important;\n}\n\n.border-pill {\n    border-radius: $bor"
  },
  {
    "path": "src/styles/utils/clearfix.scss",
    "chars": 101,
    "preview": ".clearfix {\n    &::after {\n        content: '';\n        display: table;\n        clear: both;\n    }\n}\n"
  },
  {
    "path": "src/styles/utils/display.scss",
    "chars": 137,
    "preview": "\n.invisible {\n    visibility: hidden !important;\n}\n\n.hidden {\n    display: none !important;\n}\n\n.block {\n    display: blo"
  },
  {
    "path": "src/styles/utils/flex.scss",
    "chars": 613,
    "preview": ".flex {\n    overflow: hidden;\n    display: flex;\n}\n\n.flex-vertical {\n    height: 100%;\n    flex-direction: column;\n}\n\n\n."
  },
  {
    "path": "src/styles/utils/img.scss",
    "chars": 115,
    "preview": ".img-circle {\n    border-radius: 50%;\n}\n\n.img-fluid {\n    display: block;\n    max-width: 100%;\n    height: auto;\n}\n"
  },
  {
    "path": "src/styles/utils/reboot.scss",
    "chars": 7176,
    "preview": "// scss-lint:disable ImportantRule, QualifyingElement, DuplicateProperty\n\n// Reboot\n//\n// Global resets to common HTML e"
  },
  {
    "path": "src/styles/utils/spacing.scss",
    "chars": 177,
    "preview": ".no-margin {\n    margin: 0 !important;\n}\n\n.no-padding {\n    padding: 0 !important;\n}\n\n.gap-left {\n    margin-right: $spa"
  },
  {
    "path": "src/styles/utils/text.scss",
    "chars": 656,
    "preview": "// Contextual colors\n\n@include text-emphasis-variant('.text-muted', $text-muted);\n\n@include text-emphasis-variant('.text"
  },
  {
    "path": "src/styles/utils.scss",
    "chars": 218,
    "preview": "@import 'utils/reboot';\n\n@import 'utils/flex';\n@import 'utils/spacing';\n@import 'utils/background';\n@import 'utils/text'"
  }
]

// ... and 4 more files (download for full content)

About this extraction

This page contains the full source code of the NewDadaFE/vue-impression GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 204 files (327.8 KB), approximately 81.5k tokens, and a symbol index with 28 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.

Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.

Copied to clipboard!