List continued (#174)

* working on  story with reactive subtitle

* styling of icons seems consistent with default sizing

* checkbox in list story

* colors

* lines and tests

* merge conflict

* basically im a genius styling is cool now

* Update src/components/v-button/readme.md

* Fix scoping of nav mode

* Tweak spacing of icons

* Register list item subcomponents globally

* Render icons in sidebar nav of collections module

Co-authored-by: Rijk van Zanten <rijkvanzanten@me.com>
This commit is contained in:
Jacob Rienstra
2020-03-13 16:34:06 -04:00
committed by GitHub
parent c8e9e02af0
commit 197e09abb5
18 changed files with 740 additions and 221 deletions

View File

@@ -4,10 +4,7 @@
"stylelint-config-rational-order",
"stylelint-config-prettier"
],
"plugins": [
"stylelint-order",
"stylelint-scss"
],
"plugins": ["stylelint-order", "stylelint-scss"],
"rules": {
"indentation": "tab",
"order/order": [
@@ -27,6 +24,7 @@
}
],
"string-quotes": "single",
"length-zero-no-unit": null
"length-zero-no-unit": null,
"no-descending-specificity": true
}
}

View File

@@ -9,7 +9,13 @@ import VHover from './v-hover/';
import VIcon from './v-icon/';
import VInput from './v-input/';
import VItemGroup, { VItem } from './v-item-group';
import VList, { VListItem, VListItemContent } from './v-list/';
import VList, {
VListItem,
VListItemContent,
VListItemIcon,
VListItemSubtitle,
VListItemTitle
} from './v-list/';
import VOverlay from './v-overlay/';
import VProgressLinear from './v-progress/linear/';
import VProgressCircular from './v-progress/circular/';
@@ -31,6 +37,9 @@ Vue.component('v-item', VItem);
Vue.component('v-list', VList);
Vue.component('v-list-item', VListItem);
Vue.component('v-list-item-content', VListItemContent);
Vue.component('v-list-item-icon', VListItemIcon);
Vue.component('v-list-item-subtitle', VListItemSubtitle);
Vue.component('v-list-item-title', VListItemTitle);
Vue.component('v-overlay', VOverlay);
Vue.component('v-progress-linear', VProgressLinear);
Vue.component('v-progress-circular', VProgressCircular);

View File

@@ -46,9 +46,10 @@ The button has a loading state that can be enabled with the `loading` prop. By d
The loading slot is rendered _on top_ of the content that was there before. Make sure that your loading content doesn't exceed the size of the default state content. This restriction is put in place to prevent jumps when going from and to the loading state.
## Props
| Prop | Description | Default |
|------------|---------------------------------------------------------------------------|----------|
| `block` | Enable ull width (display block) | `false` |
| `block` | Enable full width (display block) | `false` |
| `icon` | Remove padding / min-width. Meant to be used with just an icon as content | `false` |
| `outlined` | No background | `false` |
| `rounded` | Enable rounded corners | `false` |
@@ -62,17 +63,20 @@ The loading slot is rendered _on top_ of the content that was there before. Make
| `to` | Render as vue router-link | `false` |
## Slots
| Slot | Description |
|-----------|----------------------------------------------|
| _default_ | Button content |
| `loading` | Content that's rendered during loading state |
## Events
| Event | Description | Value |
|---------|-----------------------|--------------|
| `click` | User clicks on button | `MouseEvent` |
## CSS Variables
| Variable | Default |
|-----------------------------------------|----------------------------------------------------|
| `--v-button-width` | `auto` |

View File

@@ -78,6 +78,7 @@ export default defineComponent({
position: relative;
display: inline-block;
width: var(--v-icon-size);
min-width: var(--v-icon-size);
height: var(--v-icon-size);
color: var(--v-icon-color);
font-size: 0;

View File

@@ -1,6 +1,9 @@
import VList from './v-list.vue';
import VListItem from './v-list-item.vue';
import VListItemContent from './v-list-item-content.vue';
import VListItemTitle from './v-list-item-title.vue';
import VListItemSubtitle from './v-list-item-subtitle.vue';
import VListItemIcon from './v-list-item-icon.vue';
export { VList, VListItem, VListItemContent };
export { VList, VListItem, VListItemContent, VListItemTitle, VListItemSubtitle, VListItemIcon };
export default VList;

View File

@@ -10,9 +10,49 @@
</v-list>
```
## Subcomponents
### List Item
A wrapper for list items that formats children nicely. Can be used on its own or inside a list component. Best used with subcomponents (see below).
### List Item Content
A wrapper for the main text content of a list item. It adds some padding and helps control overflow. The parent of `v-list-title` and `v-list-subtitle` components, it's also the main controller of the `dense` option on lists.
### List Item Title
Wrapper that adds typographic styling and margin for the title of the list item. Responsive to `dense` styling.
### List Item Subtitle
Wrapper that adds typographic styling and margin for the subtitle/description of the list item. Responsive to `dense` and `threeLine` props.
```html
<v-list-item v-for="item in items">
<v-list-item-content>
<v-list-item-title>{{ item.title }}<v-list-item-title>
<v-list-item-subtitle>{{ item.description }}<v-list-item-subtitle>
</v-list-item-content>
</v-list-item>
```
### List Item Icon
Wrapper for icon, action, or avatar type elements in a list item. Can be used on the left or right of an item.
```html
<v-list-item v-for="item in items">
<v-list-item-icon><v-icon name="info"></v-list-item-icon>
<v-list-item-content>
<v-list-item-title>{{ item.title }}<v-list-item-title>
</v-list-item-content>
</v-list-item>
```
## Colors
You can set the default, active, and hover colors and background colors with css variables:
You can set the default, active, and hover colors and background colors with css variables. You can also set them on individual list items, which will override the list vars. Hover styles will only be set if the list item has a to link or an onClick handler.
```html
<v-list>
@@ -24,34 +64,75 @@ You can set the default, active, and hover colors and background colors with css
</v-list>
<style>
.v-list {
--v-list-color: var(--red);
--v-list-color-hover: var(--white);
--v-list-color-active: var(--white);
--v-list-background-color: var(--red-50);
--v-list-background-color-hover: var(--red-100);
--v-list-background-color-active: var(--red-800);
}
.v-list {
--v-list-color: var(--red);
--v-list-color-hover: var(--white);
--v-list-color-active: var(--white);
--v-list-background-color: var(--red-50);
--v-list-background-color-hover: var(--red-100);
--v-list-background-color-active: var(--red-800);
}
</style>
```
## Props
| Prop | Description | Default |
|---------|-------------------------------------------------------------|---------|
| `dense` | Removes some padding to make the list items closer together | `false` |
| `nav` | Adds a small margin and border-radius for nav menu styling | `false` |
### List (`v-list`)
| Prop | Description | Default |
|-------------|-------------------------------------------------------------------------------------------------------------|---------|
| `dense` | Removes some padding to make the list items closer together | `false` |
| `threeLine` | Limits list items to three lines of text (1 of title, 2 of subtitle). Only works in webkit enabled browsers | `false` |
| `nav` | Adds a small margin and border-radius for nav menu styling | `false` |
### List Item (`v-list-item`)
| Prop | Description | Default |
|-------------|------------------------------------------------------------------------------------------------------------|---------|
| `dense` | Removes some padding to make the individual list item shorter | `false` |
| `threeLine` | Limits list item to three lines of text (1 of title, 2 of subtitle). Only works in webkit enabled browsers | `false` |
| `to` | Render as vue router-link with to link | `null` |
### List Item Content (`v-list-item-content`)
### List Item Title (`v-list-item-title`)
### List Item Subtitle (`v-list-item-subtitle`)
n/a
### List Item Icon (`v-list-item-icon`)
| Prop | Description | Default |
|----------|---------------------------------------------------------------------|---------|
| `center` | Whether to center the element (good for action elements or avatars) | `false` |
## Slots
| Slot | Description |
|-----------|------------------|
| _default_ | List items, etc. |
### List (`v-list`)
### List Item (`v-list-item`)
### List Item Content (`v-list-item-content`)
### List Item Title (`v-list-item-title`)
### List Item Subtitle (`v-list-item-subtitle`)
### List Item Icon (`v-list-item-icon`)
| Slot | Description |
|-----------|---------------|
| _default_ | Content, etc. |
## Events
| Event | Description | Value |
|---------|-----------------------|--------------|
| `click` | User clicks on button | `MouseEvent` |
n/a
## CSS Variables
### List (`v-list`)
| Variable | Default |
|------------------------------------|----------------------------------|
| `--v-list-padding` | `8px 0` |
@@ -66,64 +147,7 @@ You can set the default, active, and hover colors and background colors with css
| `--v-list-background-color-hover` | `var(--background-color-hover)` |
| `--v-list-background-color-active` | `var(--background-color-active)` |
---
# List Item
A wrapper for list items that formats things nicely. Can be used on its own or inside a list component. Best used with subcomponents (see below).
```html
<v-list-item v-for="item in items">
<v-list-item-content>
{{ item.text }}
</v-list-item-content>
</v-list-item>
```
## Colors
You can set the default, active, and hover colors and background colors on individual list items with css variables. These will override the global list css vars, which you can set as well.
Hover styles will only be set if the list item has a to link or a onClick handler.
```html
<v-list-item class="item-red">
Red Stuff
</v-list-item>
<v-list-item >
Normal stuff
</v-list-item>
<style>
.item-red {
--v-list-item-color: var(--red);
--v-list-item-color-hover: var(--white);
--v-list-item-color-active: var(--white);
--v-list-item-background-color: var(--red-50);
--v-list-item-background-color-hover: var(--red-100);
--v-list-item-background-color-active: var(--red-800);
}
</style>
```
## Props
| Prop | Description | Default |
|---------|---------------------------------------------------------------|---------|
| `dense` | Removes some padding to make the individual list item shorter | `false` |
| `to` | Render as vue router-link with to link | `null` |
## Slots
| Slot | Description |
|-----------|---------------------------|
| _default_ | List content, icons, etc. |
## Events
| Event | Description | Value |
|---------|---------------------|--------------|
| `click` | User clicks on link | `MouseEvent` |
## CSS Variables
Second values are fallback ones, in case the list item is not inside a list where those vars are set.
### List Item (`v-list-item`)
| Variable | Default |
|-----------------------------------------|-----------------------------------------------------------------------|
@@ -141,32 +165,16 @@ Second values are fallback ones, in case the list item is not inside a list wher
| `--v-list-item-background-color-hover` | `var(---list-background-color-hover, var(--background-color-hover))` |
| `--v-list-item-background-color-active` | `var(--vlist-background-color-active,var(--background-color-active))` |
---
### List Item Content (`v-list-item-content`)
# List Item Content
```html
<v-list-item>
<v-list-item-content>
List test blah blah
</v-list-item-content>
</v-list-item>
```
This is simply a wrapper for the main text content of a list item. It adds some padding and helps control overflow.
## Props
n/a
## Slots
| Slot | Description |
|-----------|---------------------------|
| _default_ | List content, icons, etc. |
## Events
n/a
## CSS Variables
| Variable | Default |
|---------------------------------|----------|
| `--v-list-item-content-padding` | `12px 0` |
### List Item Title (`v-list-item-title`)
### List Item Subtitle (`v-list-item-subtitle`)
### List Item Icon (`v-list-item-icon`)
n/a

View File

@@ -9,6 +9,7 @@
--v-list-item-content-padding: 12px 0;
display: flex;
flex-basis: 0;
flex-grow: 1;
flex-shrink: 1;
flex-wrap: wrap;
@@ -17,6 +18,23 @@
padding: var(--v-list-item-content-padding);
overflow: hidden;
.v-list.three-line &,
.v-list-item.three-line & {
align-self: stretch;
}
::v-deep {
& > * {
flex-basis: 100%;
flex-grow: 1;
flex-shrink: 0;
line-height: 1.1;
&:not(:last-child) {
margin-bottom: 2px;
}
}
}
.v-list.dense &,
.v-list-item.dense & {
--v-list-item-content-padding: 8px 0;

View File

@@ -0,0 +1,66 @@
<template>
<div class="v-list-item-icon" :class="{ center }">
<slot></slot>
</div>
</template>
<script lang="ts">
import { defineComponent } from '@vue/composition-api';
export default defineComponent({
props: {
center: {
type: Boolean,
default: false
}
}
});
</script>
<style lang="scss" scoped>
@import '@/styles/mixins/type-styles';
.v-list-item-icon {
$this: &;
display: inline-flex;
align-self: center;
margin: 12px 0;
&:not(:only-child) {
&:first-child {
margin-right: 12px;
}
&:last-child {
margin-left: 12px;
}
}
.v-list,
.v-list-item {
&.three-line,
&.two-line {
align-self: flex-start;
#{$this}.center {
align-self: center;
}
}
&.dense {
#{$this} {
margin-top: 8px;
margin-bottom: 8px;
&:not(:only-child) {
&:first-child {
margin-right: 16px;
}
&:last-child {
margin-left: 8px;
}
}
}
}
}
}
</style>

View File

@@ -0,0 +1,48 @@
<template functional>
<div class="v-list-item-subtitle">
<slot></slot>
</div>
</template>
<style lang="scss" scoped>
@import '@/styles/mixins/type-styles';
.v-list-item-subtitle {
$this: &;
flex-basis: 100%;
flex-grow: 1;
flex-shrink: 1;
align-self: center;
overflow: hidden;
white-space: nowrap;
text-overflow: ellipsis;
.v-list.dense &,
.v-list-item.dense & {
@include type-item-subtitle-dense;
}
@at-root {
.v-list,
.v-list-item,
.v-list .v-list-item {
&.one-line #{$this},
&.two-line #{$this} {
display: block;
white-space: nowrap;
-webkit-line-clamp: none;
-webkit-box-orient: none;
}
&.three-line #{$this} {
display: -webkit-box;
white-space: initial;
-webkit-line-clamp: 2;
-webkit-box-orient: vertical;
}
}
}
@include type-item-subtitle;
}
</style>

View File

@@ -0,0 +1,25 @@
<template functional>
<div class="v-list-item-title">
<slot></slot>
</div>
</template>
<style lang="scss" scoped>
@import '@/styles/mixins/type-styles';
.v-list-item-title {
flex-basis: 100%;
flex-grow: 1;
flex-shrink: 1;
align-self: center;
overflow: hidden;
white-space: nowrap;
text-overflow: ellipsis;
.v-list.dense &,
.v-list-item.dense & {
@include type-item-title-dense;
}
@include type-item-title;
}
</style>

View File

@@ -4,7 +4,13 @@
active-class="active"
class="v-list-item"
:to="to"
:class="{ dense, link: isClickable }"
:class="{
dense,
link: isClickable,
'three-line': lines === 3,
'two-line': lines === 2,
'one-line': lines === 1
}"
v-on="$listeners"
>
<slot></slot>
@@ -21,6 +27,10 @@ export default defineComponent({
type: Boolean,
default: false
},
lines: {
type: Number as PropType<1 | 2 | 3>,
default: null
},
to: {
type: [String, Object] as PropType<string | Location>,
default: null
@@ -36,10 +46,18 @@ export default defineComponent({
<style lang="scss" scoped>
.v-list-item {
$this: &;
--v-list-item-one-line-min-height: 48px;
--v-list-item-two-line-min-height: 60px;
--v-list-item-three-line-min-height: 76px;
--v-list-item-one-line-min-height-dense: 40px;
--v-list-item-two-line-min-height-dense: 48px;
--v-list-item-three-line-min-height-dense: 64px;
--v-list-item-padding: 0 16px;
--v-list-item-min-width: none;
--v-list-item-max-width: none;
--v-list-item-min-height: 48px;
--v-list-item-min-height: var(--v-list-item-one-line-min-height);
--v-list-item-max-height: auto;
--v-list-item-border-radius: 0;
--v-list-item-margin-bottom: 0;
@@ -91,24 +109,44 @@ export default defineComponent({
background-color: var(--v-list-item-background-color-active);
}
}
.v-list.dense &,
&.dense {
--v-list-item-min-height: 40px;
}
.v-list.nav & {
--v-list-item-padding: 0 8px;
--v-list-item-border-radius: 4px;
&:not(:last-child):not(:only-child) {
--v-list-item-margin-bottom: 8px;
@at-root {
.v-list,
#{$this},
.v-list #{$this} {
--v-list-item-min-height: var(--v-list-item-one-line-min-height);
&.one-line {
--v-list-item-min-height: var(--v-list-item-one-line-min-height);
}
&.two-line {
--v-list-item-min-height: var(--v-list-item-two-line-min-height);
}
&.three-line {
--v-list-item-min-height: var(--v-list-item-three-line-min-height);
}
&.dense {
--v-list-item-min-height: var(--v-list-item-one-line-min-height-dense);
&.one-line {
--v-list-item-min-height: var(--v-list-item-one-line-min-height-dense);
}
&.two-line {
--v-list-item-min-height: var(--v-list-item-two-line-min-height-dense);
}
&.three-line {
--v-list-item-min-height: var(--v-list-item-three-line-min-height-dense);
}
}
}
.v-list.dense &,
&.dense {
&:not(:last-child):not(:only-child) {
--v-list-item-margin-bottom: 4px;
.v-list.nav #{$this} {
--v-list-item-padding: 0 8px;
--v-list-item-border-radius: 4px;
#{$this}:not(:last-child):not(:only-child) {
--v-list-item-margin-bottom: 8px;
}
&.dense #{$this},
#{$this}.dense {
&:not(:last-child):not(:only-child) {
--v-list-item-margin-bottom: 4px;
}
}
}
}

View File

@@ -1,17 +1,28 @@
import { withKnobs, boolean } from '@storybook/addon-knobs';
import { withKnobs, boolean, text, select } from '@storybook/addon-knobs';
import { action } from '@storybook/addon-actions';
import Vue from 'vue';
import VList from './v-list.vue';
import VListItem from './v-list-item.vue';
import withPadding from '../../../.storybook/decorators/with-padding';
import VListItemContent from './v-list-item-content.vue';
import VListItemTitle from './v-list-item-title.vue';
import VListItemSubTitle from './v-list-item-subtitle.vue';
import VListItemIcon from './v-list-item-icon.vue';
import VSheet from '../v-sheet';
import VCheckbox from '../v-checkbox';
import VueRouter from 'vue-router';
import markdown from './readme.md';
import { defineComponent, reactive } from '@vue/composition-api';
import withBackground from '../../../.storybook/decorators/with-background';
Vue.component('v-list', VList);
Vue.component('v-list-item', VListItem);
Vue.component('v-list-item-content', VListItemContent);
Vue.component('v-list-item-title', VListItemTitle);
Vue.component('v-list-item-subtitle', VListItemSubTitle);
Vue.component('v-list-item-icon', VListItemIcon);
Vue.component('v-checkbox', VCheckbox);
Vue.component('v-sheet', VSheet);
Vue.use(VueRouter);
@@ -20,39 +31,40 @@ const router = new VueRouter();
export default {
title: 'Components / List',
component: VList,
decorators: [withKnobs, withPadding],
decorators: [withKnobs, withPadding, withBackground],
parameters: {
notes: markdown
}
};
export const basic = () => ({
props: {
dense: {
default: boolean('Dense', false, 'Full List')
export const basic = () =>
defineComponent({
props: {
dense: {
default: boolean('Dense', false, 'Full List')
},
dense0: {
default: boolean('Dense', false, 'List Item 0')
},
dense1: {
default: boolean('Dense', false, 'List Item 1')
},
dense2: {
default: boolean('Dense', false, 'List Item 2')
},
dense3: {
default: boolean('Dense', false, 'List Item 3')
},
nav: {
default: boolean('Nav', false, 'Full List')
}
},
dense0: {
default: boolean('Dense', false, 'List Item 0')
setup() {
return {
items: ['Item 0', 'Item 1', 'Item 2', 'Item 3']
};
},
dense1: {
default: boolean('Dense', false, 'List Item 1')
},
dense2: {
default: boolean('Dense', false, 'List Item 2')
},
dense3: {
default: boolean('Dense', false, 'List Item 3')
},
nav: {
default: boolean('Nav', false, 'Full List')
}
},
data() {
return {
items: ['Item 0', 'Item 1', 'Item 2', 'Item 3']
};
},
template: `
template: `
<v-sheet style="--v-sheet-max-width: 200px; ">
<v-list :dense="dense" :nav="nav">
<v-list-item v-for="(item, i) in items" :dense="$props['dense' + i]" :key="i">
@@ -62,36 +74,212 @@ export const basic = () => ({
</v-list-item>
</v-list>
</v-sheet>`
});
});
export const withLinks = () => ({
router: router,
props: {
dense: {
default: boolean('Dense', false, 'Full List')
export const withSubtitle = () =>
defineComponent({
router: router,
props: {
dense: {
default: boolean('Dense', false, 'Full List')
},
dense0: {
default: boolean('Dense', false, 'List Item 0')
},
dense1: {
default: boolean('Dense', false, 'List Item 1')
},
dense2: {
default: boolean('Dense', false, 'List Item 2')
},
dense3: {
default: boolean('Dense', false, 'List Item 3')
},
lines0: {
default: select(
'Lines',
{ One: 1, Two: 2, Three: 3, Off: null },
null,
'List Item 0'
)
},
lines1: {
default: select(
'Lines',
{ One: 1, Two: 2, Three: 3, Off: null },
null,
'List Item 1'
)
},
lines2: {
default: select(
'Lines',
{ One: 1, Two: 2, Three: 3, Off: null },
null,
'List Item 2'
)
},
lines3: {
default: select(
'Lines',
{ One: 1, Two: 2, Three: 3, Off: null },
null,
'List Item 3'
)
},
nav: {
default: boolean('Nav', false, 'Full List')
},
dynamicSubtitle: {
default: text('Subtitle 3', '', 'Full List')
},
lines: {
default: select('Lines', { One: 1, Two: 2, Three: 3, Off: null }, null, 'Full List')
}
},
dense0: {
default: boolean('Dense', false, 'List Item 0')
setup() {
return {
items: {
0: { title: 'List Item 0', subtitle: 'This is a list item subtitle.' },
1: {
title: 'List Item 1',
subtitle:
"This is another example of a list item subtitle. But this time, it's pretty long so you can see who two-line and three-line wrapping work"
},
2: {
title: 'List Item 2',
subtitle:
"This is yet another example of a list subtitle. It's of medium length."
}
}
};
},
dense1: {
default: boolean('Dense', false, 'List Item 1')
template: `
<v-sheet style="--v-sheet-max-width: 400px; ">
<v-list :dense="dense" :nav="nav" :lines="lines">
<v-list-item v-for="(item, i) in items" :lines="$props['lines' + i]" :dense="$props['dense' + i]" :key="i" :to="'/' + i">
<v-list-item-content>
<v-list-item-title>{{ item.title }}</v-list-item-title>
<v-list-item-subtitle>{{ item.subtitle }}</v-list-item-subtitle>
</v-list-item-content>
</v-list-item>
<v-list-item :dense="dense3" :lines="lines3" :key=3 :to="'/' + 3">
<v-list-item-content>
<v-list-item-title>List Item 3</v-list-item-title>
<v-list-item-subtitle>{{ dynamicSubtitle }}</v-list-item-subtitle>
</v-list-item-content>
</v-list-item>
</v-list>
</v-sheet>`
});
export const withIconsToo = () =>
defineComponent({
router: router,
props: {
dense: {
default: boolean('Dense', false, 'Full List')
},
nav: {
default: boolean('Nav', false, 'Full List')
},
subtitle: {
default: boolean('Subtitle', false, 'Full List')
},
lines: {
default: select('Lines', { One: 1, Two: 2, Three: 3, Off: null }, null, 'Full List')
},
leftIconCenter: {
default: boolean('Left Icon Centered', false, 'Full List')
},
rightIconCenter: {
default: boolean('Right Icon Centered', true, 'Full List')
},
leftIcon: {
default: boolean('Left Icon', true, 'Full List')
},
rightIcon: {
default: boolean('Right Icon', false, 'Full List')
}
},
dense2: {
default: boolean('Dense', false, 'List Item 2')
setup() {
const onChange = action('change');
return {
onChange,
items: reactive({
0: {
title: 'List Item 0',
subtitle: 'This is a list item subtitle.',
checked: false
},
1: {
title: 'List Item 1',
subtitle:
"This is another example of a list item subtitle. But this time, it's pretty long so you can see who two-line and three-line wrapping work",
checked: false
},
2: {
title: 'List Item 2',
subtitle:
"This is yet another example of a list subtitle. It's of medium length.",
checked: false
},
3: {
title: 'List Item 3',
subtitle: '',
checked: false
}
})
};
},
dense3: {
default: boolean('Dense', false, 'List Item 3')
template: `
<v-sheet style="--v-sheet-max-width: 400px; ">
<v-list :dense="dense" :nav="nav" :lines="lines">
<v-list-item v-for="(item, i) in items" :key="i" :to="'/' + i">
<v-list-item-icon v-if="leftIcon" :center="leftIconCenter">
<v-icon name="info" />
</v-list-item-icon>
<v-list-item-content>
<v-list-item-title>{{ item.title }}</v-list-item-title>
<v-list-item-subtitle v-if="subtitle">{{ item.subtitle }}</v-list-item-subtitle>
</v-list-item-content>
<v-list-item-icon v-if="rightIcon" :center="rightIconCenter">
<v-checkbox v-model="item.checked" @change="onChange"/>
</v-list-item-icon>
</v-list-item>
</v-list>
</v-sheet>`
});
export const withLinks = () =>
defineComponent({
router: router,
props: {
dense: {
default: boolean('Dense', false, 'Full List')
},
dense0: {
default: boolean('Dense', false, 'List Item 0')
},
dense1: {
default: boolean('Dense', false, 'List Item 1')
},
dense2: {
default: boolean('Dense', false, 'List Item 2')
},
dense3: {
default: boolean('Dense', false, 'List Item 3')
},
nav: {
default: boolean('Nav', false, 'Full List')
}
},
nav: {
default: boolean('Nav', false, 'Full List')
}
},
data() {
return {
items: ['Item 0', 'Item 1', 'Item 2', 'Item 3']
};
},
template: `
setup() {
return {
items: ['Item 0', 'Item 1', 'Item 2', 'Item 3']
};
},
template: `
<v-sheet style="--v-sheet-max-width: 200px; ">
<v-list :dense="dense" :nav="nav">
<v-list-item v-for="(item, i) in items" :dense="$props['dense' + i]" :key="i" :to="'/' + i">
@@ -101,17 +289,18 @@ export const withLinks = () => ({
</v-list-item>
</v-list>
</v-sheet>`
});
});
export const withClicks = () => ({
props: {},
data() {
return {
items: ['Item 0', 'Item 1', 'Item 2', 'Item 3'],
clickHandler: action('onClick')
};
},
template: `
export const withClicks = () =>
defineComponent({
props: {},
setup() {
return {
items: ['Item 0', 'Item 1', 'Item 2', 'Item 3'],
clickHandler: action('onClick')
};
},
template: `
<v-sheet style="--v-sheet-max-width: 200px; ">
<v-list>
<v-list-item v-for="(item, i) in items" :key="i" @click="clickHandler">
@@ -121,30 +310,31 @@ export const withClicks = () => ({
</v-list-item>
</v-list>
</v-sheet>`
});
});
export const orphanListItems = () => ({
router: router,
props: {
dense0: {
default: boolean('Dense', false, 'List Item 0')
export const orphanListItems = () =>
defineComponent({
router: router,
props: {
dense0: {
default: boolean('Dense', false, 'List Item 0')
},
dense1: {
default: boolean('Dense', false, 'List Item 1')
},
dense2: {
default: boolean('Dense', false, 'List Item 2')
},
dense3: {
default: boolean('Dense', false, 'List Item 3')
}
},
dense1: {
default: boolean('Dense', false, 'List Item 1')
setup() {
return {
items: ['Item 0', 'Item 1', 'Item 2', 'Item 3']
};
},
dense2: {
default: boolean('Dense', false, 'List Item 2')
},
dense3: {
default: boolean('Dense', false, 'List Item 3')
}
},
data() {
return {
items: ['Item 0', 'Item 1', 'Item 2', 'Item 3']
};
},
template: `
template: `
<v-sheet style="--v-sheet-max-width: 200px; ">
<v-list-item v-for="(item, i) in items" :dense="$props['dense' + i]" :key="i" :to="'/' + i">
<v-list-item-content>
@@ -152,4 +342,4 @@ export const orphanListItems = () => ({
</v-list-item-content>
</v-list-item>
</v-sheet>`
});
});

View File

@@ -4,12 +4,14 @@ import VueRouter from 'vue-router';
import router from '@/router';
import VList from './v-list.vue';
import VListItem from './v-list-item.vue';
import VListItemIcon from './v-list-item-icon.vue';
const localVue = createLocalVue();
localVue.use(VueCompositionAPI);
localVue.use(VueRouter);
localVue.component('v-list-item', VListItem);
localVue.component('v-list', VList);
localVue.component('v-list-item-icon', VListItemIcon);
describe('List', () => {
it('Renders the provided markup in the default slot', () => {
@@ -34,6 +36,55 @@ describe('List', () => {
expect(component.classes()).toContain('dense');
});
it('Adds the three-line class for lines = 3', () => {
const component = mount(VList, {
localVue,
propsData: {
lines: 3
},
slots: {
default: `<v-list-item/>
<v-list-item/>
<v-list-item/>`
}
});
expect(component.classes()).toContain('three-line');
});
it('Adds the two-line class for lines = 2', () => {
const component = mount(VList, {
localVue,
propsData: {
lines: 2
},
slots: {
default: `<v-list-item/>
<v-list-item/>
<v-list-item/>`
}
});
expect(component.classes()).toContain('two-line');
});
it('Adds the two-line class to only one element, rest is 3', () => {
const component = mount(VList, {
localVue,
propsData: {
lines: 3
},
slots: {
default: `<v-list-item :lines="2"/>
<v-list-item/>
<v-list-item/>`
}
});
expect(component.find('.v-list-item:first-of-type').classes()).toContain('two-line');
expect(component.classes()).toContain('three-line');
});
it('Adds the nav class for nav lists', () => {
const component = mount(VList, {
localVue,
@@ -45,6 +96,20 @@ describe('List', () => {
expect(component.classes()).toContain('nav');
});
it('Adds the centered class for cenetered icons in items', () => {
const component = mount(VListItem, {
localVue,
slots: {
default: `<v-list-item-icon center/>`
},
propsData: {
nav: true
}
});
expect(component.find('.v-list-item-icon').classes()).toContain('center');
});
it('Has the right number of list items', () => {
const component = mount(VList, {
localVue,
@@ -53,8 +118,8 @@ describe('List', () => {
},
slots: {
default: `<v-list-item/>
<v-list-item/>
<v-list-item/>`
<v-list-item/>
<v-list-item/>`
}
});
@@ -69,7 +134,7 @@ describe('List', () => {
},
slots: {
default: `<v-list-item dense/>
<v-list-item/>`
<v-list-item/>`
}
});

View File

@@ -1,11 +1,20 @@
<template>
<ul class="v-list" :class="{ dense, nav }">
<ul
class="v-list"
:class="{
dense,
nav,
'three-line': lines === 3,
'two-line': lines === 2,
'one-line': lines === 1
}"
>
<slot></slot>
</ul>
</template>
<script lang="ts">
import { defineComponent } from '@vue/composition-api';
import { defineComponent, PropType } from '@vue/composition-api';
export default defineComponent({
props: {
@@ -16,6 +25,10 @@ export default defineComponent({
nav: {
type: Boolean,
default: false
},
lines: {
type: Number as PropType<1 | 2 | 3>,
default: null
}
},
setup() {

View File

@@ -1,8 +1,9 @@
<template>
<v-list nav dense>
<v-list nav>
<v-list-item v-for="navItem in navItems" :key="navItem.to" :to="navItem.to">
<v-list-item-icon><v-icon :name="navItem.icon" /></v-list-item-icon>
<v-list-item-content>
{{ navItem.name }}
<v-list-item-title>{{ navItem.name }}</v-list-item-title>
</v-list-item-content>
</v-list-item>
</v-list>

View File

@@ -210,6 +210,7 @@ body {
--grey-900: #212121;
--blue-grey: #607d8b;
--blue-grey-25: #F5F7F8;
--blue-grey-50: #eceff1;
--blue-grey-100: #cfd8dc;
--blue-grey-200: #b0bec5;

View File

@@ -22,6 +22,34 @@
font-size: 18px;
}
@mixin type-item-title {
color: var(--heading-text-color);
font-weight: 400;
font-size: 15px;
line-height: 1.2;
}
@mixin type-item-title-dense {
color: var(--heading-text-color);
font-weight: 500;
font-size: 13px;
line-height: 1.1;
}
@mixin type-item-subtitle {
color: var(--subheading-text-color);
font-weight: 400;
font-size: 14px;
line-height: 1.2;
}
@mixin type-item-subtitle-dense {
color: var(--subheading-text-color);
font-weight: 500;
font-size: 13px;
line-height: 1.1;
}
@mixin type-label {
color: var(--heading-text-color);
font-size: 18px;

View File

@@ -29,12 +29,15 @@ body,
--foreground-color-tertiary: var(--blue-grey-100);
--background-color: var(--white);
--background-color-hover: var(--blue-grey-50);
--background-color-active: var(--blue-grey-100);
--background-color-hover: var(--blue-grey-25);
--background-color-active: var(--blue-grey-50);
--background-color-alt: var(--blue-grey-50);
--highlight: var(--off-white);
--heading-text-color: var(--foreground-color);
--subheading-text-color: var(--foreground-color-secondary);
/* Inputs */
--input-foreground-color: var(--blue-grey-800);
--input-foreground-color-hover: var(--blue-grey-800);