Skip to content

Commit

Permalink
feat: add format prop #25
Browse files Browse the repository at this point in the history
  • Loading branch information
abichinger committed Nov 4, 2023
1 parent 18fc5d0 commit 1b42d11
Show file tree
Hide file tree
Showing 13 changed files with 224 additions and 76 deletions.
1 change: 1 addition & 0 deletions ant/src/CronEditor.vue
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,7 @@ export default {
type: Object,
default: () => {
return {
second: 5,
minute: 5,
hour: 4,
day: 4
Expand Down
69 changes: 69 additions & 0 deletions core/src/components/__tests__/cron-core.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
import { describe, expect, it } from 'vitest';

import { useCron, type CronFormat } from '../cron-core';

type UseCronReturn = ReturnType<typeof useCron>

function cronToString({selected: { value: selected }, period, }:UseCronReturn): string {
const fields = selected.map((seg) => {
const prefix = seg.prefix.value ? seg.prefix.value+' ' : '';
const suffix = seg.suffix.value ? ' '+seg.suffix.value : '';
return prefix+seg.text.value+suffix
}).join(' ')

const prefix = period.prefix.value ? period.prefix.value+' ' : '';
const suffix = period.suffix.value ? ' '+period.suffix.value : '';
return `${prefix}${period.selected.value.text}${suffix} ${fields}`
}

describe('useCron', () => {
it('renders properly', () => {
const tests: {
format: CronFormat,
value: string,
expected: string,
}[] = [
{
format: 'crontab',
value: '* * * * *',
expected: `Every Year in every month on every day and every day of the week at every hour : every minute`
},
{
format: 'quartz',
value: '* * * * * *',
expected: `Every Year in every month on every day and every day of the week at every hour : every minute : every second`
}
]

for(const t of tests) {
const cron = useCron({format: t.format, initialValue: t.value})
expect(cronToString(cron)).toEqual(t.expected)
}
})

it('format option', () => {
const formats: {
value: CronFormat
expectedFields: number
expectedPeriods: number
}[] = [
{
value: 'crontab',
expectedFields: 5,
expectedPeriods: 6
},
{
value: 'quartz',
expectedFields: 6,
expectedPeriods: 7
}
]

for (const format of formats) {
const cron = useCron({ format: format.value })

expect(cron.segments.length).toEqual(format.expectedFields)
expect(cron.period.items.length).toEqual(format.expectedPeriods)
}
})
})
18 changes: 7 additions & 11 deletions core/src/components/__tests__/select.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -51,27 +51,23 @@ describe('select', () => {
}
})

const withTick = async (fn : () => void) => {
const withTick = async (fn: () => void) => {
fn()
await nextTick()
}

const { select, setItems, selected } = useSelect<any, number>({items, multiple: true})
const { select, setItems, selected } = useSelect<any, number>({ items, multiple: true })

let counter = 0;
const expected = [
[0],
[],
[1,2]
]
let counter = 0
const expected = [[0], [], [1, 2]]
watch(selected, (value) => {
expect((value as number[]).sort()).toEqual(expected[counter].sort());
counter++;
expect((value as number[]).sort()).toEqual(expected[counter].sort())
counter++
})

await withTick(() => select(items[0]))
await withTick(() => select(items[0]))
await withTick(() => select({value: 100, text: 'Item-100'})) // ignored
await withTick(() => select({ value: 100, text: 'Item-100' })) // ignored
await withTick(() => setItems([items[1], items[2]]))
await withTick(() => setItems([items[1], items[2]])) // ignored
})
Expand Down
140 changes: 78 additions & 62 deletions core/src/components/cron-core.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,15 @@ import { FieldWrapper, TextPosition, type Field, type Period } from '../types'
import { defaultItems } from '../util'
import { useCronSegment } from './cron-segment'

export type CronFormat = 'crontab' | 'quartz'

interface CronOptions {
initialValue?: string
locale?: string
fields?: Field[]
periods?: Period[]
customLocale?: Localization
format?: CronFormat
}

function createCron(len: number, seg: string = '*') {
Expand All @@ -24,14 +27,17 @@ function isDefined<T>(obj: T | undefined): obj is T {
class DefaultCronOptions {
locale = 'en'

format: CronFormat = 'crontab'

initialValue(len: number, seg: string = '*') {
return createCron(len, seg)
}

fields(locale: string) {
fields(format: CronFormat, locale: string) {
const items = defaultItems(locale)

return [
...(format == 'quartz' ? [{ id: 'second', items: items.secondItems }] : []),
{ id: 'minute', items: items.minuteItems },
{ id: 'hour', items: items.hourItems },
{ id: 'day', items: items.dayItems },
Expand All @@ -40,27 +46,34 @@ class DefaultCronOptions {
]
}

periods() {
periods(format: CronFormat) {
const isQuartz = format == 'quartz';
const second = isQuartz ? [{ id: 'q-second', value: [] }] : []
const secondField = isQuartz ? ['second'] : []
const prefix = isQuartz ? 'q-' : '';

return [
{ id: 'minute', value: [] },
{ id: 'hour', value: ['minute'] },
{ id: 'day', value: ['hour', 'minute'] },
{ id: 'week', value: ['dayOfWeek', 'hour', 'minute'] },
{ id: 'month', value: ['day', 'dayOfWeek', 'hour', 'minute'] },
{ id: 'year', value: ['month', 'day', 'dayOfWeek', 'hour', 'minute'] }
...second,
{ id: prefix + 'minute', value: [...secondField] },
{ id: prefix + 'hour', value: ['minute', ...secondField] },
{ id: 'day', value: ['hour', 'minute', ...secondField] },
{ id: 'week', value: ['dayOfWeek', 'hour', 'minute', ...secondField] },
{ id: 'month', value: ['day', 'dayOfWeek', 'hour', 'minute', ...secondField] },
{ id: 'year', value: ['month', 'day', 'dayOfWeek', 'hour', 'minute', ...secondField] }
]
}
}

export function useCron(options: CronOptions) {
const cronDefaults = new DefaultCronOptions()

const locale = options.locale ?? 'en'
const { customLocale, fields = cronDefaults.fields(locale) } = options
const locale = options.locale ?? cronDefaults.locale
const format = options.format ?? cronDefaults.format
const { customLocale, fields = cronDefaults.fields(format, locale) } = options
const initialValue = options.initialValue ?? cronDefaults.initialValue(fields.length)

const l10n = getLocale(locale, customLocale)
const periods = (options.periods ?? cronDefaults.periods()).map((p) => {
const periods = (options.periods ?? cronDefaults.periods(format)).map((p) => {
return {
...p,
text: l10n.getLocaleStr(p.id, TextPosition.Text)
Expand Down Expand Up @@ -161,6 +174,9 @@ export const cronProps = {
modelValue: {
type: String
},
format: {
type: String as PropType<CronFormat>
},
locale: {
type: String
},
Expand All @@ -176,63 +192,63 @@ export const cronProps = {
}

export const CronCore = defineComponent({
name: 'VueCronCore',
props: cronProps,
emits: ['update:model-value', 'error'],
setup(props, ctx) {
const { cron, error, selected, period } = useCron(props)

watch(cron, (value) => {
ctx.emit('update:model-value', value)
})
name: 'VueCronCore',
props: cronProps,
emits: ['update:model-value', 'error'],
setup(props, ctx) {
const { cron, error, selected, period } = useCron(props)

watch(cron, (value) => {
ctx.emit('update:model-value', value)
})

watch(error, (value) => {
ctx.emit('error', value)
})
watch(error, (value) => {
ctx.emit('error', value)
})

watch(
() => props.modelValue,
(value) => {
if (value) {
cron.value = value
}
watch(
() => props.modelValue,
(value) => {
if (value) {
cron.value = value
}
)

return () => {
const slotProps = {
error: error,
fields: selected.value.map((s) => {
return {
id: s.id,
items: s.items,
cron: s.cron.value,
selectedStr: s.text.value,
events: {
'update:model-value': s.select
},
attrs: {
modelValue: s.selected.value
},
prefix: s.prefix.value,
suffix: s.suffix.value
}
}),

period: {
attrs: {
modelValue: period.selected.value.id
},
}
)

return () => {
const slotProps = {
error: error,
fields: selected.value.map((s) => {
return {
id: s.id,
items: s.items,
cron: s.cron.value,
selectedStr: s.text.value,
events: {
'update:model-value': period.select
'update:model-value': s.select
},
attrs: {
modelValue: s.selected.value
},
items: period.items,
prefix: period.prefix.value,
suffix: period.suffix.value
prefix: s.prefix.value,
suffix: s.suffix.value
}
}),

period: {
attrs: {
modelValue: period.selected.value.id
},
events: {
'update:model-value': period.select
},
items: period.items,
prefix: period.prefix.value,
suffix: period.suffix.value
}

return ctx.slots.default?.(slotProps)
}

return ctx.slots.default?.(slotProps)
}
})
}
})
2 changes: 1 addition & 1 deletion core/src/components/select.ts
Original file line number Diff line number Diff line change
Expand Up @@ -162,7 +162,7 @@ export function useSelect<T, V>(options: SelectOptions<T, V>) {
selectedStr,
itemRows: splitArray(items, cols),
setItems,
setValues,
setValues
}
}

Expand Down
36 changes: 35 additions & 1 deletion core/src/locale/de.ts
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,14 @@ const locale: Localization = {
prefix: '',
text: 'alle {{every.value}} Minuten'
}
},
second: {
'*': { prefix: ':' },
empty: { text: 'jede Sekunde' },
everyX: {
prefix: '',
text: 'alle {{every.value}} Sekunden'
}
}
},
minute: {
Expand Down Expand Up @@ -86,7 +94,33 @@ const locale: Localization = {
year: {
prefix: 'Jedes',
text: 'Jahr'
}
},

//quartz format
'q-second': {
text: 'Sekunde'
},
'q-minute': {
text: 'Minute',
second: {
'*': {
prefix: 'und',
},
}
},
'q-hour': {
text: 'Stunde',
minute: {
'*': {
prefix: 'und'
}
},
second: {
'*': {
prefix: 'und'
}
}
},
}

export default locale
Loading

0 comments on commit 1b42d11

Please sign in to comment.