Skip to content

Commit 393c180

Browse files
committed
feat(vuetify): add custom-select component
1 parent 1633d0b commit 393c180

File tree

8 files changed

+238
-25
lines changed

8 files changed

+238
-25
lines changed

.github/workflows/release-docs.yml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,8 @@ jobs:
2727
with:
2828
branch: gh-pages
2929
folder: docs/src/.vuepress/dist
30+
clean-exclude: |
31+
next
3032
3133
- name: Deploy next
3234
if: github.ref == 'refs/heads/next'

assets/cron-vuetify-hero.png

-15.6 KB
Loading

vuetify/dev/example-usage.vue

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
<v-card>
55
<v-card-text>
66
<v-text-field label="" :value="value" @change="value=$event"></v-text-field>
7-
<VueCronEditor v-model="value">
7+
<VueCronEditor v-model="value" :chip-props="{ color: 'primary' }">
88

99
</VueCronEditor>
1010
</v-card-text>

vuetify/dev/serve.js

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,15 @@
1+
import '@mdi/font/css/materialdesignicons.css'
12
import Vue from 'vue'
2-
import example from './example-usage.vue'
33
import Vuetify from 'vuetify'
44
import 'vuetify/dist/vuetify.min.css'
5+
import example from './example-usage.vue'
56

67
Vue.use(Vuetify)
7-
const opts = {}
8+
const opts = {
9+
icons: {
10+
iconfont: 'mdi',
11+
},
12+
}
813
const vuetify = new Vuetify(opts)
914

1015
Vue.config.productionTip = false

vuetify/package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
"build:unpkg": "yarn rollup --config build/rollup.config.js --format iife --file dist/vuetify.min.js"
2222
},
2323
"dependencies": {
24+
"@mdi/font": "^7.0.96",
2425
"@vue-js-cron/core": "2.1.2",
2526
"vuetify": "^2.4.6"
2627
},

vuetify/src/CronEditor.vue

Lines changed: 42 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -5,25 +5,30 @@
55
<v-row align="baseline" dense>
66
<v-col v-if="period.prefix" class="flex-grow-0">{{period.prefix}}</v-col>
77
<v-col cols="auto">
8-
<v-select class="fit" v-bind="period.attrs" :items="period.items" @input="period.events.input" item-value="id" dense></v-select>
8+
<custom-select
9+
v-bind="period.attrs"
10+
:items="period.items"
11+
v-on="period.events"
12+
item-value="id"
13+
:menu-props="menuProps || { offsetY: true }"
14+
:chipProps="chipProps" />
915
</v-col>
1016
<v-col v-if="period.suffix" class="flex-grow-0">{{period.suffix}}</v-col>
1117

1218

1319
<template v-for="f in fields">
1420
<v-col v-if="f.prefix" class="flex-grow-0" :key="f.id+'-prefix'">{{f.prefix}}</v-col>
1521
<v-col cols="auto" :key="f.id">
16-
<v-select class="fit" v-bind="f.attrs" v-on="f.events" :items="f.items" multiple dense :menu-props="{ auto: false, offsetY: true }">
17-
<template #prepend-inner>
18-
<div>{{f.selectedStr}}</div>
19-
</template>
20-
<template #selection>
21-
22-
</template>
23-
<template #item="{item, attrs}">
24-
<v-list-item-title v-bind="attrs">{{item.text}}</v-list-item-title>
25-
</template>
26-
</v-select>
22+
<custom-select
23+
v-bind="f.attrs"
24+
v-on="f.events"
25+
:cols="cols(f.id)"
26+
:items="f.items"
27+
:selection="f.selectedStr"
28+
multiple
29+
:menu-props="menuProps || { offsetY: true, closeOnContentClick: false }"
30+
:chipProps="chipProps"
31+
:clearable="true" />
2732
</v-col>
2833
<v-col v-if="f.suffix" class="flex-grow-0" :key="f.id+'-suffix'">{{f.suffix}}</v-col>
2934
</template>
@@ -35,12 +40,37 @@
3540

3641
<script>
3742
import CronCore from '@vue-js-cron/core'
43+
import CustomSelect from './components/CustomSelect.vue'
3844
3945
export default {
4046
name: "VueCronEditor",
4147
components:{
4248
'CronCore': CronCore.component,
49+
CustomSelect
4350
},
51+
props: {
52+
chipProps: {
53+
type: Object,
54+
default() {
55+
return {}
56+
}
57+
},
58+
menuProps: {
59+
type: Object,
60+
default() {
61+
return null
62+
}
63+
},
64+
cols: {
65+
type: Function,
66+
default(fieldId) {
67+
if (fieldId === 'minute') return 5
68+
else if (fieldId === 'hour') return 4
69+
else if (fieldId === 'day') return 4
70+
else return 1
71+
}
72+
}
73+
}
4474
}
4575
</script>
4676

Lines changed: 165 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,24 +1,179 @@
11
<template>
2-
<div>
3-
<v-select v-bind="$attrs" v-on="$listeners">
2+
<v-menu v-bind="menuProps">
3+
4+
<template #activator="{ on }">
5+
<v-chip v-on="on" v-bind="chipProps">
6+
7+
{{selection ? selection : selectedStr}}
48

5-
<template v-for="(_, slot) of $scopedSlots" v-slot:[slot]="scope"><slot :name="slot" v-bind="scope" /></template>
6-
7-
</v-select>
8-
</div>
9+
<template v-if="clearable">
10+
<v-icon v-if="selectedItems.length > 0" size="small" @click.stop="clear()">mdi-close</v-icon>
11+
</template>
12+
13+
</v-chip>
14+
</template>
15+
16+
17+
<v-list class="pa-0 ma-0">
18+
<v-list-item-group :value="selectedItems" multiple>
19+
<v-row v-for="(itemRow, index) in itemRows" :key="index" no-gutters>
20+
<v-col v-for="(item, index) in itemRow" :key="index">
21+
<v-list-item v-if="item" :value="item" @click="select(item)">{{item[itemText]}}</v-list-item>
22+
</v-col>
23+
</v-row>
24+
</v-list-item-group>
25+
</v-list>
26+
</v-menu>
27+
928
</template>
1029

1130
<script>
12-
1331
export default {
1432
inheritAttrs: false,
1533
name: 'CustomSelect',
16-
props:{
17-
34+
props: {
35+
multiple: {
36+
type: Boolean,
37+
default: false
38+
},
39+
value: {
40+
type: [String, Array, Object],
41+
default (props) {
42+
return props.multiple ? [] : null
43+
}
44+
},
45+
items: {
46+
type: Array,
47+
default: () => []
48+
},
49+
returnObject: {
50+
type: Boolean,
51+
default: false
52+
},
53+
itemText: {
54+
type: String,
55+
default: 'text'
56+
},
57+
itemValue: {
58+
type: String,
59+
default: 'value'
60+
},
61+
cols: {
62+
type: Number,
63+
default: 1
64+
},
65+
width: {
66+
type: String,
67+
default: 'unset'
68+
},
69+
chipProps: {
70+
type: Object,
71+
default() {
72+
return {}
73+
}
74+
},
75+
menuProps: {
76+
type: Object,
77+
default() {
78+
return {}
79+
}
80+
},
81+
selection: {
82+
type: String,
83+
default: ''
84+
},
85+
closeOnContentClick: {
86+
type: Boolean,
87+
default: true
88+
},
89+
clearable: {
90+
type: Boolean,
91+
default: false
92+
}
93+
},
94+
data () {
95+
return {
96+
menu: false
97+
}
98+
},
99+
computed: {
100+
listStyle () {
101+
return {
102+
display: (this.menu) ? 'inline-block' : 'none',
103+
minWidth: '5em',
104+
width: this.width
105+
}
106+
},
107+
listItemStyle () {
108+
return {
109+
width: 100 / this.cols + '%'
110+
}
111+
},
112+
_value () {
113+
return (this.multiple) ? this.value : [this.value]
114+
},
115+
selectedItems () {
116+
return this.items.filter((item) => {
117+
for (const value of this._value) {
118+
if (this.returnObject) {
119+
if (value === item) return true
120+
} else {
121+
if (value === item[this.itemValue]) return true
122+
}
123+
}
124+
return false
125+
})
126+
},
127+
selectedStr () {
128+
return this.selectedItems.map((item) => item[this.itemText]).join(',')
129+
},
130+
rows () {
131+
return Array.isArray(this.items) ? Math.ceil(this.items.length / this.cols) : 0
132+
},
133+
itemRows () {
134+
return Array.from(Array(this.rows), (_, i) => {
135+
return Array.from(Array(this.cols), (_, j) => {
136+
return this.items[this.cols * i + j]
137+
})
138+
})
139+
}
140+
},
141+
methods: {
142+
menuEvtListener (evt) {
143+
this.menu = false
144+
document.removeEventListener('click', this.menuEvtListener)
145+
},
146+
toggleMenu () {
147+
this.menu = !this.menu
148+
if (this.menu) {
149+
setTimeout(() => {
150+
document.addEventListener('click', this.menuEvtListener)
151+
}, 1)
152+
} else {
153+
document.removeEventListener('click', this.menuEvtListener)
154+
}
155+
},
156+
select (item) {
157+
if (this.multiple) {
158+
const value = this.selectedItems.slice()
159+
const i = this.selectedItems.indexOf(item)
160+
// deselect
161+
if (i >= 0) {
162+
value.splice(i, 1)
163+
} else { // select
164+
value.push(item)
165+
}
166+
this.$emit('input', (this.returnObject) ? value : value.map((item) => item[this.itemValue]))
167+
} else {
168+
this.$emit('input', (this.returnObject) ? item : item[this.itemValue])
169+
}
170+
},
171+
clear () {
172+
this.$emit('input', this.multiple ? [] : null)
173+
}
18174
}
19175
}
20176
</script>
21177

22178
<style>
23-
24179
</style>

yarn.lock

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1203,6 +1203,11 @@
12031203
globby "^11.0.0"
12041204
read-yaml-file "^1.1.0"
12051205

1206+
"@mdi/font@^7.0.96":
1207+
version "7.0.96"
1208+
resolved "https://registry.yarnpkg.com/@mdi/font/-/font-7.0.96.tgz#9853c222623072f5575b4039c8c195ea929b61fc"
1209+
integrity sha512-rzlxTfR64hqY8yiBzDjmANfcd8rv+T5C0Yedv/TWk2QyAQYdc66e0kaN1ipmnYU3RukHRTRcBARHzzm+tIhL7w==
1210+
12061211
"@mrmlnc/readdir-enhanced@^2.2.1":
12071212
version "2.2.1"
12081213
resolved "https://registry.yarnpkg.com/@mrmlnc/readdir-enhanced/-/readdir-enhanced-2.2.1.tgz#524af240d1a360527b730475ecfa1344aa540dde"
@@ -2026,6 +2031,21 @@
20262031
dependencies:
20272032
"@types/yargs-parser" "*"
20282033

2034+
"@vue-js-cron/core@2.0.0":
2035+
version "2.0.0"
2036+
resolved "https://registry.yarnpkg.com/@vue-js-cron/core/-/core-2.0.0.tgz#4bc59d8a8a9c9efa27732ff686eb6789e38356e5"
2037+
integrity sha512-Ce5tmQAbyQ05TSNmRZsDDwsTQDKqef2nAKIsmmSsyRul8uL9Eer33ErdOrHvRGyUfcCJZSiFPYct/cWy/I4i+w==
2038+
dependencies:
2039+
mustache "^4.2.0"
2040+
2041+
"@vue-js-cron/vuetify@2.0.0":
2042+
version "2.0.0"
2043+
resolved "https://registry.yarnpkg.com/@vue-js-cron/vuetify/-/vuetify-2.0.0.tgz#3360d696ce21f8f69de86154f5a65c5282798ca7"
2044+
integrity sha512-406qplg1nF/wuf+7C25yIx12m2XOx6FoC6WDi61UQ1oYsuMMgYwzYOWe1PxYKxDQlZrIO+gb5e9zKLuEL0QnAA==
2045+
dependencies:
2046+
"@vue-js-cron/core" "2.0.0"
2047+
vuetify "^2.4.6"
2048+
20292049
"@vue/babel-helper-vue-jsx-merge-props@^1.2.1":
20302050
version "1.2.1"
20312051
resolved "https://registry.yarnpkg.com/@vue/babel-helper-vue-jsx-merge-props/-/babel-helper-vue-jsx-merge-props-1.2.1.tgz#31624a7a505fb14da1d58023725a4c5f270e6a81"

0 commit comments

Comments
 (0)