import { Meteor } from 'meteor/meteor';
import _ from 'underscore';

import { hasPermission } from '../../../../authorization';
import { OAuthApps, Users } from '../../../../models';
import { parseUriList } from '../functions/parseUriList';

Meteor.methods({
	updateOAuthApp(applicationId, application) {
		if (!hasPermission(this.userId, 'manage-oauth-apps')) {
			throw new Meteor.Error('error-not-allowed', 'Not allowed', { method: 'updateOAuthApp' });
		}
		if (!_.isString(application.name) || application.name.trim() === '') {
			throw new Meteor.Error('error-invalid-name', 'Invalid name', { method: 'updateOAuthApp' });
		}
		if (!_.isString(application.redirectUri) || application.redirectUri.trim() === '') {
			throw new Meteor.Error('error-invalid-redirectUri', 'Invalid redirectUri', { method: 'updateOAuthApp' });
		}
		if (!_.isBoolean(application.active)) {
			throw new Meteor.Error('error-invalid-arguments', 'Invalid arguments', { method: 'updateOAuthApp' });
		}
		const currentApplication = OAuthApps.findOne(applicationId);
		if (currentApplication == null) {
			throw new Meteor.Error('error-application-not-found', 'Application not found', { method: 'updateOAuthApp' });
		}

		const redirectUri = parseUriList(application.redirectUri);

		if (redirectUri.length === 0) {
			throw new Meteor.Error('error-invalid-redirectUri', 'Invalid redirectUri', { method: 'updateOAuthApp' });
		}

		OAuthApps.update(applicationId, {
			$set: {
				name: application.name,
				active: application.active,
				redirectUri,
				_updatedAt: new Date(),
				_updatedBy: Users.findOne(this.userId, {
					fields: {
						username: 1,
					},
				}),
			},
		});
		return OAuthApps.findOne(applicationId);
	},
});


export const findWeeklyUsersRegisteredData = async ({ start, end }) => {

const daysBetweenDates = diffBetweenDaysInclusive(end, start);

keep keep keep replace keep keep keep keep keep

/* eslint no-use-before-define:0 */
import { Meteor } from 'meteor/meteor';
import { Match, check } from 'meteor/check';
import { Mongo } from 'meteor/mongo';
import { Log } from 'meteor/logging';
import _ from 'underscore';
import s from 'underscore.string';
import moment from 'moment';

import { Info } from '../../utils';
/*
	Adds migration capabilities. Migrations are defined like:

	Migrations.add({
		up: function() {}, //*required* code to run to migrate upwards
		version: 1, //*required* number to identify migration order
		down: function() {}, //*optional* code to run to migrate downwards
		name: 'Something' //*optional* display name for the migration
	});

	The ordering of migrations is determined by the version you set.

	To run the migrations, set the MIGRATION_VERSION environment variable to either
	'latest' or the version number you want to migrate to. Optionally, append
	',exit' if you want the migrations to exit the meteor process, e.g if you're
	migrating from a script (remember to pass the --once parameter).

	e.g:
	MIGRATION_VERSION="latest" mrt # ensure we'll be at the latest version and run the app
	MIGRATION_VERSION="latest,exit" mrt --once # ensure we'll be at the latest version and exit
	MIGRATION_VERSION="2,exit" mrt --once # migrate to version 2 and exit
	MIGRATION_VERSION="2,rerun,exit" mrt --once # rerun migration script for version 2 and exit

	Note: Migrations will lock ensuring only 1 app can be migrating at once. If
	a migration crashes, the control record in the migrations collection will
	remain locked and at the version it was at previously, however the db could
	be in an inconsistant state.
*/

// since we'll be at version 0 by default, we should have a migration set for it.
const DefaultMigration = {
	version: 0,
	up() {
		// @TODO: check if collection "migrations" exist
		// If exists, rename and rerun _migrateTo
	},
};

export const Migrations = {
	_list: [DefaultMigration],
	options: {
		// false disables logging
		log: true,
		// null or a function
		logger: null,
		// enable/disable info log "already at latest."
		logIfLatest: true,
		// lock will be valid for this amount of minutes
		lockExpiration: 5,
		// retry interval in seconds
		retryInterval: 10,
		// max number of attempts to retry unlock
		maxAttempts: 30,
		// migrations collection name
		collectionName: 'migrations',
		// collectionName: "rocketchat_migrations"
	},
	config(opts) {
		this.options = _.extend({}, this.options, opts);
	},
};

Migrations._collection = new Mongo.Collection(Migrations.options.collectionName);

/* Create a box around messages for displaying on a console.log */
function makeABox(message, color = 'red') {
	if (!_.isArray(message)) {
		message = message.split('\n');
	}
	const len = _(message).reduce(function(memo, msg) {
		return Math.max(memo, msg.length);
	}, 0) + 4;
	const text = message.map((msg) => '|'[color] + s.lrpad(msg, len)[color] + '|'[color]).join('\n');
	const topLine = '+'[color] + s.pad('', len, '-')[color] + '+'[color];
	const separator = '|'[color] + s.pad('', len, '') + '|'[color];
	const bottomLine = '+'[color] + s.pad('', len, '-')[color] + '+'[color];
	return `\n${ topLine }\n${ separator }\n${ text }\n${ separator }\n${ bottomLine }\n`;
}

/*
	Logger factory function. Takes a prefix string and options object
	and uses an injected `logger` if provided, else falls back to
	Meteor's `Log` package.
	Will send a log object to the injected logger, on the following form:
		message: String
		level: String (info, warn, error, debug)
		tag: 'Migrations'
*/
function createLogger(prefix) {
	check(prefix, String);

	// Return noop if logging is disabled.
	if (Migrations.options.log === false) {
		return function() {};
	}

	return function(level, message) {
		check(level, Match.OneOf('info', 'error', 'warn', 'debug'));
		check(message, Match.OneOf(String, [String]));

		const logger = Migrations.options && Migrations.options.logger;

		if (logger && _.isFunction(logger)) {
			logger({
				level,
				message,
				tag: prefix,
			});
		} else {
			Log[level]({
				message: `${ prefix }: ${ message }`,
			});
		}
	};
}

// collection holding the control record

const log = createLogger('Migrations');

['info', 'warn', 'error', 'debug'].forEach(function(level) {
	log[level] = _.partial(log, level);
});

// if (process.env.MIGRATE)
//   Migrations.migrateTo(process.env.MIGRATE);

// Add a new migration:
// {up: function *required
//  version: Number *required
//  down: function *optional
//  name: String *optional
// }
Migrations.add = function(migration) {
	if (typeof migration.up !== 'function') { throw new Meteor.Error('Migration must supply an up function.'); }

	if (typeof migration.version !== 'number') { throw new Meteor.Error('Migration must supply a version number.'); }

	if (migration.version <= 0) { throw new Meteor.Error('Migration version must be greater than 0'); }

	// Freeze the migration object to make it hereafter immutable
	Object.freeze(migration);

	this._list.push(migration);
	this._list = _.sortBy(this._list, function(m) {
		return m.version;
	});
};

// Attempts to run the migrations using command in the form of:
// e.g 'latest', 'latest,exit', 2
// use 'XX,rerun' to re-run the migration at that version
Migrations.migrateTo = function(command) {
	if (_.isUndefined(command) || command === '' || this._list.length === 0) { throw new Error(`Cannot migrate using invalid command: ${ command }`); }

	let version;
	let subcommands;
	if (typeof command === 'number') {
		version = command;
	} else {
		version = command.split(',')[0];
		subcommands = command.split(',').slice(1);
	}

	const { maxAttempts, retryInterval } = Migrations.options;
	let migrated;
	for (let attempts = 1; attempts <= maxAttempts; attempts++) {
		if (version === 'latest') {
			migrated = this._migrateTo(_.last(this._list).version);
		} else {
			migrated = this._migrateTo(parseInt(version), subcommands.includes('rerun'));
		}
		if (migrated) {
			break;
		} else {
			let willRetry;
			if (attempts < maxAttempts) {
				willRetry = ` Trying again in ${ retryInterval } seconds.`;
				Meteor._sleepForMs(retryInterval * 1000);
			} else {
				willRetry = '';
			}
			console.log(`Not migrating, control is locked. Attempt ${ attempts }/${ maxAttempts }.${ willRetry }`.yellow);
		}
	}
	if (!migrated) {
		const control = this._getControl(); // Side effect: upserts control document.
		console.log(makeABox([
			'ERROR! SERVER STOPPED',
			'',
			'Your database migration control is locked.',
			'Please make sure you are running the latest version and try again.',
			'If the problem persists, please contact support.',
			'',
			`This Rocket.Chat version: ${ Info.version }`,
			`Database locked at version: ${ control.version }`,
			`Database target version: ${ version === 'latest' ? _.last(this._list).version : version }`,
			'',
			`Commit: ${ Info.commit.hash }`,
			`Date: ${ Info.commit.date }`,
			`Branch: ${ Info.commit.branch }`,
			`Tag: ${ Info.commit.tag }`,
		]));
		process.exit(1);
	}

	// remember to run meteor with --once otherwise it will restart
	if (subcommands.includes('exit')) { process.exit(0); }
};

// just returns the current version
Migrations.getVersion = function() {
	return this._getControl().version;
};

// migrates to the specific version passed in
Migrations._migrateTo = function(version, rerun) {
	const self = this;
	const control = this._getControl(); // Side effect: upserts control document.
	let currentVersion = control.version;

	if (lock() === false) {
		// log.info('Not migrating, control is locked.');
		// Warning
		return false;
	}

	if (rerun) {
		log.info(`Rerunning version ${ version }`);
		migrate('up', this._findIndexByVersion(version));
		log.info('Finished migrating.');
		unlock();
		return true;
	}

	if (currentVersion === version) {
		if (this.options.logIfLatest) {
			log.info(`Not migrating, already at version ${ version }`);
		}
		unlock();
		return true;
	}

	const startIdx = this._findIndexByVersion(currentVersion);
	const endIdx = this._findIndexByVersion(version);

	// log.info('startIdx:' + startIdx + ' endIdx:' + endIdx);
	log.info(`Migrating from version ${ this._list[startIdx].version } -> ${ this._list[endIdx].version }`);

	// run the actual migration
	function migrate(direction, idx) {
		const migration = self._list[idx];

		if (typeof migration[direction] !== 'function') {
			unlock();
			throw new Meteor.Error(`Cannot migrate ${ direction } on version ${ migration.version }`);
		}

		function maybeName() {
			return migration.name ? ` (${ migration.name })` : '';
		}

		log.info(`Running ${ direction }() on version ${ migration.version }${ maybeName() }`);

		try {
			migration[direction](migration);
		} catch (e) {
			console.log(makeABox([
				'ERROR! SERVER STOPPED',
				'',
				'Your database migration failed:',
				e.message,
				'',
				'Please make sure you are running the latest version and try again.',
				'If the problem persists, please contact support.',
				'',
				`This Rocket.Chat version: ${ Info.version }`,
				`Database locked at version: ${ control.version }`,
				`Database target version: ${ version }`,
				'',
				`Commit: ${ Info.commit.hash }`,
				`Date: ${ Info.commit.date }`,
				`Branch: ${ Info.commit.branch }`,
				`Tag: ${ Info.commit.tag }`,
			]));
			console.log(e.stack);
			process.exit(1);
		}
	}

	// Returns true if lock was acquired.
	function lock() {
		const date = new Date();
		const dateMinusInterval = moment(date).subtract(self.options.lockExpiration, 'minutes').toDate();
		const build = Info ? Info.build.date : date;

		// This is atomic. The selector ensures only one caller at a time will see
		// the unlocked control, and locking occurs in the same update's modifier.
		// All other simultaneous callers will get false back from the update.
		return self._collection.update({
			_id: 'control',
			$or: [{
				locked: false,
			}, {
				lockedAt: {
					$lt: dateMinusInterval,
				},
			}, {
				buildAt: {
					$ne: build,
				},
			}],
		}, {
			$set: {
				locked: true,
				lockedAt: date,
				buildAt: build,
			},
		}) === 1;
	}

	// Side effect: saves version.
	function unlock() {
		self._setControl({
			locked: false,
			version: currentVersion,
		});
	}

	if (currentVersion < version) {
		for (let i = startIdx; i < endIdx; i++) {
			migrate('up', i + 1);
			currentVersion = self._list[i + 1].version;
			self._setControl({
				locked: true,
				version: currentVersion,
			});
		}
	} else {
		for (let i = startIdx; i > endIdx; i--) {
			migrate('down', i);
			currentVersion = self._list[i - 1].version;
			self._setControl({
				locked: true,
				version: currentVersion,
			});
		}
	}

	unlock();
	log.info('Finished migrating.');
};

// gets the current control record, optionally creating it if non-existant
Migrations._getControl = function() {
	const control = this._collection.findOne({
		_id: 'control',
	});

	return control || this._setControl({
		version: 0,
		locked: false,
	});
};

// sets the control record
Migrations._setControl = function(control) {
	// be quite strict
	check(control.version, Number);
	check(control.locked, Boolean);

	this._collection.update({
		_id: 'control',
	}, {
		$set: {
			version: control.version,
			locked: control.locked,
		},
	}, {
		upsert: true,
	});

	return control;
};

// returns the migration index in _list or throws if not found
Migrations._findIndexByVersion = function(version) {
	for (let i = 0; i < this._list.length; i++) {
		if (this._list[i].version === version) { return i; }
	}

	throw new Meteor.Error(`Can't find migration version ${ version }`);
};

// reset (mainly intended for tests)
Migrations._reset = function() {
	this._list = [{
		version: 0,
		up() {},
	}];
	this._collection.remove({});
};


import GraphemeSplitter from 'grapheme-splitter';

import { emoji } from '../../emoji';

const splitter = new GraphemeSplitter();

export const messageProperties = {

	length: (message) => splitter.countGraphemes(message),

	messageWithoutEmojiShortnames: (message) => message.replace(/:\w+:/gm, (match) => {
		if (emoji.list[match] !== undefined) {
			return ' ';
		}
		return match;
	}),
};


// Jest Snapshot v1, https://goo.gl/fbAQLP

exports[`VDatePickerMonthTable.ts should render component and match snapshot (multiple) 1`] = `

          <button type="button"
                  class="v-btn v-size--default v-btn--text theme--light"
          >

              Jan

          </button>

          <button type="button"
                  class="v-btn v-size--default v-btn--text theme--light"
          >

              Feb

          </button>

          <button type="button"
                  class="v-btn v-size--default v-btn--text theme--light"
          >

              Mar

          </button>

          <button type="button"
                  class="v-btn v-size--default v-btn--text theme--light"
          >

              Apr

          </button>

          <button type="button"
                  class="v-btn v-size--default v-btn--outlined theme--light accent--text"
          >

              May

          </button>

          <button type="button"
                  class="v-btn v-size--default v-btn--text theme--light"
          >

              Jun

          </button>

          <button type="button"
                  class="v-btn v-size--default v-btn--text theme--light"
          >

              Jul

          </button>

          <button type="button"
                  class="v-btn v-size--default v-btn--text theme--light"
          >

              Aug

          </button>

          <button type="button"
                  class="v-btn v-size--default v-btn--text theme--light"
          >

              Sep

          </button>

          <button type="button"
                  class="v-btn v-size--default v-btn--active theme--light accent"
          >

              Oct

          </button>

          <button type="button"
                  class="v-btn v-size--default v-btn--active theme--light accent"
          >

              Nov

          </button>

          <button type="button"
                  class="v-btn v-size--default v-btn--text theme--light"
          >

              Dec

          </button>

`;

exports[`VDatePickerMonthTable.ts should render component and match snapshot 1`] = `

          <button type="button"
                  class="v-btn v-size--default v-btn--text theme--light"
          >

              Jan

          </button>

          <button type="button"
                  class="v-btn v-size--default v-btn--text theme--light"
          >

              Feb

          </button>

          <button type="button"
                  class="v-btn v-size--default v-btn--text theme--light"
          >

              Mar

          </button>

          <button type="button"
                  class="v-btn v-size--default v-btn--text theme--light"
          >

              Apr

          </button>

          <button type="button"
                  class="v-btn v-size--default v-btn--outlined theme--light accent--text"
          >

              May

          </button>

          <button type="button"
                  class="v-btn v-size--default v-btn--text theme--light"
          >

              Jun

          </button>

          <button type="button"
                  class="v-btn v-size--default v-btn--text theme--light"
          >

              Jul

          </button>

          <button type="button"
                  class="v-btn v-size--default v-btn--text theme--light"
          >

              Aug

          </button>

          <button type="button"
                  class="v-btn v-size--default v-btn--text theme--light"
          >

              Sep

          </button>

          <button type="button"
                  class="v-btn v-size--default v-btn--text theme--light"
          >

              Oct

          </button>

          <button type="button"
                  class="v-btn v-size--default v-btn--active theme--light accent"
          >

              Nov

          </button>

          <button type="button"
                  class="v-btn v-size--default v-btn--text theme--light"
          >

              Dec

          </button>

`;

exports[`VDatePickerMonthTable.ts should render component with events (array) and match snapshot 1`] = `

          <button type="button"
                  class="v-btn v-size--default v-btn--text theme--light"
          >

              Jan

          </button>

          <button type="button"
                  class="v-btn v-size--default v-btn--text theme--light"
          >

              Feb

          </button>

          <button type="button"
                  class="v-btn v-size--default v-btn--text theme--light"
          >

              Mar

          </button>

          <button type="button"
                  class="v-btn v-size--default v-btn--text theme--light"
          >

              Apr

          </button>

          <button type="button"
                  class="v-btn v-size--default v-btn--text theme--light"
          >

              May

          </button>

          <button type="button"
                  class="v-btn v-size--default v-btn--text theme--light"
          >

              Jun

          </button>

          <button type="button"
                  class="v-btn v-size--default v-btn--text theme--light"
          >

              Jul

          </button>

          <button type="button"
                  class="v-btn v-size--default v-btn--text theme--light"
          >

              Aug

          </button>

          <button type="button"
                  class="v-btn v-size--default v-btn--text theme--light"
          >

              Sep

          </button>

          <button type="button"
                  class="v-btn v-size--default v-btn--text theme--light"
          >

              Oct

          </button>

          <button type="button"
                  class="v-btn v-size--default v-btn--text theme--light"
          >

              Nov

          </button>

          <button type="button"
                  class="v-btn v-size--default v-btn--text theme--light"
          >

              Dec

          </button>

`;

exports[`VDatePickerMonthTable.ts should render component with events (function) and match snapshot 1`] = `

          <button type="button"
                  class="v-btn v-size--default v-btn--text theme--light"
          >

              Jan

          </button>

          <button type="button"
                  class="v-btn v-size--default v-btn--text theme--light"
          >

              Feb

          </button>

          <button type="button"
                  class="v-btn v-size--default v-btn--text theme--light"
          >

              Mar

          </button>

          <button type="button"
                  class="v-btn v-size--default v-btn--text theme--light"
          >

              Apr

          </button>

          <button type="button"
                  class="v-btn v-size--default v-btn--text theme--light"
          >

              May

          </button>

          <button type="button"
                  class="v-btn v-size--default v-btn--text theme--light"
          >

              Jun

          </button>

          <button type="button"
                  class="v-btn v-size--default v-btn--text theme--light"
          >

              Jul

          </button>

          <button type="button"
                  class="v-btn v-size--default v-btn--text theme--light"
          >

              Aug

          </button>

          <button type="button"
                  class="v-btn v-size--default v-btn--text theme--light"
          >

              Sep

          </button>

          <button type="button"
                  class="v-btn v-size--default v-btn--text theme--light"
          >

              Oct

          </button>

          <button type="button"
                  class="v-btn v-size--default v-btn--text theme--light"
          >

              Nov

          </button>

          <button type="button"
                  class="v-btn v-size--default v-btn--text theme--light"
          >

              Dec

          </button>

`;

exports[`VDatePickerMonthTable.ts should render component with events colored by function and match snapshot 1`] = `

          <button type="button"
                  class="v-btn v-size--default v-btn--text theme--light"
          >

              Jan

          </button>

          <button type="button"
                  class="v-btn v-size--default v-btn--text theme--light"
          >

              Feb

          </button>

          <button type="button"
                  class="v-btn v-size--default v-btn--text theme--light"
          >

              Mar

          </button>

          <button type="button"
                  class="v-btn v-size--default v-btn--text theme--light"
          >

              Apr

          </button>

          <button type="button"
                  class="v-btn v-size--default v-btn--text theme--light"
          >

              May

          </button>

          <button type="button"
                  class="v-btn v-size--default v-btn--text theme--light"
          >

              Jun

          </button>

          <button type="button"
                  class="v-btn v-size--default v-btn--text theme--light"
          >

              Jul

          </button>

          <button type="button"
                  class="v-btn v-size--default v-btn--text theme--light"
          >

              Aug

          </button>

          <button type="button"
                  class="v-btn v-size--default v-btn--text theme--light"
          >

              Sep

          </button>

          <button type="button"
                  class="v-btn v-size--default v-btn--text theme--light"
          >

              Oct

          </button>

          <button type="button"
                  class="v-btn v-size--default v-btn--text theme--light"
          >

              Nov

          </button>

          <button type="button"
                  class="v-btn v-size--default v-btn--text theme--light"
          >

              Dec

          </button>

`;

exports[`VDatePickerMonthTable.ts should render component with events colored by object and match snapshot 1`] = `

          <button type="button"
                  class="v-btn v-size--default v-btn--text theme--light"
          >

              Jan

          </button>

          <button type="button"
                  class="v-btn v-size--default v-btn--text theme--light"
          >

              Feb

          </button>

          <button type="button"
                  class="v-btn v-size--default v-btn--text theme--light"
          >

              Mar

          </button>

          <button type="button"
                  class="v-btn v-size--default v-btn--text theme--light"
          >

              Apr

          </button>

          <button type="button"
                  class="v-btn v-size--default v-btn--text theme--light"
          >

              May

          </button>

          <button type="button"
                  class="v-btn v-size--default v-btn--text theme--light"
          >

              Jun

          </button>

          <button type="button"
                  class="v-btn v-size--default v-btn--text theme--light"
          >

              Jul

          </button>

          <button type="button"
                  class="v-btn v-size--default v-btn--text theme--light"
          >

              Aug

          </button>

          <button type="button"
                  class="v-btn v-size--default v-btn--text theme--light"
          >

              Sep

          </button>

          <button type="button"
                  class="v-btn v-size--default v-btn--text theme--light"
          >

              Oct

          </button>

          <button type="button"
                  class="v-btn v-size--default v-btn--text theme--light"
          >

              Nov

          </button>

          <button type="button"
                  class="v-btn v-size--default v-btn--text theme--light"
          >

              Dec

          </button>

`;


module.exports = {
  'v-navigation-drawer': {
    slots: [
      {
        name: 'append',
        props: undefined,
      },
      {
        name: 'img',
        props: { height: 'string', src: 'string | srcObject' },
      },
      {
        name: 'prepend',
        props: undefined,
      },
    ],
    events: [
      {
        name: 'input',
        value: 'boolean',
      },
      {
        name: 'transitionend',
        value: 'object',
      },
      {
        name: 'update:mini-variant',
        value: 'boolean',
      },
    ],
  },
}


<template>
  <v-row align="center" justify="center">
    <v-card height="300" width="250">
      <v-row justify="center">
        <v-btn
          color="orange lighten-2"
          class="mt-12"
          @click="overlay = !overlay"
        >
          Show Overlay
        </v-btn>

        <v-overlay
          :absolute="absolute"
          :opacity="opacity"
          :value="overlay"
        >
          <v-btn
            color="orange lighten-2"
            @click="overlay = false"
          >
            Hide Overlay
          </v-btn>
        </v-overlay>
      </v-row>
    </v-card>
  </v-row>
</template>

<script>
  export default {
    data: () => ({
      absolute: true,
      opacity: 1,
      overlay: false,
    }),
  }
</script>


class="v-btn v-size--default v-btn--outlined theme--light accent--text"

class="v-btn v-size--default v-date-picker-table__current v-btn--outlined theme--light accent--text"


import Vue from 'vue/dist/vue.common.js'
import {
  deepEqual,
  getNestedValue,
  getPropertyFromItem,
  convertToUnit,
  getSlotType,
  arrayDiff,
  getObjectValueByPath,
  humanReadableFileSize,
  sortItems,
} from '../helpers'

describe('helpers', () => {
  it('should return set difference of arrays A and B', () => {
    expect(arrayDiff(['one', 'two'], ['one'])).toEqual([])
    expect(arrayDiff(['one'], ['one', 'two'])).toEqual(['two'])
    expect(arrayDiff([], [])).toEqual([])
    expect(arrayDiff([], ['one'])).toEqual(['one'])
    expect(arrayDiff(['one'], ['two'])).toEqual(['two'])
    expect(arrayDiff(['one', 'two'], ['one', 'three'])).toEqual(['three'])
  })

  it('should pass comparison', () => { // eslint-disable-line max-statements
    // Null
    expect(deepEqual(null, null)).toEqual(true)
    expect(deepEqual(null, undefined)).toEqual(false)
    expect(deepEqual(null, false)).toEqual(false)
    expect(deepEqual(null, 0)).toEqual(false)
    expect(deepEqual(null, '')).toEqual(false)
    expect(deepEqual(null, [])).toEqual(false)
    expect(deepEqual(null, {})).toEqual(false)

    // Undefined
    expect(deepEqual(undefined, undefined)).toEqual(true)
    expect(deepEqual(undefined, null)).toEqual(false)
    expect(deepEqual(undefined, false)).toEqual(false)
    expect(deepEqual(undefined, 0)).toEqual(false)
    expect(deepEqual(undefined, '')).toEqual(false)
    expect(deepEqual(undefined, [])).toEqual(false)
    expect(deepEqual(undefined, {})).toEqual(false)

    // Boolean
    expect(deepEqual(true, true)).toEqual(true)
    expect(deepEqual(true, false)).toEqual(false)
    expect(deepEqual(true, undefined)).toEqual(false)
    expect(deepEqual(true, null)).toEqual(false)
    expect(deepEqual(true, 0)).toEqual(false)
    expect(deepEqual(true, 1)).toEqual(false)
    expect(deepEqual(true, '')).toEqual(false)
    expect(deepEqual(true, 'abc')).toEqual(false)
    expect(deepEqual(true, [1, 2])).toEqual(false)
    expect(deepEqual(true, { x: 1 })).toEqual(false)

    expect(deepEqual(false, false)).toEqual(true)
    expect(deepEqual(false, true)).toEqual(false)
    expect(deepEqual(false, undefined)).toEqual(false)
    expect(deepEqual(false, null)).toEqual(false)
    expect(deepEqual(false, 0)).toEqual(false)
    expect(deepEqual(false, 1)).toEqual(false)
    expect(deepEqual(false, '')).toEqual(false)
    expect(deepEqual(false, 'abc')).toEqual(false)
    expect(deepEqual(false, [1, 2])).toEqual(false)
    expect(deepEqual(false, { x: 1 })).toEqual(false)

    // Number
    expect(deepEqual(5, 5)).toEqual(true)
    expect(deepEqual(8, 8.0)).toEqual(true)
    expect(deepEqual(8, '8')).toEqual(false)
    expect(deepEqual(-10, -10)).toEqual(true)

    expect(deepEqual(0, '')).toEqual(false)
    expect(deepEqual(0, false)).toEqual(false)
    expect(deepEqual(0, null)).toEqual(false)
    expect(deepEqual(0, undefined)).toEqual(false)

    // String
    expect(deepEqual('', '')).toEqual(true)
    expect(deepEqual('a', 'a')).toEqual(true)
    expect(deepEqual('a', 'b')).toEqual(false)
    expect(deepEqual('a', 'A')).toEqual(false)
    expect(deepEqual('abc', 'abc')).toEqual(true)
    expect(deepEqual('Abc', 'abc')).toEqual(false)
    expect(deepEqual(' ', '')).toEqual(false)

    // Array
    expect(deepEqual([], [])).toEqual(true)
    expect(deepEqual([1], [1.0])).toEqual(true)
    expect(deepEqual([1, '2'], [1, '2'])).toEqual(true)
    expect(deepEqual([1, { x: 1, y: 2 }], [1, { x: 1, y: 2 }])).toEqual(true)
    expect(deepEqual([1, { x: 1, y: null }], [1, { x: 1, y: false }])).toEqual(false)
    expect(deepEqual([1, [1, 2]], [1, [1, 2]])).toEqual(true)

    // Object
    expect(deepEqual({}, {})).toEqual(true)
    expect(deepEqual({ x: 1 }, { x: 1 })).toEqual(true)
    expect(deepEqual({ x: 1 }, {})).toEqual(false)
    expect(deepEqual({ x: { a: 1, b: 2 } }, { x: { a: 1, b: 2 } })).toEqual(true)

    // Date
    const currentDate = new Date()
    const futureDate = new Date(1000)

    expect(deepEqual(currentDate, currentDate)).toEqual(true)
    expect(deepEqual({ date: currentDate }, { date: currentDate })).toEqual(true)
    expect(deepEqual(currentDate, futureDate)).toEqual(false)
    expect(deepEqual({ date: currentDate }, { date: futureDate })).toEqual(false)

    const circular = {}
    circular.me = circular

    expect(deepEqual({ r: circular }, { r: circular })).toEqual(true)
    expect(deepEqual({ r: circular, x: 1 }, { r: circular, x: 2 })).toEqual(false)
    expect(deepEqual({ r: [circular] }, { r: [circular] })).toEqual(true)
  })

  it('should get value directly on object if not undefined', () => {
    const obj = {
      a: 'foo',
      'b.a': 'foobar',
      b: {
        a: 1,
      },
      'c.d': undefined,
      c: {
        d: 'bar',
      },
    }

    expect(getObjectValueByPath(obj, 'a')).toEqual('foo')
    expect(getObjectValueByPath(obj, 'b.a')).toEqual('foobar')
    expect(getObjectValueByPath(obj, 'c.d')).toEqual('bar')
  })

  it('should get nested value', () => {
    const obj = {
      a: {
        b: {
          c: 1,
          d: 2,
        },
        e: [
          { f: 'f' },
          'e1',
        ],
      },
      g: null,
    }

    expect(getNestedValue(obj, ['a', 'b', 'c'])).toEqual(1)
    expect(getNestedValue(obj, ['a', 'b', 'd'])).toEqual(2)
    expect(getNestedValue(obj, ['a', 'b'])).toEqual({ c: 1, d: 2 })
    expect(getNestedValue(obj, ['a', 'e', '0', 'f'])).toEqual('f')
    expect(getNestedValue(obj, ['a', 'e', 0, 'f'])).toEqual('f')
    expect(getNestedValue(obj, ['a', 'e', '1'])).toEqual('e1')
    expect(getNestedValue(obj, ['g'])).toBeNull()
    expect(getNestedValue(obj, ['missing', 'key'])).toBeUndefined()

    const arr = ['val', obj]

    expect(getNestedValue(arr, ['1', 'a', 'b', 'c'])).toEqual(1)
    expect(getNestedValue(arr, ['1', 'a', 'e', 0, 'f'])).toEqual('f')
    expect(getNestedValue(arr, [0])).toEqual('val')
    expect(getNestedValue(arr, [1])).toEqual(obj)

    expect(getNestedValue('str', [])).toEqual('str')
    expect(getNestedValue(5, [])).toEqual(5)
    expect(getNestedValue(null, [])).toBeNull()

    expect(getNestedValue(null, ['a'])).toBeUndefined()
  })

  it('should get property from items', () => {
    const obj = {
      a: {
        b: 1,
      },
      c: [2, 3, { d: 'd' }],
      'x.y': 'comp',
      x: {
        y: 'nested',
      },
    }
    expect(getPropertyFromItem(obj, 'a.b')).toEqual(1)
    expect(getPropertyFromItem(obj, 'c.0')).toEqual(2)
    expect(getPropertyFromItem(obj, 'c.2.d')).toEqual('d')
    expect(getPropertyFromItem(obj, 'c.2.d.x', 'fallback')).toEqual('fallback')
    expect(getPropertyFromItem(obj, o => o.a.b + o.c[0])).toEqual(3)
    expect(getPropertyFromItem(obj, ['c', 2, 'd'])).toEqual('d')
    expect(getPropertyFromItem(obj, 'x.y')).toEqual('comp')
    expect(getPropertyFromItem(obj, ['x', 'y'])).toEqual('nested')
    expect(getPropertyFromItem(obj, ['x.y'])).toEqual('comp')
  })

  it('should return proper value in convertToUnit', () => {
    expect(convertToUnit(undefined)).toBeUndefined()
    expect(convertToUnit(null)).toBeUndefined()
    expect(convertToUnit('')).toBeUndefined()

    expect(convertToUnit(0)).toBe('0px')
    expect(convertToUnit(3)).toBe('3px')
    expect(convertToUnit(3.14)).toBe('3.14px')

    expect(convertToUnit(0, 'em')).toBe('0em')
    expect(convertToUnit(3, 'em')).toBe('3em')
    expect(convertToUnit(3.14, 'em')).toBe('3.14em')

    expect(convertToUnit('0vw')).toBe('0vw')
    expect(convertToUnit('3vw')).toBe('3vw')
    expect(convertToUnit('3.14vw')).toBe('3.14vw')

    expect(convertToUnit('foo')).toBe('foo')
  })

  describe('getSlotType', () => {
    it('should detect old slots', () => {
      const vm = new Vue({
        components: {
          foo: { render: h => h('div') },
        },
        template: `<foo ref="foo"><template slot="bar">hello</template></foo>`,
      }).$mount()

      expect(getSlotType(vm.$refs.foo, 'bar')).toBe('normal')
    })

    it('should detect old scoped slots', () => {
      const vm = new Vue({
        components: {
          foo: { render: h => h('div') },
        },
        template: `<foo ref="foo"><template slot="bar" slot-scope="data">hello</template></foo>`,
      }).$mount()

      expect(getSlotType(vm.$refs.foo, 'bar')).toBe('scoped')
    })

    it('should detect bare v-slot', () => {
      const vm = new Vue({
        components: {
          foo: { render: h => h('div') },
        },
        template: `<foo ref="foo"><template #bar>hello</template></foo>`,
      }).$mount()

      expect(getSlotType(vm.$refs.foo, 'bar', true)).toBe('v-slot')
    })

    it('should detect bound v-slot', () => {
      const vm = new Vue({
        components: {
          foo: { render: h => h('div') },
        },
        template: `<foo ref="foo"><template #bar="data">hello</template></foo>`,
      }).$mount()

      expect(getSlotType(vm.$refs.foo, 'bar', true)).toBe('scoped')
    })

    it('should count bare v-slot as scoped', () => {
      const vm = new Vue({
        components: {
          foo: { render: h => h('div') },
        },
        template: `<foo ref="foo"><template #bar>hello</template></foo>`,
      }).$mount()

      expect(getSlotType(vm.$refs.foo, 'bar')).toBe('scoped')
    })
  })

  it('humanReadableFileSize should format file sizes with base 1024', () => {
    expect(humanReadableFileSize(0, true)).toBe('0 B')
    expect(humanReadableFileSize(512, true)).toBe('512 B')

    expect(humanReadableFileSize(1024, true)).toBe('1.0 KiB')
    expect(humanReadableFileSize(4096, true)).toBe('4.0 KiB')

    expect(humanReadableFileSize(1048576, true)).toBe('1.0 MiB')
    expect(humanReadableFileSize(2097152, true)).toBe('2.0 MiB')

    expect(humanReadableFileSize(1073741824, true)).toBe('1.0 GiB')
    expect(humanReadableFileSize(2147483648, true)).toBe('2.0 GiB')
  })

  it('humanReadableFileSize should format file sizes with base 1000', () => {
    expect(humanReadableFileSize(0)).toBe('0 B')
    expect(humanReadableFileSize(512)).toBe('512 B')

    expect(humanReadableFileSize(1000)).toBe('1.0 kB')
    expect(humanReadableFileSize(4000)).toBe('4.0 kB')

    expect(humanReadableFileSize(1000000)).toBe('1.0 MB')
    expect(humanReadableFileSize(2000000)).toBe('2.0 MB')

    expect(humanReadableFileSize(1000000000)).toBe('1.0 GB')
    expect(humanReadableFileSize(2000000000)).toBe('2.0 GB')
  })

  it('should sort items by single column', () => {
    let items
    const getItems = () => [{ string: 'foo', number: 1 }, { string: 'bar', number: 2 }, { string: 'baz', number: 4 }, { string: 'fizzbuzz', number: 3 }]

    sortItems(items = getItems(), ['string'], [], 'en')
    expect(items).toStrictEqual([{ string: 'bar', number: 2 }, { string: 'baz', number: 4 }, { string: 'fizzbuzz', number: 3 }, { string: 'foo', number: 1 }])

    sortItems(items = getItems(), ['string'], [true], 'en')
    expect(items).toStrictEqual([{ string: 'foo', number: 1 }, { string: 'fizzbuzz', number: 3 }, { string: 'baz', number: 4 }, { string: 'bar', number: 2 }])

    sortItems(items = getItems(), ['number'], [], 'en')
    expect(items).toStrictEqual([{ string: 'foo', number: 1 }, { string: 'bar', number: 2 }, { string: 'fizzbuzz', number: 3 }, { string: 'baz', number: 4 }])

    sortItems(items = getItems(), ['number'], [true], 'en')
    expect(items).toStrictEqual([{ string: 'baz', number: 4 }, { string: 'fizzbuzz', number: 3 }, { string: 'bar', number: 2 }, { string: 'foo', number: 1 }])

    sortItems(items = getItems(), ['number'], [], 'en', { number: (a, b) => b - a })
    expect(items).toStrictEqual([{ string: 'baz', number: 4 }, { string: 'fizzbuzz', number: 3 }, { string: 'bar', number: 2 }, { string: 'foo', number: 1 }])

    sortItems(items = getItems(), ['number'], [true], 'en', { number: (a, b) => b - a })
    expect(items).toStrictEqual([{ string: 'foo', number: 1 }, { string: 'bar', number: 2 }, { string: 'fizzbuzz', number: 3 }, { string: 'baz', number: 4 }])
  })

  it('should sort items with deep structure', () => {
    const items = [{ foo: { bar: { baz: 3 } } }, { foo: { bar: { baz: 1 } } }, { foo: { bar: { baz: 2 } } }]

    sortItems(items, ['foo.bar.baz'], [], 'en')
    expect(items).toStrictEqual([{ foo: { bar: { baz: 1 } } }, { foo: { bar: { baz: 2 } } }, { foo: { bar: { baz: 3 } } }])
  })

  it('should sort items by multiple columns', () => {
    let items
    const getItems = () => [{ string: 'foo', number: 1 }, { string: 'bar', number: 3 }, { string: 'baz', number: 2 }, { string: 'baz', number: 1 }]

    sortItems(items = getItems(), ['string', 'number'], [], 'en')
    expect(items).toStrictEqual([{ string: 'bar', number: 3 }, { string: 'baz', number: 1 }, { string: 'baz', number: 2 }, { string: 'foo', number: 1 }])

    sortItems(items = getItems(), ['string', 'number'], [true, false], 'en')
    expect(items).toStrictEqual([{ string: 'foo', number: 1 }, { string: 'baz', number: 1 }, { string: 'baz', number: 2 }, { string: 'bar', number: 3 }])

    sortItems(items = getItems(), ['string', 'number'], [false, true], 'en')
    expect(items).toStrictEqual([{ string: 'bar', number: 3 }, { string: 'baz', number: 2 }, { string: 'baz', number: 1 }, { string: 'foo', number: 1 }])

    sortItems(items = getItems(), ['string', 'number'], [true, true], 'en')
    expect(items).toStrictEqual([{ string: 'foo', number: 1 }, { string: 'baz', number: 2 }, { string: 'baz', number: 1 }, { string: 'bar', number: 3 }])

    sortItems(items = getItems(), ['number', 'string'], [], 'en')
    expect(items).toStrictEqual([{ string: 'baz', number: 1 }, { string: 'foo', number: 1 }, { string: 'baz', number: 2 }, { string: 'bar', number: 3 }])

    sortItems(items = getItems(), ['number', 'string'], [true, false], 'en')
    expect(items).toStrictEqual([{ string: 'bar', number: 3 }, { string: 'baz', number: 2 }, { string: 'baz', number: 1 }, { string: 'foo', number: 1 }])

    sortItems(items = getItems(), ['number', 'string'], [false, true], 'en')
    expect(items).toStrictEqual([{ string: 'foo', number: 1 }, { string: 'baz', number: 1 }, { string: 'baz', number: 2 }, { string: 'bar', number: 3 }])

    sortItems(items = getItems(), ['number', 'string'], [true, true], 'en')
    expect(items).toStrictEqual([{ string: 'bar', number: 3 }, { string: 'baz', number: 2 }, { string: 'foo', number: 1 }, { string: 'baz', number: 1 }])

    sortItems(items = getItems(), ['string', 'number'], [], 'en', { number: (a, b) => b - a })
    expect(items).toStrictEqual([{ string: 'bar', number: 3 }, { string: 'baz', number: 2 }, { string: 'baz', number: 1 }, { string: 'foo', number: 1 }])

    sortItems(items = getItems(), ['number', 'string'], [], 'en', { number: (a, b) => b - a })
    expect(items).toStrictEqual([{ string: 'bar', number: 3 }, { string: 'baz', number: 2 }, { string: 'baz', number: 1 }, { string: 'foo', number: 1 }])
  })
})


<template>
  <div class="text-center">
    <v-chip
      class="ma-2"
      color="indigo"
      text-color="white"
    >
      <v-avatar left>
        <v-icon>mdi-account-circle</v-icon>
      </v-avatar>
      Ranee
    </v-chip>

    <v-chip
      class="ma-2"
      color="orange"
      text-color="white"
    >
      Premium
      <v-icon right>mdi-star</v-icon>
    </v-chip>

    <v-chip
      class="ma-2"
      color="primary"
      text-color="white"
    >
      1 Year
      <v-icon right>mdi-cake-variant</v-icon>
    </v-chip>

    <v-chip
      class="ma-2"
      color="green"
      text-color="white"
    >
      <v-avatar
        left
        class="green darken-4"
      >
        1
      </v-avatar>
      Years
    </v-chip>

    <v-chip
      class="ma-2"
      close
      color="teal"
      text-color="white"
      @click:close="close"
    >
      <v-avatar left>
        <v-icon>mdi-checkbox-marked-circle</v-icon>
      </v-avatar>
      Confirmed
    </v-chip>

    <v-chip
      class="ma-2"
      close
      color="teal"
      text-color="white"
      close-icon="mdi-delete"
      @click:close="close"
    >
      <v-avatar left>
        <v-icon>mdi-checkbox-marked-circle</v-icon>
      </v-avatar>
      Confirmed
    </v-chip>
  </div>
</template>

<script>
  export default {
    methods: {
      close () {
        alert('Chip close clicked')
      },
    },
  }
</script>


keep keep keep keep keep replace keep keep keep


<script>
  // Utilities
  import { getComponent } from '@/util/helpers'

  export default {
    functional: true,

    props: {
      children: {
        type: Array,
        default: () => ([]),
      },
    },

    render (h, { props }) {
      return props.children.map(child => {
        if (child.children) {
          return h('doc-section', {
            props: { value: child },
          })
        }

        return h(getComponent(child.type), {
          props: {
            lang: child.lang,
            value: child.value,
          },
        }, child.lang)
      })
    },
  }
</script>


genButtonClasses (isAllowed: boolean, isFloating: boolean, isSelected: boolean, isCurrent: boolean) {

'v-btn--flat': !isAllowed || this.disabled,

'v-btn--text': isSelected === isCurrent,

keep keep keep keep add keep keep keep keep

@import './_variables.scss'

+theme(v-date-picker-table) using ($material)
  th,
  .v-date-picker-table--date__week
    color: map-deep-get($material, 'text', 'disabled')

.v-date-picker-table
  position: relative
  padding: $date-picker-table-padding
  height: $date-picker-table-height

  table
    transition: $primary-transition
    top: 0
    table-layout: fixed
    width: 100%

  td, th
    text-align: center
    position: relative

  th
    font-size: $date-picker-table-font-size

  &--date .v-btn
    height: $date-picker-table-date-button-height
    width: $date-picker-table-date-button-width

  .v-btn
    z-index: auto
    margin: 0
    font-size: $date-picker-table-font-size

    &.v-btn--active
      color: $date-picker-table-active-date-color

.v-date-picker-table--month
  td
    width: 33.333333%
    height: $date-picker-table-month-height
    vertical-align: middle
    text-align: center

    .v-btn
      margin: 0 auto
      max-width: $date-picker-table-month-max-width
      min-width: $date-picker-table-month-min-width
      width: 100%

.v-date-picker-table--date
  th
    padding: $date-picker-table-date-padding
    font-weight: $date-picker-table-date-font-weight

  td
    width: $date-picker-table-date-width

.v-date-picker-table__events
  height: $date-picker-event-size
  left: 0
  position: absolute
  text-align: center
  white-space: pre
  width: 100%

  > div
    border-radius: $date-picker-event-border-radius
    display: inline-block
    height: $date-picker-event-size
    margin: $date-picker-event-margin
    width: $date-picker-event-size

.v-date-picker-table--date .v-date-picker-table__events
  bottom: $date-picker-event-month-bottom

.v-date-picker-table--month .v-date-picker-table__events
  bottom: $date-picker-event-date-bottom

.v-date-picker-table--disabled
  pointer-events: none


# バッジ

`v-badge`コンポーネントは、アバターのようなアイコンまたはテキストに上付きまたは下付きでコンテンツを表示し、情報を強調したり、特定の要素に注意を向けたりします。バッジ内のコンテンツには通常、数字またはアイコンが含まれています。

表示

Badge はテキスト内部に配置することができます。

アイコン

現在サポートされているアイコンセットのすべてを使用することができます。


const { join, dirname, relative } = require('path')
const wrapListener = require('babel-plugin-detective/wrap-listener')

module.exports = wrapListener(listener, 'transform-sass-paths')

function listener(path, file) {
  if (path.isLiteral() && (path.node.value.endsWith('.sass') || path.node.value.endsWith('.scss'))) {
    const from = dirname(relative(file.opts.cwd, file.opts.filename))
    const to = from.replace(from.split('/')[0], 'src')
    path.node.value = join(relative(from, to), path.node.value)
  }
}


<template>
  <v-card
    class="pa-4"
    flat
    height="300px"
    img="https://cdn.vuetifyjs.com/images/toolbar/map.jpg"
  >
    <v-toolbar
      dense
      floating
    >
      <v-text-field
        hide-details
        prepend-icon="search"
        single-line
      ></v-text-field>

      <v-btn icon>
        <v-icon>my_location</v-icon>
      </v-btn>

      <v-btn icon>
        <v-icon>mdi-dots-vertical</v-icon>
      </v-btn>
    </v-toolbar>
  </v-card>
</template>


import { BaseWindowItem } from '../VWindow/VWindowItem'

import VWindowItem from '../VWindow/VWindowItem'

// Extensions
import { BaseWindow } from '../VWindow/VWindow'

/* @vue/component */
export default {
  name: 'v-tabs-items',

  extends: BaseWindow,

  provide () {
    return {
      windowGroup: this
    }
  },

  inject: {
    registerItems: {
      default: null
    },
    tabProxy: {
      default: null
    },
    unregisterItems: {
      default: null
    }
  },

  props: {
    cycle: Boolean
  },

  watch: {
    internalValue (val) {
      /* istanbul ignore else */
      if (this.tabProxy) this.tabProxy(val)
    }
  },

  created () {
    this.registerItems && this.registerItems(this.changeModel)
  },

  beforeDestroy () {
    this.unregisterItems && this.unregisterItems()
  },

  methods: {
    changeModel (val) {
      this.internalValue = val
    },
    // For backwards compatability with v1.2
    getValue (item, i) {
      if (item.id) return item.id

      return BaseWindow.options.methods.getValue.call(this, item, i)
    },
    next () {
      if (!this.cycle && this.internalIndex === this.items.length - 1) {
        return
      }

      BaseWindow.options.methods.next.call(this)
    },
    prev () {
      if (!this.cycle && this.internalIndex === 0) {
        return
      }

      BaseWindow.options.methods.prev.call(this)
    }
  }
}


/* @vue/component */
export default {
  methods: {
    genTProgress () {
      const col = this.$createElement('th', {
        staticClass: 'column',
        attrs: {
          colspan: this.headerColumns
        }
      }, [this.genProgress()])

      return this.genTR([col], {
        staticClass: 'v-datatable__progress'
      })
    }
  }
}


@import '../bootstrap'
@import '../theme'

/* Theme */
v-slider($material)
  .v-slider__track,
  .v-slider__track-fill
    background: $material.slider.inactive

  .v-slider__track__container
    &:after
      border: 1px solid $material.text.primary

  .v-slider__ticks
    border-color: $material.text.primary
    color: $material.text.secondary

  &:not(.v-input--is-dirty)
    .v-slider__thumb-label
      background: $material.slider.inactive

    .v-slider__thumb
      border: 3px solid $material.slider.inactive

    &.v-input--slider--is-active
      .v-slider__thumb
        border: 3px solid $material.slider.active

  &.v-input--is-disabled
    .v-slider__thumb
      border: 5px solid $material.slider.disabled

  &.v-input--is-disabled.v-input--is-dirty
    .v-slider__thumb
      background: $material.slider.disabled

  &.v-input--slider--is-active
    .v-slider__track
      background: $material.slider.active

v-slider-rtl()
  .v-label
    margin-left: 16px
    margin-right: 0

theme(v-slider, "v-input--slider")
rtl(v-slider-rtl, "v-input--slider")

/** Input Group */
.v-input--slider
  margin-top: $input-top-spacing

  &.v-input--is-focused
    .v-slider__thumb-container--is-active:not(.v-slider__thumb-container--show-label)
      &:before
        opacity: .2
        transform: scale(1)

    .v-slider__track
      transition: none

    &.v-input--slider--ticks
      .v-slider
        &__track__container:after,
        .v-slider__tick
          opacity: 1

  &.v-input--slider--ticks
    .v-slider__ticks.v-slider__ticks--always-show
      opacity: 1

    &-labels
      .v-input__slot
        margin-bottom: 16px

  &.v-input--is-readonly
    pointer-events: none

  &.v-input--is-disabled
    .v-slider__thumb
      transform: translateY(-50%) scale(.45)
    &.v-input--is-dirty
      .v-slider__thumb
        border: 0px solid transparent

  .v-input__slot > *:first-child:not(:only-child)
    margin-right: 16px

/** Slider */
.v-slider
  cursor: default
  display: flex
  align-items: center
  position: relative
  height: 32px
  flex: 1
  outline: none
  user-select: none

  input
    cursor: default
    opacity: 0
    padding: 0
    width: 100%

/** Thumb/Track/Ticks */
.v-slider
  &__track__container
    height: 2px
    left: 0
    overflow: hidden
    pointer-events: none
    position: absolute
    top: 50%
    transform: translateY(-50%)
    width: 100%

    &:after
      content: ''
      position: absolute
      right: 0
      top: 0
      height: 2px
      transition: .3s $transition.swing
      width: 2px
      opacity: 0

  &__track,
  &__thumb,
  &__ticks
    position: absolute
    top: 0

  &__track
    height: 2px
    left: 0
    transition: .3s $transition.swing
    transform-origin: right
    overflow: hidden
    width: 100%

    &-fill
      position: absolute
      left: 0
      height: 2px
      transform-origin: left
      width: 100%
      transition: .3s $transition.swing

  &__ticks-container
    position: absolute
    left: 0
    height: 2px
    width: 100%
    top: 50%
    transform: translate(0, -50%)

  &__ticks
    opacity: 0
    border-style: solid
    transition: .3s $transition.swing

    > span
      position: absolute
      top: 8px
      transform: translateX(-50%)
      white-space: nowrap
      user-select: none

    &:first-child > span
      transform: translateX(0)

    &:last-child > span
      transform: translateX(-100%)

  &:not(.v-input--is-dirty)
    .v-slider__ticks:first-child
      border-color: transparent

  &__thumb-container
    position: absolute
    top: 50%
    transition: .3s $transition.swing

    &:before
      content: ''
      color: inherit
      border-radius: 50%
      background: currentColor
      height: 32px
      left: -16px
      opacity: 0
      overflow: hidden
      pointer-events: none
      position: absolute
      top: -16px
      transform: scale(0.2)
      transition: $ripple-animation-transition
      width: 32px
      will-change: transform, opacity

  &__thumb
    width: 24px
    height: 24px
    left: -12px
    top: 50%
    border-radius: 50%
    background: transparent
    transition: .3s $transition.swing
    transform: translateY(-50%) scale(.6)
    user-select: none

/** Thumb Label */
.v-slider
  &--is-active
    .v-slider__thumb-container--is-active
      .v-slider__thumb
        transform: translateY(-50%) scale(1)

      &.v-slider__thumb-container--show-label
        .v-slider__thumb
          transform: translateY(-50%) scale(0)

    .v-slider__ticks-container
      .v-slider__ticks
        opacity: 1

  &__thumb-label__container
    position: absolute
    left: 0
    top: 0
    transition: .3s $transition.fast-in-fast-out

  &__thumb-label
    display: flex
    align-items: center
    justify-content: center
    font-size: 12px
    color: #fff
    width: 32px
    height: 32px
    border-radius: 50% 50% 0
    position: absolute
    left: 0
    bottom: 100%
    // Needed for IE11 which does not
    // process translateY(calc(-20% - 12px))
    // or translate(-50%, calc(-20% - 12px))
    transform: translateY(-20%) translateY(-12px) translateX(-50%) rotate(45deg)
    user-select: none
    transition: .3s $transition.fast-in-fast-out

    > *
      transform: rotate(-45deg)

  &__track,
  &__track-fill
    position: absolute


// Jest Snapshot v1, https://goo.gl/fbAQLP

exports[`VCheckbox.js should be disabled 1`] = `

<input aria-checked="false"
       disabled="disabled"
       role="checkbox"
       type="checkbox"
>

`;

exports[`VCheckbox.js should render themed component 1`] = `

        <input aria-checked="false"
               role="checkbox"
               type="checkbox"
        >

          check_box_outline_blank

`;

exports[`VCheckbox.js should use custom icons 1`] = `

        <input aria-checked="mixed"
               role="checkbox"
               type="checkbox"
        >

          fizzbuzz

`;

exports[`VCheckbox.js should use custom icons 2`] = `

        <input aria-checked="mixed"
               role="checkbox"
               type="checkbox"
        >

          fizzbuzz

`;

exports[`VCheckbox.js should use custom icons 3`] = `

        <input aria-checked="mixed"
               role="checkbox"
               type="checkbox"
        >

          fizzbuzz

`;


import { deprecate } from '../../util/console'

keep keep replace replace replace keep keep keep


import { test } from '@/test'
import VAutocomplete from '@/components/VAutocomplete'

test('VAutocomplete.js', ({ mount, shallow, compileToFunctions }) => {
  const app = document.createElement('div')
  app.setAttribute('data-app', true)
  document.body.appendChild(app)

  it('should allow changing of browser autocomplete', async () => {
    const wrapper = shallow(VAutocomplete, {
      propsData: {
        browserAutocomplete: 'on'
      }
    })

    const input = wrapper.first('input')

    expect(input.element.getAttribute('autocomplete')).toBe('on')

    wrapper.setProps({ browserAutocomplete: 'off' })

    await wrapper.vm.$nextTick()

    expect(input.element.getAttribute('autocomplete')).toBe('off')
  })

  it('should have explicit tabindex passed through when autocomplete', () => {
    const wrapper = shallow(VAutocomplete, {
      attrs: {
        tabindex: 10
      }
    })

    expect(wrapper.vm.$refs.input.tabIndex).toBe(10)
    expect(wrapper.vm.$el.tabIndex).toBe(-1)
  })

  it('should emit search input changes', async () => {
    const wrapper = shallow(VAutocomplete, {
      propsData: {
      }
    })

    const input = wrapper.first('input')

    const update = jest.fn()
    wrapper.vm.$on('update:searchInput', update)

    input.element.value = 'test'
    input.trigger('input')

    await wrapper.vm.$nextTick()

    expect(update).toBeCalledWith('test')
  })

  it('should filter autocomplete search results', async () => {
    const wrapper = shallow(VAutocomplete, {
      propsData: { items: ['foo', 'bar'] }
    })

    wrapper.setData({ internalSearch: 'foo' })

    expect(wrapper.vm.filteredItems).toHaveLength(1)
    expect(wrapper.vm.filteredItems[0]).toBe('foo')
  })

  it('should filter numeric primitives', () => {
    const wrapper = shallow(VAutocomplete, {
      propsData: {
        items: [1, 2]
      }
    })

    wrapper.setData({ internalSearch: 1 })

    expect(wrapper.vm.filteredItems).toHaveLength(1)
    expect(wrapper.vm.filteredItems[0]).toBe(1)
  })

  it('should activate when search changes and not active', async () => {
    const wrapper = shallow(VAutocomplete, {
      propsData: {
        items: [1, 2, 3, 4],
        multiple: true
      }
    })

    wrapper.vm.isMenuActive = true
    await wrapper.vm.$nextTick()
    wrapper.setData({ internalSearch: 2 })
    await wrapper.vm.$nextTick()

    expect(wrapper.vm.isMenuActive).toBe(true)
  })

  it('should set searchValue to null when deactivated', async () => {
    const wrapper = shallow(VAutocomplete, {
      propsData: {
        items: [1, 2, 3, 4],
        multiple: true
      }
    })

    await wrapper.vm.$nextTick()

    const input = wrapper.first('input')

    input.trigger('focus')
    input.element.value = 2
    input.trigger('input')

    expect(wrapper.vm.internalSearch).toBe('2')

    wrapper.setProps({
      multiple: false,
      value: 1
    })

    await wrapper.vm.$nextTick()

    expect(wrapper.vm.internalSearch).toBe(1)

    input.trigger('focus')
    input.element.value = 3
    input.trigger('input')
    input.trigger('blur')

    expect(wrapper.vm.internalSearch).toBe('3')
  })

  it('should render role=combobox correctly when autocomplete', async () => {
    const wrapper = shallow(VAutocomplete)

    expect(wrapper.vm.$el.getAttribute('role')).toBeFalsy()

    const input = wrapper.first('input')
    expect(input.element.getAttribute('role')).toBe('combobox')
  })

  it('should not duplicate items after items update when caching is turned on', async () => {
    const wrapper = shallow(VAutocomplete, {
      propsData: {
        cacheItems: true,
        returnObject: true,
        itemText: 'text',
        itemValue: 'id',
        items: [],
      }
    })

    wrapper.setProps({ items: [{ id: 1, text: 'A' }] })
    expect(wrapper.vm.computedItems).toHaveLength(1)
    wrapper.setProps({ items: [{ id: 1, text: 'A' }] })
    expect(wrapper.vm.computedItems).toHaveLength(1)
  })

  it('should cache items', async () => {
    const wrapper = shallow(VAutocomplete, {
      propsData: { cacheItems: true }
    })

    wrapper.setProps({ items: ['bar', 'baz'] })
    expect(wrapper.vm.computedItems).toHaveLength(2)

    wrapper.setProps({ items: ['foo'] })
    expect(wrapper.vm.computedItems).toHaveLength(3)

    wrapper.setProps({ items: ['bar'] })
    expect(wrapper.vm.computedItems).toHaveLength(3)
  })

  it('should cache items passed via prop', async () => {
    const wrapper = shallow(VAutocomplete, {
      propsData: {
        cacheItems: true,
        items: [1, 2, 3, 4]
      }
    })

    expect(wrapper.vm.computedItems).toHaveLength(4)

    wrapper.setProps({ items: [5] })

    expect(wrapper.vm.computedItems).toHaveLength(5)
  })

  it('should show input when focused and autocomplete', async () => {
    const wrapper = shallow(VAutocomplete)

    const input = wrapper.first('input')

    expect(input.hasStyle('display', 'none'))

    wrapper.trigger('focus')

    expect(input.hasStyle('display', 'block'))
  })

  it('should not filter text with no items', async () => {
    const wrapper = shallow(VAutocomplete, {
      propsData: {
        items: ['foo', 'bar']
      }
    })

    await wrapper.vm.$nextTick()

    wrapper.setProps({ searchInput: 'asdf' })

    // Wait for watcher
    await wrapper.vm.$nextTick()

    const tile = wrapper.first('.v-list__tile__title')

    expect(tile.text()).toBe('No data available')
  })

  it('should not display menu when tab focused', async () => {
    const wrapper = shallow(VAutocomplete, {
      propsData: {
        items: [1 ,2],
        value: 1
      }
    })

    const input = wrapper.first('input')
    input.trigger('focus')

    await wrapper.vm.$nextTick()

    expect(wrapper.vm.isMenuActive).toBe(false)
  })

  it('should change selected index', async () => {
    const wrapper = shallow(VAutocomplete, {
      attachToDocument: true,
      propsData: {
        items: ['foo', 'bar', 'fizz'],
        multiple: true,
        value: ['foo', 'bar', 'fizz']
      }
    })

    await wrapper.vm.$nextTick()

    expect(wrapper.vm.selectedIndex).toBe(-1)
    expect(wrapper.vm.selectedItems.length).toBe(3)

    // Right arrow
    wrapper.vm.changeSelectedIndex(39)
    expect(wrapper.vm.selectedIndex).toBe(0)

    wrapper.vm.changeSelectedIndex(39)
    expect(wrapper.vm.selectedIndex).toBe(1)

    wrapper.vm.changeSelectedIndex(39)
    expect(wrapper.vm.selectedIndex).toBe(2)

    // Left arrow
    wrapper.vm.changeSelectedIndex(37)
    expect(wrapper.vm.selectedIndex).toBe(1)

    wrapper.vm.changeSelectedIndex(37)
    expect(wrapper.vm.selectedIndex).toBe(0)

    wrapper.vm.changeSelectedIndex(37)
    expect(wrapper.vm.selectedIndex).toBe(-1)

    wrapper.vm.changeSelectedIndex(37)
    expect(wrapper.vm.selectedIndex).toBe(2)

    wrapper.vm.changeSelectedIndex(37)
    expect(wrapper.vm.selectedIndex).toBe(1)

    // Delete key
    wrapper.vm.changeSelectedIndex(8)
    await wrapper.vm.$nextTick()
    expect(wrapper.vm.selectedIndex).toBe(1)

    wrapper.vm.changeSelectedIndex(37)
    expect(wrapper.vm.selectedIndex).toBe(0)

    wrapper.vm.changeSelectedIndex(8)
    await wrapper.vm.$nextTick()
    expect(wrapper.vm.selectedIndex).toBe(0)

    // Should not change index if search is dirty
    wrapper.setProps({ searchInput: 'foo' })
    wrapper.vm.changeSelectedIndex(8)

    expect(wrapper.vm.selectedIndex).toBe(0)
    expect(wrapper.vm.selectedItems.length).toBe(1)

    wrapper.setProps({ searchInput: undefined })

    // Should not proceed if keyCode doesn't match
    wrapper.vm.changeSelectedIndex(99)
    await wrapper.vm.$nextTick()

    expect(wrapper.vm.selectedIndex).toBe(0)
    expect(wrapper.vm.selectedItems.length).toBe(1)

    wrapper.vm.changeSelectedIndex(8)
    await wrapper.vm.$nextTick()

    expect(wrapper.vm.selectedItems.length).toBe(0)
    expect(wrapper.vm.selectedIndex).toBe(-1)

    // Should not change/error if called with no selection
    wrapper.vm.changeSelectedIndex(8)
    await wrapper.vm.$nextTick()

    expect(wrapper.vm.selectedIndex).toBe(-1)

    wrapper.setProps({ value: ['foo', 'bar', 'fizz'] })

    await wrapper.vm.$nextTick()

    expect(wrapper.vm.selectedItems.length).toBe(3)

    wrapper.vm.selectedIndex = 2

    // Simulating removing items when an index already selected
    wrapper.setProps({ value: ['foo', 'bar'] })

    await wrapper.vm.$nextTick()

    expect(wrapper.vm.selectedIndex).toBe(2)

    // Backspace
    wrapper.vm.changeSelectedIndex(46)
    expect(wrapper.vm.selectedIndex).toBe(-1)
  })

  it('should conditionally show the menu', async () => {
    const wrapper = mount(VAutocomplete, {
      attachToDocument: true,
      propsData: {
        items: ['foo', 'bar', 'fizz']
      }
    })

    const slot = wrapper.first('.v-input__slot')
    const input = wrapper.first('input')

    // Focus input should only focus
    input.trigger('focus')

    expect(wrapper.vm.isFocused).toBe(true)
    expect(wrapper.vm.menuCanShow).toBe(true)
    expect(wrapper.vm.isMenuActive).toBe(false)

    // Clicking input should open menu
    slot.trigger('click')

    expect(wrapper.vm.isMenuActive).toBe(true)
    expect(wrapper.vm.menuCanShow).toBe(true)

    wrapper.setProps({ searchInput: 'foo' })

    expect(wrapper.vm.isMenuActive).toBe(true)
    expect(wrapper.vm.menuCanShow).toBe(true)

    // Should close menu but keep focus
    input.trigger('keydown.esc')

    expect(wrapper.vm.isFocused).toBe(true)
    expect(wrapper.vm.isMenuActive).toBe(false)
    expect(wrapper.vm.menuCanShow).toBe(true)

    // TODO: Add expects for tags when impl
  })

  it('should have the correct selected item', async () => {
    const wrapper = shallow(VAutocomplete, {
      propsData: {
        items: ['foo', 'bar', 'fizz'],
        multiple: true,
        value: ['foo']
      }
    })

    expect(wrapper.vm.selectedItem).toBe(null)

    wrapper.setProps({
      multiple: false,
      value: 'foo'
    })

    expect(wrapper.vm.selectedItem).toBe('foo')
  })

  it('should reset lazySearch', async () => {
    const wrapper = shallow(VAutocomplete, {
      propsData: {
        chips: true,
        items: ['foo', 'bar', 'fizz'],
        searchInput: 'foo'
      }
    })

    expect(wrapper.vm.lazySearch).toBe('foo')
    expect(wrapper.vm.hasSlot).toBe(true)

    wrapper.setData({ isMenuActive: true })
    await wrapper.vm.$nextTick()
    wrapper.setData({ isMenuActive: false })
    await wrapper.vm.$nextTick()

    expect(wrapper.vm.lazySearch).toBe(null)
  })

  it('should select input text on focus', async () => {
    const wrapper = mount(VAutocomplete)
    const select = jest.fn()
    wrapper.vm.$refs.input.select = select

    const input = wrapper.first('input')
    input.trigger('focus')

    await wrapper.vm.$nextTick()

    expect(wrapper.vm.isFocused).toBe(true)
    expect(select).toHaveBeenCalledTimes(1)

    input.trigger('keydown.tab')

    await wrapper.vm.$nextTick()

    expect(wrapper.vm.isFocused).toBe(false)
    expect(select).toHaveBeenCalledTimes(1)
  })

  it('should not respond to click', () => {
    const onFocus = jest.fn()
    const wrapper = mount(VAutocomplete, {
      propsData: { disabled: true },
      methods: { onFocus }
    })
    const slot = wrapper.first('.v-input__slot')

    slot.trigger('click')

    expect(onFocus).not.toBeCalled()

    wrapper.setProps({ disabled: false, readonly: true })

    slot.trigger('click')

    expect(onFocus).not.toBeCalled()

    wrapper.setProps({ readonly: false })

    slot.trigger('click')

    expect(onFocus).toBeCalled()
  })

  it('should react to keydown', () => {
    const activateMenu = jest.fn()
    const changeSelectedIndex = jest.fn()
    const onEscDown = jest.fn()
    const onTabDown = jest.fn()
    const wrapper = mount(VAutocomplete, {
      methods: {
        activateMenu,
        changeSelectedIndex,
        onEscDown,
        onTabDown
      }
    })

    const input = wrapper.first('input')

    expect(wrapper.vm.isMenuActive).toBe(false)

    input.trigger('keydown.enter')
    input.trigger('keydown.space')
    input.trigger('keydown.up')
    input.trigger('keydown.down')

    expect(activateMenu).toHaveBeenCalledTimes(4)

    input.trigger('keydown.esc')

    expect(onEscDown).toHaveBeenCalledTimes(1)

    input.trigger('keydown.tab')

    expect(onTabDown).toHaveBeenCalledTimes(1)

    // Skip menu activation
    wrapper.setData({ isMenuActive: true })

    input.element.value = 'foo'
    input.trigger('input')

    wrapper.setProps({ hideSelections: true })

    expect(wrapper.vm.genSelections()).toEqual([])
  })

  // https://github.com/vuetifyjs/vuetify/issues/3793
  it('should reset menu index after selection', async () => {
    const wrapper = shallow(VAutocomplete, {
      propsData: {
        items: ['foo', 'bar'],
        value: 'foo'
      }
    })

    expect(wrapper.vm.isMenuActive).toBe(false)
    const slot = wrapper.first('.v-input__slot')
    const item = wrapper.first('.v-list__tile')
    slot.trigger('click')

    expect(wrapper.vm.isMenuActive).toBe(true)

    expect(wrapper.vm.getMenuIndex()).toBe(-1)
  })

  it('should not remove a disabled item', () => {
    const wrapper = mount(VAutocomplete, {
      propsData: {
        chips: true,
        multiple: true,
        items: [
          { text: 'foo', value: 'foo', disabled: true },
          { text: 'bar', value: 'bar' },
        ],
        value: ['foo', 'bar']
      }
    })

    const chips = wrapper.find('.v-chip')
    const input = wrapper.first('input')

    expect(chips[0].element.classList.contains('v-chip--disabled')).toBe(true)

    input.trigger('focus')
    input.trigger('keydown.left')

    expect(wrapper.vm.selectedIndex).toBe(1)

    input.trigger('keydown.delete')

    expect(wrapper.vm.internalValue).toEqual(['foo'])

    input.trigger('keydown.delete')

    expect(wrapper.vm.internalValue).toEqual(['foo'])
  })

  it('should not filter results', async () => {
    const wrapper = mount(VAutocomplete, {
      propsData: {
        items: ['foo', 'bar']
      }
    })

    const input = wrapper.first('input')
    input.element.value = 'foo'
    input.trigger('input')

    expect(wrapper.vm.filteredItems.length).toBe(1)

    wrapper.setProps({ noFilter: true })

    await wrapper.vm.$nextTick()

    expect(wrapper.vm.filteredItems.length).toBe(2)
  })

  it('should hide menu when no data', async () => {
    const wrapper = mount(VAutocomplete)

    const input = wrapper.first('input')
    input.trigger('focus')
    input.element.value = 'foo'
    input.trigger('input')

    expect(wrapper.vm.menuCanShow).toBe(true)

    wrapper.setProps({ hideNoData: true })

    await wrapper.vm.$nextTick()

    expect(wrapper.vm.menuCanShow).toBe(false)

    wrapper.setProps({ hideNoData: false })

    await wrapper.vm.$nextTick()

    expect(wrapper.vm.menuCanShow).toBe(true)

    // If we are hiding selected
    // filtered will have a positive length
    // but the hidden items will not show
    // check to make sure when all values are
    // selected to close the menu
    wrapper.setProps({
      hideNoData: true,
      hideSelected: true,
      items: [1,2,3,4],
      multiple: true,
      value: [1,2,3]
    })

    await wrapper.vm.$nextTick()

    expect(wrapper.vm.menuCanShow).toBe(true)

    wrapper.setProps({ value: [1,2,3,4] })

    await wrapper.vm.$nextTick()

    expect(wrapper.vm.menuCanShow).toBe(false)
  })

  it('should not hide menu when no data but has no-data slot', async () => {
    const wrapper = mount(VAutocomplete, {
      propsData: {
        combobox: true
      },
      slots: {
        'no-data': [compileToFunctions('show me')]
      }
    })

    const input = wrapper.first('input')
    input.trigger('focus')
    await wrapper.vm.$nextTick()
    expect(wrapper.vm.menuCanShow).toBe(true)
  })

  // https://github.com/vuetifyjs/vuetify/issues/2834
  it('should not update search if selectedIndex is > -1', () => {
    const wrapper = mount(VAutocomplete)

    const input = wrapper.first('input')

    input.trigger('focus')
    input.element.value = 'foo'
    input.trigger('input')

    expect(wrapper.vm.internalSearch).toBe('foo')

    wrapper.setData({
      lazySearch: '',
      selectedIndex: 0
    })

    expect(wrapper.vm.internalSearch).toBe('')

    input.element.value = 'bar'
    input.trigger('input')

    expect(wrapper.vm.internalSearch).toBe('')
  })

  it('should clear search input on clear callback', async () => {
    const wrapper = mount(VAutocomplete, {
      propsData: {
        clearable: true
      }
    })

    const icon = wrapper.first('.v-input__append-inner .v-icon')
    const input = wrapper.first('input')

    input.element.value = 'foobar'
    input.trigger('input')

    expect(wrapper.vm.internalSearch).toBe('foobar')

    icon.trigger('click')

    expect(wrapper.vm.internalSearch).toBe(undefined)
  })

  it('should propagate content class', () => {
    const wrapper = mount(VAutocomplete, {
      propsData: {
        menuProps: { contentClass: 'foobar' }
      }
    })

    const content = wrapper.first('.v-autocomplete__content')

    expect(content.element.classList.contains('foobar')).toBe(true)
  })

  it('should update the displayed value when items changes', async () => {
    const wrapper = mount(VAutocomplete, {
      propsData: {
        value: 1,
        items: []
      }
    })

    const input = wrapper.first('input')

    await wrapper.vm.$nextTick()
    wrapper.setProps({ items: [{ text: 'foo', value: 1 }] })
    await wrapper.vm.$nextTick()
    expect(input.element.value).toBe('foo')
  })

  it('should show menu when items are added for the first time and hide-no-data is enabled', async () => {
    const wrapper = mount(VAutocomplete, {
      propsData: {
        hideNoData: true,
        items: []
      }
    })

    const input = wrapper.first('input')

    input.trigger('focus')

    expect(wrapper.vm.isMenuActive).toBe(false)
    expect(wrapper.vm.isFocused).toBe(true)

    wrapper.setProps({
      items: ['Foo', 'Bar']
    })

    await wrapper.vm.$nextTick()

    expect(wrapper.vm.isMenuActive).toBe(true)
  })

  it('should not show menu when items are updated and hide-no-data is enabled ', async () => {
    const wrapper = mount(VAutocomplete, {
      propsData: {
        hideNoData: true,
        items: [ 'Something first' ]
      }
    })

    const input = wrapper.first('input')

    input.trigger('focus')

    expect(wrapper.vm.isMenuActive).toBe(false)
    expect(wrapper.vm.isFocused).toBe(true)

    wrapper.setProps({
      items: ['Foo', 'Bar']
    })

    await wrapper.vm.$nextTick()

    expect(wrapper.vm.isMenuActive).toBe(false)
  })
})


import { deepEqual } from '../util/helpers'
import { inject as RegistrableInject } from './registrable'
import { consoleError } from '../util/console'

// Mixins
import Colorable from './colorable'

/* @vue/component */
export default {
  name: 'validatable',

  mixins: [
    Colorable,
    RegistrableInject('form')
  ],

  props: {
    error: Boolean,
    errorCount: {
      type: [Number, String],
      default: 1
    },
    errorMessages: {
      type: [String, Array],
      default: () => []
    },
    messages: {
      type: [String, Array],
      default: () => []
    },
    rules: {
      type: Array,
      default: () => []
    },
    success: Boolean,
    successMessages: {
      type: [String, Array],
      default: () => []
    },
    validateOnBlur: Boolean
  },

  data: () => ({
    errorBucket: [],
    hasColor: false,
    hasFocused: false,
    hasInput: false,
    isResetting: false,
    valid: false
  }),

  computed: {
    hasError () {
      return this.internalErrorMessages.length > 0 ||
        this.errorBucket.length > 0 ||
        this.error
    },
    externalError () {
      return this.internalErrorMessages.length > 0 || this.error
    },
    // TODO: Add logic that allows the user to enable based
    // upon a good validation
    hasSuccess () {
      return this.successMessages.length > 0 ||
        this.success
    },
    hasMessages () {
      return this.validations.length > 0
    },
    hasState () {
      return this.hasSuccess || (this.shouldValidate && this.hasError)
    },
    internalErrorMessages () {
      return this.errorMessages || ''
    },
    shouldValidate () {
      return this.externalError || (!this.isResetting && (
        this.validateOnBlur
          ? this.hasFocused && !this.isFocused
          : (this.hasInput || this.hasFocused)
      ))
    },
    validations () {
      return this.validationTarget.slice(0, this.errorCount)
    },
    validationState () {
      if (this.hasError && this.shouldValidate) return 'error'
      if (this.hasSuccess) return 'success'
      if (this.hasColor) return this.color
      return null
    },
    validationTarget () {
      const target = this.internalErrorMessages.length > 0
        ? this.errorMessages
        : this.successMessages.length > 0
          ? this.successMessages
          : this.messages

      // String
      if (!Array.isArray(target)) {
        return [target]
      // Array with items
      } else if (target.length > 0) {
        return target
      // Currently has validation
      } else if (this.shouldValidate) {
        return this.errorBucket
      } else {
        return []
      }
    }
  },

  watch: {
    rules: {
      handler (newVal, oldVal) {
        if (deepEqual(newVal, oldVal)) return
        this.validate()
      },
      deep: true
    },
    internalValue () {
      // If it's the first time we're setting input,
      // mark it with hasInput
      this.hasInput = true
      this.validateOnBlur || this.$nextTick(this.validate)
    },
    isFocused (val) {
      if (!val) {
        this.hasFocused = true
        this.validateOnBlur && this.validate()
      }
    },
    isResetting () {
      setTimeout(() => {
        this.hasInput = false
        this.hasFocused = false
        this.isResetting = false
      }, 0)
    },
    hasError (val) {
      if (this.shouldValidate) {
        this.$emit('update:error', val)
      }
    }
  },

  beforeMount () {
    this.validate()
  },

  created () {
    this.form && this.form.register(this)
  },

  beforeDestroy () {
    this.form && this.form.unregister(this)
  },

  methods: {
    /** @public */
    reset () {
      this.isResetting = true
      this.internalValue = Array.isArray(this.internalValue)
        ? []
        : undefined
    },
    /** @public */
    resetValidation () {
      this.isResetting = true
    },
    /** @public */
    validate (force = false, value = this.internalValue) {
      const errorBucket = []

      if (force) this.hasInput = this.hasFocused = true

      for (let index = 0; index < this.rules.length; index++) {
        const rule = this.rules[index]
        const valid = typeof rule === 'function' ? rule(value) : rule

        if (valid === false || typeof valid === 'string') {
          errorBucket.push(valid)
        } else if (valid !== true) {
          consoleError(`Rules should return a string or boolean, received '${typeof valid}' instead`, this)
        }
      }

      this.errorBucket = errorBucket
      this.valid = errorBucket.length === 0

      return this.valid
    }
  }
}


import Vue from 'vue'
import Vuetify from '@/'

describe('full import', () => {
  Vue.use(Vuetify)
  const registeredComponents = Object.keys(Vue.options.components).sort()
  const registeredDirectives = Object.keys(Vue.options.directives).sort()

  it('should register all subcomponents', () => {
    expect(registeredComponents).toMatchSnapshot()
  })

  it('should register all names in PascalCase', () => {
    registeredComponents.forEach(name =>
      expect(name).toMatch(/^(?:[A-Z][a-z]*)+$/)
    )
  })

  it('should register all directives', () => {
    expect(registeredDirectives).toMatchSnapshot()
  })
})


// Extensions
import { BaseWindowItem } from '../VWindow/VWindowItem'

// Mixins
import {
  factory as GroupableFactory
} from '../../mixins/groupable'
import { deprecate } from '../../util/console'

/* @vue/component */
export default {
  name: 'v-tab-item',

  mixins: [
    BaseWindowItem,
    GroupableFactory('windowGroup', 'v-tab-item', 'v-tabs-items')
  ],

  props: {
    id: String
  },

  render (h) {
    const render = BaseWindowItem.options.render.call(this, h)

    // For backwards compatibility with v1.2
    if (this.id) {
      deprecate('id', 'value', this)

      render.data.domProps = render.data.domProps || {}
      render.data.domProps.id = this.id
    }

    return render
  }
}


@import '../bootstrap'

.v-item-group
  flex: 1 0 auto
  position: relative
  transition: $primary-transition

  > *
    cursor: pointer
    flex: 1 1 auto


// Styles
import '../../stylus/components/_selection-controls.styl'
import '../../stylus/components/_radio-group.styl'

// Components
import VInput from '../VInput'

// Mixins
import Comparable from '../../mixins/comparable'
import {
  provide as RegistrableProvide
} from '../../mixins/registrable'

/* @vue/component */
export default {
  name: 'v-radio-group',

  extends: VInput,

  mixins: [
    Comparable,
    RegistrableProvide('radio')
  ],

  model: {
    prop: 'value',
    event: 'change'
  },

  provide () {
    return {
      radio: this
    }
  },

  props: {
    column: {
      type: Boolean,
      default: true
    },
    height: {
      type: [Number, String],
      default: 'auto'
    },
    mandatory: {
      type: Boolean,
      default: true
    },
    name: String,
    row: Boolean,
    // If no value set on VRadio
    // will match valueComparator
    // force default to null
    value: {
      default: null
    }
  },

  data: () => ({
    internalTabIndex: -1,
    radios: []
  }),

  computed: {
    classes () {
      return {
        'v-input--selection-controls v-input--radio-group': true,
        'v-input--radio-group--column': this.column && !this.row,
        'v-input--radio-group--row': this.row
      }
    }
  },

  watch: {
    hasError: 'setErrorState',
    internalValue: 'setActiveRadio'
  },

  mounted () {
    this.setErrorState(this.hasError)
    this.setActiveRadio()
  },

  methods: {
    genDefaultSlot () {
      return this.$createElement('div', {
        staticClass: 'v-input--radio-group__input',
        attrs: {
          role: 'radiogroup'
        }
      }, VInput.methods.genDefaultSlot.call(this))
    },
    onRadioChange (value) {
      if (this.disabled) return

      this.hasInput = true
      this.internalValue = value
      this.setActiveRadio()
      this.$nextTick(this.validate)
    },
    onRadioBlur (e) {
      if (!e.relatedTarget || !e.relatedTarget.classList.contains('v-radio')) {
        this.hasInput = true
        this.$emit('blur', e)
      }
    },
    register (radio) {
      radio.isActive = this.valueComparator(this.internalValue, radio.value)
      radio.$on('change', this.onRadioChange)
      radio.$on('blur', this.onRadioBlur)
      this.radios.push(radio)
    },
    setErrorState (val) {
      for (let index = this.radios.length; --index >= 0;) {
        this.radios[index].parentError = val
      }
    },
    setActiveRadio () {
      for (let index = this.radios.length; --index >= 0;) {
        const radio = this.radios[index]
        radio.isActive = this.valueComparator(this.internalValue, radio.value)
      }
    },
    unregister (radio) {
      radio.$off('change', this.onRadioChange)
      radio.$off('blur', this.onRadioBlur)

      const index = this.radios.findIndex(r => r === radio)

      /* istanbul ignore else */
      if (index > -1) this.radios.splice(index, 1)
    }
  }
}


import VTooltip from './VTooltip'

export { VTooltip }
export default VTooltip


keep keep replace replace replace replace replace keep keep keep keep keep


import './stylus/app.styl'
import VuetifyComponent from './components/Vuetify'
import * as components from './components'
import * as directives from './directives'
import { VueConstructor } from 'vue'
import { Vuetify as VuetifyPlugin, VuetifyUseOptions } from 'types'

const Vuetify: VuetifyPlugin = {
  install (Vue: VueConstructor, args?: VuetifyUseOptions): void {
    Vue.use(VuetifyComponent, {
      components,
      directives,
      ...args
    })
  },
  version: __VUETIFY_VERSION__
}

if (typeof window !== 'undefined' && window.Vue) {
  window.Vue.use(Vuetify)
}

export default Vuetify


@import '../bootstrap'
@import '../theme'

v-date-picker-table($material)
  th
    color: $material.text.disabled

theme(v-date-picker-table, "v-date-picker-table")

.v-date-picker-table
  position: relative
  padding: 0 12px
  height: 242px

  table
    transition: $primary-transition
    top: 0
    table-layout: fixed
    width: 100%

  td, th
    text-align: center
    position: relative

  th
    font-size: 12px

  &--date .v-btn
    height: 32px
    width: 32px

  .v-btn
    z-index: auto
    margin: 0
    font-size: 12px

    &.v-btn--active
      color: #fff

.v-date-picker-table--month
  td
    width: 33.333333%
    height: 56px
    vertical-align: middle
    text-align: center

    .v-btn
      margin: 0 auto
      max-width: 160px
      min-width: 40px
      width: 100%

.v-date-picker-table--date
  th
    padding: 8px 0
    font-weight: 600

  td
    width: 45px

.v-date-picker-table__event
  border-radius: 50%
  bottom: 2px
  content: ""
  display: block
  height: 8px
  left: 50%
  position: absolute
  transform: translateX(-4px)
  width: 8px


@import '../bootstrap'

.v-dialog
  elevation(24)
  border-radius: $card-border-radius
  margin: 24px
  overflow-y: auto
  pointer-events: auto
  transition: .3s $transition.fast-in-fast-out
  width: 100%
  z-index: inherit

  &__content
    align-items: center
    display: flex
    height: 100%
    justify-content: center
    left: 0
    pointer-events: none
    position: fixed
    top: 0
    transition: .2s $transition.fast-in-fast-out
    width: 100%
    z-index: 6
    outline: none

  &:not(.v-dialog--fullscreen)
    max-height: 90%

  &__activator
    cursor: pointer

    *
      cursor: pointer

    &--disabled
      pointer-events: none

  &__container
    display: inline-block
    vertical-align: middle

  @keyframes animate-dialog
    0%
      transform: scale(1)

    50%
      transform: scale(1.03)

    100%
      transform: scale(1)

  &--animated
    animation-duration: .15s
    animation-name: animate-dialog
    animation-timing-function: $transition.fast-in-fast-out

  &--fullscreen
    border-radius: 0
    margin 0
    height 100%
    position fixed
    overflow-y: auto
    top: 0
    left 0

    > .v-card
      min-height 100%
      min-width 100%
      margin 0 !important
      padding 0 !important

  &--scrollable
    display: flex

    > .v-card
      display: flex
      flex: 1 1 auto
      flex-direction: column

      > .v-card__title
      > .v-card__actions
        flex: 1 0 auto

      > .v-card__text
        overflow-y: auto
        backface-visibility: hidden


const render = BaseWindowItem.options.render.call(this, h)

// For backwards compatibility with v1.2

keep keep keep keep replace keep keep keep

const render = VWindowItem.options.render.call(this, h)


// linear
export const linear = t => t
// accelerating from zero velocity
export const easeInQuad = t => t * t
// decelerating to zero velocity
export const easeOutQuad = t => t * (2 - t)
// acceleration until halfway, then deceleration
export const easeInOutQuad = t => (t < 0.5 ? 2 * t * t : -1 + (4 - 2 * t) * t)
// accelerating from zero velocity
export const easeInCubic = t => t * t * t
// decelerating to zero velocity
export const easeOutCubic = t => --t * t * t + 1
// acceleration until halfway, then deceleration
export const easeInOutCubic = t => t < 0.5 ? 4 * t * t * t : (t - 1) * (2 * t - 2) * (2 * t - 2) + 1
// accelerating from zero velocity
export const easeInQuart = t => t * t * t * t
// decelerating to zero velocity
export const easeOutQuart = t => 1 - --t * t * t * t
// acceleration until halfway, then deceleration
export const easeInOutQuart = t => (t < 0.5 ? 8 * t * t * t * t : 1 - 8 * --t * t * t * t)
// accelerating from zero velocity
export const easeInQuint = t => t * t * t * t * t
// decelerating to zero velocity
export const easeOutQuint = t => 1 + --t * t * t * t * t
// acceleration until halfway, then deceleration
export const easeInOutQuint = t => t < 0.5 ? 16 * t * t * t * t * t : 1 + 16 * --t * t * t * t * t


/* eslint-disable max-len */

import {
  VueConstructor,
  ComponentOptions,
  FunctionalComponentOptions,
  VNodeData
} from 'vue'
import { CombinedVueInstance, Vue } from 'vue/types/vue'
import {
  RecordPropsDefinition,
  ThisTypedComponentOptionsWithArrayProps,
  ThisTypedComponentOptionsWithRecordProps
} from 'vue/types/options'
import { TouchStoredHandlers } from './directives/touch'

declare global {
  interface Window {
    Vue: VueConstructor
  }

  interface HTMLCollection {
    [Symbol.iterator] (): IterableIterator<Element>
  }

  interface Element {
    getElementsByClassName(classNames: string): NodeListOf<HTMLElement>
  }

  interface HTMLElement {
    _clickOutside?: EventListenerOrEventListenerObject
    _onResize?: {
      callback: () => void
      options?: boolean | AddEventListenerOptions
    }
    _ripple?: {
      enabled?: boolean
      centered?: boolean
      class?: string
      circle?: boolean
    }
    _onScroll?: {
      callback: EventListenerOrEventListenerObject
      options: boolean | AddEventListenerOptions
      target: EventTarget
    }
    _touchHandlers?: {
      [_uid: number]: TouchStoredHandlers
    }
  }

  interface HTMLImageElement {
    decode: () => Promise<never>
  }

  function parseInt(s: string | number, radix?: number): number
  function parseFloat(string: string | number): number

  export const __VUETIFY_VERSION__: string
  export const __REQUIRED_VUE__: string
}

declare module 'vue/types/vnode' {
  export interface VNodeData {
    model?: {
      callback: (v: any) => void
      expression: string
      value: any
    }
  }
}

declare module 'vue/types/vue' {
  export type OptionsVue<Instance extends Vue, Data, Methods, Computed, Props, Options = {}> = VueConstructor<
    CombinedVueInstance<Instance, Data, Methods, Computed, Props> & Vue,
    Options
  >

  export interface Vue {
    _uid: number

    /** bindObjectListeners */
     _g (data: VNodeData, value: {}): VNodeData
  }

  export interface RawComponentOptions<
    V extends Vue = Vue,
    Data = {} | undefined,
    Methods = {} | undefined,
    Computed = {} | undefined,
    Props = {} | undefined
  > {
    name?: string
    data: Data
    methods: Methods
    computed: {
      [C in keyof Computed]: (this: V) => Computed[C]
    }
    props: Props
  }

  interface VueConstructor<
    V extends Vue = Vue,
    Options = Record<string, any>
  > {
    version: string
    /* eslint-disable-next-line camelcase */
    $_vuetify_subcomponents?: Record<string, VueConstructor>
    options: Options

    extend<Data, Methods, Computed, Options, PropNames extends string = never> (options?: ThisTypedComponentOptionsWithArrayProps<V, Data, Methods, Computed, PropNames> & Options): OptionsVue<V, Data, Methods, Computed, Record<PropNames, any>, Options>
    extend<Data, Methods, Computed, Props, Options> (options?: ThisTypedComponentOptionsWithRecordProps<V, Data, Methods, Computed, Props> & Options): OptionsVue<V, Data, Methods, Computed, Props, Options>
    extend<Options, PropNames extends string = never> (definition: FunctionalComponentOptions<Record<PropNames, any>, PropNames[]> & Options): OptionsVue<V, {}, {}, {}, Record<PropNames, any>, Options>
    extend<Props, Options> (definition: FunctionalComponentOptions<Props, RecordPropsDefinition<Props>> & Options): OptionsVue<V, {}, {}, {}, Props, Options>
    extend<V extends Vue = Vue> (options?: ComponentOptions<V> & Options): OptionsVue<V, {}, {}, {}, {}, Options>
  }
}


// Jest Snapshot v1, https://goo.gl/fbAQLP

exports[`VTimelineItem.js should conditionally render an icon or icon slot 1`] = `

        foo

`;

exports[`VTimelineItem.js should conditionally render an icon or icon slot 2`] = `

        foo

`;

exports[`VTimelineItem.js should conditionally render dot 1`] = `

`;

exports[`VTimelineItem.js should conditionally render dot 2`] = `

`;


// Styles
import '../../stylus/components/_timeline.styl'

// Types
import { VNode } from 'vue'
import mixins from '../../util/mixins'

// Mixins
import Themeable from '../../mixins/themeable'

export default mixins(
  Themeable
/* @vue/component */
).extend({
  name: 'v-timeline',

  props: {
    alignTop: Boolean,
    dense: Boolean
  },

  computed: {
    classes (): {} {
      return {
        'v-timeline--align-top': this.alignTop,
        'v-timeline--dense': this.dense,
        ...this.themeClasses
      }
    }
  },

  render (h): VNode {
    return h('div', {
      staticClass: 'v-timeline',
      'class': this.classes
    }, this.$slots.default)
  }
})


import VCounter from '@/components/VCounter'
import { test, functionalContext } from '@/test'

test('VCounter.js', ({ mount }) => {
  it('should render component', () => {
    const context = functionalContext({ props: { value: 5, max: 10 }})
    const wrapper = mount(VCounter, context)

    expect(wrapper.html()).toMatchSnapshot()
  })

  it('should render component in error state', () => {
    const context = functionalContext({ props: { value: 15, max: 10 }})
    const wrapper = mount(VCounter, context)

    expect(wrapper.element.classList).toContain('error--text')
  })

  it('should render component if max is not provided', () => {
    const context = functionalContext({ props: { value: 15 }})
    const wrapper = mount(VCounter, context)

    expect(wrapper.element.textContent).toBe('15')
  })
})


import { BaseWindow } from '../VWindow/VWindow'

import VWindow from '../VWindow/VWindow'


import '../../stylus/components/_pagination.styl'

import VIcon from '../VIcon'

// Directives
import Resize from '../../directives/resize'

// Mixins
import mixins from '../../util/mixins'
import Colorable from '../../mixins/colorable'
import Themeable from '../../mixins/themeable'

// Types
import { VNode, CreateElement } from 'vue'

/* @vue/component */
export default mixins(Colorable, Themeable).extend({
  name: 'v-pagination',

  directives: { Resize },

  props: {
    circle: Boolean,
    disabled: Boolean,
    length: {
      type: Number,
      default: 0,
      validator: (val: number) => val % 1 === 0
    },
    totalVisible: [Number, String],
    nextIcon: {
      type: String,
      default: '$vuetify.icons.next'
    },
    prevIcon: {
      type: String,
      default: '$vuetify.icons.prev'
    },
    value: {
      type: Number,
      default: 0
    }
  },

  data () {
    return {
      maxButtons: 0,
      selected: null as number | null
    }
  },

  computed: {
    classes (): object {
      return {
        'v-pagination': true,
        'v-pagination--circle': this.circle,
        'v-pagination--disabled': this.disabled,
        ...this.themeClasses
      }
    },

    items (): (string | number)[] {
      const maxLength = parseInt(this.totalVisible, 10) || this.maxButtons
      if (this.length <= maxLength) {
        return this.range(1, this.length)
      }

      const even = maxLength % 2 === 0 ? 1 : 0
      const left = Math.floor(maxLength / 2)
      const right = this.length - left + 1 + even

      if (this.value > left && this.value < right) {
        const start = this.value - left + 2
        const end = this.value + left - 2 - even

        return [1, '...', ...this.range(start, end), '...', this.length]
      } else {
        return [
          ...this.range(1, left),
          '...',
          ...this.range(this.length - left + 1 + even, this.length)
        ]
      }
    }
  },

  watch: {
    value () {
      this.init()
    }
  },

  mounted () {
    this.init()
  },

  methods: {
    init () {
      this.selected = null

      this.$nextTick(this.onResize)
      // TODO: Change this (f75dee3a, cbdf7caa)
      setTimeout(() => (this.selected = this.value), 100)
    },
    onResize () {
      const width = this.$el && this.$el.parentElement
        ? this.$el.parentElement.clientWidth
        : window.innerWidth

      this.maxButtons = Math.floor((width - 96) / 42)
    },
    next (e: Event) {
      e.preventDefault()
      this.$emit('input', this.value + 1)
      this.$emit('next')
    },
    previous (e: Event) {
      e.preventDefault()
      this.$emit('input', this.value - 1)
      this.$emit('previous')
    },
    range (from: number, to: number) {
      const range = []

      from = from > 0 ? from : 1

      for (let i = from; i <= to; i++) {
        range.push(i)
      }

      return range
    },
    genIcon (h: CreateElement, icon: string, disabled: boolean, fn: EventListener): VNode {
      return h('li', [
        h('button', {
          staticClass: 'v-pagination__navigation',
          class: {
            'v-pagination__navigation--disabled': disabled
          },
          on: disabled ? {} : { click: fn }
        }, [h(VIcon, [icon])])
      ])
    },
    genItem (h: CreateElement, i: string | number): VNode {
      const color: string | false = (i === this.value) && (this.color || 'primary')
      return h('button', this.setBackgroundColor(color, {
        staticClass: 'v-pagination__item',
        class: {
          'v-pagination__item--active': i === this.value
        },
        on: {
          click: () => this.$emit('input', i)
        }
      }), [i.toString()])
    },
    genItems (h: CreateElement): VNode[] {
      return this.items.map((i, index) => {
        return h('li', { key: index }, [
          isNaN(Number(i)) ? h('span', { class: 'v-pagination__more' }, [i.toString()]) : this.genItem(h, i)
        ])
      })
    }
  },

  render (h): VNode {
    const children = [
      this.genIcon(h, this.$vuetify.rtl ? this.nextIcon : this.prevIcon, this.value <= 1, this.previous),
      this.genItems(h),
      this.genIcon(h, this.$vuetify.rtl ? this.prevIcon : this.nextIcon, this.value >= this.length, this.next)
    ]

    return h('ul', {
      directives: [{
        modifiers: { quiet: true },
        name: 'resize',
        value: this.onResize
      }] as any,
      class: this.classes
    }, children)
  }
})


vuetify

cross-env TARGET=development webpack --config build/config.js --progress --hide-modules --watch

cross-env NODE_ENV=development webpack-dev-server --config build/webpack.dev.config.js --hot

concurrently "yarn build:dist --no-progress" "yarn build:lib" -n"dist,lib" --kill-others-on-fail -r

cross-env NODE_ENV=production node build/webpack.dev.config.js

rimraf dist && cross-env NODE_ENV=production webpack --config build/config.js --progress --hide-modules

rimraf es5 lib lib-temp && tsc -p tsconfig.dist.json && cross-env NODE_ENV=lib babel lib-temp --out-dir lib --source-maps -q && cross-env NODE_ENV=es5 babel lib-temp --out-dir es5 --source-maps -q

node --inspect --debug-brk build/config.js

cross-env NODE_ENV=test node --inspect --inspect-brk ./node_modules/jest/bin/jest.js --no-cache -i --verbose

concurrently "tsc --noEmit --pretty" "tslint -p . -c .tslint.js src/**/*.ts -t stylish" "eslint --ext .js,.ts src" "yarn test:types" --kill-others-on-fail -n "tsc,tslint,eslint,types"

node dev/warn-npm-install.js && concurrently "yarn test" "yarn lint" -n "test,lint" --kill-others-on-fail

Vue.js 2 Semantic Component Framework