sanity-plugin-recurring-dates 1.1.1 → 1.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +6 -0
- package/dist/index.d.ts +8 -3
- package/dist/index.esm.js +361 -4868
- package/dist/index.esm.js.map +1 -1
- package/dist/index.js +365 -4872
- package/dist/index.js.map +1 -1
- package/package.json +27 -26
- package/src/components/CustomRule/CustomRule.tsx +100 -107
- package/src/components/CustomRule/Monthly.tsx +1 -1
- package/src/components/CustomRule/Weekly.tsx +1 -1
- package/src/components/DateInputs/CommonDateTimeInput.tsx +5 -4
- package/src/components/DateInputs/DateInput.tsx +1 -1
- package/src/components/DateInputs/DateTimeInput.tsx +1 -1
- package/src/components/DateInputs/base/DatePicker.tsx +4 -2
- package/src/components/DateInputs/base/DateTimeInput.tsx +8 -4
- package/src/components/DateInputs/base/LazyTextInput.tsx +13 -14
- package/src/components/DateInputs/base/calendar/Calendar.tsx +18 -16
- package/src/components/DateInputs/base/calendar/CalendarDay.tsx +1 -1
- package/src/components/DateInputs/base/calendar/CalendarMonth.tsx +3 -2
- package/src/components/DateInputs/base/calendar/YearInput.tsx +4 -3
- package/src/components/RecurringDate.tsx +21 -4
- package/src/components/RecurringDatesPreview.tsx +55 -0
- package/src/components/RemoveEndDate.tsx +3 -2
- package/src/schema/recurringDates.tsx +20 -5
- package/src/types.ts +8 -4
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import {Box, Grid, Text} from '@sanity/ui'
|
|
2
2
|
import {isSameDay, isSameMonth} from 'date-fns'
|
|
3
3
|
import React from 'react'
|
|
4
|
+
|
|
4
5
|
import {CalendarDay} from './CalendarDay'
|
|
5
6
|
import {WEEK_DAY_NAMES} from './constants'
|
|
6
7
|
import {getWeeksOfMonth} from './utils'
|
|
@@ -13,7 +14,7 @@ interface CalendarMonthProps {
|
|
|
13
14
|
hidden?: boolean
|
|
14
15
|
}
|
|
15
16
|
|
|
16
|
-
export function CalendarMonth(props: CalendarMonthProps) {
|
|
17
|
+
export function CalendarMonth(props: CalendarMonthProps): React.JSX.Element {
|
|
17
18
|
return (
|
|
18
19
|
<Box aria-hidden={props.hidden || false} data-ui="CalendarMonth">
|
|
19
20
|
<Grid gap={1} style={{gridTemplateColumns: 'repeat(7, minmax(44px, 46px))'}}>
|
|
@@ -44,7 +45,7 @@ export function CalendarMonth(props: CalendarMonthProps) {
|
|
|
44
45
|
selected={selected}
|
|
45
46
|
/>
|
|
46
47
|
)
|
|
47
|
-
})
|
|
48
|
+
}),
|
|
48
49
|
)}
|
|
49
50
|
</Grid>
|
|
50
51
|
</Box>
|
|
@@ -1,5 +1,6 @@
|
|
|
1
|
-
import React from 'react'
|
|
2
1
|
import {TextInput} from '@sanity/ui'
|
|
2
|
+
import React from 'react'
|
|
3
|
+
|
|
3
4
|
import {LazyTextInput} from '../LazyTextInput'
|
|
4
5
|
|
|
5
6
|
type Props = Omit<React.ComponentProps<typeof TextInput>, 'onChange' | 'value'> & {
|
|
@@ -7,7 +8,7 @@ type Props = Omit<React.ComponentProps<typeof TextInput>, 'onChange' | 'value'>
|
|
|
7
8
|
onChange: (year: number) => void
|
|
8
9
|
}
|
|
9
10
|
|
|
10
|
-
export const YearInput = ({onChange, ...props}: Props) => {
|
|
11
|
+
export const YearInput = ({onChange, ...props}: Props): React.JSX.Element => {
|
|
11
12
|
const handleChange = React.useCallback(
|
|
12
13
|
(event: React.FocusEvent<HTMLInputElement> | React.ChangeEvent<HTMLInputElement>) => {
|
|
13
14
|
const numericValue = parseInt(event.currentTarget.value, 10)
|
|
@@ -15,7 +16,7 @@ export const YearInput = ({onChange, ...props}: Props) => {
|
|
|
15
16
|
onChange(numericValue)
|
|
16
17
|
}
|
|
17
18
|
},
|
|
18
|
-
[onChange]
|
|
19
|
+
[onChange],
|
|
19
20
|
)
|
|
20
21
|
|
|
21
22
|
return <LazyTextInput {...props} onChange={handleChange} inputMode="numeric" />
|
|
@@ -2,7 +2,13 @@ import {Box, Flex, Grid, Select, Stack, Text} from '@sanity/ui'
|
|
|
2
2
|
import {upperFirst} from 'lodash'
|
|
3
3
|
import React, {useCallback, useState} from 'react'
|
|
4
4
|
import {rrulestr} from 'rrule'
|
|
5
|
-
import {
|
|
5
|
+
import {
|
|
6
|
+
ObjectInputMember,
|
|
7
|
+
type ObjectInputProps,
|
|
8
|
+
type ObjectSchemaType,
|
|
9
|
+
type Rule,
|
|
10
|
+
set,
|
|
11
|
+
} from 'sanity'
|
|
6
12
|
import {Feedback} from 'sanity-plugin-utils'
|
|
7
13
|
|
|
8
14
|
import type {PluginConfig, WithRequiredProperty} from '../types'
|
|
@@ -18,10 +24,10 @@ type RecurringDateObjectSchemaType = Omit<ObjectSchemaType, 'options'> & {
|
|
|
18
24
|
options?: PluginConfig
|
|
19
25
|
}
|
|
20
26
|
|
|
21
|
-
export function RecurringDates(props: RecurringDatesProps) {
|
|
27
|
+
export function RecurringDates(props: RecurringDatesProps): React.JSX.Element {
|
|
22
28
|
const {onChange, members, value: currentValue, schemaType, pluginConfig} = props
|
|
23
29
|
const {options, title}: RecurringDateObjectSchemaType = schemaType
|
|
24
|
-
const {defaultRecurrences, hideEndDate, hideCustom, dateTimeOptions, dateOnly} = {
|
|
30
|
+
const {defaultRecurrences, hideEndDate, hideCustom, dateTimeOptions, dateOnly, validation} = {
|
|
25
31
|
...pluginConfig,
|
|
26
32
|
...options,
|
|
27
33
|
}
|
|
@@ -104,8 +110,19 @@ export function RecurringDates(props: RecurringDatesProps) {
|
|
|
104
110
|
}
|
|
105
111
|
}
|
|
106
112
|
|
|
113
|
+
// Add custom validation to the start and end date fields
|
|
114
|
+
if (validation?.startDate && startDateMember?.kind == 'field') {
|
|
115
|
+
startDateMember.field.schemaType.validation = (CustomValidation) =>
|
|
116
|
+
validation?.startDate?.(CustomValidation) as Rule
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
if (validation?.endDate && endDateMember?.kind == 'field') {
|
|
120
|
+
endDateMember.field.schemaType.validation = (CustomValidation) =>
|
|
121
|
+
validation?.endDate?.(CustomValidation) as Rule
|
|
122
|
+
}
|
|
123
|
+
|
|
107
124
|
// Do we have an end date set for this field?
|
|
108
|
-
const hasEndDate = currentValue
|
|
125
|
+
const hasEndDate = currentValue?.endDate
|
|
109
126
|
|
|
110
127
|
return (
|
|
111
128
|
<Stack space={3}>
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import {DEFAULT_DATE_FORMAT, DEFAULT_TIME_FORMAT, format} from '@sanity/util/legacyDateFormat'
|
|
2
|
+
import {upperFirst} from 'lodash'
|
|
3
|
+
import React from 'react'
|
|
4
|
+
import {rrulestr} from 'rrule'
|
|
5
|
+
import type {ObjectSchemaType, PreviewProps} from 'sanity'
|
|
6
|
+
|
|
7
|
+
import type {PluginConfig, RecurringDate, WithRequiredProperty} from '../types'
|
|
8
|
+
|
|
9
|
+
type CastPreviewProps = PreviewProps &
|
|
10
|
+
RecurringDate & {
|
|
11
|
+
pluginConfig: WithRequiredProperty<PluginConfig, 'defaultRecurrences'>
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
type RecurringDateObjectSchemaType = Omit<ObjectSchemaType, 'options'> & {
|
|
15
|
+
options?: PluginConfig
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export function RecurringDatesPreview(props: CastPreviewProps): React.JSX.Element {
|
|
19
|
+
const {startDate, endDate, rrule, schemaType, pluginConfig} = props
|
|
20
|
+
const options: RecurringDateObjectSchemaType = schemaType?.options
|
|
21
|
+
|
|
22
|
+
const {dateTimeOptions, dateOnly} = {
|
|
23
|
+
...pluginConfig,
|
|
24
|
+
...options,
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
const rule = rrule && rrulestr(rrule)
|
|
28
|
+
|
|
29
|
+
const dateFormat = dateTimeOptions?.dateFormat || DEFAULT_DATE_FORMAT
|
|
30
|
+
const timeFormat = dateTimeOptions?.timeFormat || DEFAULT_TIME_FORMAT
|
|
31
|
+
|
|
32
|
+
const start = startDate ? new Date(startDate) : undefined
|
|
33
|
+
const end = endDate ? new Date(endDate) : undefined
|
|
34
|
+
const sameDay = start && end && start.toDateString() === end.toDateString()
|
|
35
|
+
|
|
36
|
+
let title = 'No start date'
|
|
37
|
+
if (dateOnly) {
|
|
38
|
+
title = start ? format(start, dateFormat) : 'No start date'
|
|
39
|
+
if (end && !sameDay) {
|
|
40
|
+
title += ` - ${format(end, dateFormat)}`
|
|
41
|
+
}
|
|
42
|
+
} else {
|
|
43
|
+
title = start ? format(start, `${dateFormat} ${timeFormat}`) : 'No start date'
|
|
44
|
+
if (end) {
|
|
45
|
+
title += ` - ${format(end, sameDay ? timeFormat : `${dateFormat} ${timeFormat}`)}`
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
const previewProps = {
|
|
50
|
+
title,
|
|
51
|
+
subtitle: rule && upperFirst(rule.toText()),
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
return props.renderDefault({...previewProps, ...props})
|
|
55
|
+
}
|
|
@@ -2,6 +2,7 @@ import {TrashIcon, WarningOutlineIcon} from '@sanity/icons'
|
|
|
2
2
|
import {Box, Button, Card, Flex, Stack, Text} from '@sanity/ui'
|
|
3
3
|
import {upperFirst} from 'lodash'
|
|
4
4
|
import {useCallback} from 'react'
|
|
5
|
+
import React from 'react'
|
|
5
6
|
import {type ObjectInputProps, unset} from 'sanity'
|
|
6
7
|
|
|
7
8
|
export function RemoveEndDate({
|
|
@@ -10,7 +11,7 @@ export function RemoveEndDate({
|
|
|
10
11
|
}: {
|
|
11
12
|
title?: string
|
|
12
13
|
onChange: ObjectInputProps['onChange']
|
|
13
|
-
}) {
|
|
14
|
+
}): React.JSX.Element {
|
|
14
15
|
// Update the RRULE field when the select changes
|
|
15
16
|
const handleUnsetClick = useCallback(() => {
|
|
16
17
|
onChange(unset(['endDate']))
|
|
@@ -39,7 +40,7 @@ export function RemoveEndDate({
|
|
|
39
40
|
tone="critical"
|
|
40
41
|
text={<>Remove end date</>}
|
|
41
42
|
onClick={handleUnsetClick}
|
|
42
|
-
width="
|
|
43
|
+
width="fill"
|
|
43
44
|
/>
|
|
44
45
|
</Stack>
|
|
45
46
|
</Flex>
|
|
@@ -1,29 +1,36 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import {CalendarIcon} from '@sanity/icons'
|
|
2
|
+
import {defineField, SchemaTypeDefinition} from 'sanity'
|
|
2
3
|
|
|
3
4
|
import {RecurringDates} from '../components/RecurringDate'
|
|
5
|
+
import {RecurringDatesPreview} from '../components/RecurringDatesPreview'
|
|
4
6
|
import {PluginConfig, WithRequiredProperty} from '../types'
|
|
5
7
|
|
|
6
|
-
export default (
|
|
7
|
-
|
|
8
|
+
export default (
|
|
9
|
+
config: WithRequiredProperty<PluginConfig, 'defaultRecurrences'>,
|
|
10
|
+
): SchemaTypeDefinition => {
|
|
11
|
+
const {dateTimeOptions, dateOnly, validation} = config
|
|
8
12
|
|
|
9
13
|
return defineField({
|
|
10
14
|
name: 'recurringDates',
|
|
11
15
|
title: 'Dates',
|
|
12
16
|
type: 'object',
|
|
17
|
+
icon: CalendarIcon,
|
|
13
18
|
fields: [
|
|
14
19
|
defineField({
|
|
15
20
|
title: 'Start Date',
|
|
16
21
|
name: 'startDate',
|
|
17
22
|
type: dateOnly ? 'date' : 'datetime',
|
|
18
23
|
options: dateTimeOptions,
|
|
19
|
-
validation: (Rule) =>
|
|
24
|
+
validation: (Rule) =>
|
|
25
|
+
validation?.startDate ? validation.startDate(Rule) : Rule.required(),
|
|
20
26
|
}),
|
|
21
27
|
defineField({
|
|
22
28
|
title: 'End Date',
|
|
23
29
|
name: 'endDate',
|
|
24
30
|
type: dateOnly ? 'date' : 'datetime',
|
|
25
31
|
options: dateTimeOptions,
|
|
26
|
-
validation: (Rule) =>
|
|
32
|
+
validation: (Rule) =>
|
|
33
|
+
validation?.endDate ? validation.endDate(Rule) : Rule.min(Rule.valueOfField('startDate')),
|
|
27
34
|
}),
|
|
28
35
|
defineField({
|
|
29
36
|
title: 'Recurring event',
|
|
@@ -39,6 +46,14 @@ export default (config: WithRequiredProperty<PluginConfig, 'defaultRecurrences'>
|
|
|
39
46
|
],
|
|
40
47
|
components: {
|
|
41
48
|
input: (props) => RecurringDates({...props, pluginConfig: config}),
|
|
49
|
+
preview: (props) => RecurringDatesPreview({...props, pluginConfig: config}),
|
|
50
|
+
},
|
|
51
|
+
preview: {
|
|
52
|
+
select: {
|
|
53
|
+
startDate: 'startDate',
|
|
54
|
+
endDate: 'endDate',
|
|
55
|
+
rrule: 'rrule',
|
|
56
|
+
},
|
|
42
57
|
},
|
|
43
58
|
})
|
|
44
59
|
}
|
package/src/types.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type {DatetimeOptions, ObjectDefinition} from 'sanity'
|
|
1
|
+
import type {DateRule, DatetimeOptions, ObjectDefinition} from 'sanity'
|
|
2
2
|
|
|
3
3
|
export interface PluginConfig {
|
|
4
4
|
defaultRecurrences?: string[]
|
|
@@ -6,6 +6,10 @@ export interface PluginConfig {
|
|
|
6
6
|
hideCustom?: boolean
|
|
7
7
|
dateTimeOptions?: DatetimeOptions
|
|
8
8
|
dateOnly?: boolean
|
|
9
|
+
validation?: {
|
|
10
|
+
startDate?: (Rule: DateRule) => DateRule
|
|
11
|
+
endDate?: (Rule: DateRule) => DateRule
|
|
12
|
+
}
|
|
9
13
|
}
|
|
10
14
|
|
|
11
15
|
export type WithRequiredProperty<Type, Key extends keyof Type> = Type & {
|
|
@@ -28,7 +32,7 @@ declare module 'sanity' {
|
|
|
28
32
|
}
|
|
29
33
|
|
|
30
34
|
export interface RecurringDate {
|
|
31
|
-
rrule
|
|
32
|
-
startDate
|
|
33
|
-
endDate
|
|
35
|
+
rrule?: string
|
|
36
|
+
startDate?: string
|
|
37
|
+
endDate?: string
|
|
34
38
|
}
|