.contextual-bar {
	box-sizing: content-box;

	&.contextual-bar {
		z-index: 10;

		display: flex;

		overflow: hidden;
		flex-direction: column;
		flex: 1 0 var(--flex-tab-width);

		width: var(--flex-tab-width);
		height: 100%;

		background: var(--color-white);

		border-inline-start: 2px solid var(--color-gray-lightest);

		& > .flex-tab {
			width: 100%;
		}

		& & {
			margin-left: -2px;
		}

		.rtl & & {
			margin-right: -2px;
			margin-left: 0;
		}
	}

	&-wrap {
		position: relative;

		display: flex;
		flex-direction: column;
		flex: 1 1 100%;

		max-height: 100%;
	}

	&__content {
		display: flex;
		overflow: auto;

		flex-direction: column;

		flex: 1 1 auto;

		height: 100%;

		padding: var(--default-padding);
		justify-content: space-between;

		&--no-padding {
			padding-right: 0;
			padding-left: 0;
		}

		& .section:not(:last-child) {
			margin-bottom: 2rem !important;
		}

		&.discussions,
		&.pinned-messages,
		&.starred-messages {
			.message {
				padding-left: 46px;

				.user-card-message {
					left: 0;
				}
			}
		}
	}

	&__header {
		display: flex;
		flex: 0 0 auto;

		height: 64px;

		margin: 0 -8px;

		padding: var(--default-padding);

		border-bottom: solid 2px var(--color-gray-lightest);

		align-items: center;
		justify-content: flex-end;

		&-data {
			display: flex;
			overflow: hidden;
			flex: 1 1;
			align-items: center;
		}

		&-icon {
			flex: 0 0 auto;

			margin: 0 0.25rem;

			font-size: 22px;

			&--star {
				fill: none;
			}
		}

		&-back-btn {
			position: absolute;
			left: 0;

			width: auto;
			height: auto;
			margin: 0;
			padding: 0;

			font-size: 22px;
		}

		&-title {
			overflow: hidden;
			flex: 1;

			margin: 0 0.25rem;

			white-space: nowrap;
			text-overflow: ellipsis;

			font-size: 16px;
			font-weight: 400;
		}

		&-description {
			display: block;
			flex: 1;

			color: var(--color-gray);

			font-size: 0.85rem;
			font-weight: 400;
		}

		&-close-icon {
			transform: rotate(45deg);

			font-size: 20px;
		}

		&--empty {
			border-bottom: none;
			background: none;
		}
	}

	& .search-form .rc-input__icon-svg {
		font-size: 1.2rem;
	}
}

.contextual-bar + .contextual-bar {
	position: absolute;
	z-index: -1;
	right: 0;
}

@media (width <= 1100px) {
	.contextual-bar {
		position: absolute;
		z-index: 10;
		right: 0;
	}

	.rtl .contextual-bar {
		position: absolute;
		right: unset;
		left: 0;
	}
}

@media (width <= 500px) {
	.contextual-bar {
		position: fixed;
		z-index: 999;
		top: 0;

		width: 100%;

		animation: dropup-show 0.3s cubic-bezier(0.45, 0.05, 0.55, 0.95);
	}
}

.contextual-bar__content.mail-messages {
	& .rc-popup-list {
		z-index: 1;
	}

	& .rc-input:not(:last-child) {
		margin-bottom: 2rem;
	}
}

.mail-messages__instructions {
	display: flex;

	margin-bottom: 3rem;

	padding: 1.25rem;

	color: var(--rc-color-alert-message-primary);

	border-width: 1px;
	border-color: var(--rc-color-alert-message-primary);
	border-radius: 2px;
	background: var(--rc-color-alert-message-primary-background);

	&--selected {
		cursor: pointer;

		color: var(--rc-color-alert-message-secondary);
		border-color: var(--rc-color-alert-message-secondary);
		background: var(--rc-color-alert-message-secondary-background);
	}

	&--warning {
		color: var(--rc-color-alert-message-warning);
		border-color: var(--rc-color-alert-message-warning);
		background: var(--rc-color-alert-message-warning-background);
	}

	&-wrapper {
		display: flex;

		margin: 0 -10px;
	}

	&-icon {
		margin: 0 10px;

		font-size: 2rem;

		&--hand-pointer {
			margin: 0 15px;

			transform: rotate3d(0, 0, 1, -25deg);
			fill: currentColor;
		}
	}

	&-text {
		display: flex;
		flex-direction: column;

		margin: 0 10px;

		font-size: 0.875rem;
		font-weight: 600;
		line-height: 1.2rem;

		&-selected {
			font-weight: 400;
		}
	}
}


private ensureDefaultFields<P>(options: FindOptions<P>): FindOptions<P>;

private ensureDefaultFields<P>(options?: any): FindOptions<P> | undefined | FindOptions<T> {

warnFields("Using 'fields' in models is deprecated.", options);

import { IRoom, ISubscription } from '@rocket.chat/core-typings';
import { Rooms } from '@rocket.chat/models';

import { hasPermissionAsync, hasAtLeastOnePermissionAsync } from '../../../authorization/server/functions/hasPermission';
import { Subscriptions } from '../../../models/server';
import { adminFields } from '../../../../lib/rooms/adminFields';

export async function findAdminRooms({
	uid,
	filter,
	types = [],
	pagination: { offset, count, sort },
}: {
	uid: string;
	filter: string;
	types: string[];
	pagination: { offset: number; count: number; sort: [string, number][] };
}): Promise<{
	rooms: IRoom[];
	count: number;
	offset: number;
	total: number;
}> {
	if (!(await hasPermissionAsync(uid, 'view-room-administration'))) {
		throw new Error('error-not-authorized');
	}
	const name = filter?.trim();
	const discussion = types?.includes('discussions');
	const includeTeams = types?.includes('teams');
	const showOnlyTeams = types.length === 1 && types.includes('teams');
	const typesToRemove = ['discussions', 'teams'];
	const showTypes = Array.isArray(types) ? types.filter((type) => !typesToRemove.includes(type)) : [];
	const options = {
		fields: adminFields,
		sort: sort || { default: -1, name: 1 },
		skip: offset,
		limit: count,
	};

	let result;
	if (name && showTypes.length) {
		result = Rooms.findByNameContainingAndTypes(name, showTypes, discussion, includeTeams, showOnlyTeams, options);
	} else if (showTypes.length) {
		result = Rooms.findByTypes(showTypes, discussion, includeTeams, showOnlyTeams, options);
	} else {
		result = Rooms.findByNameContaining(name, discussion, includeTeams, showOnlyTeams, options);
	}

	const { cursor, totalCount } = result;

	const [rooms, total] = await Promise.all([cursor.toArray(), totalCount]);

	return {
		rooms,
		count: rooms.length,
		offset,
		total,
	};
}

export async function findAdminRoom({ uid, rid }: { uid: string; rid: string }): Promise<unknown> {
	if (!(await hasPermissionAsync(uid, 'view-room-administration'))) {
		throw new Error('error-not-authorized');
	}

	return Rooms.findOneById(rid, { fields: adminFields });
}

export async function findChannelAndPrivateAutocomplete({ uid, selector }: { uid: string; selector: { name: string } }): Promise<{
	items: IRoom[];
}> {
	const options = {
		fields: {
			_id: 1,
			fname: 1,
			name: 1,
			t: 1,
			avatarETag: 1,
		},
		limit: 10,
		sort: {
			name: 1,
		},
	};

	const userRoomsIds = Subscriptions.cachedFindByUserId(uid, { fields: { rid: 1 } })
		.fetch()
		.map((item: Pick<ISubscription, 'rid'>) => item.rid);

	const rooms = await Rooms.findRoomsWithoutDiscussionsByRoomIds(selector.name, userRoomsIds, options).toArray();

	return {
		items: rooms,
	};
}

export async function findAdminRoomsAutocomplete({ uid, selector }: { uid: string; selector: { name: string } }): Promise<{
	items: IRoom[];
}> {
	if (!(await hasAtLeastOnePermissionAsync(uid, ['view-room-administration', 'can-audit']))) {
		throw new Error('error-not-authorized');
	}
	const options = {
		fields: {
			_id: 1,
			fname: 1,
			name: 1,
			t: 1,
			avatarETag: 1,
		},
		limit: 10,
		sort: {
			name: 1,
		},
	};

	const rooms = await Rooms.findRoomsByNameOrFnameStarting(selector.name, options).toArray();

	return {
		items: rooms,
	};
}

export async function findChannelAndPrivateAutocompleteWithPagination({
	uid,
	selector,
	pagination: { offset, count, sort },
}: {
	uid: string;
	selector: { name: string };
	pagination: { offset: number; count: number; sort: [string, number][] };
}): Promise<{
	items: IRoom[];
	total: number;
}> {
	const userRoomsIds = Subscriptions.cachedFindByUserId(uid, { fields: { rid: 1 } })
		.fetch()
		.map((item: Pick<ISubscription, 'rid'>) => item.rid);

	const options = {
		fields: {
			_id: 1,
			fname: 1,
			name: 1,
			t: 1,
			avatarETag: 1,
		},
		sort: sort || { name: 1 },
		skip: offset,
		limit: count,
	};

	const { cursor, totalCount } = Rooms.findPaginatedRoomsWithoutDiscussionsByRoomIds(selector.name, userRoomsIds, options);

	const [rooms, total] = await Promise.all([cursor.toArray(), totalCount]);

	return {
		items: rooms,
		total,
	};
}

export async function findRoomsAvailableForTeams({ uid, name }: { uid: string; name: string }): Promise<{
	items: IRoom[];
}> {
	const options = {
		fields: {
			_id: 1,
			fname: 1,
			name: 1,
			t: 1,
			avatarETag: 1,
		},
		limit: 10,
		sort: {
			name: 1,
		},
	};

	const userRooms = (
		Subscriptions.findByUserIdAndRoles(uid, ['owner'], { fields: { rid: 1 } }).fetch() as Pick<ISubscription, 'rid'>[]
	).map((item) => item.rid);

	const rooms = await Rooms.findChannelAndGroupListWithoutTeamsByNameStartingByOwner(uid, name, userRooms, options).toArray();

	return {
		items: rooms,
	};
}


import { ButtonGroup } from '@rocket.chat/fuselage';
import React, { memo, ReactElement, ComponentProps } from 'react';

const VerticalBarActions = (props: ComponentProps<typeof ButtonGroup>): ReactElement => <ButtonGroup medium {...props} />;

export default memo(VerticalBarActions);


import { FlowRouter } from 'meteor/kadira:flow-router';

export const isLayoutEmbedded = (): boolean => FlowRouter.getQueryParam('layout') === 'embedded';


.rc-select {
	position: relative;

	display: flex;

	flex: 0 0 auto;

	height: 2.5rem;

	color: var(--input-text-color);

	border-width: var(--input-border-width);
	border-color: var(--input-border-color);
	border-radius: var(--input-border-radius);
	background-color: transparent;

	font-size: var(--input-font-size);
	align-items: center;
	justify-content: flex-end;

	&__option {
		text-align: right;
	}

	&__arrow {
		position: absolute;

		right: 5px;

		pointer-events: none;

		color: var(--color-gray-light);

		font-size: 20px;

		.rtl & {
			right: auto;
			left: 5px;
		}
	}

	&__element {
		width: 100%;

		height: 100%;
		padding: 0 2rem 0 1rem;

		text-transform: capitalize;

		border: none;
		border-radius: 0;

		font-size: inherit;

		appearance: none;

		&::-ms-expand {
			display: none;
		}
	}

	&--small {
		height: 1.75rem;
	}
}

.rc-select-wrapper {
	display: flex;

	flex: 0 0 auto;

	padding: 0.5rem 0;
	align-items: flex-end;
}

.rtl .rc-select__element {
	padding: 0 1rem 0 2rem;
}


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

import {
	BulkWriteOptions,
	ChangeStream,
	Collection,
	CollectionOptions,
	Db,
	Filter,
	FindOneAndUpdateOptions,
	IndexDescription,
	InsertOneOptions,
	ModifyResult,
	ObjectId,
	OptionalUnlessRequiredId,
	UpdateFilter,
	WithId,
	UpdateOptions,
	Document,
	FindOptions,
	FindCursor,
	UpdateResult,
	InsertManyResult,
	InsertOneResult,
	DeleteResult,
	DeleteOptions,
} from 'mongodb';
import type { IRocketChatRecord, RocketChatRecordDeleted } from '@rocket.chat/core-typings';
import type { IBaseModel, DefaultFields, ResultFields, FindPaginated, InsertionModel } from '@rocket.chat/model-typings';
import { getCollectionName } from '@rocket.chat/models';

import { setUpdatedAt } from '../../../app/models/server/lib/setUpdatedAt';

const warnFields =
	process.env.NODE_ENV !== 'production' || process.env.SHOW_WARNINGS === 'true'
		? (...rest: any): void => {
				console.warn(...rest, new Error().stack);
		  }
		: new Function();

type ModelOptions = {
	preventSetUpdatedAt?: boolean;
	collectionNameResolver?: (name: string) => string;
	collection?: CollectionOptions;
};

export abstract class BaseRaw<T, C extends DefaultFields<T> = undefined> implements IBaseModel<T, C> {
	public readonly defaultFields: C;

	public readonly col: Collection<T>;

	private preventSetUpdatedAt: boolean;

	/**
	 * Collection name to store data.
	 */
	private collectionName: string;

	/**
	 * @param db MongoDB instance
	 * @param name Name of the model without any prefix. Used by trash records to set the `__collection__` field.
	 * @param trash Trash collection instance
	 * @param options Model options
	 */
	constructor(private db: Db, protected name: string, protected trash?: Collection<RocketChatRecordDeleted<T>>, options?: ModelOptions) {
		this.collectionName = options?.collectionNameResolver ? options.collectionNameResolver(name) : getCollectionName(name);

		this.col = this.db.collection(this.collectionName, options?.collection || {});

		const indexes = this.modelIndexes();
		if (indexes?.length) {
			this.col.createIndexes(indexes).catch((e) => {
				console.warn(`Some indexes for collection '${this.collectionName}' could not be created:\n\t${e.message}`);
			});
		}

		this.preventSetUpdatedAt = options?.preventSetUpdatedAt ?? false;
	}

	protected modelIndexes(): IndexDescription[] | void {
		// noop
	}

	private doNotMixInclusionAndExclusionFields(options: FindOptions<T> = {}): FindOptions<T> {
		const optionsDef = this.ensureDefaultFields(options);
		if (optionsDef?.projection === undefined) {
			return optionsDef;
		}

		const projection: Record<string, any> = optionsDef?.projection;
		const keys = Object.keys(projection);
		const removeKeys = keys.filter((key) => projection[key] === 0);
		if (keys.length > removeKeys.length) {
			removeKeys.forEach((key) => delete projection[key]);
		}

		return {
			...optionsDef,
			projection,
		};
	}

	private ensureDefaultFields<P>(options: FindOptions<P>): FindOptions<P>;

	private ensureDefaultFields<P>(options?: any): FindOptions<P> | undefined | FindOptions<T> {
		if (this.defaultFields === undefined) {
			return options;
		}

		const { fields: deprecatedFields, projection, ...rest } = options || {};

		if (deprecatedFields) {
			warnFields("Using 'fields' in models is deprecated.", options);
		}

		const fields = { ...deprecatedFields, ...projection };

		return {
			projection: this.defaultFields,
			...(fields && Object.values(fields).length && { projection: fields }),
			...rest,
		};
	}

	public findOneAndUpdate(query: Filter<T>, update: UpdateFilter<T> | T, options?: FindOneAndUpdateOptions): Promise<ModifyResult<T>> {
		return this.col.findOneAndUpdate(query, update, options || {});
	}

	async findOneById(_id: string, options?: FindOptions<T> | undefined): Promise<T | null>;

	async findOneById<P = T>(_id: string, options?: FindOptions<P>): Promise<P | null>;

	async findOneById(_id: string, options?: any): Promise<T | null> {
		const query = { _id } as unknown as Filter<T>;
		if (options) {
			return this.findOne(query, options);
		}
		return this.findOne(query);
	}

	async findOne(query?: Filter<T> | string, options?: undefined): Promise<T | null>;

	async findOne<P = T>(query: Filter<T> | string, options: FindOptions<P extends T ? T : P>): Promise<P | null>;

	async findOne<P>(query: Filter<T> | string = {}, options?: any): Promise<WithId<T> | WithId<P> | null> {
		const q = typeof query === 'string' ? ({ _id: query } as unknown as Filter<T>) : query;

		const optionsDef = this.doNotMixInclusionAndExclusionFields(options);
		if (optionsDef) {
			return this.col.findOne(q, optionsDef);
		}
		return this.col.findOne(q);
	}

	// findUsersInRoles(): void {
	// 	throw new Error('[overwrite-function] You must overwrite this function in the extended classes');
	// }

	find(query?: Filter<T>): FindCursor<ResultFields<T, C>>;

	find<P = T>(query: Filter<T>, options: FindOptions<P extends T ? T : P>): FindCursor<P>;

	find<P>(query: Filter<T> | undefined = {}, options?: FindOptions<P extends T ? T : P>): FindCursor<WithId<P>> | FindCursor<WithId<T>> {
		const optionsDef = this.doNotMixInclusionAndExclusionFields(options);
		return this.col.find(query, optionsDef);
	}

	findPaginated<P = T>(query: Filter<T>, options?: FindOptions<P extends T ? T : P>): FindPaginated<FindCursor<WithId<P>>>;

	findPaginated(query: Filter<T> | undefined = {}, options?: any): FindPaginated<FindCursor<WithId<T>>> {
		const optionsDef = this.doNotMixInclusionAndExclusionFields(options);

		const cursor = optionsDef ? this.col.find(query, optionsDef) : this.col.find(query);
		const totalCount = this.col.countDocuments(query);

		return {
			cursor,
			totalCount,
		};
	}

	/**
	 * @deprecated use updateOne or updateAny instead
	 */
	update(
		filter: Filter<T>,
		update: UpdateFilter<T> | Partial<T>,
		options?: UpdateOptions & { multi?: true },
	): Promise<UpdateResult | Document> {
		const operation = options?.multi ? 'updateMany' : 'updateOne';

		return this[operation](filter, update, options);
	}

	updateOne(filter: Filter<T>, update: UpdateFilter<T> | Partial<T>, options?: UpdateOptions): Promise<UpdateResult> {
		this.setUpdatedAt(update);
		if (options) {
			return this.col.updateOne(filter, update, options);
		}
		return this.col.updateOne(filter, update);
	}

	updateMany(filter: Filter<T>, update: UpdateFilter<T> | Partial<T>, options?: UpdateOptions): Promise<Document | UpdateResult> {
		this.setUpdatedAt(update);
		if (options) {
			return this.col.updateMany(filter, update, options);
		}
		return this.col.updateMany(filter, update);
	}

	insertMany(docs: InsertionModel<T>[], options?: BulkWriteOptions): Promise<InsertManyResult<T>> {
		docs = docs.map((doc) => {
			if (!doc._id || typeof doc._id !== 'string') {
				const oid = new ObjectId();
				return { _id: oid.toHexString(), ...doc };
			}
			this.setUpdatedAt(doc);
			return doc;
		});

		// TODO reavaluate following type casting
		return this.col.insertMany(docs as unknown as Array<OptionalUnlessRequiredId<T>>, options || {});
	}

	insertOne(doc: InsertionModel<T>, options?: InsertOneOptions): Promise<InsertOneResult<T>> {
		if (!doc._id || typeof doc._id !== 'string') {
			const oid = new ObjectId();
			doc = { _id: oid.toHexString(), ...doc };
		}

		this.setUpdatedAt(doc);

		// TODO reavaluate following type casting
		return this.col.insertOne(doc as unknown as OptionalUnlessRequiredId<T>, options || {});
	}

	removeById(_id: string): Promise<DeleteResult> {
		return this.deleteOne({ _id } as unknown as Filter<T>);
	}

	async deleteOne(filter: Filter<T>, options?: DeleteOptions & { bypassDocumentValidation?: boolean }): Promise<DeleteResult> {
		if (!this.trash) {
			if (options) {
				return this.col.deleteOne(filter, options);
			}
			return this.col.deleteOne(filter);
		}

		const doc = (await this.findOne(filter)) as unknown as (IRocketChatRecord & T) | undefined;

		if (doc) {
			const { _id, ...record } = doc;

			const trash = {
				...record,
				_deletedAt: new Date(),
				__collection__: this.name,
			} as RocketChatRecordDeleted<T>;

			// since the operation is not atomic, we need to make sure that the record is not already deleted/inserted
			await this.trash?.updateOne(
				{ _id } as Filter<RocketChatRecordDeleted<T>>,
				{ $set: trash },
				{
					upsert: true,
				},
			);
		}

		if (options) {
			return this.col.deleteOne(filter, options);
		}
		return this.col.deleteOne(filter);
	}

	async deleteMany(filter: Filter<T>, options?: DeleteOptions): Promise<DeleteResult> {
		if (!this.trash) {
			if (options) {
				return this.col.deleteMany(filter, options);
			}
			return this.col.deleteMany(filter);
		}

		const cursor = this.find(filter);

		const ids: string[] = [];
		for await (const doc of cursor) {
			const { _id, ...record } = doc as unknown as IRocketChatRecord & T;

			const trash = {
				...record,
				_deletedAt: new Date(),
				__collection__: this.name,
			} as RocketChatRecordDeleted<T>;

			ids.push(_id);

			// since the operation is not atomic, we need to make sure that the record is not already deleted/inserted
			await this.trash?.updateOne(
				{ _id } as Filter<RocketChatRecordDeleted<T>>,
				{ $set: trash },
				{
					upsert: true,
				},
			);
		}

		if (options) {
			return this.col.deleteMany({ _id: { $in: ids } } as unknown as Filter<T>, options);
		}
		return this.col.deleteMany({ _id: { $in: ids } } as unknown as Filter<T>);
	}

	// Trash
	trashFind<P extends RocketChatRecordDeleted<T>>(
		query: Filter<RocketChatRecordDeleted<T>>,
		options?: FindOptions<P extends RocketChatRecordDeleted<T> ? RocketChatRecordDeleted<T> : P>,
	): FindCursor<WithId<RocketChatRecordDeleted<T>>> | undefined {
		if (!this.trash) {
			return undefined;
		}
		const { trash } = this;

		if (options) {
			return trash.find(
				{
					__collection__: this.name,
					...query,
				},
				options,
			);
		}
		return trash.find({
			__collection__: this.name,
			...query,
		});
	}

	trashFindOneById(_id: string): Promise<RocketChatRecordDeleted<T> | null>;

	trashFindOneById<P>(
		_id: string,
		options: FindOptions<P extends RocketChatRecordDeleted<T> ? RocketChatRecordDeleted<T> : P>,
	): Promise<P | null>;

	async trashFindOneById<P extends RocketChatRecordDeleted<T>>(
		_id: string,
		options?: FindOptions<P extends RocketChatRecordDeleted<T> ? RocketChatRecordDeleted<T> : P>,
	): Promise<WithId<RocketChatRecordDeleted<P> | RocketChatRecordDeleted<T>> | null> {
		const query = {
			_id,
			__collection__: this.name,
		} as Filter<RocketChatRecordDeleted<T>>;

		if (!this.trash) {
			return null;
		}

		if (options) {
			return this.trash.findOne(query, options);
		}
		return this.trash.findOne(query);
	}

	private setUpdatedAt(record: UpdateFilter<T> | InsertionModel<T>): void {
		if (this.preventSetUpdatedAt) {
			return;
		}
		setUpdatedAt(record);
	}

	trashFindDeletedAfter(deletedAt: Date): FindCursor<WithId<RocketChatRecordDeleted<T>>>;

	trashFindDeletedAfter<P = RocketChatRecordDeleted<T>>(
		deletedAt: Date,
		query?: Filter<RocketChatRecordDeleted<T>>,
		options?: FindOptions<P extends RocketChatRecordDeleted<T> ? RocketChatRecordDeleted<T> : P>,
	): FindCursor<WithId<RocketChatRecordDeleted<T>>> {
		const q = {
			__collection__: this.name,
			_deletedAt: {
				$gt: deletedAt,
			},
			...query,
		} as Filter<RocketChatRecordDeleted<T>>;

		if (!this.trash) {
			throw new Error('Trash is not enabled for this collection');
		}

		if (options) {
			return this.trash.find(q, options);
		}
		return this.trash.find(q);
	}

	trashFindPaginatedDeletedAfter<P = RocketChatRecordDeleted<T>>(
		deletedAt: Date,
		query?: Filter<RocketChatRecordDeleted<T>>,
		options?: FindOptions<P extends RocketChatRecordDeleted<T> ? RocketChatRecordDeleted<T> : P>,
	): FindPaginated<FindCursor<WithId<RocketChatRecordDeleted<T>>>> {
		const q = {
			__collection__: this.name,
			_deletedAt: {
				$gt: deletedAt,
			},
			...query,
		} as Filter<RocketChatRecordDeleted<T>>;

		if (!this.trash) {
			throw new Error('Trash is not enabled for this collection');
		}

		const cursor = options ? this.trash.find(q, options) : this.trash.find(q);
		const totalCount = this.trash.countDocuments(q);

		return {
			cursor,
			totalCount,
		};
	}

	watch(pipeline?: object[]): ChangeStream<T> {
		return this.col.watch(pipeline);
	}
}


import { SlackImporter } from './importer';
import { Importers } from '../../importer/server';
import { SlackImporterInfo } from '../lib/info';

Importers.add(new SlackImporterInfo(), SlackImporter);


function split(parameters: string, ignoreExtraUnquotedSpaces = true): string[] {
	// Replace \n\r with a single \n
	const line = parameters.replace(/\n\r/gm, '\n');

	const items = line.split(/[ \n\r\t]/);

	if (ignoreExtraUnquotedSpaces) {
		return items.filter(Boolean);
	}

	return items;
}

export function parseParameters(parameters: string, ignoreExtraUnquotedSpaces = true): string[] {
	if (!parameters.trim()) {
		return [];
	}

	const match = parameters.match(/((["'])(?:(?=(\\?))\3.)*?\2)/gs);
	let line = parameters;

	if (!match) {
		return split(line, ignoreExtraUnquotedSpaces);
	}

	match.forEach((item) => {
		const newItem = item
			// Replace start quote with SSA character
			.replace(/(^['"])/g, '\u0086')
			// Replace end quote with ESA character
			.replace(/['"]$/g, '\u0087')
			// Replace spaces inside the quotes with a Punctuation Space character
			.replace(/ /g, '\u2008')
			// Replace new lines inside the quotes with a PLD character
			.replace(/\n/gm, '\u008B')
			// Replace carriage returns inside the quotes with a PLU character
			.replace(/\r/gm, '\u008C')
			// Replace tabs inside the quotes with a VTS character
			.replace(/\t/g, '\u008A');

		line = line.replace(item, newItem);
	});

	// If two quoted parameters are not separated by a space, add one automatically
	line = line.replace(/\u0087\u0086/g, '\u0087 \u0086');

	const items = split(line, ignoreExtraUnquotedSpaces);

	return items.map((item) =>
		item
			// Convert back the spaces from inside quotes
			.replace(/\u2008/g, ' ')
			// Convert back the new lines from inside quotes
			.replace(/\u008B/g, '\n')
			// Convert back the carriage returns from inside quotes
			.replace(/\u008C/g, '\r')
			// Convert back the tabs from inside quotes
			.replace(/\u008A/g, '\t')
			// Remove SSA and ESA characters
			.replace(/[\u0086\u0087]/g, '')
			// Unescape quotes
			.replace(/\\\"/g, '"'),
	);
}


export { default } from './PruneMessagesWithData';


const found = await this.findOne(query, options);

keep keep keep keep replace keep keep keep keep keep

import type { FindOptions } from 'mongodb';
import type { IMessage, IUser } from '@rocket.chat/core-typings';
import { Rooms, Messages, Users } from '@rocket.chat/models';

import { canAccessRoomAsync } from '../../../authorization/server/functions/canAccessRoom';
import { getValue } from '../../../settings/server/raw';

export async function findMentionedMessages({
	uid,
	roomId,
	pagination: { offset, count, sort },
}: {
	uid: string;
	roomId: string;
	pagination: { offset: number; count: number; sort: FindOptions<IMessage>['sort'] };
}): Promise<{
	messages: IMessage[];
	count: number;
	offset: number;
	total: number;
}> {
	const room = await Rooms.findOneById(roomId);
	if (!room || !(await canAccessRoomAsync(room, { _id: uid }))) {
		throw new Error('error-not-allowed');
	}
	const user: IUser | null = await Users.findOneById(uid, { fields: { username: 1 } });
	if (!user) {
		throw new Error('invalid-user');
	}

	const { cursor, totalCount } = Messages.findVisibleByMentionAndRoomId(user.username, roomId, {
		sort: sort || { ts: -1 },
		skip: offset,
		limit: count,
	});

	const [messages, total] = await Promise.all([cursor.toArray(), totalCount]);

	return {
		messages,
		count: messages.length,
		offset,
		total,
	};
}

export async function findStarredMessages({
	uid,
	roomId,
	pagination: { offset, count, sort },
}: {
	uid: string;
	roomId: string;
	pagination: { offset: number; count: number; sort: FindOptions<IMessage>['sort'] };
}): Promise<{
	messages: IMessage[];
	count: number;
	offset: any;
	total: number;
}> {
	const room = await Rooms.findOneById(roomId);
	if (!room || !(await canAccessRoomAsync(room, { _id: uid }))) {
		throw new Error('error-not-allowed');
	}
	const user = await Users.findOneById(uid, { fields: { username: 1 } });
	if (!user) {
		throw new Error('invalid-user');
	}

	const { cursor, totalCount } = Messages.findStarredByUserAtRoom(uid, roomId, {
		sort: sort || { ts: -1 },
		skip: offset,
		limit: count,
	});

	const [messages, total] = await Promise.all([cursor.toArray(), totalCount]);

	return {
		messages,
		count: messages.length,
		offset,
		total,
	};
}

export async function findSnippetedMessageById({ uid, messageId }: { uid: string; messageId: string }): Promise<IMessage> {
	if (!(await getValue('Message_AllowSnippeting'))) {
		throw new Error('error-not-allowed');
	}

	if (!uid) {
		throw new Error('invalid-user');
	}

	const snippet = await Messages.findOne({ _id: messageId, snippeted: true });

	if (!snippet) {
		throw new Error('invalid-message');
	}

	const room = await Rooms.findOneById(snippet.rid);

	if (!room) {
		throw new Error('invalid-message');
	}

	if (!(await canAccessRoomAsync(room, { _id: uid }))) {
		throw new Error('error-not-allowed');
	}

	return snippet;
}

export async function findSnippetedMessages({
	uid,
	roomId,
	pagination: { offset, count, sort },
}: {
	uid: string;
	roomId: string;
	pagination: { offset: number; count: number; sort: FindOptions<IMessage>['sort'] };
}): Promise<{
	messages: IMessage[];
	count: number;
	offset: number;
	total: number;
}> {
	if (!(await getValue('Message_AllowSnippeting'))) {
		throw new Error('error-not-allowed');
	}
	const room = await Rooms.findOneById(roomId);

	if (!room || !(await canAccessRoomAsync(room, { _id: uid }))) {
		throw new Error('error-not-allowed');
	}

	const { cursor, totalCount } = Messages.findSnippetedByRoom(roomId, {
		sort: sort || { ts: -1 },
		skip: offset,
		limit: count,
	});

	const [messages, total] = await Promise.all([cursor.toArray(), totalCount]);

	return {
		messages,
		count: messages.length,
		offset,
		total,
	};
}

export async function findDiscussionsFromRoom({
	uid,
	roomId,
	text,
	pagination: { offset, count, sort },
}: {
	uid: string;
	roomId: string;
	text: string;
	pagination: { offset: number; count: number; sort: FindOptions<IMessage>['sort'] };
}): Promise<{
	messages: IMessage[];
	count: number;
	offset: number;
	total: number;
}> {
	const room = await Rooms.findOneById(roomId);

	if (!room || !(await canAccessRoomAsync(room, { _id: uid }))) {
		throw new Error('error-not-allowed');
	}

	const { cursor, totalCount } = await Messages.findDiscussionsByRoomAndText(roomId, text, {
		sort: sort || { ts: -1 },
		skip: offset,
		limit: count,
	});

	const [messages, total] = await Promise.all([cursor.toArray(), totalCount]);

	return {
		messages,
		count: messages.length,
		offset,
		total,
	};
}


import { createContext, useContext } from 'react';

import { AsyncState, AsyncStatePhase } from '../../../lib/asyncState';
import { App } from './types';

type AppsContextValue = {
	installedApps: AsyncState<{ apps: App[] }>;
	marketplaceApps: AsyncState<{ apps: App[] }>;
	reload: () => Promise<void>;
};

export const AppsContext = createContext<AppsContextValue>({
	installedApps: {
		phase: AsyncStatePhase.LOADING,
		value: undefined,
		error: undefined,
	},
	marketplaceApps: {
		phase: AsyncStatePhase.LOADING,
		value: undefined,
		error: undefined,
	},
	reload: () => Promise.resolve(),
});

export const useAppsReload = (): (() => void) => {
	const { reload } = useContext(AppsContext);
	return reload;
};

export const useAppsResult = (): AppsContextValue => useContext(AppsContext);


import { Icon } from '@rocket.chat/fuselage';
import React, { ReactElement, ComponentProps, memo } from 'react';

const VerticalBarIcon = (props: ComponentProps<typeof Icon>): ReactElement => <Icon {...props} pi='x2' size='x24' />;

export default memo(VerticalBarIcon);


export interface IBaseData {
	_id: string;
}


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

@at-root #{selector.append(html, &)}
position: fixed
top: var(--v-body-scroll-y)
left: var(--v-body-scroll-x)
width: 100%
height: 100%

// Utilities
import { convertToUnit, getScrollParents, hasScrollbar, IN_BROWSER, propsFactory } from '@/util'
import { effectScope, nextTick, onScopeDispose, watchEffect } from 'vue'
import { requestNewFrame } from './requestNewFrame'

// Types
import type { EffectScope, PropType, Ref } from 'vue'

export interface ScrollStrategyData {
  root: Ref<HTMLElement | undefined>
  contentEl: Ref<HTMLElement | undefined>
  activatorEl: Ref<HTMLElement | undefined>
  isActive: Ref<boolean>
  updatePosition: Ref<((e: Event) => void) | undefined>
}

const scrollStrategies = {
  none: null,
  close: closeScrollStrategy,
  block: blockScrollStrategy,
  reposition: repositionScrollStrategy,
}

interface StrategyProps {
  scrollStrategy: keyof typeof scrollStrategies | ((data: ScrollStrategyData) => void)
}

export const makeScrollStrategyProps = propsFactory({
  scrollStrategy: {
    type: [String, Function] as PropType<StrategyProps['scrollStrategy']>,
    default: 'block',
    validator: (val: any) => typeof val === 'function' || val in scrollStrategies,
  },
})

export function useScrollStrategies (
  props: StrategyProps,
  data: ScrollStrategyData
) {
  if (!IN_BROWSER) return

  let scope: EffectScope | undefined
  watchEffect(async () => {
    scope?.stop()

    if (!(data.isActive.value && props.scrollStrategy)) return

    scope = effectScope()
    await nextTick()
    scope.run(() => {
      if (typeof props.scrollStrategy === 'function') {
        props.scrollStrategy(data)
      } else {
        scrollStrategies[props.scrollStrategy]?.(data)
      }
    })
  })
}

function closeScrollStrategy (data: ScrollStrategyData) {
  function onScroll (e: Event) {
    data.isActive.value = false
  }

  bindScroll(data.activatorEl.value ?? data.contentEl.value, onScroll)
}

function blockScrollStrategy (data: ScrollStrategyData) {
  const scrollElements = [...new Set([
    ...getScrollParents(data.activatorEl.value),
    ...getScrollParents(data.contentEl.value),
  ])].filter(el => !el.classList.contains('v-overlay-scroll-blocked'))
  const scrollbarWidth = window.innerWidth - document.documentElement.offsetWidth

  const scrollableParent = (el => hasScrollbar(el) && el)(data.root.value?.offsetParent || document.documentElement)
  if (scrollableParent) {
    data.root.value!.classList.add('v-overlay--scroll-blocked')
  }

  scrollElements.forEach((el, i) => {
    if (el === document.documentElement && /iphone|ipad|ipod/i.test(navigator.userAgent)) {
      el.style.setProperty('--v-ios-body-scroll-x', convertToUnit(-el.scrollLeft))
      el.style.setProperty('--v-ios-body-scroll-y', convertToUnit(-el.scrollTop))
    }
    el.style.setProperty('--v-scrollbar-offset', convertToUnit(scrollbarWidth))
    el.classList.add('v-overlay-scroll-blocked')
  })

  onScopeDispose(() => {
    scrollElements.forEach((el, i) => {
      const x = parseFloat(el.style.getPropertyValue('--v-ios-body-scroll-x'))
      const y = parseFloat(el.style.getPropertyValue('--v-ios-body-scroll-y'))

      el.style.removeProperty('--v-ios-body-scroll-x')
      el.style.removeProperty('--v-ios-body-scroll-y')
      el.style.removeProperty('--v-scrollbar-offset')
      el.classList.remove('v-overlay-scroll-blocked')

      el.scrollLeft = -x
      el.scrollTop = -y
    })
    if (scrollableParent) {
      data.root.value!.classList.remove('v-overlay--scroll-blocked')
    }
  })
}

function repositionScrollStrategy (data: ScrollStrategyData) {
  let slow = false
  let raf = -1

  function update (e: Event) {
    requestNewFrame(() => {
      const start = performance.now()
      data.updatePosition.value?.(e)
      const time = performance.now() - start
      slow = time / (1000 / 60) > 2
    })
  }

  bindScroll(data.activatorEl.value ?? data.contentEl.value, e => {
    if (slow) {
      // If the position calculation is slow,
      // defer updates until scrolling is finished.
      // Browsers usually fire one scroll event per frame so
      // we just wait until we've got two frames without an event
      cancelAnimationFrame(raf)
      raf = requestAnimationFrame(() => {
        raf = requestAnimationFrame(() => {
          update(e)
        })
      })
    } else {
      update(e)
    }
  })
}

/** @private */
function bindScroll (el: HTMLElement | undefined, onScroll: (e: Event) => void) {
  const scrollElements = [document, ...getScrollParents(el)]
  scrollElements.forEach(el => {
    el.addEventListener('scroll', onScroll, { passive: true })
  })

  onScopeDispose(() => {
    scrollElements.forEach(el => {
      el.removeEventListener('scroll', onScroll)
    })
  })
}


@forward './variables'
@use 'sass:list'
@use '../../styles/settings'
@use './variables' as *

// Elements
.v-progress-circular
  align-items: center
  display: inline-flex
  justify-content: center
  position: relative
  vertical-align: middle

  > svg
    width: 100%
    height: 100%
    margin: auto
    position: absolute
    top: 0
    bottom: 0
    left: 0
    right: 0
    z-index: 0

.v-progress-circular__content
  align-items: center
  display: flex
  justify-content: center

.v-progress-circular__underlay
  color: $progress-circular-underlay-color
  stroke: currentColor
  z-index: 1

.v-progress-circular__overlay
  stroke: currentColor
  transition: $progress-circular-overlay-transition
  z-index: 2

// Modifiers
.v-progress-circular
  @each $name, $multiplier in $progress-circular-sizes
    $size: $progress-circular-size + (settings.$size-scale * $multiplier)

    &--size-#{$name}
      height: $size
      width: $size

.v-progress-circular--indeterminate
  > svg
    animation: $progress-circular-rotate-animation
    transform-origin: center center
    transition: $progress-circular-intermediate-svg-transition

  .v-progress-circular__overlay
    animation: $progress-circular-rotate-dash
    stroke-dasharray: 25, 200
    stroke-dashoffset: 0
    stroke-linecap: round

.v-progress-circular--disable-shrink
  > svg
    animation-duration: list.nth($progress-circular-rotate-animation, 2) * .5

  .v-progress-circular__overlay
    animation: none

.v-progress-circular--indeterminate:not(.v-progress-circular--visible)
  > svg,
  .v-progress-circular__overlay
    animation-play-state: paused !important

@keyframes progress-circular-dash
  0%
    stroke-dasharray: 1, 200
    stroke-dashoffset: 0px

  50%
    stroke-dasharray: 100, 200
    stroke-dashoffset: -15px

  100%
    stroke-dasharray: 100, 200
    stroke-dashoffset: -124px

@keyframes progress-circular-rotate
  100%
    transform: rotate(270deg)


---
nav: Item groups
meta:
  title: Item group component
  description: The item group components provides the ability to create a group of selectable items out of any component.
  keywords: item groups, vuetify item group component, vue item group component
related:
  - /components/button-groups
  - /components/carousels
  - /components/tabs
---

# Item groups

The `v-item-group` provides the ability to create a group of selectable items out of any component. This is the baseline functionality for components such as `v-tabs` and `v-carousel`.

<entry />

## Usage

The core usage of the `v-item-group` is to create groups of anything that should be controlled by a **model**.

<!-- <example file="v-item-group/usage" /> -->

## API

<api-inline />

<!-- ## Sub-components

### v-item

v-item description -->

## Examples

### Props

#### Selected class

The **selected-class** prop allows you to designate a CSS class applied to _selected_ items.

<example file="v-item-group/prop-selected-class" />

#### Mandatory

**mandatory** item groups must have at least 1 item selected.

<example file="v-item-group/prop-mandatory" />

#### Multiple

Item groups can have **multiple** items selected.

<example file="v-item-group/prop-multiple" />

### Misc

#### Chips

Easily hook up a custom chip group.

<example file="v-item-group/misc-chips" />

#### Selection

Icons can be used as toggle buttons when they allow selection, or deselection, of a single choice, such as marking an item as a favorite.

<example file="v-item-group/misc-selection" />

<backmatter />


import { createSimpleFunctional } from '@/util'

export const VListItemTitle = createSimpleFunctional('v-list-item-title')


if (el === document.documentElement && /iphone|ipad|ipod/i.test(navigator.userAgent)) {

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


<template>
  <v-container fluid>
    <v-checkbox v-model="checkbox">
      <template v-slot:label>
        <div>
          I agree that
          <v-tooltip bottom>
            <template v-slot:activator="{ on }">
              <a
                target="_blank"
                href="https://vuetifyjs.com"
                @click.stop
                v-on="on"
              >
                Vuetify
              </a>
            </template>
            Opens in new window
          </v-tooltip>
          is awesome
        </div>
      </template>
    </v-checkbox>
  </v-container>
</template>

<script>
  export default {
    data () {
      return {
        checkbox: false,
      }
    },
  }
</script>


// @ts-nocheck
/* eslint-disable */

// import Row from '../Row'
import {
  mount,
  Wrapper,
  MountOptions,
} from '@vue/test-utils'
// import Vue from 'vue'

describe.skip('Table Row', () => {
  type Instance = InstanceType<typeof Row>
  let mountFunction: (options?: MountOptions<Instance>) => Wrapper<Instance>
  beforeEach(() => {
    mountFunction = (options?: MountOptions<Instance>) => {
      return mount(Row, options)
    }
  })

  it('should render without slots', () => {
    const wrapper = mountFunction({
      context: {
        props: {
          headers: [
            { text: 'Petrol', value: 'petrol' },
            { text: 'Diesel', value: 'diesel' },
          ],
          item: {
            petrol: 0.68,
            diesel: 0.65,
          },
        },
      },
    })

    expect(wrapper.findAll('tr')).toHaveLength(1)
    expect(wrapper.findAll('td')).toHaveLength(2)
    expect(wrapper.html()).toMatchSnapshot()
  })

  it('should render non-string values', () => {
    const wrapper = mountFunction({
      context: {
        props: {
          headers: [
            { value: 'string' },
            { value: 'number' },
            { value: 'array' },
            { value: 'boolean' },
            { value: 'object' },
            { value: 'undefined' },
            { value: 'null' },
          ],
          item: {
            string: 'string',
            number: 12.34,
            array: [1, 2],
            boolean: false,
            object: { foo: 'bar' },
            null: null,
          },
        },
      },
    })

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

  it('should render with cellClass', () => {
    const wrapper = mountFunction({
      context: {
        props: {
          headers: [
            { text: 'Petrol', value: 'petrol', cellClass: 'a' },
            { text: 'Diesel', value: 'diesel', cellClass: ['b', 'c'] },
          ],
          item: {
            petrol: 0.68,
            diesel: 0.65,
          },
        },
      },
    })

    const tds = wrapper.findAll('td')
    expect(tds.at(0).classes()).toContain('a')
    expect(tds.at(1).classes()).toContain('b')
    expect(tds.at(1).classes()).toContain('c')
    expect(wrapper.html()).toMatchSnapshot()
  })

  it.skip('should render with regular slots', () => {
    const wrapper = mountFunction({
      context: {
        props: {
          headers: [
            { text: 'Petrol', value: 'petrol' },
            { text: 'Diesel', value: 'diesel' },
          ],
        },
      },
      slots: {
        'column.petrol': '
$0.68
',
        'column.diesel': '
$0.65
',
      },
    })

    expect(wrapper.findAll('tr')).toHaveLength(1)
    expect(wrapper.findAll('td')).toHaveLength(2)
    expect(wrapper.findAll('p.test')).toHaveLength(2)
    expect(wrapper.html()).toMatchSnapshot()
  })

  it.skip('should render with scoped slots', () => {
    const vm = new Vue()
    const wrapper = mountFunction({
      context: {
        props: {
          headers: [
            { text: 'Petrol', value: 'petrol' },
            { text: 'Diesel', value: 'diesel' },
          ],
          item: {
            petrol: 0.68,
            diesel: 0.65,
          },
        },
      },
      scopedSlots: {
        'column.petrol': props => vm.$createElement('p', { staticClass: `test ${props.header.value}` }, [props.value]),
        'column.diesel': props => vm.$createElement('p', { staticClass: `test ${props.header.value}` }, [props.value]),
      },
    })

    expect(wrapper.findAll('tr')).toHaveLength(1)
    expect(wrapper.findAll('td')).toHaveLength(2)
    expect(wrapper.findAll('p.test')).toHaveLength(2)
    expect(wrapper.html()).toMatchSnapshot()
  })
})


---
nav: Ratings
meta:
  title: Rating component
  description: The star rating component is a specialized widget for collecting user feedback via ratings.
  keywords: star ratings, vuetify star rating component, vue star rating component, rating component
related:
  - /components/cards
  - /components/icons
  - /components/lists
---

# Ratings

The `v-rating` component is a specialized but important piece in building user widgets. Collecting user feedback via ratings is a simple analytic that can provide a lot of feedback to your product or application.

<entry />

## Usage

The `v-rating` component provides a simple interface for gathering user feedback.

<!-- <usage name="v-rating" /> -->

## API

<api-inline />

## Examples

### Props

#### Color

The `v-rating` component can be colored as you want, you can set both selected and not selected colors.

<example file="v-rating/prop-color" />

#### Density

Control the space occupied by `v-rating` items using the **density** prop.

<example file="v-rating/prop-density" />

#### Clearable

Clicking on a current rating value can reset the rating by using **clearable** prop.

<example file="v-rating/prop-clearable" />

#### Readonly

For ratings that are not meant to be changed you can use **readonly** prop.

<example file="v-rating/prop-readonly" />

#### Hover effect

TODO

<example file="v-rating/prop-hover" />

#### Labels

The `v-rating` component can display labels above or below each item.

<example file="v-rating/prop-item-labels" />

#### Icons

You can use custom icons.

<example file="v-rating/prop-icons" />

#### Length

Change the number of items by modifying the the **length** prop.

<example file="v-rating/prop-length" />

#### Half increments

The **half-increments** prop increases the granularity of the ratings, allow for `.5` values as well.

<example file="v-rating/prop-half-increments" />

#### Size

Utilize the same sizing classes available in `v-icon` or provide your own with the **size** prop.

<example file="v-rating/prop-size" />

#### Aria Label

Provide a label to assistive technologies for each item.

<example file="v-rating/prop-icon-label" />

### Slots

#### Item slot

Slots enable advanced customization possibilities and provide you with more freedom in how you display the rating.

<example file="v-rating/slot-item" />

#### Custom labels slot

Any arbitrary content could be displayed for labels in **item-label** slot.

<example file="v-rating/slot-item-label" />

### Misc

<!-- #### Advanced usage

The `v-rating` component fits right in with existing components. Build truly complex examples with rich features and beautiful designs.

<example file="v-rating/misc-advanced" /> -->

#### Card ratings

The rating component pairs well with products allowing you to gather and display customer feedback.

<example file="v-rating/misc-card" />

<backmatter />


const x = parseFloat(el.style.getPropertyValue('--v-ios-body-scroll-x'))

const y = parseFloat(el.style.getPropertyValue('--v-ios-body-scroll-y'))

const x = parseFloat(el.style.getPropertyValue('--v-body-scroll-x'))

const y = parseFloat(el.style.getPropertyValue('--v-body-scroll-y'))


import type { DirectiveBinding } from 'vue'

interface ResizeDirectiveBinding extends Omit<DirectiveBinding, 'modifiers'> {
  value: () => void
  modifiers?: {
    active?: boolean
    quiet?: boolean
  }
}

function mounted (el: HTMLElement, binding: ResizeDirectiveBinding) {
  const handler = binding.value
  const options: AddEventListenerOptions = {
    passive: !binding.modifiers?.active,
  }

  window.addEventListener('resize', handler, options)

  el._onResize = Object(el._onResize)
  el._onResize![binding.instance!.$.uid] = {
    handler,
    options,
  }

  if (!binding.modifiers?.quiet) {
    handler()
  }
}

function unmounted (el: HTMLElement, binding: ResizeDirectiveBinding) {
  if (!el._onResize?.[binding.instance!.$.uid]) return

  const { handler, options } = el._onResize[binding.instance!.$.uid]!

  window.removeEventListener('resize', handler, options)

  delete el._onResize[binding.instance!.$.uid]
}

export const Resize = {
  mounted,
  unmounted,
}

export default Resize


// Styles
import './VLayoutItem.sass'

// Composables
import { makeLayoutItemProps, useLayoutItem } from '@/composables/layout'

// Utilities
import { computed, toRef } from 'vue'
import { defineComponent } from '@/util'

// Types
import type { PropType } from 'vue'

export const VLayoutItem = defineComponent({
  name: 'VLayoutItem',

  props: {
    position: {
      type: String as PropType<'top' | 'right' | 'bottom' | 'left'>,
      required: true,
    },
    size: {
      type: [Number, String],
      default: 300,
    },
    modelValue: Boolean,
    ...makeLayoutItemProps(),
  },

  setup (props, { slots }) {
    const { layoutItemStyles } = useLayoutItem({
      id: props.name,
      priority: computed(() => parseInt(props.priority, 10)),
      position: toRef(props, 'position'),
      elementSize: toRef(props, 'size'),
      layoutSize: toRef(props, 'size'),
      active: toRef(props, 'modelValue'),
      absolute: toRef(props, 'absolute'),
    })

    return () => (
      <div
        class={[
          'v-layout-item',
        ]}
        style={ layoutItemStyles.value }
      >
        { slots.default?.() }
      </div>
    )
  },
})


import { describe, expect, it } from '@jest/globals'

// Directives
import Resize from '../'

const instance = {
  $: { uid: 1 },
}

describe('v-resize', () => {
  it('should bind event on inserted', () => {
    const callback = jest.fn()

    jest.spyOn(window, 'addEventListener')
    jest.spyOn(window, 'removeEventListener')

    const el = {}

    Resize.mounted!(el as HTMLElement, { value: callback, instance } as any)
    expect(callback).toHaveBeenCalled()
    expect(window.addEventListener).toHaveBeenCalledWith('resize', callback, { passive: true })

    Resize.unmounted!(el as HTMLElement, { value: callback, instance } as any)
    expect(window.removeEventListener).toHaveBeenCalledWith('resize', callback, { passive: true })

    ;(window.addEventListener as jest.Mock).mockClear()
    ;(window.removeEventListener as jest.Mock).mockClear()
  })

  it('should not run the callback in quiet mode', () => {
    const callback = jest.fn()

    jest.spyOn(window, 'addEventListener')
    jest.spyOn(window, 'removeEventListener')

    const el = {}

    Resize.mounted!(el as HTMLElement, { value: callback, modifiers: { quiet: true }, instance } as any)
    expect(callback).not.toHaveBeenCalled()
    expect(window.addEventListener).toHaveBeenCalledWith('resize', callback, { passive: true })

    Resize.unmounted!(el as HTMLElement, { value: callback, modifiers: { quiet: true }, instance } as any)
    expect(window.removeEventListener).toHaveBeenCalledWith('resize', callback, { passive: true })

    ;(window.addEventListener as jest.Mock).mockClear()
    ;(window.removeEventListener as jest.Mock).mockClear()
  })
})


keep replace replace keep keep keep keep keep


<template>
  <v-card
    class="mx-auto"
    max-width="300"
  >
    <v-list shaped>
      <v-list-subheader>REPORTS</v-list-subheader>
      <v-list-item
        v-for="(item, i) in items"
        :key="i"
        :value="item"
        active-color="primary"
        rounded="shaped"
      >
        <v-list-item-avatar start>
          <v-icon :icon="item.icon"></v-icon>
        </v-list-item-avatar>
        <v-list-item-title v-text="item.text"></v-list-item-title>
      </v-list-item>
    </v-list>
  </v-card>
</template>

<script>
  export default {
    data: () => ({
      selectedItem: 1,
      items: [
        { text: 'Real-Time', icon: 'mdi-clock' },
        { text: 'Audience', icon: 'mdi-account' },
        { text: 'Conversions', icon: 'mdi-flag' },
      ],
    }),
  }
</script>


<template>

    <v-alert
      rounded="0"
      type="info"
      title="Info"
    >
      I'm an alert with no rounded borders. Lorem ipsum dolor sit, amet consectetur adipisicing elit. Aspernatur recusandae, est itaque laboriosam amet officia? Officia repellat provident sed est adipisci voluptatibus, voluptas reprehenderit dicta voluptatum, optio enim, placeat nostrum.
    </v-alert>

    <v-alert
      rounded="xl"
      variant="outlined"
      title="Success"
      type="success"
    >
      I'm an alert with extra large rounded borders. Lorem ipsum dolor, sit amet consectetur adipisicing elit. Enim cumque vel sapiente suscipit officia ullam quam possimus provident id neque, cupiditate fuga animi expedita, beatae ipsam in veniam inventore totam.
    </v-alert>

    <v-alert
      title="Warning"
      rounded="pill"
      type="warning"
      prominent
    >
      I'm an alert with pill rounded borders. Lorem ipsum dolor sit amet consectetur adipisicing elit. Laudantium ex excepturi ea odio cum libero animi vitae repellat fuga velit explicabo quae, ducimus.
    </v-alert>

    <v-alert
      title="Error"
      prominent
      rounded="t-xl b-lg"
      type="error"
    >
      I'm an alert with top and bottom borders. Lorem, ipsum dolor sit amet consectetur adipisicing elit. Rerum esse quis eius delectus odio repellat voluptates ullam doloribus eaque dignissimos
    </v-alert>

</template>


<template>
  <v-row justify="center">
    <v-col>
      <v-time-picker
        v-model="e7"
        format="24hr"
      ></v-time-picker>
    </v-col>
  </v-row>
</template>

<script>
  export default {
    data () {
      return {
        e7: null,
      }
    },
  }
</script>


import { VThemeProvider } from '../VThemeProvider'

// Styles
import './VCarousel.sass'

// Extensions
import VWindow from '../VWindow/VWindow'

// Components
import VBtn from '../VBtn'
import VIcon from '../VIcon'
import VProgressLinear from '../VProgressLinear'

// Mixins
// TODO: Move this into core components v2.0
import ButtonGroup from '../../mixins/button-group'

// Utilities
import { convertToUnit } from '../../util/helpers'
import { breaking } from '../../util/console'

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

export default VWindow.extend({
  name: 'v-carousel',

  props: {
    continuous: {
      type: Boolean,
      default: true,
    },
    cycle: Boolean,
    delimiterIcon: {
      type: String,
      default: '$delimiter',
    },
    height: {
      type: [Number, String],
      default: 500,
    },
    hideDelimiters: Boolean,
    hideDelimiterBackground: Boolean,
    interval: {
      type: [Number, String],
      default: 6000,
      validator: (value: string | number) => value > 0,
    },
    mandatory: {
      type: Boolean,
      default: true,
    },
    progress: Boolean,
    progressColor: String,
    showArrows: {
      type: Boolean,
      default: true,
    },
    verticalDelimiters: {
      type: String as PropType<'' | 'left' | 'right'>,
      default: undefined,
    },
  },

  data () {
    return {
      internalHeight: this.height,
      slideTimeout: undefined as number | undefined,
    }
  },

  computed: {
    classes (): object {
      return {
        ...VWindow.options.computed.classes.call(this),
        'v-carousel': true,
        'v-carousel--hide-delimiter-background': this.hideDelimiterBackground,
        'v-carousel--vertical-delimiters': this.isVertical,
      }
    },
    isDark (): boolean {
      return this.dark || !this.light
    },
    isVertical (): boolean {
      return this.verticalDelimiters != null
    },
  },

  watch: {
    internalValue: 'restartTimeout',
    interval: 'restartTimeout',
    height (val, oldVal) {
      if (val === oldVal || !val) return
      this.internalHeight = val
    },
    cycle (val) {
      if (val) {
        this.restartTimeout()
      } else {
        clearTimeout(this.slideTimeout)
        this.slideTimeout = undefined
      }
    },
  },

  created () {
    /* istanbul ignore next */
    if (this.$attrs.hasOwnProperty('hide-controls')) {
      breaking('hide-controls', ':show-arrows="false"', this)
    }
  },

  mounted () {
    this.startTimeout()
  },

  methods: {
    genControlIcons () {
      if (this.isVertical) return null

      return VWindow.options.methods.genControlIcons.call(this)
    },
    genDelimiters (): VNode {
      return this.$createElement('div', {
        staticClass: 'v-carousel__controls',
        style: {
          left: this.verticalDelimiters === 'left' && this.isVertical ? 0 : 'auto',
          right: this.verticalDelimiters === 'right' ? 0 : 'auto',
        },
      }, [this.genItems()])
    },
    genItems (): VNode {
      const length = this.items.length
      const children = []

      for (let i = 0; i < length; i++) {
        const child = this.$createElement(VBtn, {
          staticClass: 'v-carousel__controls__item',
          attrs: {
            'aria-label': this.$vuetify.lang.t('$vuetify.carousel.ariaLabel.delimiter', i + 1, length),
          },
          props: {
            icon: true,
            small: true,
            value: this.getValue(this.items[i], i),
          },
        }, [
          this.$createElement(VIcon, {
            props: { size: 18 },
          }, this.delimiterIcon),
        ])

        children.push(child)
      }

      return this.$createElement(ButtonGroup, {
        props: {
          value: this.internalValue,
          mandatory: this.mandatory,
        },
        on: {
          change: (val: unknown) => {
            this.internalValue = val
          },
        },
      }, children)
    },
    genProgress () {
      return this.$createElement(VProgressLinear, {
        staticClass: 'v-carousel__progress',
        props: {
          color: this.progressColor,
          value: (this.internalIndex + 1) / this.items.length * 100,
        },
      })
    },
    restartTimeout () {
      this.slideTimeout && clearTimeout(this.slideTimeout)
      this.slideTimeout = undefined

      window.requestAnimationFrame(this.startTimeout)
    },
    startTimeout () {
      if (!this.cycle) return

      this.slideTimeout = window.setTimeout(this.next, +this.interval > 0 ? +this.interval : 6000)
    },
  },

  render (h): VNode {
    const render = VWindow.options.render.call(this, h)

    render.data!.style = `height: ${convertToUnit(this.height)};`

    /* istanbul ignore else */
    if (!this.hideDelimiters) {
      render.children!.push(this.genDelimiters())
    }

    /* istanbul ignore else */
    if (this.progress || this.progressColor) {
      render.children!.push(this.genProgress())
    }

    return render
  },
})


<template>
  <v-card class="d-inline-block mx-auto">
    <v-container>
      <v-row justify="space-between">
        <v-col cols="auto">
          <v-img
            height="200"
            width="200"
            src="https://cdn.vuetifyjs.com/images/cards/store.jpg"
          ></v-img>
        </v-col>

        <v-col
          cols="auto"
          class="text-center pl-0"
        >
          <v-row
            class="flex-column ma-0 fill-height"
            justify="center"
          >
            <v-col class="px-0">
              <v-btn icon>
                <v-icon>mdi-heart</v-icon>
              </v-btn>
            </v-col>

            <v-col class="px-0">
              <v-btn icon>
                <v-icon>mdi-bookmark</v-icon>
              </v-btn>
            </v-col>

            <v-col class="px-0">
              <v-btn icon>
                <v-icon>mdi-share-variant</v-icon>
              </v-btn>
            </v-col>
          </v-row>
        </v-col>
      </v-row>
    </v-container>
  </v-card>
</template>

<script>
  export default {}
</script>


<template>
  <v-card
    flat
    tile
  >
    <v-window
      v-model="onboarding"
      reverse
    >
      <v-window-item
        v-for="n in length"
        :key="`card-${n}`"
      >
        <v-card
          color="grey"
          height="200"
        >
          <v-row
            class="fill-height"
            align="center"
            justify="center"
          >
            <h1
              style="font-size: 5rem;"
              class="white--text"
            >
              Slide {{ n }}
            </h1>
          </v-row>
        </v-card>
      </v-window-item>
    </v-window>

    <v-card-actions class="justify-space-between">
      <v-btn
        text
        @click="prev"
      >
        <v-icon>mdi-chevron-left</v-icon>
      </v-btn>
      <v-item-group
        v-model="onboarding"
        class="text-center"
        mandatory
      >
        <v-item
          v-for="n in length"
          :key="`btn-${n}`"
          v-slot="{ active, toggle }"
        >
          <v-btn
            :input-value="active"
            icon
            @click="toggle"
          >
            <v-icon>mdi-record</v-icon>
          </v-btn>
        </v-item>
      </v-item-group>
      <v-btn
        text
        @click="next"
      >
        <v-icon>mdi-chevron-right</v-icon>
      </v-btn>
    </v-card-actions>
  </v-card>
</template>

<script>
  export default {
    data: () => ({
      length: 3,
      onboarding: 0,
    }),

    methods: {
      next () {
        this.onboarding = this.onboarding + 1 === this.length
          ? 0
          : this.onboarding + 1
      },
      prev () {
        this.onboarding = this.onboarding - 1 < 0
          ? this.length - 1
          : this.onboarding - 1
      },
    },
  }
</script>


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

exports[`VDataTable/header.ts should generate select 1`] = `

        $checkboxOff

`;

exports[`VDataTable/header.ts should generate select 2`] = `

        $checkboxOn

`;

exports[`VDataTable/header.ts should generate select 3`] = `

        $checkboxIndeterminate

`;

exports[`VDataTable/header.ts should generate select scoped slot 1`] = `

    {"props":{"value":false,"indeterminate":false},"on":{}}

`;

exports[`VDataTable/header.ts should generate select scoped slot 2`] = `

    {"props":{"value":true,"indeterminate":false},"on":{}}

`;

exports[`VDataTable/header.ts should generate select scoped slot 3`] = `

    {"props":{"value":false,"indeterminate":true},"on":{}}

`;

exports[`VDataTable/header.ts should generate sort icon 1`] = `

`;


keep keep keep keep add keep keep keep keep keep

return this.$slots.default?.map(item => {

// Styles
import './VWindow.sass'

// Types
import { VNode, VNodeDirective } from 'vue/types/vnode'
import { PropType } from 'vue'
import { TouchHandlers } from 'vuetify/types'

// Directives
import Touch from '../../directives/touch'

// Components
import VBtn from '../VBtn'
import VIcon from '../VIcon'
import { BaseItemGroup } from '../VItemGroup/VItemGroup'

/* @vue/component */
export default BaseItemGroup.extend({
  name: 'v-window',

  directives: { Touch },

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

  props: {
    activeClass: {
      type: String,
      default: 'v-window-item--active',
    },
    continuous: Boolean,
    mandatory: {
      type: Boolean,
      default: true,
    },
    nextIcon: {
      type: [Boolean, String],
      default: '$next',
    },
    prevIcon: {
      type: [Boolean, String],
      default: '$prev',
    },
    reverse: Boolean,
    showArrows: Boolean,
    showArrowsOnHover: Boolean,
    touch: Object as PropType<TouchHandlers>,
    touchless: Boolean,
    value: {
      required: false,
    },
    vertical: Boolean,
  },

  data () {
    return {
      changedByDelimiters: false,
      internalHeight: undefined as undefined | string, // This can be fixed by child class.
      transitionHeight: undefined as undefined | string, // Intermediate height during transition.
      transitionCount: 0, // Number of windows in transition state.
      isBooted: false,
      isReverse: false,
    }
  },

  computed: {
    isActive (): boolean {
      return this.transitionCount > 0
    },
    classes (): object {
      return {
        ...BaseItemGroup.options.computed.classes.call(this),
        'v-window--show-arrows-on-hover': this.showArrowsOnHover,
      }
    },
    computedTransition (): string {
      if (!this.isBooted) return ''

      const axis = this.vertical ? 'y' : 'x'
      const reverse = this.internalReverse ? !this.isReverse : this.isReverse
      const direction = reverse ? '-reverse' : ''

      return `v-window-${axis}${direction}-transition`
    },
    hasActiveItems (): boolean {
      return Boolean(
        this.items.find(item => !item.disabled)
      )
    },
    hasNext (): boolean {
      return this.continuous || this.internalIndex < this.items.length - 1
    },
    hasPrev (): boolean {
      return this.continuous || this.internalIndex > 0
    },
    internalIndex (): number {
      return this.items.findIndex((item, i) => {
        return this.internalValue === this.getValue(item, i)
      })
    },
    internalReverse (): boolean {
      return this.$vuetify.rtl ? !this.reverse : this.reverse
    },
  },

  watch: {
    internalIndex (val, oldVal) {
      this.isReverse = this.updateReverse(val, oldVal)
    },
  },

  mounted () {
    window.requestAnimationFrame(() => (this.isBooted = true))
  },

  methods: {
    genContainer (): VNode {
      const children = [this.$slots.default]

      if (this.showArrows) {
        children.push(this.genControlIcons())
      }

      return this.$createElement('div', {
        staticClass: 'v-window__container',
        class: {
          'v-window__container--is-active': this.isActive,
        },
        style: {
          height: this.internalHeight || this.transitionHeight,
        },
      }, children)
    },
    genIcon (
      direction: 'prev' | 'next',
      icon: string,
      click: () => void
    ) {
      const on = {
        click: (e: Event) => {
          e.stopPropagation()
          this.changedByDelimiters = true
          click()
        },
      }
      const attrs = {
        'aria-label': this.$vuetify.lang.t(`$vuetify.carousel.${direction}`),
      }
      const children = this.$scopedSlots[direction]?.({
        on,
        attrs,
      }) ?? [this.$createElement(VBtn, {
        props: { icon: true },
        attrs,
        on,
      }, [
        this.$createElement(VIcon, {
          props: { large: true },
        }, icon),
      ])]

      return this.$createElement('div', {
        staticClass: `v-window__${direction}`,
      }, children)
    },
    genControlIcons () {
      const icons = []

      const prevIcon = this.$vuetify.rtl
        ? this.nextIcon
        : this.prevIcon

      /* istanbul ignore else */
      if (
        this.hasPrev &&
        prevIcon &&
        typeof prevIcon === 'string'
      ) {
        const icon = this.genIcon('prev', prevIcon, this.prev)
        icon && icons.push(icon)
      }

      const nextIcon = this.$vuetify.rtl
        ? this.prevIcon
        : this.nextIcon

      /* istanbul ignore else */
      if (
        this.hasNext &&
        nextIcon &&
        typeof nextIcon === 'string'
      ) {
        const icon = this.genIcon('next', nextIcon, this.next)
        icon && icons.push(icon)
      }

      return icons
    },
    getNextIndex (index: number): number {
      const nextIndex = (index + 1) % this.items.length
      const item = this.items[nextIndex]

      if (item.disabled) return this.getNextIndex(nextIndex)

      return nextIndex
    },
    getPrevIndex (index: number): number {
      const prevIndex = (index + this.items.length - 1) % this.items.length
      const item = this.items[prevIndex]

      if (item.disabled) return this.getPrevIndex(prevIndex)

      return prevIndex
    },
    next () {
      /* istanbul ignore if */
      if (!this.hasActiveItems || !this.hasNext) return

      const nextIndex = this.getNextIndex(this.internalIndex)
      const item = this.items[nextIndex]

      this.internalValue = this.getValue(item, nextIndex)
    },
    prev () {
      /* istanbul ignore if */
      if (!this.hasActiveItems || !this.hasPrev) return

      const lastIndex = this.getPrevIndex(this.internalIndex)
      const item = this.items[lastIndex]

      this.internalValue = this.getValue(item, lastIndex)
    },
    updateReverse (val: number, oldVal: number) {
      const itemsLength = this.items.length
      const lastIndex = itemsLength - 1

      if (itemsLength <= 2) return val < oldVal

      if (val === lastIndex && oldVal === 0) {
        return true
      } else if (val === 0 && oldVal === lastIndex) {
        return false
      } else {
        return val < oldVal
      }
    },
  },

  render (h): VNode {
    const data = {
      staticClass: 'v-window',
      class: this.classes,
      directives: [] as VNodeDirective[],
    }

    if (!this.touchless) {
      const value = this.touch || {
        left: () => {
          this.$vuetify.rtl ? this.prev() : this.next()
        },
        right: () => {
          this.$vuetify.rtl ? this.next() : this.prev()
        },
        end: (e: TouchEvent) => {
          e.stopPropagation()
        },
        start: (e: TouchEvent) => {
          e.stopPropagation()
        },
      }

      data.directives.push({
        name: 'touch',
        value,
      })
    }

    return h('div', data, [this.genContainer()])
  },
})


<template>
  <v-divider
    v-bind="$attrs"
    v-on="$listeners"
  ></v-divider>
</template>

<script>
  export default {
    name: 'Usage',

    inheritAttrs: false,

    data: () => ({
      defaults: {
        inset: false,
        vertical: false,
      },
      options: {
        booleans: ['inset', 'vertical'],
      },
      tabs: [],
    }),
  }
</script>


---
meta:
  title: Data table component
  description: The data table component is used for displaying tabular data in a way that is easy for users to scan. It includes sorting, searching, pagination and selection.
  keywords: data tables, vuetify data table component, vue data table component
related:
  - /components/data-iterators/
  - /components/simple-tables/
  - /components/lists/
---

# Data tables

The `v-data-table` component is used for displaying tabular data. Features include sorting, searching, pagination, content-editing, and row selection.

<promoted-ad slug="vuetify-lux-admin-pro" />

## Usage

The standard data-table will by default render your data as simple rows.

<example file="v-data-table/usage" />

## API

- [v-data-table](/api/v-data-table)
- [v-data-table-header](/api/v-data-table-header)
- [v-data-footer](/api/v-data-footer)
- [v-edit-dialog](/api/v-edit-dialog)
- [v-simple-checkbox](/api/v-simple-checkbox)

<!-- ## Sub-components

### v-data-table-header

v-data-table-header description

### v-data-footer

v-data-footer description

### v-edit-dialog

v-edit-dialog description

### v-simple-checkbox

v-simple-checkbox description -->

## Examples

### Props

#### Custom filter

You can override the default filtering used with **search** prop by supplying a function to the **custom-filter** prop. If you need to customize the filtering of a specific column, you can supply a function to the **filter** property on header items. The signature is `(value: any, search: string | null, item: any) => boolean`. This function will always be run even if **search** prop has not been provided. Thus you need to make sure to exit early with a value of `true` if filter should not be applied.

<example file="v-data-table/prop-custom-filter" />

#### Dense

Using the **dense** prop you are able to give your data tables an alternate style.

<example file="v-data-table/prop-dense" />

#### Filterable

You can easily disable specific columns from being included when searching through table rows by setting the property **filterable** to false on the header item(s). In the example below the dessert name column is no longer searchable.

<example file="v-data-table/prop-filterable" />

#### Footer props

The `v-data-table` renders a default footer using the `v-data-footer` component. You can pass props to this component using **footer-props**.

<example file="v-data-table/prop-footer-props" />

#### Grouping

Using the **group-by** and **group-desc** props you can group rows on an item property. The **show-group-by** prop will show a group button in the default header. You can use the **groupable** property on header items to disable the group button.

<example file="v-data-table/prop-grouping" />

#### Hide default header and footer

You can apply the **hide-default-header** and **hide-default-footer** props to remove the default header and footer respectively.

<example file="v-data-table/prop-hide-header-footer" />

#### Loading

You can use the **loading** prop to indicate that data in the table is currently loading. If there is no data in the table, a loading message will also be displayed. This message can be customized using the **loading-text** prop or the `loading` slot.

<example file="v-data-table/prop-loading" />

#### Multi sort

Using the **multi-sort** prop will enable you to sort on multiple columns at the same time. When enabled, you can pass arrays to both **sort-by** and **sort-desc** to programmatically control the sorting, instead of single values.

<example file="v-data-table/prop-multi-sort" />

#### Row selection

The **show-select** prop will render a checkbox in the default header to toggle all rows, and a checkbox for each default row. You can customize these with the slots `header.data-table-select` and `item.data-table-select` respectively. You can also switch between allowing multiple selected rows at the same time or just one with the **single-select** prop.

<example file="v-data-table/prop-row-selection" />

#### Search

The data table exposes a **search** prop that allows you to filter your data.

<example file="v-data-table/prop-search" />

### Slots

The `v-data-table` provides a large number of slots for customizing the table. This example showcases some of these slots and what you can do with each. It is important to note some slot (eg: `item`/`body`/`header`) will completely takes over the internal rendering of the component which will require you to re-implement functionalities such as selection and expansion. Some slots will override each other such as: `body` > `item` > `item.<name>` and `header`/`header.<name>`.

<example file="v-data-table/slot-main" />

#### Header

You can use the dynamic slots `header.<name>` to customize only certain columns. `<name>` is the name of the `value` property in the corresponding header item sent to **headers**.

<example file="v-data-table/slot-header" />

#### Item

You can use the dynamic slots `item.<name>` to customize only certain columns. `<name>` is the name of the `value` property in the corresponding header item sent to **headers**. So to customize the calories column we're using the `item.calories` slot.

<example file="v-data-table/slot-item" />

#### Simple checkbox

When wanting to use a checkbox component inside of a slot template in your data tables, use the `v-simple-checkbox` component rather than the `v-checkbox` component. The `v-simple-checkbox` component is used internally and will respect header alignment.

<example file="v-data-table/slot-simple-checkbox" />

### Misc

#### CRUD Actions

`v-data-table` with CRUD actions using a `v-dialog` component for editing each row

<example file="v-data-table/misc-crud" />

#### Edit dialog

The `v-edit-dialog` component can be used for editing data directly within a `v-data-table`. You can block the closing of the `v-edit-dialog` when clicked outside by adding the **persistent** prop.

<example file="v-data-table/misc-edit-dialog" />

#### Expandable rows

The **show-expand** prop will render an expand icon on each default row. You can customize this with the `item.data-table-expand` slot. The position of this slot can be customized by adding a column with `value: 'data-table-expand'` to the headers array. You can also switch between allowing multiple expanded rows at the same time or just one with the **single-expand** prop. The expanded rows are available on the synced prop `expanded.sync`

<example file="v-data-table/misc-expand" />

#### External pagination

Pagination can be controlled externally by using the individual props, or by using the **options** prop. Remember that you must apply the **.sync** modifier.

<example file="v-data-table/misc-external-paginate" />

#### External sorting

Sorting can also be controlled externally by using the individual props, or by using the the **options** prop. Remember that you must apply the **.sync** modifier.

<example file="v-data-table/misc-external-sort" />

#### Server-side paginate and sort

If you're loading data already paginated and sorted from a backend, you can use the **server-items-length** prop. Defining this prop will disable the built-in sorting and pagination, and you will instead need to use the available events (`update:page`, `update:sortBy`, `update:options`, etc) to know when to request new pages from your backend. Use the **loading** prop to display a progress bar while fetching data.

<example file="v-data-table/misc-server-side-paginate-and-sort" />

<backmatter />


module.exports = {
  'v-chip-group': {
    slots: [
      {
        name: 'default',
        props: undefined,
      },
    ],
    events: [
      {
        name: 'change',
        value: 'any[] | any',
      },
    ],
  },
}


window.requestAnimationFrame(() => (this.isBooted = true))

genDefaultSlot () {
return this.$slots.default
},


---
meta:
  title: Color picker component
  description: The color picker component allows users to select a from pre-defined or custom colors using a variety of different inputs and formats.
  keyword: color pickers, vuetify color picker component, vue color picker component
related:
  - /components/menu/
  - /styles/colors/
  - /features/theme/
---

# Color pickers

The `v-color-picker` allows you to select a color using a variety of input methods.

<entry-ad />

## Usage

<usage name="v-color-picker" />

## API

- [v-color-picker](/api/v-color-picker)

## Examples

### Props

#### Canvas

The canvas can be hidden with the `hide-canvas` prop, and you can set its height with the prop `canvas-height`. The size of the selection dot can be controlled with the `dot-size` prop.

<example file="v-color-picker/prop-canvas" />

#### Elevation

Adjust the elevation of the `v-color-picker` component using the **elevation** or **flat** prop. The **flat** is equivalent to setting **elevation** to 0.

<example file="v-color-picker/prop-elevation" />

#### Inputs

The number inputs can be hidden with the `hide-inputs` prop, and the sliders can be hidden with the `hide-sliders` prop. You can also hide the mode switch icon with the `hide-mode-switch` prop. The mode can also be controlled externally through the `mode` prop.

<example file="v-color-picker/prop-inputs" />

#### Model

The `v-color-picker` uses the `v-model` prop to control the color displayed. It supports hex strings such as **#FF00FF** and **#FF00FF00**, and objects representing **RGBA**, **HSLA** and **HSVA** values.

<example file="v-color-picker/prop-model" />

#### Swatches

Using the `show-swatches` prop you can display an array of color swatches that users can pick from. It is also possible to customize what colors are shown using the `swatches` prop. This prop accepts a two-dimensional array, where the first dimension defines a column, and second dimension defines the swatches from top to bottom by providing rgba hex strings. You can also set the max height of the swatches section with the `swatches-max-height` prop.

<example file="v-color-picker/prop-swatches" />

<backmatter />


<template>
  <v-timeline :dense="$vuetify.breakpoint.smAndDown">
    <v-timeline-item
      color="purple lighten-2"
      fill-dot
      right
    >
      <v-card>
        <v-card-title class="purple lighten-2">
          <v-icon
            dark
            size="42"
            class="mr-4"
          >
            mdi-magnify
          </v-icon>

            Title 1

        </v-card-title>
        <v-container>
          <v-row>
            <v-col
              cols="12"
              md="10"
            >
              Lorem ipsum dolor sit amet, no nam oblique veritus. Commune scaevola imperdiet nec ut, sed euismod convenire principes at. Est et nobis iisque percipit.
            </v-col>
            <v-col
              class="hidden-sm-and-down text-right"
              md="2"
            >
              <v-icon size="64">
                mdi-calendar-text
              </v-icon>
            </v-col>
          </v-row>
        </v-container>
      </v-card>
    </v-timeline-item>

    <v-timeline-item
      color="amber lighten-1"
      fill-dot
      left
      small
    >
      <v-card>
        <v-card-title class="amber lighten-1 justify-end">

            Title 2

          <v-icon
            dark
            size="42"
          >
            mdi-home-outline
          </v-icon>
        </v-card-title>
        <v-container>
          <v-row>
            <v-col
              cols="12"
              md="8"
            >
              Lorem ipsum dolor sit amet, no nam oblique veritus. Commune scaevola imperdiet nec ut, sed euismod convenire principes at. Est et nobis iisque percipit.
            </v-col>
            <v-col
              cols="12"
              md="4"
            >
              Lorem ipsum dolor sit amet, no nam oblique veritus.
            </v-col>
          </v-row>
        </v-container>
      </v-card>
    </v-timeline-item>

    <v-timeline-item
      color="cyan lighten-1"
      fill-dot
      right
    >
      <v-card>
        <v-card-title class="cyan lighten-1">
          <v-icon
            class="mr-4"
            dark
            size="42"
          >
            mdi-email-outline
          </v-icon>

            Title 3

        </v-card-title>
        <v-container>
          <v-row>
            <v-col
              v-for="n in 3"
              :key="n"
              cols="12"
              md="4"
            >
              Lorem ipsum dolor sit amet, no nam oblique veritus no nam oblique.
            </v-col>
          </v-row>
        </v-container>
      </v-card>
    </v-timeline-item>

    <v-timeline-item
      color="red lighten-1"
      fill-dot
      left
      small
    >
      <v-card>
        <v-card-title class="red lighten-1 justify-end">

            Title 4

          <v-icon
            dark
            size="42"
          >
            mdi-account-multiple-outline
          </v-icon>
        </v-card-title>
        <v-container>
          <v-row>
            <v-col
              class="hidden-sm-and-down"
              md="2"
            >
              <v-icon size="64">
                mdi-server-network
              </v-icon>
            </v-col>
            <v-col
              cols="12"
              md="10"
            >
              Lorem ipsum dolor sit amet, no nam oblique veritus. Commune scaevola imperdiet nec ut, sed euismod convenire principes at. Est et nobis iisque percipit, an vim zril disputando voluptatibus.
            </v-col>
          </v-row>
        </v-container>
      </v-card>
    </v-timeline-item>

    <v-timeline-item
      color="green lighten-1"
      fill-dot
      right
    >
      <v-card>
        <v-card-title class="green lighten-1">
          <v-icon
            class="mr-4"
            dark
            size="42"
          >
            mdi-phone-in-talk
          </v-icon>

            Title 5

        </v-card-title>
        <v-container>
          <v-row>
            <v-col>
              Lorem ipsum dolor sit amet, no nam oblique veritus. Commune scaevola imperdiet nec ut, sed euismod convenire principes at. Est et nobis iisque percipit, an vim zril disputando voluptatibus, vix an salutandi sententiae.
            </v-col>
          </v-row>
        </v-container>
      </v-card>
    </v-timeline-item>
  </v-timeline>
</template>


const children = [this.genDefaultSlot()]


// Imports
@import './_variables.scss'

// Theme
+theme(v-sheet) using ($material)
  background-color: map-get($material, 'cards')
  border-color: map-get($material, 'cards')
  color: map-deep-get($material, 'text', 'primary')

  &--outlined
    border: $sheet-outlined-border-width solid map-get($material, 'dividers')

// Sheet
.v-sheet
  +paper($sheet-elevation, $sheet-border-radius, $sheet-shaped-border-radius)


<template>
  <div>
    <v-card
      class="d-flex align-start mb-6"
      color="grey lighten-2"
      flat
      height="100"
      tile
    >
      <v-card
        v-for="n in 3"
        :key="n"
        class="pa-2"
        outlined
        tile
      >
        align-start
      </v-card>
    </v-card>

    <v-card
      class="d-flex align-end mb-6"
      color="grey lighten-2"
      flat
      height="100"
      tile
    >
      <v-card
        v-for="n in 3"
        :key="n"
        class="pa-2"
        outlined
        tile
      >
        align-end
      </v-card>
    </v-card>

    <v-card
      class="d-flex align-center mb-6"
      color="grey lighten-2"
      flat
      height="100"
      tile
    >
      <v-card
        v-for="n in 3"
        :key="n"
        class="pa-2"
        outlined
        tile
      >
        align-center
      </v-card>
    </v-card>

    <v-card
      class="d-flex align-baseline mb-6"
      color="grey lighten-2"
      flat
      height="100"
      tile
    >
      <v-card
        v-for="n in 3"
        :key="n"
        class="pa-2"
        outlined
        tile
      >
        align-baseline
      </v-card>
    </v-card>

    <v-card
      class="d-flex align-stretch mb-6"
      color="grey lighten-2"
      flat
      height="100"
      tile
    >
      <v-card
        v-for="n in 3"
        :key="n"
        class="pa-2"
        outlined
        tile
      >
        align-stretch
      </v-card>
    </v-card>
  </div>
</template>


<template>

    <v-data-table
      :headers="headers"
      :items="desserts"
      item-key="name"
      class="elevation-1"
      :search="search"
      :custom-filter="filterOnlyCapsText"
    >
      <template v-slot:top>
        <v-text-field
          v-model="search"
          label="Search (UPPER CASE ONLY)"
          class="mx-4"
        ></v-text-field>
      </template>
      <template v-slot:body.append>

            <v-text-field
              v-model="calories"
              type="number"
              label="Less than"
            ></v-text-field>

      </template>
    </v-data-table>

</template>


keep keep keep keep keep replace keep keep keep keep

const viewWidth = width || this.value.length * padding * 2

// Components
import Gradient from './components/gradient'
import Rect from './components/rect'
import Text from './components/text'

// Mixins
import props from './mixins/props'

// Utilities
import mixins from '../../util/mixins'
import { genPoints } from './helpers/core'

// Types
import { VNode } from 'vue'

export default mixins(props).extend({
  name: 'VBar',

  props: {
    autoDrawDuration: {
      type: Number,
      default: 500
    }
  },

  render (h): VNode {
    if (!this.value || this.value.length < 2) return undefined as never
    const { width, height, padding, lineWidth } = this
    const viewWidth = width || 300
    const viewHeight = height || 75
    const boundary = {
      minX: padding,
      minY: padding,
      maxX: viewWidth - padding,
      maxY: viewHeight - padding
    }
    const props = {
      ...this.$props
    }

    props.points = genPoints(this.value, boundary)

    const totalWidth = boundary.maxX / (props.points.length - 1)

    props.boundary = boundary
    props.id = 'sparkline-bar-' + this._uid
    props.lineWidth = lineWidth || (totalWidth - (padding || 5))
    props.offsetX = (totalWidth - props.lineWidth) / 2

    return h('svg', {
      attrs: {
        width: width || '100%',
        height: height || '25%',
        viewBox: `0 0 ${viewWidth} ${viewHeight}`
      }
    }, [
      h(Gradient, { props }),
      h(Rect, { props }),
      this.showLabel ? h(Text, { props }) : undefined as never,
      h('g', {
        attrs: {
          transform: `scale(1,-1) translate(0,-${boundary.maxY})`,
          'clip-path': `url(#${props.id}-clip)`,
          fill: `url(#${props.id})`
        }
      }, [
        h('rect', {
          attrs: {
            x: 0,
            y: 0,
            width: viewWidth,
            height: viewHeight
          }
        })
      ])
    ])
  }
})


将输入框设置为手动错误状态。

将输入框置于错误状态，并传入自定义的错误信息。将与来自**规则（rules）**属性的任何验证相结合。这个字段不会触发验证。

返回True或带有错误信息的字符串的函数数组。

延迟验证直到失去焦点的事件被触发


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

v-date-picker-table($material)
  th,
  .v-date-picker-table--date__week
    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__events
  height: 8px
  left: 0
  position: absolute
  text-align: center
  white-space: pre
  width: 100%

  > div
    border-radius: 50%
    display: inline-block
    height: 8px
    margin: 0 1px
    width: 8px

.v-date-picker-table--date .v-date-picker-table__events
  bottom: 6px

.v-date-picker-table--month .v-date-picker-table__events
  bottom: 8px

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


Компонент `v-dialog` информирует пользователей о конкретной задаче и может содержать критическую информацию, принимать решения или включать несколько задач. Используйте диалоги экономно, потому что они прерываются.

### Простые диалоги

Выбор опции немедленно выполняет эту опцию и закрывает меню. Прикосновение к окну диалога или нажатие клавиши «Back» отменяет действие и закрывает диалог.

### Без активатора

Если по какой-то причине вы не можете использовать слот активатора, обязательно добавьте модификатор `.stop` в событие, которое запускает диалог.

### Модальный

Подобно простому диалогу, за исключением того, что он не убирается при касании снаружи.

### Полноэкранный

Из-за ограниченного пространства полноэкранные диалоги могут быть более подходящими для мобильных устройств, чем диалоги, используемые на устройствах с большими экранами.

Просто простой пример формы в диалоговом окне.

### Прокручиваемый

Пример диалога с прокручиваемым контентом.

### Переполнение

Модификации, которые не помещаются в доступное пространство окна, будут прокручивать контейнер.

Отключено возможность открытия диалога

Указывает, что модальное действие 100% ширины

Изменение макета для полноэкранного отображения

Скрыть отображение наложения

Максимальная ширина содержимого

Клик снаружи не отменяет диалог

Если установлено значение true, ожидается карта, название карты, текст карты и действия карты. Кроме того, текст карты должен иметь указанную высоту. Устанавливает перенос текста карты на переполнение

Устанавливает ширину диалогового окна


keep keep keep keep keep replace keep keep keep

// Types
import Vue, { VNode } from 'vue'
import { Prop } from 'vue/types/options'
import { Boundary, Point } from '../VSparkline'

export default Vue.extend({
  name: 'VSparklineText',

  props: ['points', 'boundary', 'offsetX'] as any as {
    points: Prop<Point[]>
    boundary: Prop<Boundary>
    offsetX: Prop<number>
  },

  render (h): VNode {
    const offsetX = (this.offsetX || 0) / 2

    return h('g', {
      style: {
        fontSize: '8',
        textAnchor: 'middle',
        dominantBaseline: 'mathematical',
        fill: this.$vuetify.theme.secondary
      }
    }, this.points.map(item => (
      h('text', {
        attrs: {
          x: item.x - offsetX,
          y: this.boundary.maxY + 2
        }
      }, item.value.toString())
    )))
  }
})


header


import VBtnToggle from './VBtnToggle'

export { VBtnToggle }
export default VBtnToggle


<template>
  <v-card height="200px">
    <v-bottom-nav
      :active.sync="bottomNav"
      :color="color"
      :value="true"
      absolute
      dark
      shift
    >
      <v-btn dark>
        Video
        <v-icon>ondemand_video</v-icon>
      </v-btn>

      <v-btn dark>
        Music
        <v-icon>music_note</v-icon>
      </v-btn>

      <v-btn dark>
        Book
        <v-icon>book</v-icon>
      </v-btn>

      <v-btn dark>
        Image
        <v-icon>image</v-icon>
      </v-btn>
    </v-bottom-nav>
  </v-card>
</template>


export function genPoints (points: SparklineItem[], boundary: Boundary): Point[] {

const { minX, minY, maxX, maxY } = boundary

const normalisedPoints = points.map(item => (typeof item === 'number' ? item : item.value))

const minValue = Math.min(...normalisedPoints) - 0.001

const gridX = (maxX - minX) / (normalisedPoints.length - 1)

const maxValue = Math.max(...normalisedPoints) + 1

let minValue = Math.min(...normalisedPoints)


import Vue from 'vue'
import { test } from '@/test'
import VTreeviewNode from '@/components/VTreeview/VTreeviewNode'

const singleRootTwoChildren = { id: 0, name: 'Root', children: [{ id: 1, name: 'Child' }, { id: 2, name: 'Child 2' }] }

const vm = new Vue()
const defaultSlot = () => vm.$createElement('div', 'foobar')

const Mock = {
  name: 'test',

  render: h => h(VTreeviewNode, {
    scopedSlots: {
      prepend: defaultSlot,
      append: defaultSlot
    }
  })
}

const MockScopedLabel = {
  name: 'test',

  render: h => h(VTreeviewNode, {
    props: {
      item: singleRootTwoChildren
    },
    scopedSlots: {
      label: props => vm.$createElement('div', [props.item.name.toUpperCase()])
    }
  })
}

test('VTreeViewNode.ts', ({ mount }) => {
  let treeview

  beforeEach(() => {
    treeview = {
      register: jest.fn(),
      unregister: jest.fn()
    }
  })

  it('should return indeterminate icon', async () => {
    const wrapper = mount(VTreeviewNode, {
      provide: { treeview }
    })

    expect(wrapper.vm.computedIcon).toBe('$vuetify.icons.checkboxOff')

    wrapper.setData({ isIndeterminate: true })

    expect(wrapper.vm.computedIcon).toBe('$vuetify.icons.checkboxIndeterminate')
  })

  it('should use scoped slots', () => {
    const wrapper = mount(Mock, {
      provide: { treeview }
    })

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

  it('should generate a transition element', () => {
    const wrapper = mount(VTreeviewNode, {
      propsData: { transition: true },
      provide: { treeview }
    })

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

  it('should use label slot', () => {
    const wrapper = mount(MockScopedLabel, {
      provide: { treeview }
    })

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


<template>
  <v-container fluid>
    <v-layout column>
      <v-flex xs12>

          <v-layout v-bind="layoutAttributes">

          </v-layout>

      </v-flex>
      <v-flex xs12>
        <v-layout row xs12 wrap>
          <v-flex xs12 md4>
            <v-radio-group v-model="alignment">
              <v-radio
                v-for="n in alignmentsAvailable"
                :key="n"
                :label="n === '' ? 'Nothing' : n"
                :value="n"
              ></v-radio>
            </v-radio-group>
          </v-flex>
          <v-flex xs12 md4>
            <v-radio-group v-model="justify">
              <v-radio
                v-for="n in justifyAvailable"
                :key="n"
                :label="n === '' ? 'Nothing' : n"
                :value="n"
              ></v-radio>
            </v-radio-group>
          </v-flex>
          <v-flex xs12 md4>
            <v-layout column>
              <v-radio-group v-model="flexDirection">
                <v-checkbox
                  v-model="reverse"
                  label="reverse"
                  hide-details
                ></v-checkbox>
                <v-checkbox
                  v-model="fillHeight"
                  label="fill-height"
                  hide-details
                ></v-checkbox>
                <v-radio
                  v-for="n in flexDirectionAvailable"
                  :key="n"
                  :label="n === '' ? 'Nothing' : n"
                  :value="n"
                ></v-radio>
              </v-radio-group>
            </v-layout>
          </v-flex>
        </v-layout>
      </v-flex>
      <v-flex xs12>

Output:

        <markup>{{ formatAttributes(layoutAttributes) }}</markup>
      </v-flex>
    </v-layout>
  </v-container>
</template>


import VBadge from './VBadge'

export { VBadge }
export default VBadge


const gridY = (maxY - minY) / (maxValue - minValue)

import { SparklineItem, Boundary, Point } from '../VSparkline'

export function genPoints (points: SparklineItem[], boundary: Boundary): Point[] {
  const { minX, minY, maxX, maxY } = boundary
  const normalisedPoints = points.map(item => (typeof item === 'number' ? item : item.value))
  const minValue = Math.min(...normalisedPoints) - 0.001
  const gridX = (maxX - minX) / (normalisedPoints.length - 1)
  const gridY = (maxY - minY) / (Math.max(...normalisedPoints) + 0.001 - minValue)

  return normalisedPoints.map((value, index) => {
    return {
      x: index * gridX + minX,
      y:
        maxY -
        (value - minValue) * gridY +
        +(index === normalisedPoints.length - 1) * 0.00001 -
        +(index === 0) * 0.00001,
      value
    }
  })
}


<template>
  <v-hover>
    <v-card
      slot-scope="{ hover }"
      class="mx-auto"
      color="grey lighten-4"
      max-width="600"
    >
      <v-img
        :aspect-ratio="16/9"
        src="https://cdn.vuetifyjs.com/images/cards/kitchen.png"
      >
        <v-expand-transition>

            $14.99

        </v-expand-transition>
      </v-img>
      <v-card-text
        class="pt-4"
        style="position: relative;"
      >
        <v-btn
          absolute
          color="orange"
          class="white--text"
          fab
          large
          right
          top
        >
          <v-icon>mdi-cart</v-icon>
        </v-btn>

For the perfect meal

QW cooking utensils

          Our Vintage kitchen utensils delight any chef.

          Made of bamboo by hand

      </v-card-text>
    </v-card>
  </v-hover>
</template>

<codepen-resources lang="json">
  {
    "css": ["https://cdn.materialdesignicons.com/2.5.94/css/materialdesignicons.min.css"]
  }
</codepen-resources>


import VDatePicker from './VDatePicker'
import VDatePickerTitle from './VDatePickerTitle'
import VDatePickerHeader from './VDatePickerHeader'
import VDatePickerDateTable from './VDatePickerDateTable'
import VDatePickerMonthTable from './VDatePickerMonthTable'
import VDatePickerYears from './VDatePickerYears'

export {
  VDatePicker,
  VDatePickerTitle,
  VDatePickerHeader,
  VDatePickerDateTable,
  VDatePickerMonthTable,
  VDatePickerYears
}

export default {
  $_vuetify_subcomponents: {
    VDatePicker,
    VDatePickerTitle,
    VDatePickerHeader,
    VDatePickerDateTable,
    VDatePickerMonthTable,
    VDatePickerYears
  }
}