Skip to content

Commit f5d4846

Browse files
committed
initial commit
0 parents  commit f5d4846

20 files changed

+6090
-0
lines changed

.babelrc

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
{
2+
"presets": ["@babel/preset-env"]
3+
}

.gitignore

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
.vscode
2+
node_modules
3+
dist

build/rollup.config.js

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
import commonjs from '@rollup/plugin-commonjs'; // Convert CommonJS modules to ES6
2+
import vue from 'rollup-plugin-vue'; // Handle .vue SFC files
3+
import buble from '@rollup/plugin-buble'; // Transpile/polyfill with reasonable browser support
4+
export default {
5+
input: 'src/index.js', // Path relative to package.json
6+
output: {
7+
name: 'core',
8+
exports: 'named',
9+
},
10+
plugins: [
11+
commonjs(),
12+
vue({
13+
css: true, // Dynamically inject css as a <style> tag
14+
compileTemplate: true, // Explicitly convert template to render function
15+
}),
16+
buble({
17+
objectAssign: 'Object.assign',
18+
transforms: {
19+
forOf: false
20+
}
21+
}), // Transpile to ES5
22+
],
23+
};

examples/vue-cron.vue

Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
<template>
2+
<div>
3+
Cron: <input :value="value" @change="value = $event.target.value" type="text" />
4+
<VueCronCore v-model="value">
5+
<template #default="p">
6+
<div>
7+
<span>
8+
Every:
9+
<select @input="p.rankEvents.input(parseInt($event.target.value))">
10+
<option v-for="item in p.rankData.items" :key="item.value" :value="item.value">{{item.text}}</option>
11+
</select>
12+
</span>
13+
14+
15+
<template v-for="f in p.fields">
16+
<span :key="f.name" v-if="p.rankAttrs.value >= f.rank">
17+
{{f.name}}:
18+
<select @input="f.events.input(getSelected($event.target))" multiple>
19+
<option v-for="item in f.items" :key="item.value" :value="item.value">{{item.text}}</option>
20+
</select>
21+
</span>
22+
</template>
23+
24+
<div>-</div>
25+
26+
<div>error:{{p.error}}</div>
27+
<div>rank:{{p.rankAttrs.value}}</div>
28+
<div v-for="f in p.fields" :key="'div'+f.name">{{f.name}}: {{f.attrs.value}}, {{f.cron}}, {{f.selectedStr}}</div>
29+
</div>
30+
</template>
31+
</VueCronCore>
32+
</div>
33+
</template>
34+
35+
<style scoped>
36+
37+
select[multiple] {
38+
height: 200px;
39+
}
40+
41+
</style>
42+
43+
<script>
44+
import VueCronCore from '../src/core'
45+
46+
export default {
47+
components: {
48+
VueCronCore
49+
},
50+
props: {
51+
52+
},
53+
data(){
54+
return {
55+
value: '* * * * *'
56+
}
57+
},
58+
59+
computed: {
60+
61+
},
62+
63+
watch: {
64+
65+
},
66+
67+
methods: {
68+
getSelected(select){
69+
let options = select && select.options;
70+
return Array.from(options).filter((opt) => opt.selected).map((opt) => opt.value)
71+
}
72+
}
73+
}
74+
</script>

jest.config.js

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
/*
2+
* For a detailed explanation regarding each configuration property, visit:
3+
* https://jestjs.io/docs/en/configuration.html
4+
*/
5+
6+
module.exports = {
7+
8+
coverageProvider: "v8",
9+
10+
11+
"moduleFileExtensions": [
12+
"js",
13+
"ts",
14+
"json",
15+
// tell Jest to handle `*.vue` files
16+
"vue"
17+
],
18+
"transform": {
19+
'^.+\\.vue$': 'vue-jest',
20+
'^.+\\.(js|jsx)?$': 'babel-jest'
21+
},
22+
moduleNameMapper: {
23+
'^@/(.*)$': '<rootDir>/src/$1'
24+
},
25+
//transformIgnorePatterns: ['<rootDir>/node_modules/']
26+
};

package.json

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
{
2+
"name": "@vue-js-cron/core",
3+
"version": "1.0.0",
4+
"description": "A renderless Vue.js cron editor.",
5+
"main": "dist/core.umd.js",
6+
"module": "dist/core.esm.js",
7+
"unpkg": "dist/core.min.js",
8+
"browser": {
9+
"./sfc": "src/core.vue"
10+
},
11+
"scripts": {
12+
"test": "jest",
13+
"build": "yarn build:umd & yarn run build:es & yarn run build:unpkg",
14+
"build:umd": "yarn rollup --config build/rollup.config.js --format umd --file dist/core.umd.js",
15+
"build:es": "yarn rollup --config build/rollup.config.js --format es --file dist/core.esm.js",
16+
"build:unpkg": "yarn rollup --config build/rollup.config.js --format iife --file dist/core.min.js"
17+
},
18+
"keywords": [
19+
"cron",
20+
"editor",
21+
"renderless",
22+
"vue.js",
23+
"vue"
24+
],
25+
"author": "Andreas Bichinger",
26+
"license": "MIT",
27+
"devDependencies": {
28+
"@babel/core": "^7.12.13",
29+
"@babel/preset-env": "^7.12.13",
30+
"@rollup/plugin-buble": "^0.21.3",
31+
"@rollup/plugin-commonjs": "^17.1.0",
32+
"@vue/compiler-sfc": "^3.0.5",
33+
"@vue/test-utils": "^1.1.3",
34+
"babel-core": "^7.0.0-bridge.0",
35+
"babel-jest": "^26.6.3",
36+
"jest": "^26.6.3",
37+
"rollup": "^2.38.5",
38+
"rollup-jest": "^1.1.1",
39+
"rollup-plugin-vue": "^6.0.0",
40+
"vue": "^2.6.12",
41+
"vue-jest": "^3.0.7",
42+
"vue-template-compiler": "^2.6.12"
43+
}
44+
}

src/core.vue

Lines changed: 190 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,190 @@
1+
<script>
2+
import multiple from './fields/multiple'
3+
import util from './util'
4+
import en from './locale/en'
5+
6+
const {Field} = util
7+
8+
export default {
9+
name: "VueCronCore",
10+
props: {
11+
value: {
12+
type: String,
13+
required: true
14+
},
15+
fields: {
16+
type: Array,
17+
default: () => {
18+
return [
19+
{name: 'minute', items: en.minuteItems, rank: 0},
20+
{name: 'hour', items: en.hourItems, rank: 1},
21+
{name: 'day', items: en.dayItems, rank: 2},
22+
{name: 'month', items: en.monthItems, rank: 4},
23+
{name: 'dayOfWeek', items: en.dayOfWeekItems, rank: 3},
24+
]
25+
}
26+
},
27+
ranks: {
28+
type: Array,
29+
default: () => {
30+
return [
31+
{ text: 'Minute', value: -1 },
32+
{ text: 'Hour', value: 0 },
33+
{ text: 'Day', value: 1 },
34+
{ text: 'Week', value: 2 },
35+
{ text: 'Month', value: 3 },
36+
{ text: 'Year', value: 4 },
37+
]
38+
}
39+
}
40+
},
41+
data(){
42+
43+
let selected = {}
44+
for(let field of this.fields){
45+
selected[field.name] = []
46+
}
47+
48+
return {
49+
selected: selected,
50+
error: '',
51+
selectedRank: this.ranks[this.ranks.length-1].value
52+
}
53+
},
54+
55+
computed: {
56+
splitValue(){
57+
return this.value.split(' ')
58+
},
59+
fieldIndex(){
60+
return this.fields.reduce((acc, f, i) => {
61+
acc[f.name] = i
62+
return acc
63+
}, {})
64+
},
65+
computedFields(){
66+
return this.fields.map((f) => new Field(f.name, f.items, f.rank))
67+
}
68+
},
69+
70+
watch: {
71+
value: {
72+
handler: function(value){
73+
this.cronToSelected(value)
74+
},
75+
immediate: true
76+
},
77+
selected: {
78+
handler: function(selected){
79+
this.selectedToCron(selected)
80+
},
81+
deep:true
82+
},
83+
selectedRank: {
84+
handler: function(){
85+
this.selectedToCron(this.selected)
86+
},
87+
}
88+
},
89+
90+
render(){
91+
92+
if(!this.$scopedSlots.default){
93+
return
94+
}
95+
96+
let fieldProps = []
97+
for(let field of this.computedFields){
98+
let i = this.fieldIndex[field.name]
99+
let values = this.selected[field.name]
100+
101+
let attrs = {
102+
value: values,
103+
}
104+
let events = {
105+
input: ((fieldName) => (evt) => {
106+
console.log('input', fieldName, evt)
107+
this.selected[fieldName] = evt
108+
})(field.name)
109+
}
110+
111+
fieldProps.push({
112+
...field,
113+
cron: this.splitValue[i],
114+
selectedStr: multiple.arrayToStr(values, field),
115+
events,
116+
attrs
117+
})
118+
}
119+
120+
return this.$scopedSlots.default({
121+
error: this.error,
122+
fields: fieldProps,
123+
124+
rankAttrs: {
125+
value: this.selectedRank
126+
},
127+
rankEvents: {
128+
input: (evt) => {
129+
this.selectedRank = evt
130+
}
131+
},
132+
rankData: {
133+
items: this.ranks
134+
},
135+
})
136+
},
137+
138+
methods: {
139+
defaultValue(){
140+
return new Array(this.fields.length).fill('*').join(' ')
141+
},
142+
cronToSelected(value){
143+
if(!value){
144+
this.$emit('input', this.defaultValue())
145+
return
146+
}
147+
148+
if(this.splitValue.length != this.fields.length){
149+
this.error = 'invalid pattern'
150+
return
151+
}
152+
153+
for(var i = 0; i < this.splitValue.length; i++){
154+
let field = this.computedFields[i]
155+
if(field.rank > this.selectedRank){
156+
continue
157+
}
158+
159+
let array = multiple.strToArray(this.splitValue[i], field)
160+
if(array === null){
161+
this.error = 'invalid pattern'
162+
return
163+
}
164+
this.selected[field.name] = array
165+
}
166+
167+
this.error = ''
168+
},
169+
selectedToCron(selected){
170+
171+
let strings = []
172+
for(let field of this.computedFields){
173+
if(field.rank > this.selectedRank){
174+
strings.push('*')
175+
continue
176+
}
177+
let array = selected[field.name]
178+
let str = multiple.arrayToStr(array, field)
179+
if(str === null){
180+
this.error = 'invalid selection'
181+
return
182+
}
183+
strings.push(str)
184+
}
185+
this.error = ''
186+
this.$emit('input', strings.join(' '))
187+
}
188+
}
189+
}
190+
</script>

0 commit comments

Comments
 (0)