import React, { PureComponent } from 'react';
import { List, Icon, Avatar, Tag } from 'antd';
import moment from 'moment';
import { connect } from 'dva';
import stylesArticles from '../../List/Articles.less';
import styles from './Articles.less';

@connect(({ list }) => ({
  list,
}))
export default class Center extends PureComponent {
  render() {
    const {
      list: { list },
    } = this.props;
    const IconText = ({ type, text }) => (

        <Icon type={type} style={{ marginRight: 8 }} />
        {text}

    );
    const ListContent = ({ data: { content, updatedAt, avatar, owner, href } }) => (

{content}

          <Avatar src={avatar} size="small" />
          {owner} 发布在
          {href}
          {moment(updatedAt).format('YYYY-MM-DD HH:mm')}

    );
    return (
      <List
        size="large"
        className={styles.articleList}
        rowKey="id"
        itemLayout="vertical"
        dataSource={list}
        renderItem={item => (
          <List.Item
            key={item.id}
            actions={[
              <IconText type="star-o" text={item.star} />,
              <IconText type="like-o" text={item.like} />,
              <IconText type="message" text={item.message} />,
            ]}
          >
            <List.Item.Meta
              title={

                  {item.title}

              }
              description={

                  <Tag>Ant Design</Tag>
                  <Tag>设计语言</Tag>
                  <Tag>蚂蚁金服</Tag>

              }
            />
            <ListContent data={item} />
          </List.Item>
        )}
      />
    );
  }
}


.right {
float: right;
height: 100%;
width: 324px;
.action {
cursor: pointer;
padding: 0 12px;

@import '~antd/lib/style/themes/default.less';

.standardTable {
  :global {
    .ant-table-pagination {
      margin-top: 24px;
    }
  }

  .tableAlert {
    margin-bottom: 16px;
  }
}


display: none;
}
i.trigger {
padding: 0 12px;
}
.logo {
padding-right: 12px;
position: relative;

keep keep keep replace keep keep keep keep

import React, { PureComponent } from 'react';
import Link from 'umi/link';
import RightContent from '../GlobalHeader/RightContent';
import BaseMenu from '../SiderMenu/BaseMenu';
import styles from './index.less';

export default class TopNavHeader extends PureComponent {
  constructor(props) {
    super(props);

    this.state = {
      maxWidth: (props.grid === 'Wide' ? 1200 : window.innerWidth) - 324 - 165 - 4,
    };
  }

  static getDerivedStateFromProps(props) {
    return {
      maxWidth: (props.grid === 'Wide' ? 1200 : window.innerWidth) - 324 - 165 - 4,
    };
  }

  render() {
    const { theme, grid, logo } = this.props;
    const { maxWidth } = this.state;
    return (
      <div className={`${styles.head} ${theme === 'light' ? styles.light : ''}`}>

 {
            this.maim = ref;
          }}
          className={`${styles.main} ${grid === 'Wide' ? styles.wide : ''}`}
        >

              <Link to="/">

Ant Design Pro

              </Link>

              <BaseMenu {...this.props} style={{ border: 'none', height: 64 }} />

          <RightContent {...this.props} />

    );
  }
}


import React, { PureComponent } from 'react';
import { List, Card, Icon, Dropdown, Menu, Avatar, Tooltip } from 'antd';
import numeral from 'numeral';
import { connect } from 'dva';
import { formatWan } from '@/utils/utils';
import stylesApplications from '../../List/Applications.less';

@connect(({ list }) => ({
  list,
}))
export default class Center extends PureComponent {
  render() {
    const {
      list: { list },
    } = this.props;
    const itemMenu = (
      <Menu>
        <Menu.Item>

            1st menu item

        </Menu.Item>
        <Menu.Item>

            2nd menu item

        </Menu.Item>
        <Menu.Item>

            3d menu item

        </Menu.Item>
      </Menu>
    );
    const CardInfo = ({ activeUser, newUser }) => (

活跃用户

{activeUser}

新增用户

{newUser}

    );
    return (
      <List
        rowKey="id"
        className={stylesApplications.filterCardList}
        grid={{ gutter: 24, xxl: 3, xl: 2, lg: 2, md: 2, sm: 2, xs: 1 }}
        dataSource={list}
        renderItem={item => (
          <List.Item key={item.id}>
            <Card
              hoverable
              bodyStyle={{ paddingBottom: 20 }}
              actions={[
                <Tooltip title="下载">
                  <Icon type="download" />
                </Tooltip>,
                <Tooltip title="编辑">
                  <Icon type="edit" />
                </Tooltip>,
                <Tooltip title="分享">
                  <Icon type="share-alt" />
                </Tooltip>,
                <Dropdown overlay={itemMenu}>
                  <Icon type="ellipsis" />
                </Dropdown>,
              ]}
            >
              <Card.Meta avatar={<Avatar size="small" src={item.avatar} />} title={item.title} />

                <CardInfo
                  activeUser={formatWan(item.activeUser)}
                  newUser={numeral(item.newUser).format('0,0')}
                />

            </Card>
          </List.Item>
        )}
      />
    );
  }
}


---
title:
  en-US: Charts
  zh-CN: Charts
subtitle: 图表
order: 2
cols: 2
---

Ant Design Pro 提供的业务中常用的图表类型，都是基于 [G2](https://antv.alipay.com/g2/doc/index.html) 按照 Ant Design 图表规范封装，需要注意的是 Ant Design Pro 的图表组件以套件形式提供，可以任意组合实现复杂的业务需求。

因为结合了 Ant Design 的标准设计，本着极简的设计思想以及开箱即用的理念，简化了大量 API 配置，所以如果需要灵活定制图表，可以参考 Ant Design Pro 图表实现，自行基于 [G2](https://antv.alipay.com/g2/doc/index.html) 封装图表组件使用。

## API

### ChartCard

| 参数      | 说明                                      | 类型         | 默认值 |
|----------|------------------------------------------|-------------|-------|
| title | 卡片标题 | ReactNode\|string | - |
| action | 卡片操作 | ReactNode | - |
| total | 数据总量 | ReactNode \| number \| function | - |
| footer | 卡片底部 | ReactNode | - |
| contentHeight | 内容区域高度 | number | - |
| avatar | 右侧图标 | React.ReactNode | - |
### MiniBar

| 参数      | 说明                                      | 类型         | 默认值 |
|----------|------------------------------------------|-------------|-------|
| color | 图表颜色 | string | `#1890FF` |
| height | 图表高度 | number | - |
| data | 数据 | array<{x, y}> | - |

### MiniArea

| 参数      | 说明                                      | 类型         | 默认值 |
|----------|------------------------------------------|-------------|-------|
| color | 图表颜色 | string | `rgba(24, 144, 255, 0.2)` |
| borderColor | 图表边颜色 | string | `#1890FF` |
| height | 图表高度 | number | - |
| line | 是否显示描边 | boolean | false |
| animate | 是否显示动画 | boolean | true |
| xAxis | [x 轴配置](http://antvis.github.io/g2/doc/tutorial/start/axis.html) | object | - |
| yAxis | [y 轴配置](http://antvis.github.io/g2/doc/tutorial/start/axis.html) | object | - |
| data | 数据 | array<{x, y}> | - |

### MiniProgress

| 参数      | 说明                                      | 类型         | 默认值 |
|----------|------------------------------------------|-------------|-------|
| target | 目标比例 | number | - |
| color | 进度条颜色 | string | - |
| strokeWidth | 进度条高度 | number | - |
| percent | 进度比例 | number | - |

### Bar

| 参数      | 说明                                      | 类型         | 默认值 |
|----------|------------------------------------------|-------------|-------|
| title | 图表标题 | ReactNode\|string | - |
| color | 图表颜色 | string | `rgba(24, 144, 255, 0.85)` |
| padding | 图表内部间距 | [array](https://github.com/alibaba/BizCharts/blob/master/doc/api/chart.md#7padding-object--number--array-) | `'auto'` |
| height | 图表高度 | number | - |
| data | 数据 | array<{x, y}> | - |
| autoLabel | 在宽度不足时，自动隐藏 x 轴的 label | boolean | `true` |

### Pie

| 参数      | 说明                                      | 类型         | 默认值 |
|----------|------------------------------------------|-------------|-------|
| animate | 是否显示动画 | boolean | true |
| color | 图表颜色 | string | `rgba(24, 144, 255, 0.85)` |
| height | 图表高度 | number | - |
| hasLegend | 是否显示 legend | boolean | `false` |
| padding | 图表内部间距 | [array](https://github.com/alibaba/BizCharts/blob/master/doc/api/chart.md#7padding-object--number--array-) | `'auto'` |
| percent | 占比 | number | - |
| tooltip | 是否显示 tooltip | boolean | true |
| valueFormat | 显示值的格式化函数 | function | - |
| title | 图表标题 | ReactNode\|string | - |
| subTitle | 图表子标题 | ReactNode\|string | - |
| total | 图标中央的总数 | string | function | - |

### Radar

| 参数      | 说明                                      | 类型         | 默认值 |
|----------|------------------------------------------|-------------|-------|
| title | 图表标题 | ReactNode\|string | - |
| height | 图表高度 | number | - |
| hasLegend | 是否显示 legend | boolean | `false` |
| padding | 图表内部间距 | [array](https://github.com/alibaba/BizCharts/blob/master/doc/api/chart.md#7padding-object--number--array-) | `'auto'` |
| data | 图标数据 | array<{name,label,value}> | - |

### Gauge

| 参数      | 说明                                      | 类型         | 默认值 |
|----------|------------------------------------------|-------------|-------|
| title | 图表标题 | ReactNode\|string | - |
| height | 图表高度 | number | - |
| color | 图表颜色 | string | `#2F9CFF` |
| bgColor | 图表背景颜色 | string | `#F0F2F5` |
| percent | 进度比例 | number | - |

### WaterWave

| 参数      | 说明                                      | 类型         | 默认值 |
|----------|------------------------------------------|-------------|-------|
| title | 图表标题 | ReactNode\|string | - |
| height | 图表高度 | number | - |
| color | 图表颜色 | string | `#1890FF` |
| percent | 进度比例 | number | - |

### TagCloud

| 参数      | 说明                                      | 类型         | 默认值 |
|----------|------------------------------------------|-------------|-------|
| data | 标题 | Array<name, value\> | - |
| height | 高度值 | number | - |

### TimelineChart

| 参数      | 说明                                      | 类型         | 默认值 |
|----------|------------------------------------------|-------------|-------|
| data | 标题 | Array<x, y1, y2\> | - |
| titleMap | 指标别名 | Object{y1: '客流量', y2: '支付笔数'} | - |
| height | 高度值 | number | 400 |

### Field

| 参数      | 说明                                      | 类型         | 默认值 |
|----------|------------------------------------------|-------------|-------|
| label | 标题 | ReactNode\|string | - |
| value | 值 | ReactNode\|string | - |


@import '~antd/lib/style/themes/default.less';

.field {
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  margin: 0;
  span {
    font-size: @font-size-base;
    line-height: 22px;
  }
  span:last-child {
    margin-left: 8px;
    color: @heading-color;
  }
}


</div>
<div
style={{
flex: 1,
maxWidth,
}}
>

@import '~antd/lib/style/themes/default.less';

.header {
  height: 64px;
  padding: 0 12px 0 0;
  background: #fff;
  box-shadow: 0 1px 4px rgba(0, 21, 41, 0.08);
  position: relative;
}

.logo {
  height: 64px;
  line-height: 58px;
  vertical-align: top;
  display: inline-block;
  padding: 0 0 0 24px;
  cursor: pointer;
  font-size: 20px;
  img {
    display: inline-block;
    vertical-align: middle;
  }
}

.menu {
  :global(.anticon) {
    margin-right: 8px;
  }
  :global(.ant-dropdown-menu-item) {
    width: 160px;
  }
}

i.trigger {
  font-size: 20px;
  line-height: 64px;
  cursor: pointer;
  transition: all 0.3s, padding 0s;
  padding: 0 24px;
  &:hover {
    background: @primary-1;
  }
}

.right {
  float: right;
  height: 100%;
  width: 324px;
  .action {
    cursor: pointer;
    padding: 0 12px;
    display: inline-block;
    transition: all 0.3s;
    height: 100%;
    > i {
      font-size: 16px;
      vertical-align: middle;
      color: @text-color;
    }
    &:hover {
      background: @primary-1;
    }
    :global(&.ant-popover-open) {
      background: @primary-1;
    }
  }
  .search {
    padding: 0 12px;
    &:hover {
      background: transparent;
    }
  }
  .account {
    .avatar {
      margin: 20px 8px 20px 0;
      color: @primary-color;
      background: rgba(255, 255, 255, 0.85);
      vertical-align: middle;
    }
  }
}

.dark {
  height: 64px;
  .action {
    color: rgba(255, 255, 255, 0.85);
    > i {
      color: rgba(255, 255, 255, 0.85);
    }
    &:hover,
    &:global(.ant-popover-open) {
      background: @primary-color;
    }
    :global(.ant-badge) {
      color: rgba(255, 255, 255, 0.85);
    }
  }
}

@media only screen and (max-width: @screen-md) {
  .header {
    :global(.ant-divider-vertical) {
      vertical-align: unset;
    }
    .name {
      display: none;
    }
    i.trigger {
      padding: 0 12px;
    }
    .logo {
      padding-right: 12px;
      position: relative;
    }
    .right {
      position: absolute;
      right: 12px;
      top: 0;
      background: #fff;
      .account {
        .avatar {
          margin-right: 0;
        }
      }
    }
  }
}


---
order: 0
title:
  zh-CN: 404
  en-US: 404
---

## zh-CN

404 页面。

## en-US

404 page.

````jsx
import Exception from 'ant-design-pro/lib/Exception';

ReactDOM.render(
  <Exception type="404" />
, mountNode);
````


import * as React from 'react';

export interface ITrendProps {
  colorful?: boolean;
  flag: 'up' | 'down';
  style?: React.CSSProperties;
  reverseColor?: boolean;
}

export default class Trend extends React.Component<ITrendProps, any> {}


@import '~antd/lib/style/themes/default.less';

.area_code {
  max-width: 128px;
  margin-right: 8px;
  width: 30%;
}
.phone_number {
  max-width: 312px;
  width: ~'calc(70% - 8px)';
}


"fixedFooter": "Use the fixed-footer prop together with the height prop to fix the footer to the bottom of the table.",

"fixedHeader": "Use the fixed-header prop together with the height prop to fix the header to the top of the table.",

"height": "Use the height prop to set the height of the table.",

"hover": "Will add a hover effect to a table's row when the mouse is over it."

"bottom": "Slot to add content below the table.",

"top": "Slot to add content above the table.",

"wrapper": "Slots for custom rendering of the table wrapper."

Display timeline in a **vertical** or **horizontal** direction.

Shift the position of the line. By default the line will evenly split items before/after.

Mirror the before/after ordering of timeline items.

Display all timeline items on one side of the timeline, either **before** or **after**.

Truncate timeline directly at the **start** or **end** of the line, or on **both** ends.


// Plugins
import octokit from '@/plugins/octokit'

// Utilities
import { defineStore } from 'pinia'
import { onBeforeMount, ref } from 'vue'

// Data
import team from '@/data/team.json'

export type Member = {
  discord?: string
  focus: string[]
  funding?: string[]
  languages: string[]
  linkedin?: string
  location?: string
  work?: string
  name: string
  avatar?: string
  github?: string
  team: string
  twitter?: string
  joined?: string
}

export type GithubMember = {
  avatar_url: string
  login: string
}

export const useTeamStore = defineStore('team', () => {
  const members = ref<Member[]>([])

  for (const key in team) {
    const record: Member = (team as Record<string, Member>)[key]

    members.value.push({
      ...record,
      github: key,
    })
  }

  onBeforeMount(async () => {
    const res = await octokit.request('GET /orgs/vuetifyjs/members')
    const data = res.data as GithubMember[]

    members.value = members.value.map(member => {
      const record = data.find(u => u.login.localeCompare(member.github ?? '', 'en', {
        sensitivity: 'base',
      }) === 0)

      return {
        ...member,
        avatar: record?.avatar_url,
      }
    })
  })

  return { members }
})


// Utilities
import { SUPPORTS_INTERSECTION } from '@/util'

// Types
import type {
  DirectiveBinding,
} from 'vue'

type ObserveHandler = (
  isIntersecting: boolean,
  entries: IntersectionObserverEntry[],
  observer: IntersectionObserver,
) => void

export interface ObserveDirectiveBinding extends Omit<DirectiveBinding, 'modifiers' | 'value'> {
  value?: ObserveHandler | { handler: ObserveHandler, options?: IntersectionObserverInit }
  modifiers: {
    once?: boolean
    quiet?: boolean
  }
}

function mounted (el: HTMLElement, binding: ObserveDirectiveBinding) {
  if (!SUPPORTS_INTERSECTION) return

  const modifiers = binding.modifiers || {}
  const value = binding.value
  const { handler, options } = typeof value === 'object'
    ? value
    : { handler: value, options: {} }

  const observer = new IntersectionObserver((
    entries: IntersectionObserverEntry[] = [],
    observer: IntersectionObserver
  ) => {
    const _observe = el._observe?.[binding.instance!.$.uid]
    if (!_observe) return // Just in case, should never fire

    const isIntersecting = entries.some(entry => entry.isIntersecting)

    // If is not quiet or has already been
    // initted, invoke the user callback
    if (
      handler && (
        !modifiers.quiet ||
        _observe.init
      ) && (
        !modifiers.once ||
        isIntersecting ||
        _observe.init
      )
    ) {
      handler(isIntersecting, entries, observer)
    }

    if (isIntersecting && modifiers.once) unmounted(el, binding)
    else _observe.init = true
  }, options)

  el._observe = Object(el._observe)
  el._observe![binding.instance!.$.uid] = { init: false, observer }

  observer.observe(el)
}

function unmounted (el: HTMLElement, binding: ObserveDirectiveBinding) {
  const observe = el._observe?.[binding.instance!.$.uid]
  if (!observe) return

  observe.observer.unobserve(el)
  delete el._observe![binding.instance!.$.uid]
}

export const Intersect = {
  mounted,
  unmounted,
}

export default Intersect


<template>
  <v-input
    :error-messages="['Fatal error']"
    error
    disabled
  >
    Input
  </v-input>
</template>


"direction": "Display timeline in a **vertical** or **horizontal** direction.",

"align": "Places the timeline dot at the top or center of the timeline item.",

Delay (in ms) after which menu closes (when open-on-hover prop is set to true).

Duration before tooltip is shown and hidden when hovered.

Designates whether to use an internal activator.

Delay (in ms) after which tooltip opens (when `open-on-hover` prop is set to **true**).

Designates whether the tooltip should open on activator click.

Designates whether the tooltip should open on activator hover.

Specifies a custom tag for the activator wrapper.


<template>
  <v-card
    class="mx-auto"
    max-width="450"
  >
    <v-toolbar
      color="primary"
      height="88"
      flat
    >
      <template v-slot:prepend>
        <v-btn icon="mdi-arrow-left">
        </v-btn>
      </template>

      <v-text-field
        v-model="search"
        clearable
        hide-details
        label="Search News"
        prepend-inner-icon="mdi-magnify"
        single-line
      ></v-text-field>

      <template v-slot:append>
        <v-btn icon="mdi-dots-vertical"></v-btn>
      </template>
    </v-toolbar>

 0" class="py-3 px-4">
      <v-chip
        v-for="(keyword, i) in keywords"
        :key="i"
        class="me-2"
      >
        {{ keyword }}
      </v-chip>

    <v-divider></v-divider>

    <v-list lines="three">
      <v-list-item
        v-for="(item, i) in searching"
        :key="i"
        link
      >
        <template v-slot:prepend>
          <v-avatar
            class="me-4 mt-2"
            rounded="0"
          >
            <v-img :src="item.image" cover></v-img>
          </v-avatar>
        </template>

        <v-list-item-title
          class="text-uppercase font-weight-regular text-caption"
          v-text="item.category"
        ></v-list-item-title>

      </v-list-item>
    </v-list>
  </v-card>
</template>


export default {
  badge: 'Значка',
  open: 'Отвори',
  close: 'Затвори',
  dataIterator: {
    noResultsText: 'Не са намерени записи',
    loadingText: 'Зареждане на елементи...',
  },
  dataTable: {
    itemsPerPageText: 'Редове на страница:',
    ariaLabel: {
      sortDescending: 'Подреди в намаляващ ред.',
      sortAscending: 'Подреди в нарастващ ред.',
      sortNone: 'Без подредба.',
      activateNone: 'Активирай за премахване на подредбата.',
      activateDescending: 'Активирай за подредба в намаляващ ред.',
      activateAscending: 'Активирай за подредба в нарастващ ред.',
    },
    sortBy: 'Сортирай по',
  },
  dataFooter: {
    itemsPerPageText: 'Елементи на страница:',
    itemsPerPageAll: 'Всички',
    nextPage: 'Следваща страница',
    prevPage: 'Предишна страница',
    firstPage: 'Първа страница',
    lastPage: 'Последна страница',
    pageText: '{0}-{1} от {2}',
  },
  dateRangeInput: {
    divider: 'до',
  },
  datePicker: {
    ok: 'ОК',
    cancel: 'Отмяна',
    range: {
      title: 'Избор на дати',
      header: 'Въвеждане на дати',
    },
    title: 'Избор на дата',
    header: 'Въвеждане на дата',
    input: {
      placeholder: 'Въведете дата',
    },
  },
  noDataText: 'Няма налични данни',
  carousel: {
    prev: 'Предишна визуализация',
    next: 'Следваща визуализация',
    ariaLabel: {
      delimiter: 'Кадър {0} от {1} на въртележката',
    },
  },
  calendar: {
    moreEvents: 'Още {0}',
  },
  input: {
    clear: 'Изчисти {0}',
    prependAction: '{0} предшестващо действие',
    appendAction: '{0} последващо действие',
    otp: 'Моля, въведете OTP символ {0}',
  },
  fileInput: {
    counter: '{0} файла',
    counterSize: '{0} файла ({1} общо)',
  },
  timePicker: {
    am: 'пр. обяд',
    pm: 'сл. обяд',
  },
  pagination: {
    ariaLabel: {
      root: 'Странициране',
      next: 'Следваща страница',
      previous: 'Предишна страница',
      page: 'Отиди на страница {0}',
      currentPage: 'Текуща страница, Страница {0}',
      first: 'Първа страница',
      last: 'Последна страница',
    },
  },
  stepper: {
    next: 'Следващ',
    prev: 'Предишен',
  },
  rating: {
    ariaLabel: {
      item: 'Оценка {0} от {1}',
    },
  },
  loading: 'Зареждане...',
  infiniteScroll: {
    loadMore: 'Зареди още',
    empty: 'Няма повече',
  },
}


<template>
  <v-table>

          Name

          Calories

        {{ item.name }}
        {{ item.calories }}

  </v-table>
</template>


"linePosition": "Shift the position of the line. By default the line will evenly split items before/after.",

"justify": "Places timeline line at the center or automatically on the left or right side.",

"lineColor": "Color of the timeline line.",

"lineInset": "Specifies the distance between the line and the dot of timeline items.",


<template>
  <div>
    <v-text-field
      label="Main input"
      :rules="rules"
      hide-details="auto"
    ></v-text-field>
    <v-text-field label="Another input"></v-text-field>
  </div>
</template>

<script setup>
  const rules = [
    value => !!value || 'Required.',
    value => (value && value.length >= 3) || 'Min 3 characters',
  ]
</script>

<script>
  export default {
    data: () => ({
      rules: [
        value => !!value || 'Required.',
        value => (value && value.length >= 3) || 'Min 3 characters',
      ],
    }),
  }
</script>


<template>
  <div class="text-center">
    <v-rating
      v-model="rating"
      readonly
    ></v-rating>
  </div>
</template>

<script setup>
  import { ref } from 'vue'

  const rating = ref(3)
</script>

<script>
  export default {
    data: () => ({
      rating: 3,
    }),
  }
</script>


"mirror": "Mirror the before/after ordering of timeline items.",

"lineThickness": "Thickness of the timeline line.",


Constantly animates, use when loading progress is unknown. If set to the string `'disable-shrink'` it will use a simpler animation that does not run on the main thread.

The percentage value for current progress.

Question: Animates like **indeterminate** prop but inverse.

Rotates the circle start point in degrees.

Sets the diameter of the circle in pixels.

Sets the stroke of the circle in pixels.


<template>
  <v-range-slider
    v-model="value"
    step="10"
    thumb-label="always"
  ></v-range-slider>
</template>

<script setup>
  import { ref } from 'vue'

  const value = ref([20, 40])
</script>

<script>
  export default {
    data () {
      return {
        value: [20, 40],
      }
    },
  }
</script>


@use 'sass:math'
@use 'sass:selector'
@use '../../styles/settings'
@use '../../styles/tools'
@use './variables' as *

.v-textarea
  .v-field
    --v-textarea-control-height: var(--v-input-control-height)

  .v-field__field
    --v-input-control-height: var(--v-textarea-control-height)

  .v-field__input
    $a: calc((var(--v-field-padding-top, 0) + var(--v-input-padding-top, 0)) - 6px)
    $b: calc(var(--v-field-padding-top, 0) + var(--v-input-padding-top, 0) + 4px)

    flex: 1 1 auto
    outline: none
    -webkit-mask-image: linear-gradient(to bottom, transparent, transparent $a, black $b)
    mask-image: linear-gradient(to bottom, transparent, transparent $a, black $b)

    &.v-textarea__sizer
      visibility: hidden
      position: absolute
      top: 0
      left: 0
      height: 0 !important
      min-height: 0 !important
      pointer-events: none

  &--no-resize
    .v-field__input
      resize: none

  .v-field--no-label,
  .v-field--active
    textarea
      opacity: 1

  textarea
    opacity: 0
    flex: 1
    min-width: 0
    transition: .15s opacity settings.$standard-easing

    &:focus,
    &:active
      outline: none

    // Remove Firefox red outline
    &:invalid
      box-shadow: none


"singleSide": "Display all timeline items on one side of the timeline, either **before** or **after**.",

"truncateLine": "Truncate timeline directly at the **start** or **end** of the line, or on **both** ends."

"side": "Display all timeline items on one side of the timeline, either **before** or **after**.",


Formats day of the month string that appears in a day to a specified locale.

The ending date on the calendar (inclusive) in the format of `YYYY-MM-DD`. This may be ignored depending on the `type` of the calendar.

If the header at the top of the `day` view should be visible.

Sets the day that determines the first week of the year, starting with 0 for **Sunday**. For ISO 8601 this should be 4.

The minimum number of weeks to display in the `month` or `week` view.

Formats month string that appears in a day to specified locale.

Override the day & time which is considered now. This is in the format of `YYYY-MM-DD hh:mm:ss`. The calendar is styled according to now.

Whether the short versions of a month should be used (Jan vs January).

Whether the short versions of a weekday should be used (Mon vs Monday).

Whether the name of the month should be displayed on the first day of the month.

Whether week numbers should be displayed when using the `month` view.

The starting date on the calendar (inclusive) in the format of `YYYY-MM-DD`. This may be ignored depending on the `type` of the calendar.

Formats day of the week string that appears in the header to specified locale.

Specifies which days of the week to display. To display Monday through Friday only, a value of `[1, 2, 3, 4, 5]` can be used. To display a week starting on Monday a value of `[1, 2, 3, 4, 5, 6, 0]` can be used.


/// <reference types="../../../../types/cypress" />

// Components
import { VBtnGroup } from '..'
import { VBtn } from '@/components/VBtn'

const colors = ['success', 'info', 'warning', 'error', 'invalid'] as const
const densities = ['default', 'comfortable', 'compact'] as const
const variants = ['elevated', 'flat', 'tonal', 'outlined', 'text', 'plain'] as const

// TODO: screenshot tests
describe('VBtnGroup', () => {
  describe('color', () => {
    it('should render set length', () => {
      cy.mount(() => (
        <>
          { colors.map(color => (
            <VBtnGroup color={ color }>
              <VBtn>{ color } Button 1</VBtn>
              <VBtn>Button 2</VBtn>
              <VBtn>Button 3</VBtn>
            </VBtnGroup>
          ))}
        </>
      ))
        .get('.v-btn-group')
        .should('have.length', colors.length)
    })
  })

  describe('density', () => {
    it('supports density props', () => {
      cy.mount(() => (
        <>
          { densities.map(density => (
            <VBtnGroup color="primary" density={ density }>
              <VBtn>{ density } Button 1</VBtn>
              <VBtn>Button 2</VBtn>
              <VBtn>Button 3</VBtn>
            </VBtnGroup>
          ))}
        </>
      ))
        .get('.v-btn-group')
        .should('have.length', densities.length)
    })
  })

  describe('variant', () => {
    it('supports variant props', () => {
      cy.mount(() => (
        <>
          { variants.map(variant => (
            <VBtnGroup color="primary" variant={ variant }>
              <VBtn>{ variant } Button 1</VBtn>
              <VBtn>Button 2</VBtn>
              <VBtn>Button 3</VBtn>
            </VBtnGroup>
          ))}
        </>
      ))
        .get('.v-btn-group')
        .should('have.length', variants.length)
    })
  })
})


/* eslint-disable jest/no-commented-out-tests */

// Composables
import { createTheme } from '../theme'

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

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

describe('createTheme', () => {
  let app: App

  beforeEach(() => {
    const child = document.querySelector('#vuetify-theme-stylesheet')
    child && document.head.removeChild(child)
    app = createApp({})
  })

  it('should create style element', async () => {
    const { install } = createTheme()

    install(app)

    expect(document.head).toMatchSnapshot()
  })

  it('should not generate style element if disabled', async () => {
    const { install } = createTheme(false)

    install(app)

    expect(document.head).toMatchSnapshot()
  })

  it('should generate on-* colors', async () => {
    const theme = createTheme()

    theme.install(app)

    const colors = [
      'on-background',
      'on-surface',
      'on-primary',
      'on-secondary',
      'on-success',
      'on-warning',
      'on-error',
      'on-info',
    ]

    for (const color of colors) {
      expect(theme.computedThemes.value.light.colors).toHaveProperty(color)
    }
  })

  it('should generate color variants', async () => {
    const theme = createTheme({
      variations: {
        colors: ['primary', 'secondary'],
        lighten: 2,
        darken: 2,
      },
    })

    theme.install(app)

    for (const color of ['primary', 'secondary']) {
      for (const variant of ['lighten', 'darken']) {
        for (const amount of [1, 2]) {
          expect(theme.computedThemes.value.light.colors).toHaveProperty(`${color}-${variant}-${amount}`)
          expect(theme.computedThemes.value.light.colors).toHaveProperty(`on-${color}-${variant}-${amount}`)
        }
      }
    }
  })

  it('should update existing theme', async () => {
    const theme = createTheme({
      variations: false,
    })

    theme.install(app)

    expect(theme.computedThemes.value.light.colors.background).not.toBe('#FF0000')

    theme.themes.value.light = {
      ...theme.themes.value.light,
      colors: {
        ...theme.themes.value.light.colors,
        background: '#FF0000',
      },
    }

    expect(theme.computedThemes.value.light.colors.background).toBe('#FF0000')
  })

  it('should set a CSP nonce if configured', async () => {
    const { install } = createTheme({ cspNonce: 'my-csp-nonce' })

    install(app)

    const styleElement = document.getElementById('vuetify-theme-stylesheet')
    expect(styleElement?.getAttribute('nonce')).toBe('my-csp-nonce')
  })

  it('should not set a CSP nonce if option was left blank', async () => {
    const { install } = createTheme({})

    install(app)

    const styleElement = document.getElementById('vuetify-theme-stylesheet')
    expect(styleElement?.getAttribute('nonce')).toBeNull()
  })

  it('should merge custom theme based upon the supplied dark property', async () => {
    for (const dark of [true, false, undefined]) {
      const theme = createTheme({
        defaultTheme: 'myTheme',
        themes: { myTheme: { dark } },
      })

      theme.install(app)

      expect(theme.computedThemes.value.myTheme.dark).toBe(dark)
      expect(theme.computedThemes.value.myTheme.colors).toHaveProperty('primary')
    }
  })

  it('should generate variations for custom color keys', async () => {
    const theme = createTheme({
      themes: {
        light: {
          colors: { color2: '#1697f6' },
        },
      },
      variations: {
        colors: ['color2'],
        lighten: 1,
        darken: 1,
      },
    })

    theme.install(app)

    expect(theme.computedThemes.value.light.colors).toHaveProperty('color2-darken-1')
    expect(theme.computedThemes.value.light.colors).toHaveProperty('color2-lighten-1')
  })

  // it('should use vue-meta@2.3 functionality', () => {
  //   const theme = createTheme()
  //   const set = jest.fn()
  //   const $meta = () => ({
  //     addApp: () => ({ set }),
  //   })
  //   ;(instance as any).$meta = $meta as any
  //   theme.init(instance)
  //   expect(set).toHaveBeenCalled()
  // })

  // it('should set theme with vue-meta@2', () => {
  //   const theme = mockTheme()
  //   const anyInstance = instance as any
  //   anyInstance.$meta = () => ({
  //     getOptions: () => ({ keyName: 'metaInfo' }),
  //   })
  //   theme.init(anyInstance)
  //   const metaKeyName = anyInstance.$meta().getOptions().keyName
  //   expect(typeof anyInstance.$options[metaKeyName]).toBe('function')
  //   const metaInfo = anyInstance.$options[metaKeyName]()
  //   expect(metaInfo).toBeTruthy()
  //   expect(metaInfo.style).toHaveLength(1)
  //   expect(metaInfo.style[0].cssText).toMatchSnapshot()
  // })

  // it('should set theme with vue-meta@1', () => {
  //   const theme = mockTheme()
  //   const anyInstance = instance as any
  //   anyInstance.$meta = () => ({})
  //   theme.init(anyInstance)
  //   expect(typeof anyInstance.$options.metaInfo).toBe('function')
  //   const metaInfo = anyInstance.$options.metaInfo()
  //   expect(metaInfo).toBeTruthy()
  //   expect(metaInfo.style).toHaveLength(1)
  //   expect(metaInfo.style[0].cssText).toMatchSnapshot()
  // })
})


"alignDot": "Align the dot to the **start** or **end** of the item.",

"color": "Applies specified color to the item dot - supports utility colors (for example `success` or `purple`) or css color (for example `success` or `purple`) or css color (`#033` or `rgba(255, 0, 0, 0.5)`). Find a list of built-in classes on the [colors page](/styles/colors#material-colors).",

"fillDot": "Remove outer border of item dot, making the color fill the entire dot.",

"hideDot": "Hide the timeline item dot.",

"hideOpposite": "Hide opposite content if it exists.",

If `true`, window will "wrap around" from the last item to the first, and from the first item to the last.

The transition direction when changing windows.

Icon used for the "next" button if `show-arrows` is `true`.

Icon used for the "prev" button if `show-arrows` is `true`.

Reverse the normal transition direction.

The transition used when the component is reversing items. Can be one of the [built in transitions](/styles/transitions) or one your own.

Display the "next" and "prev" buttons on hover. `show-arrows` MUST ALSO be set.

Provide a custom **left** and **right** function when swiped left or right.

Uses a vertical transition when changing windows.

Emitted when the component value is changed by user interaction.

Slot displaying the arrow switching to the next item.

Slot displaying the arrow switching to the previous item.

Slot for additional content at the end of the component.


<template>
  <v-alert
    type="success"
    title="Alert title"
    text="Lorem ipsum dolor sit amet consectetur adipisicing elit. Commodi, ratione debitis quis est labore voluptatibus! Eaque cupiditate minima, at placeat totam, magni doloremque veniam neque porro libero rerum unde voluptatem!"
  ></v-alert>
</template>


@import '../../styles/styles.sass';

$speed-dial-padding: 16px !default;
$speed-dial-button-margin: 6px !default;
$speed-dial-z-index: 1 !default;


Add dividers between children [v-btn](/components/buttons) components.


"icon": "Apply a specific icon to the inside dot using the [v-icon](/components/icons/) component.",

"index": "Used to allow dynamically shown items to be re-inserted in the correct position.",

"side": "Show the item either **before** or **after** the timeline. This will override the implicit ordering of items, but will in turn be overriden by the `v-timeline` **single-side** prop.",

keep keep keep keep add keep keep keep keep

"lineInset": "Specifies the distance between the line and the dot of the item.",


/// <reference types="../../../../types/cypress" />

// Components
import { VBottomNavigation } from '..'
import { VLayout } from '@/components/VLayout'

describe('VBottomNavigation', () => {
  it('should allow custom height', () => {
    cy.mount(() => (
      <VLayout>
        <VBottomNavigation height="200" />
      </VLayout>
    ))

    cy.get('.v-bottom-navigation').should('have.css', 'height', '200px')
  })

  it('should support density', () => {
    cy.mount(({ density }: any) => (
      <VLayout>
        <VBottomNavigation density={ density } />
      </VLayout>
    ))

    cy.get('.v-bottom-navigation').should('have.css', 'height', '56px')
      .setProps({ density: 'comfortable' })
      .get('.v-bottom-navigation').should('have.css', 'height', '48px')
      .setProps({ density: 'compact' })
      .get('.v-bottom-navigation').should('have.css', 'height', '40px')
  })
})


<template>
  <app-btn>
    {{ title }}

    <chevron-down />

    <app-menu
      :items="items"
      activator="parent"
      width="200"
    />
  </app-btn>
</template>

<script setup lang="ts">
  // Components
  import ChevronDown from '@/components/icons/ChevronDown.vue'

  // Composables
  import { useI18n } from 'vue-i18n'

  // Utilities
  import { computed } from 'vue'
  import { rpath } from '@/util/routes'

  const { t } = useI18n()
  const title = t('learn')
  const items = computed(() => ([
    { subheader: t('overview') },
    {
      title: t('api-explorer'),
      to: rpath('/components/explorer/'),
      appendIcon: 'mdi-code-json',
    },
    {
      title: t('colors'),
      to: rpath('/styles/colors/#material-colors'),
      appendIcon: 'mdi-palette-swatch-outline',
    },
    {
      title: t('components'),
      to: rpath('/components/all/'),
      appendIcon: 'mdi-view-dashboard-outline',
    },
    {
      title: t('feature-guides'),
      to: rpath('/introduction/why-vuetify/#feature-guides'),
      appendIcon: 'mdi-lightbulb-on-outline',
    },
    {
      title: t('labs'),
      to: rpath('/labs/introduction/'),
      appendIcon: 'mdi-beaker-outline',
    },
    {
      title: t('roadmap'),
      to: rpath('/introduction/roadmap/'),
      appendIcon: 'mdi-compass-outline',
    },
    {
      title: t('sass-variables'),
      to: rpath('/features/sass-variables/#variable-api'),
      appendIcon: 'mdi-sass',
    },
    {
      title: t('unit-testing'),
      to: rpath('/getting-started/unit-testing/'),
      appendIcon: 'mdi-list-status',
    },
    { divider: true },
    { subheader: t('documentation') },
    {
      title: '3.x (latest)',
      href: 'https://vuetifyjs.com/',
      appendIcon: 'mdi-numeric-3-box',
    },
    {
      title: '2.6.x (v2-stable)',
      href: 'https://v2.vuetifyjs.com/',
      appendIcon: 'mdi-numeric-2-box',
    },
    {
      title: '1.5.x (v1-stable)',
      href: 'https://v15.vuetifyjs.com/',
      appendIcon: 'mdi-numeric-1-box',
    },
  ]))
</script>


<template>
  <div :id="id" ref="rootEl" />
</template>

<script setup lang="ts">
  // Utilities
  import { IN_BROWSER } from '@/util/globals'
  import { onBeforeMount, onBeforeUnmount, onMounted, ref } from 'vue'

  const props = defineProps({
    id: {
      type: String,
      required: true,
    },
    scriptId: {
      type: String,
      required: true,
    },
    src: {
      type: String,
      required: true,
    },
  })

  const emit = defineEmits(['script:error', 'script:load'])

  const rootEl = ref<HTMLElement>()
  const scriptEl = ref<HTMLScriptElement>()

  onBeforeMount(async () => {
    if (!IN_BROWSER) return

    const script = document.createElement('script')
    const onError = () => emit('script:error')

    script.type = 'text/javascript'
    script.id = props.scriptId
    script.src = props.src
    script.onload = () => emit('script:load')
    script.onerror = onError

    scriptEl.value = script
  })

  onMounted(() => {
    scriptEl.value && rootEl.value?.appendChild(scriptEl.value)
  })

  onBeforeUnmount(() => {
    scriptEl.value && rootEl.value?.removeChild(scriptEl.value)
  })
</script>


"closeDelay": "Delay (in ms) after which menu closes (when open-on-hover prop is set to true).",

"debounce": "Duration before tooltip is shown and hidden when hovered.",

"internalActivator": "Designates whether to use an internal activator.",

"openDelay": "Delay (in ms) after which tooltip opens (when `open-on-hover` prop is set to **true**).",

"openOnClick": "Designates whether the tooltip should open on activator click.",

"id": "HTML id attribute of the tooltip overlay. If not set, a globally unique id will be used.",


---
meta:
  title: Frequently asked questions
  description: Stuck on a problem? Check out the most frequently asked questions by the Vuetify community.
  keywords: frequently asked questions, faq
related:
  - /introduction/why-vuetify/
  - /getting-started/contributing/
  - /getting-started/installation/
---

# Frequently asked questions

Stuck on a particular problem? Check some of these common gotchas before creating a ticket. If you still cannot find what you are looking for, you can submit an [issue](https://issues.vuetifyjs.com/) on GitHub or ask in our [community](https://community.vuetifyjs.com/).

<entry />

## Questions

<promoted slug="vuetify-discord" />

* **What is Vuetify?** { #what-is-vuetify }

  Vuetify is a Vue.js framework that helps to create beautiful and responsive user interfaces. It includes a wide variety of customizable and reusable components for building modern applications.

* **Does Vuetify provide support?** { #does-vuetify-provide-support }

  Vuetify is a free to use Open Source project released under the [MIT](http://opensource.org/licenses/MIT) license. There are multiple ways to receive support for Vuetify:

  * Join our [Discord Community](https://community.vuetifyjs.com/) - (Free/Paid)
  * Ask a question on [GitHub Discussions](https://discussions.vuetifyjs.com/) - (Free)
  * Get [Direct Support](/introduction/enterprise-support/) from Vuetify - (Paid)

* **What is the difference between Vuetify and Vue?** { #what-is-the-difference-between-vuetify-and-vue }

  Vuetify is a framework that is built on top of Vue.js. It is a collection of components that can be used to build applications. Vue.js is a JavaScript framework that is used to build user interfaces.

* **What versions of Vue.js are compatible with Vuetify?** { #what-versions-of-vuejs-are-compatible-with-vuetify }

  Vuetify is compatible with Vue.js 3.0.0 and above.

* **Can I use Vuetify with other CSS frameworks?** { #can-i-use-vuetify-with-other-css-frameworks }

  Yes, you can use Vuetify with other CSS frameworks, but it is typically not recommended. If you are integrating Vuetify into an existing application that is using another CSS framework, you may want to disable the default color and utility generation. See the [SASS Variables](/features/sass-variables/) page for more information.

* **Can I customize the styling of Vuetify components?** { #can-i-customize-the-styling-of-vuetify-components}

  Yes, you can customize the styling of Vuetify components using the [Global configuration](/features/global-configuration/). Vuetify also provides [SASS variables](/features/sass-variables/) that can be overridden to change the look and feel of the components.

* **Where can I get help with Vuetify?** { #where-can-i-get-help-with-vuetify }

  If you need help with an issue, please use one of our help channels:

  * [Vuetify Enterprise Support](/introduction/enterprise-support/)
  * [Discord Community](https://community.vuetifyjs.com/)
  * [GitHub Discussions](https://discussions.vuetifyjs.com/)

* **Can I contribute to Vuetify?** { #can-i-contribute-to-vuetify }

  Yes, we welcome all contributions. Please see our [contributing guide](/getting-started/contributing/) for more information.

* **Can I use Vuetify with server-side rendering?** { #can-i-use-vuetify-with-server-side-rendering }

  Yes, Vuetify supports server-side rendering. Set the `ssr` property to `true` in your `vuetify` configuration object.

  ```js { resource="src/plugins/vuetify.js" }
  import { createVuetify } from 'vuetify'

  export default createVuetify({
    ssr: true,
  })
  ```

* **Is there still support for IE11 in Vuetify 3?** { #is-there-still-support-for-ie11-in-vuetify-3 }

  No. If you need to support IE11, use [Vuetify 2](https://v2.vuetifyjs.com/).

* **Is there support for Nuxt 3?** { #is-there-support-for-nuxt-3 }

  Yes, Vuetify 3 is compatible with Nuxt 3 but does not currently have a community Nuxt module.

* **What is Vuetify Labs?** { #what-is-vuetify-labs }

  Vuetify Labs is a collection of components that are still in development. They are not considered stable and may change at any time. They are not included in the default Vuetify installation and must be imported individually. See the [Labs](/labs/introduction/) page for more information.

* **Does Vuetify have Nightly Builds?** { #does-vuetify-have-nightly-builds }

  Yes, Vuetify has nightly builds. See the [Nightly Builds](/getting-started/installation/#nightly-builds) page for more information.

* **I found a bug, what should I do?** { #i-found-a-bug-what-should-i-do }

  Please create a new [issue](https://issues.vuetifyjs.com/) with our Issue Generator. Please make sure to check for existing issues before creating a new one.

<promoted type="theme" />


<template>
  <v-footer
    class="bg-indigo-lighten-1 text-center d-flex flex-column"
  >

      <v-btn
        v-for="icon in icons"
        :key="icon"
        class="mx-4"
        :icon="icon"
        variant="text"
      ></v-btn>

      Phasellus feugiat arcu sapien, et iaculis ipsum elementum sit amet. Mauris cursus commodo interdum. Praesent ut risus eget metus luctus accumsan id ultrices nunc. Sed at orci sed massa consectetur dignissim a sit amet dui. Duis commodo vitae velit et faucibus. Morbi vehicula lacinia malesuada. Nulla placerat augue vel ipsum ultrices, cursus iaculis dui sollicitudin. Vestibulum eu ipsum vel diam elementum tempor vel ut orci. Orci varius natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus.

    <v-divider></v-divider>

      {{ new Date().getFullYear() }} — Vuetify

  </v-footer>
</template>


<template>
  <div class="d-flex align-center flex-column bg-grey-lighten-4 pa-6">
    <v-btn-toggle
      v-model="toggle"
      divided
    >
      <v-btn icon="mdi-format-align-left"></v-btn>
      <v-btn icon="mdi-format-align-center"></v-btn>
      <v-btn icon="mdi-format-align-right"></v-btn>
      <v-btn icon="mdi-format-align-justify"></v-btn>
    </v-btn-toggle>
  </div>
</template>

<script setup>
  import { ref } from 'vue'

  const toggle = ref(null)
</script>

<script>
  export default {
    data: () => ({
      toggle: null,
    }),
  }
</script>


"openOnHover": "Designates whether the tooltip should open on activator hover.",

"tag": "Specifies a custom tag for the activator wrapper."

"activatorEl": "Ref to the current activator element.",

"animateClick": "Function invoked when user clicks outside.",

"contentEl": "Ref to the current content element.",

"globalTop": "Used by activator to determine a components position in the global stack order.",

"localTop": "Used by activator to determine a components position in the local stack order.",

"updateLocation": "Function used for locationStrategy positioning."


export function getScrollParent (el?: HTMLElement, includeHidden = false) {
  while (el) {
    if (includeHidden ? isPotentiallyScrollable(el) : hasScrollbar(el)) return el
    el = el.parentElement!
  }

  return document.scrollingElement as HTMLElement
}

export function getScrollParents (el?: Element | null, stopAt?: Element | null) {
  const elements: HTMLElement[] = []

  if (stopAt && el && !stopAt.contains(el)) return elements

  while (el) {
    if (hasScrollbar(el)) elements.push(el as HTMLElement)
    if (el === stopAt) break
    el = el.parentElement!
  }

  return elements
}

export function hasScrollbar (el?: Element | null) {
  if (!el || el.nodeType !== Node.ELEMENT_NODE) return false

  const style = window.getComputedStyle(el)
  return style.overflowY === 'scroll' || (style.overflowY === 'auto' && el.scrollHeight > el.clientHeight)
}

function isPotentiallyScrollable (el?: Element | null) {
  if (!el || el.nodeType !== Node.ELEMENT_NODE) return false

  const style = window.getComputedStyle(el)
  return ['scroll', 'auto'].includes(style.overflowY)
}


@use '../tools';
@use '../settings';

@if (settings.$utilities != false and length(settings.$utilities) > 0) {
  $z: 24;
  @while $z >= 0 {
    .elevation-#{$z} {
      @include tools.elevation($z, $important: true);
    }

    $z: $z - 1;
  }
}


"next": "Slot displaying the arrow switching to the next item.",

"prev": "Slot displaying the arrow switching to the previous item.",

"additional": "Slot for additional content at the end of the component."

"group": "Returns item group data, state and helper methods."


<template>
  <v-footer class="bg-grey-lighten-1">
    <v-row justify="center" no-gutters>
      <v-btn
        v-for="link in links"
        :key="link"
        color="white"
        variant="text"
        class="mx-2"
        rounded="xl"
      >
        {{ link }}
      </v-btn>
      <v-col class="text-center mt-4" cols="12">
        {{ new Date().getFullYear() }} — Vuetify
      </v-col>
    </v-row>
  </v-footer>
</template>


---
meta:
  title: Wireframes
  description: Select from a multitude of Vuetify Material Design layouts built to help kickstart your application.
  keywords: pre-defined layouts, layouts, application layout, material design layouts
related:
  - /features/application-layout/
  - /features/theme/
  - /features/blueprints/
---
<script setup>
  import WireframeExamples from '@/components/getting-started/WireframeExamples.vue'
</script>

# Wireframes

The Vuetify **layout system** makes it easy to rapidly scaffold an application's UI regions with little effort.

<entry />

## Examples

These templates are known as **wireframes** and are designed to provide a consistent approach to layouts, layering, and shadows. They are a starting point that is meant to be modified to meet the specific needs of your application.

<wireframe-examples />

::: info
  Additional information on how these templates are structured is located on the [Application page](/features/application-layout/).
:::


"disabled": "Prevents the item from becoming active when using the \"next\" and \"prev\" buttons or the `toggle` method.",

"reverseTransition": "Sets the reverse transition.",

"transition": "The transition used when the component progressing through items. Can be one of the [built in](/styles/transitions/) or custom transition."

"groupItem": "Returns item and item group data, state and helper methods."

Align the dot to the **start** or **end** of the item.

Applies specified color to the item dot - supports utility colors (for example `success` or `purple`) or css color (for example `success` or `purple`) or css color (`#033` or `rgba(255, 0, 0, 0.5)`). Find a list of built-in classes on the [colors page](/styles/colors#material-colors).

Remove outer border of item dot, making the color fill the entire dot.

Apply a specific icon to the inside dot using the [v-icon](/components/icons/) component.

Used to allow dynamically shown items to be re-inserted in the correct position.

Show the item either **before** or **after** the timeline. This will override the implicit ordering of items, but will in turn be overriden by the `v-timeline` **single-side** prop.

Used to customize the icon inside the item dot.

Used to customize the opposite side of timeline items.


<template>
  <div class="d-flex flex-column align-center justify-center">
    <v-rating
      v-model="rating"
      class="ma-2"
      density="default"
    ></v-rating>

    <v-rating
      v-model="rating"
      class="ma-2"
      density="comfortable"
    ></v-rating>

    <v-rating
      v-model="rating"
      class="ma-2"
      density="compact"
    ></v-rating>
  </div>
</template>

<script setup>
  import { ref } from 'vue'

  const rating = ref(3)
</script>

<script>
  export default {
    data: () => ({ rating: 3 }),
  }
</script>


@use '../../styles/settings';
@use '../../styles/tools';

// Defaults
$breadcrumbs-density: ('default': 0, 'comfortable': -1, 'compact': -2) !default;
$breadcrumbs-divider-padding: 0 8px !default;
$breadcrumbs-item-disabled-opacity: var(--v-disabled-opacity) !default;
$breadcrumbs-item-icon-font-size: tools.map-deep-get(settings.$typography, 'body-1', 'size') !default;
$breadcrumbs-item-icon-margin-inline-end: 2px !default;
$breadcrumbs-item-icon-margin-inline-start: -4px !default;
$breadcrumbs-item-link-text-decoration: underline !default;
$breadcrumbs-item-padding: 0 4px !default;
$breadcrumbs-line-height: tools.map-deep-get(settings.$typography, 'subtitle-2', 'line-height') !default;
$breadcrumbs-padding-y: 16px !default;
$breadcrumbs-padding-x: 12px !default;
$breadcrumbs-rounded-border-radius: settings.$border-radius-root !default;
$breadcrumbs-vertical-align: middle !default;


export { md1 } from './md1'
export { md2 } from './md2'
export { md3 } from './md3'


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

<template>
  <v-card
    :to="{ name: 'store/Product', params: { id: shortId }}"
    hover
  >
    <v-container>
      <v-layout>
        <v-flex xs5>
          <v-card-media
            :src="src"
            contain
            height="150"
          />
        </v-flex>
        <v-flex
          d-flex
          xs7
        >
          <v-divider
            class="mr-4"
            vertical
          />
          <div>
            <h3
              class="headline font-weight-medium mb-2"
              v-text="title"
            />
            <div
              class="grey--text mb-4"
              v-text="description"
            />
            <div
              class="headline green--text text--darken-2"
              v-text="`$${price}`"
            />
          </div>
        </v-flex>
      </v-layout>
    </v-container>
  </v-card>
</template>

<script>
  // Utilities
  import {
    isOnSale,
    shortId
  } from '@/util/helpers'

  export default {
    props: {
      price: {
        type: String,
        required: true
      },
      src: {
        type: String,
        required: true
      },
      title: {
        type: String,
        required: true
      },
      value: {
        type: Object,
        required: true
      }
    },

    computed: {
      description () {
        const { description } = this.value

        if (description.length < 55) return description

        return description.slice(0, 52) + '...'
      },
      isOnSale () {
        return isOnSale(this.value.variants)
      },
      shortId () {
        return shortId(this.value.id)
      }
    }
  }
</script>

<style lang="stylus" scoped>
  // Temp fix until update
  .v-divider--vertical
    max-width: 1px
</style>


<template>
  <v-container fluid grid-list-sm>
    <v-layout row wrap>
      <v-flex d-flex xs12 order-xs5>
        <v-layout column>
          <v-flex d-flex>
            <v-card color="blue-grey" dark tile flat>
              <v-card-text>{{ lorem }}</v-card-text>
            </v-card>
          </v-flex>
          <v-flex d-flex>
            <v-card color="brown" dark tile flat>
              <v-card-text>{{ lorem }}</v-card-text>
            </v-card>
          </v-flex>
        </v-layout>
      </v-flex>
      <v-flex d-flex xs12 sm7>
        <v-layout row wrap>
          <v-flex d-flex>
            <v-card color="indigo lighten-2" dark tile flat>
              <v-card-text>{{ lorem.slice(0, 70) }}</v-card-text>
            </v-card>
          </v-flex>
          <v-flex d-flex>
            <v-layout row>
              <v-flex
                v-for="n in 2"
                :key="n"
                d-flex
              >
                <v-card
                  color="amber lighten-2"
                  tile
                  flat
                >
                  <v-card-text>{{ lorem.slice(0, 40) }}</v-card-text>
                </v-card>
              </v-flex>
            </v-layout>
          </v-flex>
        </v-layout>
      </v-flex>
      <v-flex d-flex xs12 sm2 child-flex>
        <v-card color="orange lighten-2" tile flat>
          <v-card-text>{{ lorem.slice(0, 90) }}</v-card-text>
        </v-card>
      </v-flex>
      <v-flex d-flex xs12 sm3>
        <v-card color="red lighten-2" dark tile flat>
          <v-card-text>{{ lorem.slice(0, 100) }}</v-card-text>
        </v-card>
      </v-flex>
    </v-layout>
  </v-container>
</template>

<script>
  export default {
    data: () => ({
      lorem: `Lorem ipsum dolor sit amet, mel at clita quando. Te sit oratio vituperatoribus, nam ad ipsum posidonium mediocritatem, explicari dissentiunt cu mea. Repudiare disputationi vim in, mollis iriure nec cu, alienum argumentum ius ad. Pri eu justo aeque torquatos.`
    })
  }
</script>


수동으로 인풋을 에러 상태로 만듬

인풋을 에러 상태로 만들고 커스텀 에러메시지를 전달. **rules** prop에 의해 발생하는 임이의 검증과  결합가능. 이 필드가 검증을 호출하지는 않습니다.

True나 에러메시지 문자열을 반환하는 함수들의 배열

blur 이벤트까지 검증을 지연


<template>
  <v-navigation-drawer permanent floating light class="transparent">
    <v-card class="ma-3">
      <v-list dense class="py-0">
        <v-list-tile v-for="item in items" :key="item.title" @click="">
          <v-list-tile-action>
            <v-icon>{{ item.icon }}</v-icon>
          </v-list-tile-action>
          <v-list-tile-content>
            <v-list-tile-title>{{ item.title }}</v-list-tile-title>
          </v-list-tile-content>
        </v-list-tile>
      </v-list>
    </v-card>
  </v-navigation-drawer>
</template>

<script>
  export default {
    data () {
      return {
        items: [
          { title: 'Home', icon: 'dashboard' },
          { title: 'About', icon: 'question_answer' }
        ]
      }
    }
  }
</script>


keep keep keep keep keep replace keep keep keep keep keep


<template>
  <div class="text-xs-center">
    <v-btn
      :disabled="dialog"
      :loading="dialog"
      class="white--text"
      color="purple darken-2"
      @click.stop="dialog = true"
    >
      Start loading
    </v-btn>
    <v-dialog
      v-model="dialog"
      hide-overlay
      persistent
      width="300"
    >
      <v-card
        color="primary"
        dark
      >
        <v-card-text>
          Please stand by
          <v-progress-linear
            indeterminate
            color="white"
            class="mb-0"
          ></v-progress-linear>
        </v-card-text>
      </v-card>
    </v-dialog>
  </div>
</template>

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

    watch: {
      dialog (val) {
        if (!val) return

        setTimeout(() => (this.dialog = false), 4000)
      }
    }
  }
</script>


<template>
  <v-container fluid>
    <v-layout row wrap>
      <v-flex xs12 md8 offset-md2>
        <v-card>
          <v-toolbar color="pink" dark>
            <v-icon color="white">arrow_back</v-icon>
            <v-toolbar-title>Compose</v-toolbar-title>
            <v-spacer></v-spacer>
            <v-icon color="white">send</v-icon>
          </v-toolbar>
          <v-container fluid class="pa-0 mt-2">
            <v-layout wrap>
              <v-flex xs2>
                <v-subheader>To</v-subheader>
              </v-flex>
              <v-flex xs10 class="text-xs-right">
                <v-chip>
                  <v-avatar>

                  </v-avatar>
                  Trevor Hansen
                </v-chip>
                <v-chip>
                  <v-avatar>

                  </v-avatar>
                  Alex Nelson
                </v-chip>
              </v-flex>
              <v-flex xs12>
                <v-divider></v-divider>
                <v-text-field
                  label="Subject"
                  value="Plans for the weekend"
                  single-line
                  full-width
                  hide-details
                ></v-text-field>
              </v-flex>
              <v-flex xs12>
                <v-divider></v-divider>
                <v-text-field
                  label="Message"
                  v-model="title"
                  counter
                  max="120"
                  full-width
                  multi-line
                  single-line
                ></v-text-field>
              </v-flex>
            </v-layout>
          </v-container>
        </v-card>
      </v-flex>
    </v-layout>
  </v-container>
</template>


데이터가 없을때 보여지는 문자열

Emitted when the select is changed by user interaction


import { shortId } from '@/util/helpers'

keep keep keep keep keep replace keep keep keep

import {
isOnSale,
shortId
} from '@/util/helpers'

Your purchase helps support the continued maintenance and development of Vuetify

Help support Vuetify's ongoing development and get you and your team some swag!

Description

Having issues?<br>Come to the [community](https://community.vuetifyjs.com)

Product

Name

Price

Quantity


<template lang="pug">
  component-view(v-bind:doc="doc" id="fab-view")
    v-alert(color="info" icon="info" value) For more information on all the options available for the v-btn component, please navigate to the <router-link to="/components/buttons" class="white--text">Button documentation</router-link>.
</template>


<template>
  <v-toolbar>
    <v-toolbar-title>Title</v-toolbar-title>
    <v-spacer></v-spacer>
    <v-toolbar-side-icon class="hidden-md-and-up"></v-toolbar-side-icon>
    <v-toolbar-items class="hidden-sm-and-down">
      <v-btn flat>Link One</v-btn>
      <v-btn flat>Link Two</v-btn>
      <v-btn flat>Link Three</v-btn>
    </v-toolbar-items>
  </v-toolbar>
</template>


<template>
  <div class="text-xs-center">
    <v-menu transition="scale-transition" origin="center center">
      <v-btn dark color="primary" slot="activator">Scale Transition</v-btn>
      <v-list>
        <v-list-tile v-for="n in 5" :key="n" @click="">
          <v-list-tile-title v-text="'Item ' + n"></v-list-tile-title>
        </v-list-tile>
      </v-list>
    </v-menu>
  </div>
</template>


this.highlights = this.products.slice(0, 4).map(product =>

Object.assign({ value: false }, product)

keep keep keep replace replace replace keep keep keep keep

this.highlights = this.products.map(product =>


<template>
  <v-footer
    height="auto"
    color="primary lighten-1"
  >
    <v-layout
      justify-center
      row
      wrap
    >
      <v-btn
        v-for="link in links"
        :key="link"
        color="white"
        flat
        round
      >
        {{ link }}
      </v-btn>
      <v-flex
        primary
        lighten-2
        py-3
        text-xs-center
        white--text
        xs12
      >
        ©2018 — Vuetify
      </v-flex>
    </v-layout>
  </v-footer>
</template>


.v-ripple
  &__container
    color: inherit
    border-radius: inherit
    position: absolute
    width: 100%
    height: 100%
    left: 0
    top: 0
    overflow: hidden
    z-index: 0
    pointer-events: none
    contain: strict

  &__animation
    color: inherit
    position: absolute
    top: 0
    left: 0
    border-radius: 50%
    background: currentColor
    opacity: 0
    pointer-events: none
    overflow: hidden
    will-change: transform, opacity

    &--enter
      transition: none

    &--in
      transition: $ripple-animation-transition-in

    &--out
      transition: $ripple-animation-transition-out


Компоненты `v-progress-circular` и `v-progress-linear` используются для визуального отображения передачи данных пользователям. Они также могут представлять неопределенность, такую как загрузка или обработка. `v-progress-circular` компонент содержат слот, который расположен по центру в контейнере компонента.

По умолчанию

По умолчанию, круговой прогресс бар использует вторичный цвет приложения.

Могут применяться альтернативные цвета.

Неопределенный

Неопределенный прогресс бар всегда выполняется.

Размер и ширина

Компонент кругового прогресс бара может изменять ширину и размер.

Начало прогресс бара может быть повернуто.

Определенный

Компонент линейного прогресс бара может иметь заданное состояние, модифицированное `v-model`.

Неопределенный

Как и в случае с круговым прогресс баром, линейный имеет также неопределенное состояние.

Состояние буфера представляет два значения одновременно. Основное значение контролируется моделью, тогда как буфер управляется значением свойства `buffer-value`.

Запрос неопределенный и выполненный

Состояние управляется правдивостью неопределенного с запросом свойства, установленным в true.

Пользовательская высота и контекстные цвета

Пользовательская высота или контекстный цвет могут быть применены к индикатору выполнения. Основной цвет - это основной цвет вашего приложения.

Пользовательские цвета

Вы также можете установить цвет с помощью реквизита `color` и `background-color`.


keep keep keep keep replace replace keep keep keep keep keep

class="headline grey--text text--darken-2 text--line-through"

:class="compareAtPrice ? 'red--text' : 'grey--text'"

<template>
  <v-card
    class="elevation-8 mb-5"
    tile
  >
    <v-layout
      fill-height
      wrap
    >
      <v-flex
        md6
        xs12
      >
        <v-card
          flat
          height="100%"
          tile
        >
          <v-card-media
            height="100%"
            src="/doc-images/store/angryman.png"
          />
        </v-card>
      </v-flex>

      <v-flex
        md6
        xs12
      >
        <v-layout
          fill-height
          wrap
        >
          <v-flex
            v-for="(highlight, i) in highlights"
            :key="i"
            xs6
          >
            <v-card
              :to="{
                name: 'store/Product',
                params: { id: shortId(highlight.id) }
              }"
              :color="highlight.value ? 'blue lighten-4' : ''"
              class="pa-3"
              flat
              height="auto"
              tile
              style="transition: .2s ease;"
              @mouseenter="highlight.value = true"
              @mouseleave="highlight.value = false"
            >
              <v-card-media
                :src="highlight.variants[0].image.src"
                contain
                height="135px"
              />
            </v-card>
          </v-flex>
        </v-layout>
      </v-flex>
    </v-layout>
  </v-card>
</template>

<script>
  // Helpers
  import { mapState } from 'vuex'
  import { shortId } from '@/util/helpers'

  export default {
    data: () => ({
      highlights: []
    }),

    computed: {
      ...mapState('store', ['products'])
    },

    watch: {
      products: {
        immediate: true,
        handler () {
          this.highlights = this.products.slice(0, 4).map(product =>
            Object.assign({ value: false }, product)
          )
        }
      }
    },

    methods: {
      shortId: shortId
    }
  }
</script>


import Common from './Common'
import Events from './Events'
import Pages from './Pages'
import Params from './Params'
import Props from './Props'
import Slots from './Slots'

export default {
  Common,
  Events,
  Pages,
  Params,
  Props,
  Slots
}


// Mixins
import { factory as GroupableFactory } from '../../mixins/groupable'
import Routable from '../../mixins/routable'
import Themeable from '../../mixins/themeable'

// Utilities
import { getObjectValueByPath } from '../../util/helpers'

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

  mixins: [
    Routable,
    // Must be after routable
    // to overwrite activeClass
    GroupableFactory('tabGroup'),
    Themeable
  ],

  props: {
    ripple: {
      type: [Boolean, Object],
      default: true
    }
  },

  computed: {
    classes () {
      return {
        'v-tabs__item': true,
        'v-tabs__item--disabled': this.disabled,
        ...this.groupClasses
      }
    },
    value () {
      let to = this.to || this.href || ''

      if (this.$router &&
        this.to === Object(this.to)
      ) {
        const resolve = this.$router.resolve(
          this.to,
          this.$route,
          this.append
        )

        to = resolve.href
      }

      return to.replace('#', '')
    }
  },

  watch: {
    $route: 'onRouteChange'
  },

  mounted () {
    this.onRouteChange()
  },

  methods: {
    click (e) {
      // If user provides an
      // actual link, do not
      // prevent default
      if (this.href &&
        this.href.indexOf('#') > -1
      ) e.preventDefault()

      this.$emit('click', e)

      this.to || this.toggle()
    },
    onRouteChange () {
      if (!this.to || !this.$refs.link) return

      const path = `_vnode.data.class.${this.activeClass}`

      this.$nextTick(() => {
        if (getObjectValueByPath(this.$refs.link, path)) {
          this.toggle()
        }
      })
    }
  },

  render (h) {
    const link = this.generateRouteLink(this.classes)
    const { data } = link

    // If disabled, use div as anchor tags do not support
    // being disabled
    const tag = this.disabled ? 'div' : link.tag

    data.ref = 'link'

    return h('div', {
      staticClass: 'v-tabs__div'
    }, [h(tag, data, this.$slots.default)])
  }
}


<template>
  <div class="text-xs-center">
    <v-menu
      origin="center center"
      transition="scale-transition"
      bottom
    >
      <v-btn color="primary" dark slot="activator">
        Scale Transition
      </v-btn>
      <v-list>
        <v-list-tile v-for="item in items" :key="item.title" @click="">
          <v-list-tile-title>{{ item.title }}</v-list-tile-title>
        </v-list-tile>
      </v-list>
    </v-menu>

    <v-menu
      transition="slide-x-transition"
      bottom
      right
    >
      <v-btn color="primary" dark slot="activator" class="deep-orange">
        Slide X Transition
      </v-btn>
      <v-list>
        <v-list-tile v-for="item in items" :key="item.title" @click="">
          <v-list-tile-title>{{ item.title }}</v-list-tile-title>
        </v-list-tile>
      </v-list>
    </v-menu>

    <v-menu
      transition="slide-y-transition"
      bottom
    >
      <v-btn color="primary" dark slot="activator" class="purple">
        Slide Y Transition
      </v-btn>
      <v-list>
        <v-list-tile v-for="item in items" :key="item.title" @click="">
          <v-list-tile-title>{{ item.title }}</v-list-tile-title>
        </v-list-tile>
      </v-list>
    </v-menu>
  </div>
</template>

<script>
  export default {
    data: () => ({
      items: [
        { title: 'Click Me' },
        { title: 'Click Me' },
        { title: 'Click Me' },
        { title: 'Click Me 2' }
      ]
    })
  }
</script>


<script>
// Utilities
import {
isOnSale,
shortId
} from '@/util/helpers'

Description

Product

Name

Price

Quantity


The `v-tooltip` component is useful for conveying information when a user hovers over an element. You can also programmatically control the display of tooltips through a **v-model**

Tooltip can be aligned to any of the four sides of the activator element

Tooltip visibility can be programmatically changed using `v-model`.

Delay (in ms) after which menu closes (when open-on-hover prop is set to true)

Duration before tooltip is shown and hidden when hovered

Delay (in ms) after which menu opens (when open-on-hover prop is set to true)

Specifies a custom tag for the activator wrapper


The `v-btn` component replaces the standard html button with a material design theme and a multitude of options. Any color helper class can be used to alter the background or text color.

Flat buttons have no box shadow and no background. Only on hover is the container for the button shown.

Raised buttons have a box shadow that increases when clicked. This is the default style.

Depressed buttons still maintain their background color, but have no box shadow.

Button dropdowns are regular selects with additional styling.

Toggle buttons are essentially styled radio or checkboxes, depending on the properties selected. This component is compatible with the `v-toolbar` component.

Icons can be used for the primary content of a button.

Floating buttons are round and usually contain an icon.

Using the loading prop, you can notify a user that there is processing taking place. The default behavior is to use a `v-progress-circular` component but this can be customized.

Buttons can be given different sizing options to fit a multitude of scenarios.

Outline buttons inherit their borders from the current color applied.

Rounded buttons behave the same as regular buttons but have rounded edges.

Block buttons extend the full available width.

Easily integrate customized button solutions with a `v-toolbar`

Expands the button to 100% of available space

Designates the button as icon - round and flat


// Styles
import '../../stylus/components/_item-group.styl'

// Mixins
import Groupable from '../../mixins/groupable'
import Proxyable from '../../mixins/proxyable'
import Themeable from '../../mixins/themeable'

// Utilities
import mixins from '../../util/mixins'
import { consoleWarn } from '../../util/console'

// Types
import { VNode } from 'vue/types'

type GroupableInstance = InstanceType<typeof Groupable> & { value?: any }

export const BaseItemGroup = mixins(
  Proxyable,
  Themeable
).extend({
  name: 'base-item-group',

  props: {
    activeClass: {
      type: String,
      default: 'v-item--active'
    },
    mandatory: Boolean,
    max: {
      type: [Number, String],
      default: null
    },
    multiple: Boolean
  },

  data () {
    return {
      // As long as a value is defined, show it
      // Otherwise, check if multiple
      // to determine which default to provide
      internalLazyValue: this.value !== undefined
        ? this.value
        : this.multiple ? [] : undefined,
      items: [] as GroupableInstance[]
    }
  },

  computed: {
    classes (): Record<string, boolean> {
      return {
        ...this.themeClasses
      }
    },
    selectedItems (): GroupableInstance[] {
      return this.items.filter((item, index) => {
        return this.toggleMethod(this.getValue(item, index))
      })
    },
    selectedValues (): any[] {
      return Array.isArray(this.internalValue)
        ? this.internalValue
        : [this.internalValue]
    },
    toggleMethod (): (v: any) => boolean {
      if (!this.multiple) {
        return (v: any) => this.internalValue === v
      }

      const internalValue = this.internalValue
      if (Array.isArray(internalValue)) {
        return (v: any) => internalValue.includes(v)
      }

      return () => false
    }
  },

  watch: {
    internalValue () {
      // https://github.com/vuetifyjs/vuetify/issues/5352
      this.$nextTick(this.updateItemsState)
    }
  },

  created () {
    if (this.multiple && !Array.isArray(this.internalValue)) {
      consoleWarn('Model must be bound to an array if the multiple property is true.', this)
    }
  },

  methods: {
    getValue (item: GroupableInstance, i: number): unknown {
      return item.value == null || item.value === ''
        ? i
        : item.value
    },
    onClick (item: GroupableInstance, index: number) {
      this.updateInternalValue(
        this.getValue(item, index)
      )
    },
    register (item: GroupableInstance) {
      const index = this.items.push(item) - 1

      item.$on('change', () => this.onClick(item, index))

      // If no value provided and mandatory,
      // assign first registered item
      if (this.mandatory && this.internalLazyValue == null) {
        this.updateMandatory()
      }

      this.updateItem(item, index)
    },
    unregister (item: GroupableInstance) {
      if (this._isDestroyed) return

      const index = this.items.indexOf(item)
      const value = this.getValue(item, index)

      this.items.splice(index, 1)

      const valueIndex = this.selectedValues.indexOf(value)

      // Items is not selected, do nothing
      if (valueIndex < 0) return

      // If not mandatory, use regular update process
      if (!this.mandatory) {
        return this.updateInternalValue(value)
      }

      // Remove the value
      if (this.multiple && Array.isArray(this.internalValue)) {
        this.internalValue = this.internalValue.filter(v => v !== value)
      } else {
        this.internalValue = undefined
      }

      // If mandatory and we have no selection
      // add the last item as value
      /* istanbul ignore else */
      if (!this.selectedItems.length) {
        this.updateMandatory(true)
      }
    },
    updateItem (item: GroupableInstance, index: number) {
      const value = this.getValue(item, index)

      item.isActive = this.toggleMethod(value)
    },
    updateItemsState () {
      if (this.mandatory &&
        !this.selectedItems.length
      ) {
        return this.updateMandatory()
      }

      // TODO: Make this smarter so it
      // doesn't have to iterate every
      // child in an update
      this.items.forEach(this.updateItem)
    },
    updateInternalValue (value: any) {
      this.multiple
        ? this.updateMultiple(value)
        : this.updateSingle(value)
    },
    updateMandatory (last?: boolean) {
      if (!this.items.length) return

      const index = last ? this.items.length - 1 : 0

      this.updateInternalValue(
        this.getValue(this.items[index], index)
      )
    },
    updateMultiple (value: any) {
      const defaultValue = Array.isArray(this.internalValue)
        ? this.internalValue
        : []
      const internalValue = defaultValue.slice()
      const index = internalValue.findIndex(val => val === value)

      if (
        this.mandatory &&
        // Item already exists
        index > -1 &&
        // value would be reduced below min
        internalValue.length - 1 < 1
      ) return

      if (
        // Max is set
        this.max != null &&
        // Item doesn't exist
        index < 0 &&
        // value woudl be increased above max
        internalValue.length + 1 > this.max
      ) return

      index > -1
        ? internalValue.splice(index, 1)
        : internalValue.push(value)

      this.internalValue = internalValue
    },
    updateSingle (value: any) {
      const isSame = value === this.internalValue

      if (this.mandatory && isSame) return

      this.internalValue = isSame ? undefined : value
    }
  },

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

export default BaseItemGroup.extend({
  name: 'v-item-group',

  provide (): object {
    return {
      itemGroup: this
    }
  }
})


export default {
props: {
price: {
type: String,
required: true
},
src: {
type: String,
required: true
},
title: {
type: String,
required: true
},
value: {
type: Object,
required: true

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


const fs = require('fs')
const path = require('path')
const LRU = require('lru-cache')
const express = require('express')
const cookieParser = require('cookie-parser')
const favicon = require('serve-favicon')
const compression = require('compression')
const microcache = require('route-cache')
const resolve = file => path.resolve(__dirname, file)
const { createBundleRenderer } = require('vue-server-renderer')
const Ouch = require('ouch')
const redirects = require('./src/router/301.json')

const isProd = process.env.NODE_ENV === 'production'
const useMicroCache = process.env.MICRO_CACHE !== 'false'
const serverInfo =
  `express/${require('express/package.json').version} ` +
  `vue-server-renderer/${require('vue-server-renderer/package.json').version}`

const availableLanguages = require('./src/data/i18n/languages').map(lang => lang.locale)

const app = express()

function createRenderer (bundle, options) {
  // https://github.com/vuejs/vue/blob/dev/packages/vue-server-renderer/README.md#why-use-bundlerenderer
  return createBundleRenderer(bundle, Object.assign(options, {
    // for component caching
    cache: LRU({
      max: 1000,
      maxAge: 1000 * 60 * 15
    }),
    // this is only needed when vue-server-renderer is npm-linked
    basedir: resolve('./public'),
    // recommended for performance
    runInNewContext: false
  }))
}

let renderer
let readyPromise
const templatePath = resolve('./src/index.template.html')
if (isProd) {
  // In production: create server renderer using template and built server bundle.
  // The server bundle is generated by vue-ssr-webpack-plugin.
  const template = fs.readFileSync(templatePath, 'utf-8')
  const bundle = require('./dist/vue-ssr-server-bundle.json')
  // The client manifests are optional, but it allows the renderer
  // to automatically infer preload/prefetch links and directly add <script>
  // tags for any async chunks used during render, avoiding waterfall requests.
  const clientManifest = require('./dist/vue-ssr-client-manifest.json')
  renderer = createRenderer(bundle, {
    template,
    clientManifest
  })
} else {
  // In development: setup the dev server with watch and hot-reload,
  // and create a new renderer on bundle / index template update.
  readyPromise = require('./build/setup-dev-server')(
    app,
    templatePath,
    (bundle, options) => {
      renderer = createRenderer(bundle, options)
    }
  )
}

const serve = (path, cache) => express.static(resolve(path), {
  maxAge: cache && isProd ? 1000 * 60 * 60 * 24 * 30 : 0
})

app.use(express.json())
app.use(cookieParser())
app.use(compression({ threshold: 0 }))
app.use(favicon('./src/public/favicon.ico'))
app.use('/', serve('./src/public', true))
app.use('/dist', serve('./dist', true))
app.use('/releases', serve('./src/releases'))
app.use('/themes', serve('./src/themes'))
app.get('/releases/:release', (req, res) => {
  res.setHeader('Content-Type', 'text/html')
  res.sendFile(resolve(`./src/releases/${req.params.release}`))
})

app.get('/sitemap.xml', (req, res) => {
  res.setHeader('Content-Type', 'text/xml')
  res.sendFile(resolve('./src/public/sitemap.xml'))
})

if (process.env.TRANSLATE) {
  app.use('/api/translation', require('./src/translation/router'))
}

// 301 redirect for changed routes
Object.keys(redirects).forEach(k => {
  app.get(k, (req, res) => res.redirect(301, redirects[k]))
})

// since this app has no user-specific content, every page is micro-cacheable.
// if your app involves user-specific content, you need to implement custom
// logic to determine whether a request is cacheable based on its url and
// headers.
// 10-minute microcache.
// https://www.nginx.com/blog/benefits-of-microcaching-nginx/
const isStore = req => !!req.params && !!req.params[1] && req.params[1].includes('store')
const cacheMiddleware = microcache.cacheSeconds(10 * 60, req => useMicroCache && !isStore(req) && req.originalUrl)

const ouchInstance = (new Ouch()).pushHandler(new Ouch.handlers.PrettyPageHandler('orange', null, 'sublime'))

function render (req, res) {
  const s = Date.now()

  res.setHeader('Content-Type', 'text/html')
  res.setHeader('Server', serverInfo)
  res.cookie('currentLanguage', req.params[0], {
    maxAge: 1000 * 60 * 60 * 24 * 7 // 7 days
  })

  const handleError = err => {
    if (err.url) {
      res.redirect(err.url)
    } else if (err.code === 404) {
      res.status(404).send('404 | Page Not Found')
    } else {
      ouchInstance.handleException(err, req, res, output => {
        console.log('Error handled!')
      })
    }
  }

  const context = {
    title: 'Vuetify', // default title
    url: req.url,
    lang: req.params[0],
    res,
    hreflangs: availableLanguages.reduce((acc, lang) => {
      return acc + `<link rel="alternate" hreflang="${lang}" href="https://${req.hostname}/${lang}${req.params[1]}" />`
    }, '')
  }
  renderer.renderToString(context, (err, html) => {
    if (err) {
      return handleError(err)
    }
    res.end(html)
    if (!isProd) {
      console.log(`whole request: ${Date.now() - s}ms`)
    }
  })
}

const languageRegex = /^\/([a-z]{2,3}|[a-z]{2,3}-[a-zA-Z]{4}|[a-z]{2,3}-[A-Z]{2,3})(\/.*)?$/

app.get(languageRegex, isProd ? render : (req, res) => {
  readyPromise.then(() => render(req, res))
}, cacheMiddleware)

// 302 redirect for no language
app.get('*', (req, res) => {
  let lang = req.cookies.currentLanguage || req.acceptsLanguages(availableLanguages) || 'en'
  if (!languageRegex.test('/' + lang)) lang = 'en'
  res.redirect(302, `/${lang}${req.originalUrl}`)
})

const port = process.env.PORT || 8095
const host = process.env.HOST || '0.0.0.0'
app.listen(port, host, () => {
  console.log(`server started at ${host}:${port}`)
})


<template lang="pug">
  component-view(v-bind:doc="doc")
</template>


<template>
  <v-layout row>
    <v-flex xs12 sm6 offset-sm3>
      <v-card>
        <v-toolbar class="white--text indigo" dark>
          <v-toolbar-side-icon></v-toolbar-side-icon>
          <v-toolbar-title>Inbox</v-toolbar-title>
          <v-spacer></v-spacer>
          <v-btn icon>
             <v-icon>search</v-icon>
          </v-btn>
          <v-btn icon>
            <v-icon>more_vert</v-icon>
          </v-btn>
        </v-toolbar>
        <v-list>
          <v-list-tile avatar v-for="item in items" v-bind:key="item.title" @click="">
            <v-list-tile-action>
              <v-icon v-if="item.icon" class="pink--text">star</v-icon>
            </v-list-tile-action>
            <v-list-tile-content>
              <v-list-tile-title v-text="item.title"></v-list-tile-title>
            </v-list-tile-content>
            <v-list-tile-avatar>
              <img v-bind:src="item.avatar"/>
            </v-list-tile-avatar>
          </v-list-tile>
        </v-list>
      </v-card>
    </v-flex>
  </v-layout>
</template>

<script>
  export default {
    data () {
      return {
        items: [
          { icon: true, title: 'Jason Oner', avatar: '/doc-images/lists/1.jpg' },
          { title: 'Travis Howard', avatar: '/doc-images/lists/2.jpg' },
          { title: 'Ali Connors', avatar: '/doc-images/lists/3.jpg' },
          { title: 'Cindy Baker', avatar: '/doc-images/lists/4.jpg' },
        ]
      }
    }
  }
</script>


keep keep keep keep keep replace replace keep keep keep

for (const variant of this.value.variants) {

const diff = parseFloat(variant.price) < parseFloat(variant.compareAtPrice)

<template>
  <v-card
    :to="{ name: 'store/Product', params: { id: shortId }}"
    hover
  >
    <v-container>
      <v-layout>
        <v-flex xs5>
          <v-card-media
            :src="src"
            contain
            height="150"
          />
        </v-flex>
        <v-flex
          d-flex
          xs7
        >
          <v-divider
            class="mr-4"
            vertical
          />
          <div>
            <h3
              class="headline font-weight-medium mb-2"
              v-text="title"
            />
            <div
              class="grey--text mb-4"
              v-text="description"
            />
            <div
              class="headline green--text text--darken-2"
              v-text="`$${price}`"
            />
          </div>
        </v-flex>
      </v-layout>
    </v-container>
  </v-card>
</template>

<script>
  // Utilities
  import {
    isOnSale,
    shortId
  } from '@/util/helpers'

  export default {
    props: {
      price: {
        type: String,
        required: true
      },
      src: {
        type: String,
        required: true
      },
      title: {
        type: String,
        required: true
      },
      value: {
        type: Object,
        required: true
      }
    },

    computed: {
      description () {
        const { description } = this.value

        if (description.length < 55) return description

        return description.slice(0, 52) + '...'
      },
      isOnSale () {
        return isOnSale(this.value.variants)
      },
      shortId () {
        return shortId(this.value.id)
      }
    }
  }
</script>

<style lang="stylus" scoped>
  // Temp fix until update
  .v-divider--vertical
    max-width: 1px
</style>


<template>
  <v-container fluid>
    <v-layout row wrap class="dark--text">
      <v-flex xs6>on</v-flex>
      <v-flex xs6>off</v-flex>
    </v-layout>
    <v-layout row wrap>
      <v-flex xs6>
        <v-switch value input-value="true" ></v-switch>
      </v-flex>
      <v-flex xs6>
        <v-switch ></v-switch>
      </v-flex>
    </v-layout>
    <v-layout row wrap class="dark--text">
      <v-flex xs6>on disabled</v-flex>
      <v-flex xs6>off disabled</v-flex>
    </v-layout>
    <v-layout row wrap>
      <v-flex xs6>
        <v-switch value input-value="true" disabled></v-switch>
      </v-flex>
      <v-flex xs6>
        <v-switch disabled></v-switch>
      </v-flex>
    </v-layout>
  </v-container>
</template>


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

// Components
import VIcon from '../VIcon'
// import { VFadeTransition } from '../transitions'

// Mixins
import Selectable from '../../mixins/selectable'

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

  mixins: [
    Selectable
  ],

  props: {
    indeterminate: Boolean,
    indeterminateIcon: {
      type: String,
      default: '$vuetify.icons.checkboxIndeterminate'
    },
    onIcon: {
      type: String,
      default: '$vuetify.icons.checkboxOn'
    },
    offIcon: {
      type: String,
      default: '$vuetify.icons.checkboxOff'
    }
  },

  data: vm => ({
    inputIndeterminate: vm.indeterminate
  }),

  computed: {
    classes () {
      return {
        'v-input--selection-controls': true,
        'v-input--checkbox': true
      }
    },
    computedIcon () {
      if (this.inputIndeterminate) {
        return this.indeterminateIcon
      } else if (this.isActive) {
        return this.onIcon
      } else {
        return this.offIcon
      }
    }
  },

  watch: {
    indeterminate (val) {
      this.inputIndeterminate = val
    }
  },

  methods: {
    genCheckbox () {
      return this.$createElement('div', {
        staticClass: 'v-input--selection-controls__input'
      }, [
        this.genInput('checkbox', {
          ...this.$attrs,
          'aria-checked': this.inputIndeterminate
            ? 'mixed'
            : this.isActive.toString()
        }),
        !this.disabled && this.genRipple(this.setTextColor(this.computedColor)),
        this.$createElement(VIcon, this.setTextColor(this.computedColor, {
          props: {
            dark: this.dark,
            light: this.light
          }
        }), this.computedIcon)
      ])
    },
    genDefaultSlot () {
      return [
        this.genCheckbox(),
        this.genLabel()
      ]
    }
  }
}


<template>
  <v-card>
    <v-card-text>
      <v-text-field label="Mask" v-model="mask"></v-text-field>
    </v-card-text>
    <v-card-text>
      <v-text-field label="Value" :mask="mask" v-model="value"></v-text-field>
    </v-card-text>
  </v-card>
</template>

<script>
  export default {
    data: () => ({
      mask: 'credit-card',
      value: '4444444444444444'
    })
  }
</script>


},
shortId () {
return shortId(this.value.id)
}
}
}
</script>

Description

Product

Name

Price

Quantity


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

/* Theme */
v-treeview($material)
  color: $material.text.primary

  &--hoverable .v-treeview-node__root:hover,
  .v-treeview-node--active
    background: $material.dividers

theme(v-treeview, "v-treeview")

.v-treeview
  > .v-treeview-node
    margin-left: 0

    &--leaf
      margin-left: 24px

  &-node
    margin-left: 26px

    &--click
      > .v-treeview-node__root,
      > .v-treeview-node__root > .v-treeview-node__content > *
        cursor: pointer
        user-select: none

    &--leaf
      margin-left: 50px

    &__root
      display: flex
      align-items: center
      height: 34px

    &__content
      display: flex
      flex-grow: 1
      flex-shrink: 0
      align-items: center

      // TODO: this is temporary fix for d-flex * shenanigans
      .v-btn
        flex-grow: 0 !important
        flex-shrink: 1 !important

    &__label
      font-size: 1.2rem
      margin-left: 6px
      flex-grow: 1
      flex-shrink: 0

      .v-icon
        padding-right: 8px

    &__checkbox
      user-select: none

    &__toggle
      user-select: none
      transform: rotate(-90deg)

      &--open
        transform: none

      &--loading
        animation: progress-circular-rotate 1s linear infinite

    &__children
      transition: all $treeview-transition


<template lang="pug">
  doc-view#sponsors-and-backers-view
    v-layout(row)
      v-flex(xs12)
        section-def
          dt(slot="title") Support Vuetify.js development
          dd(slot="desc") Vuetify.js is an open source project under the MIT license. It is and will always be free to use. However, the amount of effort it takes to maintain the core framework and all supplementing packages is quite considerable. With the goal to continue development of additional functionality even after the completion of Material Design spec, you can help by backing Vuetify on Patreon.

    section#sponsors-and-backers
      h2.headline Patreon backers:
      v-divider
      v-list.transparent
        v-list-tile
          v-list-tile-title
            strong Diamond ($500/mo)
      v-container(fluid grid-list-md).mb-5
        v-layout(row wrap justify-start align-center)
          a(
            :href="`${backer.href}?ref=vuetifyjs.com`"
            target="_blank"
            :title="backer.title"
            v-for="backer in diamond"
            v-bind:key="backer.title"
          ).text-xs-center.mx-3
            img(
              :src="`/doc-images/backers/${backer.src}`"
              :alt="backer.title"
            )

      v-list.transparent
        v-list-tile(tag="div")
          v-list-tile-title
            strong Palladium ($250/mo)
      v-container(fluid grid-list-md).mb-5
        v-layout(row wrap justify-start align-center)
          a(
            target="_blank"
            :class="[backer.dark ? 'black' : '']"
            :href="`${backer.href}?ref=vuetifyjs.com`"
            :title="backer.title"
            v-for="backer in palladium"
            v-bind:key="backer.title"
          ).text-xs-center.mx-3
            img(
              :src="`/doc-images/backers/${backer.src}`"
              :alt="backer.title"
            )

      v-list.transparent
        v-list-tile(tag="div")
          v-list-tile-title
            strong Gold ($50/mo)
      v-container(fluid grid-list-md).mb-5
        v-layout(row wrap justify-start align-center)
          a(
            target="_blank"
            :class="[backer.dark ? 'black' : '']"
            :href="`${backer.href}?ref=vuetifyjs.com`"
            :title="backer.title"
            v-for="backer in gold"
            v-bind:key="backer.title"
          ).text-xs-center.mx-3
            img(
              :src="`/doc-images/backers/${backer.src}`"
              :alt="backer.title"
              style="max-width: 175px;"
            )

      h2.headline Affiliates:
      v-divider.mb-3
      v-container(fluid grid-list-md).mb-5.affiliates
        v-layout(row wrap justify-start align-center)
          a(
            target="_blank"
            :class="[affiliate.dark ? 'black' : '']"
            :href="`${affiliate.href}${affiliate.noref ? '' : '?ref=vuetifyjs.com'}`"
            :title="affiliate.title"
            v-for="affiliate in affiliates"
            v-bind:key="affiliate.title"
          ).text-xs-center.mx-3
            img(
              :src="`/doc-images/affiliates/${affiliate.src}`"
              :alt="affiliate.title"
              style="max-width: 175px;"
            )

      h2.headline Service sponsors:
      v-divider.mb-3
      v-container(fluid).mb-5
        v-layout(row wrap justify-start align-center)
          a(
            :href="`${sponsor.href}?ref=vuetifyjs.com`"
            target="_blank"
            :title="sponsor.title"
            v-for="sponsor in sponsors"
            v-bind:key="sponsor.title"
          ).text-xs-center.mx-3
            img(
              :src="`/doc-images/${sponsor.src}`"
              alt="sponsor.title"
            )

      div.text-xs-center
        div.mb-3 Have additional questions?
        v-btn(outline color="success" round href="mailto:john@vuetifyjs.com") Contact Us
</template>