Let the date picker take a first day of the week and a format
An application that lets each person choose when their week starts and how dates are written needs the picker to follow that choice rather than the locale. <x-datepicker> takes week-start, 0 (Sunday) to 6 (Saturday), and format, dd, MM and yyyy each once around one delimiter (dd.MM.yyyy, dd/MM/yyyy, MM/dd/yyyy, yyyy-MM-dd). They replace what Intl derives for firstDayOfWeek() and inputFormat(), so the field, the server-rendered value, the typed-date reader (year first too), the calendar's columns and weekday header, Home and End, the dialog's text fields and ranges all follow them. Month and weekday names stay the locale's, and wire:model still stores Y-m-d. Values that are neither are ignored, and without the props nothing changes. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01RHoXZSHc8gGpZjFmA5fPc2
This commit is contained in:
co-authored by
Claude Opus 5
parent
fdb13313f7
commit
fb29169d44
+24
-10
@@ -11,7 +11,9 @@
|
||||
* Dates are ISO strings (`2026-09-13`) throughout, computed in UTC so no time zone or daylight
|
||||
* saving change can move a day; only "today" is read in the browser's own zone. Month and weekday
|
||||
* names, the week's first day and the typed format come from `Intl` for the locale the server
|
||||
* passes (the application's).
|
||||
* passes (the application's), unless the component names the first day (`weekStart`, 0 for Sunday
|
||||
* to 6) or the format (`format`, such as `dd.MM.yyyy`); everything that reads `firstDay` and
|
||||
* `format` below then follows those.
|
||||
*
|
||||
* The keyboard is WAI-ARIA's date picker dialog: arrows move a day or a week, Home and End go to
|
||||
* the start and end of the week, PageUp and PageDown a month (with Shift a year), Space selects,
|
||||
@@ -82,8 +84,12 @@ function localToday() {
|
||||
return `${pad(now.getFullYear(), 4)}-${pad(now.getMonth() + 1)}-${pad(now.getDate())}`
|
||||
}
|
||||
|
||||
/** 0 for Sunday … 6 for Saturday. */
|
||||
function firstDayOfWeek(locale) {
|
||||
/** 0 for Sunday … 6 for Saturday: `weekStart` when it is one of those, otherwise the locale's. */
|
||||
function firstDayOfWeek(locale, weekStart = null) {
|
||||
if (Number.isInteger(weekStart) && weekStart >= 0 && weekStart <= 6) {
|
||||
return weekStart
|
||||
}
|
||||
|
||||
try {
|
||||
const tag = new Intl.Locale(locale)
|
||||
const info = typeof tag.getWeekInfo === 'function' ? tag.getWeekInfo() : tag.weekInfo
|
||||
@@ -111,9 +117,16 @@ function firstDayOfWeek(locale) {
|
||||
/**
|
||||
* The typed format: the locale's short numeric date, reduced to `dd`, `MM` and `yyyy` and one
|
||||
* delimiter — androidx's `datePatternAsInputFormat`, fed from `formatToParts` because `Intl` has
|
||||
* no pattern to give. `de` gives `dd.MM.yyyy`, `en-US` `MM/dd/yyyy`, `ja` `yyyy/MM/dd`.
|
||||
* no pattern to give. `de` gives `dd.MM.yyyy`, `en-US` `MM/dd/yyyy`, `ja` `yyyy/MM/dd`. A `chosen`
|
||||
* pattern of the same shape (each unit once, one delimiter) replaces the locale's.
|
||||
*/
|
||||
function inputFormat(locale) {
|
||||
function inputFormat(locale, chosen = null) {
|
||||
const units = /^(dd|MM|yyyy)([/\-.])(dd|MM|yyyy)\2(dd|MM|yyyy)$/.exec(typeof chosen === 'string' ? chosen : '')
|
||||
|
||||
if (units && new Set([units[1], units[3], units[4]]).size === 3) {
|
||||
return formatOf(chosen)
|
||||
}
|
||||
|
||||
let pattern = ''
|
||||
|
||||
try {
|
||||
@@ -131,10 +144,11 @@ function inputFormat(locale) {
|
||||
pattern = ''
|
||||
}
|
||||
|
||||
if (!/^(?=.*dd)(?=.*MM)(?=.*yyyy)[dMy]+[/\-.][dMy]+[/\-.][dMy]+$/.test(pattern)) {
|
||||
pattern = 'yyyy-MM-dd'
|
||||
}
|
||||
return formatOf(/^(?=.*dd)(?=.*MM)(?=.*yyyy)[dMy]+[/\-.][dMy]+[/\-.][dMy]+$/.test(pattern) ? pattern : 'yyyy-MM-dd')
|
||||
}
|
||||
|
||||
/** A pattern, the placeholder it shows (`DD.MM.YYYY`) and the order its units are typed in (`dMy`). */
|
||||
function formatOf(pattern) {
|
||||
return {
|
||||
pattern,
|
||||
placeholder: pattern.toUpperCase(),
|
||||
@@ -169,8 +183,8 @@ document.addEventListener('alpine:init', () => {
|
||||
const locale = config.locale || document.documentElement.lang || 'en'
|
||||
const format = (options) => new Intl.DateTimeFormat(locale, { timeZone: 'UTC', ...options })
|
||||
|
||||
this.firstDay = firstDayOfWeek(locale)
|
||||
this.format = inputFormat(locale)
|
||||
this.firstDay = firstDayOfWeek(locale, config.weekStart ?? null)
|
||||
this.format = inputFormat(locale, config.format ?? null)
|
||||
this.numbers = new Intl.NumberFormat(locale, { useGrouping: false })
|
||||
this.formats = {
|
||||
monthYear: format({ year: 'numeric', month: 'long' }),
|
||||
|
||||
Reference in New Issue
Block a user