Skip to content

Commit 4aae066

Browse files
committed
feat(ddatepicker): add view part
1 parent 5cf1898 commit 4aae066

File tree

10 files changed

+60
-82
lines changed

10 files changed

+60
-82
lines changed

.changeset/puny-lies-take.md

+7
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
---
2+
"@zag-js/date-picker": patch
3+
"@zag-js/date-utils": patch
4+
---
5+
6+
- Add support for `api.getViewProps`.
7+
- Add `visibleRangeText` property to `api.offset()` return value.
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,18 @@
11
import { createAnatomy } from "@zag-js/anatomy"
22

33
export const anatomy = createAnatomy("date-picker").parts(
4-
"root",
5-
"label",
64
"clearTrigger",
75
"content",
86
"control",
97
"input",
8+
"label",
109
"monthSelect",
1110
"nextTrigger",
1211
"positioner",
12+
"presetTrigger",
1313
"prevTrigger",
1414
"rangeText",
15+
"root",
1516
"table",
1617
"tableBody",
1718
"tableCell",
@@ -20,10 +21,10 @@ export const anatomy = createAnatomy("date-picker").parts(
2021
"tableHeader",
2122
"tableRow",
2223
"trigger",
23-
"viewTrigger",
24+
"view",
2425
"viewControl",
26+
"viewTrigger",
2527
"yearSelect",
26-
"presetTrigger",
2728
)
2829

2930
export const parts = anatomy.build()

packages/machines/date-picker/src/date-picker.connect.ts

+22-10
Original file line numberDiff line numberDiff line change
@@ -12,10 +12,8 @@ import {
1212
getUnitDuration,
1313
getWeekDays,
1414
getYearsRange,
15-
isDateDisabled,
1615
isDateEqual,
17-
isDateInvalid,
18-
isDateOutsideVisibleRange,
16+
isDateOutsideRange,
1917
isDateUnavailable,
2018
} from "@zag-js/date-utils"
2119
import { ariaAttr, dataAttr, getEventKey, getNativeEvent, isComposingEvent } from "@zag-js/dom-query"
@@ -81,6 +79,7 @@ export function connect<T extends PropTypes>(
8179
})
8280

8381
const separator = getLocaleSeparator(locale)
82+
const translations = { ...defaultTranslations, ...prop("translations") }
8483

8584
function getMonthWeeks(from = startValue) {
8685
const numOfWeeks = prop("fixedWeeks") ? 6 : undefined
@@ -136,7 +135,7 @@ export function connect<T extends PropTypes>(
136135
const formatter = getMonthFormatter(locale, timeZone)
137136
const cellState = {
138137
focused: focusedValue.month === props.value,
139-
selectable: !isDateInvalid(normalized, min, max),
138+
selectable: !isDateOutsideRange(normalized, min, max),
140139
selected: !!selectedValue.find((date) => date.month === value && date.year === focusedValue.year),
141140
valueText: formatter.format(normalized.toDate(timeZone)),
142141
get disabled() {
@@ -146,8 +145,6 @@ export function connect<T extends PropTypes>(
146145
return cellState
147146
}
148147

149-
const translations = prop("translations") || defaultTranslations
150-
151148
function getDayTableCellState(props: DayTableCellProps): DayTableCellState {
152149
const { value, disabled, visibleRange = computed("visibleRange") } = props
153150

@@ -157,11 +154,11 @@ export function connect<T extends PropTypes>(
157154
const end = visibleRange.start.add(unitDuration).subtract({ days: 1 })
158155

159156
const cellState = {
160-
invalid: isDateInvalid(value, min, max),
161-
disabled: disabled || isDateDisabled(value, visibleRange.start, end, min, max),
157+
invalid: isDateOutsideRange(value, min, max),
158+
disabled: disabled || isDateOutsideRange(value, visibleRange.start, end) || isDateOutsideRange(value, min, max),
162159
selected: selectedValue.some((date) => isDateEqual(value, date)),
163160
unavailable: isDateUnavailable(value, isDateUnavailableFn, locale, min, max) && !disabled,
164-
outsideRange: isDateOutsideVisibleRange(value, visibleRange.start, end),
161+
outsideRange: isDateOutsideRange(value, visibleRange.start, end),
165162
inRange:
166163
isRangePicker && (isDateWithinRange(value, selectedValue) || isDateWithinRange(value, hoveredRangeValue)),
167164
firstInRange: isRangePicker && isDateEqual(value, selectedValue[0]),
@@ -199,9 +196,15 @@ export function connect<T extends PropTypes>(
199196
},
200197
getOffset(duration) {
201198
const from = startValue.add(duration)
199+
const end = endValue.add(duration)
200+
const formatter = getMonthFormatter(locale, timeZone)
202201
return {
203-
visibleRange: { start: from, end: endValue.add(duration) },
202+
visibleRange: { start: from, end },
204203
weeks: getMonthWeeks(from),
204+
visibleRangeText: {
205+
start: formatter.format(from.toDate(timeZone)),
206+
end: formatter.format(end.toDate(timeZone)),
207+
},
205208
}
206209
},
207210
getMonthWeeks,
@@ -641,6 +644,15 @@ export function connect<T extends PropTypes>(
641644
})
642645
},
643646

647+
getViewProps(props = {}) {
648+
const { view = "day" } = props
649+
return normalize.element({
650+
...parts.view.attrs,
651+
"data-view": view,
652+
hidden: context.get("view") !== view,
653+
})
654+
},
655+
644656
getViewTriggerProps(props = {}) {
645657
const { view = "day" } = props
646658
return normalize.button({

packages/machines/date-picker/src/date-picker.machine.ts

+6-6
Original file line numberDiff line numberDiff line change
@@ -13,9 +13,9 @@ import {
1313
getPreviousSection,
1414
getTodayDate,
1515
isDateEqual,
16-
isDateOutsideVisibleRange,
17-
isNextVisibleRangeInvalid,
18-
isPreviousVisibleRangeInvalid,
16+
isDateOutsideRange,
17+
isNextRangeInvalid,
18+
isPreviousRangeInvalid,
1919
parseDateString,
2020
type AdjustDateReturn,
2121
} from "@zag-js/date-utils"
@@ -189,9 +189,9 @@ export const machine = createMachine<DatePickerSchema>({
189189
return { start, end, formatted }
190190
},
191191
isPrevVisibleRangeValid: ({ context, prop }) =>
192-
!isPreviousVisibleRangeInvalid(context.get("startValue"), prop("min"), prop("max")),
192+
!isPreviousRangeInvalid(context.get("startValue"), prop("min"), prop("max")),
193193
isNextVisibleRangeValid: ({ prop, computed }) =>
194-
!isNextVisibleRangeInvalid(computed("endValue"), prop("min"), prop("max")),
194+
!isNextRangeInvalid(computed("endValue"), prop("min"), prop("max")),
195195
valueAsString({ context, prop }) {
196196
const value = context.get("value")
197197
return value.map((date) => prop("format")(date, { locale: prop("locale"), timeZone: prop("timeZone") }))
@@ -1085,7 +1085,7 @@ export const machine = createMachine<DatePickerSchema>({
10851085
setStartValue({ context, computed, prop }) {
10861086
const focusedValue = context.get("focusedValue")
10871087

1088-
const outside = isDateOutsideVisibleRange(focusedValue, context.get("startValue"), computed("endValue"))
1088+
const outside = isDateOutsideRange(focusedValue, context.get("startValue"), computed("endValue"))
10891089
if (!outside) return
10901090

10911091
const startValue = alignDate(focusedValue, "start", { months: prop("numOfMonths") }, prop("locale"))

packages/machines/date-picker/src/date-picker.types.ts

+3
Original file line numberDiff line numberDiff line change
@@ -372,6 +372,7 @@ export type VisibleRange = Range<DateValue>
372372
export interface DateValueOffset {
373373
visibleRange: VisibleRange
374374
weeks: DateValue[][]
375+
visibleRangeText: { start: string; end: string }
375376
}
376377

377378
export interface TableCellProps {
@@ -656,6 +657,8 @@ export interface DatePickerApi<T extends PropTypes = PropTypes> {
656657
getClearTriggerProps(): T["button"]
657658
getTriggerProps(): T["button"]
658659
getPresetTriggerProps(props: PresetTriggerProps): T["button"]
660+
661+
getViewProps(props?: ViewProps): T["element"]
659662
getViewTriggerProps(props?: ViewProps): T["button"]
660663
getViewControlProps(props?: ViewProps): T["element"]
661664
getInputProps(props?: InputProps): T["input"]

packages/utilities/date-utils/src/assertion.ts

+9-35
Original file line numberDiff line numberDiff line change
@@ -5,54 +5,28 @@ export function isDateEqual(dateA: DateValue, dateB?: DateValue | null) {
55
return dateB != null && isSameDay(dateA, dateB)
66
}
77

8-
export function isDateInvalid(date: DateValue, minValue?: DateValue | null, maxValue?: DateValue | null) {
9-
return (minValue != null && date.compare(minValue) < 0) || (maxValue != null && date.compare(maxValue) > 0)
10-
}
11-
12-
export function isDateDisabled(
13-
date: DateValue,
14-
startDate: DateValue,
15-
endDate: DateValue,
16-
minValue?: DateValue | null,
17-
maxValue?: DateValue | null,
18-
) {
19-
return isDateOutsideVisibleRange(date, startDate, endDate) || isDateInvalid(date, minValue, maxValue)
20-
}
21-
228
export function isDateUnavailable(
239
date: DateValue | null,
2410
isUnavailable: DateAvailableFn | undefined,
2511
locale: string,
2612
minValue?: DateValue | null,
2713
maxValue?: DateValue | null,
2814
) {
29-
if (!date) {
30-
return false
31-
}
32-
if (isUnavailable?.(date, locale)) {
33-
return true
34-
}
35-
return isDateInvalid(date, minValue, maxValue)
15+
if (!date) return false
16+
if (isUnavailable?.(date, locale)) return true
17+
return isDateOutsideRange(date, minValue, maxValue)
3618
}
3719

38-
export function isDateOutsideVisibleRange(date: DateValue, startDate: DateValue, endDate: DateValue) {
39-
return date.compare(startDate) < 0 || date.compare(endDate) > 0
20+
export function isDateOutsideRange(date: DateValue, startDate?: DateValue | null, endDate?: DateValue | null) {
21+
return (startDate != null && date.compare(startDate) < 0) || (endDate != null && date.compare(endDate) > 0)
4022
}
4123

42-
export function isPreviousVisibleRangeInvalid(
43-
startDate: DateValue,
44-
minValue?: DateValue | null,
45-
maxValue?: DateValue | null,
46-
) {
24+
export function isPreviousRangeInvalid(startDate: DateValue, minValue?: DateValue | null, maxValue?: DateValue | null) {
4725
const prevDate = startDate.subtract({ days: 1 })
48-
return isSameDay(prevDate, startDate) || isDateInvalid(prevDate, minValue, maxValue)
26+
return isSameDay(prevDate, startDate) || isDateOutsideRange(prevDate, minValue, maxValue)
4927
}
5028

51-
export function isNextVisibleRangeInvalid(
52-
endDate: DateValue,
53-
minValue?: DateValue | null,
54-
maxValue?: DateValue | null,
55-
) {
29+
export function isNextRangeInvalid(endDate: DateValue, minValue?: DateValue | null, maxValue?: DateValue | null) {
5630
const nextDate = endDate.add({ days: 1 })
57-
return isSameDay(nextDate, endDate) || isDateInvalid(nextDate, minValue, maxValue)
31+
return isSameDay(nextDate, endDate) || isDateOutsideRange(nextDate, minValue, maxValue)
5832
}

packages/utilities/date-utils/src/pagination.ts

+2-2
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ import {
66
startOfMonth,
77
startOfWeek,
88
} from "@internationalized/date"
9-
import { isDateInvalid } from "./assertion"
9+
import { isDateOutsideRange } from "./assertion"
1010
import { alignEnd, alignStart, constrainStart, constrainValue } from "./constrain"
1111
import { getEndDate, getUnitDuration } from "./duration"
1212

@@ -30,7 +30,7 @@ export function getAdjustedDateFn(
3030
const endDate = getEndDate(startDate, visibleDuration)
3131

3232
// If the focused date was moved to an invalid value, it can't be focused, so constrain it.
33-
if (isDateInvalid(focusedDate, minValue, maxValue)) {
33+
if (isDateOutsideRange(focusedDate, minValue, maxValue)) {
3434
return {
3535
startDate,
3636
focusedDate: constrainValue(focusedDate, minValue, maxValue),
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,6 @@
11
import { parseDate } from "@internationalized/date"
22
import { describe, expect, test } from "vitest"
3-
import { getEndDate, isDateDisabled, isDateEqual } from "../src"
4-
5-
const duration = { months: 1 }
6-
7-
const min = parseDate("2023-01-01")
8-
const max = parseDate("2023-12-31")
3+
import { isDateEqual } from "../src"
94

105
describe("Date utilities / Assertion", () => {
116
test("isEqual / truthy", () => {
@@ -24,22 +19,4 @@ describe("Date utilities / Assertion", () => {
2419
const dateA = parseDate("2024-04-15")
2520
expect(isDateEqual(dateA, null)).toBe(false)
2621
})
27-
28-
test("isDisabled / truthy", () => {
29-
const date = parseDate("2024-04-15")
30-
31-
const startDate = parseDate("2024-04-01")
32-
const endDate = getEndDate(startDate, duration)
33-
34-
expect(isDateDisabled(date, startDate, endDate, min, max)).toBe(true)
35-
})
36-
37-
test("isDisabled / falsy", () => {
38-
const date = parseDate("2023-04-15")
39-
40-
const startDate = parseDate("2023-04-01")
41-
const endDate = getEndDate(startDate, duration)
42-
43-
expect(isDateDisabled(date, startDate, endDate, min, max)).toBe(false)
44-
})
4522
})

shared/src/css/date-picker.css

+4
Original file line numberDiff line numberDiff line change
@@ -76,3 +76,7 @@
7676
align-items: center;
7777
margin-block: 10px;
7878
}
79+
80+
[data-scope="date-picker"][data-part="view"] {
81+
width: 100%;
82+
}

0 commit comments

Comments
 (0)