aboutsummaryrefslogtreecommitdiff
path: root/src/components/selectable_list
diff options
context:
space:
mode:
Diffstat (limited to 'src/components/selectable_list')
-rw-r--r--src/components/selectable_list/selectable_list.js66
-rw-r--r--src/components/selectable_list/selectable_list.vue63
2 files changed, 129 insertions, 0 deletions
diff --git a/src/components/selectable_list/selectable_list.js b/src/components/selectable_list/selectable_list.js
new file mode 100644
index 00000000..10980d46
--- /dev/null
+++ b/src/components/selectable_list/selectable_list.js
@@ -0,0 +1,66 @@
+import List from '../list/list.vue'
+import Checkbox from '../checkbox/checkbox.vue'
+
+const SelectableList = {
+ components: {
+ List,
+ Checkbox
+ },
+ props: {
+ items: {
+ type: Array,
+ default: () => []
+ },
+ getKey: {
+ type: Function,
+ default: item => item.id
+ }
+ },
+ data () {
+ return {
+ selected: []
+ }
+ },
+ computed: {
+ allKeys () {
+ return this.items.map(this.getKey)
+ },
+ filteredSelected () {
+ return this.allKeys.filter(key => this.selected.indexOf(key) !== -1)
+ },
+ allSelected () {
+ return this.filteredSelected.length === this.items.length
+ },
+ noneSelected () {
+ return this.filteredSelected.length === 0
+ },
+ someSelected () {
+ return !this.allSelected && !this.noneSelected
+ }
+ },
+ methods: {
+ isSelected (item) {
+ return this.filteredSelected.indexOf(this.getKey(item)) !== -1
+ },
+ toggle (checked, item) {
+ const key = this.getKey(item)
+ const oldChecked = this.isSelected(key)
+ if (checked !== oldChecked) {
+ if (checked) {
+ this.selected.push(key)
+ } else {
+ this.selected.splice(this.selected.indexOf(key), 1)
+ }
+ }
+ },
+ toggleAll (value) {
+ if (value) {
+ this.selected = this.allKeys.slice(0)
+ } else {
+ this.selected = []
+ }
+ }
+ }
+}
+
+export default SelectableList
diff --git a/src/components/selectable_list/selectable_list.vue b/src/components/selectable_list/selectable_list.vue
new file mode 100644
index 00000000..3f16c921
--- /dev/null
+++ b/src/components/selectable_list/selectable_list.vue
@@ -0,0 +1,63 @@
+<template>
+ <div class="selectable-list">
+ <div class="selectable-list-header" v-if="items.length > 0">
+ <div class="selectable-list-checkbox-wrapper">
+ <Checkbox :checked="allSelected" @change="toggleAll" :indeterminate="someSelected">{{ $t('selectable_list.select_all') }}</Checkbox>
+ </div>
+ <div class="selectable-list-header-actions">
+ <slot name="header" :selected="filteredSelected" />
+ </div>
+ </div>
+ <List :items="items" :getKey="getKey">
+ <template slot="item" slot-scope="{item}">
+ <div class="selectable-list-item-inner" :class="{ 'selectable-list-item-selected-inner': isSelected(item) }">
+ <div class="selectable-list-checkbox-wrapper">
+ <Checkbox :checked="isSelected(item)" @change="checked => toggle(checked, item)" />
+ </div>
+ <slot name="item" :item="item" />
+ </div>
+ </template>
+ <template slot="empty"><slot name="empty" /></template>
+ </List>
+ </div>
+</template>
+
+<script src="./selectable_list.js"></script>
+
+<style lang="scss">
+@import '../../_variables.scss';
+
+.selectable-list {
+ &-item-inner {
+ display: flex;
+ align-items: center;
+
+ > * {
+ min-width: 0;
+ }
+ }
+
+ &-item-selected-inner {
+ background-color: $fallback--lightBg;
+ background-color: var(--lightBg, $fallback--lightBg);
+ }
+
+ &-header {
+ display: flex;
+ align-items: center;
+ padding: 0.6em 0;
+ border-bottom: 2px solid;
+ border-bottom-color: $fallback--border;
+ border-bottom-color: var(--border, $fallback--border);
+
+ &-actions {
+ flex: 1;
+ }
+ }
+
+ &-checkbox-wrapper {
+ padding: 0 10px;
+ flex: none;
+ }
+}
+</style>