mirror of
https://github.com/IT4Change/Ocelot-Social.git
synced 2025-12-13 07:45:56 +00:00
upgrade styleguide the second
This commit is contained in:
parent
0440d06e54
commit
2ef13980a6
@ -19,7 +19,7 @@
|
||||
height: 100%;
|
||||
top: 0px;
|
||||
left: 0px;
|
||||
box-shadow: inset 0 0 0 1px rgba(0, 0, 0, 0.1);
|
||||
box-shadow: inset 0 0 0 1px rgba($color-neutral-0, .1);
|
||||
border-radius: 50%;
|
||||
}
|
||||
|
||||
|
||||
@ -5,7 +5,11 @@
|
||||
<table
|
||||
cellpadding="0"
|
||||
cellspacing="0"
|
||||
class="ds-table">
|
||||
class="ds-table"
|
||||
:class="[
|
||||
condensed && 'ds-table-condensed',
|
||||
bordered && 'ds-table-bordered'
|
||||
]">
|
||||
<colgroup>
|
||||
<col
|
||||
v-for="header in headers"
|
||||
@ -24,7 +28,7 @@
|
||||
<tbody>
|
||||
<tr
|
||||
v-for="(row, index) in rows"
|
||||
:key="index">
|
||||
:key="row.key || index">
|
||||
<ds-table-col
|
||||
v-for="col in row"
|
||||
:key="col.key">
|
||||
@ -75,6 +79,20 @@ export default {
|
||||
default() {
|
||||
return null
|
||||
}
|
||||
},
|
||||
/**
|
||||
* Should the table be more condense?
|
||||
*/
|
||||
condensed: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
/**
|
||||
* Should the table have borders?
|
||||
*/
|
||||
bordered: {
|
||||
type: Boolean,
|
||||
default: true
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
|
||||
@ -11,19 +11,34 @@
|
||||
|
||||
.ds-table-col {
|
||||
@include reset;
|
||||
border-bottom: $border-color-softer solid $border-size-base;
|
||||
vertical-align: top;
|
||||
padding: $space-small $space-xx-small;
|
||||
|
||||
&:last-child {
|
||||
padding-right: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.ds-table-head-col {
|
||||
@include reset;
|
||||
border-bottom: $border-color-softer solid $border-size-base;
|
||||
padding: $space-small $space-xx-small;
|
||||
text-align: left;
|
||||
font-weight: $font-weight-bold;
|
||||
}
|
||||
}
|
||||
|
||||
// bordered
|
||||
.ds-table-bordered {
|
||||
.ds-table-col,
|
||||
.ds-table-head-col {
|
||||
border-bottom: $border-color-softer dotted $border-size-base;
|
||||
}
|
||||
|
||||
tr:last-child .ds-table-col {
|
||||
border-bottom: none;
|
||||
}
|
||||
}
|
||||
|
||||
// condensed
|
||||
.ds-table-condensed {
|
||||
.ds-table-col,
|
||||
.ds-table-head-col {
|
||||
padding-top: $space-x-small;
|
||||
padding-bottom: $space-x-small;
|
||||
}
|
||||
}
|
||||
|
||||
9
styleguide/src/system/components/data-input/FormItem/FormItem.vue
Normal file → Executable file
9
styleguide/src/system/components/data-input/FormItem/FormItem.vue
Normal file → Executable file
@ -1,10 +1,9 @@
|
||||
<template>
|
||||
<div
|
||||
<div
|
||||
class="ds-form-item"
|
||||
:class="$parentInput ? $parentInput.stateClasses : ''">
|
||||
<ds-input-label
|
||||
v-if="$parentInput"
|
||||
:label="$parentInput.label"
|
||||
:class="$parentInput.stateClasses">
|
||||
<ds-input-label
|
||||
:label="$parentInput.label"
|
||||
:for="$parentInput.id" />
|
||||
<slot/>
|
||||
<ds-input-error :error="$parentInput ? $parentInput.error : null" />
|
||||
|
||||
0
styleguide/src/system/components/data-input/FormItem/InputError.vue
Normal file → Executable file
0
styleguide/src/system/components/data-input/FormItem/InputError.vue
Normal file → Executable file
0
styleguide/src/system/components/data-input/FormItem/InputLabel.vue
Normal file → Executable file
0
styleguide/src/system/components/data-input/FormItem/InputLabel.vue
Normal file → Executable file
0
styleguide/src/system/components/data-input/FormItem/style.scss
Normal file → Executable file
0
styleguide/src/system/components/data-input/FormItem/style.scss
Normal file → Executable file
3
styleguide/src/system/components/data-input/Input/Input.vue
Normal file → Executable file
3
styleguide/src/system/components/data-input/Input/Input.vue
Normal file → Executable file
@ -17,11 +17,12 @@
|
||||
:type="type"
|
||||
:autofocus="autofocus"
|
||||
:placeholder="placeholder"
|
||||
:tabindex="tabindex"
|
||||
:disabled="disabled"
|
||||
:readonly="readonly"
|
||||
:is="tag"
|
||||
:value.prop="innerValue"
|
||||
@input="input"
|
||||
@input="handleInput"
|
||||
@focus="handleFocus"
|
||||
@blur="handleBlur"
|
||||
:rows="type === 'textarea' ? rows : null"
|
||||
|
||||
0
styleguide/src/system/components/data-input/Input/demo.md
Normal file → Executable file
0
styleguide/src/system/components/data-input/Input/demo.md
Normal file → Executable file
0
styleguide/src/system/components/data-input/Input/style.scss
Normal file → Executable file
0
styleguide/src/system/components/data-input/Input/style.scss
Normal file → Executable file
248
styleguide/src/system/components/data-input/Select/Select.vue
Normal file → Executable file
248
styleguide/src/system/components/data-input/Select/Select.vue
Normal file → Executable file
@ -1,33 +1,119 @@
|
||||
<template>
|
||||
<ds-form-item>
|
||||
<div class="ds-select-wrap">
|
||||
<div
|
||||
class="ds-select-wrap"
|
||||
v-click-outside="handleBlur"
|
||||
:tabindex="searchable ? -1 : tabindex"
|
||||
@keydown.self.down.prevent="pointerNext"
|
||||
@keydown.self.up.prevent="pointerPrev"
|
||||
@keypress.enter.prevent.stop.self="selectPointerOption"
|
||||
@keyup.esc="close">
|
||||
<div
|
||||
v-if="icon"
|
||||
class="ds-select-icon">
|
||||
<ds-icon :name="icon"/>
|
||||
</div>
|
||||
<select
|
||||
<div
|
||||
class="ds-select"
|
||||
@click="handleClick"
|
||||
:class="[
|
||||
icon && `ds-select-has-icon`,
|
||||
iconRight && `ds-select-has-icon-right`
|
||||
]"
|
||||
:id="id"
|
||||
:name="model"
|
||||
:autofocus="autofocus"
|
||||
:placeholder="placeholder"
|
||||
:disabled="disabled"
|
||||
:readonly="readonly"
|
||||
:value.prop="innerValue"
|
||||
@input="input"
|
||||
@focus="handleFocus"
|
||||
@blur="handleBlur">
|
||||
<option
|
||||
v-for="option in options"
|
||||
:key="option.label || option">
|
||||
{{ option.label || option }}
|
||||
</option>
|
||||
</select>
|
||||
iconRight && `ds-select-has-icon-right`,
|
||||
multiple && `ds-select-multiple`
|
||||
]">
|
||||
<div
|
||||
v-if="multiple"
|
||||
class="ds-selected-options">
|
||||
<div
|
||||
class="ds-selected-option"
|
||||
v-for="(value, index) in innerValue"
|
||||
:key="value">
|
||||
<!-- @slot Slot to provide a custom selected option display -->
|
||||
<slot
|
||||
name="optionitem"
|
||||
:value="value">
|
||||
<ds-chip
|
||||
removable
|
||||
@remove="deselectOption(index)"
|
||||
color="primary">
|
||||
{{ value }}
|
||||
</ds-chip>
|
||||
</slot>
|
||||
</div>
|
||||
<input
|
||||
ref="search"
|
||||
class="ds-select-search"
|
||||
:id="id"
|
||||
:name="model"
|
||||
:autofocus="autofocus"
|
||||
:placeholder="placeholder"
|
||||
:tabindex="tabindex"
|
||||
:disabled="disabled"
|
||||
:readonly="readonly"
|
||||
v-model="searchString"
|
||||
@focus="handleFocus"
|
||||
@keydown.delete.stop="deselectLastOption"
|
||||
@keydown.down.prevent="pointerNext"
|
||||
@keydown.up.prevent="pointerPrev"
|
||||
@keypress.enter.prevent.stop="selectPointerOption"
|
||||
@keyup.esc="close">
|
||||
</div>
|
||||
<div
|
||||
v-else
|
||||
class="ds-select-value">
|
||||
<div
|
||||
v-if="placeholder && !innerValue"
|
||||
class="ds-select-placeholder">
|
||||
{{ placeholder }}
|
||||
</div>
|
||||
<!-- @slot Slot to provide a custom value display -->
|
||||
<slot
|
||||
v-else
|
||||
name="value"
|
||||
:value="innerValue">
|
||||
{{ innerValue }}
|
||||
</slot>
|
||||
</div>
|
||||
<input
|
||||
v-if="!multiple"
|
||||
ref="search"
|
||||
class="ds-select-search"
|
||||
:id="id"
|
||||
:name="model"
|
||||
:autofocus="autofocus"
|
||||
:placeholder="placeholder"
|
||||
:tabindex="tabindex"
|
||||
:disabled="disabled"
|
||||
:readonly="readonly"
|
||||
v-model="searchString"
|
||||
@focus="handleFocus"
|
||||
@keydown.delete.stop="deselectLastOption"
|
||||
@keydown.down.prevent="pointerNext"
|
||||
@keydown.up.prevent="pointerPrev"
|
||||
@keypress.enter.prevent.stop="selectPointerOption"
|
||||
@keyup.esc="close">
|
||||
</div>
|
||||
<div class="ds-select-dropdown">
|
||||
<ul class="ds-select-options">
|
||||
<li
|
||||
class="ds-select-option"
|
||||
:class="[
|
||||
isSelected(option) && `ds-select-option-is-selected`,
|
||||
pointer === index && `ds-select-option-hover`
|
||||
]"
|
||||
v-for="(option, index) in filteredOptions"
|
||||
@click="handleSelect(option)"
|
||||
@mouseover="setPointer(index)"
|
||||
:key="option.label || option">
|
||||
<!-- @slot Slot to provide custom option items -->
|
||||
<slot
|
||||
name="option"
|
||||
:option="option">
|
||||
{{ option.label || option }}
|
||||
</slot>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div
|
||||
v-if="iconRight"
|
||||
class="ds-select-icon-right">
|
||||
@ -39,6 +125,11 @@
|
||||
|
||||
<script>
|
||||
import inputMixin from '../shared/input'
|
||||
import multiinputMixin from '../shared/multiinput'
|
||||
import ClickOutside from 'vue-click-outside'
|
||||
import DsFormItem from '@@/components/data-input/FormItem/FormItem'
|
||||
import DsChip from '@@/components/typography/Chip/Chip'
|
||||
import DsIcon from '@@/components/typography/Icon/Icon'
|
||||
|
||||
/**
|
||||
* Used for handling basic user input.
|
||||
@ -46,7 +137,21 @@ import inputMixin from '../shared/input'
|
||||
*/
|
||||
export default {
|
||||
name: 'DsSelect',
|
||||
mixins: [inputMixin],
|
||||
mixins: [inputMixin, multiinputMixin],
|
||||
components: {
|
||||
DsFormItem,
|
||||
DsChip,
|
||||
DsIcon
|
||||
},
|
||||
directives: {
|
||||
ClickOutside
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
searchString: '',
|
||||
pointer: 0
|
||||
}
|
||||
},
|
||||
props: {
|
||||
/**
|
||||
* The placeholder shown when value is empty.
|
||||
@ -69,13 +174,6 @@ export default {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
/**
|
||||
* Whether the user can select multiple items
|
||||
*/
|
||||
multiple: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
/**
|
||||
* The name of the input's icon.
|
||||
*/
|
||||
@ -88,7 +186,7 @@ export default {
|
||||
*/
|
||||
iconRight: {
|
||||
type: String,
|
||||
default: null
|
||||
default: 'angle-down'
|
||||
},
|
||||
/**
|
||||
* The select options.
|
||||
@ -98,6 +196,98 @@ export default {
|
||||
default() {
|
||||
return []
|
||||
}
|
||||
},
|
||||
/**
|
||||
* Whether the options are searchable
|
||||
*/
|
||||
searchable: {
|
||||
type: Boolean,
|
||||
default: true
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
filteredOptions() {
|
||||
if (!this.searchString) {
|
||||
return this.options
|
||||
}
|
||||
const searchParts = this.searchString.split(' ')
|
||||
|
||||
return this.options.filter(option => {
|
||||
const value = option.value || option
|
||||
return searchParts.every(part => {
|
||||
if (!part) {
|
||||
return true
|
||||
}
|
||||
return value.toLowerCase().includes(part.toLowerCase())
|
||||
})
|
||||
})
|
||||
},
|
||||
pointerMax() {
|
||||
return this.filteredOptions.length - 1
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
pointerMax(max) {
|
||||
if (max < this.pointer) {
|
||||
this.$nextTick(() => {
|
||||
this.pointer = max
|
||||
})
|
||||
}
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
handleSelect(options) {
|
||||
this.selectOption(options)
|
||||
this.resetSearch()
|
||||
if (this.multiple) {
|
||||
this.$refs.search.focus()
|
||||
this.handleFocus()
|
||||
} else {
|
||||
this.close()
|
||||
}
|
||||
},
|
||||
resetSearch() {
|
||||
this.searchString = ''
|
||||
},
|
||||
handleClick() {
|
||||
if (!this.focus || this.multiple) {
|
||||
this.$refs.search.focus()
|
||||
this.handleFocus()
|
||||
}
|
||||
},
|
||||
close() {
|
||||
this.$refs.search.blur()
|
||||
this.handleBlur()
|
||||
},
|
||||
deselectLastOption() {
|
||||
if (
|
||||
this.multiple &&
|
||||
this.innerValue &&
|
||||
this.innerValue.length &&
|
||||
!this.searchString.length
|
||||
) {
|
||||
this.deselectOption(this.innerValue.length - 1)
|
||||
}
|
||||
},
|
||||
setPointer(index) {
|
||||
this.pointer = index
|
||||
},
|
||||
pointerPrev() {
|
||||
if (this.pointer === 0) {
|
||||
this.pointer = this.pointerMax
|
||||
} else {
|
||||
this.pointer--
|
||||
}
|
||||
},
|
||||
pointerNext() {
|
||||
if (this.pointer === this.pointerMax) {
|
||||
this.pointer = 0
|
||||
} else {
|
||||
this.pointer++
|
||||
}
|
||||
},
|
||||
selectPointerOption() {
|
||||
this.handleSelect(this.filteredOptions[this.pointer])
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -0,0 +1,69 @@
|
||||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`Select.vue matches snapshot 1`] = `
|
||||
<dsformitem-stub>
|
||||
<div
|
||||
class="ds-select-wrap"
|
||||
tabindex="-1"
|
||||
>
|
||||
<!---->
|
||||
|
||||
<div
|
||||
class="ds-select ds-select-has-icon-right"
|
||||
>
|
||||
<div
|
||||
class="ds-select-value"
|
||||
>
|
||||
|
||||
1
|
||||
|
||||
</div>
|
||||
|
||||
<input
|
||||
class="ds-select-search"
|
||||
tabindex="0"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div
|
||||
class="ds-select-dropdown"
|
||||
>
|
||||
<ul
|
||||
class="ds-select-options"
|
||||
>
|
||||
<li
|
||||
class="ds-select-option ds-select-option-is-selected ds-select-option-hover"
|
||||
>
|
||||
|
||||
1
|
||||
|
||||
</li>
|
||||
<li
|
||||
class="ds-select-option"
|
||||
>
|
||||
|
||||
2
|
||||
|
||||
</li>
|
||||
<li
|
||||
class="ds-select-option"
|
||||
>
|
||||
|
||||
3
|
||||
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div
|
||||
class="ds-select-icon-right"
|
||||
>
|
||||
<dsicon-stub
|
||||
arialabel="icon"
|
||||
name="angle-down"
|
||||
tag="span"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</dsformitem-stub>
|
||||
`;
|
||||
154
styleguide/src/system/components/data-input/Select/demo.md
Normal file → Executable file
154
styleguide/src/system/components/data-input/Select/demo.md
Normal file → Executable file
@ -2,4 +2,158 @@
|
||||
|
||||
```
|
||||
<ds-select :options="['blue', 'red', 'green']" />
|
||||
```
|
||||
|
||||
## Usage with label
|
||||
|
||||
```
|
||||
<ds-select
|
||||
label="Color"
|
||||
:options="['blue', 'red', 'green']" />
|
||||
```
|
||||
|
||||
## Bind to a value
|
||||
|
||||
Use v-model to bind a value to the select input.
|
||||
|
||||
```
|
||||
<template>
|
||||
<div>
|
||||
<ds-select
|
||||
v-model="color"
|
||||
:options="['blue', 'red', 'green']"
|
||||
placeholder="Color ..."></ds-select>
|
||||
<ds-text>Your color: {{ color }}</ds-text>
|
||||
</div>
|
||||
</template>
|
||||
<script>
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
color: 'blue'
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
```
|
||||
|
||||
## Multiselect
|
||||
|
||||
Use the multiple prop to allow the user selecting multiple values.
|
||||
|
||||
```
|
||||
<template>
|
||||
<div>
|
||||
<ds-select
|
||||
v-model="color"
|
||||
:options="['blue', 'red', 'green']"
|
||||
placeholder="Color ..."
|
||||
multiple></ds-select>
|
||||
<ds-text>Your colors: {{ color }}</ds-text>
|
||||
</div>
|
||||
</template>
|
||||
<script>
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
color: ['blue', 'red']
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
```
|
||||
|
||||
## Options as objects
|
||||
|
||||
Options can be objects with a label and a value property.
|
||||
|
||||
```
|
||||
<template>
|
||||
<div>
|
||||
<ds-select
|
||||
v-model="color"
|
||||
:options="colorOptions"
|
||||
placeholder="Color ..."></ds-select>
|
||||
<ds-text>Your color: {{ color }}</ds-text>
|
||||
</div>
|
||||
</template>
|
||||
<script>
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
color: '',
|
||||
colorOptions: [
|
||||
{
|
||||
label: 'blue',
|
||||
value: '#0e17d8'
|
||||
},
|
||||
{
|
||||
label: 'red',
|
||||
value: '#d80e3f'
|
||||
},
|
||||
{
|
||||
label: 'green',
|
||||
value: '#0ed853'
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
```
|
||||
|
||||
## Validation
|
||||
|
||||
We use <a href="https://github.com/yiminghe/async-validator" targe="_blank">async-validator schemas</a> for validation.
|
||||
|
||||
If you need to validate more than one field it is better to use the form component.
|
||||
|
||||
```
|
||||
<template>
|
||||
<div>
|
||||
<ds-select
|
||||
v-model="color"
|
||||
:options="['blue', 'red', 'green']"
|
||||
:schema="{type: 'enum', enum: ['green'], message: 'Please choose green :)' }"
|
||||
placeholder="Color ..." />
|
||||
</div>
|
||||
</template>
|
||||
<script>
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
color: ''
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
```
|
||||
|
||||
## Select sizes
|
||||
|
||||
```
|
||||
<ds-select placeholder="Small ..." size="small"></ds-select>
|
||||
<ds-select placeholder="Base ..."></ds-select>
|
||||
<ds-select placeholder="Large ..." size="large"></ds-select>
|
||||
```
|
||||
|
||||
## Select icons
|
||||
|
||||
Add an icon to help the user identify the select fields usage.
|
||||
|
||||
```
|
||||
<ds-select
|
||||
placeholder="User ..."
|
||||
icon="user"></ds-select>
|
||||
<ds-select
|
||||
placeholder="Day ..."
|
||||
icon="clock"></ds-select>
|
||||
<ds-select
|
||||
placeholder="User ..."
|
||||
size="small"
|
||||
icon="user"></ds-select>
|
||||
<ds-select
|
||||
placeholder="User ..."
|
||||
size="large"
|
||||
icon="user"></ds-select>
|
||||
```
|
||||
309
styleguide/src/system/components/data-input/Select/spec.js
Executable file
309
styleguide/src/system/components/data-input/Select/spec.js
Executable file
@ -0,0 +1,309 @@
|
||||
import { shallowMount } from '@vue/test-utils'
|
||||
import Comp from './Select.vue'
|
||||
|
||||
describe('Select.vue', () => {
|
||||
describe('Events emitting', () => {
|
||||
describe('@input', () => {
|
||||
test('should be called when the value is changed passing the new value', () => {
|
||||
const wrapper = shallowMount(Comp, {
|
||||
propsData: {
|
||||
value: '3',
|
||||
options: ['1', '2', '3']
|
||||
}
|
||||
})
|
||||
wrapper.vm.selectOption(wrapper.vm.options[0])
|
||||
expect(wrapper.emitted().input[0]).toEqual(['1'])
|
||||
})
|
||||
test('should be called when an option is clicked passing the options value', () => {
|
||||
const wrapper = shallowMount(Comp, {
|
||||
propsData: {
|
||||
value: '3',
|
||||
options: ['1', '2', '3']
|
||||
}
|
||||
})
|
||||
wrapper.find('.ds-select-option').trigger('click')
|
||||
expect(wrapper.emitted().input[0]).toEqual(['1'])
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
describe('innerValue', () => {
|
||||
test('should contain a single selected value by default', () => {
|
||||
const wrapper = shallowMount(Comp, {
|
||||
propsData: {
|
||||
value: '1',
|
||||
options: ['1', '2', '3']
|
||||
}
|
||||
})
|
||||
expect(wrapper.vm.innerValue).toEqual('1')
|
||||
})
|
||||
test('should contain an array of values when multiple: true', () => {
|
||||
const wrapper = shallowMount(Comp, {
|
||||
propsData: {
|
||||
value: ['1'],
|
||||
options: ['1', '2', '3'],
|
||||
multiple: true
|
||||
}
|
||||
})
|
||||
expect(wrapper.vm.innerValue).toEqual(['1'])
|
||||
})
|
||||
})
|
||||
|
||||
describe('options', () => {
|
||||
test('should highlight the selected value', () => {
|
||||
const wrapper = shallowMount(Comp, {
|
||||
propsData: {
|
||||
value: '1',
|
||||
options: ['1', '2', '3']
|
||||
}
|
||||
})
|
||||
const option = wrapper.find('.ds-select-option')
|
||||
expect(option.classes()).toContain('ds-select-option-is-selected')
|
||||
})
|
||||
test('should highlight all selected values when multiple: true', () => {
|
||||
const wrapper = shallowMount(Comp, {
|
||||
propsData: {
|
||||
value: ['1', '2'],
|
||||
options: ['1', '2', '3'],
|
||||
multiple: true
|
||||
}
|
||||
})
|
||||
const option = wrapper.findAll('.ds-select-option')
|
||||
expect(option.at(0).classes()).toContain('ds-select-option-is-selected')
|
||||
expect(option.at(1).classes()).toContain('ds-select-option-is-selected')
|
||||
})
|
||||
})
|
||||
|
||||
describe('selectOption', () => {
|
||||
test('should set innerValue to selected value', () => {
|
||||
const wrapper = shallowMount(Comp, {
|
||||
propsData: {
|
||||
value: '3',
|
||||
options: ['1', '2', '3']
|
||||
}
|
||||
})
|
||||
wrapper.vm.selectOption(wrapper.vm.options[0])
|
||||
expect(wrapper.vm.innerValue).toEqual('1')
|
||||
})
|
||||
test('should add selected value to innerValue when multiple: true', () => {
|
||||
const wrapper = shallowMount(Comp, {
|
||||
propsData: {
|
||||
value: ['3'],
|
||||
options: ['1', '2', '3'],
|
||||
multiple: true
|
||||
}
|
||||
})
|
||||
wrapper.vm.selectOption(wrapper.vm.options[0])
|
||||
expect(wrapper.vm.innerValue).toEqual(['3', '1'])
|
||||
})
|
||||
test('should toggle selected value in innerValue when multiple: true', () => {
|
||||
const wrapper = shallowMount(Comp, {
|
||||
propsData: {
|
||||
value: ['3', '1'],
|
||||
options: ['1', '2', '3'],
|
||||
multiple: true
|
||||
}
|
||||
})
|
||||
wrapper.vm.selectOption(wrapper.vm.options[0])
|
||||
expect(wrapper.vm.innerValue).toEqual(['3'])
|
||||
})
|
||||
})
|
||||
|
||||
describe('search', () => {
|
||||
test('should filter options by search string', () => {
|
||||
const wrapper = shallowMount(Comp, {
|
||||
propsData: {
|
||||
options: ['cat', 'duck', 'dog']
|
||||
}
|
||||
})
|
||||
wrapper.vm.searchString = 'do'
|
||||
expect(wrapper.vm.filteredOptions).toEqual(['dog'])
|
||||
})
|
||||
|
||||
test('should be case insensitive', () => {
|
||||
const wrapper = shallowMount(Comp, {
|
||||
propsData: {
|
||||
options: ['cat', 'duck', 'dog']
|
||||
}
|
||||
})
|
||||
wrapper.vm.searchString = 'DO'
|
||||
expect(wrapper.vm.filteredOptions).toEqual(['dog'])
|
||||
})
|
||||
|
||||
test('should ignore spaces', () => {
|
||||
const wrapper = shallowMount(Comp, {
|
||||
propsData: {
|
||||
options: ['cat', 'duck', 'dog']
|
||||
}
|
||||
})
|
||||
wrapper.vm.searchString = 'd o'
|
||||
expect(wrapper.vm.filteredOptions).toEqual(['dog'])
|
||||
})
|
||||
|
||||
test('should display filtered options', () => {
|
||||
const wrapper = shallowMount(Comp, {
|
||||
propsData: {
|
||||
options: ['cat', 'duck', 'dog']
|
||||
}
|
||||
})
|
||||
wrapper.vm.searchString = 'do'
|
||||
const filteredOptions = wrapper.findAll('.ds-select-option')
|
||||
expect(filteredOptions.length).toEqual(1)
|
||||
})
|
||||
|
||||
test('should work when using search input', () => {
|
||||
const wrapper = shallowMount(Comp, {
|
||||
propsData: {
|
||||
options: ['cat', 'duck', 'dog']
|
||||
}
|
||||
})
|
||||
const searchInput = wrapper.find('.ds-select-search')
|
||||
searchInput.setValue('do')
|
||||
expect(wrapper.vm.filteredOptions).toEqual(['dog'])
|
||||
})
|
||||
})
|
||||
|
||||
describe('pointer', () => {
|
||||
test('should be set by mouse over option', () => {
|
||||
const wrapper = shallowMount(Comp, {
|
||||
propsData: {
|
||||
options: ['1', '2', '3']
|
||||
}
|
||||
})
|
||||
const options = wrapper.findAll('.ds-select-option')
|
||||
options.at(2).trigger('mouseover')
|
||||
expect(wrapper.vm.pointer).toEqual(2)
|
||||
})
|
||||
|
||||
test('should be set by pointerNext', () => {
|
||||
const wrapper = shallowMount(Comp, {
|
||||
propsData: {
|
||||
options: ['1', '2', '3']
|
||||
}
|
||||
})
|
||||
wrapper.vm.pointerNext()
|
||||
expect(wrapper.vm.pointer).toEqual(1)
|
||||
})
|
||||
|
||||
test('should be set to 0 by pointerNext when on last entry', () => {
|
||||
const wrapper = shallowMount(Comp, {
|
||||
propsData: {
|
||||
options: ['1', '2', '3']
|
||||
}
|
||||
})
|
||||
wrapper.vm.pointer = 2
|
||||
wrapper.vm.pointerNext()
|
||||
expect(wrapper.vm.pointer).toEqual(0)
|
||||
})
|
||||
|
||||
test('should be set by pointerPrev', () => {
|
||||
const wrapper = shallowMount(Comp, {
|
||||
propsData: {
|
||||
options: ['1', '2', '3']
|
||||
}
|
||||
})
|
||||
wrapper.vm.pointer = 1
|
||||
wrapper.vm.pointerPrev()
|
||||
expect(wrapper.vm.pointer).toEqual(0)
|
||||
})
|
||||
|
||||
test('should be set to last entry by pointerPrev when 0', () => {
|
||||
const wrapper = shallowMount(Comp, {
|
||||
propsData: {
|
||||
options: ['1', '2', '3']
|
||||
}
|
||||
})
|
||||
wrapper.vm.pointerPrev()
|
||||
expect(wrapper.vm.pointer).toEqual(2)
|
||||
})
|
||||
|
||||
test('should be set by key down on wrap', () => {
|
||||
const wrapper = shallowMount(Comp, {
|
||||
propsData: {
|
||||
options: ['1', '2', '3']
|
||||
}
|
||||
})
|
||||
const wrap = wrapper.find('.ds-select-wrap')
|
||||
wrap.trigger('keydown.down')
|
||||
expect(wrapper.vm.pointer).toEqual(1)
|
||||
})
|
||||
|
||||
test('should be set by key up on wrap', () => {
|
||||
const wrapper = shallowMount(Comp, {
|
||||
propsData: {
|
||||
options: ['1', '2', '3']
|
||||
}
|
||||
})
|
||||
const wrap = wrapper.find('.ds-select-wrap')
|
||||
wrap.trigger('keydown.up')
|
||||
expect(wrapper.vm.pointer).toEqual(2)
|
||||
})
|
||||
|
||||
test('should be set by key down on search input', () => {
|
||||
const wrapper = shallowMount(Comp, {
|
||||
propsData: {
|
||||
options: ['1', '2', '3']
|
||||
}
|
||||
})
|
||||
const searchInput = wrapper.find('.ds-select-search')
|
||||
searchInput.trigger('keydown.down')
|
||||
expect(wrapper.vm.pointer).toEqual(1)
|
||||
})
|
||||
|
||||
test('should be set by key up on search input', () => {
|
||||
const wrapper = shallowMount(Comp, {
|
||||
propsData: {
|
||||
options: ['1', '2', '3']
|
||||
}
|
||||
})
|
||||
const searchInput = wrapper.find('.ds-select-search')
|
||||
searchInput.trigger('keydown.up')
|
||||
expect(wrapper.vm.pointer).toEqual(2)
|
||||
})
|
||||
|
||||
test('should select option by pointer value', () => {
|
||||
const wrapper = shallowMount(Comp, {
|
||||
propsData: {
|
||||
options: ['1', '2', '3']
|
||||
}
|
||||
})
|
||||
wrapper.vm.pointer = 1
|
||||
wrapper.vm.selectPointerOption()
|
||||
expect(wrapper.vm.innerValue).toEqual('2')
|
||||
})
|
||||
|
||||
test('should select option by enter key on wrap', () => {
|
||||
const wrapper = shallowMount(Comp, {
|
||||
propsData: {
|
||||
options: ['1', '2', '3']
|
||||
}
|
||||
})
|
||||
wrapper.vm.pointer = 1
|
||||
const wrap = wrapper.find('.ds-select-wrap')
|
||||
wrap.trigger('keypress.enter')
|
||||
expect(wrapper.vm.innerValue).toEqual('2')
|
||||
})
|
||||
|
||||
test('should select option by enter key on search input', () => {
|
||||
const wrapper = shallowMount(Comp, {
|
||||
propsData: {
|
||||
options: ['1', '2', '3']
|
||||
}
|
||||
})
|
||||
wrapper.vm.pointer = 1
|
||||
const searchInput = wrapper.find('.ds-select-search')
|
||||
searchInput.trigger('keypress.enter')
|
||||
expect(wrapper.vm.innerValue).toEqual('2')
|
||||
})
|
||||
})
|
||||
|
||||
it('matches snapshot', () => {
|
||||
const wrapper = shallowMount(Comp, {
|
||||
propsData: {
|
||||
value: '1',
|
||||
options: ['1', '2', '3']
|
||||
}
|
||||
})
|
||||
expect(wrapper.element).toMatchSnapshot()
|
||||
})
|
||||
})
|
||||
148
styleguide/src/system/components/data-input/Select/style.scss
Normal file → Executable file
148
styleguide/src/system/components/data-input/Select/style.scss
Normal file → Executable file
@ -1,3 +1,149 @@
|
||||
@import '../shared/input.scss';
|
||||
|
||||
@include input(ds-select);
|
||||
@include input(ds-select);
|
||||
|
||||
.ds-select {
|
||||
user-select: none;
|
||||
.ds-input-has-focus & {
|
||||
border-bottom-left-radius: 0;
|
||||
border-bottom-right-radius: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.ds-select-search, .ds-select-value {
|
||||
box-sizing: border-box;
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
border: $input-border-size solid transparent;
|
||||
padding: $input-padding-vertical $space-x-small;
|
||||
line-height: $line-height-base;
|
||||
|
||||
.ds-input-size-small & {
|
||||
padding: $input-padding-vertical-small $space-x-small;
|
||||
}
|
||||
|
||||
.ds-input-size-large & {
|
||||
padding: $input-padding-vertical-large $space-x-small;
|
||||
}
|
||||
|
||||
.ds-select-has-icon & {
|
||||
padding-left: $input-height;
|
||||
|
||||
.ds-input-size-small & {
|
||||
padding-left: $input-height-small;
|
||||
}
|
||||
|
||||
.ds-input-size-large & {
|
||||
padding-left: $input-height-large;
|
||||
}
|
||||
}
|
||||
|
||||
.ds-select-has-icon-right & {
|
||||
padding-right: $input-height;
|
||||
|
||||
.ds-input-size-small & {
|
||||
padding-right: $input-height-small;
|
||||
}
|
||||
|
||||
.ds-input-size-large & {
|
||||
padding-right: $input-height-large;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.ds-select-search {
|
||||
appearance: none;
|
||||
font-size: inherit;
|
||||
font-family: $font-family-text;
|
||||
width: 100%;
|
||||
background: transparent;
|
||||
color: $text-color-base;
|
||||
outline: none;
|
||||
user-select: text;
|
||||
|
||||
opacity: 0;
|
||||
|
||||
&::placeholder {
|
||||
color: $text-color-disabled;
|
||||
}
|
||||
|
||||
.ds-input-has-focus & {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
.ds-select-multiple & {
|
||||
position: relative;
|
||||
display: inline-flex;
|
||||
width: auto;
|
||||
height: auto;
|
||||
padding: 0;
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
|
||||
.ds-select-placeholder, .ds-select-value {
|
||||
pointer-events: none;
|
||||
|
||||
.ds-input-has-focus & {
|
||||
opacity: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.ds-select-placeholder {
|
||||
color: $text-color-disabled;
|
||||
}
|
||||
|
||||
.ds-selected-options {
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.ds-selected-option {
|
||||
display: inline-flex;
|
||||
margin-right: $space-xx-small;
|
||||
}
|
||||
|
||||
.ds-select-dropdown {
|
||||
position: absolute;
|
||||
z-index: $z-index-dropdown;
|
||||
top: 100%;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
background-color: $background-color-base;
|
||||
border: $input-border-size solid $border-color-active;
|
||||
border-top: 0;
|
||||
border-bottom-left-radius: $border-radius-base;
|
||||
border-bottom-right-radius: $border-radius-base;
|
||||
visibility: hidden;
|
||||
opacity: 0;
|
||||
transition: all $duration-short $ease-out;
|
||||
max-height: 240px;
|
||||
overflow: auto;
|
||||
|
||||
.ds-input-has-focus & {
|
||||
visibility: visible;
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
|
||||
.ds-select-options {
|
||||
@include reset-list;
|
||||
}
|
||||
|
||||
.ds-select-option {
|
||||
padding: $input-padding-vertical $space-x-small;
|
||||
cursor: pointer;
|
||||
transition: all $duration-short $ease-out;
|
||||
|
||||
&.ds-select-option-hover {
|
||||
background-color: $background-color-primary;
|
||||
color: $text-color-primary-inverse;
|
||||
}
|
||||
}
|
||||
|
||||
.ds-select-option-is-selected {
|
||||
background-color: $background-color-soft;
|
||||
color: $text-color-primary;
|
||||
}
|
||||
|
||||
23
styleguide/src/system/components/data-input/shared/input.js
Normal file → Executable file
23
styleguide/src/system/components/data-input/shared/input.js
Normal file → Executable file
@ -20,7 +20,7 @@ export default {
|
||||
* The value of the input. Can be passed via v-model.
|
||||
*/
|
||||
value: {
|
||||
type: [String, Object, Number],
|
||||
type: [String, Object, Number, Array],
|
||||
default: null
|
||||
},
|
||||
/**
|
||||
@ -56,7 +56,7 @@ export default {
|
||||
*/
|
||||
schema: {
|
||||
type: Object,
|
||||
default: () => ({})
|
||||
default: () => null
|
||||
},
|
||||
/**
|
||||
* The input's size.
|
||||
@ -68,6 +68,10 @@ export default {
|
||||
validator: value => {
|
||||
return value.match(/(small|base|large)/)
|
||||
}
|
||||
},
|
||||
tabindex: {
|
||||
type: Number,
|
||||
default: 0
|
||||
}
|
||||
},
|
||||
data() {
|
||||
@ -107,9 +111,13 @@ export default {
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
input(event) {
|
||||
handleInput(event) {
|
||||
this.input(event.target.value)
|
||||
},
|
||||
input(value) {
|
||||
this.innerValue = value
|
||||
if (this.$parentForm) {
|
||||
this.$parentForm.update(this.model, event.target.value)
|
||||
this.$parentForm.update(this.model, value)
|
||||
} else {
|
||||
/**
|
||||
* Fires after user input.
|
||||
@ -117,8 +125,8 @@ export default {
|
||||
*
|
||||
* @event input
|
||||
*/
|
||||
this.$emit('input', event.target.value)
|
||||
this.validate(event.target.value)
|
||||
this.$emit('input', value)
|
||||
this.validate(value)
|
||||
}
|
||||
},
|
||||
handleFormUpdate(data, errors) {
|
||||
@ -126,6 +134,9 @@ export default {
|
||||
this.error = errors ? errors[this.model] : null
|
||||
},
|
||||
validate(value) {
|
||||
if (!this.schema) {
|
||||
return
|
||||
}
|
||||
const validator = new Schema({ input: this.schema })
|
||||
// Prevent validator from printing to console
|
||||
// eslint-disable-next-line
|
||||
|
||||
79
styleguide/src/system/components/data-input/shared/input.scss
Normal file → Executable file
79
styleguide/src/system/components/data-input/shared/input.scss
Normal file → Executable file
@ -2,63 +2,67 @@
|
||||
.#{$class}-wrap {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
|
||||
.#{$class} {
|
||||
appearance: none;
|
||||
box-sizing: border-box;
|
||||
font-size: $font-size-base;
|
||||
font-size: $input-font-size-base;
|
||||
line-height: $line-height-base;
|
||||
font-family: $font-family-text;
|
||||
width: 100%;
|
||||
padding: $input-padding-vertical $space-x-small;
|
||||
height: $input-height;
|
||||
|
||||
|
||||
color: $text-color-base;
|
||||
background: $background-color-base;
|
||||
|
||||
border: $input-border-size solid $border-color-soft;
|
||||
|
||||
border: $input-border-size solid $border-color-base;
|
||||
border-radius: $border-radius-base;
|
||||
outline: none;
|
||||
transition: all $duration-short $ease-out;
|
||||
|
||||
|
||||
&::placeholder {
|
||||
color: $text-color-disabled;
|
||||
}
|
||||
|
||||
|
||||
.ds-input-has-focus &,
|
||||
&:focus {
|
||||
border-color: $border-color-active;
|
||||
background: $background-color-base;
|
||||
}
|
||||
|
||||
.#{$class}-is-disabled &,
|
||||
|
||||
.ds-input-is-disabled &,
|
||||
&:disabled {
|
||||
color: $text-color-disabled;
|
||||
opacity: $opacity-disabled;
|
||||
cursor: not-allowed;
|
||||
}
|
||||
|
||||
.#{$class}-has-error & {
|
||||
|
||||
.ds-input-has-error & {
|
||||
border-color: $border-color-danger;
|
||||
}
|
||||
}
|
||||
|
||||
.#{$class}-size-small {
|
||||
|
||||
.ds-input-size-small {
|
||||
font-size: $font-size-small;
|
||||
|
||||
|
||||
.#{$class} {
|
||||
font-size: $input-font-size-small;
|
||||
height: $input-height-small;
|
||||
padding: $input-padding-vertical-small $space-x-small;
|
||||
}
|
||||
}
|
||||
|
||||
.#{$class}-size-large {
|
||||
|
||||
.ds-input-size-large {
|
||||
font-size: $font-size-large;
|
||||
|
||||
|
||||
.#{$class} {
|
||||
font-size: $input-font-size-large;
|
||||
height: $input-height-large;
|
||||
padding: $input-padding-vertical-large $space-x-small;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
.#{$class}-icon,
|
||||
.#{$class}-icon-right {
|
||||
position: absolute;
|
||||
@ -71,32 +75,47 @@
|
||||
width: $input-height;
|
||||
color: $text-color-softer;
|
||||
transition: color $duration-short $ease-out;
|
||||
|
||||
.#{$class}-has-focus & {
|
||||
pointer-events: none;
|
||||
|
||||
.ds-input-has-focus & {
|
||||
color: $text-color-base;
|
||||
}
|
||||
}
|
||||
|
||||
.ds-input-size-small & {
|
||||
width: $input-height-small;
|
||||
}
|
||||
|
||||
.ds-input-size-large & {
|
||||
width: $input-height-large;
|
||||
}
|
||||
}
|
||||
|
||||
.#{$class}-icon-right {
|
||||
right: 0;
|
||||
left: auto;
|
||||
}
|
||||
|
||||
|
||||
.#{$class}-has-icon {
|
||||
padding-left: $input-height;
|
||||
|
||||
.ds-input-size-small & {
|
||||
padding-left: $input-height-small;
|
||||
}
|
||||
|
||||
.#{$class}-size-small &,
|
||||
.#{$class}-size-large & {
|
||||
padding-left: $input-height;
|
||||
.ds-input-size-large & {
|
||||
padding-left: $input-height-large;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
.#{$class}-has-icon-right {
|
||||
padding-right: $input-height;
|
||||
|
||||
.#{$class}-size-small &,
|
||||
.#{$class}-size-large & {
|
||||
padding-right: $input-height;
|
||||
.ds-input-size-small & {
|
||||
padding-right: $input-height-small;
|
||||
}
|
||||
}
|
||||
|
||||
.ds-input-size-large & {
|
||||
padding-right: $input-height-large;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
49
styleguide/src/system/components/data-input/shared/multiinput.js
Executable file
49
styleguide/src/system/components/data-input/shared/multiinput.js
Executable file
@ -0,0 +1,49 @@
|
||||
/**
|
||||
* @mixin
|
||||
*/
|
||||
export default {
|
||||
props: {
|
||||
/**
|
||||
* Whether the user can select multiple items
|
||||
*/
|
||||
multiple: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
selectOption(option) {
|
||||
const newValue = option.value || option
|
||||
if (this.multiple) {
|
||||
this.selectMultiOption(newValue)
|
||||
} else {
|
||||
this.input(newValue)
|
||||
}
|
||||
},
|
||||
selectMultiOption(value) {
|
||||
if (!this.innerValue) {
|
||||
return this.input([value])
|
||||
}
|
||||
const index = this.innerValue.indexOf(value)
|
||||
if (index < 0) {
|
||||
return this.input([...this.innerValue, value])
|
||||
}
|
||||
this.deselectOption(index)
|
||||
},
|
||||
deselectOption(index) {
|
||||
const newArray = [...this.innerValue]
|
||||
newArray.splice(index, 1)
|
||||
this.input(newArray)
|
||||
},
|
||||
isSelected(option) {
|
||||
if (!this.innerValue) {
|
||||
return false
|
||||
}
|
||||
const value = option.value || option
|
||||
if (this.multiple) {
|
||||
return this.innerValue.includes(value)
|
||||
}
|
||||
return this.innerValue === value
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -14,9 +14,9 @@
|
||||
v-if="image || $slots.image">
|
||||
<!-- @slot Content of the card's image -->
|
||||
<slot name="image">
|
||||
<img
|
||||
:src="image"
|
||||
v-if="!error"
|
||||
<img
|
||||
:src="image"
|
||||
v-if="!error"
|
||||
@error="onError" >
|
||||
</slot>
|
||||
</div>
|
||||
@ -36,7 +36,14 @@
|
||||
</slot>
|
||||
</header>
|
||||
<div class="ds-card-content">
|
||||
<slot />
|
||||
<template v-if="space">
|
||||
<ds-space :margin="space">
|
||||
<slot />
|
||||
</ds-space>
|
||||
</template>
|
||||
<template v-else>
|
||||
<slot />
|
||||
</template>
|
||||
</div>
|
||||
<footer
|
||||
class="ds-card-footer"
|
||||
@ -125,6 +132,14 @@ export default {
|
||||
hover: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
/**
|
||||
* If you need some spacing you can provide it here like for ds-space
|
||||
* `xxx-small, xx-small, x-small, small, large, x-large, xx-large, xxx-large`
|
||||
*/
|
||||
space: {
|
||||
type: String,
|
||||
default: null
|
||||
}
|
||||
},
|
||||
data() {
|
||||
|
||||
@ -21,6 +21,23 @@ Set a header and image for the card and provide some content.
|
||||
</ds-flex>
|
||||
```
|
||||
|
||||
## Space
|
||||
|
||||
Need more or less space top and bottom of the card?
|
||||
Specify with with the `space` prop
|
||||
|
||||
```html
|
||||
<ds-card space="xx-small">
|
||||
xx-small
|
||||
</ds-card>
|
||||
```
|
||||
|
||||
```html
|
||||
<ds-card space="xx-large">
|
||||
xx-large
|
||||
</ds-card>
|
||||
```
|
||||
|
||||
## Cards with footer
|
||||
|
||||
Most commonly the footer will contain some actions connected to the content.
|
||||
|
||||
93
styleguide/src/system/components/typography/Chip/Chip.vue
Executable file
93
styleguide/src/system/components/typography/Chip/Chip.vue
Executable file
@ -0,0 +1,93 @@
|
||||
<template>
|
||||
<component
|
||||
:is="tag"
|
||||
class="ds-chip"
|
||||
:class="[
|
||||
`ds-chip-size-${size}`,
|
||||
`ds-chip-${color}`,
|
||||
removable && 'ds-chip-removable',
|
||||
round && 'ds-chip-round'
|
||||
]"
|
||||
>
|
||||
<slot />
|
||||
<button
|
||||
v-if="removable"
|
||||
@click="remove"
|
||||
class="ds-chip-close">
|
||||
<ds-icon name="close" />
|
||||
</button>
|
||||
</component>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
/**
|
||||
* Chips are used to represent small blocks of information.
|
||||
* Their most common usage is for displaying contacts or tags.
|
||||
* @version 1.0.0
|
||||
*/
|
||||
export default {
|
||||
name: 'DsChip',
|
||||
props: {
|
||||
/**
|
||||
* The background color used for the chip.
|
||||
* `medium, inverse, primary, success, warning, danger`
|
||||
*/
|
||||
color: {
|
||||
type: String,
|
||||
default: 'medium',
|
||||
validator: value => {
|
||||
return value.match(/(medium|inverse|primary|success|warning|danger)/)
|
||||
}
|
||||
},
|
||||
/**
|
||||
* The size used for the text.
|
||||
* `base, large, small`
|
||||
*/
|
||||
size: {
|
||||
type: String,
|
||||
default: 'base',
|
||||
validator: value => {
|
||||
return value.match(/(base|large|small)/)
|
||||
}
|
||||
},
|
||||
/**
|
||||
* Whether the chip should be removeable
|
||||
* `true, false`
|
||||
*/
|
||||
removable: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
/**
|
||||
* Whether the chip should be rounded
|
||||
* `true, false`
|
||||
*/
|
||||
round: {
|
||||
type: Boolean,
|
||||
default: true
|
||||
},
|
||||
/**
|
||||
* The html element name used for the text.
|
||||
*/
|
||||
tag: {
|
||||
type: String,
|
||||
default: 'span'
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
remove() {
|
||||
/**
|
||||
* Fires after user clicked the remove button.
|
||||
*
|
||||
* @event remove
|
||||
*/
|
||||
this.$emit('remove')
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" src="./style.scss">
|
||||
</style>
|
||||
|
||||
<docs src="./demo.md"></docs>
|
||||
62
styleguide/src/system/components/typography/Chip/demo.md
Executable file
62
styleguide/src/system/components/typography/Chip/demo.md
Executable file
@ -0,0 +1,62 @@
|
||||
## Chip colors
|
||||
|
||||
Use different colors to emphasize or provide meaning.
|
||||
|
||||
```
|
||||
<ds-chip>Medium</ds-chip>
|
||||
<ds-chip color="inverse">Inverse</ds-chip>
|
||||
<ds-chip color="primary">Primary</ds-chip>
|
||||
<ds-chip color="success">Success</ds-chip>
|
||||
<ds-chip color="warning">Warning</ds-chip>
|
||||
<ds-chip color="danger">Danger</ds-chip>
|
||||
```
|
||||
|
||||
## Chip sizes
|
||||
|
||||
Use different sizes to create hierarchy (defaults to `base`).
|
||||
|
||||
```
|
||||
<ds-chip size="small">Small</ds-chip>
|
||||
<ds-chip size="base">Base</ds-chip>
|
||||
<ds-chip size="large">Large</ds-chip>
|
||||
```
|
||||
|
||||
## Deletable
|
||||
|
||||
A chip can be deletable.
|
||||
|
||||
```
|
||||
<template>
|
||||
<div>
|
||||
<ds-chip
|
||||
v-for="(tag, index) in tags"
|
||||
@remove="removeTag(index)"
|
||||
removable
|
||||
:key="tag">
|
||||
{{ tag }}
|
||||
</ds-chip>
|
||||
<ds-chip
|
||||
v-for="(tag, index) in tags"
|
||||
@remove="removeTag(index)"
|
||||
removable
|
||||
color="primary"
|
||||
:key="tag">
|
||||
{{ tag }}
|
||||
</ds-chip>
|
||||
</div>
|
||||
</template>
|
||||
<script>
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
tags: ['Dog', 'Cat', 'Duck']
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
removeTag (index) {
|
||||
this.tags.splice(index, 1)
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
```
|
||||
81
styleguide/src/system/components/typography/Chip/style.scss
Executable file
81
styleguide/src/system/components/typography/Chip/style.scss
Executable file
@ -0,0 +1,81 @@
|
||||
.ds-chip {
|
||||
@include reset;
|
||||
@include stack-space($space-xx-small);
|
||||
position: relative;
|
||||
display: inline-block;
|
||||
font-family: $font-family-text;
|
||||
line-height: $line-height-base;
|
||||
padding: $space-xx-small $space-x-small;
|
||||
border-radius: $border-radius-base;
|
||||
font-weight: $font-weight-bold;
|
||||
color: $text-color-base;
|
||||
background-color: $background-color-softest;
|
||||
|
||||
&.ds-chip-removable {
|
||||
padding-right: $space-x-small + $space-small;
|
||||
}
|
||||
}
|
||||
|
||||
.ds-chip-inverse {
|
||||
color: $text-color-inverse;
|
||||
background-color: $background-color-inverse-softer;
|
||||
}
|
||||
|
||||
.ds-chip-primary {
|
||||
color: $text-color-primary-inverse;
|
||||
background-color: $background-color-primary;
|
||||
}
|
||||
|
||||
.ds-chip-success {
|
||||
color: $text-color-success-inverse;
|
||||
background-color: $background-color-success;
|
||||
}
|
||||
|
||||
.ds-chip-warning {
|
||||
color: $text-color-warning-inverse;
|
||||
background-color: $background-color-warning;
|
||||
}
|
||||
|
||||
.ds-chip-danger {
|
||||
color: $text-color-danger-inverse;
|
||||
background-color: $background-color-danger;
|
||||
}
|
||||
|
||||
.ds-chip-round {
|
||||
border-radius: $border-radius-rounded;
|
||||
}
|
||||
|
||||
.ds-chip-size-base {
|
||||
font-size: $font-size-small;
|
||||
}
|
||||
|
||||
.ds-chip-size-small {
|
||||
font-size: $font-size-x-small;
|
||||
}
|
||||
|
||||
.ds-chip-size-large {
|
||||
font-size: $font-size-base;
|
||||
}
|
||||
|
||||
.ds-chip-close {
|
||||
@include reset-button;
|
||||
position: absolute;
|
||||
right: $space-xx-small;
|
||||
top: 50%;
|
||||
transform: translateY(-50%);
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-size: $font-size-x-small;
|
||||
width: $space-small;
|
||||
height: $space-small;
|
||||
border-radius: $border-radius-circle;
|
||||
//background-color: $background-color-base;
|
||||
opacity: $opacity-soft;
|
||||
cursor: pointer;
|
||||
transition: all $duration-short $ease-out-sharp;
|
||||
|
||||
&:hover {
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
@ -1,23 +1,27 @@
|
||||
/* FORM VARIABLES / MIXINS
|
||||
--------------------------------------------- */
|
||||
|
||||
$input-font-size-base: $font-size-base;
|
||||
$input-border-size: $border-size-base;
|
||||
$input-padding-vertical: $space-x-small;
|
||||
$input-height: calc(
|
||||
#{$font-size-base * $line-height-base} + #{($input-padding-vertical + $input-border-size) * 2}
|
||||
);
|
||||
|
||||
$input-padding-vertical-small: $space-xx-small;
|
||||
$input-font-size-small: $font-size-base;
|
||||
$input-padding-vertical-small: $space-xxx-small;
|
||||
$input-height-small: calc(
|
||||
#{$font-size-small * $line-height-base} + #{($input-padding-vertical-small + $input-border-size) * 2}
|
||||
#{$font-size-base * $line-height-base} + #{($input-padding-vertical-small + $input-border-size) * 2}
|
||||
);
|
||||
|
||||
$input-font-size-large: $font-size-large;
|
||||
$input-padding-vertical-large: $space-x-small;
|
||||
$input-height-large: calc(
|
||||
#{$font-size-large * $line-height-base} + #{($input-padding-vertical-large + $input-border-size) * 2}
|
||||
#{$input-font-size-large * $line-height-base} + #{($input-padding-vertical-large + $input-border-size) * 2}
|
||||
);
|
||||
|
||||
// $input-font-size-large: $font-size-x-large;
|
||||
$input-padding-vertical-x-large: $space-small;
|
||||
$input-height-x-large: calc(
|
||||
#{$font-size-x-large * $line-height-base} + #{($input-padding-vertical-x-large + $input-border-size) * 2}
|
||||
#{$input-font-size-large * $line-height-base} + #{($input-padding-vertical-x-large + $input-border-size) * 2}
|
||||
);
|
||||
|
||||
@ -2,15 +2,11 @@
|
||||
--------------------------------------------- */
|
||||
|
||||
/* AUTO SCALING FOR TYPE WITH MIN/MAX SIZES
|
||||
|
||||
@param {Number} $responsive - Viewport-based size
|
||||
@param {Number} $min - Minimum font size (px)
|
||||
@param {Number} $max - Maximum font size (px) (optional)
|
||||
|
||||
@param {Number} $fallback - Fallback for viewport-based units (optional)
|
||||
|
||||
@example SCSS - 5vw size, 35px min & 150px max size + 50px fallback:
|
||||
|
||||
@include responsive-font(5vw, 35px, 150px, 50px);
|
||||
*/
|
||||
@mixin responsive-font($responsive, $min, $max: false, $fallback: false) {
|
||||
@ -44,6 +40,32 @@
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
@mixin reset-list {
|
||||
@include reset;
|
||||
list-style: none;
|
||||
}
|
||||
|
||||
@mixin reset-button {
|
||||
@include reset;
|
||||
border: 0;
|
||||
width: auto;
|
||||
overflow: visible;
|
||||
background: transparent;
|
||||
color: inherit;
|
||||
font: inherit;
|
||||
line-height: normal;
|
||||
outline: none;
|
||||
font-smoothing: inherit;
|
||||
-webkit-font-smoothing: inherit;
|
||||
-moz-osx-font-smoothing: inherit;
|
||||
-webkit-appearance: none;
|
||||
|
||||
&::-moz-focus-inner {
|
||||
border: 0;
|
||||
padding: 0;
|
||||
}
|
||||
}
|
||||
|
||||
@mixin clearfix {
|
||||
&:before, &:after {
|
||||
display: table;
|
||||
@ -104,4 +126,4 @@
|
||||
border-bottom-left-radius: $size;
|
||||
border-bottom-right-radius: $size;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -291,6 +291,10 @@ props:
|
||||
value: *color-neutral-80
|
||||
category: border-color
|
||||
|
||||
- name: border-color-light
|
||||
value: *color-neutral-90
|
||||
category: border-color
|
||||
|
||||
- name: border-color-active
|
||||
value: *color-primary
|
||||
category: border-color
|
||||
|
||||
@ -17,7 +17,7 @@ props:
|
||||
- name: font-size-base
|
||||
value: "1rem"
|
||||
- name: font-size-body
|
||||
value: "16px"
|
||||
value: "15px"
|
||||
- name: font-size-small
|
||||
value: "0.8rem"
|
||||
- name: font-size-x-small
|
||||
|
||||
@ -6,6 +6,8 @@
|
||||
props:
|
||||
- name: z-index-modal
|
||||
value: "9999"
|
||||
- name: z-index-dropdown
|
||||
value: "8888"
|
||||
- name: z-index-page-submenu
|
||||
value: "2500"
|
||||
- name: z-index-page-header
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user